Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
2b39815
Fix native clipboard editing in fields and plugin pages
BIackFIame Oct 5, 2026
22620f3
Preserve edit chords in shortcut recorders
BIackFIame Oct 5, 2026
4480245
Coordinate subagents with task graphs, reviews, budgets and retry
BIackFIame Oct 5, 2026
b30bc3a
Align launcher and tool regressions with the orchestration slice
BIackFIame Oct 5, 2026
46f95ac
Make signal-exit readiness checks portable across POSIX hosts
BIackFIame Oct 5, 2026
ad851ca
Confirm prompt delivery after CLI readiness and bound agent waits
BIackFIame Oct 5, 2026
7d12e88
Recognize the first OpenCode prompt before its conversation exists
BIackFIame Oct 5, 2026
ceed3bc
Return OpenCode agent waits before its sixty-second MCP deadline
BIackFIame Oct 5, 2026
5c0f3ba
Avoid rescanning unchanged prompt acknowledgement output
BIackFIame Oct 5, 2026
72accd2
Return pending reviews without blocking result requests
BIackFIame Oct 5, 2026
31dd501
Isolate subagent work and review changes with rollback points
BIackFIame Oct 5, 2026
98e65cd
Expose session history, reports, usage and attention signals
BIackFIame Oct 5, 2026
2b98821
Accept the action set required by worktree review
BIackFIame Oct 5, 2026
21d5c99
Align lifecycle and redaction tests with observability
BIackFIame Oct 5, 2026
f9298fe
Constrain network access and approve temporary secret requests
BIackFIame Oct 5, 2026
83102ba
Cover temporary secret tools in delegation invariants
BIackFIame Oct 5, 2026
9eb1109
Isolate synthetic HTTPS certificate extensions from host OpenSSL defa…
BIackFIame Oct 5, 2026
e2c0b76
Reuse project context and organize workspace sessions
BIackFIame Oct 5, 2026
b691ed7
fix: preserve edit menus in plugin windows and whitespace selections
BIackFIame Oct 7, 2026
a884b2b
Merge remote-tracking branch 'fork/fix/macos-field-clipboard' into ba…
BIackFIame Oct 7, 2026
a94be6b
fix: supply workflow tasks and attribute worker reviews
BIackFIame Oct 7, 2026
bc72abd
Merge upstream main into orchestration review branch
BIackFIame Oct 7, 2026
b09cfca
test: cover workflow task and endpoint launch guards
BIackFIame Oct 7, 2026
b8d9edb
fix: invalidate reviews when worker input changes
BIackFIame Oct 7, 2026
44d4519
test: wait for clicked document navigation
BIackFIame Oct 7, 2026
d93a42e
fix: preserve review ownership across input delivery
BIackFIame Oct 7, 2026
3f67c37
fix: bind review invalidation to all terminal input
BIackFIame Oct 7, 2026
c486cd9
fix: correlate worker answers with submitted turns
BIackFIame Oct 7, 2026
fd39534
test: verify disabled lifecycle status behavior
BIackFIame Oct 7, 2026
de0b5a9
fix: complete reviewer result transport independently of status UI
BIackFIame Oct 7, 2026
5fabc32
Serialize launch cleanup before removing session directories
BIackFIame Oct 7, 2026
d3a6cff
Drain pending launches before deleting owned run directories
BIackFIame Oct 8, 2026
e35a552
Merge reviewed orchestration fixes into worktree integration
BIackFIame Oct 8, 2026
321df1f
Fix provider-aware worktree routing and pre-turn checkpoint ordering
BIackFIame Oct 8, 2026
d1fdef1
Preserve turn checkpoints and restore review tracking on worktree retry
BIackFIame Oct 8, 2026
69ff25b
Serialize checkpoint restoration and retain failed turn snapshots
BIackFIame Oct 8, 2026
dbb7414
Refuse checkpoint restoration across isolated environment boundaries
BIackFIame Oct 8, 2026
e312192
Persist restore isolation evidence and handle large reviews
BIackFIame Oct 8, 2026
b94ac81
Test conservative isolation migration for saved environments
BIackFIame Oct 8, 2026
189be67
Restore tracked additions before replacing checkpoint index
BIackFIame Oct 8, 2026
8d496bb
Refuse checkpoint restores with uncaptured submodule state
BIackFIame Oct 8, 2026
3f25388
Merge verified worktree fixes and correct observability state
BIackFIame Oct 8, 2026
ea765f9
Deduplicate shared usage and clear answered glasses notices
BIackFIame Oct 8, 2026
69dad37
Keep companion attention tied to actionable conditions
BIackFIame Oct 8, 2026
1da0f6e
Restrict activity telemetry and correlate OpenCode tool outcomes
BIackFIame Oct 8, 2026
7eb562b
Report Kimi and Hermes core tool outcomes
BIackFIame Oct 8, 2026
5899829
fix(ci): preserve generated hook command quoting on Windows
BIackFIame Oct 8, 2026
3ecc36a
Merge reviewed observability fixes into protection
BIackFIame Oct 8, 2026
e62f856
Bind turn secret grants to observed launches and honor subdomain rules
BIackFIame Oct 8, 2026
3aa14ce
Keep secret approval requests available after turn completion
BIackFIame Oct 8, 2026
977b313
Enforce secret grant deadlines and isolate task approval state
BIackFIame Oct 8, 2026
54a703b
Cancel secret API workers with orchestration requests
BIackFIame Oct 8, 2026
4414da8
Bound proxy connection attempts and preserve UTF-8 response limits
BIackFIame Oct 8, 2026
d5e1b36
Preserve masked response boundaries and compact worker test fixtures
BIackFIame Oct 8, 2026
ab13292
Merge verified protection fixes into workspace improvements
BIackFIame Oct 8, 2026
84f4b17
Preserve portable workspace data and selected user actions
BIackFIame Oct 8, 2026
0df5f9c
fix: align workspace fixtures with integrated session contracts
BIackFIame Oct 8, 2026
e01c02a
fix: validate workspace imports and apply restored canvas state
BIackFIame Oct 8, 2026
6ced4b0
fix: preserve canvas boot prop ordering
BIackFIame Oct 8, 2026
957a11e
fix: prevent duplicate imports and retain overlapping terminal input
BIackFIame Oct 8, 2026
da8a0ca
Protect portable workspace data and preflight bypass imports
BIackFIame Oct 8, 2026
9ffb641
Preserve negotiated clipboard paste during terminal loading
BIackFIame Oct 8, 2026
3633848
Export live workspace cards and align context delivery limits
BIackFIame Oct 8, 2026
e8316ad
Fix retry capacity by replacing the launch in its existing card
BIackFIame Oct 8, 2026
e584c5a
Test fresh retry processes while retaining their card identity
BIackFIame Oct 8, 2026
d926c52
Merge retry replacement fixes while preserving worktree safeguards
BIackFIame Oct 8, 2026
7e1adea
Merge retry fixes and clear stale loop warnings after replacement
BIackFIame Oct 8, 2026
245f13d
Merge verified retry replacement into protection backlog
BIackFIame Oct 8, 2026
3b151b2
Merge verified retry replacement into workspace backlog
BIackFIame Oct 8, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Launch a shell or agent in a project directory, move and resize its live termina

Agents start in **Auto** by default: the CLI's own auto mode (Claude Code, Codex, Grok), CanvasTTY's per-run rules for OpenCode, or, for a CLI without one, its approval bypass only inside agent isolation. Manual, Accept edits, Plan and Bypass are offered only where the CLI has them; Bypass is acknowledged once per CLI and never handed to a subagent. Around every mode sit layers the person controls in Settings → Agents: base protection (hooks that refuse elevation, `curl | sh`, disk commands and writes outside the project before the tool call runs), delegation rules (a subagent never gets more than its orchestrator and stays in its project; 2 levels and 8 live subagents by default), and an OS isolation layer (macOS `sandbox-exec`, Linux bubblewrap; none on Windows yet, where subagents run in Manual). When an isolated session ends, CanvasTTY checks the repositories it touched for git settings that would run programs outside isolation and offers to neutralize them.

An **Orchestrator** session gets the `canvastty_agents` tools: `list_providers` (installed agents, sign-in state, models, efforts, profiles), `spawn_agent` with optional `model`, `effort` and `profile`, `wait_for_agent` (up to 600 s) and `get_agent_result`, which returns a Codex or OpenCode subagent's final reply as `answer` (up to 4,096 characters, masked). See [agent orchestration and isolation](docs/agent-orchestration.md) and [protection layers](docs/installing-and-security.md#agent-protection-layers).
An **Orchestrator** session gets the `canvastty_agents` tools: `list_providers` (installed agents, sign-in state, models, efforts, profiles), `spawn_agent` with optional `model`, `effort` and `profile`, `wait_for_agent` (up to 100 s per call, capped at 50 s for OpenCode) and `get_agent_result`, which returns a Codex or OpenCode subagent's final reply as `answer` (up to 4,096 characters, masked). See [agent orchestration and isolation](docs/agent-orchestration.md) and [protection layers](docs/installing-and-security.md#agent-protection-layers).

