Skip to content

feat(core): M3a — modes + hooks framework + memory dual system#3

Merged
oratis merged 1 commit into
mainfrom
feat/m3-modes-hooks-memory
May 27, 2026
Merged

feat(core): M3a — modes + hooks framework + memory dual system#3
oratis merged 1 commit into
mainfrom
feat/m3-modes-hooks-memory

Conversation

@oratis

@oratis oratis commented May 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Partial M3 (first PR): mode policies, hook dispatcher (command handler), memory loader with @-import recursion + cycle detection + AGENTS.md.
  • 197 tests across all packages (156 core + 41 CLI, 4 skipped, 0 failed).
  • M3b (next PR) wires these into the agent loop and adds MCP / compaction / statusLine / auto classifier / additional hook handler types.

Test plan

  • `pnpm typecheck` — green
  • `pnpm test` — 197 passed / 4 skipped / 0 failed
  • `pnpm build` — all packages emit dist/
  • `pnpm format:check` — conformant

Key invariants tested

From `docs/design/sandbox-plan-worktree.md` §3.3:

  • ✅ Plan mode denies write tools regardless of permission
  • ✅ acceptEdits permission-deny still wins
  • ✅ dontAsk upgrades ask → deny (no prompt)
  • ✅ bypassPermissions skips permission rules (sandbox is M3.5+ safety)

Documentation

  • `docs/milestones/M3.md` — what M3a ships, what M3b adds

Release notes label

  • `release-notes:feature`

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Partial M3 (first of two PRs). Ships the policy/safety/memory primitives;
M3b (next PR) wires them into the agent loop and adds MCP + compaction +
statusLine + /init multi-phase + auto classifier mode.

What ships
----------
Core (packages/core/src/):
- modes/index.ts          — 5 mode policies (default / acceptEdits / plan /
                            auto / dontAsk / bypassPermissions) → ModeVerdict
                            with the invariants from docs/design/sandbox-plan-
                            worktree.md §3.3 hardcoded:
                              · plan blocks all write tools, regardless of
                                permission
                              · acceptEdits permission-deny still wins
                              · dontAsk upgrades ask → deny (strict allowlist)
                              · bypass skips permissions (sandbox-only safety)
- hooks/types.ts          — HookContext / HookHandlerOutput / HookResult
- hooks/dispatcher.ts     — 9 events × command handler, sequential dispatch,
                            matcher tool-name with `|` OR separator, JSON
                            output parsing (last {...} in stdout), per-handler
                            timeout (default 60s), disableAllHooks override,
                            payload streamed to handler stdin as JSON
