@ian-pascoe/pi-codemode 0.1.0 → 0.3.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.
- package/README.md +96 -24
- package/package.json +6 -6
- package/src/codemode-console-output.ts +11 -0
- package/src/codemode-observer-ui.ts +19 -38
- package/src/codemode-session-coordinator.ts +313 -158
- package/src/codemode-tool-catalog.ts +4 -6
- package/src/codemode-tool-contract.ts +108 -14
- package/src/codemode-tool-rendering.ts +138 -39
- package/src/codemode-worker-protocol.ts +141 -33
- package/src/codemode-worker.ts +283 -18
- package/src/pi-codemode-extension.ts +31 -28
- package/src/pi-tool-bridge.ts +5 -38
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
} from "./pi-tool-bridge.js";
|
|
40
40
|
|
|
41
41
|
const CODEMODE_EXECUTE_DESCRIPTION =
|
|
42
|
-
"Execute a TypeScript Cell in a persistent isolated Deno CodeMode Session.
|
|
42
|
+
"Execute a TypeScript Cell in a persistent isolated Deno CodeMode Session. Reuse a Session ID to retain Notebook Bindings; a new Session reclaims the least-recently-used idle Session at capacity. Use the read-only tools object for registered Pi tools. Cells may call console.log, console.info, console.warn, console.error, and console.debug; captured output arrives only with terminal results.";
|
|
43
43
|
|
|
44
44
|
type PiCodeModeGeneration = {
|
|
45
45
|
readonly captured: CapturedPiAgentSession;
|
|
@@ -126,11 +126,11 @@ class PiCodeModeLifecycleController {
|
|
|
126
126
|
this.pi.on("session_start", async (_event, context) => this.startSession(context));
|
|
127
127
|
this.pi.on("before_agent_start", () => this.synchronizeCurrentGeneration());
|
|
128
128
|
this.pi.on("tool_execution_end", () => this.synchronizeCurrentGeneration());
|
|
129
|
-
this.pi.on("session_shutdown", async (
|
|
129
|
+
this.pi.on("session_shutdown", async () => this.shutdownSession());
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
private async startSession(context: ExtensionContext): Promise<void> {
|
|
133
|
-
await this.shutdownSession(
|
|
133
|
+
await this.shutdownSession();
|
|
134
134
|
const capturedResult = capturePiAgentSession(this.pi);
|
|
135
135
|
if (!capturedResult.ok) {
|
|
136
136
|
this.notifyWarning(context, capturedResult.warning);
|
|
@@ -207,6 +207,10 @@ class PiCodeModeLifecycleController {
|
|
|
207
207
|
},
|
|
208
208
|
result: async (input) => coordinator.result(input.sessionId),
|
|
209
209
|
cancel: async (input) => coordinator.cancel(input.sessionId),
|
|
210
|
+
sessions: async () => ({
|
|
211
|
+
result: "success",
|
|
212
|
+
sessions: [...coordinator.listSessions()],
|
|
213
|
+
}),
|
|
210
214
|
};
|
|
211
215
|
generation = {
|
|
212
216
|
captured,
|
|
@@ -243,7 +247,7 @@ class PiCodeModeLifecycleController {
|
|
|
243
247
|
} catch {
|
|
244
248
|
// Observer cleanup is presentation-only; execution resources still require release.
|
|
245
249
|
}
|
|
246
|
-
await coordinator.shutdown(
|
|
250
|
+
await coordinator.shutdown();
|
|
247
251
|
await sessionFiles.close();
|
|
248
252
|
if (this.generation === generation) this.generation = undefined;
|
|
249
253
|
this.notifyWarning(
|
|
@@ -253,13 +257,16 @@ class PiCodeModeLifecycleController {
|
|
|
253
257
|
return;
|
|
254
258
|
}
|
|
255
259
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
const [executeTool, resultTool, cancelTool, sessionsTool] =
|
|
261
|
+
createRenderedCodeModeToolDefinitions(
|
|
262
|
+
operations,
|
|
263
|
+
generation.executeDescription,
|
|
264
|
+
(sessionId) => coordinator.formatSessionPrefix(sessionId),
|
|
265
|
+
);
|
|
266
|
+
this.pi.registerTool(executeTool);
|
|
267
|
+
this.pi.registerTool(resultTool);
|
|
268
|
+
this.pi.registerTool(cancelTool);
|
|
269
|
+
this.pi.registerTool(sessionsTool);
|
|
263
270
|
generation.toolsRegistered = true;
|
|
264
271
|
this.synchronizeGeneration(generation);
|
|
265
272
|
}
|
|
@@ -382,12 +389,8 @@ class PiCodeModeLifecycleController {
|
|
|
382
389
|
now: CODEMODE_SYSTEM_RUNTIME.now,
|
|
383
390
|
signal: AbortSignal.any([batch.signal, terminationController.signal]),
|
|
384
391
|
onTerminate: () => terminationController.abort(),
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
Object.assign(bridgeOptions, { outerAssistantMessage });
|
|
388
|
-
}
|
|
389
|
-
if (batch.onUpdate !== undefined) {
|
|
390
|
-
Object.assign(bridgeOptions, {
|
|
392
|
+
...(outerAssistantMessage !== undefined && { outerAssistantMessage }),
|
|
393
|
+
...(batch.onUpdate !== undefined && {
|
|
391
394
|
onUpdate: (_callId: string, update: AgentToolResult<unknown>) => {
|
|
392
395
|
const outerUpdate: AgentToolResult<CodeModeResultDetails> = {
|
|
393
396
|
content: update.content,
|
|
@@ -395,8 +398,8 @@ class PiCodeModeLifecycleController {
|
|
|
395
398
|
};
|
|
396
399
|
batch.onUpdate?.(outerUpdate);
|
|
397
400
|
},
|
|
398
|
-
})
|
|
399
|
-
}
|
|
401
|
+
}),
|
|
402
|
+
};
|
|
400
403
|
const bridged = await executePiToolBridgeBatch(bridgeCaptured, bridgeOptions);
|
|
401
404
|
const bridgedResults = new Map<string, CodeModeNestedToolResult>(
|
|
402
405
|
bridged.calls.map((outcome) => [
|
|
@@ -424,16 +427,16 @@ class PiCodeModeLifecycleController {
|
|
|
424
427
|
"Pi CodeMode nested tool returned no result",
|
|
425
428
|
),
|
|
426
429
|
);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
430
|
+
return {
|
|
431
|
+
results,
|
|
432
|
+
presentation: bridged.presentation,
|
|
433
|
+
...(bridged.usage !== undefined && { usage: bridged.usage }),
|
|
434
|
+
...(bridged.addedToolNames.length > 0 && { addedToolNames: bridged.addedToolNames }),
|
|
435
|
+
...(bridged.terminate && { terminate: true }),
|
|
436
|
+
};
|
|
434
437
|
}
|
|
435
438
|
|
|
436
|
-
private async shutdownSession(
|
|
439
|
+
private async shutdownSession(): Promise<void> {
|
|
437
440
|
const generation = this.generation;
|
|
438
441
|
if (generation === undefined || !generation.active) return;
|
|
439
442
|
generation.active = false;
|
|
@@ -443,7 +446,7 @@ class PiCodeModeLifecycleController {
|
|
|
443
446
|
// Observer cleanup is presentation-only; execution resources still require release.
|
|
444
447
|
}
|
|
445
448
|
try {
|
|
446
|
-
await generation.coordinator.shutdown(
|
|
449
|
+
await generation.coordinator.shutdown();
|
|
447
450
|
} finally {
|
|
448
451
|
try {
|
|
449
452
|
await generation.sessionFiles.close();
|
package/src/pi-tool-bridge.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
parseCodeModeJsonValue,
|
|
13
13
|
type CodeModeJsonValue,
|
|
14
14
|
} from "./codemode-tool-contract.js";
|
|
15
|
+
import { addCodeModeUsage, boundedCodeModeElapsedMs } from "./codemode-session-coordinator.js";
|
|
15
16
|
import type { CapturedPiAgentSession } from "./pi-agent-session-capture.js";
|
|
16
17
|
|
|
17
18
|
const PI_TOOL_BRIDGE_RESULT_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
@@ -505,36 +506,6 @@ function translatePiToolResult(
|
|
|
505
506
|
};
|
|
506
507
|
}
|
|
507
508
|
|
|
508
|
-
function addUsage(left: Usage | undefined, right: Usage | undefined): Usage | undefined {
|
|
509
|
-
if (right === undefined) return left;
|
|
510
|
-
if (left === undefined) return right;
|
|
511
|
-
const reasoning =
|
|
512
|
-
left.reasoning === undefined && right.reasoning === undefined
|
|
513
|
-
? undefined
|
|
514
|
-
: (left.reasoning ?? 0) + (right.reasoning ?? 0);
|
|
515
|
-
const cacheWrite1h =
|
|
516
|
-
left.cacheWrite1h === undefined && right.cacheWrite1h === undefined
|
|
517
|
-
? undefined
|
|
518
|
-
: (left.cacheWrite1h ?? 0) + (right.cacheWrite1h ?? 0);
|
|
519
|
-
const combined: Usage = {
|
|
520
|
-
input: left.input + right.input,
|
|
521
|
-
output: left.output + right.output,
|
|
522
|
-
cacheRead: left.cacheRead + right.cacheRead,
|
|
523
|
-
cacheWrite: left.cacheWrite + right.cacheWrite,
|
|
524
|
-
totalTokens: left.totalTokens + right.totalTokens,
|
|
525
|
-
cost: {
|
|
526
|
-
input: left.cost.input + right.cost.input,
|
|
527
|
-
output: left.cost.output + right.cost.output,
|
|
528
|
-
cacheRead: left.cost.cacheRead + right.cost.cacheRead,
|
|
529
|
-
cacheWrite: left.cost.cacheWrite + right.cost.cacheWrite,
|
|
530
|
-
total: left.cost.total + right.cost.total,
|
|
531
|
-
},
|
|
532
|
-
};
|
|
533
|
-
if (cacheWrite1h !== undefined) combined.cacheWrite1h = cacheWrite1h;
|
|
534
|
-
if (reasoning !== undefined) combined.reasoning = reasoning;
|
|
535
|
-
return combined;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
509
|
function terminatedPiToolCall(callId: string): FinalizedPiToolCall {
|
|
539
510
|
return createBridgeFailure(
|
|
540
511
|
callId,
|
|
@@ -553,12 +524,6 @@ function presentationOutcome(
|
|
|
553
524
|
: "failed";
|
|
554
525
|
}
|
|
555
526
|
|
|
556
|
-
function elapsedMilliseconds(startedAt: number, finishedAt: number): number {
|
|
557
|
-
const elapsed = Math.round(finishedAt - startedAt);
|
|
558
|
-
if (!Number.isFinite(elapsed)) return 0;
|
|
559
|
-
return Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, elapsed));
|
|
560
|
-
}
|
|
561
|
-
|
|
562
527
|
function timedFinalizedPiToolCall(
|
|
563
528
|
call: PiToolBridgeCall,
|
|
564
529
|
finalized: FinalizedPiToolCall,
|
|
@@ -569,7 +534,7 @@ function timedFinalizedPiToolCall(
|
|
|
569
534
|
finalized,
|
|
570
535
|
presentation: {
|
|
571
536
|
callId: call.callId,
|
|
572
|
-
elapsedMs:
|
|
537
|
+
elapsedMs: boundedCodeModeElapsedMs(startedAt, now()),
|
|
573
538
|
name: call.name,
|
|
574
539
|
outcome: presentationOutcome(finalized.outcome),
|
|
575
540
|
},
|
|
@@ -590,7 +555,9 @@ function collectPiToolBridgeBatch(
|
|
|
590
555
|
const addedToolNames: string[] = [];
|
|
591
556
|
const seenToolNames = new Set<string>();
|
|
592
557
|
for (const { finalized } of timedCalls) {
|
|
593
|
-
|
|
558
|
+
if (finalized.usage !== undefined) {
|
|
559
|
+
usage = addCodeModeUsage(usage, finalized.usage);
|
|
560
|
+
}
|
|
594
561
|
for (const toolName of finalized.addedToolNames) {
|
|
595
562
|
if (seenToolNames.has(toolName)) continue;
|
|
596
563
|
seenToolNames.add(toolName);
|