@dreb/dashboard 2.45.0 → 2.45.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -3
- package/dist/server/runtime-pool.d.ts +4 -0
- package/dist/server/runtime-pool.d.ts.map +1 -1
- package/dist/server/runtime-pool.js +48 -1
- package/dist/server/runtime-pool.js.map +1 -1
- package/dist/shared/protocol.d.ts +4 -0
- package/dist/shared/protocol.d.ts.map +1 -1
- package/dist/shared/protocol.js.map +1 -1
- package/dist/static/assets/{index-BrWSaUHE.js → index-BJcyaSXg.js} +11 -11
- package/dist/static/assets/index-DdcUWCjw.css +1 -0
- package/dist/static/index.html +2 -2
- package/dist/static/sw.js +1 -1
- package/package.json +1 -1
- package/dist/static/assets/index-C0tLqeae.css +0 -1
package/README.md
CHANGED
|
@@ -33,14 +33,16 @@ Open `http://127.0.0.1:5343`.
|
|
|
33
33
|
- **Fleet** (home) — live-first: every live session in one grid at the top
|
|
34
34
|
(stable order by project path, then session start time; each card shows its
|
|
35
35
|
project path, status chip, current activity, live subagents, task progress,
|
|
36
|
-
ctx%, model, cost, last-assistant preview). Below, past sessions grouped by project — three
|
|
36
|
+
ctx%, model, cost, last-assistant preview, and terminal provider-error reason). Below, past sessions grouped by project — three
|
|
37
37
|
compact rows each with an "all N on disk" expander — with resume/delete.
|
|
38
38
|
At <=700px, cards stack; long session names, status chips, project paths,
|
|
39
39
|
activity/subagent text, and past-session labels wrap within cards or rows
|
|
40
40
|
rather than spilling off-screen. `+ new session` anywhere.
|
|
41
41
|
- **Session view** — full chat parity: markdown streaming transcript, tool
|
|
42
|
-
cards
|
|
43
|
-
|
|
42
|
+
cards with sanitized inline PNG/JPEG/GIF/WebP result images, thinking blocks,
|
|
43
|
+
inline provider/API failures with partial output preserved, compaction summaries,
|
|
44
|
+
per-message copy, tasks panel, suggest-next chip,
|
|
45
|
+
slash-command autocomplete, image attach/paste,
|
|
44
46
|
queued-message restore, persistent session-header live indicator, footer-parity info bar (branch, tokens, cost, ctx%,
|
|
45
47
|
median tok/s), stats/loaded-context/fork modals, steer/follow-up composer
|
|
46
48
|
modes, ■ abort, model/thinking switchers, extension-UI modals, export HTML,
|
|
@@ -72,6 +74,32 @@ Open `http://127.0.0.1:5343`.
|
|
|
72
74
|
and paired-devices management.
|
|
73
75
|
- **Pairing** — remote first-login rotating-code flow.
|
|
74
76
|
|
|
77
|
+
### Tool-result images
|
|
78
|
+
|
|
79
|
+
Image blocks returned by any tool render inline for the human viewing the
|
|
80
|
+
transcript, even when the active model does not support vision. The dashboard
|
|
81
|
+
accepts only PNG, JPEG, GIF, and WebP with valid base64 payloads; SVG and
|
|
82
|
+
malformed blocks are dropped. Accepted images also survive refresh/resync and
|
|
83
|
+
are embedded in exported HTML transcripts. Oversized live events use the
|
|
84
|
+
existing SSE resync barrier and HTTP snapshot path; CSS bounds display
|
|
85
|
+
dimensions but does not compress the underlying bytes.
|
|
86
|
+
|
|
87
|
+
### Provider failures and retries
|
|
88
|
+
|
|
89
|
+
A failed assistant attempt renders an explicit `Error: <message>` line inline,
|
|
90
|
+
after any nonblank partial thinking or text. Empty finalized thinking placeholders
|
|
91
|
+
are omitted. The same per-attempt failure metadata is restored from session
|
|
92
|
+
history after hydration or resync; missing provider text is shown as `Unknown
|
|
93
|
+
error`, and aborted messages remain distinct from failures.
|
|
94
|
+
|
|
95
|
+
The latest idle assistant failure also marks the session and fleet card as a
|
|
96
|
+
terminal error with its reason. If automatic retry starts, terminal session and
|
|
97
|
+
fleet state clear in favor of the retry warning while the failed attempt remains
|
|
98
|
+
visible in transcript history. Hydration and resync carry retry activity, so a
|
|
99
|
+
refresh during backoff restores that non-terminal warning and persisted attempt.
|
|
100
|
+
A later success stays clear; disabled, non-retryable, and exhausted failures
|
|
101
|
+
remain terminal without duplicate status entries.
|
|
102
|
+
|
|
75
103
|
## Fleet transport and freshness
|
|
76
104
|
|
|
77
105
|
A normal dashboard load makes one authoritative `GET /api/fleet`; exceptional
|
|
@@ -35,6 +35,8 @@ export interface RuntimeHandle {
|
|
|
35
35
|
attention: Map<string, string>;
|
|
36
36
|
/** Last runtime-level error, persisted server-side so fleet refreshes stay honest. */
|
|
37
37
|
error?: string;
|
|
38
|
+
/** Provider error currently responsible for error/attention; retry may clear only this source. */
|
|
39
|
+
providerError?: string;
|
|
38
40
|
/** Last authoritative state, patched only with event-derivable fields between RPC reads. */
|
|
39
41
|
lastState?: SessionStateDto;
|
|
40
42
|
/** Resume-path fallback; events must never invent or overwrite session identity. */
|
|
@@ -143,6 +145,8 @@ export declare class RuntimePool {
|
|
|
143
145
|
*/
|
|
144
146
|
private updateStateFromEvent;
|
|
145
147
|
private recordRuntimeError;
|
|
148
|
+
private recordProviderError;
|
|
149
|
+
private clearProviderError;
|
|
146
150
|
private pruneCompletedBackgroundAgents;
|
|
147
151
|
private fallbackState;
|
|
148
152
|
private describeFleetRuntime;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-pool.d.ts","sourceRoot":"","sources":["../../src/server/runtime-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RPC runtime pool — one `dreb --mode rpc` child process per live session.\n *\n * dreb's RPC mode is strictly one-session-per-process (switch_session repoints\n * the same process; it never multiplexes), so the pool spawns N children keyed\n * by an opaque runtime key. The telegram bridge is the in-repo precedent.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { RpcClient, type RpcExitInfo } from \"@dreb/coding-agent/rpc\";\nimport {\n\ttype BackgroundAgentDto,\n\ttype FleetRuntimeSnapshotDto,\n\ttype FleetSnapshotEventDto,\n\tMAX_COMPLETED_BACKGROUND_AGENTS,\n\ttype RuntimeInfoDto,\n\ttype RuntimeStatsSummaryDto,\n\ttype SessionStateDto,\n} from \"../shared/protocol.js\";\n\n/** Resolve the absolute path to the dreb CLI (RpcClient defaults to a cwd-relative path). */\nexport function resolveDrebCliPath(): string {\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\treturn join(dirname(fileURLToPath(resolved)), \"cli.js\");\n}\n\nfunction formatRpcExit(info: RpcExitInfo): string {\n\tif (info.error) return `RPC process failed: ${info.error.message}`;\n\treturn `RPC process exited (code ${info.code}, signal ${info.signal})`;\n}\n\nexport type RuntimeEventListener = (key: string, event: Record<string, unknown>) => void;\n\n/** Listener for coalesced, synchronous fleet runtime snapshots. */\nexport type FleetSnapshotListener = (event: FleetSnapshotEventDto) => void;\n\nexport { MAX_COMPLETED_BACKGROUND_AGENTS };\n\ninterface RpcDashboardSnapshot {\n\tsnapshotId: string;\n\tstate: SessionStateDto;\n\tmessages: unknown[];\n\tbackgroundAgents: BackgroundAgentDto[];\n}\n\ntype DashboardSnapshotClient = RpcClient & { getDashboardSnapshot(): Promise<RpcDashboardSnapshot> };\n\nexport interface DashboardRuntimeSnapshot {\n\tkey: string;\n\tbarrierSeq: number;\n\tsnapshot: RpcDashboardSnapshot;\n}\n\nexport interface RuntimeHandle {\n\tkey: string;\n\tcwd: string;\n\tclient: RpcClient;\n\t/** Session start time (ms epoch) — stable tiebreak for deterministic fleet ordering. */\n\tcreatedAt: number;\n\tlastActivity: number;\n\t/** Needs-attention sources, keyed so they can be cleared independently. */\n\tattention: Map<string, string>;\n\t/** Last runtime-level error, persisted server-side so fleet refreshes stay honest. */\n\terror?: string;\n\t/** Last authoritative state, patched only with event-derivable fields between RPC reads. */\n\tlastState?: SessionStateDto;\n\t/** Resume-path fallback; events must never invent or overwrite session identity. */\n\tsessionFileFallback?: string;\n\t/** Background agents seen via events (agentId → latest info). */\n\tbackgroundAgents: Map<string, BackgroundAgentDto>;\n}\n\nexport const DEFAULT_DASHBOARD_BARRIER_TTL_MS = 5 * 60_000;\nexport const DEFAULT_DASHBOARD_BARRIER_LIMIT = 1000;\nexport const DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS = 200;\n\n/** Events that mutate a field carried by the lightweight fleet snapshot. */\nconst FLEET_SNAPSHOT_EVENT_TYPES = new Set([\n\t\"agent_start\",\n\t\"agent_end\",\n\t\"auto_compaction_start\",\n\t\"auto_compaction_end\",\n\t\"auto_retry_end\",\n\t\"background_agent_start\",\n\t\"background_agent_end\",\n\t\"extension_ui_request\",\n\t\"extension_ui_response_handled\",\n\t\"message_start\",\n\t\"parent_paused_for_background_agents\",\n\t\"runtime_removed\",\n\t\"session_name_changed\",\n\t\"suggest_next\",\n\t\"tasks_update\",\n]);\n\nexport interface RuntimePoolOptions {\n\tcliPath?: string;\n\t/** Extra args for every runtime (e.g. --provider). */\n\tbaseArgs?: string[];\n\t/** RpcClient factory override for tests. */\n\tclientFactory?: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tlogger?: (line: string) => void;\n\t/** Bounds unclaimed RPC snapshot ordering records. */\n\tdashboardBarrierTtlMs?: number;\n\tdashboardBarrierLimit?: number;\n\t/** Injectable clock for deterministic barrier-expiry tests. */\n\tnow?: () => number;\n\t/** Coalescing delay for event-derived fleet snapshot emissions. */\n\tfleetSnapshotDebounceMs?: number;\n}\n\ninterface DashboardBarrier {\n\tseq: number;\n\trecordedAt: number;\n}\n\nexport class RuntimePool {\n\tprivate readonly runtimes = new Map<string, RuntimeHandle>();\n\tprivate readonly listeners: RuntimeEventListener[] = [];\n\tprivate readonly fleetSnapshotListeners: FleetSnapshotListener[] = [];\n\tprivate readonly cliPath: string;\n\tprivate readonly baseArgs: string[];\n\tprivate readonly clientFactory: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tprivate readonly logger: (line: string) => void;\n\t/**\n\t * A single lazily-spawned utility runtime used to service settings/model/\n\t * agent-type endpoints when no user session is live. Kept out of `runtimes`\n\t * (and therefore out of the fleet) so it never shows as a session card.\n\t */\n\tprivate readonly utilities = new Map<string, RuntimeHandle>();\n\tprivate readonly utilityPromises = new Map<string, Promise<RuntimeHandle>>();\n\tprivate readonly starting = new Set<RuntimeHandle>();\n\tprivate readonly startupPromises = new Set<Promise<unknown>>();\n\tprivate readonly exitedHandles = new WeakSet<RuntimeHandle>();\n\t/** Snapshot ordering records observed synchronously from RpcClient stdout. */\n\tprivate readonly dashboardBarriers = new Map<string, DashboardBarrier>();\n\tprivate readonly dashboardBarrierTtlMs: number;\n\tprivate readonly dashboardBarrierLimit: number;\n\tprivate readonly now: () => number;\n\tprivate readonly fleetSnapshotDebounceMs: number;\n\tprivate dashboardBarrierPruneTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate fleetSnapshotTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate closing = false;\n\n\tconstructor(options: RuntimePoolOptions = {}) {\n\t\tthis.cliPath = options.cliPath ?? resolveDrebCliPath();\n\t\tthis.baseArgs = options.baseArgs ?? [];\n\t\tthis.clientFactory =\n\t\t\toptions.clientFactory ?? ((o) => new RpcClient({ cliPath: o.cliPath, cwd: o.cwd, args: o.args }));\n\t\tthis.logger = options.logger ?? ((line) => console.warn(`[dashboard] ${line}`));\n\t\tthis.dashboardBarrierTtlMs = options.dashboardBarrierTtlMs ?? DEFAULT_DASHBOARD_BARRIER_TTL_MS;\n\t\tthis.dashboardBarrierLimit = options.dashboardBarrierLimit ?? DEFAULT_DASHBOARD_BARRIER_LIMIT;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.fleetSnapshotDebounceMs = options.fleetSnapshotDebounceMs ?? DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS;\n\t}\n\n\t/** Subscribe to events from every runtime, tagged with the runtime key. */\n\tonEvent(listener: RuntimeEventListener): () => void {\n\t\tthis.listeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.listeners.indexOf(listener);\n\t\t\tif (i !== -1) this.listeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/** Subscribe to debounced, in-memory fleet snapshots. */\n\tonFleetSnapshot(listener: FleetSnapshotListener): () => void {\n\t\tthis.fleetSnapshotListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.fleetSnapshotListeners.indexOf(listener);\n\t\t\tif (i !== -1) this.fleetSnapshotListeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Build the fleet's live-runtime view without RPC or disk access. Map\n\t * insertion order is retained intentionally; the UI owns presentation order.\n\t */\n\tfleetSnapshot(): FleetRuntimeSnapshotDto[] {\n\t\treturn [...this.runtimes.values()].map((handle) => this.describeFleetRuntime(handle));\n\t}\n\n\tlist(): RuntimeHandle[] {\n\t\treturn [...this.runtimes.values()];\n\t}\n\n\tget(key: string): RuntimeHandle | undefined {\n\t\treturn this.runtimes.get(key);\n\t}\n\n\t/**\n\t * Record the EventHub sequence synchronously when the RPC snapshot marker\n\t * arrives. The marker line precedes its response on stdout, so this runs\n\t * before the RpcClient response continuation even across separate chunks.\n\t */\n\trecordDashboardBarrier(runtimeKey: string, snapshotId: string, seq: number): void {\n\t\tthis.pruneDashboardBarriers();\n\t\tthis.dashboardBarriers.set(this.dashboardBarrierKey(runtimeKey, snapshotId), { seq, recordedAt: this.now() });\n\t\tthis.pruneDashboardBarriers();\n\t}\n\n\t/**\n\t * Capture a parent-session recovery snapshot and pair it with the sequence\n\t * captured at its RPC marker. This deliberately does not infer ordering from\n\t * await: later EventHub publications naturally have higher sequence numbers.\n\t */\n\tasync snapshotDashboard(handle: RuntimeHandle): Promise<DashboardRuntimeSnapshot> {\n\t\tconst snapshot = await (handle.client as DashboardSnapshotClient).getDashboardSnapshot();\n\t\tthis.pruneDashboardBarriers();\n\t\tconst barrierKey = this.dashboardBarrierKey(handle.key, snapshot.snapshotId);\n\t\tconst barrier = this.dashboardBarriers.get(barrierKey);\n\t\tthis.dashboardBarriers.delete(barrierKey);\n\t\tthis.scheduleDashboardBarrierPrune();\n\t\tif (!barrier) {\n\t\t\tthrow new Error(`Dashboard snapshot ${snapshot.snapshotId} arrived without its ordering barrier`);\n\t\t}\n\t\t// This is an authoritative RPC baseline, so it may legitimately lower the\n\t\t// count after a fork/rewind instead of retaining an event-derived maximum.\n\t\thandle.lastState = snapshot.state;\n\t\tthis.scheduleFleetSnapshot();\n\t\treturn { key: handle.key, barrierSeq: barrier.seq, snapshot };\n\t}\n\n\tprivate dashboardBarrierKey(runtimeKey: string, snapshotId: string): string {\n\t\treturn `${runtimeKey}\\0${snapshotId}`;\n\t}\n\n\tprivate pruneDashboardBarriers(): void {\n\t\tconst oldestAllowed = this.now() - this.dashboardBarrierTtlMs;\n\t\tfor (const [snapshotId, barrier] of this.dashboardBarriers) {\n\t\t\tif (barrier.recordedAt < oldestAllowed) this.dashboardBarriers.delete(snapshotId);\n\t\t}\n\t\twhile (this.dashboardBarriers.size > this.dashboardBarrierLimit) {\n\t\t\tconst oldest = this.dashboardBarriers.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.dashboardBarriers.delete(oldest);\n\t\t}\n\t\tthis.scheduleDashboardBarrierPrune();\n\t}\n\n\tprivate scheduleDashboardBarrierPrune(): void {\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tlet oldest: DashboardBarrier | undefined;\n\t\tfor (const barrier of this.dashboardBarriers.values()) {\n\t\t\tif (!oldest || barrier.recordedAt < oldest.recordedAt) oldest = barrier;\n\t\t}\n\t\tif (!oldest) return;\n\t\tconst delay = Math.max(1, oldest.recordedAt + this.dashboardBarrierTtlMs - this.now() + 1);\n\t\tthis.dashboardBarrierPruneTimer = setTimeout(() => {\n\t\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\t\tthis.pruneDashboardBarriers();\n\t\t}, delay);\n\t\tthis.dashboardBarrierPruneTimer.unref?.();\n\t}\n\n\tprivate scheduleFleetSnapshot(): void {\n\t\tif (this.closing) return;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = setTimeout(() => {\n\t\t\tthis.fleetSnapshotTimer = undefined;\n\t\t\tif (this.closing) return;\n\t\t\tconst event: FleetSnapshotEventDto = { type: \"fleet_snapshot\", runtimes: this.fleetSnapshot() };\n\t\t\tfor (const listener of this.fleetSnapshotListeners) {\n\t\t\t\ttry {\n\t\t\t\t\tlistener(event);\n\t\t\t\t} catch {\n\t\t\t\t\t// An SSE bridge subscriber must not break the pool's event loop.\n\t\t\t\t}\n\t\t\t}\n\t\t}, this.fleetSnapshotDebounceMs);\n\t\tthis.fleetSnapshotTimer.unref?.();\n\t}\n\n\t/** Spawn a new runtime in `cwd`, optionally opening an existing session file. */\n\tasync create(cwd: string, sessionPath?: string): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst key = randomBytes(6).toString(\"hex\");\n\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\tif (sessionPath) args.push(\"--session\", sessionPath);\n\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\tconst handle: RuntimeHandle = {\n\t\t\tkey,\n\t\t\tcwd,\n\t\t\tclient,\n\t\t\tcreatedAt: this.now(),\n\t\t\tlastActivity: this.now(),\n\t\t\tattention: new Map(),\n\t\t\tsessionFileFallback: sessionPath,\n\t\t\tbackgroundAgents: new Map(),\n\t\t};\n\t\tclient.onEvent((event) => this.handleEvent(handle, event as unknown as Record<string, unknown>));\n\t\tclient.onExit((info) => this.handleRuntimeExit(handle, info));\n\n\t\tconst startup = this.startSessionRuntime(handle);\n\t\tthis.startupPromises.add(startup);\n\t\ttry {\n\t\t\treturn await startup;\n\t\t} finally {\n\t\t\tthis.startupPromises.delete(startup);\n\t\t}\n\t}\n\n\tprivate async startSessionRuntime(handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tawait this.seedBackgroundAgents(handle);\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.runtimes.set(handle.key, handle);\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\t/** Stop a runtime and remove it from the pool. */\n\tasync stop(key: string): Promise<boolean> {\n\t\tconst handle = this.runtimes.get(key);\n\t\tif (!handle) return false;\n\t\tthis.handleEvent(handle, { type: \"runtime_removed\" });\n\t\tthis.runtimes.delete(key);\n\t\tthis.scheduleFleetSnapshot();\n\t\tawait handle.client.stop();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Return any live runtime suitable for process-global settings work, spawning\n\t * a hidden utility runtime (in the home directory) if no user session exists.\n\t * This is what lets the settings page — models, agent types, defaults — work\n\t * with zero sessions open, instead of 503-ing.\n\t */\n\tasync ensureUtilityRuntime(cwd = homedir()): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst existing = this.utilities.get(cwd);\n\t\tif (existing) return existing;\n\t\tlet promise = this.utilityPromises.get(cwd);\n\t\tif (!promise) {\n\t\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\t\tconst handle: RuntimeHandle = {\n\t\t\t\tkey: `utility:${cwd}`,\n\t\t\t\tcwd,\n\t\t\t\tclient,\n\t\t\t\tcreatedAt: this.now(),\n\t\t\t\tlastActivity: this.now(),\n\t\t\t\tattention: new Map(),\n\t\t\t\tbackgroundAgents: new Map(),\n\t\t\t};\n\t\t\tconst startup = this.startUtilityRuntime(cwd, handle);\n\t\t\tthis.startupPromises.add(startup);\n\t\t\tpromise = startup\n\t\t\t\t.catch((err) => {\n\t\t\t\t\t// Allow a retry on the next request instead of caching the failure.\n\t\t\t\t\tthis.utilityPromises.delete(cwd);\n\t\t\t\t\tthrow err;\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis.startupPromises.delete(startup);\n\t\t\t\t});\n\t\t\tthis.utilityPromises.set(cwd, promise);\n\t\t}\n\t\treturn promise;\n\t}\n\n\tprivate async startUtilityRuntime(cwd: string, handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.utilities.set(cwd, handle);\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\tasync stopAll(): Promise<void> {\n\t\tthis.closing = true;\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = undefined;\n\t\tthis.fleetSnapshotListeners.length = 0;\n\t\tthis.dashboardBarriers.clear();\n\t\tconst handles = new Set<RuntimeHandle>([\n\t\t\t...this.runtimes.values(),\n\t\t\t...this.utilities.values(),\n\t\t\t...this.starting.values(),\n\t\t]);\n\t\tconst startupPromises = new Set<Promise<unknown>>([...this.startupPromises, ...this.utilityPromises.values()]);\n\t\tthis.runtimes.clear();\n\t\tthis.utilities.clear();\n\t\tthis.utilityPromises.clear();\n\t\tawait Promise.allSettled([...handles].map((handle) => handle.client.stop()));\n\t\tawait Promise.allSettled([...startupPromises]);\n\t}\n\n\tprivate handleRuntimeExit(handle: RuntimeHandle, info: RpcExitInfo): void {\n\t\tif (this.closing || this.exitedHandles.has(handle) || !this.isLiveHandle(handle)) return;\n\t\tthis.exitedHandles.add(handle);\n\t\tconst message = formatRpcExit(info);\n\t\tthis.recordRuntimeError(handle, message);\n\t\tthis.logger(`runtime ${handle.key} ${message}`);\n\t\tthis.handleEvent(handle, { type: \"agent_end\", messages: [], aborted: true, errorMessage: message });\n\t}\n\n\tprivate isLiveHandle(handle: RuntimeHandle): boolean {\n\t\treturn (\n\t\t\tthis.runtimes.get(handle.key) === handle ||\n\t\t\tthis.utilities.get(handle.cwd) === handle ||\n\t\t\tthis.starting.has(handle)\n\t\t);\n\t}\n\n\tprivate handleEvent(handle: RuntimeHandle, event: Record<string, unknown>): void {\n\t\tconst type = event.type as string;\n\t\tif (type !== \"dashboard_snapshot_barrier\") {\n\t\t\thandle.lastActivity = this.now();\n\t\t\tthis.updateStateFromEvent(handle, event, type);\n\t\t}\n\n\t\t// Track needs-attention sources: extension UI requests, parent\n\t\t// paused, error states.\n\t\tif (type === \"extension_ui_request\") {\n\t\t\tconst method = event.method as string;\n\t\t\tif (method === \"select\" || method === \"confirm\" || method === \"input\" || method === \"editor\") {\n\t\t\t\thandle.attention.set(`ui:${event.id}`, `extension ${method} awaiting response`);\n\t\t\t}\n\t\t}\n\t\tif (type === \"extension_ui_response_handled\") {\n\t\t\thandle.attention.delete(`ui:${event.id}`);\n\t\t}\n\t\tif (type === \"agent_start\") {\n\t\t\t// A new turn clears prior UI-request attention (requests were resolved or timed out).\n\t\t\tfor (const k of [...handle.attention.keys()]) {\n\t\t\t\tif (k.startsWith(\"ui:\")) handle.attention.delete(k);\n\t\t\t}\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\thandle.attention.delete(\"suggest\");\n\t\t\thandle.attention.delete(\"error\");\n\t\t\thandle.error = undefined;\n\t\t}\n\t\tif (type === \"suggest_next\") {\n\t\t\t// suggest_next as the ending action = \"your move\": mark needs-attention\n\t\t\t// so the fleet card doesn't read idle. Cleared on the next agent_start.\n\t\t\thandle.attention.set(\"suggest\", \"suggested command awaiting\");\n\t\t}\n\t\tif (type === \"parent_paused_for_background_agents\") {\n\t\t\thandle.attention.set(\"paused\", `paused — ${event.runningAgentCount} background agents running`);\n\t\t}\n\t\tif (type === \"agent_end\") {\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\tif (event.errorMessage) this.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\t\tif (type === \"auto_retry_end\" && event.success === false && event.finalError) {\n\t\t\tthis.recordRuntimeError(handle, String(event.finalError));\n\t\t}\n\t\tif (type === \"auto_compaction_end\" && event.errorMessage) {\n\t\t\tthis.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\n\t\t// Track background agents from lifecycle events.\n\t\tif (type === \"background_agent_start\") {\n\t\t\thandle.backgroundAgents.set(event.agentId as string, {\n\t\t\t\tagentId: event.agentId as string,\n\t\t\t\tagentType: event.agentType as string,\n\t\t\t\ttaskSummary: event.taskSummary as string,\n\t\t\t\tstartedAt: new Date().toISOString(),\n\t\t\t\tstatus: \"running\",\n\t\t\t\tsessionDir: event.sessionDir as string | undefined,\n\t\t\t});\n\t\t}\n\t\tif (type === \"background_agent_end\") {\n\t\t\tconst existing = handle.backgroundAgents.get(event.agentId as string);\n\t\t\tif (existing) {\n\t\t\t\texisting.status = event.success ? \"completed\" : \"failed\";\n\t\t\t\texisting.sessionFile = (event.sessionFile as string | undefined) ?? existing.sessionFile;\n\t\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t\t}\n\t\t}\n\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(handle.key, event);\n\t\t\t} catch {\n\t\t\t\t// A broken SSE subscriber must not break event distribution.\n\t\t\t}\n\t\t}\n\t\tif (this.runtimes.get(handle.key) === handle && FLEET_SNAPSHOT_EVENT_TYPES.has(type)) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t}\n\n\t/**\n\t * Events intentionally patch only fields they can prove. Session identity,\n\t * session file, configuration, and context usage remain from the last RPC\n\t * baseline (or the stable creation fallback) until a later reconciliation.\n\t */\n\tprivate updateStateFromEvent(handle: RuntimeHandle, event: Record<string, unknown>, type: string): void {\n\t\tconst state = this.fallbackState(handle);\n\t\tswitch (type) {\n\t\t\tcase \"agent_start\": {\n\t\t\t\tconst model = event.model;\n\t\t\t\tif (\n\t\t\t\t\tmodel &&\n\t\t\t\t\ttypeof model === \"object\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).provider === \"string\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).id === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tconst nextModel = model as { provider: string; id: string };\n\t\t\t\t\tstate.model =\n\t\t\t\t\t\tstate.model?.provider === nextModel.provider && state.model.id === nextModel.id\n\t\t\t\t\t\t\t? { ...state.model, ...nextModel }\n\t\t\t\t\t\t\t: nextModel;\n\t\t\t\t}\n\t\t\t\tstate.isStreaming = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"agent_end\":\n\t\t\t\tstate.isStreaming = false;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_start\":\n\t\t\t\tstate.isCompacting = true;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_end\":\n\t\t\t\tstate.isCompacting = false;\n\t\t\t\tbreak;\n\t\t\tcase \"tasks_update\":\n\t\t\t\tif (Array.isArray(event.tasks)) state.tasks = [...event.tasks] as SessionStateDto[\"tasks\"];\n\t\t\t\tbreak;\n\t\t\tcase \"session_name_changed\":\n\t\t\t\tif (typeof event.name === \"string\") state.sessionName = event.name;\n\t\t\t\tbreak;\n\t\t\tcase \"message_start\":\n\t\t\t\tstate.messageCount += 1;\n\t\t\t\tbreak;\n\t\t}\n\t\thandle.lastState = state;\n\t}\n\n\tprivate recordRuntimeError(handle: RuntimeHandle, message: string): void {\n\t\thandle.error = message;\n\t\thandle.attention.set(\"error\", message);\n\t\tif (this.runtimes.get(handle.key) === handle) this.scheduleFleetSnapshot();\n\t}\n\n\tprivate pruneCompletedBackgroundAgents(handle: RuntimeHandle): void {\n\t\tconst evictable = [...handle.backgroundAgents.values()]\n\t\t\t.map((agent, index) => {\n\t\t\t\tconst startedAtMs = Date.parse(agent.startedAt);\n\t\t\t\treturn { agent, index, startedAtMs: Number.isFinite(startedAtMs) ? startedAtMs : 0 };\n\t\t\t})\n\t\t\t.filter(({ agent }) => agent.status !== \"running\")\n\t\t\t.sort((a, b) => a.startedAtMs - b.startedAtMs || a.index - b.index);\n\t\tconst excess = evictable.length - MAX_COMPLETED_BACKGROUND_AGENTS;\n\t\tif (excess <= 0) return;\n\t\tfor (const { agent } of evictable.slice(0, excess)) {\n\t\t\thandle.backgroundAgents.delete(agent.agentId);\n\t\t}\n\t}\n\n\tprivate fallbackState(handle: RuntimeHandle): SessionStateDto {\n\t\tconst previous = handle.lastState;\n\t\treturn {\n\t\t\t...previous,\n\t\t\tsessionId: previous?.sessionId ?? handle.key,\n\t\t\ttasks: previous?.tasks ? [...previous.tasks] : [],\n\t\t\tthinkingLevel: previous?.thinkingLevel ?? \"off\",\n\t\t\tisStreaming: previous?.isStreaming ?? false,\n\t\t\tisCompacting: previous?.isCompacting ?? false,\n\t\t\tsteeringMode: previous?.steeringMode ?? \"all\",\n\t\t\tfollowUpMode: previous?.followUpMode ?? \"all\",\n\t\t\tsessionFile: previous?.sessionFile ?? handle.sessionFileFallback,\n\t\t\tautoCompactionEnabled: previous?.autoCompactionEnabled ?? false,\n\t\t\tmessageCount: previous?.messageCount ?? 0,\n\t\t\tpendingMessageCount: previous?.pendingMessageCount ?? 0,\n\t\t};\n\t}\n\n\tprivate describeFleetRuntime(handle: RuntimeHandle): FleetRuntimeSnapshotDto {\n\t\tconst state = this.fallbackState(handle);\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()].map((agent) => ({ ...agent })),\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n\n\tprivate async seedBackgroundAgents(handle: RuntimeHandle): Promise<void> {\n\t\ttry {\n\t\t\tconst agents = (await handle.client.listBackgroundAgents()) as unknown as BackgroundAgentDto[];\n\t\t\tfor (const agent of agents) {\n\t\t\t\thandle.backgroundAgents.set(agent.agentId, agent);\n\t\t\t}\n\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} background-agent registry unavailable: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Snapshot a runtime for the fleet endpoint. */\n\tasync describe(handle: RuntimeHandle): Promise<RuntimeInfoDto> {\n\t\tconst previousFleetRuntime =\n\t\t\tthis.runtimes.get(handle.key) === handle ? this.describeFleetRuntime(handle) : undefined;\n\t\tlet fleetRuntimeEnriched = false;\n\t\tlet state: SessionStateDto;\n\t\ttry {\n\t\t\tconst authoritative = (await handle.client.getState()) as unknown as SessionStateDto;\n\t\t\tconst fallback = this.fallbackState(handle);\n\t\t\tstate = {\n\t\t\t\t...fallback,\n\t\t\t\t...authoritative,\n\t\t\t\tsessionId: authoritative.sessionId ?? fallback.sessionId,\n\t\t\t\tsessionFile: authoritative.sessionFile ?? fallback.sessionFile,\n\t\t\t\ttasks: authoritative.tasks ?? fallback.tasks,\n\t\t\t};\n\t\t\thandle.lastState = state;\n\t\t\tfleetRuntimeEnriched = true;\n\t\t\tif (handle.error?.startsWith(\"RPC process\")) {\n\t\t\t\thandle.error = undefined;\n\t\t\t\thandle.attention.delete(\"error\");\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.recordRuntimeError(handle, message);\n\t\t\tthis.logger(`runtime ${handle.key} state unavailable for fleet card: ${message}`);\n\t\t\tstate = this.fallbackState(handle);\n\t\t}\n\t\tlet stats: RuntimeStatsSummaryDto | undefined;\n\t\ttry {\n\t\t\tconst sessionStats = await handle.client.getSessionStats();\n\t\t\tstats = { tokensTotal: sessionStats.tokens.total, cost: sessionStats.cost };\n\t\t\tif (sessionStats.contextUsage) {\n\t\t\t\thandle.lastState = { ...this.fallbackState(handle), contextUsage: sessionStats.contextUsage };\n\t\t\t\tfleetRuntimeEnriched = true;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} stats unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tlet lastAssistantText: string | undefined;\n\t\ttry {\n\t\t\tconst text = await handle.client.getLastAssistantText();\n\t\t\tlastAssistantText = text ? text.slice(0, 200) : undefined;\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} last assistant text unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tfleetRuntimeEnriched &&\n\t\t\tpreviousFleetRuntime &&\n\t\t\tthis.runtimes.get(handle.key) === handle &&\n\t\t\t!isDeepStrictEqual(previousFleetRuntime, this.describeFleetRuntime(handle))\n\t\t) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tstats,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()],\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tlastAssistantText,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime-pool.d.ts","sourceRoot":"","sources":["../../src/server/runtime-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RPC runtime pool — one `dreb --mode rpc` child process per live session.\n *\n * dreb's RPC mode is strictly one-session-per-process (switch_session repoints\n * the same process; it never multiplexes), so the pool spawns N children keyed\n * by an opaque runtime key. The telegram bridge is the in-repo precedent.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { RpcClient, type RpcExitInfo } from \"@dreb/coding-agent/rpc\";\nimport {\n\ttype BackgroundAgentDto,\n\ttype FleetRuntimeSnapshotDto,\n\ttype FleetSnapshotEventDto,\n\tMAX_COMPLETED_BACKGROUND_AGENTS,\n\ttype RuntimeInfoDto,\n\ttype RuntimeStatsSummaryDto,\n\ttype SessionStateDto,\n} from \"../shared/protocol.js\";\n\n/** Resolve the absolute path to the dreb CLI (RpcClient defaults to a cwd-relative path). */\nexport function resolveDrebCliPath(): string {\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\treturn join(dirname(fileURLToPath(resolved)), \"cli.js\");\n}\n\nfunction formatRpcExit(info: RpcExitInfo): string {\n\tif (info.error) return `RPC process failed: ${info.error.message}`;\n\treturn `RPC process exited (code ${info.code}, signal ${info.signal})`;\n}\n\nexport type RuntimeEventListener = (key: string, event: Record<string, unknown>) => void;\n\n/** Listener for coalesced, synchronous fleet runtime snapshots. */\nexport type FleetSnapshotListener = (event: FleetSnapshotEventDto) => void;\n\nexport { MAX_COMPLETED_BACKGROUND_AGENTS };\n\ninterface RpcDashboardSnapshot {\n\tsnapshotId: string;\n\tstate: SessionStateDto;\n\tmessages: unknown[];\n\tbackgroundAgents: BackgroundAgentDto[];\n}\n\ntype DashboardSnapshotClient = RpcClient & { getDashboardSnapshot(): Promise<RpcDashboardSnapshot> };\n\nexport interface DashboardRuntimeSnapshot {\n\tkey: string;\n\tbarrierSeq: number;\n\tsnapshot: RpcDashboardSnapshot;\n}\n\nexport interface RuntimeHandle {\n\tkey: string;\n\tcwd: string;\n\tclient: RpcClient;\n\t/** Session start time (ms epoch) — stable tiebreak for deterministic fleet ordering. */\n\tcreatedAt: number;\n\tlastActivity: number;\n\t/** Needs-attention sources, keyed so they can be cleared independently. */\n\tattention: Map<string, string>;\n\t/** Last runtime-level error, persisted server-side so fleet refreshes stay honest. */\n\terror?: string;\n\t/** Provider error currently responsible for error/attention; retry may clear only this source. */\n\tproviderError?: string;\n\t/** Last authoritative state, patched only with event-derivable fields between RPC reads. */\n\tlastState?: SessionStateDto;\n\t/** Resume-path fallback; events must never invent or overwrite session identity. */\n\tsessionFileFallback?: string;\n\t/** Background agents seen via events (agentId → latest info). */\n\tbackgroundAgents: Map<string, BackgroundAgentDto>;\n}\n\nexport const DEFAULT_DASHBOARD_BARRIER_TTL_MS = 5 * 60_000;\nexport const DEFAULT_DASHBOARD_BARRIER_LIMIT = 1000;\nexport const DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS = 200;\n\n/** Events that mutate a field carried by the lightweight fleet snapshot. */\nconst FLEET_SNAPSHOT_EVENT_TYPES = new Set([\n\t\"agent_start\",\n\t\"agent_end\",\n\t\"auto_compaction_start\",\n\t\"auto_compaction_end\",\n\t\"auto_retry_start\",\n\t\"auto_retry_end\",\n\t\"background_agent_start\",\n\t\"background_agent_end\",\n\t\"extension_ui_request\",\n\t\"extension_ui_response_handled\",\n\t\"message_end\",\n\t\"message_start\",\n\t\"parent_paused_for_background_agents\",\n\t\"runtime_removed\",\n\t\"session_name_changed\",\n\t\"suggest_next\",\n\t\"tasks_update\",\n]);\n\nexport interface RuntimePoolOptions {\n\tcliPath?: string;\n\t/** Extra args for every runtime (e.g. --provider). */\n\tbaseArgs?: string[];\n\t/** RpcClient factory override for tests. */\n\tclientFactory?: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tlogger?: (line: string) => void;\n\t/** Bounds unclaimed RPC snapshot ordering records. */\n\tdashboardBarrierTtlMs?: number;\n\tdashboardBarrierLimit?: number;\n\t/** Injectable clock for deterministic barrier-expiry tests. */\n\tnow?: () => number;\n\t/** Coalescing delay for event-derived fleet snapshot emissions. */\n\tfleetSnapshotDebounceMs?: number;\n}\n\ninterface DashboardBarrier {\n\tseq: number;\n\trecordedAt: number;\n}\n\nexport class RuntimePool {\n\tprivate readonly runtimes = new Map<string, RuntimeHandle>();\n\tprivate readonly listeners: RuntimeEventListener[] = [];\n\tprivate readonly fleetSnapshotListeners: FleetSnapshotListener[] = [];\n\tprivate readonly cliPath: string;\n\tprivate readonly baseArgs: string[];\n\tprivate readonly clientFactory: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tprivate readonly logger: (line: string) => void;\n\t/**\n\t * A single lazily-spawned utility runtime used to service settings/model/\n\t * agent-type endpoints when no user session is live. Kept out of `runtimes`\n\t * (and therefore out of the fleet) so it never shows as a session card.\n\t */\n\tprivate readonly utilities = new Map<string, RuntimeHandle>();\n\tprivate readonly utilityPromises = new Map<string, Promise<RuntimeHandle>>();\n\tprivate readonly starting = new Set<RuntimeHandle>();\n\tprivate readonly startupPromises = new Set<Promise<unknown>>();\n\tprivate readonly exitedHandles = new WeakSet<RuntimeHandle>();\n\t/** Snapshot ordering records observed synchronously from RpcClient stdout. */\n\tprivate readonly dashboardBarriers = new Map<string, DashboardBarrier>();\n\tprivate readonly dashboardBarrierTtlMs: number;\n\tprivate readonly dashboardBarrierLimit: number;\n\tprivate readonly now: () => number;\n\tprivate readonly fleetSnapshotDebounceMs: number;\n\tprivate dashboardBarrierPruneTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate fleetSnapshotTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate closing = false;\n\n\tconstructor(options: RuntimePoolOptions = {}) {\n\t\tthis.cliPath = options.cliPath ?? resolveDrebCliPath();\n\t\tthis.baseArgs = options.baseArgs ?? [];\n\t\tthis.clientFactory =\n\t\t\toptions.clientFactory ?? ((o) => new RpcClient({ cliPath: o.cliPath, cwd: o.cwd, args: o.args }));\n\t\tthis.logger = options.logger ?? ((line) => console.warn(`[dashboard] ${line}`));\n\t\tthis.dashboardBarrierTtlMs = options.dashboardBarrierTtlMs ?? DEFAULT_DASHBOARD_BARRIER_TTL_MS;\n\t\tthis.dashboardBarrierLimit = options.dashboardBarrierLimit ?? DEFAULT_DASHBOARD_BARRIER_LIMIT;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.fleetSnapshotDebounceMs = options.fleetSnapshotDebounceMs ?? DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS;\n\t}\n\n\t/** Subscribe to events from every runtime, tagged with the runtime key. */\n\tonEvent(listener: RuntimeEventListener): () => void {\n\t\tthis.listeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.listeners.indexOf(listener);\n\t\t\tif (i !== -1) this.listeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/** Subscribe to debounced, in-memory fleet snapshots. */\n\tonFleetSnapshot(listener: FleetSnapshotListener): () => void {\n\t\tthis.fleetSnapshotListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.fleetSnapshotListeners.indexOf(listener);\n\t\t\tif (i !== -1) this.fleetSnapshotListeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Build the fleet's live-runtime view without RPC or disk access. Map\n\t * insertion order is retained intentionally; the UI owns presentation order.\n\t */\n\tfleetSnapshot(): FleetRuntimeSnapshotDto[] {\n\t\treturn [...this.runtimes.values()].map((handle) => this.describeFleetRuntime(handle));\n\t}\n\n\tlist(): RuntimeHandle[] {\n\t\treturn [...this.runtimes.values()];\n\t}\n\n\tget(key: string): RuntimeHandle | undefined {\n\t\treturn this.runtimes.get(key);\n\t}\n\n\t/**\n\t * Record the EventHub sequence synchronously when the RPC snapshot marker\n\t * arrives. The marker line precedes its response on stdout, so this runs\n\t * before the RpcClient response continuation even across separate chunks.\n\t */\n\trecordDashboardBarrier(runtimeKey: string, snapshotId: string, seq: number): void {\n\t\tthis.pruneDashboardBarriers();\n\t\tthis.dashboardBarriers.set(this.dashboardBarrierKey(runtimeKey, snapshotId), { seq, recordedAt: this.now() });\n\t\tthis.pruneDashboardBarriers();\n\t}\n\n\t/**\n\t * Capture a parent-session recovery snapshot and pair it with the sequence\n\t * captured at its RPC marker. This deliberately does not infer ordering from\n\t * await: later EventHub publications naturally have higher sequence numbers.\n\t */\n\tasync snapshotDashboard(handle: RuntimeHandle): Promise<DashboardRuntimeSnapshot> {\n\t\tconst snapshot = await (handle.client as DashboardSnapshotClient).getDashboardSnapshot();\n\t\tthis.pruneDashboardBarriers();\n\t\tconst barrierKey = this.dashboardBarrierKey(handle.key, snapshot.snapshotId);\n\t\tconst barrier = this.dashboardBarriers.get(barrierKey);\n\t\tthis.dashboardBarriers.delete(barrierKey);\n\t\tthis.scheduleDashboardBarrierPrune();\n\t\tif (!barrier) {\n\t\t\tthrow new Error(`Dashboard snapshot ${snapshot.snapshotId} arrived without its ordering barrier`);\n\t\t}\n\t\t// This is an authoritative RPC baseline, so it may legitimately lower the\n\t\t// count after a fork/rewind instead of retaining an event-derived maximum.\n\t\thandle.lastState = snapshot.state;\n\t\tthis.scheduleFleetSnapshot();\n\t\treturn { key: handle.key, barrierSeq: barrier.seq, snapshot };\n\t}\n\n\tprivate dashboardBarrierKey(runtimeKey: string, snapshotId: string): string {\n\t\treturn `${runtimeKey}\\0${snapshotId}`;\n\t}\n\n\tprivate pruneDashboardBarriers(): void {\n\t\tconst oldestAllowed = this.now() - this.dashboardBarrierTtlMs;\n\t\tfor (const [snapshotId, barrier] of this.dashboardBarriers) {\n\t\t\tif (barrier.recordedAt < oldestAllowed) this.dashboardBarriers.delete(snapshotId);\n\t\t}\n\t\twhile (this.dashboardBarriers.size > this.dashboardBarrierLimit) {\n\t\t\tconst oldest = this.dashboardBarriers.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.dashboardBarriers.delete(oldest);\n\t\t}\n\t\tthis.scheduleDashboardBarrierPrune();\n\t}\n\n\tprivate scheduleDashboardBarrierPrune(): void {\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tlet oldest: DashboardBarrier | undefined;\n\t\tfor (const barrier of this.dashboardBarriers.values()) {\n\t\t\tif (!oldest || barrier.recordedAt < oldest.recordedAt) oldest = barrier;\n\t\t}\n\t\tif (!oldest) return;\n\t\tconst delay = Math.max(1, oldest.recordedAt + this.dashboardBarrierTtlMs - this.now() + 1);\n\t\tthis.dashboardBarrierPruneTimer = setTimeout(() => {\n\t\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\t\tthis.pruneDashboardBarriers();\n\t\t}, delay);\n\t\tthis.dashboardBarrierPruneTimer.unref?.();\n\t}\n\n\tprivate scheduleFleetSnapshot(): void {\n\t\tif (this.closing) return;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = setTimeout(() => {\n\t\t\tthis.fleetSnapshotTimer = undefined;\n\t\t\tif (this.closing) return;\n\t\t\tconst event: FleetSnapshotEventDto = { type: \"fleet_snapshot\", runtimes: this.fleetSnapshot() };\n\t\t\tfor (const listener of this.fleetSnapshotListeners) {\n\t\t\t\ttry {\n\t\t\t\t\tlistener(event);\n\t\t\t\t} catch {\n\t\t\t\t\t// An SSE bridge subscriber must not break the pool's event loop.\n\t\t\t\t}\n\t\t\t}\n\t\t}, this.fleetSnapshotDebounceMs);\n\t\tthis.fleetSnapshotTimer.unref?.();\n\t}\n\n\t/** Spawn a new runtime in `cwd`, optionally opening an existing session file. */\n\tasync create(cwd: string, sessionPath?: string): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst key = randomBytes(6).toString(\"hex\");\n\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\tif (sessionPath) args.push(\"--session\", sessionPath);\n\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\tconst handle: RuntimeHandle = {\n\t\t\tkey,\n\t\t\tcwd,\n\t\t\tclient,\n\t\t\tcreatedAt: this.now(),\n\t\t\tlastActivity: this.now(),\n\t\t\tattention: new Map(),\n\t\t\tsessionFileFallback: sessionPath,\n\t\t\tbackgroundAgents: new Map(),\n\t\t};\n\t\tclient.onEvent((event) => this.handleEvent(handle, event as unknown as Record<string, unknown>));\n\t\tclient.onExit((info) => this.handleRuntimeExit(handle, info));\n\n\t\tconst startup = this.startSessionRuntime(handle);\n\t\tthis.startupPromises.add(startup);\n\t\ttry {\n\t\t\treturn await startup;\n\t\t} finally {\n\t\t\tthis.startupPromises.delete(startup);\n\t\t}\n\t}\n\n\tprivate async startSessionRuntime(handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tawait this.seedBackgroundAgents(handle);\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.runtimes.set(handle.key, handle);\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\t/** Stop a runtime and remove it from the pool. */\n\tasync stop(key: string): Promise<boolean> {\n\t\tconst handle = this.runtimes.get(key);\n\t\tif (!handle) return false;\n\t\tthis.handleEvent(handle, { type: \"runtime_removed\" });\n\t\tthis.runtimes.delete(key);\n\t\tthis.scheduleFleetSnapshot();\n\t\tawait handle.client.stop();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Return any live runtime suitable for process-global settings work, spawning\n\t * a hidden utility runtime (in the home directory) if no user session exists.\n\t * This is what lets the settings page — models, agent types, defaults — work\n\t * with zero sessions open, instead of 503-ing.\n\t */\n\tasync ensureUtilityRuntime(cwd = homedir()): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst existing = this.utilities.get(cwd);\n\t\tif (existing) return existing;\n\t\tlet promise = this.utilityPromises.get(cwd);\n\t\tif (!promise) {\n\t\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\t\tconst handle: RuntimeHandle = {\n\t\t\t\tkey: `utility:${cwd}`,\n\t\t\t\tcwd,\n\t\t\t\tclient,\n\t\t\t\tcreatedAt: this.now(),\n\t\t\t\tlastActivity: this.now(),\n\t\t\t\tattention: new Map(),\n\t\t\t\tbackgroundAgents: new Map(),\n\t\t\t};\n\t\t\tconst startup = this.startUtilityRuntime(cwd, handle);\n\t\t\tthis.startupPromises.add(startup);\n\t\t\tpromise = startup\n\t\t\t\t.catch((err) => {\n\t\t\t\t\t// Allow a retry on the next request instead of caching the failure.\n\t\t\t\t\tthis.utilityPromises.delete(cwd);\n\t\t\t\t\tthrow err;\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis.startupPromises.delete(startup);\n\t\t\t\t});\n\t\t\tthis.utilityPromises.set(cwd, promise);\n\t\t}\n\t\treturn promise;\n\t}\n\n\tprivate async startUtilityRuntime(cwd: string, handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.utilities.set(cwd, handle);\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\tasync stopAll(): Promise<void> {\n\t\tthis.closing = true;\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = undefined;\n\t\tthis.fleetSnapshotListeners.length = 0;\n\t\tthis.dashboardBarriers.clear();\n\t\tconst handles = new Set<RuntimeHandle>([\n\t\t\t...this.runtimes.values(),\n\t\t\t...this.utilities.values(),\n\t\t\t...this.starting.values(),\n\t\t]);\n\t\tconst startupPromises = new Set<Promise<unknown>>([...this.startupPromises, ...this.utilityPromises.values()]);\n\t\tthis.runtimes.clear();\n\t\tthis.utilities.clear();\n\t\tthis.utilityPromises.clear();\n\t\tawait Promise.allSettled([...handles].map((handle) => handle.client.stop()));\n\t\tawait Promise.allSettled([...startupPromises]);\n\t}\n\n\tprivate handleRuntimeExit(handle: RuntimeHandle, info: RpcExitInfo): void {\n\t\tif (this.closing || this.exitedHandles.has(handle) || !this.isLiveHandle(handle)) return;\n\t\tthis.exitedHandles.add(handle);\n\t\tconst message = formatRpcExit(info);\n\t\tthis.recordRuntimeError(handle, message);\n\t\tthis.logger(`runtime ${handle.key} ${message}`);\n\t\tthis.handleEvent(handle, { type: \"agent_end\", messages: [], aborted: true, errorMessage: message });\n\t}\n\n\tprivate isLiveHandle(handle: RuntimeHandle): boolean {\n\t\treturn (\n\t\t\tthis.runtimes.get(handle.key) === handle ||\n\t\t\tthis.utilities.get(handle.cwd) === handle ||\n\t\t\tthis.starting.has(handle)\n\t\t);\n\t}\n\n\tprivate handleEvent(handle: RuntimeHandle, event: Record<string, unknown>): void {\n\t\tconst type = event.type as string;\n\t\tif (type !== \"dashboard_snapshot_barrier\") {\n\t\t\thandle.lastActivity = this.now();\n\t\t\tthis.updateStateFromEvent(handle, event, type);\n\t\t}\n\n\t\t// Track needs-attention sources: extension UI requests, parent\n\t\t// paused, error states.\n\t\tif (type === \"extension_ui_request\") {\n\t\t\tconst method = event.method as string;\n\t\t\tif (method === \"select\" || method === \"confirm\" || method === \"input\" || method === \"editor\") {\n\t\t\t\thandle.attention.set(`ui:${event.id}`, `extension ${method} awaiting response`);\n\t\t\t}\n\t\t}\n\t\tif (type === \"extension_ui_response_handled\") {\n\t\t\thandle.attention.delete(`ui:${event.id}`);\n\t\t}\n\t\tif (type === \"agent_start\") {\n\t\t\t// A new turn clears prior UI-request attention (requests were resolved or timed out).\n\t\t\tfor (const k of [...handle.attention.keys()]) {\n\t\t\t\tif (k.startsWith(\"ui:\")) handle.attention.delete(k);\n\t\t\t}\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\thandle.attention.delete(\"suggest\");\n\t\t\thandle.attention.delete(\"error\");\n\t\t\thandle.error = undefined;\n\t\t\thandle.providerError = undefined;\n\t\t}\n\t\tif (type === \"suggest_next\") {\n\t\t\t// suggest_next as the ending action = \"your move\": mark needs-attention\n\t\t\t// so the fleet card doesn't read idle. Cleared on the next agent_start.\n\t\t\thandle.attention.set(\"suggest\", \"suggested command awaiting\");\n\t\t}\n\t\tif (type === \"parent_paused_for_background_agents\") {\n\t\t\thandle.attention.set(\"paused\", `paused — ${event.runningAgentCount} background agents running`);\n\t\t}\n\t\tif (type === \"agent_end\") {\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\tif (event.errorMessage) this.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\t\tif (type === \"message_end\") {\n\t\t\tconst message = event.message;\n\t\t\tif (message && typeof message === \"object\" && !Array.isArray(message)) {\n\t\t\t\tconst assistant = message as Record<string, unknown>;\n\t\t\t\tif (assistant.role === \"assistant\" && assistant.stopReason === \"error\") {\n\t\t\t\t\tconst rawError = assistant.errorMessage;\n\t\t\t\t\tconst error = typeof rawError === \"string\" && rawError.trim().length > 0 ? rawError : \"Unknown error\";\n\t\t\t\t\tthis.recordProviderError(handle, error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (type === \"auto_retry_start\") this.clearProviderError(handle);\n\t\tif (type === \"auto_compaction_start\" && event.reason === \"overflow\") this.clearProviderError(handle);\n\t\tif (type === \"auto_retry_end\" && event.success === false && event.finalError) {\n\t\t\tthis.recordProviderError(handle, String(event.finalError));\n\t\t} else if (type === \"auto_retry_end\" && event.success === true) {\n\t\t\tthis.clearProviderError(handle);\n\t\t}\n\t\tif (type === \"auto_compaction_end\" && event.errorMessage) {\n\t\t\tthis.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\n\t\t// Track background agents from lifecycle events.\n\t\tif (type === \"background_agent_start\") {\n\t\t\thandle.backgroundAgents.set(event.agentId as string, {\n\t\t\t\tagentId: event.agentId as string,\n\t\t\t\tagentType: event.agentType as string,\n\t\t\t\ttaskSummary: event.taskSummary as string,\n\t\t\t\tstartedAt: new Date().toISOString(),\n\t\t\t\tstatus: \"running\",\n\t\t\t\tsessionDir: event.sessionDir as string | undefined,\n\t\t\t});\n\t\t}\n\t\tif (type === \"background_agent_end\") {\n\t\t\tconst existing = handle.backgroundAgents.get(event.agentId as string);\n\t\t\tif (existing) {\n\t\t\t\texisting.status = event.success ? \"completed\" : \"failed\";\n\t\t\t\texisting.sessionFile = (event.sessionFile as string | undefined) ?? existing.sessionFile;\n\t\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t\t}\n\t\t}\n\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(handle.key, event);\n\t\t\t} catch {\n\t\t\t\t// A broken SSE subscriber must not break event distribution.\n\t\t\t}\n\t\t}\n\t\tif (this.runtimes.get(handle.key) === handle && FLEET_SNAPSHOT_EVENT_TYPES.has(type)) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t}\n\n\t/**\n\t * Events intentionally patch only fields they can prove. Session identity,\n\t * session file, configuration, and context usage remain from the last RPC\n\t * baseline (or the stable creation fallback) until a later reconciliation.\n\t */\n\tprivate updateStateFromEvent(handle: RuntimeHandle, event: Record<string, unknown>, type: string): void {\n\t\tconst state = this.fallbackState(handle);\n\t\tswitch (type) {\n\t\t\tcase \"agent_start\": {\n\t\t\t\tconst model = event.model;\n\t\t\t\tif (\n\t\t\t\t\tmodel &&\n\t\t\t\t\ttypeof model === \"object\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).provider === \"string\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).id === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tconst nextModel = model as { provider: string; id: string };\n\t\t\t\t\tstate.model =\n\t\t\t\t\t\tstate.model?.provider === nextModel.provider && state.model.id === nextModel.id\n\t\t\t\t\t\t\t? { ...state.model, ...nextModel }\n\t\t\t\t\t\t\t: nextModel;\n\t\t\t\t}\n\t\t\t\tstate.isStreaming = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"agent_end\":\n\t\t\t\tstate.isStreaming = false;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_start\":\n\t\t\t\tstate.isCompacting = true;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_end\":\n\t\t\t\tstate.isCompacting = false;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_retry_start\":\n\t\t\t\tstate.isRetrying = true;\n\t\t\t\tstate.retryAttempt = typeof event.attempt === \"number\" ? event.attempt : state.retryAttempt;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_retry_end\":\n\t\t\t\tstate.isRetrying = false;\n\t\t\t\tstate.retryAttempt = 0;\n\t\t\t\tbreak;\n\t\t\tcase \"tasks_update\":\n\t\t\t\tif (Array.isArray(event.tasks)) state.tasks = [...event.tasks] as SessionStateDto[\"tasks\"];\n\t\t\t\tbreak;\n\t\t\tcase \"session_name_changed\":\n\t\t\t\tif (typeof event.name === \"string\") state.sessionName = event.name;\n\t\t\t\tbreak;\n\t\t\tcase \"message_start\":\n\t\t\t\tstate.messageCount += 1;\n\t\t\t\tbreak;\n\t\t}\n\t\thandle.lastState = state;\n\t}\n\n\tprivate recordRuntimeError(handle: RuntimeHandle, message: string): void {\n\t\thandle.error = message;\n\t\thandle.attention.set(\"error\", message);\n\t\tif (this.runtimes.get(handle.key) === handle) this.scheduleFleetSnapshot();\n\t}\n\n\tprivate recordProviderError(handle: RuntimeHandle, message: string): void {\n\t\thandle.providerError = message;\n\t\tthis.recordRuntimeError(handle, message);\n\t}\n\n\tprivate clearProviderError(handle: RuntimeHandle): void {\n\t\tconst providerError = handle.providerError;\n\t\tif (providerError === undefined) return;\n\t\thandle.providerError = undefined;\n\t\tif (handle.error === providerError) handle.error = undefined;\n\t\tif (handle.attention.get(\"error\") === providerError) handle.attention.delete(\"error\");\n\t\tif (this.runtimes.get(handle.key) === handle) this.scheduleFleetSnapshot();\n\t}\n\n\tprivate pruneCompletedBackgroundAgents(handle: RuntimeHandle): void {\n\t\tconst evictable = [...handle.backgroundAgents.values()]\n\t\t\t.map((agent, index) => {\n\t\t\t\tconst startedAtMs = Date.parse(agent.startedAt);\n\t\t\t\treturn { agent, index, startedAtMs: Number.isFinite(startedAtMs) ? startedAtMs : 0 };\n\t\t\t})\n\t\t\t.filter(({ agent }) => agent.status !== \"running\")\n\t\t\t.sort((a, b) => a.startedAtMs - b.startedAtMs || a.index - b.index);\n\t\tconst excess = evictable.length - MAX_COMPLETED_BACKGROUND_AGENTS;\n\t\tif (excess <= 0) return;\n\t\tfor (const { agent } of evictable.slice(0, excess)) {\n\t\t\thandle.backgroundAgents.delete(agent.agentId);\n\t\t}\n\t}\n\n\tprivate fallbackState(handle: RuntimeHandle): SessionStateDto {\n\t\tconst previous = handle.lastState;\n\t\treturn {\n\t\t\t...previous,\n\t\t\tsessionId: previous?.sessionId ?? handle.key,\n\t\t\ttasks: previous?.tasks ? [...previous.tasks] : [],\n\t\t\tthinkingLevel: previous?.thinkingLevel ?? \"off\",\n\t\t\tisStreaming: previous?.isStreaming ?? false,\n\t\t\tisRetrying: previous?.isRetrying ?? false,\n\t\t\tretryAttempt: previous?.retryAttempt ?? 0,\n\t\t\tisCompacting: previous?.isCompacting ?? false,\n\t\t\tsteeringMode: previous?.steeringMode ?? \"all\",\n\t\t\tfollowUpMode: previous?.followUpMode ?? \"all\",\n\t\t\tsessionFile: previous?.sessionFile ?? handle.sessionFileFallback,\n\t\t\tautoCompactionEnabled: previous?.autoCompactionEnabled ?? false,\n\t\t\tmessageCount: previous?.messageCount ?? 0,\n\t\t\tpendingMessageCount: previous?.pendingMessageCount ?? 0,\n\t\t};\n\t}\n\n\tprivate describeFleetRuntime(handle: RuntimeHandle): FleetRuntimeSnapshotDto {\n\t\tconst state = this.fallbackState(handle);\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()].map((agent) => ({ ...agent })),\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n\n\tprivate async seedBackgroundAgents(handle: RuntimeHandle): Promise<void> {\n\t\ttry {\n\t\t\tconst agents = (await handle.client.listBackgroundAgents()) as unknown as BackgroundAgentDto[];\n\t\t\tfor (const agent of agents) {\n\t\t\t\thandle.backgroundAgents.set(agent.agentId, agent);\n\t\t\t}\n\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} background-agent registry unavailable: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Snapshot a runtime for the fleet endpoint. */\n\tasync describe(handle: RuntimeHandle): Promise<RuntimeInfoDto> {\n\t\tconst previousFleetRuntime =\n\t\t\tthis.runtimes.get(handle.key) === handle ? this.describeFleetRuntime(handle) : undefined;\n\t\tlet fleetRuntimeEnriched = false;\n\t\tlet state: SessionStateDto;\n\t\ttry {\n\t\t\tconst authoritative = (await handle.client.getState()) as unknown as SessionStateDto;\n\t\t\tconst fallback = this.fallbackState(handle);\n\t\t\tstate = {\n\t\t\t\t...fallback,\n\t\t\t\t...authoritative,\n\t\t\t\tsessionId: authoritative.sessionId ?? fallback.sessionId,\n\t\t\t\tsessionFile: authoritative.sessionFile ?? fallback.sessionFile,\n\t\t\t\ttasks: authoritative.tasks ?? fallback.tasks,\n\t\t\t};\n\t\t\thandle.lastState = state;\n\t\t\tfleetRuntimeEnriched = true;\n\t\t\tif (handle.error?.startsWith(\"RPC process\")) {\n\t\t\t\thandle.error = undefined;\n\t\t\t\thandle.attention.delete(\"error\");\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.recordRuntimeError(handle, message);\n\t\t\tthis.logger(`runtime ${handle.key} state unavailable for fleet card: ${message}`);\n\t\t\tstate = this.fallbackState(handle);\n\t\t}\n\t\tlet stats: RuntimeStatsSummaryDto | undefined;\n\t\ttry {\n\t\t\tconst sessionStats = await handle.client.getSessionStats();\n\t\t\tstats = { tokensTotal: sessionStats.tokens.total, cost: sessionStats.cost };\n\t\t\tif (sessionStats.contextUsage) {\n\t\t\t\thandle.lastState = { ...this.fallbackState(handle), contextUsage: sessionStats.contextUsage };\n\t\t\t\tfleetRuntimeEnriched = true;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} stats unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tlet lastAssistantText: string | undefined;\n\t\ttry {\n\t\t\tconst text = await handle.client.getLastAssistantText();\n\t\t\tlastAssistantText = text ? text.slice(0, 200) : undefined;\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} last assistant text unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tfleetRuntimeEnriched &&\n\t\t\tpreviousFleetRuntime &&\n\t\t\tthis.runtimes.get(handle.key) === handle &&\n\t\t\t!isDeepStrictEqual(previousFleetRuntime, this.describeFleetRuntime(handle))\n\t\t) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tstats,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()],\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tlastAssistantText,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n}\n"]}
|
|
@@ -32,11 +32,13 @@ const FLEET_SNAPSHOT_EVENT_TYPES = new Set([
|
|
|
32
32
|
"agent_end",
|
|
33
33
|
"auto_compaction_start",
|
|
34
34
|
"auto_compaction_end",
|
|
35
|
+
"auto_retry_start",
|
|
35
36
|
"auto_retry_end",
|
|
36
37
|
"background_agent_start",
|
|
37
38
|
"background_agent_end",
|
|
38
39
|
"extension_ui_request",
|
|
39
40
|
"extension_ui_response_handled",
|
|
41
|
+
"message_end",
|
|
40
42
|
"message_start",
|
|
41
43
|
"parent_paused_for_background_agents",
|
|
42
44
|
"runtime_removed",
|
|
@@ -384,6 +386,7 @@ export class RuntimePool {
|
|
|
384
386
|
handle.attention.delete("suggest");
|
|
385
387
|
handle.attention.delete("error");
|
|
386
388
|
handle.error = undefined;
|
|
389
|
+
handle.providerError = undefined;
|
|
387
390
|
}
|
|
388
391
|
if (type === "suggest_next") {
|
|
389
392
|
// suggest_next as the ending action = "your move": mark needs-attention
|
|
@@ -398,8 +401,26 @@ export class RuntimePool {
|
|
|
398
401
|
if (event.errorMessage)
|
|
399
402
|
this.recordRuntimeError(handle, String(event.errorMessage));
|
|
400
403
|
}
|
|
404
|
+
if (type === "message_end") {
|
|
405
|
+
const message = event.message;
|
|
406
|
+
if (message && typeof message === "object" && !Array.isArray(message)) {
|
|
407
|
+
const assistant = message;
|
|
408
|
+
if (assistant.role === "assistant" && assistant.stopReason === "error") {
|
|
409
|
+
const rawError = assistant.errorMessage;
|
|
410
|
+
const error = typeof rawError === "string" && rawError.trim().length > 0 ? rawError : "Unknown error";
|
|
411
|
+
this.recordProviderError(handle, error);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (type === "auto_retry_start")
|
|
416
|
+
this.clearProviderError(handle);
|
|
417
|
+
if (type === "auto_compaction_start" && event.reason === "overflow")
|
|
418
|
+
this.clearProviderError(handle);
|
|
401
419
|
if (type === "auto_retry_end" && event.success === false && event.finalError) {
|
|
402
|
-
this.
|
|
420
|
+
this.recordProviderError(handle, String(event.finalError));
|
|
421
|
+
}
|
|
422
|
+
else if (type === "auto_retry_end" && event.success === true) {
|
|
423
|
+
this.clearProviderError(handle);
|
|
403
424
|
}
|
|
404
425
|
if (type === "auto_compaction_end" && event.errorMessage) {
|
|
405
426
|
this.recordRuntimeError(handle, String(event.errorMessage));
|
|
@@ -467,6 +488,14 @@ export class RuntimePool {
|
|
|
467
488
|
case "auto_compaction_end":
|
|
468
489
|
state.isCompacting = false;
|
|
469
490
|
break;
|
|
491
|
+
case "auto_retry_start":
|
|
492
|
+
state.isRetrying = true;
|
|
493
|
+
state.retryAttempt = typeof event.attempt === "number" ? event.attempt : state.retryAttempt;
|
|
494
|
+
break;
|
|
495
|
+
case "auto_retry_end":
|
|
496
|
+
state.isRetrying = false;
|
|
497
|
+
state.retryAttempt = 0;
|
|
498
|
+
break;
|
|
470
499
|
case "tasks_update":
|
|
471
500
|
if (Array.isArray(event.tasks))
|
|
472
501
|
state.tasks = [...event.tasks];
|
|
@@ -487,6 +516,22 @@ export class RuntimePool {
|
|
|
487
516
|
if (this.runtimes.get(handle.key) === handle)
|
|
488
517
|
this.scheduleFleetSnapshot();
|
|
489
518
|
}
|
|
519
|
+
recordProviderError(handle, message) {
|
|
520
|
+
handle.providerError = message;
|
|
521
|
+
this.recordRuntimeError(handle, message);
|
|
522
|
+
}
|
|
523
|
+
clearProviderError(handle) {
|
|
524
|
+
const providerError = handle.providerError;
|
|
525
|
+
if (providerError === undefined)
|
|
526
|
+
return;
|
|
527
|
+
handle.providerError = undefined;
|
|
528
|
+
if (handle.error === providerError)
|
|
529
|
+
handle.error = undefined;
|
|
530
|
+
if (handle.attention.get("error") === providerError)
|
|
531
|
+
handle.attention.delete("error");
|
|
532
|
+
if (this.runtimes.get(handle.key) === handle)
|
|
533
|
+
this.scheduleFleetSnapshot();
|
|
534
|
+
}
|
|
490
535
|
pruneCompletedBackgroundAgents(handle) {
|
|
491
536
|
const evictable = [...handle.backgroundAgents.values()]
|
|
492
537
|
.map((agent, index) => {
|
|
@@ -510,6 +555,8 @@ export class RuntimePool {
|
|
|
510
555
|
tasks: previous?.tasks ? [...previous.tasks] : [],
|
|
511
556
|
thinkingLevel: previous?.thinkingLevel ?? "off",
|
|
512
557
|
isStreaming: previous?.isStreaming ?? false,
|
|
558
|
+
isRetrying: previous?.isRetrying ?? false,
|
|
559
|
+
retryAttempt: previous?.retryAttempt ?? 0,
|
|
513
560
|
isCompacting: previous?.isCompacting ?? false,
|
|
514
561
|
steeringMode: previous?.steeringMode ?? "all",
|
|
515
562
|
followUpMode: previous?.followUpMode ?? "all",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-pool.js","sourceRoot":"","sources":["../../src/server/runtime-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RPC runtime pool — one `dreb --mode rpc` child process per live session.\n *\n * dreb's RPC mode is strictly one-session-per-process (switch_session repoints\n * the same process; it never multiplexes), so the pool spawns N children keyed\n * by an opaque runtime key. The telegram bridge is the in-repo precedent.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { RpcClient, type RpcExitInfo } from \"@dreb/coding-agent/rpc\";\nimport {\n\ttype BackgroundAgentDto,\n\ttype FleetRuntimeSnapshotDto,\n\ttype FleetSnapshotEventDto,\n\tMAX_COMPLETED_BACKGROUND_AGENTS,\n\ttype RuntimeInfoDto,\n\ttype RuntimeStatsSummaryDto,\n\ttype SessionStateDto,\n} from \"../shared/protocol.js\";\n\n/** Resolve the absolute path to the dreb CLI (RpcClient defaults to a cwd-relative path). */\nexport function resolveDrebCliPath(): string {\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\treturn join(dirname(fileURLToPath(resolved)), \"cli.js\");\n}\n\nfunction formatRpcExit(info: RpcExitInfo): string {\n\tif (info.error) return `RPC process failed: ${info.error.message}`;\n\treturn `RPC process exited (code ${info.code}, signal ${info.signal})`;\n}\n\nexport type RuntimeEventListener = (key: string, event: Record<string, unknown>) => void;\n\n/** Listener for coalesced, synchronous fleet runtime snapshots. */\nexport type FleetSnapshotListener = (event: FleetSnapshotEventDto) => void;\n\nexport { MAX_COMPLETED_BACKGROUND_AGENTS };\n\ninterface RpcDashboardSnapshot {\n\tsnapshotId: string;\n\tstate: SessionStateDto;\n\tmessages: unknown[];\n\tbackgroundAgents: BackgroundAgentDto[];\n}\n\ntype DashboardSnapshotClient = RpcClient & { getDashboardSnapshot(): Promise<RpcDashboardSnapshot> };\n\nexport interface DashboardRuntimeSnapshot {\n\tkey: string;\n\tbarrierSeq: number;\n\tsnapshot: RpcDashboardSnapshot;\n}\n\nexport interface RuntimeHandle {\n\tkey: string;\n\tcwd: string;\n\tclient: RpcClient;\n\t/** Session start time (ms epoch) — stable tiebreak for deterministic fleet ordering. */\n\tcreatedAt: number;\n\tlastActivity: number;\n\t/** Needs-attention sources, keyed so they can be cleared independently. */\n\tattention: Map<string, string>;\n\t/** Last runtime-level error, persisted server-side so fleet refreshes stay honest. */\n\terror?: string;\n\t/** Last authoritative state, patched only with event-derivable fields between RPC reads. */\n\tlastState?: SessionStateDto;\n\t/** Resume-path fallback; events must never invent or overwrite session identity. */\n\tsessionFileFallback?: string;\n\t/** Background agents seen via events (agentId → latest info). */\n\tbackgroundAgents: Map<string, BackgroundAgentDto>;\n}\n\nexport const DEFAULT_DASHBOARD_BARRIER_TTL_MS = 5 * 60_000;\nexport const DEFAULT_DASHBOARD_BARRIER_LIMIT = 1000;\nexport const DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS = 200;\n\n/** Events that mutate a field carried by the lightweight fleet snapshot. */\nconst FLEET_SNAPSHOT_EVENT_TYPES = new Set([\n\t\"agent_start\",\n\t\"agent_end\",\n\t\"auto_compaction_start\",\n\t\"auto_compaction_end\",\n\t\"auto_retry_end\",\n\t\"background_agent_start\",\n\t\"background_agent_end\",\n\t\"extension_ui_request\",\n\t\"extension_ui_response_handled\",\n\t\"message_start\",\n\t\"parent_paused_for_background_agents\",\n\t\"runtime_removed\",\n\t\"session_name_changed\",\n\t\"suggest_next\",\n\t\"tasks_update\",\n]);\n\nexport interface RuntimePoolOptions {\n\tcliPath?: string;\n\t/** Extra args for every runtime (e.g. --provider). */\n\tbaseArgs?: string[];\n\t/** RpcClient factory override for tests. */\n\tclientFactory?: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tlogger?: (line: string) => void;\n\t/** Bounds unclaimed RPC snapshot ordering records. */\n\tdashboardBarrierTtlMs?: number;\n\tdashboardBarrierLimit?: number;\n\t/** Injectable clock for deterministic barrier-expiry tests. */\n\tnow?: () => number;\n\t/** Coalescing delay for event-derived fleet snapshot emissions. */\n\tfleetSnapshotDebounceMs?: number;\n}\n\ninterface DashboardBarrier {\n\tseq: number;\n\trecordedAt: number;\n}\n\nexport class RuntimePool {\n\tprivate readonly runtimes = new Map<string, RuntimeHandle>();\n\tprivate readonly listeners: RuntimeEventListener[] = [];\n\tprivate readonly fleetSnapshotListeners: FleetSnapshotListener[] = [];\n\tprivate readonly cliPath: string;\n\tprivate readonly baseArgs: string[];\n\tprivate readonly clientFactory: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tprivate readonly logger: (line: string) => void;\n\t/**\n\t * A single lazily-spawned utility runtime used to service settings/model/\n\t * agent-type endpoints when no user session is live. Kept out of `runtimes`\n\t * (and therefore out of the fleet) so it never shows as a session card.\n\t */\n\tprivate readonly utilities = new Map<string, RuntimeHandle>();\n\tprivate readonly utilityPromises = new Map<string, Promise<RuntimeHandle>>();\n\tprivate readonly starting = new Set<RuntimeHandle>();\n\tprivate readonly startupPromises = new Set<Promise<unknown>>();\n\tprivate readonly exitedHandles = new WeakSet<RuntimeHandle>();\n\t/** Snapshot ordering records observed synchronously from RpcClient stdout. */\n\tprivate readonly dashboardBarriers = new Map<string, DashboardBarrier>();\n\tprivate readonly dashboardBarrierTtlMs: number;\n\tprivate readonly dashboardBarrierLimit: number;\n\tprivate readonly now: () => number;\n\tprivate readonly fleetSnapshotDebounceMs: number;\n\tprivate dashboardBarrierPruneTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate fleetSnapshotTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate closing = false;\n\n\tconstructor(options: RuntimePoolOptions = {}) {\n\t\tthis.cliPath = options.cliPath ?? resolveDrebCliPath();\n\t\tthis.baseArgs = options.baseArgs ?? [];\n\t\tthis.clientFactory =\n\t\t\toptions.clientFactory ?? ((o) => new RpcClient({ cliPath: o.cliPath, cwd: o.cwd, args: o.args }));\n\t\tthis.logger = options.logger ?? ((line) => console.warn(`[dashboard] ${line}`));\n\t\tthis.dashboardBarrierTtlMs = options.dashboardBarrierTtlMs ?? DEFAULT_DASHBOARD_BARRIER_TTL_MS;\n\t\tthis.dashboardBarrierLimit = options.dashboardBarrierLimit ?? DEFAULT_DASHBOARD_BARRIER_LIMIT;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.fleetSnapshotDebounceMs = options.fleetSnapshotDebounceMs ?? DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS;\n\t}\n\n\t/** Subscribe to events from every runtime, tagged with the runtime key. */\n\tonEvent(listener: RuntimeEventListener): () => void {\n\t\tthis.listeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.listeners.indexOf(listener);\n\t\t\tif (i !== -1) this.listeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/** Subscribe to debounced, in-memory fleet snapshots. */\n\tonFleetSnapshot(listener: FleetSnapshotListener): () => void {\n\t\tthis.fleetSnapshotListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.fleetSnapshotListeners.indexOf(listener);\n\t\t\tif (i !== -1) this.fleetSnapshotListeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Build the fleet's live-runtime view without RPC or disk access. Map\n\t * insertion order is retained intentionally; the UI owns presentation order.\n\t */\n\tfleetSnapshot(): FleetRuntimeSnapshotDto[] {\n\t\treturn [...this.runtimes.values()].map((handle) => this.describeFleetRuntime(handle));\n\t}\n\n\tlist(): RuntimeHandle[] {\n\t\treturn [...this.runtimes.values()];\n\t}\n\n\tget(key: string): RuntimeHandle | undefined {\n\t\treturn this.runtimes.get(key);\n\t}\n\n\t/**\n\t * Record the EventHub sequence synchronously when the RPC snapshot marker\n\t * arrives. The marker line precedes its response on stdout, so this runs\n\t * before the RpcClient response continuation even across separate chunks.\n\t */\n\trecordDashboardBarrier(runtimeKey: string, snapshotId: string, seq: number): void {\n\t\tthis.pruneDashboardBarriers();\n\t\tthis.dashboardBarriers.set(this.dashboardBarrierKey(runtimeKey, snapshotId), { seq, recordedAt: this.now() });\n\t\tthis.pruneDashboardBarriers();\n\t}\n\n\t/**\n\t * Capture a parent-session recovery snapshot and pair it with the sequence\n\t * captured at its RPC marker. This deliberately does not infer ordering from\n\t * await: later EventHub publications naturally have higher sequence numbers.\n\t */\n\tasync snapshotDashboard(handle: RuntimeHandle): Promise<DashboardRuntimeSnapshot> {\n\t\tconst snapshot = await (handle.client as DashboardSnapshotClient).getDashboardSnapshot();\n\t\tthis.pruneDashboardBarriers();\n\t\tconst barrierKey = this.dashboardBarrierKey(handle.key, snapshot.snapshotId);\n\t\tconst barrier = this.dashboardBarriers.get(barrierKey);\n\t\tthis.dashboardBarriers.delete(barrierKey);\n\t\tthis.scheduleDashboardBarrierPrune();\n\t\tif (!barrier) {\n\t\t\tthrow new Error(`Dashboard snapshot ${snapshot.snapshotId} arrived without its ordering barrier`);\n\t\t}\n\t\t// This is an authoritative RPC baseline, so it may legitimately lower the\n\t\t// count after a fork/rewind instead of retaining an event-derived maximum.\n\t\thandle.lastState = snapshot.state;\n\t\tthis.scheduleFleetSnapshot();\n\t\treturn { key: handle.key, barrierSeq: barrier.seq, snapshot };\n\t}\n\n\tprivate dashboardBarrierKey(runtimeKey: string, snapshotId: string): string {\n\t\treturn `${runtimeKey}\\0${snapshotId}`;\n\t}\n\n\tprivate pruneDashboardBarriers(): void {\n\t\tconst oldestAllowed = this.now() - this.dashboardBarrierTtlMs;\n\t\tfor (const [snapshotId, barrier] of this.dashboardBarriers) {\n\t\t\tif (barrier.recordedAt < oldestAllowed) this.dashboardBarriers.delete(snapshotId);\n\t\t}\n\t\twhile (this.dashboardBarriers.size > this.dashboardBarrierLimit) {\n\t\t\tconst oldest = this.dashboardBarriers.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.dashboardBarriers.delete(oldest);\n\t\t}\n\t\tthis.scheduleDashboardBarrierPrune();\n\t}\n\n\tprivate scheduleDashboardBarrierPrune(): void {\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tlet oldest: DashboardBarrier | undefined;\n\t\tfor (const barrier of this.dashboardBarriers.values()) {\n\t\t\tif (!oldest || barrier.recordedAt < oldest.recordedAt) oldest = barrier;\n\t\t}\n\t\tif (!oldest) return;\n\t\tconst delay = Math.max(1, oldest.recordedAt + this.dashboardBarrierTtlMs - this.now() + 1);\n\t\tthis.dashboardBarrierPruneTimer = setTimeout(() => {\n\t\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\t\tthis.pruneDashboardBarriers();\n\t\t}, delay);\n\t\tthis.dashboardBarrierPruneTimer.unref?.();\n\t}\n\n\tprivate scheduleFleetSnapshot(): void {\n\t\tif (this.closing) return;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = setTimeout(() => {\n\t\t\tthis.fleetSnapshotTimer = undefined;\n\t\t\tif (this.closing) return;\n\t\t\tconst event: FleetSnapshotEventDto = { type: \"fleet_snapshot\", runtimes: this.fleetSnapshot() };\n\t\t\tfor (const listener of this.fleetSnapshotListeners) {\n\t\t\t\ttry {\n\t\t\t\t\tlistener(event);\n\t\t\t\t} catch {\n\t\t\t\t\t// An SSE bridge subscriber must not break the pool's event loop.\n\t\t\t\t}\n\t\t\t}\n\t\t}, this.fleetSnapshotDebounceMs);\n\t\tthis.fleetSnapshotTimer.unref?.();\n\t}\n\n\t/** Spawn a new runtime in `cwd`, optionally opening an existing session file. */\n\tasync create(cwd: string, sessionPath?: string): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst key = randomBytes(6).toString(\"hex\");\n\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\tif (sessionPath) args.push(\"--session\", sessionPath);\n\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\tconst handle: RuntimeHandle = {\n\t\t\tkey,\n\t\t\tcwd,\n\t\t\tclient,\n\t\t\tcreatedAt: this.now(),\n\t\t\tlastActivity: this.now(),\n\t\t\tattention: new Map(),\n\t\t\tsessionFileFallback: sessionPath,\n\t\t\tbackgroundAgents: new Map(),\n\t\t};\n\t\tclient.onEvent((event) => this.handleEvent(handle, event as unknown as Record<string, unknown>));\n\t\tclient.onExit((info) => this.handleRuntimeExit(handle, info));\n\n\t\tconst startup = this.startSessionRuntime(handle);\n\t\tthis.startupPromises.add(startup);\n\t\ttry {\n\t\t\treturn await startup;\n\t\t} finally {\n\t\t\tthis.startupPromises.delete(startup);\n\t\t}\n\t}\n\n\tprivate async startSessionRuntime(handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tawait this.seedBackgroundAgents(handle);\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.runtimes.set(handle.key, handle);\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\t/** Stop a runtime and remove it from the pool. */\n\tasync stop(key: string): Promise<boolean> {\n\t\tconst handle = this.runtimes.get(key);\n\t\tif (!handle) return false;\n\t\tthis.handleEvent(handle, { type: \"runtime_removed\" });\n\t\tthis.runtimes.delete(key);\n\t\tthis.scheduleFleetSnapshot();\n\t\tawait handle.client.stop();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Return any live runtime suitable for process-global settings work, spawning\n\t * a hidden utility runtime (in the home directory) if no user session exists.\n\t * This is what lets the settings page — models, agent types, defaults — work\n\t * with zero sessions open, instead of 503-ing.\n\t */\n\tasync ensureUtilityRuntime(cwd = homedir()): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst existing = this.utilities.get(cwd);\n\t\tif (existing) return existing;\n\t\tlet promise = this.utilityPromises.get(cwd);\n\t\tif (!promise) {\n\t\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\t\tconst handle: RuntimeHandle = {\n\t\t\t\tkey: `utility:${cwd}`,\n\t\t\t\tcwd,\n\t\t\t\tclient,\n\t\t\t\tcreatedAt: this.now(),\n\t\t\t\tlastActivity: this.now(),\n\t\t\t\tattention: new Map(),\n\t\t\t\tbackgroundAgents: new Map(),\n\t\t\t};\n\t\t\tconst startup = this.startUtilityRuntime(cwd, handle);\n\t\t\tthis.startupPromises.add(startup);\n\t\t\tpromise = startup\n\t\t\t\t.catch((err) => {\n\t\t\t\t\t// Allow a retry on the next request instead of caching the failure.\n\t\t\t\t\tthis.utilityPromises.delete(cwd);\n\t\t\t\t\tthrow err;\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis.startupPromises.delete(startup);\n\t\t\t\t});\n\t\t\tthis.utilityPromises.set(cwd, promise);\n\t\t}\n\t\treturn promise;\n\t}\n\n\tprivate async startUtilityRuntime(cwd: string, handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.utilities.set(cwd, handle);\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\tasync stopAll(): Promise<void> {\n\t\tthis.closing = true;\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = undefined;\n\t\tthis.fleetSnapshotListeners.length = 0;\n\t\tthis.dashboardBarriers.clear();\n\t\tconst handles = new Set<RuntimeHandle>([\n\t\t\t...this.runtimes.values(),\n\t\t\t...this.utilities.values(),\n\t\t\t...this.starting.values(),\n\t\t]);\n\t\tconst startupPromises = new Set<Promise<unknown>>([...this.startupPromises, ...this.utilityPromises.values()]);\n\t\tthis.runtimes.clear();\n\t\tthis.utilities.clear();\n\t\tthis.utilityPromises.clear();\n\t\tawait Promise.allSettled([...handles].map((handle) => handle.client.stop()));\n\t\tawait Promise.allSettled([...startupPromises]);\n\t}\n\n\tprivate handleRuntimeExit(handle: RuntimeHandle, info: RpcExitInfo): void {\n\t\tif (this.closing || this.exitedHandles.has(handle) || !this.isLiveHandle(handle)) return;\n\t\tthis.exitedHandles.add(handle);\n\t\tconst message = formatRpcExit(info);\n\t\tthis.recordRuntimeError(handle, message);\n\t\tthis.logger(`runtime ${handle.key} ${message}`);\n\t\tthis.handleEvent(handle, { type: \"agent_end\", messages: [], aborted: true, errorMessage: message });\n\t}\n\n\tprivate isLiveHandle(handle: RuntimeHandle): boolean {\n\t\treturn (\n\t\t\tthis.runtimes.get(handle.key) === handle ||\n\t\t\tthis.utilities.get(handle.cwd) === handle ||\n\t\t\tthis.starting.has(handle)\n\t\t);\n\t}\n\n\tprivate handleEvent(handle: RuntimeHandle, event: Record<string, unknown>): void {\n\t\tconst type = event.type as string;\n\t\tif (type !== \"dashboard_snapshot_barrier\") {\n\t\t\thandle.lastActivity = this.now();\n\t\t\tthis.updateStateFromEvent(handle, event, type);\n\t\t}\n\n\t\t// Track needs-attention sources: extension UI requests, parent\n\t\t// paused, error states.\n\t\tif (type === \"extension_ui_request\") {\n\t\t\tconst method = event.method as string;\n\t\t\tif (method === \"select\" || method === \"confirm\" || method === \"input\" || method === \"editor\") {\n\t\t\t\thandle.attention.set(`ui:${event.id}`, `extension ${method} awaiting response`);\n\t\t\t}\n\t\t}\n\t\tif (type === \"extension_ui_response_handled\") {\n\t\t\thandle.attention.delete(`ui:${event.id}`);\n\t\t}\n\t\tif (type === \"agent_start\") {\n\t\t\t// A new turn clears prior UI-request attention (requests were resolved or timed out).\n\t\t\tfor (const k of [...handle.attention.keys()]) {\n\t\t\t\tif (k.startsWith(\"ui:\")) handle.attention.delete(k);\n\t\t\t}\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\thandle.attention.delete(\"suggest\");\n\t\t\thandle.attention.delete(\"error\");\n\t\t\thandle.error = undefined;\n\t\t}\n\t\tif (type === \"suggest_next\") {\n\t\t\t// suggest_next as the ending action = \"your move\": mark needs-attention\n\t\t\t// so the fleet card doesn't read idle. Cleared on the next agent_start.\n\t\t\thandle.attention.set(\"suggest\", \"suggested command awaiting\");\n\t\t}\n\t\tif (type === \"parent_paused_for_background_agents\") {\n\t\t\thandle.attention.set(\"paused\", `paused — ${event.runningAgentCount} background agents running`);\n\t\t}\n\t\tif (type === \"agent_end\") {\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\tif (event.errorMessage) this.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\t\tif (type === \"auto_retry_end\" && event.success === false && event.finalError) {\n\t\t\tthis.recordRuntimeError(handle, String(event.finalError));\n\t\t}\n\t\tif (type === \"auto_compaction_end\" && event.errorMessage) {\n\t\t\tthis.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\n\t\t// Track background agents from lifecycle events.\n\t\tif (type === \"background_agent_start\") {\n\t\t\thandle.backgroundAgents.set(event.agentId as string, {\n\t\t\t\tagentId: event.agentId as string,\n\t\t\t\tagentType: event.agentType as string,\n\t\t\t\ttaskSummary: event.taskSummary as string,\n\t\t\t\tstartedAt: new Date().toISOString(),\n\t\t\t\tstatus: \"running\",\n\t\t\t\tsessionDir: event.sessionDir as string | undefined,\n\t\t\t});\n\t\t}\n\t\tif (type === \"background_agent_end\") {\n\t\t\tconst existing = handle.backgroundAgents.get(event.agentId as string);\n\t\t\tif (existing) {\n\t\t\t\texisting.status = event.success ? \"completed\" : \"failed\";\n\t\t\t\texisting.sessionFile = (event.sessionFile as string | undefined) ?? existing.sessionFile;\n\t\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t\t}\n\t\t}\n\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(handle.key, event);\n\t\t\t} catch {\n\t\t\t\t// A broken SSE subscriber must not break event distribution.\n\t\t\t}\n\t\t}\n\t\tif (this.runtimes.get(handle.key) === handle && FLEET_SNAPSHOT_EVENT_TYPES.has(type)) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t}\n\n\t/**\n\t * Events intentionally patch only fields they can prove. Session identity,\n\t * session file, configuration, and context usage remain from the last RPC\n\t * baseline (or the stable creation fallback) until a later reconciliation.\n\t */\n\tprivate updateStateFromEvent(handle: RuntimeHandle, event: Record<string, unknown>, type: string): void {\n\t\tconst state = this.fallbackState(handle);\n\t\tswitch (type) {\n\t\t\tcase \"agent_start\": {\n\t\t\t\tconst model = event.model;\n\t\t\t\tif (\n\t\t\t\t\tmodel &&\n\t\t\t\t\ttypeof model === \"object\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).provider === \"string\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).id === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tconst nextModel = model as { provider: string; id: string };\n\t\t\t\t\tstate.model =\n\t\t\t\t\t\tstate.model?.provider === nextModel.provider && state.model.id === nextModel.id\n\t\t\t\t\t\t\t? { ...state.model, ...nextModel }\n\t\t\t\t\t\t\t: nextModel;\n\t\t\t\t}\n\t\t\t\tstate.isStreaming = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"agent_end\":\n\t\t\t\tstate.isStreaming = false;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_start\":\n\t\t\t\tstate.isCompacting = true;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_end\":\n\t\t\t\tstate.isCompacting = false;\n\t\t\t\tbreak;\n\t\t\tcase \"tasks_update\":\n\t\t\t\tif (Array.isArray(event.tasks)) state.tasks = [...event.tasks] as SessionStateDto[\"tasks\"];\n\t\t\t\tbreak;\n\t\t\tcase \"session_name_changed\":\n\t\t\t\tif (typeof event.name === \"string\") state.sessionName = event.name;\n\t\t\t\tbreak;\n\t\t\tcase \"message_start\":\n\t\t\t\tstate.messageCount += 1;\n\t\t\t\tbreak;\n\t\t}\n\t\thandle.lastState = state;\n\t}\n\n\tprivate recordRuntimeError(handle: RuntimeHandle, message: string): void {\n\t\thandle.error = message;\n\t\thandle.attention.set(\"error\", message);\n\t\tif (this.runtimes.get(handle.key) === handle) this.scheduleFleetSnapshot();\n\t}\n\n\tprivate pruneCompletedBackgroundAgents(handle: RuntimeHandle): void {\n\t\tconst evictable = [...handle.backgroundAgents.values()]\n\t\t\t.map((agent, index) => {\n\t\t\t\tconst startedAtMs = Date.parse(agent.startedAt);\n\t\t\t\treturn { agent, index, startedAtMs: Number.isFinite(startedAtMs) ? startedAtMs : 0 };\n\t\t\t})\n\t\t\t.filter(({ agent }) => agent.status !== \"running\")\n\t\t\t.sort((a, b) => a.startedAtMs - b.startedAtMs || a.index - b.index);\n\t\tconst excess = evictable.length - MAX_COMPLETED_BACKGROUND_AGENTS;\n\t\tif (excess <= 0) return;\n\t\tfor (const { agent } of evictable.slice(0, excess)) {\n\t\t\thandle.backgroundAgents.delete(agent.agentId);\n\t\t}\n\t}\n\n\tprivate fallbackState(handle: RuntimeHandle): SessionStateDto {\n\t\tconst previous = handle.lastState;\n\t\treturn {\n\t\t\t...previous,\n\t\t\tsessionId: previous?.sessionId ?? handle.key,\n\t\t\ttasks: previous?.tasks ? [...previous.tasks] : [],\n\t\t\tthinkingLevel: previous?.thinkingLevel ?? \"off\",\n\t\t\tisStreaming: previous?.isStreaming ?? false,\n\t\t\tisCompacting: previous?.isCompacting ?? false,\n\t\t\tsteeringMode: previous?.steeringMode ?? \"all\",\n\t\t\tfollowUpMode: previous?.followUpMode ?? \"all\",\n\t\t\tsessionFile: previous?.sessionFile ?? handle.sessionFileFallback,\n\t\t\tautoCompactionEnabled: previous?.autoCompactionEnabled ?? false,\n\t\t\tmessageCount: previous?.messageCount ?? 0,\n\t\t\tpendingMessageCount: previous?.pendingMessageCount ?? 0,\n\t\t};\n\t}\n\n\tprivate describeFleetRuntime(handle: RuntimeHandle): FleetRuntimeSnapshotDto {\n\t\tconst state = this.fallbackState(handle);\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()].map((agent) => ({ ...agent })),\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n\n\tprivate async seedBackgroundAgents(handle: RuntimeHandle): Promise<void> {\n\t\ttry {\n\t\t\tconst agents = (await handle.client.listBackgroundAgents()) as unknown as BackgroundAgentDto[];\n\t\t\tfor (const agent of agents) {\n\t\t\t\thandle.backgroundAgents.set(agent.agentId, agent);\n\t\t\t}\n\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} background-agent registry unavailable: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Snapshot a runtime for the fleet endpoint. */\n\tasync describe(handle: RuntimeHandle): Promise<RuntimeInfoDto> {\n\t\tconst previousFleetRuntime =\n\t\t\tthis.runtimes.get(handle.key) === handle ? this.describeFleetRuntime(handle) : undefined;\n\t\tlet fleetRuntimeEnriched = false;\n\t\tlet state: SessionStateDto;\n\t\ttry {\n\t\t\tconst authoritative = (await handle.client.getState()) as unknown as SessionStateDto;\n\t\t\tconst fallback = this.fallbackState(handle);\n\t\t\tstate = {\n\t\t\t\t...fallback,\n\t\t\t\t...authoritative,\n\t\t\t\tsessionId: authoritative.sessionId ?? fallback.sessionId,\n\t\t\t\tsessionFile: authoritative.sessionFile ?? fallback.sessionFile,\n\t\t\t\ttasks: authoritative.tasks ?? fallback.tasks,\n\t\t\t};\n\t\t\thandle.lastState = state;\n\t\t\tfleetRuntimeEnriched = true;\n\t\t\tif (handle.error?.startsWith(\"RPC process\")) {\n\t\t\t\thandle.error = undefined;\n\t\t\t\thandle.attention.delete(\"error\");\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.recordRuntimeError(handle, message);\n\t\t\tthis.logger(`runtime ${handle.key} state unavailable for fleet card: ${message}`);\n\t\t\tstate = this.fallbackState(handle);\n\t\t}\n\t\tlet stats: RuntimeStatsSummaryDto | undefined;\n\t\ttry {\n\t\t\tconst sessionStats = await handle.client.getSessionStats();\n\t\t\tstats = { tokensTotal: sessionStats.tokens.total, cost: sessionStats.cost };\n\t\t\tif (sessionStats.contextUsage) {\n\t\t\t\thandle.lastState = { ...this.fallbackState(handle), contextUsage: sessionStats.contextUsage };\n\t\t\t\tfleetRuntimeEnriched = true;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} stats unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tlet lastAssistantText: string | undefined;\n\t\ttry {\n\t\t\tconst text = await handle.client.getLastAssistantText();\n\t\t\tlastAssistantText = text ? text.slice(0, 200) : undefined;\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} last assistant text unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tfleetRuntimeEnriched &&\n\t\t\tpreviousFleetRuntime &&\n\t\t\tthis.runtimes.get(handle.key) === handle &&\n\t\t\t!isDeepStrictEqual(previousFleetRuntime, this.describeFleetRuntime(handle))\n\t\t) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tstats,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()],\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tlastAssistantText,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime-pool.js","sourceRoot":"","sources":["../../src/server/runtime-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RPC runtime pool — one `dreb --mode rpc` child process per live session.\n *\n * dreb's RPC mode is strictly one-session-per-process (switch_session repoints\n * the same process; it never multiplexes), so the pool spawns N children keyed\n * by an opaque runtime key. The telegram bridge is the in-repo precedent.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { RpcClient, type RpcExitInfo } from \"@dreb/coding-agent/rpc\";\nimport {\n\ttype BackgroundAgentDto,\n\ttype FleetRuntimeSnapshotDto,\n\ttype FleetSnapshotEventDto,\n\tMAX_COMPLETED_BACKGROUND_AGENTS,\n\ttype RuntimeInfoDto,\n\ttype RuntimeStatsSummaryDto,\n\ttype SessionStateDto,\n} from \"../shared/protocol.js\";\n\n/** Resolve the absolute path to the dreb CLI (RpcClient defaults to a cwd-relative path). */\nexport function resolveDrebCliPath(): string {\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\treturn join(dirname(fileURLToPath(resolved)), \"cli.js\");\n}\n\nfunction formatRpcExit(info: RpcExitInfo): string {\n\tif (info.error) return `RPC process failed: ${info.error.message}`;\n\treturn `RPC process exited (code ${info.code}, signal ${info.signal})`;\n}\n\nexport type RuntimeEventListener = (key: string, event: Record<string, unknown>) => void;\n\n/** Listener for coalesced, synchronous fleet runtime snapshots. */\nexport type FleetSnapshotListener = (event: FleetSnapshotEventDto) => void;\n\nexport { MAX_COMPLETED_BACKGROUND_AGENTS };\n\ninterface RpcDashboardSnapshot {\n\tsnapshotId: string;\n\tstate: SessionStateDto;\n\tmessages: unknown[];\n\tbackgroundAgents: BackgroundAgentDto[];\n}\n\ntype DashboardSnapshotClient = RpcClient & { getDashboardSnapshot(): Promise<RpcDashboardSnapshot> };\n\nexport interface DashboardRuntimeSnapshot {\n\tkey: string;\n\tbarrierSeq: number;\n\tsnapshot: RpcDashboardSnapshot;\n}\n\nexport interface RuntimeHandle {\n\tkey: string;\n\tcwd: string;\n\tclient: RpcClient;\n\t/** Session start time (ms epoch) — stable tiebreak for deterministic fleet ordering. */\n\tcreatedAt: number;\n\tlastActivity: number;\n\t/** Needs-attention sources, keyed so they can be cleared independently. */\n\tattention: Map<string, string>;\n\t/** Last runtime-level error, persisted server-side so fleet refreshes stay honest. */\n\terror?: string;\n\t/** Provider error currently responsible for error/attention; retry may clear only this source. */\n\tproviderError?: string;\n\t/** Last authoritative state, patched only with event-derivable fields between RPC reads. */\n\tlastState?: SessionStateDto;\n\t/** Resume-path fallback; events must never invent or overwrite session identity. */\n\tsessionFileFallback?: string;\n\t/** Background agents seen via events (agentId → latest info). */\n\tbackgroundAgents: Map<string, BackgroundAgentDto>;\n}\n\nexport const DEFAULT_DASHBOARD_BARRIER_TTL_MS = 5 * 60_000;\nexport const DEFAULT_DASHBOARD_BARRIER_LIMIT = 1000;\nexport const DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS = 200;\n\n/** Events that mutate a field carried by the lightweight fleet snapshot. */\nconst FLEET_SNAPSHOT_EVENT_TYPES = new Set([\n\t\"agent_start\",\n\t\"agent_end\",\n\t\"auto_compaction_start\",\n\t\"auto_compaction_end\",\n\t\"auto_retry_start\",\n\t\"auto_retry_end\",\n\t\"background_agent_start\",\n\t\"background_agent_end\",\n\t\"extension_ui_request\",\n\t\"extension_ui_response_handled\",\n\t\"message_end\",\n\t\"message_start\",\n\t\"parent_paused_for_background_agents\",\n\t\"runtime_removed\",\n\t\"session_name_changed\",\n\t\"suggest_next\",\n\t\"tasks_update\",\n]);\n\nexport interface RuntimePoolOptions {\n\tcliPath?: string;\n\t/** Extra args for every runtime (e.g. --provider). */\n\tbaseArgs?: string[];\n\t/** RpcClient factory override for tests. */\n\tclientFactory?: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tlogger?: (line: string) => void;\n\t/** Bounds unclaimed RPC snapshot ordering records. */\n\tdashboardBarrierTtlMs?: number;\n\tdashboardBarrierLimit?: number;\n\t/** Injectable clock for deterministic barrier-expiry tests. */\n\tnow?: () => number;\n\t/** Coalescing delay for event-derived fleet snapshot emissions. */\n\tfleetSnapshotDebounceMs?: number;\n}\n\ninterface DashboardBarrier {\n\tseq: number;\n\trecordedAt: number;\n}\n\nexport class RuntimePool {\n\tprivate readonly runtimes = new Map<string, RuntimeHandle>();\n\tprivate readonly listeners: RuntimeEventListener[] = [];\n\tprivate readonly fleetSnapshotListeners: FleetSnapshotListener[] = [];\n\tprivate readonly cliPath: string;\n\tprivate readonly baseArgs: string[];\n\tprivate readonly clientFactory: (options: { cliPath: string; cwd: string; args: string[] }) => RpcClient;\n\tprivate readonly logger: (line: string) => void;\n\t/**\n\t * A single lazily-spawned utility runtime used to service settings/model/\n\t * agent-type endpoints when no user session is live. Kept out of `runtimes`\n\t * (and therefore out of the fleet) so it never shows as a session card.\n\t */\n\tprivate readonly utilities = new Map<string, RuntimeHandle>();\n\tprivate readonly utilityPromises = new Map<string, Promise<RuntimeHandle>>();\n\tprivate readonly starting = new Set<RuntimeHandle>();\n\tprivate readonly startupPromises = new Set<Promise<unknown>>();\n\tprivate readonly exitedHandles = new WeakSet<RuntimeHandle>();\n\t/** Snapshot ordering records observed synchronously from RpcClient stdout. */\n\tprivate readonly dashboardBarriers = new Map<string, DashboardBarrier>();\n\tprivate readonly dashboardBarrierTtlMs: number;\n\tprivate readonly dashboardBarrierLimit: number;\n\tprivate readonly now: () => number;\n\tprivate readonly fleetSnapshotDebounceMs: number;\n\tprivate dashboardBarrierPruneTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate fleetSnapshotTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate closing = false;\n\n\tconstructor(options: RuntimePoolOptions = {}) {\n\t\tthis.cliPath = options.cliPath ?? resolveDrebCliPath();\n\t\tthis.baseArgs = options.baseArgs ?? [];\n\t\tthis.clientFactory =\n\t\t\toptions.clientFactory ?? ((o) => new RpcClient({ cliPath: o.cliPath, cwd: o.cwd, args: o.args }));\n\t\tthis.logger = options.logger ?? ((line) => console.warn(`[dashboard] ${line}`));\n\t\tthis.dashboardBarrierTtlMs = options.dashboardBarrierTtlMs ?? DEFAULT_DASHBOARD_BARRIER_TTL_MS;\n\t\tthis.dashboardBarrierLimit = options.dashboardBarrierLimit ?? DEFAULT_DASHBOARD_BARRIER_LIMIT;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.fleetSnapshotDebounceMs = options.fleetSnapshotDebounceMs ?? DEFAULT_FLEET_SNAPSHOT_DEBOUNCE_MS;\n\t}\n\n\t/** Subscribe to events from every runtime, tagged with the runtime key. */\n\tonEvent(listener: RuntimeEventListener): () => void {\n\t\tthis.listeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.listeners.indexOf(listener);\n\t\t\tif (i !== -1) this.listeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/** Subscribe to debounced, in-memory fleet snapshots. */\n\tonFleetSnapshot(listener: FleetSnapshotListener): () => void {\n\t\tthis.fleetSnapshotListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst i = this.fleetSnapshotListeners.indexOf(listener);\n\t\t\tif (i !== -1) this.fleetSnapshotListeners.splice(i, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Build the fleet's live-runtime view without RPC or disk access. Map\n\t * insertion order is retained intentionally; the UI owns presentation order.\n\t */\n\tfleetSnapshot(): FleetRuntimeSnapshotDto[] {\n\t\treturn [...this.runtimes.values()].map((handle) => this.describeFleetRuntime(handle));\n\t}\n\n\tlist(): RuntimeHandle[] {\n\t\treturn [...this.runtimes.values()];\n\t}\n\n\tget(key: string): RuntimeHandle | undefined {\n\t\treturn this.runtimes.get(key);\n\t}\n\n\t/**\n\t * Record the EventHub sequence synchronously when the RPC snapshot marker\n\t * arrives. The marker line precedes its response on stdout, so this runs\n\t * before the RpcClient response continuation even across separate chunks.\n\t */\n\trecordDashboardBarrier(runtimeKey: string, snapshotId: string, seq: number): void {\n\t\tthis.pruneDashboardBarriers();\n\t\tthis.dashboardBarriers.set(this.dashboardBarrierKey(runtimeKey, snapshotId), { seq, recordedAt: this.now() });\n\t\tthis.pruneDashboardBarriers();\n\t}\n\n\t/**\n\t * Capture a parent-session recovery snapshot and pair it with the sequence\n\t * captured at its RPC marker. This deliberately does not infer ordering from\n\t * await: later EventHub publications naturally have higher sequence numbers.\n\t */\n\tasync snapshotDashboard(handle: RuntimeHandle): Promise<DashboardRuntimeSnapshot> {\n\t\tconst snapshot = await (handle.client as DashboardSnapshotClient).getDashboardSnapshot();\n\t\tthis.pruneDashboardBarriers();\n\t\tconst barrierKey = this.dashboardBarrierKey(handle.key, snapshot.snapshotId);\n\t\tconst barrier = this.dashboardBarriers.get(barrierKey);\n\t\tthis.dashboardBarriers.delete(barrierKey);\n\t\tthis.scheduleDashboardBarrierPrune();\n\t\tif (!barrier) {\n\t\t\tthrow new Error(`Dashboard snapshot ${snapshot.snapshotId} arrived without its ordering barrier`);\n\t\t}\n\t\t// This is an authoritative RPC baseline, so it may legitimately lower the\n\t\t// count after a fork/rewind instead of retaining an event-derived maximum.\n\t\thandle.lastState = snapshot.state;\n\t\tthis.scheduleFleetSnapshot();\n\t\treturn { key: handle.key, barrierSeq: barrier.seq, snapshot };\n\t}\n\n\tprivate dashboardBarrierKey(runtimeKey: string, snapshotId: string): string {\n\t\treturn `${runtimeKey}\\0${snapshotId}`;\n\t}\n\n\tprivate pruneDashboardBarriers(): void {\n\t\tconst oldestAllowed = this.now() - this.dashboardBarrierTtlMs;\n\t\tfor (const [snapshotId, barrier] of this.dashboardBarriers) {\n\t\t\tif (barrier.recordedAt < oldestAllowed) this.dashboardBarriers.delete(snapshotId);\n\t\t}\n\t\twhile (this.dashboardBarriers.size > this.dashboardBarrierLimit) {\n\t\t\tconst oldest = this.dashboardBarriers.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.dashboardBarriers.delete(oldest);\n\t\t}\n\t\tthis.scheduleDashboardBarrierPrune();\n\t}\n\n\tprivate scheduleDashboardBarrierPrune(): void {\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tlet oldest: DashboardBarrier | undefined;\n\t\tfor (const barrier of this.dashboardBarriers.values()) {\n\t\t\tif (!oldest || barrier.recordedAt < oldest.recordedAt) oldest = barrier;\n\t\t}\n\t\tif (!oldest) return;\n\t\tconst delay = Math.max(1, oldest.recordedAt + this.dashboardBarrierTtlMs - this.now() + 1);\n\t\tthis.dashboardBarrierPruneTimer = setTimeout(() => {\n\t\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\t\tthis.pruneDashboardBarriers();\n\t\t}, delay);\n\t\tthis.dashboardBarrierPruneTimer.unref?.();\n\t}\n\n\tprivate scheduleFleetSnapshot(): void {\n\t\tif (this.closing) return;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = setTimeout(() => {\n\t\t\tthis.fleetSnapshotTimer = undefined;\n\t\t\tif (this.closing) return;\n\t\t\tconst event: FleetSnapshotEventDto = { type: \"fleet_snapshot\", runtimes: this.fleetSnapshot() };\n\t\t\tfor (const listener of this.fleetSnapshotListeners) {\n\t\t\t\ttry {\n\t\t\t\t\tlistener(event);\n\t\t\t\t} catch {\n\t\t\t\t\t// An SSE bridge subscriber must not break the pool's event loop.\n\t\t\t\t}\n\t\t\t}\n\t\t}, this.fleetSnapshotDebounceMs);\n\t\tthis.fleetSnapshotTimer.unref?.();\n\t}\n\n\t/** Spawn a new runtime in `cwd`, optionally opening an existing session file. */\n\tasync create(cwd: string, sessionPath?: string): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst key = randomBytes(6).toString(\"hex\");\n\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\tif (sessionPath) args.push(\"--session\", sessionPath);\n\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\tconst handle: RuntimeHandle = {\n\t\t\tkey,\n\t\t\tcwd,\n\t\t\tclient,\n\t\t\tcreatedAt: this.now(),\n\t\t\tlastActivity: this.now(),\n\t\t\tattention: new Map(),\n\t\t\tsessionFileFallback: sessionPath,\n\t\t\tbackgroundAgents: new Map(),\n\t\t};\n\t\tclient.onEvent((event) => this.handleEvent(handle, event as unknown as Record<string, unknown>));\n\t\tclient.onExit((info) => this.handleRuntimeExit(handle, info));\n\n\t\tconst startup = this.startSessionRuntime(handle);\n\t\tthis.startupPromises.add(startup);\n\t\ttry {\n\t\t\treturn await startup;\n\t\t} finally {\n\t\t\tthis.startupPromises.delete(startup);\n\t\t}\n\t}\n\n\tprivate async startSessionRuntime(handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tawait this.seedBackgroundAgents(handle);\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.runtimes.set(handle.key, handle);\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\t/** Stop a runtime and remove it from the pool. */\n\tasync stop(key: string): Promise<boolean> {\n\t\tconst handle = this.runtimes.get(key);\n\t\tif (!handle) return false;\n\t\tthis.handleEvent(handle, { type: \"runtime_removed\" });\n\t\tthis.runtimes.delete(key);\n\t\tthis.scheduleFleetSnapshot();\n\t\tawait handle.client.stop();\n\t\treturn true;\n\t}\n\n\t/**\n\t * Return any live runtime suitable for process-global settings work, spawning\n\t * a hidden utility runtime (in the home directory) if no user session exists.\n\t * This is what lets the settings page — models, agent types, defaults — work\n\t * with zero sessions open, instead of 503-ing.\n\t */\n\tasync ensureUtilityRuntime(cwd = homedir()): Promise<RuntimeHandle> {\n\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\tconst existing = this.utilities.get(cwd);\n\t\tif (existing) return existing;\n\t\tlet promise = this.utilityPromises.get(cwd);\n\t\tif (!promise) {\n\t\t\tconst args = [\"--ui\", \"dashboard\", ...this.baseArgs];\n\t\t\tconst client = this.clientFactory({ cliPath: this.cliPath, cwd, args });\n\t\t\tconst handle: RuntimeHandle = {\n\t\t\t\tkey: `utility:${cwd}`,\n\t\t\t\tcwd,\n\t\t\t\tclient,\n\t\t\t\tcreatedAt: this.now(),\n\t\t\t\tlastActivity: this.now(),\n\t\t\t\tattention: new Map(),\n\t\t\t\tbackgroundAgents: new Map(),\n\t\t\t};\n\t\t\tconst startup = this.startUtilityRuntime(cwd, handle);\n\t\t\tthis.startupPromises.add(startup);\n\t\t\tpromise = startup\n\t\t\t\t.catch((err) => {\n\t\t\t\t\t// Allow a retry on the next request instead of caching the failure.\n\t\t\t\t\tthis.utilityPromises.delete(cwd);\n\t\t\t\t\tthrow err;\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis.startupPromises.delete(startup);\n\t\t\t\t});\n\t\t\tthis.utilityPromises.set(cwd, promise);\n\t\t}\n\t\treturn promise;\n\t}\n\n\tprivate async startUtilityRuntime(cwd: string, handle: RuntimeHandle): Promise<RuntimeHandle> {\n\t\tthis.starting.add(handle);\n\t\ttry {\n\t\t\tif (this.closing) throw new Error(\"Runtime pool is closing\");\n\t\t\tawait handle.client.start();\n\t\t\tif (this.closing) {\n\t\t\t\tawait handle.client.stop();\n\t\t\t\tthrow new Error(\"Runtime pool is closing\");\n\t\t\t}\n\t\t\tthis.utilities.set(cwd, handle);\n\t\t\treturn handle;\n\t\t} finally {\n\t\t\tthis.starting.delete(handle);\n\t\t}\n\t}\n\n\tasync stopAll(): Promise<void> {\n\t\tthis.closing = true;\n\t\tif (this.dashboardBarrierPruneTimer) clearTimeout(this.dashboardBarrierPruneTimer);\n\t\tthis.dashboardBarrierPruneTimer = undefined;\n\t\tif (this.fleetSnapshotTimer) clearTimeout(this.fleetSnapshotTimer);\n\t\tthis.fleetSnapshotTimer = undefined;\n\t\tthis.fleetSnapshotListeners.length = 0;\n\t\tthis.dashboardBarriers.clear();\n\t\tconst handles = new Set<RuntimeHandle>([\n\t\t\t...this.runtimes.values(),\n\t\t\t...this.utilities.values(),\n\t\t\t...this.starting.values(),\n\t\t]);\n\t\tconst startupPromises = new Set<Promise<unknown>>([...this.startupPromises, ...this.utilityPromises.values()]);\n\t\tthis.runtimes.clear();\n\t\tthis.utilities.clear();\n\t\tthis.utilityPromises.clear();\n\t\tawait Promise.allSettled([...handles].map((handle) => handle.client.stop()));\n\t\tawait Promise.allSettled([...startupPromises]);\n\t}\n\n\tprivate handleRuntimeExit(handle: RuntimeHandle, info: RpcExitInfo): void {\n\t\tif (this.closing || this.exitedHandles.has(handle) || !this.isLiveHandle(handle)) return;\n\t\tthis.exitedHandles.add(handle);\n\t\tconst message = formatRpcExit(info);\n\t\tthis.recordRuntimeError(handle, message);\n\t\tthis.logger(`runtime ${handle.key} ${message}`);\n\t\tthis.handleEvent(handle, { type: \"agent_end\", messages: [], aborted: true, errorMessage: message });\n\t}\n\n\tprivate isLiveHandle(handle: RuntimeHandle): boolean {\n\t\treturn (\n\t\t\tthis.runtimes.get(handle.key) === handle ||\n\t\t\tthis.utilities.get(handle.cwd) === handle ||\n\t\t\tthis.starting.has(handle)\n\t\t);\n\t}\n\n\tprivate handleEvent(handle: RuntimeHandle, event: Record<string, unknown>): void {\n\t\tconst type = event.type as string;\n\t\tif (type !== \"dashboard_snapshot_barrier\") {\n\t\t\thandle.lastActivity = this.now();\n\t\t\tthis.updateStateFromEvent(handle, event, type);\n\t\t}\n\n\t\t// Track needs-attention sources: extension UI requests, parent\n\t\t// paused, error states.\n\t\tif (type === \"extension_ui_request\") {\n\t\t\tconst method = event.method as string;\n\t\t\tif (method === \"select\" || method === \"confirm\" || method === \"input\" || method === \"editor\") {\n\t\t\t\thandle.attention.set(`ui:${event.id}`, `extension ${method} awaiting response`);\n\t\t\t}\n\t\t}\n\t\tif (type === \"extension_ui_response_handled\") {\n\t\t\thandle.attention.delete(`ui:${event.id}`);\n\t\t}\n\t\tif (type === \"agent_start\") {\n\t\t\t// A new turn clears prior UI-request attention (requests were resolved or timed out).\n\t\t\tfor (const k of [...handle.attention.keys()]) {\n\t\t\t\tif (k.startsWith(\"ui:\")) handle.attention.delete(k);\n\t\t\t}\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\thandle.attention.delete(\"suggest\");\n\t\t\thandle.attention.delete(\"error\");\n\t\t\thandle.error = undefined;\n\t\t\thandle.providerError = undefined;\n\t\t}\n\t\tif (type === \"suggest_next\") {\n\t\t\t// suggest_next as the ending action = \"your move\": mark needs-attention\n\t\t\t// so the fleet card doesn't read idle. Cleared on the next agent_start.\n\t\t\thandle.attention.set(\"suggest\", \"suggested command awaiting\");\n\t\t}\n\t\tif (type === \"parent_paused_for_background_agents\") {\n\t\t\thandle.attention.set(\"paused\", `paused — ${event.runningAgentCount} background agents running`);\n\t\t}\n\t\tif (type === \"agent_end\") {\n\t\t\thandle.attention.delete(\"paused\");\n\t\t\tif (event.errorMessage) this.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\t\tif (type === \"message_end\") {\n\t\t\tconst message = event.message;\n\t\t\tif (message && typeof message === \"object\" && !Array.isArray(message)) {\n\t\t\t\tconst assistant = message as Record<string, unknown>;\n\t\t\t\tif (assistant.role === \"assistant\" && assistant.stopReason === \"error\") {\n\t\t\t\t\tconst rawError = assistant.errorMessage;\n\t\t\t\t\tconst error = typeof rawError === \"string\" && rawError.trim().length > 0 ? rawError : \"Unknown error\";\n\t\t\t\t\tthis.recordProviderError(handle, error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (type === \"auto_retry_start\") this.clearProviderError(handle);\n\t\tif (type === \"auto_compaction_start\" && event.reason === \"overflow\") this.clearProviderError(handle);\n\t\tif (type === \"auto_retry_end\" && event.success === false && event.finalError) {\n\t\t\tthis.recordProviderError(handle, String(event.finalError));\n\t\t} else if (type === \"auto_retry_end\" && event.success === true) {\n\t\t\tthis.clearProviderError(handle);\n\t\t}\n\t\tif (type === \"auto_compaction_end\" && event.errorMessage) {\n\t\t\tthis.recordRuntimeError(handle, String(event.errorMessage));\n\t\t}\n\n\t\t// Track background agents from lifecycle events.\n\t\tif (type === \"background_agent_start\") {\n\t\t\thandle.backgroundAgents.set(event.agentId as string, {\n\t\t\t\tagentId: event.agentId as string,\n\t\t\t\tagentType: event.agentType as string,\n\t\t\t\ttaskSummary: event.taskSummary as string,\n\t\t\t\tstartedAt: new Date().toISOString(),\n\t\t\t\tstatus: \"running\",\n\t\t\t\tsessionDir: event.sessionDir as string | undefined,\n\t\t\t});\n\t\t}\n\t\tif (type === \"background_agent_end\") {\n\t\t\tconst existing = handle.backgroundAgents.get(event.agentId as string);\n\t\t\tif (existing) {\n\t\t\t\texisting.status = event.success ? \"completed\" : \"failed\";\n\t\t\t\texisting.sessionFile = (event.sessionFile as string | undefined) ?? existing.sessionFile;\n\t\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t\t}\n\t\t}\n\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener(handle.key, event);\n\t\t\t} catch {\n\t\t\t\t// A broken SSE subscriber must not break event distribution.\n\t\t\t}\n\t\t}\n\t\tif (this.runtimes.get(handle.key) === handle && FLEET_SNAPSHOT_EVENT_TYPES.has(type)) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t}\n\n\t/**\n\t * Events intentionally patch only fields they can prove. Session identity,\n\t * session file, configuration, and context usage remain from the last RPC\n\t * baseline (or the stable creation fallback) until a later reconciliation.\n\t */\n\tprivate updateStateFromEvent(handle: RuntimeHandle, event: Record<string, unknown>, type: string): void {\n\t\tconst state = this.fallbackState(handle);\n\t\tswitch (type) {\n\t\t\tcase \"agent_start\": {\n\t\t\t\tconst model = event.model;\n\t\t\t\tif (\n\t\t\t\t\tmodel &&\n\t\t\t\t\ttypeof model === \"object\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).provider === \"string\" &&\n\t\t\t\t\ttypeof (model as Record<string, unknown>).id === \"string\"\n\t\t\t\t) {\n\t\t\t\t\tconst nextModel = model as { provider: string; id: string };\n\t\t\t\t\tstate.model =\n\t\t\t\t\t\tstate.model?.provider === nextModel.provider && state.model.id === nextModel.id\n\t\t\t\t\t\t\t? { ...state.model, ...nextModel }\n\t\t\t\t\t\t\t: nextModel;\n\t\t\t\t}\n\t\t\t\tstate.isStreaming = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"agent_end\":\n\t\t\t\tstate.isStreaming = false;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_start\":\n\t\t\t\tstate.isCompacting = true;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_compaction_end\":\n\t\t\t\tstate.isCompacting = false;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_retry_start\":\n\t\t\t\tstate.isRetrying = true;\n\t\t\t\tstate.retryAttempt = typeof event.attempt === \"number\" ? event.attempt : state.retryAttempt;\n\t\t\t\tbreak;\n\t\t\tcase \"auto_retry_end\":\n\t\t\t\tstate.isRetrying = false;\n\t\t\t\tstate.retryAttempt = 0;\n\t\t\t\tbreak;\n\t\t\tcase \"tasks_update\":\n\t\t\t\tif (Array.isArray(event.tasks)) state.tasks = [...event.tasks] as SessionStateDto[\"tasks\"];\n\t\t\t\tbreak;\n\t\t\tcase \"session_name_changed\":\n\t\t\t\tif (typeof event.name === \"string\") state.sessionName = event.name;\n\t\t\t\tbreak;\n\t\t\tcase \"message_start\":\n\t\t\t\tstate.messageCount += 1;\n\t\t\t\tbreak;\n\t\t}\n\t\thandle.lastState = state;\n\t}\n\n\tprivate recordRuntimeError(handle: RuntimeHandle, message: string): void {\n\t\thandle.error = message;\n\t\thandle.attention.set(\"error\", message);\n\t\tif (this.runtimes.get(handle.key) === handle) this.scheduleFleetSnapshot();\n\t}\n\n\tprivate recordProviderError(handle: RuntimeHandle, message: string): void {\n\t\thandle.providerError = message;\n\t\tthis.recordRuntimeError(handle, message);\n\t}\n\n\tprivate clearProviderError(handle: RuntimeHandle): void {\n\t\tconst providerError = handle.providerError;\n\t\tif (providerError === undefined) return;\n\t\thandle.providerError = undefined;\n\t\tif (handle.error === providerError) handle.error = undefined;\n\t\tif (handle.attention.get(\"error\") === providerError) handle.attention.delete(\"error\");\n\t\tif (this.runtimes.get(handle.key) === handle) this.scheduleFleetSnapshot();\n\t}\n\n\tprivate pruneCompletedBackgroundAgents(handle: RuntimeHandle): void {\n\t\tconst evictable = [...handle.backgroundAgents.values()]\n\t\t\t.map((agent, index) => {\n\t\t\t\tconst startedAtMs = Date.parse(agent.startedAt);\n\t\t\t\treturn { agent, index, startedAtMs: Number.isFinite(startedAtMs) ? startedAtMs : 0 };\n\t\t\t})\n\t\t\t.filter(({ agent }) => agent.status !== \"running\")\n\t\t\t.sort((a, b) => a.startedAtMs - b.startedAtMs || a.index - b.index);\n\t\tconst excess = evictable.length - MAX_COMPLETED_BACKGROUND_AGENTS;\n\t\tif (excess <= 0) return;\n\t\tfor (const { agent } of evictable.slice(0, excess)) {\n\t\t\thandle.backgroundAgents.delete(agent.agentId);\n\t\t}\n\t}\n\n\tprivate fallbackState(handle: RuntimeHandle): SessionStateDto {\n\t\tconst previous = handle.lastState;\n\t\treturn {\n\t\t\t...previous,\n\t\t\tsessionId: previous?.sessionId ?? handle.key,\n\t\t\ttasks: previous?.tasks ? [...previous.tasks] : [],\n\t\t\tthinkingLevel: previous?.thinkingLevel ?? \"off\",\n\t\t\tisStreaming: previous?.isStreaming ?? false,\n\t\t\tisRetrying: previous?.isRetrying ?? false,\n\t\t\tretryAttempt: previous?.retryAttempt ?? 0,\n\t\t\tisCompacting: previous?.isCompacting ?? false,\n\t\t\tsteeringMode: previous?.steeringMode ?? \"all\",\n\t\t\tfollowUpMode: previous?.followUpMode ?? \"all\",\n\t\t\tsessionFile: previous?.sessionFile ?? handle.sessionFileFallback,\n\t\t\tautoCompactionEnabled: previous?.autoCompactionEnabled ?? false,\n\t\t\tmessageCount: previous?.messageCount ?? 0,\n\t\t\tpendingMessageCount: previous?.pendingMessageCount ?? 0,\n\t\t};\n\t}\n\n\tprivate describeFleetRuntime(handle: RuntimeHandle): FleetRuntimeSnapshotDto {\n\t\tconst state = this.fallbackState(handle);\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()].map((agent) => ({ ...agent })),\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n\n\tprivate async seedBackgroundAgents(handle: RuntimeHandle): Promise<void> {\n\t\ttry {\n\t\t\tconst agents = (await handle.client.listBackgroundAgents()) as unknown as BackgroundAgentDto[];\n\t\t\tfor (const agent of agents) {\n\t\t\t\thandle.backgroundAgents.set(agent.agentId, agent);\n\t\t\t}\n\t\t\tthis.pruneCompletedBackgroundAgents(handle);\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} background-agent registry unavailable: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/** Snapshot a runtime for the fleet endpoint. */\n\tasync describe(handle: RuntimeHandle): Promise<RuntimeInfoDto> {\n\t\tconst previousFleetRuntime =\n\t\t\tthis.runtimes.get(handle.key) === handle ? this.describeFleetRuntime(handle) : undefined;\n\t\tlet fleetRuntimeEnriched = false;\n\t\tlet state: SessionStateDto;\n\t\ttry {\n\t\t\tconst authoritative = (await handle.client.getState()) as unknown as SessionStateDto;\n\t\t\tconst fallback = this.fallbackState(handle);\n\t\t\tstate = {\n\t\t\t\t...fallback,\n\t\t\t\t...authoritative,\n\t\t\t\tsessionId: authoritative.sessionId ?? fallback.sessionId,\n\t\t\t\tsessionFile: authoritative.sessionFile ?? fallback.sessionFile,\n\t\t\t\ttasks: authoritative.tasks ?? fallback.tasks,\n\t\t\t};\n\t\t\thandle.lastState = state;\n\t\t\tfleetRuntimeEnriched = true;\n\t\t\tif (handle.error?.startsWith(\"RPC process\")) {\n\t\t\t\thandle.error = undefined;\n\t\t\t\thandle.attention.delete(\"error\");\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.recordRuntimeError(handle, message);\n\t\t\tthis.logger(`runtime ${handle.key} state unavailable for fleet card: ${message}`);\n\t\t\tstate = this.fallbackState(handle);\n\t\t}\n\t\tlet stats: RuntimeStatsSummaryDto | undefined;\n\t\ttry {\n\t\t\tconst sessionStats = await handle.client.getSessionStats();\n\t\t\tstats = { tokensTotal: sessionStats.tokens.total, cost: sessionStats.cost };\n\t\t\tif (sessionStats.contextUsage) {\n\t\t\t\thandle.lastState = { ...this.fallbackState(handle), contextUsage: sessionStats.contextUsage };\n\t\t\t\tfleetRuntimeEnriched = true;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} stats unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tlet lastAssistantText: string | undefined;\n\t\ttry {\n\t\t\tconst text = await handle.client.getLastAssistantText();\n\t\t\tlastAssistantText = text ? text.slice(0, 200) : undefined;\n\t\t} catch (err) {\n\t\t\tthis.logger(\n\t\t\t\t`runtime ${handle.key} last assistant text unavailable for fleet card: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tfleetRuntimeEnriched &&\n\t\t\tpreviousFleetRuntime &&\n\t\t\tthis.runtimes.get(handle.key) === handle &&\n\t\t\t!isDeepStrictEqual(previousFleetRuntime, this.describeFleetRuntime(handle))\n\t\t) {\n\t\t\tthis.scheduleFleetSnapshot();\n\t\t}\n\t\treturn {\n\t\t\tkey: handle.key,\n\t\t\tcwd: handle.cwd,\n\t\t\tstate,\n\t\t\tstats,\n\t\t\tbackgroundAgents: [...handle.backgroundAgents.values()],\n\t\t\tneedsAttention: handle.attention.size > 0,\n\t\t\terror: handle.error,\n\t\t\tlastAssistantText,\n\t\t\tcreatedAt: new Date(handle.createdAt).toISOString(),\n\t\t\tlastActivity: new Date(handle.lastActivity).toISOString(),\n\t\t};\n\t}\n}\n"]}
|
|
@@ -176,6 +176,10 @@ export interface SessionStateDto {
|
|
|
176
176
|
tasks: SessionTaskDto[];
|
|
177
177
|
thinkingLevel: string;
|
|
178
178
|
isStreaming: boolean;
|
|
179
|
+
/** True while automatic retry classification, backoff, or execution is active. */
|
|
180
|
+
isRetrying?: boolean;
|
|
181
|
+
/** Current automatic retry attempt, or 0 outside an emitted retry attempt. */
|
|
182
|
+
retryAttempt?: number;
|
|
179
183
|
isCompacting: boolean;
|
|
180
184
|
steeringMode: "all" | "one-at-a-time";
|
|
181
185
|
followUpMode: "all" | "one-at-a-time";
|