Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- Anything rendered by the composer must be styled from an always-loaded stylesheet: `architect.css` (and other lazily imported CSS) only arrives with its module, which is how the Team button once changed size on first click.
- Native: `workspace::run_process` is the one bounded runner for external tools (`git`, `gh`); user text goes to `gh` as `--flag=value`.
- Turn speed and tokens: the fixed app instructions (sidebar titles, `pandacode-run.sh`, change tracking; `app_instructions()` in `lib.rs`) go to Claude-family harnesses once as `--append-system-prompt`, which caches with the prefix; other harnesses get them in the message on a new conversation and every 8th turn. Only a one-line current-title note rides each message. After a successful Claude turn, `providers/warm.rs` starts the next turn's process in the background (at most 2, 10 idle minutes); a follow-up adopts it only when model, mode, effort, folder, resume ID, instructions and tool catalog all match (the MCP server is rebound to the new turn at the same address), so it skips the CLI's startup. Anything else starts cold.
- Harnesses: adding a provider means touching every `"kimi" | "opencode" | "antigravity"`-style match in `providers.rs`/`acp.rs`, the allowlists in `storage.rs`, `overlay.rs`, `architect*.rs`, `terminal.rs`, `slash_commands*.rs`, `provider_settings.rs`, plus `ProviderId` and its `Record` maps on the frontend (tsc flags the maps). ACP harnesses share `acp.rs`; provider quirks (Grok's `authenticate` step, plan-mode policy, `_meta.totalTokens` usage) live behind `config.provider` checks there. Record real frames in the tests, and keep an ignored live smoke test per installed CLI. Key-based Claude Code relays (GLM, MiMo) each get a `providers/<id>.rs` with `environment()/arguments()/configure()/models()`, a `Credential` in `provider_settings.rs` (own secure-store account + env var), and an entry in the `keyed` table of `src/provider-settings.ts`, which renders the card's key form and status. Provider environment variables ([docs/providers.md](docs/providers.md#environment-variables)): a global set plus per-provider lists in `provider-settings.json` (`provider_settings.rs`; names-only IPC, plain values, connection's `valid_env_name` rules) are merged by `environment()` (global unless the provider opted out, then its own) and applied by `providers::provider_command`/`apply_environment` at every harness spawn — `run_inner`, warm agents (whose key carries `environment_fingerprint`, so an edit starts cold), `terminal.rs`'s harness console, and the catalog/discovery/usage/integrations probes; saving invalidates that provider's usage, catalog and slash-command caches. Cloud runs inherit nothing by default: `cloud_agents/transport.rs::inherited_environment` adds the harness's saved variables only when `cloud::Settings.providers[<cloud>]["inheritEnvironment"] == "true"` (`cloud_set_inherit_environment`, the checkbox in `src/cloud-settings.ts`), refuses loopback proxy values at start, chains them before credentials so credentials win, and mirror tasks on other computers always use that computer's own settings. Schema is at `user_version` 36 (version 36 added machine orchestration in one step: `machine_threads` with `reported_by`, the wake message that reported a thread, `sessions.restart_interrupted`, set when startup interrupts a running task (mirrors keep their status), and `sessions.stop_requested`, the user's Stop saved before the run is signalled so a quit right after it never counts as a restart interruption; version 35 added `task_schedules`, `task_schedule_runs` and `scheduler_settings` for task schedules; version 34 added `task_usage_links`, every harness session id a task has run under, so per-task usage totals can be summed from the agents' own logs; version 33 added `messages.remote_cut`; version 32 `sessions.remote_backfill_base`; version 31 `remote_cursors`; version 30 added `message_attachments`, the images a user message carried: captures copied to `<app data>/attachments/<session>/`, moodboard images linked by id; served to phones by `task.attachment`; version 29 added `bots.skills`/`bots.mcp`, a bot's skill and MCP references, and `bot_tasks`, the bot a project task started with; version 28 added `bots.soul`, a bot's optional personality; version 27 added `sessions.cloud` for cloud agents; version 26 added the mirror columns `sessions.remote_device`, `remote_session` and `remote_synced_id`; the bot tables in `bots::SCHEMA`; version 24 added `sessions.limited_at`, `limited_until` and `resume_at_reset`; version 23 `queued_messages`, the durable per-task queue in `message_queue.rs`; version 22 added `sessions.draft`, set for a New task until its first saved prompt publishes it; version 21 added `sessions.pinned` and `sessions.sidebar_order`, version 20 `sessions.title_origin`, version 19 `sessions.last_prompt_at`). Drafts are excluded from `list_sessions`; empty ones are deleted on leave (`discard_draft`) and at startup. Adding a migration step means bumping `storage::SCHEMA_VERSION` (the outer guard reads it; a test checks it matches the newest `PRAGMA user_version=`) and updating the tests that assert the number.
- Harnesses: adding a provider means touching every `"kimi" | "opencode" | "antigravity"`-style match in `providers.rs`/`acp.rs`, the allowlists in `storage.rs`, `overlay.rs`, `architect*.rs`, `terminal.rs`, `slash_commands*.rs`, `provider_settings.rs`, plus `ProviderId` and its `Record` maps on the frontend (tsc flags the maps). ACP harnesses share `acp.rs`; provider quirks (Grok's `authenticate` step, plan-mode policy, `_meta.totalTokens` usage) live behind `config.provider` checks there. Record real frames in the tests, and keep an ignored live smoke test per installed CLI. Key-based Claude Code relays (GLM, MiMo) each get a `providers/<id>.rs` with `environment()/arguments()/configure()/models()`, a `Credential` in `provider_settings.rs` (own secure-store account + env var), and an entry in the `keyed` table of `src/provider-settings.ts`, which renders the card's key form and status. Provider environment variables ([docs/providers.md](docs/providers.md#environment-variables)): a global set plus per-provider lists in `provider-settings.json` (`provider_settings.rs`; names-only IPC, plain values, connection's `valid_env_name` rules) are merged by `environment()` (global unless the provider opted out, then its own) and applied by `providers::provider_command`/`apply_environment` at every harness spawn — `run_inner`, warm agents (whose key carries `environment_fingerprint`, so an edit starts cold), `terminal.rs`'s harness console, and the catalog/discovery/usage/integrations probes; saving invalidates that provider's usage, catalog and slash-command caches. Cloud runs inherit nothing by default: `cloud_agents/transport.rs::inherited_environment` adds the harness's saved variables only when `cloud::Settings.providers[<cloud>]["inheritEnvironment"] == "true"` (`cloud_set_inherit_environment`, the checkbox in `src/cloud-settings.ts`), refuses loopback proxy values at start, chains them before credentials so credentials win, and mirror tasks on other computers always use that computer's own settings. Schema is at `user_version` 37 (version 37 added `sessions.auto_writes`, the per-task Auto-approve writes toggle that pre-answers write-class approval requests in Standard and Accept edits modes (Standard only for Codex); version 36 added machine orchestration in one step: `machine_threads` with `reported_by`, the wake message that reported a thread, `sessions.restart_interrupted`, set when startup interrupts a running task (mirrors keep their status), and `sessions.stop_requested`, the user's Stop saved before the run is signalled so a quit right after it never counts as a restart interruption; version 35 added `task_schedules`, `task_schedule_runs` and `scheduler_settings` for task schedules; version 34 added `task_usage_links`, every harness session id a task has run under, so per-task usage totals can be summed from the agents' own logs; version 33 added `messages.remote_cut`; version 32 `sessions.remote_backfill_base`; version 31 `remote_cursors`; version 30 added `message_attachments`, the images a user message carried: captures copied to `<app data>/attachments/<session>/`, moodboard images linked by id; served to phones by `task.attachment`; version 29 added `bots.skills`/`bots.mcp`, a bot's skill and MCP references, and `bot_tasks`, the bot a project task started with; version 28 added `bots.soul`, a bot's optional personality; version 27 added `sessions.cloud` for cloud agents; version 26 added the mirror columns `sessions.remote_device`, `remote_session` and `remote_synced_id`; the bot tables in `bots::SCHEMA`; version 24 added `sessions.limited_at`, `limited_until` and `resume_at_reset`; version 23 `queued_messages`, the durable per-task queue in `message_queue.rs`; version 22 added `sessions.draft`, set for a New task until its first saved prompt publishes it; version 21 added `sessions.pinned` and `sessions.sidebar_order`, version 20 `sessions.title_origin`, version 19 `sessions.last_prompt_at`). Drafts are excluded from `list_sessions`; empty ones are deleted on leave (`discard_draft`) and at startup. Adding a migration step means bumping `storage::SCHEMA_VERSION` (the outer guard reads it; a test checks it matches the newest `PRAGMA user_version=`) and updating the tests that assert the number.
- Devin CLI (`providers/devin.rs`, docs/providers.md#devin-cli) runs `devin acp` with `ACP_BACKEND` removed (Devin Desktop terminals set it, and then the server ignores the CLI sign-in); PandaCode never calls `authenticate`. Each model has its own `thought_level`, reported only after switching to it, so discovery switches every non-Fusion model across parallel empty sessions (about 2.5 s; Fusion switches take ~1 s each and are listed without efforts) and deletes them, as command discovery does, so Devin's history stays clean. Modes: Standard/Accept edits → `accept-edits`, Plan → `plan` (its `exit_plan_mode` request is the "Implement this plan?" approval, answered `plan_accept_edits`), Full → `bypass`; approvals only ever pick `allow_once`. Unchanged mode/model/effort are not resent on resumed sessions; steering is live. Live tests: `cargo test --lib installed_devin -- --ignored` (one sends two short prompts and deletes its session).
- ZCode (`providers/zcode.rs`, docs/providers.md#zcode) has no ACP mode; PandaCode speaks z.ai's private `zcode app-server` protocol (NDJSON stdio, `session/create|subscribe|send`, events on `session/event`, permissions as `interaction/requestPermission` reverse requests answered with the offered `response` object). One server per run, deferred persistence, resume by saved `sess_<uuid>`; model/effort are set and verified against the returned snapshot. Modes: Standard → `build`, Accept edits → `edit`, Plan → `plan`, Full → `yolo`; steering holds for the next turn; screenshots and interactive questions are refused with a note; slash-command discovery and the harness console are not offered. Protocol shapes are pinned per recorded version (`.agents/state/zcode-harness/protocol.md`); live test `cargo test --lib installed_zcode_lists_models -- --ignored` (catalog only, never a prompt).
- Queue and steering: while an agent runs, Enter queues (`queue_*` commands, shown by `src/message-queue.ts` above the composer) and ⌘/Ctrl Enter steers (`steer_task` → `RunInput::Steer`, `providers/steering.rs`). Steering never stops a run: Claude-family harnesses steer on stream input (launched with `--replay-user-messages` so a late steer keeps the run open for its turn), Codex uses `turn/steer`, Pi `steer`, OpenCode a second `session/prompt` (`acp.rs`); anything a turn cannot take (startup, between turns, refused, other ACP agents) is held by the run and sent as the next turn of the same session (`mode: "next-turn"`). `steer` agent events report `pending`/`delivered`/`failed` by id; the composer shows the pending state and requeues a failed steer. A clean run end delivers the next entry; a failure, stop or restart holds the queue until Resume. The prompt stays enabled during runs, so `renderComposer` must keep focus and the caret. The wider roadmap is in `docs/parity-plan.md`.
Expand Down
4 changes: 2 additions & 2 deletions docs/remote-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ prevents accidents; it is not a boundary against a device that can also answer a
| `projects.icons` | `{ projectIds: string[] }` (1..=50; use project ids from `tasks.list`) | `{ icons: { [projectId]: string \| null } }`. Known non-bot local projects map to their icon `data:` URL, or `null` when no icon exists or it exceeds 64 KiB. Unknown ids are omitted. Icons stop being added before the result exceeds 180 KiB, so omitted ids can be requested again. |
| `providers.list` | `{ force?: boolean }` | the host's `list_providers` result: `[{ id, name, installed, path, version, models }]`. `force` drops the same ACP catalog caches. If it would not fit one reply, the longest `models` lists are halved until it does. |
| `providers.models` | `{ provider, force?: boolean }` | the host's `list_provider_models` result: `[{ id, name, description, reasoningEfforts, defaultReasoningEffort, contextWindow }]`, its tail dropped to fit one reply. `provider` must be a known harness id (`Unknown provider.`). |
| `task.start` | `{ repo?: "owner/name", projectId?: string, branch?, worktree, prompt, provider, model, mode?, effort?, startId? }` (exactly one of `repo` or `projectId`) | `{ sessionId }`. A repeated `startId` from the same device runs nothing new: it waits for (or returns) the first start's result. |
| `task.start` | `{ repo?: "owner/name", projectId?: string, branch?, worktree, prompt, provider, model, mode?, autoWrites?, effort?, startId? }` (exactly one of `repo` or `projectId`) | `{ sessionId }`. A repeated `startId` from the same device runs nothing new: it waits for (or returns) the first start's result. `autoWrites` sets the task's Auto-approve writes toggle. |
| `task.startStatus` | `{ startId }` | `{ state: "running" \| "done" \| "failed" \| "unknown", sessionId?, error? }`. Finished starts are remembered for an hour. |
| `task.cancelStart` | `{ startId }` | `{ cancelled: true }` once a running start was told to stop (its callers get `Start cancelled.` at once, the clone's process group is killed); `{ cancelled: false, sessionId }` when it had already made its task. |
| `task.messages` | `{ sessionId, afterId?, tail?, beforeId?, limit?, budget? }` (`limit` is 1..400; defaults to 100 for `tail`/`beforeId`, 400 for `afterId`; `budget` is the page's JSON bytes, see [Message copies](#message-copies)) | `{ messages: [{ id, role, content, createdAt, turnDurationMs, attachments }], hasMore, hasOlder, title, lastId }`. `attachments` is always an array; saved images include `{ id, kind: "image", name, mime, bytes, width, height }`, while document and folder context markers carry `{ id, kind, name }`. `tail: true` returns the newest page; `beforeId` returns the page immediately before that message id. Both are oldest-first. Use only one of `afterId`, `tail: true` or `beforeId`; mixing them is refused. `hasMore` means newer rows follow this page, `hasOlder` means older rows precede its first message, and `lastId` is the newest message id (or `null` for an empty thread). |
Expand All @@ -272,7 +272,7 @@ prevents accidents; it is not a boundary against a device that can also answer a
| `task.send` | `{ sessionId, text }` | `{ queued: boolean }`. Queued if the task is running, like Enter. If the run ended between the check and the queue add, the host takes the entry back and sends it. |
| `task.stop` | `{ sessionId }` | `{}`. For a task whose team is running or waiting this is the desktop Stop button (`architect_control` with `stop`): the team is paused, unsettled workers are marked stopped, and the architect's and workers' runs are stopped, also between architect turns when no agent of the task itself runs. Otherwise it stops the task's run (`stop_session`). |
| `task.steer` | `{ sessionId, text }` | `{}` (as `steer_task`) |
| `task.configure` | `{ sessionId, provider?, model?, effort?, mode? }` | `{}`. `provider` and `model` together (a provider not installed there is refused as in `task.start`); `effort: null` clears it; `mode` changes permissions. |
| `task.configure` | `{ sessionId, provider?, model?, effort?, mode?, autoWrites? }` | `{}`. `provider` and `model` together (a provider not installed there is refused as in `task.start`); `effort: null` clears it; `mode` changes permissions; `autoWrites` sets the Auto-approve writes toggle. |
| `task.pull` | `{ sessionId }` | `{ pull: PullDetail \| null }`. `PullDetail` is `{ number, title, body, bodyTruncated, state, url, author, base, head, additions, deletions, changedFiles, createdAt, updatedAt, mergedAt, mergeable, reviewDecision, labels: string[], checks: [{ name, state, url }], files: [{ path, additions, deletions }], commits: [{ oid, headline, author, date }], timeline: [{ kind, author, body, state, createdAt }], truncated }`. `truncated` is true if Connect trimmed the detail's body, timeline, files or other fields (or the GitHub body was already truncated); the whole result fits under 180 KiB. |
| `task.pullDiff` | `{ sessionId, path? }` | Without `path`, `{ files: [{ path, additions, deletions }], truncated }` with the tail dropped to fit 180 KiB. With `path`, `{ patch, truncated }` for that file; patches are cut at 120 KiB. |
| `task.pullMerge` | `{ sessionId, method: "merge" \| "squash" \| "rebase" }` | `{}`. Uses the same merge operation and refusals as the desktop merge button; an unlinked pull request is refused. |
Expand Down
Loading
Loading