## Windows shells and provider CLIs

Expand Down
2 changes: 1 addition & 1 deletion agent/orchestrator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A session started from CanvasTTY's launcher with the **Orchestrator** role has t

1. `list_providers`: which agents CanvasTTY can launch here. Each entry's `id` is the exact `spawn_agent.provider` value; prefer entries that are `available` with `signIn: "ok"` (`unknown` only means CanvasTTY has not checked yet). When plugins offer launch options (for example an accounts plugin's `list_routes` tool), the answer names that tool.
2. `spawn_agent` once per part of the task: a known `provider` id, an absolute `cwd` and a self-contained prompt. An unknown provider is refused with a pointer back to `list_providers`. **If the person names a model, pass it as `model`** in the format `list_providers` gives for that provider (`model.format`; OpenCode takes `provider/model`, for example `zai-coding-plan/glm-5.3-flash`, and `model.known` lists what `opencode models` reported); without `model` the CLI uses its own default. `effort` sets the reasoning effort where the CLI has one (`efforts`). Without `profile` the subagent gets your own launch profile, or the next lower one its CLI has (never more than yours, never YOLO); `list_providers` shows each provider's `profiles`. `cwd` must be this project's folder or a folder inside it. A refusal says why (a folder outside the project, a profile above yours, a limit the person set): change the request, do not retry it.
3. `wait_for_agent` for each subagent (`timeoutSeconds` up to 600; call it again after `timeout`) instead of polling. `needs_approval` means the person must answer a prompt in that card: never answer it yourself.
3. `wait_for_agent` for each subagent (`timeoutSeconds` up to 100; OpenCode calls return within 50 seconds; call it again after `timeout`) instead of polling. `needs_approval` means the person must answer a prompt in that card: never answer it yourself.
4. `get_agent_result` to read what the subagent produced; check the actual files before reporting success.

