Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
3667ae6
chore(spec-lint): split AGENTS.md's budget into prose plus a per-inde…
nedtwigg Aug 31, 2026
d5a844c
docs(dor-tool): re-cut the design — opt-in identity, port-triggered s…
nedtwigg Aug 31, 2026
8b47a63
feat(host): dormouse.yml parsing, dedupe keys, and the repo-trust record
nedtwigg Aug 31, 2026
6cfe6b7
feat(dor): `dor tool` and the tool Surface kind
nedtwigg Aug 31, 2026
d25ffc7
feat(tool): the tool Surface — host plumbing, params, and the two-cap…
nedtwigg Aug 31, 2026
3ed079b
feat(tool): `surface.tool` handler, the serving trigger, and the trus…
nedtwigg Aug 31, 2026
08b0ef9
feat(tool): OSC 367 — the announcement that disambiguates, never mints
nedtwigg Aug 31, 2026
fada288
feat(tool): ab-screencast rendering, so an agent can drive a tool's b…
nedtwigg Aug 31, 2026
dc780bb
docs(dor-tool): promote the atom above the fold; gate it on dormouse.…
nedtwigg Aug 31, 2026
a8c4c08
fix(tool): hide the inactive half with visibility, not display
nedtwigg Aug 31, 2026
8375116
simplify(tool): apply /simplify findings
nedtwigg Aug 31, 2026
50a625d
fix(tool): apply the correctness review — namespaced keys, safe rende…
nedtwigg Aug 31, 2026
a1d7fe4
chore(website): disclose the yaml dependency
nedtwigg Aug 31, 2026
1b05bdf
fix(tool): address the PR #493 review
nedtwigg Aug 31, 2026
ba789ca
fix(tool): make the size-cap tests load-bearing; stop offering pop-ou…
nedtwigg Aug 31, 2026
c460a83
fix(tool): gate pop-out at both registration sites; make the gate tes…
nedtwigg Aug 31, 2026
3d61939
fix(tool): call isToolParams in the controller; correct the header's …
nedtwigg Aug 31, 2026
cff909d
fix(tool): actually move the orphaned doc comment
nedtwigg Aug 31, 2026
0e3e7f1
feat(tool): autobind — declare the port strategy, and refuse to guess…
nedtwigg Aug 31, 2026
f572c11
feat(tool): key trust on the upstream remote, and ask in the pane
nedtwigg Aug 31, 2026
8bdb85c
fix(tool): let a late announcement override a committed conflict
nedtwigg Aug 31, 2026
22dbf2b
simplify(tool): apply /simplify findings
nedtwigg Aug 31, 2026
b5820bb
fix(tool): the pending-approval flow was broken end to end
nedtwigg Aug 31, 2026
a100a1b
fix(tools): stage approved shell before reveal
nedtwigg Aug 31, 2026
55269c6
fix(tools): bound untrusted tool file reads
nedtwigg Aug 31, 2026
433af76
fix(tools): respawn commands on session restore
nedtwigg Aug 31, 2026
71c2369
fix(tools): retire browser resources on exit
nedtwigg Aug 31, 2026
10d7e7b
fix(tools): route terminal-face clipboard keys
nedtwigg Aug 31, 2026
4eb2ea8
fix(tools): preserve untouched state until input
nedtwigg Aug 31, 2026
7abc558
fix(tools): validate integration before approval
nedtwigg Aug 31, 2026
14e11ac
fix(tools): serialize trust-file grants
nedtwigg Aug 31, 2026
e493eae
fix(tools): report revealed reuse as visible
nedtwigg Aug 31, 2026
437b650
fix(tools): start approved minimized sessions
nedtwigg Aug 31, 2026
c599f7a
fix(tools): guard untouched destructive actions
nedtwigg Aug 31, 2026
053c489
fix(tools): report revealed pending reuse
nedtwigg Aug 31, 2026
1ad6eb9
fix(tools): expire orphaned trust locks
nedtwigg Aug 31, 2026
149ea04
fix(tools): reject symlinks on every host
nedtwigg Aug 31, 2026
f4dff4d
fix(tools): resolve trust upstream host-side
nedtwigg Aug 31, 2026
a3f5995
fix(tools): preserve browser navigation
nedtwigg Aug 31, 2026
4592af2
fix(tools): reveal pending approval surfaces
nedtwigg Aug 31, 2026
a4123e9
docs(tools): repair trust approval flow
nedtwigg Aug 31, 2026
9254df4
fix(tools): confirm keyboard kills
nedtwigg Aug 31, 2026
5e18609
fix(tools): remove untrusted grant URL
nedtwigg Aug 31, 2026
fb6d7ee
fix(tools): make stale lock recovery race-free
nedtwigg Aug 31, 2026
d5fd9b9
refactor(tools): reuse tool params classifier
nedtwigg Aug 31, 2026
a6521f0
docs(tools): sync untouched kill shortcuts
nedtwigg Aug 31, 2026
a131c0e
fix(tools): migrate legacy trust lock
nedtwigg Aug 31, 2026
b4857b4
Merge main into phase-b; integrate Tools with Terminal Context and cu…
nedtwigg Sep 6, 2026
fe6de35
Fix Tool context focusing its browser instead of its terminal
nedtwigg Sep 6, 2026
f9bfede
Wait for deferred notepad closure in Wall tests
nedtwigg Sep 6, 2026
eaa7b18
Resolve Tool note pins after context mount and terminal refit
nedtwigg Sep 6, 2026
71f15b3
Merge main into phase-b and integrate Tools with Workspace routing an…
nedtwigg Sep 15, 2026
4a2bc00
Keep Tool reconnects transfer-safe and update the harness command fix…
nedtwigg Sep 15, 2026
5ee35bb
Integrate Tools with current main and alert transfer lifecycle
nedtwigg Sep 16, 2026
eee6c21
Preserve valid source pins when opening Tool context changes layout
nedtwigg Sep 16, 2026
7996423
Require a recorded trust grant before resolving an approved Tool
nedtwigg Sep 16, 2026
1c7ad05
simplify(tool): apply /simplify findings to PR #493
nedtwigg Sep 16, 2026
45b5c5d
Keep existing Tool lifecycle active when creation is disabled
nedtwigg Sep 16, 2026
f0a28ef
Validate Tool trust receipts before accepting grants
nedtwigg Sep 16, 2026
993df67
Focus the terminal capability when a Tool browser retires
nedtwigg Sep 16, 2026
6ecfba2
Isolate Tool serving state across command runs
nedtwigg Sep 16, 2026
bdf54fd
Hold Tool launch queue through initial command startup
nedtwigg Sep 16, 2026
b2c577d
Serialize Tool approvals with keyed launch reuse
nedtwigg Sep 16, 2026
280a896
Account for Tool lifecycle and approval invariants
nedtwigg Sep 16, 2026
8c1257a
Show rejected Tool permission grants in pending approval panes
nedtwigg Sep 16, 2026
416b59c
Keep rejected Tool permission errors visible until retry
nedtwigg Sep 16, 2026
ea661ad
Keep Tool grant failures actionable for blank host reasons
nedtwigg Sep 16, 2026
fac876e
Keep Tool approval content reachable in small panes
nedtwigg Sep 16, 2026
b30c6c3
Share scroll-safe layout across pane messages
nedtwigg Sep 16, 2026
8e84c6d
Cover approval wrapping and scroll reachability in Storybook
nedtwigg Sep 16, 2026
fe3684e
Keep pane message layout classes private
nedtwigg Sep 16, 2026
ef71ef6
Keep Tool approval and Workspace move failures visible
nedtwigg Sep 16, 2026
74c9314
Merge remote-tracking branch 'origin/main' into phase-b
nedtwigg Sep 16, 2026
d44f1c3
Verify quit clears pending Workspace move errors
nedtwigg Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ standalone/sidecar/alert-store.cjs
# Kept beside it: a checkout that built before the Burrow rename still holds
# the old bundle, and `bundle.resources` would ship it.
standalone/sidecar/remote-host.cjs
standalone/sidecar/tool-host.cjs
standalone/sidecar/node_modules/
standalone/node_modules/

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ A spec is the accurate reference for the current code: it states the invariants
- **`docs/specs/theme.md`** — The two-layer CSS variable strategy, consumed-token resolver, terminal color contract, theme debugger.
- **`docs/specs/dor-cli.md`** — The `dor` CLI on every Dormouse terminal's `PATH`: bundling and env contract, `spawnAndCapture` rules, control-socket plumbing, the Surface handle model, the command set.
- **`docs/specs/dor-browser.md`** — The browser surface: `BrowserPanel` with swappable `renderMode`, browser chrome, the agent-browser stack, the iframe proxy and CSP boundaries.
- **`docs/specs/dor-tool.md`** — Dor Tools (design-stage): the `tool` Surface — a terminal and a browser on one Session spine — its capability-gated verbs and OSC 367 contract. Only capability gating is built.
- **`docs/specs/dor-tool.md`** — Dor Tools: the `tool` Surface — a terminal and a browser on one Session spine — its capability-gated verbs and OSC 367 contract. Designation, trust, serving, and persistence are built behind the Tools flag.
- **`docs/specs/vscode.md`** — VS Code host: webview hosting, webview ↔ Workspace mapping, persistence ordering, theme integration, CSP, the build/dogfood pipeline.
- **`docs/specs/standalone.md`** — Tauri host: the Rust ↔ Node-sidecar bridge, boot sequence, AppBar, persistence, shutdown ordering, the build/dev workflow.
- **`docs/specs/auto-update.md`** — Standalone auto-update: check → approved download → install-on-quit, the Baseboard notice, Windows sidecar teardown, per-platform quit behavior.
Expand Down
6 changes: 6 additions & 0 deletions docs/specs/dor-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,12 @@ Source of truth: `dor/src/commands/skill.ts`, `scripts/generate-dor-skill.mjs`,

