@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,1092 @@
1
+ import {
2
+ CODEMODE_WORKER_MESSAGE_LIMIT_BYTES,
3
+ parseCodeModeWorkerRequest,
4
+ serializeCodeModeWorkerResponse,
5
+ type CodeModeWorkerRequest,
6
+ type CodeModeWorkerResponse,
7
+ type CodeModeWorkerToolCall,
8
+ type CodeModeWorkerToolSettlement,
9
+ } from "./codemode-worker-protocol.ts";
10
+
11
+ const CODEMODE_WORKER_READ_BUFFER_BYTES = 64 * 1024;
12
+
13
+ type DenoByteReader = { read(buffer: Uint8Array): Promise<number | null> };
14
+ type DenoByteWriter = { write(buffer: Uint8Array): Promise<number> };
15
+ type CodeModeDenoNamespace = {
16
+ readonly args: readonly string[];
17
+ readonly stdin: DenoByteReader;
18
+ readonly stdout: DenoByteWriter;
19
+ readonly version: {
20
+ readonly deno: string;
21
+ readonly v8: string;
22
+ readonly typescript: string;
23
+ };
24
+ };
25
+
26
+ declare const Deno: CodeModeDenoNamespace;
27
+
28
+ const denoProcess = Deno;
29
+ const arrayIsArray = Array.isArray;
30
+ const arrayPrototype = Array.prototype;
31
+ const blobConstructor = Blob;
32
+ const createObject = Object.create;
33
+ const defineProperty = Object.defineProperty;
34
+ const deleteProperty = Reflect.deleteProperty;
35
+ const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
36
+ const getPrototypeOf = Object.getPrototypeOf;
37
+ const errorConstructor = Error;
38
+ const typeErrorConstructor = TypeError;
39
+ const referenceErrorConstructor = ReferenceError;
40
+ const jsonParse = JSON.parse;
41
+ const jsonStringify = JSON.stringify;
42
+ const numberFrom = Number;
43
+ const numberIsFinite = Number.isFinite;
44
+ const numberIsSafeInteger = Number.isSafeInteger;
45
+ const objectFreeze = Object.freeze;
46
+ const objectPrototype = Object.prototype;
47
+ const ownKeys = Reflect.ownKeys;
48
+ const queueRuntimeMicrotask = queueMicrotask.bind(globalThis);
49
+ const createPromiseWithResolvers = Promise.withResolvers.bind(Promise);
50
+ const promisePrototype = Promise.prototype;
51
+ const randomUuid = crypto.randomUUID.bind(crypto);
52
+ // oxlint-disable-next-line typescript/unbound-method -- Capturing this primordial before guest execution prevents a Cell from replacing it.
53
+ const replaceAllStringPrimordial = String.prototype.replaceAll;
54
+ const stringPrototype = String.prototype;
55
+ const applyFunction = Reflect.apply;
56
+ function replaceAllString(value: string, searchValue: string, replaceValue: string): string {
57
+ return applyFunction(replaceAllStringPrimordial, value, [searchValue, replaceValue]);
58
+ }
59
+ const stringFrom = String;
60
+ const textDecoderConstructor = TextDecoder;
61
+ const textDecoderPrototype = TextDecoder.prototype;
62
+ const uint8ArrayConstructor = Uint8Array;
63
+ const uint8ArrayPrototype = Uint8Array.prototype;
64
+ const textEncoder = new TextEncoder();
65
+ const encodeUtf8 = textEncoder.encode.bind(textEncoder);
66
+ const createBlobUrl = URL.createObjectURL.bind(URL);
67
+ const revokeBlobUrl = URL.revokeObjectURL.bind(URL);
68
+ const serializationErrorInstances = new WeakSet<object>();
69
+ const addSerializationErrorInstance = serializationErrorInstances.add.bind(
70
+ serializationErrorInstances,
71
+ );
72
+ const hasSerializationErrorInstance = serializationErrorInstances.has.bind(
73
+ serializationErrorInstances,
74
+ );
75
+ const toolErrorCodes = new WeakMap<object, string>();
76
+ const getToolErrorCode = toolErrorCodes.get.bind(toolErrorCodes);
77
+ const setToolErrorCode = toolErrorCodes.set.bind(toolErrorCodes);
78
+
79
+ class CodeModeSerializationError extends Error {
80
+ override readonly name = "CodeModeSerializationError";
81
+
82
+ constructor(message: string) {
83
+ super(message);
84
+ addSerializationErrorInstance(this);
85
+ }
86
+ }
87
+
88
+ class CodeModeToolError extends Error {
89
+ override readonly name = "CodeModeToolError";
90
+
91
+ constructor(
92
+ readonly code: string,
93
+ message: string,
94
+ ) {
95
+ super(message);
96
+ }
97
+ }
98
+
99
+ function createCodeModeToolError(code: string, message: string): CodeModeToolError {
100
+ const error = new CodeModeToolError(code, message);
101
+ setToolErrorCode(error, code);
102
+ return error;
103
+ }
104
+
105
+ function isGuestReference(cause: unknown): cause is object {
106
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- SAFETY: `typeof` cannot invoke guest Proxy traps; coordinator hostile-value tests prove this non-observable classification.
107
+ return (typeof cause === "object" && cause !== null) || typeof cause === "function";
108
+ }
109
+
110
+ function isGuestString(cause: unknown): cause is string {
111
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- SAFETY: `typeof` cannot invoke guest coercion or Proxy traps; coordinator hostile-value tests prove this non-observable classification.
112
+ return typeof cause === "string";
113
+ }
114
+
115
+ function isStringPropertyKey(key: PropertyKey): key is string {
116
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- SAFETY: Reflect and Proxy keys require a primitive string/symbol split; coordinator hostile JSON and tool-key tests cover symbol rejection and dynamic lookup.
117
+ return typeof key === "string";
118
+ }
119
+
120
+ function internalToolErrorCode(cause: unknown): string | undefined {
121
+ return isGuestReference(cause) ? getToolErrorCode(cause) : undefined;
122
+ }
123
+
124
+ type CodeModeNotebookBindingKind = "var" | "let" | "const" | "function" | "class";
125
+ type CodeModeNotebookBinding = {
126
+ readonly name: string;
127
+ kind: CodeModeNotebookBindingKind;
128
+ value: unknown;
129
+ };
130
+ type CodeModeNotebookStage = {
131
+ readonly name: string;
132
+ readonly kind: CodeModeNotebookBindingKind;
133
+ readonly priorDescriptor?: PropertyDescriptor;
134
+ readonly temporaryProperty: boolean;
135
+ initialized: boolean;
136
+ value: unknown;
137
+ };
138
+ type CodeModeNotebookDeclarationHelper = {
139
+ readonly init: object;
140
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters -- SAFETY: Transformed Cell source completes with arbitrary guest data; coordinator hostile JSON and Notebook Binding tests cover deferred inspection.
141
+ complete(value: unknown): void;
142
+ declare(
143
+ entries: readonly (readonly [string, CodeModeNotebookBindingKind])[],
144
+ initialize: () => Promise<void>,
145
+ ): Promise<void>;
146
+ fail(cause: unknown): void;
147
+ hoistVars(names: readonly string[]): void;
148
+ };
149
+
150
+ type PendingGuestToolCall = {
151
+ readonly call: CodeModeWorkerToolCall;
152
+ readonly promise: Promise<string>;
153
+ readonly resolve: (value: string) => void;
154
+ readonly reject: (error: CodeModeToolError) => void;
155
+ sent: boolean;
156
+ };
157
+
158
+ type ActiveWorkerCell = {
159
+ readonly sessionId: string;
160
+ readonly cellId: string;
161
+ readonly pendingCalls: PendingGuestToolCall[];
162
+ batchSequence: number;
163
+ callSequence: number;
164
+ batchScheduled: boolean;
165
+ finishScheduled: boolean;
166
+ mainFailed: boolean;
167
+ mainSettled: boolean;
168
+ mainResult?: unknown;
169
+ mainError?: unknown;
170
+ outstandingBatch?: { readonly batchId: string; readonly callIds: readonly string[] };
171
+ };
172
+
173
+ type GuestErrorDescription = {
174
+ readonly name: string;
175
+ readonly message: string;
176
+ readonly stack?: string;
177
+ };
178
+
179
+ const RESERVED_NOTEBOOK_BINDING_NAMES = [
180
+ "tools",
181
+ "CodeModeToolError",
182
+ "Deno",
183
+ "process",
184
+ "console",
185
+ "Worker",
186
+ "close",
187
+ "globalThis",
188
+ "global",
189
+ "self",
190
+ "window",
191
+ "WebAssembly",
192
+ "ShadowRealm",
193
+ ] as const;
194
+ const notebookBindings: CodeModeNotebookBinding[] = [];
195
+ let activeNotebookStage: CodeModeNotebookStage[] | undefined;
196
+ let activeCell: ActiveWorkerCell | undefined;
197
+ let responseWrites = Promise.resolve();
198
+ let toolNames: string[] = [];
199
+ let toolFunctions = objectFreeze(createObject(null));
200
+
201
+ function isReservedNotebookBindingName(name: string): boolean {
202
+ if (name.startsWith("__piCodeModeRuntimeKey_")) return true;
203
+ for (const reserved of RESERVED_NOTEBOOK_BINDING_NAMES) {
204
+ if (name === reserved) return true;
205
+ }
206
+ return false;
207
+ }
208
+
209
+ function assertNotebookBindingNameAvailable(name: string): void {
210
+ if (isReservedNotebookBindingName(name)) {
211
+ throw new typeErrorConstructor(`CodeMode Notebook Binding '${name}' is reserved`);
212
+ }
213
+ }
214
+
215
+ function findNotebookBinding(name: string): CodeModeNotebookBinding | undefined {
216
+ for (const binding of notebookBindings) {
217
+ if (binding.name === name) return binding;
218
+ }
219
+ return undefined;
220
+ }
221
+
222
+ function findNotebookStage(name: string): CodeModeNotebookStage | undefined {
223
+ const stage = activeNotebookStage;
224
+ if (stage === undefined) return undefined;
225
+ for (const binding of stage) {
226
+ if (binding.name === name) return binding;
227
+ }
228
+ return undefined;
229
+ }
230
+
231
+ function defineNotebookBindingProperty(name: string, configurable: boolean): void {
232
+ defineProperty(globalThis, name, {
233
+ configurable,
234
+ enumerable: false,
235
+ get() {
236
+ const staged = findNotebookStage(name);
237
+ if (staged !== undefined) {
238
+ if (!staged.initialized) {
239
+ throw new referenceErrorConstructor(`Cannot access '${name}' before initialization`);
240
+ }
241
+ return staged.value;
242
+ }
243
+ return findNotebookBinding(name)?.value;
244
+ },
245
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters -- SAFETY: Notebook Binding assignment accepts arbitrary Cell values by design; coordinator Notebook Binding reuse tests cover this dynamic seam.
246
+ set(value: unknown) {
247
+ const staged = findNotebookStage(name);
248
+ if (staged !== undefined) {
249
+ if (!staged.initialized) {
250
+ throw new referenceErrorConstructor(`Cannot access '${name}' before initialization`);
251
+ }
252
+ if (staged.kind === "const") {
253
+ throw new typeErrorConstructor(`Assignment to constant Notebook Binding '${name}'`);
254
+ }
255
+ staged.value = value;
256
+ return;
257
+ }
258
+ const binding = findNotebookBinding(name);
259
+ if (binding === undefined) {
260
+ throw new referenceErrorConstructor(`${name} is not defined`);
261
+ }
262
+ if (binding.kind === "const") {
263
+ throw new typeErrorConstructor(`Assignment to constant Notebook Binding '${name}'`);
264
+ }
265
+ binding.value = value;
266
+ },
267
+ });
268
+ }
269
+
270
+ function restoreNotebookStageProperties(stage: readonly CodeModeNotebookStage[]): void {
271
+ for (const staged of stage) {
272
+ if (!staged.temporaryProperty) continue;
273
+ if (staged.priorDescriptor === undefined) deleteProperty(globalThis, staged.name);
274
+ else defineProperty(globalThis, staged.name, staged.priorDescriptor);
275
+ }
276
+ }
277
+
278
+ function commitNotebookStage(stage: readonly CodeModeNotebookStage[]): void {
279
+ for (const staged of stage) {
280
+ const existing = findNotebookBinding(staged.name);
281
+ if (existing === undefined) {
282
+ notebookBindings[notebookBindings.length] = {
283
+ name: staged.name,
284
+ kind: staged.kind,
285
+ value: staged.value,
286
+ };
287
+ if (staged.temporaryProperty) defineNotebookBindingProperty(staged.name, false);
288
+ } else {
289
+ existing.kind = staged.kind;
290
+ existing.value = staged.value;
291
+ }
292
+ }
293
+ }
294
+
295
+ const notebookInitializationTarget = new Proxy(createObject(null), {
296
+ set(_target, name, value) {
297
+ if (!isStringPropertyKey(name) || activeNotebookStage === undefined) {
298
+ throw new errorConstructor("Pi CodeMode: declaration initialization outside an active stage");
299
+ }
300
+ const staged = findNotebookStage(name);
301
+ if (staged === undefined) {
302
+ throw new errorConstructor(
303
+ "Pi CodeMode: declaration initialized an unplanned Notebook Binding",
304
+ );
305
+ }
306
+ staged.initialized = true;
307
+ staged.value = value;
308
+ return true;
309
+ },
310
+ });
311
+
312
+ const notebookDeclarationHelper: CodeModeNotebookDeclarationHelper = objectFreeze({
313
+ init: notebookInitializationTarget,
314
+ complete(value) {
315
+ const cell = activeCell;
316
+ if (cell === undefined) {
317
+ throw new errorConstructor("Pi CodeMode: Cell completed without an active worker Cell");
318
+ }
319
+ cell.mainResult = value;
320
+ cell.mainSettled = true;
321
+ },
322
+ async declare(entries, initialize) {
323
+ if (activeNotebookStage !== undefined) {
324
+ throw new errorConstructor("Pi CodeMode: overlapping declaration stages");
325
+ }
326
+ const stage: CodeModeNotebookStage[] = [];
327
+ activeNotebookStage = stage;
328
+ try {
329
+ for (const [name, kind] of entries) {
330
+ assertNotebookBindingNameAvailable(name);
331
+ const existing = findNotebookBinding(name);
332
+ const priorDescriptor = getOwnPropertyDescriptor(globalThis, name);
333
+ const temporaryProperty = existing === undefined;
334
+ const stagedBase = {
335
+ name,
336
+ kind,
337
+ temporaryProperty,
338
+ initialized: kind === "var",
339
+ value: kind === "var" ? existing?.value : undefined,
340
+ } as const;
341
+ stage[stage.length] =
342
+ priorDescriptor === undefined ? stagedBase : { ...stagedBase, priorDescriptor };
343
+ if (temporaryProperty) defineNotebookBindingProperty(name, true);
344
+ }
345
+ await initialize();
346
+ for (const staged of stage) {
347
+ if (!staged.initialized) {
348
+ throw new errorConstructor(
349
+ "Pi CodeMode: declaration did not initialize its Notebook Binding",
350
+ );
351
+ }
352
+ }
353
+ commitNotebookStage(stage);
354
+ } catch (cause) {
355
+ restoreNotebookStageProperties(stage);
356
+ throw cause;
357
+ } finally {
358
+ activeNotebookStage = undefined;
359
+ }
360
+ },
361
+ fail(cause) {
362
+ const cell = activeCell;
363
+ if (cell === undefined) {
364
+ throw new errorConstructor("Pi CodeMode: Cell failed without an active worker Cell");
365
+ }
366
+ cell.mainError = cause;
367
+ cell.mainFailed = true;
368
+ cell.mainSettled = true;
369
+ },
370
+ hoistVars(names) {
371
+ if (activeNotebookStage !== undefined) {
372
+ throw new errorConstructor(
373
+ "Pi CodeMode: variable hoisting during an active declaration stage",
374
+ );
375
+ }
376
+ for (const name of names) {
377
+ assertNotebookBindingNameAvailable(name);
378
+ const existing = findNotebookBinding(name);
379
+ if (existing !== undefined) {
380
+ existing.kind = "var";
381
+ continue;
382
+ }
383
+ const priorDescriptor = getOwnPropertyDescriptor(globalThis, name);
384
+ try {
385
+ defineNotebookBindingProperty(name, true);
386
+ notebookBindings[notebookBindings.length] = { name, kind: "var", value: undefined };
387
+ defineNotebookBindingProperty(name, false);
388
+ } catch (cause) {
389
+ if (priorDescriptor === undefined) deleteProperty(globalThis, name);
390
+ else defineProperty(globalThis, name, priorDescriptor);
391
+ throw cause;
392
+ }
393
+ }
394
+ },
395
+ });
396
+
397
+ function utf8ByteLength(value: string): number {
398
+ return encodeUtf8(value).byteLength;
399
+ }
400
+
401
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters -- SAFETY: This is arbitrary guest ingress; descriptor-only traversal avoids invoking accessors, coercion, and guest-mutated methods. Coordinator hostile JSON tests cover the boundary.
402
+ function serializeGuestJson(value: unknown, allowUndefined: boolean): string | undefined {
403
+ const seen: object[] = [];
404
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns -- SAFETY: Recursive guest inspection remains unknown until each descriptor value is classified; coordinator hostile JSON tests cover accessors, Proxies, cycles, and sparse arrays.
405
+ const inspect = (candidate: unknown, path: string): unknown => {
406
+ // oxlint-disable-next-line anti-slop/no-known-value-widening -- SAFETY: The recursive result remains unknown so JSON.stringify performs the existing transport normalization without a lint-only universal value hierarchy; coordinator serialization tests cover null and undefined.
407
+ if (candidate === null) return null;
408
+ if (candidate === undefined) {
409
+ if (path === "$" && allowUndefined) return undefined;
410
+ throw new CodeModeSerializationError(
411
+ `CodeMode serialization failed: ${path} must be JSON data`,
412
+ );
413
+ }
414
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- SAFETY: Primitive classification with `typeof` cannot execute guest code; coordinator hostile JSON and mutated-primordial tests cover this ingress.
415
+ const type = typeof candidate;
416
+ if (type === "boolean" || type === "string") return candidate;
417
+ if (type === "number") {
418
+ if (!numberIsFinite(candidate)) {
419
+ throw new CodeModeSerializationError(
420
+ `CodeMode serialization failed: ${path} must be finite`,
421
+ );
422
+ }
423
+ return candidate;
424
+ }
425
+ if (type !== "object") {
426
+ throw new CodeModeSerializationError(
427
+ `CodeMode serialization failed: ${path} is not JSON data`,
428
+ );
429
+ }
430
+ for (const prior of seen) {
431
+ if (prior === candidate)
432
+ throw new CodeModeSerializationError(`CodeMode serialization failed: ${path} is cyclic`);
433
+ }
434
+ seen[seen.length] = candidate;
435
+ try {
436
+ if (arrayIsArray(candidate)) {
437
+ const output: unknown[] = [];
438
+ for (const key of ownKeys(candidate)) {
439
+ if (key === "length") continue;
440
+ if (!isStringPropertyKey(key)) {
441
+ throw new CodeModeSerializationError(
442
+ `CodeMode serialization failed: ${path} has a symbol property`,
443
+ );
444
+ }
445
+ const descriptor = getOwnPropertyDescriptor(candidate, key);
446
+ const index = numberFrom(key);
447
+ if (
448
+ descriptor === undefined ||
449
+ !descriptor.enumerable ||
450
+ !numberIsSafeInteger(index) ||
451
+ index < 0 ||
452
+ stringFrom(index) !== key
453
+ ) {
454
+ throw new CodeModeSerializationError(
455
+ `CodeMode serialization failed: ${path}.${key} is not a JSON array index`,
456
+ );
457
+ }
458
+ }
459
+ for (let index = 0; index < candidate.length; index += 1) {
460
+ const descriptor = getOwnPropertyDescriptor(candidate, stringFrom(index));
461
+ if (descriptor === undefined || !("value" in descriptor)) {
462
+ throw new CodeModeSerializationError(
463
+ `CodeMode serialization failed: ${path}[${index}] is sparse or accessor-backed`,
464
+ );
465
+ }
466
+ output[output.length] = inspect(descriptor.value, `${path}[${index}]`);
467
+ }
468
+ // oxlint-disable-next-line anti-slop/no-known-value-widening -- SAFETY: The hostile-safe recursive inspector intentionally carries the array as unknown until JSON.stringify; coordinator sparse/accessor and undefined-normalization tests cover it.
469
+ return output;
470
+ }
471
+ const prototype = getPrototypeOf(candidate);
472
+ if (prototype !== objectPrototype && prototype !== null) {
473
+ throw new CodeModeSerializationError(
474
+ `CodeMode serialization failed: ${path} must be a plain object`,
475
+ );
476
+ }
477
+ const output = createObject(null);
478
+ for (const key of ownKeys(candidate)) {
479
+ if (!isStringPropertyKey(key)) {
480
+ throw new CodeModeSerializationError(
481
+ `CodeMode serialization failed: ${path} has a symbol property`,
482
+ );
483
+ }
484
+ const descriptor = getOwnPropertyDescriptor(candidate, key);
485
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
486
+ throw new CodeModeSerializationError(
487
+ `CodeMode serialization failed: ${path}.${key} is non-enumerable or accessor-backed`,
488
+ );
489
+ }
490
+ defineProperty(output, key, {
491
+ configurable: true,
492
+ enumerable: true,
493
+ value: inspect(descriptor.value, `${path}.${key}`),
494
+ writable: true,
495
+ });
496
+ }
497
+ return output;
498
+ } finally {
499
+ seen.length -= 1;
500
+ }
501
+ };
502
+
503
+ const inspected = inspect(value, "$");
504
+ if (inspected === undefined) return undefined;
505
+ const json = jsonStringify(inspected);
506
+ if (utf8ByteLength(json) > CODEMODE_WORKER_MESSAGE_LIMIT_BYTES) {
507
+ throw new CodeModeSerializationError("CodeMode serialization failed: JSON exceeds 8 MiB");
508
+ }
509
+ return json;
510
+ }
511
+
512
+ function describeGuestError(cause: unknown): GuestErrorDescription {
513
+ if (isGuestString(cause)) return { name: "Error", message: cause };
514
+ if (cause === null) return { name: "Error", message: "null" };
515
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- SAFETY: One non-observable primitive split avoids coercing guest objects; coordinator hostile-thrown-value tests cover Proxy containment.
516
+ const causeType = typeof cause;
517
+ if (
518
+ causeType === "number" ||
519
+ causeType === "boolean" ||
520
+ causeType === "bigint" ||
521
+ causeType === "undefined"
522
+ ) {
523
+ return { name: "Error", message: stringFrom(cause) };
524
+ }
525
+ if (causeType === "symbol") return { name: "Error", message: "Symbol" };
526
+ try {
527
+ const nameDescriptor = getOwnPropertyDescriptor(cause, "name");
528
+ const messageDescriptor = getOwnPropertyDescriptor(cause, "message");
529
+ const stackDescriptor = getOwnPropertyDescriptor(cause, "stack");
530
+ const name =
531
+ nameDescriptor !== undefined &&
532
+ "value" in nameDescriptor &&
533
+ isGuestString(nameDescriptor.value)
534
+ ? nameDescriptor.value
535
+ : "Error";
536
+ const message =
537
+ messageDescriptor !== undefined &&
538
+ "value" in messageDescriptor &&
539
+ isGuestString(messageDescriptor.value)
540
+ ? messageDescriptor.value
541
+ : "CodeMode Cell rejected";
542
+ if (
543
+ stackDescriptor === undefined ||
544
+ !("value" in stackDescriptor) ||
545
+ !isGuestString(stackDescriptor.value)
546
+ ) {
547
+ return { name, message };
548
+ }
549
+ return { name, message, stack: stackDescriptor.value };
550
+ } catch {
551
+ return { name: "Error", message: "CodeMode Cell threw an unreadable value" };
552
+ }
553
+ }
554
+
555
+ function renderGuestError(error: GuestErrorDescription): string {
556
+ return error.stack ?? `${error.name}: ${error.message}`;
557
+ }
558
+
559
+ async function* readBoundedJsonLines(reader: DenoByteReader): AsyncGenerator<string> {
560
+ const decoder = new textDecoderConstructor("utf-8", { fatal: true });
561
+ const readBuffer = new uint8ArrayConstructor(CODEMODE_WORKER_READ_BUFFER_BYTES);
562
+ let pendingText = "";
563
+ let pendingBytes = 0;
564
+
565
+ while (true) {
566
+ const bytesRead = await reader.read(readBuffer);
567
+ if (bytesRead === null) break;
568
+ const chunk = readBuffer.subarray(0, bytesRead);
569
+ for (const byte of chunk) {
570
+ if (byte === 0x0a) pendingBytes = 0;
571
+ else {
572
+ pendingBytes += 1;
573
+ if (pendingBytes > CODEMODE_WORKER_MESSAGE_LIMIT_BYTES) {
574
+ throw new errorConstructor("Pi CodeMode: worker request exceeds 8 MiB");
575
+ }
576
+ }
577
+ }
578
+ pendingText += decoder.decode(chunk, { stream: true });
579
+ let newlineIndex = pendingText.indexOf("\n");
580
+ while (newlineIndex !== -1) {
581
+ const line = pendingText.slice(0, newlineIndex);
582
+ pendingText = pendingText.slice(newlineIndex + 1);
583
+ if (line.length > 0) yield line;
584
+ newlineIndex = pendingText.indexOf("\n");
585
+ }
586
+ }
587
+
588
+ pendingText += decoder.decode();
589
+ if (pendingText.length > 0) yield pendingText;
590
+ }
591
+
592
+ async function writeJsonLine(
593
+ writer: DenoByteWriter,
594
+ response: CodeModeWorkerResponse,
595
+ ): Promise<void> {
596
+ const bytes = encodeUtf8(`${serializeCodeModeWorkerResponse(response)}\n`);
597
+ let written = 0;
598
+ while (written < bytes.byteLength) written += await writer.write(bytes.subarray(written));
599
+ }
600
+
601
+ async function writeWorkerResponseAfter(
602
+ priorWrite: Promise<void>,
603
+ response: CodeModeWorkerResponse,
604
+ ): Promise<void> {
605
+ await priorWrite;
606
+ await writeJsonLine(denoProcess.stdout, response);
607
+ }
608
+
609
+ function enqueueWorkerResponse(response: CodeModeWorkerResponse): Promise<void> {
610
+ const next = writeWorkerResponseAfter(responseWrites, response);
611
+ responseWrites = next;
612
+ return next;
613
+ }
614
+
615
+ function copyToolNames(): string[] {
616
+ const names: string[] = [];
617
+ for (const name of toolNames) names[names.length] = name;
618
+ return names;
619
+ }
620
+
621
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns -- SAFETY: Guest tool calls accept arbitrary Cell input and return JSON.parse output; coordinator nested-tool and hostile JSON tests cover both directions.
622
+ async function nativeToolCall(name: string, input: unknown): Promise<unknown> {
623
+ const cell = activeCell;
624
+ if (cell === undefined) {
625
+ throw createCodeModeToolError("runtime", "CodeMode Cell tool bridge is closed");
626
+ }
627
+ let inputJson: string;
628
+ try {
629
+ const serialized = serializeGuestJson(input, false);
630
+ if (serialized === undefined) {
631
+ throw createCodeModeToolError("serialization", "CodeMode tool input must be JSON data");
632
+ }
633
+ inputJson = serialized;
634
+ } catch (cause) {
635
+ if (internalToolErrorCode(cause) !== undefined) throw cause;
636
+ const error = describeGuestError(cause);
637
+ throw createCodeModeToolError("serialization", error.message);
638
+ }
639
+
640
+ const deferred = createPromiseWithResolvers<string>();
641
+ const callId = `${cell.cellId}:call-${++cell.callSequence}`;
642
+ const pending: PendingGuestToolCall = {
643
+ call: { callId, toolName: name, inputJson },
644
+ promise: deferred.promise,
645
+ resolve: deferred.resolve,
646
+ reject: deferred.reject,
647
+ sent: false,
648
+ };
649
+ cell.pendingCalls[cell.pendingCalls.length] = pending;
650
+ scheduleToolBatch(cell);
651
+ return jsonParse(await pending.promise);
652
+ }
653
+
654
+ function setToolNames(names: readonly string[]): void {
655
+ const nextNames: string[] = [];
656
+ const nextFunctions = createObject(null);
657
+ for (const name of names) {
658
+ nextNames[nextNames.length] = name;
659
+ defineProperty(nextFunctions, name, {
660
+ configurable: false,
661
+ enumerable: true,
662
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters -- SAFETY: This is the guest-facing tool function boundary; nativeToolCall immediately performs hostile-safe JSON inspection. Coordinator nested-tool tests exercise it.
663
+ value: (input: unknown) => {
664
+ const result = nativeToolCall(name, input);
665
+ // Observe ignored direct calls without changing rejection behavior for callers that await them.
666
+ void result.catch(() => {});
667
+ return result;
668
+ },
669
+ writable: false,
670
+ });
671
+ }
672
+ toolNames = nextNames;
673
+ toolFunctions = objectFreeze(nextFunctions);
674
+ }
675
+
676
+ const tools = new Proxy(createObject(null), {
677
+ defineProperty() {
678
+ return false;
679
+ },
680
+ deleteProperty() {
681
+ return false;
682
+ },
683
+ get(_target, name) {
684
+ return isStringPropertyKey(name)
685
+ ? getOwnPropertyDescriptor(toolFunctions, name)?.value
686
+ : undefined;
687
+ },
688
+ getOwnPropertyDescriptor(_target, name) {
689
+ if (!isStringPropertyKey(name)) return undefined;
690
+ const value = getOwnPropertyDescriptor(toolFunctions, name)?.value;
691
+ return value === undefined
692
+ ? undefined
693
+ : { configurable: true, enumerable: true, value, writable: false };
694
+ },
695
+ has(_target, name) {
696
+ return isStringPropertyKey(name) && getOwnPropertyDescriptor(toolFunctions, name) !== undefined;
697
+ },
698
+ ownKeys() {
699
+ return copyToolNames();
700
+ },
701
+ set() {
702
+ return false;
703
+ },
704
+ });
705
+
706
+ defineProperty(globalThis, "CodeModeToolError", {
707
+ configurable: false,
708
+ enumerable: false,
709
+ value: CodeModeToolError,
710
+ writable: false,
711
+ });
712
+ defineProperty(globalThis, "tools", {
713
+ configurable: false,
714
+ enumerable: true,
715
+ value: tools,
716
+ writable: false,
717
+ });
718
+
719
+ function disableGuestGlobal(name: string, value?: Readonly<typeof safeDenoIdentity>): void {
720
+ const descriptor = getOwnPropertyDescriptor(globalThis, name);
721
+ if (descriptor?.configurable === false) return;
722
+ defineProperty(globalThis, name, {
723
+ configurable: false,
724
+ enumerable: false,
725
+ value,
726
+ writable: false,
727
+ });
728
+ }
729
+
730
+ const ordinaryFunctionPrototype = Function.prototype;
731
+ const asyncFunctionPrototype = getPrototypeOf(async function codeModeAsyncFunction() {});
732
+ const generatorFunctionPrototype = getPrototypeOf(function* codeModeGeneratorFunction() {});
733
+ const asyncGeneratorFunctionPrototype = getPrototypeOf(
734
+ async function* codeModeAsyncGeneratorFunction() {},
735
+ );
736
+ for (const functionPrototype of [
737
+ ordinaryFunctionPrototype,
738
+ asyncFunctionPrototype,
739
+ generatorFunctionPrototype,
740
+ asyncGeneratorFunctionPrototype,
741
+ ]) {
742
+ defineProperty(functionPrototype, "constructor", {
743
+ configurable: false,
744
+ enumerable: false,
745
+ value: undefined,
746
+ writable: false,
747
+ });
748
+ }
749
+ for (const runtimePrototype of [
750
+ arrayPrototype,
751
+ stringPrototype,
752
+ promisePrototype,
753
+ textDecoderPrototype,
754
+ uint8ArrayPrototype,
755
+ ordinaryFunctionPrototype,
756
+ asyncFunctionPrototype,
757
+ generatorFunctionPrototype,
758
+ asyncGeneratorFunctionPrototype,
759
+ ]) {
760
+ objectFreeze(runtimePrototype);
761
+ }
762
+
763
+ const safeDenoIdentity = objectFreeze({
764
+ version: objectFreeze({
765
+ deno: denoProcess.version.deno,
766
+ typescript: denoProcess.version.typescript,
767
+ v8: denoProcess.version.v8,
768
+ }),
769
+ });
770
+ disableGuestGlobal("Deno", safeDenoIdentity);
771
+ for (const unsafeGlobal of [
772
+ "process",
773
+ "console",
774
+ "alert",
775
+ "confirm",
776
+ "prompt",
777
+ "eval",
778
+ "Function",
779
+ "Worker",
780
+ "close",
781
+ "fetch",
782
+ "WebSocket",
783
+ "EventSource",
784
+ "WebAssembly",
785
+ "ShadowRealm",
786
+ "setTimeout",
787
+ "clearTimeout",
788
+ "setInterval",
789
+ "clearInterval",
790
+ ]) {
791
+ disableGuestGlobal(unsafeGlobal);
792
+ }
793
+
794
+ function scheduleToolBatch(cell: ActiveWorkerCell): void {
795
+ if (
796
+ activeCell !== cell ||
797
+ cell.batchScheduled ||
798
+ cell.outstandingBatch !== undefined ||
799
+ !cell.pendingCalls.some((pending) => !pending.sent)
800
+ ) {
801
+ return;
802
+ }
803
+ cell.batchScheduled = true;
804
+ queueRuntimeMicrotask(() => {
805
+ cell.batchScheduled = false;
806
+ if (activeCell !== cell || cell.outstandingBatch !== undefined) return;
807
+ const pendingBatch = cell.pendingCalls.filter((pending) => !pending.sent);
808
+ if (pendingBatch.length === 0) {
809
+ scheduleCellFinish(cell);
810
+ return;
811
+ }
812
+ const calls = pendingBatch.map((pending) => pending.call);
813
+ const batchId = `${cell.cellId}:batch-${++cell.batchSequence}`;
814
+ const response: CodeModeWorkerResponse = {
815
+ version: 1,
816
+ type: "tool-batch",
817
+ sessionId: cell.sessionId,
818
+ cellId: cell.cellId,
819
+ batchId,
820
+ calls,
821
+ };
822
+ if (utf8ByteLength(jsonStringify(response)) > CODEMODE_WORKER_MESSAGE_LIMIT_BYTES) {
823
+ for (const pending of pendingBatch) {
824
+ pending.reject(
825
+ createCodeModeToolError("serialization", "CodeMode nested tool batch exceeds 8 MiB"),
826
+ );
827
+ removePendingCall(cell, pending.call.callId);
828
+ }
829
+ scheduleCellFinish(cell);
830
+ return;
831
+ }
832
+ for (const pending of pendingBatch) pending.sent = true;
833
+ cell.outstandingBatch = { batchId, callIds: calls.map((call) => call.callId) };
834
+ void enqueueWorkerResponse(response);
835
+ });
836
+ }
837
+
838
+ function removePendingCall(cell: ActiveWorkerCell, callId: string): void {
839
+ const retained: PendingGuestToolCall[] = [];
840
+ for (const pending of cell.pendingCalls) {
841
+ if (pending.call.callId !== callId) retained[retained.length] = pending;
842
+ }
843
+ cell.pendingCalls.length = 0;
844
+ for (const pending of retained) cell.pendingCalls[cell.pendingCalls.length] = pending;
845
+ }
846
+
847
+ function settleGuestToolCalls(
848
+ cell: ActiveWorkerCell,
849
+ results: readonly CodeModeWorkerToolSettlement[],
850
+ ): string | undefined {
851
+ const batch = cell.outstandingBatch;
852
+ if (batch === undefined) {
853
+ return "Pi CodeMode: worker received tool results without an outstanding batch";
854
+ }
855
+ if (results.length !== batch.callIds.length) {
856
+ return "Pi CodeMode: worker received an incomplete tool result batch";
857
+ }
858
+ const remainingIds = [...batch.callIds];
859
+ for (const result of results) {
860
+ const expectedIndex = remainingIds.indexOf(result.callId);
861
+ if (expectedIndex === -1) return "Pi CodeMode: worker received an unexpected tool result";
862
+ remainingIds.splice(expectedIndex, 1);
863
+ const pending = cell.pendingCalls.find((candidate) => candidate.call.callId === result.callId);
864
+ if (pending === undefined) return "Pi CodeMode: worker lost a guest tool promise";
865
+ if (result.outcome === "success") pending.resolve(result.resultJson);
866
+ else pending.reject(createCodeModeToolError(result.error.code, result.error.message));
867
+ removePendingCall(cell, result.callId);
868
+ }
869
+ delete cell.outstandingBatch;
870
+ queueRuntimeMicrotask(() => {
871
+ scheduleToolBatch(cell);
872
+ scheduleCellFinish(cell);
873
+ });
874
+ return undefined;
875
+ }
876
+
877
+ function scheduleCellFinish(cell: ActiveWorkerCell): void {
878
+ if (activeCell !== cell || !cell.mainSettled || cell.finishScheduled) return;
879
+ cell.finishScheduled = true;
880
+ queueRuntimeMicrotask(() => {
881
+ cell.finishScheduled = false;
882
+ if (
883
+ activeCell !== cell ||
884
+ !cell.mainSettled ||
885
+ cell.pendingCalls.length > 0 ||
886
+ cell.outstandingBatch !== undefined ||
887
+ cell.batchScheduled
888
+ ) {
889
+ return;
890
+ }
891
+ activeCell = undefined;
892
+ let response: CodeModeWorkerResponse;
893
+ if (cell.mainFailed) {
894
+ const error = describeGuestError(cell.mainError);
895
+ const serializationFailure =
896
+ isGuestReference(cell.mainError) &&
897
+ (hasSerializationErrorInstance(cell.mainError) ||
898
+ internalToolErrorCode(cell.mainError) === "serialization");
899
+ response = {
900
+ version: 1,
901
+ type: "cell-error",
902
+ sessionId: cell.sessionId,
903
+ cellId: cell.cellId,
904
+ error: {
905
+ code: serializationFailure ? "serialization" : "script",
906
+ message: renderGuestError(error),
907
+ },
908
+ };
909
+ } else {
910
+ try {
911
+ const resultJson = serializeGuestJson(cell.mainResult, true);
912
+ const responseBase = {
913
+ version: 1,
914
+ type: "cell-result",
915
+ sessionId: cell.sessionId,
916
+ cellId: cell.cellId,
917
+ } as const;
918
+ response = resultJson === undefined ? responseBase : { ...responseBase, resultJson };
919
+ } catch (cause) {
920
+ const error = describeGuestError(cause);
921
+ response = {
922
+ version: 1,
923
+ type: "cell-error",
924
+ sessionId: cell.sessionId,
925
+ cellId: cell.cellId,
926
+ error: { code: "serialization", message: renderGuestError(error) },
927
+ };
928
+ }
929
+ }
930
+ void enqueueWorkerResponse(response);
931
+ });
932
+ }
933
+
934
+ function startWorkerCell(
935
+ request: Extract<CodeModeWorkerRequest, { readonly type: "execute" }>,
936
+ ): void {
937
+ setToolNames(request.toolNames);
938
+ const cell: ActiveWorkerCell = {
939
+ sessionId: request.sessionId,
940
+ cellId: request.cellId,
941
+ pendingCalls: [],
942
+ batchSequence: 0,
943
+ callSequence: 0,
944
+ batchScheduled: false,
945
+ finishScheduled: false,
946
+ mainFailed: false,
947
+ mainSettled: false,
948
+ };
949
+ activeCell = cell;
950
+
951
+ const suffix = replaceAllString(randomUuid(), "-", "_");
952
+ const internalIdentifier = `__piCodeModeRuntime_${suffix}`;
953
+ const runtimeKey = `__piCodeModeRuntimeKey_${suffix}`;
954
+ const executableSource = replaceAllString(
955
+ request.source,
956
+ request.internalIdentifierPlaceholder,
957
+ internalIdentifier,
958
+ );
959
+ defineProperty(globalThis, runtimeKey, {
960
+ configurable: true,
961
+ enumerable: false,
962
+ value: notebookDeclarationHelper,
963
+ writable: false,
964
+ });
965
+ const runtimeKeyLiteral = jsonStringify(runtimeKey);
966
+ if (runtimeKeyLiteral === undefined) {
967
+ throw new errorConstructor("Pi CodeMode: failed to serialize the internal runtime key");
968
+ }
969
+ // ponytail: Deno retains one small compiled Blob module per Cell; use a future public
970
+ // transpile-and-eval API if long-lived Sessions reach the process heap limit.
971
+ const moduleSource = `
972
+ let ${internalIdentifier} = globalThis[${runtimeKeyLiteral}];
973
+ delete globalThis[${runtimeKeyLiteral}];
974
+ try {
975
+ ${internalIdentifier}.complete(await (async () => {
976
+ ${executableSource}
977
+ })());
978
+ } catch (cause) {
979
+ ${internalIdentifier}.fail(cause);
980
+ } finally {
981
+ ${internalIdentifier} = undefined;
982
+ }
983
+ export default undefined;
984
+ `;
985
+ const moduleUrl = createBlobUrl(
986
+ new blobConstructor([moduleSource], { type: "application/typescript" }),
987
+ );
988
+ void evaluateWorkerCellModule(cell, moduleUrl, runtimeKey);
989
+ }
990
+
991
+ async function evaluateWorkerCellModule(
992
+ cell: ActiveWorkerCell,
993
+ moduleUrl: string,
994
+ runtimeKey: string,
995
+ ): Promise<void> {
996
+ try {
997
+ await import(moduleUrl);
998
+ if (!cell.mainSettled) {
999
+ cell.mainError = new errorConstructor("Pi CodeMode: Cell module did not settle its result");
1000
+ cell.mainFailed = true;
1001
+ cell.mainSettled = true;
1002
+ }
1003
+ } catch (cause) {
1004
+ cell.mainError = cause;
1005
+ cell.mainFailed = true;
1006
+ cell.mainSettled = true;
1007
+ } finally {
1008
+ deleteProperty(globalThis, runtimeKey);
1009
+ revokeBlobUrl(moduleUrl);
1010
+ scheduleCellFinish(cell);
1011
+ }
1012
+ }
1013
+
1014
+ const workerSessionId = denoProcess.args[0];
1015
+ if (workerSessionId === undefined || workerSessionId.length === 0) {
1016
+ throw new errorConstructor("Pi CodeMode: worker requires a non-empty Session ID");
1017
+ }
1018
+
1019
+ try {
1020
+ await enqueueWorkerResponse({ version: 1, type: "ready", sessionId: workerSessionId });
1021
+ for await (const message of readBoundedJsonLines(denoProcess.stdin)) {
1022
+ const parsed = parseCodeModeWorkerRequest(message);
1023
+ if (!parsed.ok) {
1024
+ await enqueueWorkerResponse({
1025
+ version: 1,
1026
+ type: "protocol-error",
1027
+ sessionId: workerSessionId,
1028
+ message: parsed.message,
1029
+ });
1030
+ break;
1031
+ }
1032
+ const request = parsed.value;
1033
+ if (request.sessionId !== workerSessionId) {
1034
+ await enqueueWorkerResponse({
1035
+ version: 1,
1036
+ type: "protocol-error",
1037
+ sessionId: workerSessionId,
1038
+ message: "Pi CodeMode: worker received a stale Session ID",
1039
+ });
1040
+ break;
1041
+ }
1042
+ if (request.type === "shutdown") {
1043
+ if (activeCell !== undefined) {
1044
+ await enqueueWorkerResponse({
1045
+ version: 1,
1046
+ type: "protocol-error",
1047
+ sessionId: workerSessionId,
1048
+ message: "Pi CodeMode: cannot gracefully shut down an active Cell",
1049
+ });
1050
+ }
1051
+ break;
1052
+ }
1053
+ if (request.type === "execute") {
1054
+ if (activeCell !== undefined) {
1055
+ await enqueueWorkerResponse({
1056
+ version: 1,
1057
+ type: "protocol-error",
1058
+ sessionId: workerSessionId,
1059
+ message: "Pi CodeMode: worker received overlapping Cells",
1060
+ });
1061
+ break;
1062
+ }
1063
+ startWorkerCell(request);
1064
+ continue;
1065
+ }
1066
+ if (
1067
+ activeCell === undefined ||
1068
+ request.cellId !== activeCell.cellId ||
1069
+ request.batchId !== activeCell.outstandingBatch?.batchId
1070
+ ) {
1071
+ await enqueueWorkerResponse({
1072
+ version: 1,
1073
+ type: "protocol-error",
1074
+ sessionId: workerSessionId,
1075
+ message: "Pi CodeMode: worker received stale tool results",
1076
+ });
1077
+ break;
1078
+ }
1079
+ const settlementError = settleGuestToolCalls(activeCell, request.results);
1080
+ if (settlementError !== undefined) {
1081
+ await enqueueWorkerResponse({
1082
+ version: 1,
1083
+ type: "protocol-error",
1084
+ sessionId: workerSessionId,
1085
+ message: settlementError,
1086
+ });
1087
+ break;
1088
+ }
1089
+ }
1090
+ } finally {
1091
+ await responseWrites;
1092
+ }