- memory/loader.ts        — hierarchical DEEPCODE.md walk (cwd → parents),
                            user-level ~/.deepcode/DEEPCODE.md, AGENTS.md
                            auto-import at project root, .deepcode/rules/*.md
                            sorted load, @-import with 4-hop max + cycle
                            detection + maxBytes budget

Tests
-----
- modes/index.test.ts     (14 tests, all 6 modes × 4 permission verdicts)
- hooks/dispatcher.test.ts (14 tests: exec, JSON parse, matcher, timeout,
                            stdin, disableAllHooks, unimplemented-type
                            graceful)
- memory/loader.test.ts   (14 tests: empty / user / project / parents walk /
                            AGENTS.md / rules dir / @-import / unresolved /
                            cycle / maxBytes / maxDepth + walkUpwards unit)

Total: 197 passed / 4 skipped / 0 failed (was 151).

Deferred to M3b
---------------
- MCP client (stdio transport at minimum)
- Compaction (LLM summarizer when context > threshold)
- statusLine runner (JSON-on-stdin contract)
- /init multi-phase interactive flow
- auto classifier mode (LLM-judged per-call)
- 4 additional hook handler types (http / mcp_tool / prompt / agent)
- hooks `if` field (permission-syntax filtering)
- Wiring evaluateMode + HookDispatcher into runAgent (currently callable
  but not yet enforcing in the agent loop)

Verified
--------
  pnpm typecheck   → green
  pnpm build       → green
  pnpm test        → 197 passed / 4 skipped / 0 failed
  pnpm format:check → conformant

Docs
----
- docs/milestones/M3.md — delivery breakdown of M3a vs M3b deferred items

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@oratis oratis merged commit 3d08bd9 into main May 27, 2026
1 of 2 checks passed
@oratis oratis deleted the feat/m3-modes-hooks-memory branch May 27, 2026 16:29
oratis added a commit that referenced this pull request May 30, 2026
…ession

Implements the deferred inspector panel (design spec screen #3, HANDOFF §10).
The right column is a 48px rail by default; the ‹ button or ⌘\ expands it to a
320px panel that squeezes the chat stream (via the .inspector-open grid
modifier), not overlays it. Four sections:
  ▤ Plan          — the agent's TodoWrite list + pending count
  ◐ Context       — token usage bar (contextWindowFor(model) denominator)
  📁 Recent files — files touched by Write/Edit/MultiEdit this conversation
  ⓘ Session info  — project / path / model / mode / cumulative cost
Empty sections show honest empty states (no fake placeholders).

Wiring: ReplScreen lifts its inspector slice (usage/model/mode/recent files/
todos) to App via a single onInspector callback; App merges it into one
InspectorData that feeds both the collapsed rail's badges and the expanded
panel. The rail's ◐ context dot + ▤ plan badge are now driven by real data
(previously hardcoded undefined).

Taken over from the epic-neumann worktree and rebased onto main. Builds clean
(vite 222 modules), typecheck + 24 desktop tests pass, format + 0 new lint
warnings.

NOTE: this is UI that still needs a visual pass in `pnpm --filter
@deepcode/desktop tauri:dev` — confirm the 48↔320px toggle, ⌘\, and that all
four sections render against the design spec. Logic/build are verified; pixels
are not.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
oratis added a commit that referenced this pull request May 30, 2026
…ession (#91)

Implements the deferred inspector panel (design spec screen #3, HANDOFF §10).
The right column is a 48px rail by default; the ‹ button or ⌘\ expands it to a
320px panel that squeezes the chat stream (via the .inspector-open grid
modifier), not overlays it. Four sections:
  ▤ Plan          — the agent's TodoWrite list + pending count
  ◐ Context       — token usage bar (contextWindowFor(model) denominator)
  📁 Recent files — files touched by Write/Edit/MultiEdit this conversation
  ⓘ Session info  — project / path / model / mode / cumulative cost
Empty sections show honest empty states (no fake placeholders).

Wiring: ReplScreen lifts its inspector slice (usage/model/mode/recent files/
todos) to App via a single onInspector callback; App merges it into one
InspectorData that feeds both the collapsed rail's badges and the expanded
panel. The rail's ◐ context dot + ▤ plan badge are now driven by real data
(previously hardcoded undefined).

Taken over from the epic-neumann worktree and rebased onto main. Builds clean
(vite 222 modules), typecheck + 24 desktop tests pass, format + 0 new lint
warnings.

NOTE: this is UI that still needs a visual pass in `pnpm --filter
@deepcode/desktop tauri:dev` — confirm the 48↔320px toggle, ⌘\, and that all
four sections render against the design spec. Logic/build are verified; pixels
are not.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
oratis added a commit that referenced this pull request Jun 1, 2026
…137)

The collapsed inspector rail now matches design spec screen #3 exactly:
‹ expand · ▤ Plan (pending badge) · ◐ Context · 📁 Recent files ·
ⓘ Session info · ⚙ Settings. The four middle icons are inspector hints —
clicking one expands the 320px panel and scrolls to that section (new
`focusSection` prop on InspectorPanel + `data-section` anchors).

The rail previously doubled as the *only* navigation to Permissions / MCP /
Plugins / Skills / About. Per spec screen #9 those live behind the ⚙ cog in a
shared Settings shell, so this adds <SettingsLayout> (a `.set-nav` left column
+ pane) and folds the settings-family screens into it — nothing is stranded.
Sessions stay in the left sidebar where the spec puts them.

- InspectorRail: trimmed to the 6 spec elements; onExpand(section) / onSettings
  / settingsActive replace the old screen-routing props.
- SettingsLayout + SETTINGS_FAMILY: unified nav for settings-family screens.
- App: expandInspector(section), settings-shell wrapping in renderScreen.
- index.css: `.settings-shell` / `.set-nav` / `.set-pane` per spec lines
  754-768; sticky `.inspector-head` so focused sections land below it.

Verified in a browser harness: rail = 48px with exactly ‹/▤(badge)/◐/📁/ⓘ/⚙,
panel = 320px, set-nav switches active screen, ⌘\ unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant