Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 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
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
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
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: 10 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ An environment is where a card runs: a git worktree, a container, a remote host.

`keeps` declares what of CanvasTTY's protection reaches the agent there: `launch` (the launch's arguments and environment reach the agent unchanged, so CanvasTTY's hooks and the profile's per-run settings work), `isolated` (the agent does not run on this computer's files: a container or a remote host) and `confines` (the environment itself confines the agent to the project). Undeclared means no: any profile but normal is refused without `launch`, and the card says that base protection does not reach the agent there. An `isolated` environment is not wrapped in CanvasTTY's agent isolation again (the card names the environment's own boundary); any other runs inside it, with the environment's folder as the project.

Checkpoint restoration also refuses repositories when the current index or saved checkpoint contains submodules: parent-repository snapshots do not preserve dirty or untracked data inside nested repositories. Safeguard and restore that state with Git separately; CanvasTTY does not recursively delete or reset submodules.

Checkpoint restoration cannot suspend an isolated agent by pausing its local SSH/container wrapper. CanvasTTY refuses restoration for related isolated launches, including after the wrapper exits; that exit does not establish remote workload termination. The host remembers the isolation declaration and project scope used by successful launches in the saved host-only card record, independently of later plugin changes or app restarts. Legacy placed cards without this evidence, and malformed evidence, are treated as unknown isolation scope and refuse restoration. These guards cover tracked host sessions, not untracked external workloads.

CanvasTTY keeps the card, the PTY, the saved record and the restore order; the service answers five host-only requests (surfaces cannot send them):

| Request | Params | Answer | Budget |
Expand Down Expand Up @@ -338,6 +342,12 @@ A service may offer up to 16 `tools` to agents. They appear in the `canvastty_ag

### Session events and plugin-owned cards (`sessions:*`)

`canvastty.activity` is a separate host-only integration for the enabled, native-code-trusted Assistant service installed from a canonical Assistant repository. `sessions:events` does not grant activity access. Tool equality fingerprints sent to that service are host-secret HMAC values scoped to the card and fingerprint kind; they are not plain hashes of tool input or output. Install provenance is checked on each delivery.
OpenCode tool outcomes use its direct `tool.execute.after` hook and terminal `message.part.updated` tool states, correlated with a root-turn `tool.execute.before` call. Duplicate, child-session and obsolete completions are ignored. Shell nonzero exit codes are errors; missing exit status remains unknown. Separately trusted native `after-tool` hooks keep their provider-payload permission: the direct callback supplies `{ input, output }`, and a tool-part completion supplies its provider event. This does not grant `sessions:events` subscribers access to those payloads.
Kimi core hooks report `PostToolUse` and `PostToolUseFailure`; Hermes reports `post_tool_call`, independently of native plugin hooks. Kimi's `tool_output` is an opaque `str(ret)` that can also represent a tool error: its class stays `unknown`, while an explicit failure event reports `error`. It supplies no verified changed-path evidence. Hermes shell-hook `extra` carries the result, explicit outcome status and stable turn ID; result text is never interpreted as a status.



A service with `sessions:events` calls `sessions.subscribe` `{ ownedOnly? }` (again after every start). The answer lists the open cards; after that the host sends `canvastty.sessions.event` notifications:

```ts
Expand Down
6 changes: 5 additions & 1 deletion electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export default defineConfig({
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: resolve("src/main/index.ts")
input: {
index: resolve("src/main/index.ts"),
TerminalOutputHistoryWorker: resolve("src/main/services/TerminalOutputHistoryWorker.ts"),
SessionTimelineIndexWorker: resolve("src/main/services/SessionTimelineIndexWorker.ts")
}
}
}
},
Expand Down
12 changes: 12 additions & 0 deletions integrations/even-g2/src/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,17 @@ async function event(e) {
paint();
}
}
let attentionNotice = "";
let attentionId = "";
function showAttention(data) {
if(data.attention && data.attention.id!==attentionId) {
attentionId=data.attention.id;
attentionNotice=`${data.attention.title}: ${data.attention.kind}`;
notice=attentionNotice;
} else if(!data.attention && notice===attentionNotice) {
notice="";attentionNotice="";attentionId="";
}
}
async function poll() {
if (!alive) return;
try {
Expand All @@ -1033,6 +1044,7 @@ async function poll() {
"/g2/api/terminal?id=" + encodeURIComponent(selected),
);
model.terminal(data);
showAttention(data);
}
if (native) {
const state = {
Expand Down
Loading
Loading