From b254047989672eb1920a56ae138306db18b21e97 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sun, 27 Sep 2026 11:24:55 -0400 Subject: [PATCH 1/3] feat(subagents): show each running child's live context window Children report provider usage once per response; publish it as a non-durable chat:subagent-context reading (tokens / model window) instead of a snapshot field, so strict snapshot parsers, revisions, durable writes and the Remote roster are unchanged. The subagents roster shows the percentage per active row and the detail pane shows used / window, reusing the #187 token formatting and 80% warning threshold. Co-Authored-By: Claude Opus 5.5 --- .papercuts/troubleshooting.md | 5 + docs/plans/README.md | 1 + .../subagent-live-context-window-plan.md | 28 ++++ main/services/llm-client.ts | 11 ++ .../subagent-event-projector.test.ts | 91 ++++++++++++ .../subagents/subagent-event-projector.ts | 23 +++ package.json | 6 +- renderer/components/subagent-detail.tsx | 27 ++++ renderer/components/subagent-roster.tsx | 35 ++++- renderer/components/subagents-panel.test.tsx | 40 ++++++ renderer/components/subagents-panel.tsx | 20 +++ .../lib/subagent-context-usage-store.test.ts | 136 ++++++++++++++++++ renderer/lib/subagent-context-usage-store.ts | 119 +++++++++++++++ renderer/preload-channels.ts | 1 + renderer/shared/subagent-context-usage.ts | 120 ++++++++++++++++ scripts/ci-test-registry.json | 1 + 16 files changed, 659 insertions(+), 5 deletions(-) create mode 100644 docs/plans/subagent-live-context-window-plan.md create mode 100644 renderer/lib/subagent-context-usage-store.test.ts create mode 100644 renderer/lib/subagent-context-usage-store.ts create mode 100644 renderer/shared/subagent-context-usage.ts diff --git a/.papercuts/troubleshooting.md b/.papercuts/troubleshooting.md index ceeb89458..217da0bae 100644 --- a/.papercuts/troubleshooting.md +++ b/.papercuts/troubleshooting.md @@ -1404,3 +1404,8 @@ because their native file-mutator test binary had not been built. Run ## 2026-09-26 PR #121 merge of #251 (Remote contract revision 14) - A PR that adds to the Remote contract has to renumber when main bumps `contractRevision`. The conflicts show up in 7 files: both fixtures, the TS/iOS/Android fixture assertions and the iOS fixture CodingKeys. After resolving, `cmp` the Android copy against the shared fixture. Plan docs that name the revision also go stale. + +## 2026-09-27 Live subagent context window +- A fresh workflow worktree has no `node_modules`, so `tsc` reports hundreds of misleading pi-ai type errors. Run `npm ci` before the first type-check. +- The worktree guard refuses a heredoc and a `python3` run in the same Bash command. Write the script to the scratchpad and run it as a separate command. +- Adding a field to `SubagentRunSnapshot` is costly: the exact-key parsers reject it on older builds, and revision/replay checks gate history reads. Live, per-response data belongs on a separate notification channel. diff --git a/docs/plans/README.md b/docs/plans/README.md index 054d3b166..e731fe318 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -19,6 +19,7 @@ This directory is the source of truth for Aiden's implementation plans. The engi | [Scoped MCP resources](mcp-scoped-resources-plan.md) | Implemented | Per-server resource inventory/templates/read; MCP93/Bots448/scheduled151/onboarding56 and both independent reviews pass. PR CI pending. | | [Chronological Chat Motion](chronological-chat-motion-plan.md) | In review | Readable Thinking stretches, tool rows, and prose project in sequence across desktop and regular native chats. [PR #224](https://github.com/sambitcreate/aiden-agent/pull/224) and visual acceptance remain. | | [Composer Context Meter](composer-context-meter-plan.md) | In review | Composer gauge and popover driven by `projectNextContextUsage` (the runtime compaction projection) with live `chat:context-pressure` pushes; PR #187 kept only this part after PR #224 shipped ordered thinking traces. Visual acceptance pending. | +| [Live subagent context window](subagent-live-context-window-plan.md) | In review | Each running child shows its latest provider-reported context use (percent in the roster row, `used / window` in detail, warning tone at 80%) through a non-durable `chat:subagent-context` side channel; snapshots, revisions and Remote are unchanged. Desktop only; Remote/iOS/Android live context is a follow-up. | | [Managed Worktree Lifecycle](managed-worktree-lifecycle-plan.md) | Partial | P0 lifecycle merged in #185; admission hardening and [old-stack disposition](managed-worktree-stack-disposition.md) under review: hook-free managed creation, free-space admission, `.worktreeinclude` provisioning, durable snapshot records + synthetic `refs/aiden/snapshots/*` commits, byte-exact provisioned-ignored blob restore, journal v4 snapshot-aware quarantine deletion with force semantics, and first-class restore. GC/owner-kind/setup-script/UI phases remain open. | | [Durable chat pull requests](chat-pull-requests-plan.md) | Implemented | PR #184 hardens durable multi-link identities, exact create intent, selected push repository, atomic recovery and persistent unlink dismissal; local full suite and two independent final reviews pass; hosted acceptance tracked on PR #184. | | [Trusted AGENTS refresh](trusted-agents-refresh-plan.md) | Implemented | Bounded global/workspace instruction refresh and per-dispatch scope fence; local loader/runtime/Bot/scheduled/onboarding checks and both reviews pass. PR CI pending. | diff --git a/docs/plans/subagent-live-context-window-plan.md b/docs/plans/subagent-live-context-window-plan.md new file mode 100644 index 000000000..892c79cf7 --- /dev/null +++ b/docs/plans/subagent-live-context-window-plan.md @@ -0,0 +1,28 @@ +# Live subagent context window + +Status: In review (desktop). Source: pi-subagents #2448, reusing the #187 context meter's formatting and 80% warning threshold. + +## Goal + +While a child agent runs, show how full its context window is, so a user can see which child is close to its limit before it fails or compacts. + +## Design + +- **Figure.** After each child provider response, `SubagentEventProjector.usage()` builds `{tokens, window}`. `tokens` is the response's reported usage total, the same figure Pi uses as a response's context size. `window` is the child model's `contextWindow`. No reading is made when the model has no window or the provider reported nothing. +- **Transport.** The reading goes on a separate `chat:subagent-context` notification, emitted by `llm-client` through `sendGeneration`. It is **not** a snapshot field. The reasons: + - Snapshot parsers use exact key sets, so older builds would reject a new field. + - History and detail reads gate on revision monotonicity and exact replay, and a reading per response would bump revisions. + - Durable writes are capped per run. + - Remote progress would churn on every child response. +- **Renderer.** `renderer/lib/subagent-context-usage-store.ts` is a bounded, in-memory LRU of up to 32 chats × 64 runs. The panel shows a reading only while the run's view state is active. Terminal runs drop their reading. +- **UI.** + - Each active roster row shows a gauge icon with the percentage, and says it in the row's accessible name. + - The detail pane adds `Context window: 84K / 200K tokens (42%)` under the model line. + - Both use the warning tone at 80% or more. + +## Follow-ups + +- Remote, iOS and Android: the Remote roster reads persisted snapshots, so live readings need a Remote side channel and a contract revision. +- Show readings in the message-list subagent chips. +- The percentage is of the full window, not the usable input budget (window minus reserved output). +- Readings sent before any subagents panel has mounted in the window are not replayed. diff --git a/main/services/llm-client.ts b/main/services/llm-client.ts index b05e858b5..7b1ba467b 100644 --- a/main/services/llm-client.ts +++ b/main/services/llm-client.ts @@ -986,6 +986,17 @@ async function prepareGeneration( chatId: params.chatId, workspaceId: workspace.id, modelId: model.id, + contextWindow: model.contextWindow, + // Presentation-only: bypasses persistence and the Remote progress + // revision, which only track durable snapshots. + onContextUsage: (runId, usage) => { + sendGeneration(streamId, "chat:subagent-context", { + streamId, + chatId: params.chatId, + runId, + usage, + }); + }, prepareSnapshot: (snapshot) => subagentPersistence.prepare(snapshot), onControlSnapshot: async (snapshot) => { subagentPersistence.projectControlSnapshot(snapshot); diff --git a/main/services/subagents/subagent-event-projector.test.ts b/main/services/subagents/subagent-event-projector.test.ts index b13f476e0..b87d1e0a2 100644 --- a/main/services/subagents/subagent-event-projector.test.ts +++ b/main/services/subagents/subagent-event-projector.test.ts @@ -993,6 +993,97 @@ test("turn, usage, and tool telemetry has a hard durable-write bound", async () assert.equal(emitted[emitted.length - 1]?.state, "completed"); }); +test("live context usage follows the latest response without durable writes or revisions", async () => { + const emitted: SubagentRunSnapshotV1[] = []; + const readings: Array<{ runId: string; tokens: number; window: number }> = []; + const projector = new SubagentEventProjector({ + generationId: "generation-context", + chatId: "chat-context", + workspaceId: "workspace-context", + modelId: "model-context", + contextWindow: 200_000, + onContextUsage: (runId, usage) => { + readings.push({ runId, ...usage }); + }, + onSnapshot: (snapshot) => { + emitted.push(snapshot); + }, + }); + const runId = "run-context"; + projector.begin( + { runId, groupId: "generation-context:group", childId: "child-context" }, + { role: "scout", label: "Context", task: "Measure context." }, + ); + projector.running(runId); + await projector.flush(); + const durableBefore = emitted.length; + const revisionBefore = projector.snapshot()[0]!.revision; + + projector.usage(runId, usageMessage(12_000)); + projector.usage(runId, usageMessage(84_000)); + // A child compaction shrinks context; the reading is the latest, not a sum. + projector.usage(runId, usageMessage(30_000)); + projector.usage(runId, usageMessage(0)); + await projector.flush(); + + assert.deepEqual(readings, [ + { runId, tokens: 12_000, window: 200_000 }, + { runId, tokens: 84_000, window: 200_000 }, + { runId, tokens: 30_000, window: 200_000 }, + ]); + // Cumulative spend still accrues on the snapshot, which never carries context. + const live = projector.snapshot()[0]!; + assert.equal(live.tokens, 126_000); + assert.equal("contextUsage" in live, false); + assert.equal(emitted.length, durableBefore); + assert.equal(live.revision, revisionBefore + 4); + + projector.finish(runId, { + role: "scout", + label: "Context", + status: "completed", + summary: "Done.", + }); + projector.usage(runId, usageMessage(99_000)); + assert.equal(readings.length, 3, "a finished run publishes no further readings"); +}); + +test("live context usage is skipped without a known window and never breaks the run", () => { + let calls = 0; + const unknownWindow = new SubagentEventProjector({ + generationId: "generation-no-window", + chatId: "chat-no-window", + workspaceId: "workspace-no-window", + modelId: "model-no-window", + onContextUsage: () => { + calls += 1; + }, + }); + unknownWindow.begin( + { runId: "run-no-window", groupId: "generation-no-window:group", childId: "child" }, + { role: "scout", label: "No window", task: "Measure context." }, + ); + unknownWindow.usage("run-no-window", usageMessage(500)); + assert.equal(calls, 0); + + const throwing = new SubagentEventProjector({ + generationId: "generation-throwing", + chatId: "chat-throwing", + workspaceId: "workspace-throwing", + modelId: "model-throwing", + contextWindow: 1_000, + onContextUsage: () => { + throw new Error("renderer gone"); + }, + }); + throwing.begin( + { runId: "run-throwing", groupId: "generation-throwing:group", childId: "child" }, + { role: "scout", label: "Throwing", task: "Measure context." }, + ); + throwing.usage("run-throwing", usageMessage(500)); + assert.equal(throwing.snapshot()[0]!.tokens, 500); +}); + test("projector withholds text across stream boundaries until terminal sanitization", () => { const cases = [ { diff --git a/main/services/subagents/subagent-event-projector.ts b/main/services/subagents/subagent-event-projector.ts index dba33b860..febfc6a33 100644 --- a/main/services/subagents/subagent-event-projector.ts +++ b/main/services/subagents/subagent-event-projector.ts @@ -19,6 +19,10 @@ import { type SubagentProjectionNoticeKind, } from "../../../renderer/shared/subagent-runs.js"; import { reportedTokens } from "../usage-accounting.js"; +import { + subagentContextUsageFromReport, + type SubagentContextUsageV1, +} from "../../../renderer/shared/subagent-context-usage.js"; import type { SubagentTaskRequest, SubagentTaskResult } from "./contracts.js"; import { sanitizeSubagentSnapshotTextWithFacts } from "../../../renderer/shared/subagent-safe-text.js"; @@ -35,6 +39,13 @@ export interface SubagentRunProjectorInput { chatId: string; workspaceId: string; modelId: string; + /** The child model's context window, for live context-usage readings. */ + contextWindow?: number; + /** + * Live, non-durable context reading after each child response. It never + * enters the run snapshot, so it cannot bump a revision or reach storage. + */ + onContextUsage?: (runId: string, usage: SubagentContextUsageV1) => void; /** Synchronous authority/admission seam that runs before a new run is published. */ prepareSnapshot?: (snapshot: SubagentRunSnapshotV1) => void; onSnapshot?: (snapshot: SubagentRunSnapshotV1) => void | Promise; @@ -327,6 +338,7 @@ export class SubagentEventProjector { usage(runId: string, message: AssistantMessage): void { const current = this.require(runId); const reported = reportedTokens(message.usage)?.total ?? 0; + this.publishContextUsage(current, reported); const tokens = Number.isSafeInteger(reported) ? reported : Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(reported))); @@ -456,6 +468,17 @@ export class SubagentEventProjector { if (this.persistenceError) throw this.persistenceError; } + private publishContextUsage(current: SubagentRunSnapshotV1, reported: number): void { + if (current.finishedAt !== undefined || !this.input.onContextUsage) return; + const usage = subagentContextUsageFromReport(reported, this.input.contextWindow); + if (!usage) return; + try { + this.input.onContextUsage(current.runId, usage); + } catch { + // A presentation-only observer must never affect the child run. + } + } + private require(runId: string): SubagentRunSnapshotV1 { const record = this.records.get(runId); if (!record) throw new Error("Unknown subagent run."); diff --git a/package.json b/package.json index fe9c8a2ff..f63fc8f1d 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "test:subagents:phase6b": "tsx --test main/services/subagents/agent-compatibility.test.ts main/services/subagents/subagent-foreground-persistence-v2.test.ts main/services/subagents/subagent-nesting-core.test.ts main/services/subagents/subagent-supervisor.test.ts main/services/subagents/subagent-tool-schema.test.ts", "test:subagents:phase7a": "tsx --test main/services/subagents/background-lifecycle-v2.test.ts renderer/shared/subagent-runs-v2.test.ts", "test:subagents:workspace-write": "tsx --test main/services/subagents/subagent-workspace-write.test.ts", - "test:subagents": "tsx --test main/services/subagents/agent-compatibility.test.ts main/services/subagents/approval-v2.test.ts main/services/subagents/authority-v2.test.ts main/services/subagents/network-budget-v2.test.ts main/services/subagents/outbound-approval-v2.test.ts main/services/subagents/request-capabilities-v2.test.ts main/services/subagents/subagent-child-runtime.test.ts main/services/subagents/capability-profile.test.ts main/services/subagents/forked-context.test.ts main/services/subagents/forked-context-supervisor.test.ts main/services/subagents/management-v2.test.ts main/services/subagents/subagent-control-ipc-core.test.ts main/services/subagents/subagent-control-main.test.ts main/services/subagents/subagent-control-v2.test.ts main/services/subagents/subagent-event-projector.test.ts main/services/subagents/subagent-semantic-preservation.test.ts main/services/subagents/subagent-file-mutation-core.test.ts main/services/subagents/subagent-file-mutator-io.test.ts main/services/subagents/subagent-foreground-persistence-v2.test.ts main/services/subagents/subagent-health-metrics-core.test.ts main/services/subagents/subagent-history-read-core.test.ts main/services/subagents/subagent-identifier-privacy.test.ts main/services/subagents/subagent-nesting-core.test.ts main/services/subagents/subagent-phase3-contract.test.ts main/services/subagents/subagent-mcp-client-core.test.ts main/services/subagents/subagent-mcp-credential-core.test.ts main/services/subagents/subagent-mcp-inventory-core.test.ts main/services/subagents/subagent-mcp-bounded-fetch.test.ts main/services/subagents/subagent-mcp-read.test.ts main/services/subagents/subagent-run-store-core.test.ts main/services/subagents/subagent-run-store-dispatcher.test.ts main/services/subagents/subagent-run-store-v2-core.test.ts main/services/subagents/subagent-run-store-v2-migration.test.ts main/services/subagents/subagent-run-store-production.test.ts main/services/subagents/subagent-supervisor.test.ts main/services/subagents/subagent-tool-schema.test.ts main/services/subagents/subagent-web-proxy.test.ts main/services/chat-deletion-gate.test.ts main/services/chat-generation-start.test.ts main/services/chat-workspace-authority.test.ts main/services/chat-workspace-mutation-gate.test.ts main/services/managed-worktree-admission.test.ts main/services/managed-worktree-creation-core.test.ts main/services/managed-worktree-deletion-recovery.test.ts main/services/managed-worktree-lifecycle.test.ts main/services/managed-worktree-removal-core.test.ts main/services/managed-worktree-remover.test.ts main/services/workspace-mutation-gate.test.ts main/services/workspace-operation-registry.test.ts main/services/workspace-record-removal.test.ts main/services/workspace-schedule-restoration.test.ts main/services/terminal.test.ts renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/subagent-tree.test.ts renderer/shared/subagent-management-v2.test.ts renderer/shared/subagent-runs-v2.test.ts && node --test scripts/subagent-run-store.test.mjs scripts/worktree-file-io.test.mjs && npm run test:subagent-file-mutator:native", + "test:subagents": "tsx --test main/services/subagents/agent-compatibility.test.ts main/services/subagents/approval-v2.test.ts main/services/subagents/authority-v2.test.ts main/services/subagents/network-budget-v2.test.ts main/services/subagents/outbound-approval-v2.test.ts main/services/subagents/request-capabilities-v2.test.ts main/services/subagents/subagent-child-runtime.test.ts main/services/subagents/capability-profile.test.ts main/services/subagents/forked-context.test.ts main/services/subagents/forked-context-supervisor.test.ts main/services/subagents/management-v2.test.ts main/services/subagents/subagent-control-ipc-core.test.ts main/services/subagents/subagent-control-main.test.ts main/services/subagents/subagent-control-v2.test.ts main/services/subagents/subagent-event-projector.test.ts main/services/subagents/subagent-semantic-preservation.test.ts main/services/subagents/subagent-file-mutation-core.test.ts main/services/subagents/subagent-file-mutator-io.test.ts main/services/subagents/subagent-foreground-persistence-v2.test.ts main/services/subagents/subagent-health-metrics-core.test.ts main/services/subagents/subagent-history-read-core.test.ts main/services/subagents/subagent-identifier-privacy.test.ts main/services/subagents/subagent-nesting-core.test.ts main/services/subagents/subagent-phase3-contract.test.ts main/services/subagents/subagent-mcp-client-core.test.ts main/services/subagents/subagent-mcp-credential-core.test.ts main/services/subagents/subagent-mcp-inventory-core.test.ts main/services/subagents/subagent-mcp-bounded-fetch.test.ts main/services/subagents/subagent-mcp-read.test.ts main/services/subagents/subagent-run-store-core.test.ts main/services/subagents/subagent-run-store-dispatcher.test.ts main/services/subagents/subagent-run-store-v2-core.test.ts main/services/subagents/subagent-run-store-v2-migration.test.ts main/services/subagents/subagent-run-store-production.test.ts main/services/subagents/subagent-supervisor.test.ts main/services/subagents/subagent-tool-schema.test.ts main/services/subagents/subagent-web-proxy.test.ts main/services/chat-deletion-gate.test.ts main/services/chat-generation-start.test.ts main/services/chat-workspace-authority.test.ts main/services/chat-workspace-mutation-gate.test.ts main/services/managed-worktree-admission.test.ts main/services/managed-worktree-creation-core.test.ts main/services/managed-worktree-deletion-recovery.test.ts main/services/managed-worktree-lifecycle.test.ts main/services/managed-worktree-removal-core.test.ts main/services/managed-worktree-remover.test.ts main/services/workspace-mutation-gate.test.ts main/services/workspace-operation-registry.test.ts main/services/workspace-record-removal.test.ts main/services/workspace-schedule-restoration.test.ts main/services/terminal.test.ts renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/subagent-tree.test.ts renderer/lib/subagent-context-usage-store.test.ts renderer/shared/subagent-management-v2.test.ts renderer/shared/subagent-runs-v2.test.ts && node --test scripts/subagent-run-store.test.mjs scripts/worktree-file-io.test.mjs && npm run test:subagent-file-mutator:native", "test:subagents:soak:contracts": "tsx --test main/services/subagents/subagent-packaged-soak-core.test.ts main/services/subagents/subagent-packaged-soak-main.test.ts && node --test scripts/subagent-packaged-soak.test.mjs scripts/subagent-run-store-packaged-acceptance.test.mjs", "test:subagents:packaged": "node scripts/subagent-packaged-soak.mjs", "test:subagents:migration:packaged": "node scripts/subagent-run-store-packaged-acceptance.mjs", @@ -129,8 +129,8 @@ "test:tts": "tsx --test main/services/tts/authority-contract.test.ts renderer/components/settings/tts-settings.test.tsx main/services/tts/audio-store.test.ts renderer/lib/tts-client.test.ts renderer/lib/tts-player.test.ts main/services/tts/speech-text.test.ts main/services/tts/source.test.ts main/services/tts/gemini-wire.test.ts main/services/tts/service.test.ts main/services/tts/gemini-provider.loopback.test.ts renderer/components/message-bubble.test.tsx renderer/shared/tts.test.ts", "test:diagnostics": "tsx --test main/services/diagnostics-contract.test.ts main/services/diagnostic-health.test.ts main/services/diagnostic-journal.test.ts main/services/diagnostic-support.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/renderer-crash-recovery.test.ts main/services/renderer-diagnostic-rate.test.ts main/services/subagents/subagent-runtime-diagnostics.test.ts renderer/components/settings/diagnostics-settings.test.tsx && node --test scripts/diagnostic-policy.test.mjs", "diagnostics:failure-receipt": "node scripts/write-diagnostic-failure-receipt.mjs", - "test": "npm run test:agents-instructions && npm run test:custom-model-options && tsx --test main/services/tool-output-store.test.ts main/services/mcp-oauth-client-metadata.test.ts main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/empty-chat-migration.test.ts main/services/chat-first-message-commit.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/secret-map-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/dictation-cleanup-core.test.ts main/services/dictation-hotkey.test.ts main/services/dictation-key-state.test.ts main/services/dictation-keycode.test.ts main/services/parakeet-protocol.test.ts main/services/parakeet-process-core.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/gemini-context-cache.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/github-pull-request.test.ts main/services/model-runtime-core.test.ts main/services/opencode-session-attribution.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts renderer/shared/plugin-catalog.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/provider-artwork.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/activity-feed.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/components/context-meter.test.tsx renderer/lib/chat-message-queue.test.ts renderer/lib/chat-draft.test.ts renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/scrollbar-gutter-contract.test.ts renderer/lib/scroll-follow.test.ts renderer/lib/text-entry-focus-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/composer-attachment-operation.test.ts renderer/components/composer-attachments-mounted.test.tsx renderer/lib/computer-use-notice.test.ts renderer/lib/context-pressure-feed.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/media-recorder-stop.test.ts renderer/lib/dictation-vad.test.ts renderer/lib/dictation-sounds.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/components/settings/mcp-settings.test.tsx renderer/components/settings/mcp-preset-icons.test.ts renderer/lib/model-display.test.ts renderer/lib/model-picker-data.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/context-pressure.test.ts renderer/components/interface-polish.test.tsx renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/hide-dmg-support-files.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs main/services/chat-pull-request-store.test.ts main/services/chat-pull-request-service.test.ts main/services/pull-request-create-reconciliation.test.ts main/services/pull-request-current-resolver.test.ts renderer/shared/chat-pull-requests.test.ts main/services/form-fill/artifacts-core.test.ts main/services/form-fill/extract-core.test.ts main/services/form-fill/helper-client.test.ts main/services/form-fill/planner-core.test.ts main/services/form-fill/batch-core.test.ts main/services/form-fill/service.test.ts main/services/form-fill/eval-fixtures.test.ts main/services/computer-use/controller-form-fill.test.ts renderer/components/form-fill-approval.test.tsx && npm run test:telegram && npm run test:worktree-remover:native && npm run test:computer-use:native && npm run test:settings-design && npm run test:ci-policy && npm run test:devices && npm run test:durable-jobs && npm run test:cli", - "test:coverage": "tsx --test --experimental-test-coverage main/services/mcp-oauth-client-metadata.test.ts main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/empty-chat-migration.test.ts main/services/chat-first-message-commit.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/dictation-cleanup-core.test.ts main/services/dictation-hotkey.test.ts main/services/dictation-key-state.test.ts main/services/dictation-keycode.test.ts main/services/parakeet-protocol.test.ts main/services/parakeet-process-core.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/github-pull-request.test.ts main/services/model-runtime-core.test.ts main/services/opencode-session-attribution.test.ts main/services/pi-compaction-core.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts renderer/shared/plugin-catalog.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/provider-artwork.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-auth-view-state.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/composer-attachment-operation.test.ts renderer/components/composer-attachments-mounted.test.tsx renderer/lib/computer-use-notice.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/media-recorder-stop.test.ts renderer/lib/dictation-vad.test.ts renderer/lib/dictation-sounds.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/components/settings/mcp-settings.test.tsx renderer/components/settings/mcp-preset-icons.test.ts renderer/lib/model-display.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs main/services/tool-output-store.test.ts main/services/chat-pull-request-store.test.ts main/services/chat-pull-request-service.test.ts main/services/pull-request-create-reconciliation.test.ts main/services/pull-request-current-resolver.test.ts renderer/shared/chat-pull-requests.test.ts main/services/form-fill/artifacts-core.test.ts main/services/form-fill/extract-core.test.ts main/services/form-fill/helper-client.test.ts main/services/form-fill/planner-core.test.ts main/services/form-fill/batch-core.test.ts main/services/form-fill/service.test.ts main/services/form-fill/eval-fixtures.test.ts main/services/computer-use/controller-form-fill.test.ts renderer/components/form-fill-approval.test.tsx", + "test": "npm run test:agents-instructions && npm run test:custom-model-options && tsx --test main/services/tool-output-store.test.ts main/services/mcp-oauth-client-metadata.test.ts main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/empty-chat-migration.test.ts main/services/chat-first-message-commit.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/secret-map-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/dictation-cleanup-core.test.ts main/services/dictation-hotkey.test.ts main/services/dictation-key-state.test.ts main/services/dictation-keycode.test.ts main/services/parakeet-protocol.test.ts main/services/parakeet-process-core.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/gemini-context-cache.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/github-pull-request.test.ts main/services/model-runtime-core.test.ts main/services/opencode-session-attribution.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts renderer/shared/plugin-catalog.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/provider-artwork.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/activity-feed.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/lib/subagent-context-usage-store.test.ts renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/components/context-meter.test.tsx renderer/lib/chat-message-queue.test.ts renderer/lib/chat-draft.test.ts renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/scrollbar-gutter-contract.test.ts renderer/lib/scroll-follow.test.ts renderer/lib/text-entry-focus-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/composer-attachment-operation.test.ts renderer/components/composer-attachments-mounted.test.tsx renderer/lib/computer-use-notice.test.ts renderer/lib/context-pressure-feed.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/media-recorder-stop.test.ts renderer/lib/dictation-vad.test.ts renderer/lib/dictation-sounds.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/components/settings/mcp-settings.test.tsx renderer/components/settings/mcp-preset-icons.test.ts renderer/lib/model-display.test.ts renderer/lib/model-picker-data.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/context-pressure.test.ts renderer/components/interface-polish.test.tsx renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/hide-dmg-support-files.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs main/services/chat-pull-request-store.test.ts main/services/chat-pull-request-service.test.ts main/services/pull-request-create-reconciliation.test.ts main/services/pull-request-current-resolver.test.ts renderer/shared/chat-pull-requests.test.ts main/services/form-fill/artifacts-core.test.ts main/services/form-fill/extract-core.test.ts main/services/form-fill/helper-client.test.ts main/services/form-fill/planner-core.test.ts main/services/form-fill/batch-core.test.ts main/services/form-fill/service.test.ts main/services/form-fill/eval-fixtures.test.ts main/services/computer-use/controller-form-fill.test.ts renderer/components/form-fill-approval.test.tsx && npm run test:telegram && npm run test:worktree-remover:native && npm run test:computer-use:native && npm run test:settings-design && npm run test:ci-policy && npm run test:devices && npm run test:durable-jobs && npm run test:cli", + "test:coverage": "tsx --test --experimental-test-coverage main/services/mcp-oauth-client-metadata.test.ts main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/empty-chat-migration.test.ts main/services/chat-first-message-commit.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/dictation-cleanup-core.test.ts main/services/dictation-hotkey.test.ts main/services/dictation-key-state.test.ts main/services/dictation-keycode.test.ts main/services/parakeet-protocol.test.ts main/services/parakeet-process-core.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/github-pull-request.test.ts main/services/model-runtime-core.test.ts main/services/opencode-session-attribution.test.ts main/services/pi-compaction-core.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts renderer/shared/plugin-catalog.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/provider-artwork.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/lib/subagent-context-usage-store.test.ts renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-auth-view-state.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/composer-attachment-operation.test.ts renderer/components/composer-attachments-mounted.test.tsx renderer/lib/computer-use-notice.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/media-recorder-stop.test.ts renderer/lib/dictation-vad.test.ts renderer/lib/dictation-sounds.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/components/settings/mcp-settings.test.tsx renderer/components/settings/mcp-preset-icons.test.ts renderer/lib/model-display.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs main/services/tool-output-store.test.ts main/services/chat-pull-request-store.test.ts main/services/chat-pull-request-service.test.ts main/services/pull-request-create-reconciliation.test.ts main/services/pull-request-current-resolver.test.ts renderer/shared/chat-pull-requests.test.ts main/services/form-fill/artifacts-core.test.ts main/services/form-fill/extract-core.test.ts main/services/form-fill/helper-client.test.ts main/services/form-fill/planner-core.test.ts main/services/form-fill/batch-core.test.ts main/services/form-fill/service.test.ts main/services/form-fill/eval-fixtures.test.ts main/services/computer-use/controller-form-fill.test.ts renderer/components/form-fill-approval.test.tsx", "test:computer-use": "tsx --test main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/quit-barrier.test.ts main/services/tool-approval.test.ts scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs main/services/form-fill/artifacts-core.test.ts main/services/form-fill/extract-core.test.ts main/services/form-fill/helper-client.test.ts main/services/form-fill/planner-core.test.ts main/services/form-fill/batch-core.test.ts main/services/form-fill/service.test.ts main/services/form-fill/eval-fixtures.test.ts main/services/computer-use/controller-form-fill.test.ts renderer/components/form-fill-approval.test.tsx && npm run test:computer-use:native", "test:computer-use:packaged": "node scripts/computer-use-packaged-acceptance.mjs", "test:form-fill": "tsx --test main/services/form-fill/artifacts-core.test.ts main/services/form-fill/extract-core.test.ts main/services/form-fill/helper-client.test.ts main/services/form-fill/planner-core.test.ts main/services/form-fill/batch-core.test.ts main/services/form-fill/service.test.ts main/services/form-fill/eval-fixtures.test.ts main/services/computer-use/controller-form-fill.test.ts renderer/components/form-fill-approval.test.tsx", diff --git a/renderer/components/subagent-detail.tsx b/renderer/components/subagent-detail.tsx index 1a23e5769..24ee313cd 100644 --- a/renderer/components/subagent-detail.tsx +++ b/renderer/components/subagent-detail.tsx @@ -11,6 +11,11 @@ import type { SubagentMilestoneKind, SubagentRunSnapshot, } from "../shared/subagent-runs"; +import { + subagentContextIsApproachingLimit, + subagentContextUsageLabels, + type SubagentContextUsageV1, +} from "../shared/subagent-context-usage"; import { Markdown } from "./markdown"; import { SubagentOrb, subagentStateLabel } from "./subagent-chips"; import { Button, Callout, ErrorBoundary, Text } from "./ui"; @@ -75,6 +80,11 @@ function UnrenderableSubagentUpdate({ content }: { content: string }) { ); } +function subagentContextSummary(usage: SubagentContextUsageV1): string { + const labels = subagentContextUsageLabels(usage); + return `Context window: ${labels.amount} (${labels.percent})`; +} + export interface SubagentDetailProps { run: SubagentRunSnapshot; effectActivity?: readonly SubagentEffectActivityV1[]; @@ -86,6 +96,8 @@ export interface SubagentDetailProps { refreshError?: string | null; onRetryRefresh?: () => void; now?: number; + /** Live context reading; supplied only while the run is active. */ + contextUsage?: SubagentContextUsageV1; className?: string; } @@ -102,6 +114,7 @@ export const SubagentDetail = React.forwardRef Model: {run.modelId} + {contextUsage ? ( + + {subagentContextSummary(contextUsage)} + + ) : null} {run.version === 2 ? ( >; @@ -63,12 +69,15 @@ function RosterNode({ onSelect: (runId: string, trigger: HTMLButtonElement) => void; onKeyDown: (event: React.KeyboardEvent, runId: string) => void; presentationByRunId?: ReadonlyMap; + contextUsageByRunId?: SubagentContextUsageByRunId; }) { const run = node.run; const expanded = subagentTreeIsExpanded(node, expansion); const selected = run.runId === selectedRunId; const presentation = presentationByRunId?.get(run.runId); const displayState = presentation?.label ?? subagentStateLabel(run.state); + const contextUsage = contextUsageByRunId?.get(run.runId); + const contextLabels = contextUsage ? subagentContextUsageLabels(contextUsage) : undefined; const hiddenCount = expanded ? 0 : node.descendantCount; const activeDescendantLabel = node.activeDescendantCount ? `${node.activeDescendantCount} active descendant${node.activeDescendantCount === 1 ? "" : "s"}` @@ -109,7 +118,7 @@ function RosterNode({ tabIndex={focusedRunId === run.runId ? 0 : -1} aria-selected={selected} data-subagent-presentation={presentation?.state} - aria-label={`${run.label}, ${run.role}, ${displayState}${activeDescendantLabel ? `, ${activeDescendantLabel}` : ""}${hiddenCount ? `, ${hiddenCount} hidden descendant${hiddenCount === 1 ? "" : "s"}` : ""}`} + aria-label={`${run.label}, ${run.role}, ${displayState}${activeDescendantLabel ? `, ${activeDescendantLabel}` : ""}${contextLabels ? `, ${contextLabels.spoken}` : ""}${hiddenCount ? `, ${hiddenCount} hidden descendant${hiddenCount === 1 ? "" : "s"}` : ""}`} onFocus={() => onFocusRun(run.runId)} onKeyDown={(event) => onKeyDown(event, run.runId)} onClick={(event) => onSelect(run.runId, event.currentTarget)} @@ -128,6 +137,22 @@ function RosterNode({ (run.role === "unknown" ? "Saved subagent result" : run.role)} + {contextUsage && contextLabels ? ( + + + ) : null} {hiddenCount > 0 ? ( +{hiddenCount} @@ -162,6 +187,7 @@ function RosterNode({ onSelect={onSelect} onKeyDown={onKeyDown} presentationByRunId={presentationByRunId} + contextUsageByRunId={contextUsageByRunId} /> ))} @@ -185,6 +211,7 @@ function RosterGroup({ onSelect: (runId: string, trigger: HTMLButtonElement) => void; onKeyDown: (event: React.KeyboardEvent, runId: string) => void; presentationByRunId?: ReadonlyMap; + contextUsageByRunId?: SubagentContextUsageByRunId; }) { if (nodes.length === 0) return null; return ( @@ -204,6 +231,8 @@ export interface SubagentRosterProps { selectedRunId: string | null; onSelect: (runId: string, trigger: HTMLButtonElement) => void; presentationByRunId?: ReadonlyMap; + /** Live context readings; supply only for runs that are still active. */ + contextUsageByRunId?: SubagentContextUsageByRunId; className?: string; } @@ -212,6 +241,7 @@ export function SubagentRoster({ selectedRunId, onSelect, presentationByRunId, + contextUsageByRunId, className, }: SubagentRosterProps) { const treeRef = React.useRef(null); @@ -270,6 +300,7 @@ export function SubagentRoster({ onSelect, onKeyDown: handleKeyDown, presentationByRunId, + contextUsageByRunId, }; return (