Source of truth: `buildDorSurfacesInternal` in `lib/src/components/Wall.tsx`; `dispatchDorControlRequest` in `lib/src/lib/platform/dor-control-dispatch.ts`.

## Dor Tools

**Must route `dor tool` through the Tool launch contract**, including feature gating, approval, explicit-key reuse, and focus-neutral placement (`docs/specs/dor-tool.md` → CLI). Generated help owns syntax.

Source of truth: `toolCommand` in `dor/src/commands/tool.ts`; `ToolSurfaceResponse` in `dor/src/commands/types.ts`.

## Future

- **Surface a dead control channel in the UI.** A lost bind leaves one
Expand Down
437 changes: 201 additions & 236 deletions docs/specs/dor-tool.md

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions docs/specs/dor-tool.rationale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Dor Tools — Rationale

> Informative evidence for `docs/specs/dor-tool.md`, keyed by its headings.

## Declaring tools

YAML authors naturally collapse one-element lists to scalars. Overloading a scalar dedupe key as a command would make `prespawn_dedupe: storybook` execute instead of identify. Separate future fields avoid that ambiguity.

A misspelled substitution such as `$PROJECTROOT` retained as a literal silently makes distinct checkouts share a key. Rejecting unknown substitutions exposes the typo before reuse can target another checkout.

## Identity and dedupe

`pnpm storybook`, `pnpm run storybook`, and `pnpm storybook --quiet` are different command strings for the same intended tool. `dor ensure` already supplies exact-command/CWD identity. An explicit Tool key allows authors to choose their own scope without making the declaration of a short command name implicitly enable dedupe.

A key list makes scope visible: `$PROJECT_ROOT` distinguishes worktrees without string-concatenation conventions. A runtime collision differs from a redundant spawn: both Surfaces may already hold edited documents, so merging or killing either can destroy work.

## Trust

A prompt rendered as terminal output is forgeable, and `dor send` can type bytes identical to a user's. The dedicated chrome action prevents terminal/control-socket input from granting approval through the normal command path. It does not establish a boundary against arbitrary programs running as the same OS user.

Upstream grants reduce repeated approval across clones and worktrees. They rely on the URL reported by Git, without authenticating the checkout's provenance. Folder grants provide narrower scope. A copied directory carrying `.git/config` can claim a previously trusted URL; cloning a chosen URL has a different provenance story.

Remembering a denial would disable tools across worktrees without a corresponding grant-management UI. Closing the pending pane is recoverable on another explicit invocation. Content-hashing approval would prompt after routine edits or pulls, making acceptance habitual.

## Serving

An exit and rerun can both occur inside the 1.5-second polling interval. Command text alone then leaves the previous browser and settle state attached to a new process. Run ids expose that transition; observing a transferred Workspace for the first time does not imply a restart. Clearing hints at the command-start event, in stream order, also avoids deleting a new serve emitted before the next poll.

Renderer swaps and Workspace transfers can give a Tool a browser session name other than the serving hook's default. Reopening that existing session preserves its browser state and avoids orphaning it behind a second daemon.

The standalone browser harness binds more than one HTTP port. Choosing the lowest port or the first observed listener cannot identify which service the user intended to see. A conflict in the browser area gives that refusal a visible explanation while keeping the terminal accessible.

Successive startup listeners can appear in different scan ticks. One unchanged tick catches changes within that window; it does not prove no later listener will appear. Remembering the last applied announced port keeps repeated announcements from undoing URL-bar navigation.

A hardcoded Storybook port can disagree with the port it obtains under contention, while Vite with strict-port behavior can fail entirely. Discovery therefore checks the Session process tree. An OSC can cross SSH, but the current host scan still requires a locally discoverable listener.

## Lifecycle

The September 2026 integration reuses Terminal Context for the Tool's primary terminal. The auxiliary helper's automatic refresh, Reset, and Promote semantics do not describe a serving command, whose Session also owns the browser and remote terminal identity. Sharing the presentation avoids introducing a second navigation mechanism or a second shell.