Do not explore the filesystem, `PATH` or config folders (for example `~/.local/bin` or `~/.config/<cli>`) looking for agents or their configuration: `list_providers` is what CanvasTTY can launch, and delegation goes through `spawn_agent`.
Expand Down
4 changes: 2 additions & 2 deletions docs/agent-orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ A session launched from the desktop with the **Orchestrator** role gets the `can
| --- | --- |
| `list_providers` | The agent providers CanvasTTY can launch as subagents: `id` (the exact `spawn_agent.provider` value), `name`, `installed`/`available` (from the provider CLI registry resolved at startup or on a recheck), `signIn` (`ok`, `signed_out`, `expired` or `unknown`, from the last usage-limits read; it never starts a read and never reads credentials), `subagent` and `orchestrator` support, `model` (whether the CLI takes one, its format, and for OpenCode the models a cached `opencode models` listing reported: run in the background with a 5 s timeout, refreshed after 10 minutes, never awaited) and `efforts`, and plugin launch options (`launchOptions`, plus `launchOptionTools` such as an accounts plugin's `list_routes`). Fast: cached state only, no network calls. |
| `spawn_agent` | Launches a subagent (profile: see below). `provider` must be a `list_providers` id (the schema lists them); an unknown one is refused with `INVALID_REQUEST` naming `list_providers`. Optional `model` goes to the CLI's own `--model` for that run (OpenCode `provider/model`; Codex, Claude, Qwen, Kimi alias, Grok, OMP, Pi, Cursor; Hermes, MiniMax, Devin and Antigravity take none) and optional `effort` to its reasoning effort (Codex `-c model_reasoning_effort`, Claude `--effort`, Grok `--reasoning-effort`). Both are checked per provider, kept on restart and restore, and never written to the CLI's configuration. If the person names a model, the orchestrator passes it as `model`. An OpenCode model its own `opencode models` list does not contain is refused before launch with up to five closest listed ids (OpenCode would otherwise stop with only "Unexpected server error"); when that list cannot be read, the model is passed as given. A subagent that exits anyway reports the last lines of its screen as `exitLines` in `wait_for_agent`, `observe_agent` and `get_agent_result`. |
| `wait_for_agent` | `{ sessionId, timeoutSeconds ≤ 600 }` (default 55). Returns when the subagent is `idle` (after a prompt: only once a turn that started after the latest delivered prompt has ended, so the CLI's startup idle does not count), `needs_approval`, `done`/`failed` (exited), `quiet` (reports no status and its screen stopped changing), `closed`, or on `timeout`, with `status`, `exitCode`, `waitedMs` and the masked terminal tail (masked before it is cut). Only this orchestrator's own subagents; it stops at once when the call is canceled or the orchestrator disconnects. |
| `get_agent_result`, `observe_agent` | `get_agent_result`: `answer`, the final reply of the subagent's last turn as the agent itself reported it (Codex through its Stop hook, OpenCode through CanvasTTY's plugin reading the session's last assistant message at `session.idle`; at most 4,096 characters with the end kept, masked, in memory only and cleared when the next turn starts), plus `status`, the exit state and the masked terminal tail. Other providers have no `answer`: their tail is raw screen output. `observe_agent`: the current status and tail. |
| `wait_for_agent` | `{ sessionId, timeoutSeconds ≤ 100 }` (default 55; OpenCode caps each call at 50 seconds). A timeout returns progress while the agent continues; call again to keep waiting. Pending reviews do not extend this deadline. Returns when the subagent is `idle` (after a prompt: only once a turn that started after the latest delivered prompt has ended, so the CLI's startup idle does not count), `needs_approval`, `done`/`failed` (exited), `quiet` (reports no status and its screen stopped changing), `closed`, or on `timeout`, with `status`, `exitCode`, `waitedMs` and the masked terminal tail (masked before it is cut). Only this orchestrator's own subagents; it stops at once when the call is canceled or the orchestrator disconnects. |
| `get_agent_result`, `observe_agent` | `get_agent_result`: `answer`, the final reply of the subagent's last turn as the agent itself reported it (Codex through its Stop hook, OpenCode through CanvasTTY's plugin reading the session's last assistant message at `session.idle`; at most 4,096 characters with the end kept, masked, in memory only and cleared when the next turn starts), plus `status`, the exit state and the masked terminal tail. Other providers have no `answer`: their tail is raw screen output. An unfinished review returns `review.status: "pending"` immediately; call `get_agent_result` later for its outcome. `observe_agent`: the current status and tail. |
| `send_to_agent`, `cancel_agent`, `list_agents` | Follow-up prompts, disposing a subagent, listing this session's subagents. |

### Subagent profiles
Expand Down
2 changes: 1 addition & 1 deletion docs/canvastty-plugin.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
},
"cardActions": {
"description": "Card actions (needs cards:decorate): items in the options menu of matching cards; the host calls canvastty.cards.invoke and shows the answer's message as a toast.",
"type": "array", "minItems": 1, "maxItems": 8,
"type": "array", "minItems": 1, "maxItems": 16,
"items": { "$ref": "#/$defs/cardAction" }
},
"browserEngine": { "$ref": "#/$defs/browserEngine" }
Expand Down
10 changes: 9 additions & 1 deletion docs/installing-and-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,18 @@ Agents work in their own mode; CanvasTTY's layers sit outside it as a quiet safe
| The agent's own permission system | What its CLI promises in the chosen mode (Claude Code, Codex, OpenCode, Grok…). | Anything the CLI's own classifier or reviewer lets through. |
| Base protection (hooks) | Denies elevation, pipes into a shell, disk commands, fork bombs, writes and deletes outside the project and access to CanvasTTY's private data, before the tool call runs, for agents whose CLI has hooks (Claude Code, Codex, Qwen Code, OpenCode). A decision plugin's "ask" is shown by Claude Code; for a CLI that cannot ask it is a deny with the reason. | It reads commands; code that hides what it does, or a CLI without hooks, is not covered. It is a guard, not a sandbox. |
| Delegation rules | A subagent never gets more than its orchestrator (never YOLO), works only in its orchestrator's project folder, within the depth and count limits the person set; orchestrators get a control connection of their own; no agent-facing tool changes settings, protection, profiles, trust or isolation; plugin launch options from an agent only where the plugin declared them delegable. | What a plugin the person trusted does with its own permissions. |
| Agent isolation (OS layer) | On macOS (sandbox-exec) and Linux (bubblewrap), for subagents, plugin-started agents and every agent not in Manual: the whole process tree writes only in the project, its own temporary folder and its CLI's own folders; keys, other CLIs' credentials and CanvasTTY's tokens are unreadable; no other process, app, Apple event, preference write, launchd job or foreign Unix socket (Docker, tmux, SSH agent) is reachable. Fails closed. | Network is not restricted. Linux cannot filter Unix sockets, only hide the user runtime folder. The CLI's own sandbox cannot run inside the layer on macOS, so it is switched off there (approvals unchanged) and commands can use the network. A Claude launch may rewrite the login keychain file to save a refreshed sign-in, so it could also damage that file (other items stay behind their own access lists). Windows has no layer yet: subagents run in Manual there unless the person turns isolation off. |
| Agent isolation (OS layer) | On macOS (sandbox-exec) and Linux (bubblewrap), for subagents, plugin-started agents and every agent not in Manual: the whole process tree writes only in the project, its own temporary folder and its CLI's own folders; keys, other CLIs' credentials and CanvasTTY's tokens are unreadable. The layer blocks foreign processes, apps, Apple events, preference writes and launchd jobs. macOS blocks foreign Unix sockets; Linux hides the user runtime folder. Strict network modes also use Landlock to deny external Unix sockets except the exact runtime, orchestration and authorized proxy endpoints; local CLI sockets created inside the sandbox remain usable. A validated plugin worktree hides its private `plugin-data` parent and sibling worktrees while keeping its own files editable. The original task checkout remains readable and unwritable. Shared Git metadata stays read-only under isolation so an agent cannot change shared objects, references or index data; `git add` and `git commit` are unavailable inside the sandbox. The host environment collects file edits into a review for the user to accept. Plan has no Git metadata writes. Fails closed. | Network is unrestricted in open mode. Strict Linux network modes require the packaged native helper and Landlock ABI 9 or newer, enabled by the kernel. Unsupported kernels refuse strict launches with a reason; open mode retains the existing bubblewrap behavior. The CLI's own sandbox cannot run inside the layer on macOS, so it is switched off there (approvals unchanged). A Claude launch may rewrite the login keychain file to save a refreshed sign-in, so it could also damage that file (other items stay behind their own access lists). Windows has no layer yet: subagents run in Manual there unless the person turns isolation off. |
| Network policy | Per project: open, allowed domains (optionally provider APIs and package registries) or offline, applied when an agent starts and shown on its card. The policy lives in CanvasTTY's private data, so a project file or plugin cannot widen it; an extra HTTPS API domain is allowed only by the person in Settings. | It applies from the next agent start. Strict modes need the macOS sandbox, or on Linux the packaged helper and Landlock ABI 9; elsewhere a strict launch is refused. Plain SSH sessions are not covered. |
| Provider keys on request | An agent asks for one configured provider key with `request_secret`; the person allows it for 10 minutes, one turn or the session, and `run_secret_request` sends typed HTTPS requests with it. The key itself is never given to the agent, and a grant can be revoked in the card's details. | What the provider's API does with the requests the person approved. |

One-turn provider-key approval is available only when the current launch installed reliable turn-completion hooks and the host has observed its active turn. It is unavailable with lifecycle hooks disabled and for providers without a per-turn completion event (including the current Hermes integration). A new submitted task, hook disable, launch replacement or turn completion revokes the turn grant; enabling hooks later requires a new launch. Ten-minute and session approvals keep their separate lifetimes.

Allowed network domains include the root and dot-separated subdomains (`example.com` permits `api.example.com`, not `notexample.com` or `example.com.attacker.test`). An explicit `*.example.com` permits descendants only. DNS resolution must still produce a public address before the proxy connects.

Isolation keeps approval configuration read-only, including OpenCode's project and custom config files and its `agent`/`agents` directories. CLI state and caches remain writable. A moved CLI home is refused if its real path overlaps host credentials or private CanvasTTY data; a selected account folder below `account-homes` remains available. Credential exclusions use the host environment captured before launch contributions, even if the launch changes `HOME`.

For the existing Accounts plugin, the host derives one transient exception from its own plugin data directory and the exact selected account ID. Only that provider's `plugin-data/canvastty-accounts/homes/<provider>-<account>` subtree is reopened. The final sandbox wrapper rechecks every directory below userData for symlinks and canonical containment; the plugin cannot return an exception list. Parent data, sibling accounts and worktrees remain hidden, and the CLI's approval configuration remains write-protected.

OpenCode Auto inspects only regular local config files of at most 1 MiB. An existing source that cannot be inspected adds no Auto allowances. A launch contributor's uninspectable config is refused; only absent optional directory candidates are skipped. JSONC comments and trailing commas are parsed without changing quoted strings.

**Git audit.** When an isolated agent session ends, is closed or is restored after a quit, CanvasTTY checks the repositories under its folder whose git folder changed. If git would now run something outside isolation (a `core.hooksPath`, a filter or diff driver, `fsmonitor`, a hook file, `info/attributes`), a notice lists exactly what changed; **Neutralize** removes those keys and disables those files, **Keep as is** leaves them. It does not undo other file changes inside the project.
Expand Down
Loading
Loading