@ian-pascoe/pi-codemode 0.1.0

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.
@@ -0,0 +1,1297 @@
1
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
+ import type { Usage } from "@earendil-works/pi-ai";
3
+ import { truncateHead } from "@earendil-works/pi-coding-agent";
4
+ import { transformCodeModeCell } from "./codemode-cell-transform.js";
5
+ import { CodeModeWorkerProcess } from "./codemode-deno-process.js";
6
+ import { formatCodeModePresentationData } from "./codemode-presentation-output.js";
7
+ import type { CodeModeRuntime, CodeModeTimerHandle } from "./codemode-runtime.js";
8
+ import type { CodeModeResultSpillWriter } from "./codemode-session-files.js";
9
+ import {
10
+ createCodeModeFailure,
11
+ createCodeModePending,
12
+ createCodeModeSuccess,
13
+ parseCodeModeJsonValue,
14
+ type CodeModeErrorCode,
15
+ type CodeModeExecuteParameters,
16
+ type CodeModeJsonValue,
17
+ type CodeModePresentationSnapshot,
18
+ type CodeModeResult,
19
+ type CodeModeResultDetails,
20
+ type CodeModeToolOperationMetadata,
21
+ } from "./codemode-tool-contract.js";
22
+ import {
23
+ CODEMODE_WORKER_MESSAGE_LIMIT_BYTES,
24
+ type CodeModeWorkerRequest,
25
+ type CodeModeWorkerResponse,
26
+ type CodeModeWorkerToolSettlement,
27
+ } from "./codemode-worker-protocol.js";
28
+
29
+ const CODEMODE_MAX_TERMINAL_RECORDS = 64;
30
+ const CODEMODE_PRESENTED_NESTED_TOOL_LIMIT = 20;
31
+ const CODEMODE_ACTIVE_TOOL_NAME_LIMIT = 32;
32
+ const CODEMODE_PROGRESS_REFRESH_MS = 1_000;
33
+ const CODEMODE_RESULT_PRESENTATION_MAX_BYTES = 50 * 1024;
34
+ const CODEMODE_RESULT_PRESENTATION_MAX_LINES = 2_000;
35
+ const CODEMODE_WATCHDOG_GRACE_MS = 100;
36
+ const INVALID_CODEMODE_SESSION_ID = "invalid-session-id";
37
+
38
+ /** Branded identifier for one retained CodeMode Session. */
39
+ export type CodeModeSessionId = string & { readonly CodeModeSessionId: unique symbol };
40
+
41
+ /** Observer state of one settled CodeMode Cell. */
42
+ export type CodeModeObservedCellState = "completed" | "failed" | "cancelled" | "timed_out";
43
+
44
+ /** Immutable read-only facts for the currently running CodeMode Cell. */
45
+ export type CodeModeObservedCurrentCell = {
46
+ /** One-based Cell Ordinal within its CodeMode Session. */
47
+ readonly ordinal: number;
48
+ /** Parent wall-clock start time in Unix-epoch milliseconds. */
49
+ readonly started_at_ms: number;
50
+ /** Bounded unique names currently delegated to registered Pi handlers. */
51
+ readonly active_tool_names: readonly string[];
52
+ /** Exact current nested-call count, including names omitted from the bounded list. */
53
+ readonly active_tool_count: number;
54
+ /** Exact nested-call count accumulated by the current Cell. */
55
+ readonly nested_tool_count: number;
56
+ };
57
+
58
+ /** Immutable read-only facts for the most recently settled CodeMode Cell. */
59
+ export type CodeModeObservedLastCell = {
60
+ /** One-based Cell Ordinal within its CodeMode Session. */
61
+ readonly ordinal: number;
62
+ /** Parent wall-clock start time in Unix-epoch milliseconds. */
63
+ readonly started_at_ms: number;
64
+ /** Parent wall-clock settlement time in Unix-epoch milliseconds. */
65
+ readonly settled_at_ms: number;
66
+ readonly state: CodeModeObservedCellState;
67
+ readonly error_code?: CodeModeErrorCode;
68
+ readonly nested_tool_count: number;
69
+ };
70
+
71
+ /** Immutable read-only facts for one CodeMode Session with at least one Cell. */
72
+ export type CodeModeObservedSession = {
73
+ readonly sessionId: CodeModeSessionId;
74
+ readonly lifecycle: "running" | "idle" | "terminal";
75
+ /** Number of Cells started in this CodeMode Session. */
76
+ readonly cell_count: number;
77
+ /** Parent wall-clock time of the latest execution-visible transition. */
78
+ readonly last_activity_at_ms: number;
79
+ readonly current_cell?: CodeModeObservedCurrentCell;
80
+ readonly last_cell?: CodeModeObservedLastCell;
81
+ readonly terminal_error_code?: CodeModeErrorCode;
82
+ };
83
+
84
+ /** Immutable point-in-time facts used by the ephemeral CodeMode Observer UI. */
85
+ export type CodeModeObserverSnapshot = {
86
+ readonly sessions: readonly CodeModeObservedSession[];
87
+ };
88
+
89
+ /** One idle CodeMode worker failure not represented by an active Cell result. */
90
+ export type CodeModeUnexpectedFailure = {
91
+ readonly sessionId: CodeModeSessionId;
92
+ readonly message: string;
93
+ };
94
+
95
+ /** One guest-originated nested Pi tool call in a Deno microtask batch. */
96
+ export type CodeModeNestedToolCall = {
97
+ readonly callId: string;
98
+ readonly toolName: string;
99
+ readonly input: CodeModeJsonValue;
100
+ };
101
+
102
+ /** One catchable nested Pi tool outcome returned to the guest. */
103
+ export type CodeModeNestedToolResult =
104
+ | { readonly callId: string; readonly outcome: "success"; readonly result: CodeModeJsonValue }
105
+ | {
106
+ readonly callId: string;
107
+ readonly outcome: "error";
108
+ readonly error: { readonly code: string; readonly message: string };
109
+ };
110
+
111
+ /** Recording/update callback supplied to a nested Pi tool batch executor. */
112
+ export type CodeModeNestedToolUpdate = AgentToolResult<unknown>;
113
+
114
+ /** One complete guest batch plus its Cell-scoped cancellation and update capabilities. */
115
+ export type CodeModeNestedToolBatch = {
116
+ readonly sessionId: CodeModeSessionId;
117
+ readonly batchId: string;
118
+ readonly calls: readonly CodeModeNestedToolCall[];
119
+ readonly signal: AbortSignal;
120
+ readonly onUpdate?: (update: CodeModeNestedToolUpdate) => void;
121
+ };
122
+
123
+ /** Nested Pi results and metadata accumulated onto the next outer terminal result. */
124
+ export type CodeModeNestedToolBatchResult = {
125
+ readonly results: readonly CodeModeNestedToolResult[];
126
+ readonly presentation?: readonly {
127
+ readonly callId: string;
128
+ readonly name: string;
129
+ readonly outcome: "success" | "failed" | "cancelled";
130
+ readonly elapsedMs: number;
131
+ }[];
132
+ readonly usage?: Usage;
133
+ readonly addedToolNames?: readonly string[];
134
+ readonly terminate?: boolean;
135
+ };
136
+
137
+ /** Callback seam that adapts one guest job-drain batch to Pi's wrapped tool bridge. */
138
+ export type ExecuteCodeModeNestedToolBatch = (
139
+ batch: CodeModeNestedToolBatch,
140
+ ) => Promise<CodeModeNestedToolBatchResult>;
141
+
142
+ /** Metadata attached to the next outer CodeMode tool result, never its public details. */
143
+ export type CodeModeOuterToolMetadata = CodeModeToolOperationMetadata;
144
+
145
+ /** One coordinator operation result plus one-shot outer Pi metadata when available. */
146
+ export type CodeModeSessionOperationResult = {
147
+ readonly result: CodeModeResult;
148
+ readonly metadata?: CodeModeOuterToolMetadata;
149
+ readonly presentation?: CodeModePresentationSnapshot;
150
+ };
151
+
152
+ /** Construction capabilities and limits for one CodeMode Session coordinator. */
153
+ export type CodeModeSessionCoordinatorOptions = {
154
+ readonly maxSessions: number;
155
+ readonly getToolNames: () => readonly string[];
156
+ readonly executeToolBatch: ExecuteCodeModeNestedToolBatch;
157
+ /** Private Result Spill storage for complete oversized presentation data. */
158
+ readonly resultSpillWriter: CodeModeResultSpillWriter;
159
+ /** Explicit parent clock and Session-ID capabilities. */
160
+ readonly runtime: CodeModeRuntime;
161
+ /** Receives immutable Observer facts after a visible Session transition. */
162
+ readonly onSnapshotChange?: (snapshot: CodeModeObserverSnapshot) => void;
163
+ /** Receives an idle worker failure that no active Cell result represents. */
164
+ readonly onUnexpectedFailure?: (failure: CodeModeUnexpectedFailure) => void;
165
+ };
166
+
167
+ type CodeModeMetadataAccumulator = {
168
+ usage?: Usage;
169
+ readonly addedToolNames: Set<string>;
170
+ terminate: boolean;
171
+ };
172
+
173
+ type MutableCodeModeOuterToolMetadata = {
174
+ usage?: Usage;
175
+ addedToolNames?: readonly string[];
176
+ terminate?: boolean;
177
+ };
178
+
179
+ type ActiveCodeModeCell = {
180
+ readonly cellId: string;
181
+ readonly ordinal: number;
182
+ readonly startedAtMs: number;
183
+ readonly abortController: AbortController;
184
+ readonly completion: Promise<CodeModeResult>;
185
+ readonly resolveCompletion: (result: CodeModeResult) => void;
186
+ readonly metadata: CodeModeMetadataAccumulator;
187
+ readonly nestedTools: CodeModePresentationSnapshot["nested_tools"];
188
+ activeToolNames: readonly string[];
189
+ activeToolCount: number;
190
+ failedNestedToolCount: number;
191
+ nestedToolCount: number;
192
+ succeededNestedToolCount: number;
193
+ acceptsUpdates: boolean;
194
+ settled: boolean;
195
+ progressTimer?: CodeModeTimerHandle;
196
+ watchdog?: CodeModeTimerHandle;
197
+ };
198
+
199
+ type LiveCodeModeSession = {
200
+ readonly state: "live";
201
+ readonly sessionId: CodeModeSessionId;
202
+ readonly worker: CodeModeWorkerProcess;
203
+ lastAccess: number;
204
+ lastActivityAtMs: number;
205
+ cellCount: number;
206
+ latestResult?: CodeModeResult;
207
+ latestPresentation?: CodeModePresentationSnapshot;
208
+ currentCell?: ActiveCodeModeCell;
209
+ lastCell?: CodeModeObservedLastCell;
210
+ availableMetadata?: CodeModeOuterToolMetadata;
211
+ };
212
+
213
+ type TerminalCodeModeSession = {
214
+ readonly state: "terminal";
215
+ readonly sessionId: CodeModeSessionId;
216
+ lastAccess: number;
217
+ readonly lastActivityAtMs: number;
218
+ readonly cellCount: number;
219
+ readonly latestResult: CodeModeResult;
220
+ readonly latestPresentation?: CodeModePresentationSnapshot;
221
+ readonly lastCell?: CodeModeObservedLastCell;
222
+ availableMetadata?: CodeModeOuterToolMetadata;
223
+ };
224
+
225
+ type CodeModeSessionRecord = LiveCodeModeSession | TerminalCodeModeSession;
226
+
227
+ type LocateCodeModeSessionResult = {
228
+ readonly record?: CodeModeSessionRecord;
229
+ readonly failure: CodeModeResult;
230
+ };
231
+
232
+ type FatalCodeModeSessionFailure = {
233
+ readonly code: Extract<CodeModeErrorCode, "timeout" | "cancellation" | "termination" | "runtime">;
234
+ readonly message: string;
235
+ };
236
+
237
+ type ParseCodeModeSessionIdResult =
238
+ | { readonly ok: true; readonly value: CodeModeSessionId }
239
+ | { readonly ok: false };
240
+
241
+ function parseCodeModeSessionId(value: string): ParseCodeModeSessionIdResult {
242
+ if (value.length === 0) return { ok: false };
243
+ // SAFETY: This parser establishes the only CodeMode Session ID invariant (non-empty) before applying the domain brand.
244
+ return { ok: true, value: value as CodeModeSessionId };
245
+ }
246
+
247
+ function invalidCodeModeSessionResult(): CodeModeSessionOperationResult {
248
+ return {
249
+ result: createCodeModeFailure(
250
+ INVALID_CODEMODE_SESSION_ID,
251
+ "unknown",
252
+ "Invalid CodeMode Session ID",
253
+ ),
254
+ };
255
+ }
256
+
257
+ function emptyMetadataAccumulator(): CodeModeMetadataAccumulator {
258
+ return { addedToolNames: new Set(), terminate: false };
259
+ }
260
+
261
+ function combineCodeModeUsage(left: Usage | undefined, right: Usage): Usage {
262
+ if (left === undefined) {
263
+ return {
264
+ ...right,
265
+ cost: { ...right.cost },
266
+ };
267
+ }
268
+ const combined: Usage = {
269
+ input: left.input + right.input,
270
+ output: left.output + right.output,
271
+ cacheRead: left.cacheRead + right.cacheRead,
272
+ cacheWrite: left.cacheWrite + right.cacheWrite,
273
+ totalTokens: left.totalTokens + right.totalTokens,
274
+ cost: {
275
+ input: left.cost.input + right.cost.input,
276
+ output: left.cost.output + right.cost.output,
277
+ cacheRead: left.cost.cacheRead + right.cost.cacheRead,
278
+ cacheWrite: left.cost.cacheWrite + right.cost.cacheWrite,
279
+ total: left.cost.total + right.cost.total,
280
+ },
281
+ };
282
+ if (left.cacheWrite1h !== undefined || right.cacheWrite1h !== undefined) {
283
+ combined.cacheWrite1h = (left.cacheWrite1h ?? 0) + (right.cacheWrite1h ?? 0);
284
+ }
285
+ if (left.reasoning !== undefined || right.reasoning !== undefined) {
286
+ combined.reasoning = (left.reasoning ?? 0) + (right.reasoning ?? 0);
287
+ }
288
+ return combined;
289
+ }
290
+
291
+ function mergeCodeModeOuterMetadata(
292
+ accumulator: CodeModeMetadataAccumulator,
293
+ metadata: CodeModeOuterToolMetadata,
294
+ ): void {
295
+ if (metadata.usage !== undefined) {
296
+ accumulator.usage = combineCodeModeUsage(accumulator.usage, metadata.usage);
297
+ }
298
+ for (const name of metadata.addedToolNames ?? []) accumulator.addedToolNames.add(name);
299
+ if (metadata.terminate === true) accumulator.terminate = true;
300
+ }
301
+
302
+ function finalizeCodeModeMetadata(
303
+ accumulator: CodeModeMetadataAccumulator,
304
+ ): CodeModeOuterToolMetadata | undefined {
305
+ const addedToolNames = [...accumulator.addedToolNames];
306
+ if (accumulator.usage === undefined && addedToolNames.length === 0 && !accumulator.terminate)
307
+ return undefined;
308
+ const metadata: MutableCodeModeOuterToolMetadata = {};
309
+ if (accumulator.usage !== undefined) metadata.usage = accumulator.usage;
310
+ if (addedToolNames.length > 0) metadata.addedToolNames = addedToolNames;
311
+ if (accumulator.terminate) metadata.terminate = true;
312
+ return metadata;
313
+ }
314
+
315
+ /** Owns bounded CodeMode Session records and one isolated Deno process per live Session. */
316
+ export class CodeModeSessionCoordinator {
317
+ private readonly records = new Map<CodeModeSessionId, CodeModeSessionRecord>();
318
+ private readonly runtime: CodeModeRuntime;
319
+ private accessSequence = 0;
320
+ private cellSequence = 0;
321
+ private shuttingDown = false;
322
+ private shutdownPromise?: Promise<void>;
323
+ private readonly activeUpdateCallbacks = new WeakMap<
324
+ ActiveCodeModeCell,
325
+ (update: CodeModeNestedToolUpdate) => void
326
+ >();
327
+ private readonly pendingProcessStops = new Set<Promise<void>>();
328
+
329
+ /** Creates one coordinator from its Pi bridge, limits, and parent runtime capabilities. */
330
+ constructor(private readonly options: CodeModeSessionCoordinatorOptions) {
331
+ this.runtime = options.runtime;
332
+ }
333
+
334
+ /** Starts one Cell, optionally returning before its retained result settles. */
335
+ async execute(
336
+ input: CodeModeExecuteParameters,
337
+ signal?: AbortSignal,
338
+ onUpdate?: (update: CodeModeNestedToolUpdate) => void,
339
+ ): Promise<CodeModeSessionOperationResult> {
340
+ if (this.shuttingDown) {
341
+ const candidateSessionId = input.sessionId ?? this.runtime.createSessionId();
342
+ const parsedSessionId = parseCodeModeSessionId(candidateSessionId);
343
+ if (!parsedSessionId.ok) return invalidCodeModeSessionResult();
344
+ return {
345
+ result: createCodeModeFailure(
346
+ parsedSessionId.value,
347
+ "runtime",
348
+ "CodeMode coordinator is shutting down",
349
+ ),
350
+ };
351
+ }
352
+ let located: LocateCodeModeSessionResult;
353
+ if (input.sessionId === undefined) located = this.createLiveSession();
354
+ else {
355
+ const parsedSessionId = parseCodeModeSessionId(input.sessionId);
356
+ if (!parsedSessionId.ok) return invalidCodeModeSessionResult();
357
+ located = this.findSession(parsedSessionId.value);
358
+ }
359
+ if (located.record === undefined) return { result: located.failure };
360
+ const record = located.record;
361
+ this.touch(record);
362
+ if (record.state === "terminal")
363
+ return this.operationResult(
364
+ record.latestResult,
365
+ this.takeMetadata(record),
366
+ record.latestPresentation,
367
+ );
368
+ if (record.currentCell !== undefined) {
369
+ return {
370
+ result: createCodeModeFailure(
371
+ record.sessionId,
372
+ "busy",
373
+ "CodeMode Session already has an active Cell",
374
+ ),
375
+ };
376
+ }
377
+
378
+ const shouldWait = input.wait !== false;
379
+ const cell = this.createActiveCell(
380
+ record,
381
+ shouldWait && onUpdate !== undefined ? { onUpdate } : {},
382
+ );
383
+ const priorMetadata = this.takeMetadata(record);
384
+ if (priorMetadata !== undefined) mergeCodeModeOuterMetadata(cell.metadata, priorMetadata);
385
+ record.currentCell = cell;
386
+ record.latestResult = createCodeModePending(record.sessionId);
387
+ record.lastActivityAtMs = cell.startedAtMs;
388
+ this.publishObserverSnapshot();
389
+ this.emitCellProgress(record, cell);
390
+ this.scheduleCellProgress(record, cell);
391
+ void this.startCell(record, cell, input);
392
+
393
+ const pending = createCodeModePending(record.sessionId);
394
+ if (!shouldWait) {
395
+ return this.operationResult(pending, undefined, this.runningCellPresentation(cell));
396
+ }
397
+ const abort = (): void => {
398
+ this.fatalizeSession(record, cell, {
399
+ code: "cancellation",
400
+ message: "CodeMode Cell was cancelled",
401
+ });
402
+ };
403
+ if (signal?.aborted === true) abort();
404
+ else signal?.addEventListener("abort", abort, { once: true });
405
+ try {
406
+ const result = await cell.completion;
407
+ const retainedRecord = this.records.get(record.sessionId) ?? record;
408
+ return this.operationResult(
409
+ result,
410
+ this.takeMetadata(retainedRecord),
411
+ retainedRecord.latestPresentation,
412
+ );
413
+ } finally {
414
+ cell.acceptsUpdates = false;
415
+ signal?.removeEventListener("abort", abort);
416
+ }
417
+ }
418
+
419
+ /** Polls the latest retained Cell result without consuming that public result. */
420
+ result(sessionIdValue: string): CodeModeSessionOperationResult {
421
+ const parsedSessionId = parseCodeModeSessionId(sessionIdValue);
422
+ if (!parsedSessionId.ok) return invalidCodeModeSessionResult();
423
+ const sessionId = parsedSessionId.value;
424
+ const record = this.records.get(sessionId);
425
+ if (record === undefined) {
426
+ return { result: createCodeModeFailure(sessionId, "unknown", "Unknown CodeMode Session") };
427
+ }
428
+ this.touch(record);
429
+ if (record.state === "live" && record.currentCell !== undefined) {
430
+ return this.operationResult(
431
+ createCodeModePending(sessionId),
432
+ undefined,
433
+ this.runningCellPresentation(record.currentCell),
434
+ );
435
+ }
436
+ const result = record.latestResult ?? createCodeModePending(sessionId);
437
+ return this.operationResult(result, this.takeMetadata(record), record.latestPresentation);
438
+ }
439
+
440
+ /** Returns immutable, non-authoritative facts for the ephemeral CodeMode Observer UI. */
441
+ inspectObserverSnapshot(): CodeModeObserverSnapshot {
442
+ const sessions = [...this.records.values()]
443
+ .filter((record) => record.cellCount > 0)
444
+ .map((record) => this.observeSession(record));
445
+ return Object.freeze({ sessions: Object.freeze(sessions) });
446
+ }
447
+
448
+ /** Return the shortest currently unique Session prefix, or the full unknown historical ID. */
449
+ formatSessionPrefix(sessionIdValue: string): string {
450
+ const parsed = parseCodeModeSessionId(sessionIdValue);
451
+ if (!parsed.ok || !this.records.has(parsed.value)) return sessionIdValue;
452
+ const sessionIds = [...this.records.keys()];
453
+ let length = Math.min(8, sessionIdValue.length);
454
+ while (
455
+ length < sessionIdValue.length &&
456
+ sessionIds.some(
457
+ (candidate) =>
458
+ candidate !== parsed.value && candidate.startsWith(sessionIdValue.slice(0, length)),
459
+ )
460
+ ) {
461
+ length += 1;
462
+ }
463
+ return sessionIdValue.slice(0, length);
464
+ }
465
+
466
+ /** Force-terminates one live CodeMode Session and retains its cancellation result. */
467
+ async cancel(sessionIdValue: string): Promise<CodeModeSessionOperationResult> {
468
+ const parsedSessionId = parseCodeModeSessionId(sessionIdValue);
469
+ if (!parsedSessionId.ok) return invalidCodeModeSessionResult();
470
+ const sessionId = parsedSessionId.value;
471
+ const record = this.records.get(sessionId);
472
+ if (record === undefined) {
473
+ return { result: createCodeModeFailure(sessionId, "unknown", "Unknown CodeMode Session") };
474
+ }
475
+ this.touch(record);
476
+ if (record.state === "terminal") {
477
+ return this.operationResult(
478
+ createCodeModeSuccess(sessionId),
479
+ undefined,
480
+ record.latestPresentation,
481
+ );
482
+ }
483
+ if (record.currentCell !== undefined) {
484
+ this.fatalizeSession(record, record.currentCell, {
485
+ code: "cancellation",
486
+ message: "CodeMode Session was cancelled",
487
+ });
488
+ } else {
489
+ record.lastActivityAtMs = this.runtime.now();
490
+ record.latestPresentation = {
491
+ version: 1,
492
+ cell_state: "cancelled",
493
+ session_state: "closed",
494
+ elapsed_ms: 0,
495
+ active_tool_names: [],
496
+ active_tool_count: 0,
497
+ nested_tool_count: 0,
498
+ succeeded_nested_tool_count: 0,
499
+ failed_nested_tool_count: 0,
500
+ nested_tools: [],
501
+ omitted_nested_tool_count: 0,
502
+ };
503
+ this.replaceWithTerminal(
504
+ record,
505
+ createCodeModeFailure(sessionId, "cancellation", "CodeMode Session was cancelled"),
506
+ );
507
+ void this.stopWorker(record.worker, "terminate");
508
+ }
509
+ await Promise.allSettled(this.pendingProcessStops);
510
+ const terminal = this.records.get(sessionId);
511
+ return this.operationResult(
512
+ createCodeModeSuccess(sessionId),
513
+ undefined,
514
+ terminal?.latestPresentation,
515
+ );
516
+ }
517
+
518
+ /** Releases every live Deno process; repeated shutdown calls share one completion. */
519
+ shutdown(_reason: string): Promise<void> {
520
+ if (this.shutdownPromise !== undefined) return this.shutdownPromise;
521
+ this.shuttingDown = true;
522
+ this.shutdownPromise = this.shutdownAllSessions();
523
+ return this.shutdownPromise;
524
+ }
525
+
526
+ private async shutdownAllSessions(): Promise<void> {
527
+ for (const record of this.records.values()) {
528
+ if (record.state !== "live") continue;
529
+ if (record.currentCell === undefined) void this.stopWorker(record.worker, "shutdown");
530
+ else {
531
+ this.fatalizeSession(record, record.currentCell, {
532
+ code: "cancellation",
533
+ message: "CodeMode coordinator shut down",
534
+ });
535
+ }
536
+ }
537
+ await Promise.all(this.pendingProcessStops);
538
+ }
539
+
540
+ private createLiveSession(): LocateCodeModeSessionResult {
541
+ const parsedSessionId = parseCodeModeSessionId(this.runtime.createSessionId());
542
+ if (!parsedSessionId.ok) {
543
+ return {
544
+ failure: createCodeModeFailure(
545
+ INVALID_CODEMODE_SESSION_ID,
546
+ "runtime",
547
+ "Pi CodeMode: Session ID capability returned an invalid identifier",
548
+ ),
549
+ };
550
+ }
551
+ const sessionId = parsedSessionId.value;
552
+ this.evictTerminalRecords();
553
+ const liveCount = [...this.records.values()].filter((record) => record.state === "live").length;
554
+ if (liveCount >= this.options.maxSessions) {
555
+ const failure = createCodeModeFailure(
556
+ sessionId,
557
+ "capacity",
558
+ "CodeMode Session capacity is exhausted",
559
+ );
560
+ this.retainTerminalFailure(sessionId, failure);
561
+ return { failure };
562
+ }
563
+
564
+ let worker: CodeModeWorkerProcess;
565
+ try {
566
+ worker = new CodeModeWorkerProcess({
567
+ sessionId,
568
+ runtime: this.runtime,
569
+ onResponse: (response) => this.handleWorkerResponse(sessionId, response),
570
+ onFailure: (message) => this.handleWorkerFailure(sessionId, message),
571
+ });
572
+ } catch (cause) {
573
+ const message =
574
+ cause instanceof Error ? cause.message : "CodeMode Deno process failed to start";
575
+ const failure = createCodeModeFailure(sessionId, "runtime", message);
576
+ this.retainTerminalFailure(sessionId, failure);
577
+ return { failure };
578
+ }
579
+ const record: LiveCodeModeSession = {
580
+ state: "live",
581
+ sessionId,
582
+ worker,
583
+ lastAccess: ++this.accessSequence,
584
+ lastActivityAtMs: this.runtime.now(),
585
+ cellCount: 0,
586
+ };
587
+ this.records.set(sessionId, record);
588
+ return { record, failure: createCodeModePending(sessionId) };
589
+ }
590
+
591
+ private findSession(sessionId: CodeModeSessionId): LocateCodeModeSessionResult {
592
+ const record = this.records.get(sessionId);
593
+ return record === undefined
594
+ ? { failure: createCodeModeFailure(sessionId, "unknown", "Unknown CodeMode Session") }
595
+ : { record, failure: createCodeModePending(sessionId) };
596
+ }
597
+
598
+ private createActiveCell(
599
+ record: LiveCodeModeSession,
600
+ options: { readonly onUpdate?: (update: CodeModeNestedToolUpdate) => void },
601
+ ): ActiveCodeModeCell {
602
+ const completion = Promise.withResolvers<CodeModeResult>();
603
+ const cell: ActiveCodeModeCell = {
604
+ cellId: `cell-${++this.cellSequence}`,
605
+ ordinal: ++record.cellCount,
606
+ startedAtMs: this.runtime.now(),
607
+ abortController: new AbortController(),
608
+ completion: completion.promise,
609
+ resolveCompletion: completion.resolve,
610
+ metadata: emptyMetadataAccumulator(),
611
+ nestedTools: [],
612
+ activeToolNames: [],
613
+ activeToolCount: 0,
614
+ failedNestedToolCount: 0,
615
+ nestedToolCount: 0,
616
+ succeededNestedToolCount: 0,
617
+ acceptsUpdates: options.onUpdate !== undefined,
618
+ settled: false,
619
+ };
620
+ if (options.onUpdate !== undefined) this.activeUpdateCallbacks.set(cell, options.onUpdate);
621
+ return cell;
622
+ }
623
+
624
+ private async startCell(
625
+ record: LiveCodeModeSession,
626
+ cell: ActiveCodeModeCell,
627
+ input: CodeModeExecuteParameters,
628
+ ): Promise<void> {
629
+ const transformed = transformCodeModeCell(input.script);
630
+ if (!transformed.ok) {
631
+ queueMicrotask(() =>
632
+ this.settleReusableCell(
633
+ record,
634
+ cell,
635
+ createCodeModeFailure(record.sessionId, "script", transformed.error.message),
636
+ ),
637
+ );
638
+ return;
639
+ }
640
+ try {
641
+ await record.worker.ready;
642
+ } catch (cause) {
643
+ const message =
644
+ cause instanceof Error ? cause.message : "CodeMode Deno process failed to start";
645
+ this.fatalizeSession(record, cell, { code: "runtime", message });
646
+ return;
647
+ }
648
+ if (!this.isCurrentCell(record, cell)) return;
649
+ if (input.timeoutMs !== undefined) {
650
+ cell.watchdog = this.runtime.setTimeout(() => {
651
+ this.fatalizeSession(record, cell, {
652
+ code: "timeout",
653
+ message: "CodeMode Cell exceeded its timeout",
654
+ });
655
+ }, input.timeoutMs + CODEMODE_WATCHDOG_GRACE_MS);
656
+ }
657
+ try {
658
+ const toolNames = [...new Set(this.options.getToolNames())];
659
+ const requestBase = {
660
+ version: 1,
661
+ type: "execute",
662
+ sessionId: record.sessionId,
663
+ cellId: cell.cellId,
664
+ source: transformed.cell.source,
665
+ internalIdentifierPlaceholder: transformed.cell.internalIdentifierPlaceholder,
666
+ toolNames,
667
+ } as const;
668
+ const request: CodeModeWorkerRequest = requestBase;
669
+ const sent = record.worker.send(request);
670
+ if (!sent.ok) {
671
+ this.settleReusableCell(
672
+ record,
673
+ cell,
674
+ createCodeModeFailure(record.sessionId, "serialization", sent.message),
675
+ );
676
+ }
677
+ } catch (cause) {
678
+ const message = cause instanceof Error ? cause.message : "CodeMode tool snapshot failed";
679
+ this.fatalizeSession(record, cell, { code: "runtime", message });
680
+ }
681
+ }
682
+
683
+ private handleWorkerResponse(
684
+ sessionId: CodeModeSessionId,
685
+ response: CodeModeWorkerResponse,
686
+ ): void {
687
+ const record = this.records.get(sessionId);
688
+ if (record?.state !== "live" || record.currentCell === undefined) return;
689
+ const cell = record.currentCell;
690
+ if (response.type === "cell-result") {
691
+ if (response.cellId !== cell.cellId) {
692
+ this.fatalizeSession(record, cell, {
693
+ code: "runtime",
694
+ message: "CodeMode worker returned a stale Cell result",
695
+ });
696
+ return;
697
+ }
698
+ if (response.resultJson === undefined) {
699
+ this.settleReusableCell(record, cell, createCodeModeSuccess(sessionId));
700
+ return;
701
+ }
702
+ const data = this.parseJsonString(response.resultJson, { allowUndefined: true });
703
+ if (!data.ok) {
704
+ this.settleReusableCell(
705
+ record,
706
+ cell,
707
+ createCodeModeFailure(sessionId, "serialization", data.message),
708
+ );
709
+ return;
710
+ }
711
+ this.settleReusableCell(record, cell, createCodeModeSuccess(sessionId, data.value));
712
+ return;
713
+ }
714
+ if (response.type === "cell-error") {
715
+ if (response.cellId !== cell.cellId) {
716
+ this.fatalizeSession(record, cell, {
717
+ code: "runtime",
718
+ message: "CodeMode worker returned a stale Cell failure",
719
+ });
720
+ return;
721
+ }
722
+ if (response.error.code === "runtime") {
723
+ this.fatalizeSession(record, cell, {
724
+ code: response.error.code,
725
+ message: response.error.message,
726
+ });
727
+ } else {
728
+ this.settleReusableCell(
729
+ record,
730
+ cell,
731
+ createCodeModeFailure(sessionId, response.error.code, response.error.message),
732
+ );
733
+ }
734
+ return;
735
+ }
736
+ if (response.type === "tool-batch") {
737
+ if (response.cellId !== cell.cellId) {
738
+ this.fatalizeSession(record, cell, {
739
+ code: "runtime",
740
+ message: "CodeMode worker returned a stale tool batch",
741
+ });
742
+ return;
743
+ }
744
+ void this.executeNestedToolBatch(record, cell, response);
745
+ return;
746
+ }
747
+ }
748
+
749
+ private async executeNestedToolBatch(
750
+ record: LiveCodeModeSession,
751
+ cell: ActiveCodeModeCell,
752
+ response: Extract<CodeModeWorkerResponse, { readonly type: "tool-batch" }>,
753
+ ): Promise<void> {
754
+ cell.activeToolNames = [...new Set(response.calls.map((call) => call.toolName))];
755
+ cell.activeToolCount = response.calls.length;
756
+ cell.nestedToolCount += response.calls.length;
757
+ record.lastActivityAtMs = this.runtime.now();
758
+ this.publishObserverSnapshot();
759
+
760
+ const parsedCalls: CodeModeNestedToolCall[] = [];
761
+ const earlyResults: CodeModeWorkerToolSettlement[] = [];
762
+ for (const call of response.calls) {
763
+ const input = this.parseJsonString(call.inputJson, { allowUndefined: false });
764
+ if (!input.ok) {
765
+ earlyResults.push({
766
+ callId: call.callId,
767
+ outcome: "error",
768
+ error: { code: "serialization", message: input.message },
769
+ });
770
+ } else {
771
+ parsedCalls.push({ callId: call.callId, toolName: call.toolName, input: input.value });
772
+ }
773
+ }
774
+
775
+ let batchResult: CodeModeNestedToolBatchResult;
776
+ try {
777
+ const batch = {
778
+ sessionId: record.sessionId,
779
+ batchId: response.batchId,
780
+ calls: parsedCalls,
781
+ signal: cell.abortController.signal,
782
+ } as const;
783
+ const onUpdate = (_update: CodeModeNestedToolUpdate): void => {
784
+ if (cell.acceptsUpdates && this.isCurrentCell(record, cell)) {
785
+ this.emitCellProgress(record, cell);
786
+ }
787
+ };
788
+ batchResult =
789
+ parsedCalls.length === 0
790
+ ? { results: [] }
791
+ : await this.options.executeToolBatch(
792
+ cell.acceptsUpdates ? { ...batch, onUpdate } : batch,
793
+ );
794
+ } catch (cause) {
795
+ const message = cause instanceof Error ? cause.message : "Nested Pi tool batch failed";
796
+ batchResult = {
797
+ results: parsedCalls.map((call) => ({
798
+ callId: call.callId,
799
+ outcome: "error",
800
+ error: { code: "runtime", message },
801
+ })),
802
+ };
803
+ }
804
+ if (!this.isCurrentCell(record, cell)) return;
805
+ this.recordNestedToolPresentation(cell, response.calls, batchResult);
806
+ cell.activeToolNames = [];
807
+ cell.activeToolCount = 0;
808
+ record.lastActivityAtMs = this.runtime.now();
809
+ this.publishObserverSnapshot();
810
+ this.emitCellProgress(record, cell);
811
+ mergeCodeModeOuterMetadata(cell.metadata, batchResult);
812
+ if (batchResult.terminate === true) {
813
+ this.fatalizeSession(record, cell, {
814
+ code: "termination",
815
+ message: "Nested Pi tool requested agent termination",
816
+ });
817
+ return;
818
+ }
819
+
820
+ const returned = new Map(batchResult.results.map((result) => [result.callId, result]));
821
+ const settlements: CodeModeWorkerToolSettlement[] = [...earlyResults];
822
+ for (const call of parsedCalls) {
823
+ const result = returned.get(call.callId);
824
+ if (result === undefined) {
825
+ settlements.push({
826
+ callId: call.callId,
827
+ outcome: "error",
828
+ error: { code: "runtime", message: "Nested Pi tool returned no result" },
829
+ });
830
+ continue;
831
+ }
832
+ if (result.outcome === "error") {
833
+ settlements.push({
834
+ callId: call.callId,
835
+ outcome: "error",
836
+ error: {
837
+ code: result.error.code || "runtime",
838
+ message: result.error.message || "Nested Pi tool failed",
839
+ },
840
+ });
841
+ continue;
842
+ }
843
+ const parsedResult = parseCodeModeJsonValue(result.result, {
844
+ maxBytes: CODEMODE_WORKER_MESSAGE_LIMIT_BYTES,
845
+ });
846
+ if (!parsedResult.ok || parsedResult.value === undefined) {
847
+ settlements.push({
848
+ callId: call.callId,
849
+ outcome: "error",
850
+ error: {
851
+ code: "serialization",
852
+ message: parsedResult.ok
853
+ ? "Nested Pi tool returned no JSON value"
854
+ : parsedResult.message,
855
+ },
856
+ });
857
+ } else {
858
+ settlements.push({
859
+ callId: call.callId,
860
+ outcome: "success",
861
+ resultJson: JSON.stringify(parsedResult.value),
862
+ });
863
+ }
864
+ }
865
+ const resultRequest = {
866
+ version: 1,
867
+ type: "tool-results",
868
+ sessionId: record.sessionId,
869
+ cellId: cell.cellId,
870
+ batchId: response.batchId,
871
+ results: settlements,
872
+ } as const;
873
+ const sent = record.worker.send(resultRequest);
874
+ if (sent.ok) return;
875
+ if (sent.message === "CodeMode worker request exceeds 8 MiB") {
876
+ const boundedResults: CodeModeWorkerToolSettlement[] = settlements.map((settlement) =>
877
+ settlement.outcome === "success"
878
+ ? {
879
+ callId: settlement.callId,
880
+ outcome: "error",
881
+ error: {
882
+ code: "serialization",
883
+ message: "Nested Pi tool result exceeds the process message limit",
884
+ },
885
+ }
886
+ : {
887
+ ...settlement,
888
+ error: {
889
+ code: settlement.error.code.slice(0, 128) || "runtime",
890
+ message: settlement.error.message.slice(0, 4_096) || "Nested Pi tool failed",
891
+ },
892
+ },
893
+ );
894
+ const bounded = record.worker.send({ ...resultRequest, results: boundedResults });
895
+ if (bounded.ok) return;
896
+ this.fatalizeSession(record, cell, { code: "runtime", message: bounded.message });
897
+ return;
898
+ }
899
+ this.fatalizeSession(record, cell, { code: "runtime", message: sent.message });
900
+ }
901
+
902
+ private recordNestedToolPresentation(
903
+ cell: ActiveCodeModeCell,
904
+ calls: readonly { readonly callId: string; readonly toolName: string }[],
905
+ batchResult: CodeModeNestedToolBatchResult,
906
+ ): void {
907
+ const results = new Map(batchResult.results.map((result) => [result.callId, result]));
908
+ const presented = new Map(batchResult.presentation?.map((item) => [item.callId, item]) ?? []);
909
+ for (const call of calls) {
910
+ const bridgePresentation = presented.get(call.callId);
911
+ const outcome =
912
+ bridgePresentation?.outcome ??
913
+ (results.get(call.callId)?.outcome === "success" ? "success" : "failed");
914
+ if (outcome === "success") cell.succeededNestedToolCount += 1;
915
+ else cell.failedNestedToolCount += 1;
916
+ if (cell.nestedTools.length >= CODEMODE_PRESENTED_NESTED_TOOL_LIMIT) continue;
917
+ cell.nestedTools.push({
918
+ name: call.toolName.slice(0, 256) || "unknown-tool",
919
+ outcome,
920
+ elapsed_ms: Math.max(
921
+ 0,
922
+ Math.min(Number.MAX_SAFE_INTEGER, Math.round(bridgePresentation?.elapsedMs ?? 0)),
923
+ ),
924
+ });
925
+ }
926
+ }
927
+
928
+ private parseJsonString(
929
+ json: string,
930
+ options: { readonly allowUndefined: boolean },
931
+ ):
932
+ | { readonly ok: true; readonly value: CodeModeJsonValue }
933
+ | { readonly ok: false; readonly message: string } {
934
+ let decoded: unknown;
935
+ try {
936
+ decoded = JSON.parse(json);
937
+ } catch {
938
+ return { ok: false, message: "CodeMode process returned invalid nested JSON" };
939
+ }
940
+ const parsed = parseCodeModeJsonValue(decoded, {
941
+ allowUndefined: options.allowUndefined,
942
+ maxBytes: CODEMODE_WORKER_MESSAGE_LIMIT_BYTES,
943
+ });
944
+ if (!parsed.ok || parsed.value === undefined) {
945
+ return {
946
+ ok: false,
947
+ message: parsed.ok ? "CodeMode process returned no JSON value" : parsed.message,
948
+ };
949
+ }
950
+ return { ok: true, value: parsed.value };
951
+ }
952
+
953
+ private runningCellPresentation(cell: ActiveCodeModeCell): CodeModePresentationSnapshot {
954
+ return this.cellPresentation(cell, "running", "live", this.runtime.now());
955
+ }
956
+
957
+ private cellPresentation(
958
+ cell: ActiveCodeModeCell,
959
+ cellState: CodeModePresentationSnapshot["cell_state"],
960
+ sessionState: CodeModePresentationSnapshot["session_state"],
961
+ observedAtMs: number,
962
+ spillPath?: string,
963
+ ): CodeModePresentationSnapshot {
964
+ const presentation: CodeModePresentationSnapshot = {
965
+ version: 1,
966
+ cell_ordinal: cell.ordinal,
967
+ cell_state: cellState,
968
+ session_state: sessionState,
969
+ elapsed_ms: Math.max(
970
+ 0,
971
+ Math.min(Number.MAX_SAFE_INTEGER, Math.round(observedAtMs - cell.startedAtMs)),
972
+ ),
973
+ active_tool_names: cell.activeToolNames
974
+ .slice(0, CODEMODE_ACTIVE_TOOL_NAME_LIMIT)
975
+ .map((name) => name.slice(0, 256) || "unknown-tool"),
976
+ active_tool_count: cell.activeToolCount,
977
+ nested_tool_count: cell.nestedToolCount,
978
+ succeeded_nested_tool_count: cell.succeededNestedToolCount,
979
+ failed_nested_tool_count: cell.failedNestedToolCount,
980
+ nested_tools: [...cell.nestedTools],
981
+ omitted_nested_tool_count: Math.max(0, cell.nestedToolCount - cell.nestedTools.length),
982
+ };
983
+ return spillPath === undefined ? presentation : { ...presentation, spill_path: spillPath };
984
+ }
985
+
986
+ private settledCellPresentation(
987
+ cell: ActiveCodeModeCell,
988
+ result: CodeModeResult,
989
+ sessionState: CodeModePresentationSnapshot["session_state"],
990
+ ): CodeModePresentationSnapshot {
991
+ let spillPath: string | undefined;
992
+ if (result.result === "success" && result.data !== undefined) {
993
+ const completeOutput = formatCodeModePresentationData(result.data);
994
+ const visible = truncateHead(completeOutput, {
995
+ maxBytes: CODEMODE_RESULT_PRESENTATION_MAX_BYTES,
996
+ maxLines: CODEMODE_RESULT_PRESENTATION_MAX_LINES,
997
+ });
998
+ if (visible.truncated) {
999
+ try {
1000
+ const spill = this.options.resultSpillWriter.writeResultSpill(completeOutput);
1001
+ spillPath = spill.path;
1002
+ void spill.completion.catch(() => undefined);
1003
+ } catch {
1004
+ // Presentation storage failure must not change the successful model-facing Cell result.
1005
+ }
1006
+ }
1007
+ }
1008
+ return this.cellPresentation(
1009
+ cell,
1010
+ this.presentationCellState(result),
1011
+ sessionState,
1012
+ this.runtime.now(),
1013
+ spillPath,
1014
+ );
1015
+ }
1016
+
1017
+ private presentationCellState(
1018
+ result: CodeModeResult,
1019
+ ): CodeModePresentationSnapshot["cell_state"] {
1020
+ if (result.result !== "failed") return result.result === "pending" ? "running" : "completed";
1021
+ if (result.error.code === "timeout") return "timed_out";
1022
+ if (result.error.code === "cancellation") return "cancelled";
1023
+ return "failed";
1024
+ }
1025
+
1026
+ private emitCellProgress(record: LiveCodeModeSession, cell: ActiveCodeModeCell): void {
1027
+ if (!cell.acceptsUpdates || !this.isCurrentCell(record, cell)) return;
1028
+ const result = createCodeModePending(record.sessionId);
1029
+ const details: CodeModeResultDetails = {
1030
+ ...result,
1031
+ presentation: this.runningCellPresentation(cell),
1032
+ };
1033
+ try {
1034
+ this.activeUpdateCallbacks.get(cell)?.({
1035
+ content: [{ type: "text", text: JSON.stringify(result) }],
1036
+ details,
1037
+ });
1038
+ } catch {
1039
+ // A non-authoritative progress renderer cannot alter Cell execution.
1040
+ }
1041
+ }
1042
+
1043
+ private scheduleCellProgress(record: LiveCodeModeSession, cell: ActiveCodeModeCell): void {
1044
+ if (!cell.acceptsUpdates || !this.isCurrentCell(record, cell)) return;
1045
+ cell.progressTimer = this.runtime.setTimeout(() => {
1046
+ delete cell.progressTimer;
1047
+ this.emitCellProgress(record, cell);
1048
+ this.scheduleCellProgress(record, cell);
1049
+ }, CODEMODE_PROGRESS_REFRESH_MS);
1050
+ }
1051
+
1052
+ private settleReusableCell(
1053
+ record: LiveCodeModeSession,
1054
+ cell: ActiveCodeModeCell,
1055
+ result: CodeModeResult,
1056
+ ): void {
1057
+ if (!this.isCurrentCell(record, cell)) return;
1058
+ this.clearCellResources(cell);
1059
+ const presentation = this.settledCellPresentation(cell, result, "live");
1060
+ const settledAtMs = this.runtime.now();
1061
+ record.latestResult = result;
1062
+ record.latestPresentation = presentation;
1063
+ record.lastCell = this.observeSettledCell(cell, result, settledAtMs);
1064
+ record.lastActivityAtMs = settledAtMs;
1065
+ const metadata = finalizeCodeModeMetadata(cell.metadata);
1066
+ if (metadata === undefined) delete record.availableMetadata;
1067
+ else record.availableMetadata = metadata;
1068
+ delete record.currentCell;
1069
+ cell.settled = true;
1070
+ cell.resolveCompletion(result);
1071
+ this.touch(record);
1072
+ this.publishObserverSnapshot();
1073
+ }
1074
+
1075
+ private fatalizeSession(
1076
+ record: LiveCodeModeSession,
1077
+ cell: ActiveCodeModeCell,
1078
+ failure: FatalCodeModeSessionFailure,
1079
+ ): void {
1080
+ if (!this.isCurrentCell(record, cell)) return;
1081
+ if (failure.code === "termination") cell.metadata.terminate = true;
1082
+ cell.abortController.abort();
1083
+ this.clearCellResources(cell);
1084
+ const result = createCodeModeFailure(record.sessionId, failure.code, failure.message);
1085
+ const settledAtMs = this.runtime.now();
1086
+ record.latestPresentation = this.cellPresentation(
1087
+ cell,
1088
+ this.presentationCellState(result),
1089
+ "closed",
1090
+ settledAtMs,
1091
+ );
1092
+ record.lastCell = this.observeSettledCell(cell, result, settledAtMs);
1093
+ record.lastActivityAtMs = settledAtMs;
1094
+ const metadata = finalizeCodeModeMetadata(cell.metadata);
1095
+ this.replaceWithTerminal(record, result, metadata);
1096
+ cell.settled = true;
1097
+ cell.resolveCompletion(result);
1098
+ void this.stopWorker(record.worker, "terminate");
1099
+ }
1100
+
1101
+ private replaceWithTerminal(
1102
+ record: LiveCodeModeSession,
1103
+ result: CodeModeResult,
1104
+ metadata = record.availableMetadata,
1105
+ ): void {
1106
+ let terminal: TerminalCodeModeSession = {
1107
+ state: "terminal",
1108
+ sessionId: record.sessionId,
1109
+ lastAccess: ++this.accessSequence,
1110
+ lastActivityAtMs: record.lastActivityAtMs,
1111
+ cellCount: record.cellCount,
1112
+ latestResult: result,
1113
+ };
1114
+ if (record.latestPresentation !== undefined) {
1115
+ terminal = { ...terminal, latestPresentation: record.latestPresentation };
1116
+ }
1117
+ if (record.lastCell !== undefined) terminal = { ...terminal, lastCell: record.lastCell };
1118
+ if (metadata !== undefined) terminal = { ...terminal, availableMetadata: metadata };
1119
+ this.records.set(record.sessionId, terminal);
1120
+ this.evictTerminalRecords();
1121
+ this.publishObserverSnapshot();
1122
+ }
1123
+
1124
+ private stopWorker(worker: CodeModeWorkerProcess, mode: "shutdown" | "terminate"): Promise<void> {
1125
+ const stop = (mode === "shutdown" ? worker.shutdown() : worker.terminate()).finally(() => {
1126
+ this.pendingProcessStops.delete(stop);
1127
+ });
1128
+ this.pendingProcessStops.add(stop);
1129
+ return stop;
1130
+ }
1131
+
1132
+ private handleWorkerFailure(sessionId: CodeModeSessionId, message: string): void {
1133
+ const record = this.records.get(sessionId);
1134
+ if (record?.state !== "live") return;
1135
+ if (record.currentCell !== undefined) {
1136
+ this.fatalizeSession(record, record.currentCell, { code: "runtime", message });
1137
+ } else {
1138
+ record.lastActivityAtMs = this.runtime.now();
1139
+ record.latestPresentation = {
1140
+ version: 1,
1141
+ cell_state: "failed",
1142
+ session_state: "closed",
1143
+ elapsed_ms: 0,
1144
+ active_tool_names: [],
1145
+ active_tool_count: 0,
1146
+ nested_tool_count: 0,
1147
+ succeeded_nested_tool_count: 0,
1148
+ failed_nested_tool_count: 0,
1149
+ nested_tools: [],
1150
+ omitted_nested_tool_count: 0,
1151
+ };
1152
+ this.replaceWithTerminal(record, createCodeModeFailure(sessionId, "runtime", message));
1153
+ try {
1154
+ this.options.onUnexpectedFailure?.(Object.freeze({ sessionId, message }));
1155
+ } catch {
1156
+ // A non-authoritative Observer failure cannot alter CodeMode Session lifecycle.
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ private isCurrentCell(record: LiveCodeModeSession, cell: ActiveCodeModeCell): boolean {
1162
+ return (
1163
+ !cell.settled && this.records.get(record.sessionId) === record && record.currentCell === cell
1164
+ );
1165
+ }
1166
+
1167
+ private clearCellResources(cell: ActiveCodeModeCell): void {
1168
+ if (cell.progressTimer !== undefined) {
1169
+ this.runtime.clearTimeout(cell.progressTimer);
1170
+ delete cell.progressTimer;
1171
+ }
1172
+ if (cell.watchdog !== undefined) {
1173
+ this.runtime.clearTimeout(cell.watchdog);
1174
+ delete cell.watchdog;
1175
+ }
1176
+ this.activeUpdateCallbacks.delete(cell);
1177
+ cell.acceptsUpdates = false;
1178
+ }
1179
+
1180
+ private takeMetadata(record: CodeModeSessionRecord): CodeModeOuterToolMetadata | undefined {
1181
+ const metadata = record.availableMetadata;
1182
+ delete record.availableMetadata;
1183
+ return metadata;
1184
+ }
1185
+
1186
+ private operationResult(
1187
+ result: CodeModeResult,
1188
+ metadata: CodeModeOuterToolMetadata | undefined,
1189
+ presentation: CodeModePresentationSnapshot | undefined,
1190
+ ): CodeModeSessionOperationResult {
1191
+ if (metadata === undefined) {
1192
+ return presentation === undefined ? { result } : { result, presentation };
1193
+ }
1194
+ if (presentation === undefined) return { result, metadata };
1195
+ return { result, metadata, presentation };
1196
+ }
1197
+
1198
+ private touch(record: CodeModeSessionRecord): void {
1199
+ record.lastAccess = ++this.accessSequence;
1200
+ }
1201
+
1202
+ private observeSession(record: CodeModeSessionRecord): CodeModeObservedSession {
1203
+ const lifecycle =
1204
+ record.state === "terminal"
1205
+ ? "terminal"
1206
+ : record.currentCell === undefined
1207
+ ? "idle"
1208
+ : "running";
1209
+ const currentCell = record.state === "live" ? record.currentCell : undefined;
1210
+ const current_cell =
1211
+ currentCell === undefined
1212
+ ? undefined
1213
+ : Object.freeze({
1214
+ ordinal: currentCell.ordinal,
1215
+ started_at_ms: currentCell.startedAtMs,
1216
+ active_tool_names: Object.freeze([...currentCell.activeToolNames]),
1217
+ active_tool_count: currentCell.activeToolCount,
1218
+ nested_tool_count: currentCell.nestedToolCount,
1219
+ });
1220
+ const terminalErrorCode =
1221
+ record.state === "terminal" && record.latestResult.result === "failed"
1222
+ ? record.latestResult.error.code
1223
+ : undefined;
1224
+ let observed: CodeModeObservedSession = {
1225
+ sessionId: record.sessionId,
1226
+ lifecycle,
1227
+ cell_count: record.cellCount,
1228
+ last_activity_at_ms: record.lastActivityAtMs,
1229
+ };
1230
+ if (current_cell !== undefined) observed = { ...observed, current_cell };
1231
+ if (record.lastCell !== undefined) observed = { ...observed, last_cell: record.lastCell };
1232
+ if (terminalErrorCode !== undefined) {
1233
+ observed = { ...observed, terminal_error_code: terminalErrorCode };
1234
+ }
1235
+ return Object.freeze(observed);
1236
+ }
1237
+
1238
+ private observeSettledCell(
1239
+ cell: ActiveCodeModeCell,
1240
+ result: CodeModeResult,
1241
+ settledAtMs: number,
1242
+ ): CodeModeObservedLastCell {
1243
+ const errorCode = result.result === "failed" ? result.error.code : undefined;
1244
+ const state: CodeModeObservedCellState =
1245
+ errorCode === "cancellation"
1246
+ ? "cancelled"
1247
+ : errorCode === "timeout"
1248
+ ? "timed_out"
1249
+ : errorCode === undefined
1250
+ ? "completed"
1251
+ : "failed";
1252
+ const settledCell = {
1253
+ ordinal: cell.ordinal,
1254
+ started_at_ms: cell.startedAtMs,
1255
+ settled_at_ms: settledAtMs,
1256
+ state,
1257
+ nested_tool_count: cell.nestedToolCount,
1258
+ };
1259
+ return Object.freeze(
1260
+ errorCode === undefined ? settledCell : { ...settledCell, error_code: errorCode },
1261
+ );
1262
+ }
1263
+
1264
+ private publishObserverSnapshot(): void {
1265
+ try {
1266
+ this.options.onSnapshotChange?.(this.inspectObserverSnapshot());
1267
+ } catch {
1268
+ // A non-authoritative Observer failure cannot alter CodeMode Session lifecycle.
1269
+ }
1270
+ }
1271
+
1272
+ private retainTerminalFailure(sessionId: CodeModeSessionId, failure: CodeModeResult): void {
1273
+ this.records.set(sessionId, {
1274
+ state: "terminal",
1275
+ sessionId,
1276
+ lastAccess: ++this.accessSequence,
1277
+ lastActivityAtMs: this.runtime.now(),
1278
+ cellCount: 0,
1279
+ latestResult: failure,
1280
+ });
1281
+ this.evictTerminalRecords();
1282
+ }
1283
+
1284
+ private evictTerminalRecords(): void {
1285
+ const terminalRecords = [...this.records.values()]
1286
+ .filter((record): record is TerminalCodeModeSession => record.state === "terminal")
1287
+ .sort((left, right) => left.lastAccess - right.lastAccess);
1288
+ for (
1289
+ let index = 0;
1290
+ index < terminalRecords.length - CODEMODE_MAX_TERMINAL_RECORDS;
1291
+ index += 1
1292
+ ) {
1293
+ const record = terminalRecords[index];
1294
+ if (record !== undefined) this.records.delete(record.sessionId);
1295
+ }
1296
+ }
1297
+ }