## Security

Hostile text printed by the designated command can contain an announcement. The current process-tree check limits port selection to that Session's discovered listeners; browser content still executes under the existing renderer boundaries. Earlier text describing arbitrary local-port selection did not match the scan implementation.

## Persistence and hosts

A derived URL or browser daemon binding belongs to one execution. Reusing it after cold restore can connect a Tool to another process that obtained the old port. The saved command and declaration metadata are sufficient to start again and discover the new endpoint.

Routing `dor tool` to a native editor on one host would change its result from a Surface handle to a host-specific side effect. Native file opening remains a separate operation.

A Workspace transfer carries the live browser binding separately from its durable record. The arrival record can reach disk while the windows coordinate, whereas the content channel stays in memory; reusing the saved-record projection alone would reopen a Tool browser and lose its current page state. Pending approvals and unfinished browser startup still own asynchronous work in the source window, so the move waits for the user to resolve the approval or retry after startup.
7 changes: 5 additions & 2 deletions docs/specs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

## The core idea

A **Surface** is the durable occupant of a Pane — the content in a slot. Two kinds:
A **Surface** is the durable occupant of a Pane — the content in a slot. Three kinds:

- a **terminal Surface**, which Dormouse calls a **Session**: a PTY-backed shell with scrollback and semantic terminal state. The six-axis model below describes this kind.
- a **tool Surface**: a Session with terminal and browser capabilities (`docs/specs/dor-tool.md`).
- a **browser Surface**: a web view (`docs/specs/dor-browser.md`), taking only a subset of the axes ([Panes and Surfaces](#panes-and-surfaces)).

**Unless a passage says "Surface" or "browser Surface," it describes a Session.** A Session's state lives on six distinct axes; an operation can change several together. Their separate preconditions define the **[Liskov contract](#liskov-contract)**.
Expand All @@ -22,18 +23,20 @@ A Pane holds exactly one Surface today, but the model reserves several (a future
| Kind | Sub-kinds | Backed by |
|---|---|---|
| `terminal` | — | a PTY + xterm.js instance — a **Session** |
| `tool` | — | a PTY and an optional browser on the same Session |
| `browser` | `iframe`, `ab-screencast`, `ab-popout` | an iframe proxy grant, or an agent-browser daemon session (`docs/specs/dor-browser.md`) |

**For a browser Surface `renderMode` is canonical**; the CLI `render_mode` is derived from it and never stored.

| Surface | Persisted `surfaceType` (`docs/specs/transport.md`) | `renderMode` (`docs/specs/dor-browser.md`) | CLI `kind` | CLI `render_mode` |
|---|---|---|---|---|
| tool Session | `'tool'` | `iframe` or `ab-screencast` when serving | `tool` | renderer or `null` |
| terminal Session | `'terminal'` (default, omitted) | — | `terminal` | `null` |
| browser · iframe | `'browser'` | `iframe` | `browser` | `iframe` |
| browser · screencast | `'browser'` | `ab-screencast` | `browser` | `ab-screencast` |
| browser · popped out | `'browser'` | `ab-popout` | `browser` | `ab-popout` |

**Kinds are capability sets, not exclusive categories** — the two above carry one capability each, the staged `tool` (`docs/specs/dor-tool.md`) both. **Operations gate on the capability they need, never on the kind enum** ([Liskov contract](#liskov-contract)): `read` / `send` / `await` / port scans need the terminal, nav / render-mode / agent-browser verbs the browser. **`dor list --json` rows always emit `has_terminal` and `has_browser`** (rationale). **Must declare each kind's capabilities in the `hasTerminal` / `hasBrowser` table.** Persistence keeps its own `PersistedSurfaceType` discriminant (`docs/specs/transport.md`).
**Kinds are capability sets, not exclusive categories** — terminal and browser carry one capability each, `tool` both. **Operations gate on the capability they need, never on the kind enum** ([Liskov contract](#liskov-contract)): `read` / `send` / `await` / port scans need the terminal, nav / render-mode / agent-browser verbs the browser. **`dor list --json` rows always emit `has_terminal` and `has_browser`** (rationale). **Must declare each kind's capabilities in the `hasTerminal` / `hasBrowser` table.** Persistence keeps its own `PersistedSurfaceType` discriminant (`docs/specs/transport.md`).

Source of truth: `hasTerminal` / `hasBrowser` in `dor/src/commands/types.ts`; `surfaceKindFromParams` in `lib/src/components/wall/browser-surface.ts`.

Expand Down
Loading