@ian-pascoe/pi-codemode 0.2.0 → 0.3.1
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 +50 -13
- package/package.json +1 -1
- package/src/codemode-console-output.ts +11 -0
- package/src/codemode-observer-ui.ts +15 -36
- package/src/codemode-session-coordinator.ts +127 -90
- package/src/codemode-tool-catalog.ts +4 -6
- package/src/codemode-tool-contract.ts +37 -7
- package/src/codemode-tool-rendering.ts +71 -27
- package/src/codemode-worker-protocol.ts +141 -33
- package/src/codemode-worker.ts +283 -18
- package/src/pi-codemode-extension.ts +17 -21
- package/src/pi-tool-bridge.ts +5 -38
package/README.md
CHANGED
|
@@ -87,12 +87,18 @@ type CodeModeSessionsResult = {
|
|
|
87
87
|
The execute, result, and cancel tools return:
|
|
88
88
|
|
|
89
89
|
```ts
|
|
90
|
+
type CodeModeConsoleEntry = {
|
|
91
|
+
method: "log" | "info" | "warn" | "error" | "debug";
|
|
92
|
+
text: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
90
95
|
type CodeModeResult =
|
|
91
96
|
| {
|
|
92
97
|
result: "success";
|
|
93
98
|
sessionId: string;
|
|
94
99
|
data?: JsonValue;
|
|
95
100
|
reclaimedSessionId?: string;
|
|
101
|
+
console?: CodeModeConsoleEntry[];
|
|
96
102
|
}
|
|
97
103
|
| { result: "pending"; sessionId: string }
|
|
98
104
|
| {
|
|
@@ -112,6 +118,7 @@ type CodeModeResult =
|
|
|
112
118
|
| "runtime";
|
|
113
119
|
message: string;
|
|
114
120
|
};
|
|
121
|
+
console?: CodeModeConsoleEntry[];
|
|
115
122
|
};
|
|
116
123
|
```
|
|
117
124
|
|
|
@@ -119,15 +126,41 @@ type CodeModeResult =
|
|
|
119
126
|
result text returned to the model. Pi retains additional bounded Presentation
|
|
120
127
|
Snapshots in tool-result details for Transcript replay and the TUI.
|
|
121
128
|
|
|
129
|
+
Cells may call `console.log`, `console.info`, `console.warn`, `console.error`,
|
|
130
|
+
and `console.debug`. One call creates one ordered entry without a trailing
|
|
131
|
+
newline, while embedded newlines remain intact:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
console.log("answer:", 42);
|
|
135
|
+
return 42;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"result": "success",
|
|
141
|
+
"sessionId": "...",
|
|
142
|
+
"data": 42,
|
|
143
|
+
"console": [{ "method": "log", "text": "answer: 42" }]
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Formatting matches the pinned Deno Console for format tokens, primitives,
|
|
148
|
+
spacing, and multiline inspection. Getters, coercion hooks, and custom
|
|
149
|
+
inspectors do not run; CodeMode uses safe inspection instead when that differs
|
|
150
|
+
from Deno. Console output arrives only with terminal results. Ordinary script,
|
|
151
|
+
serialization, and worker-reported runtime failures retain prior calls. A
|
|
152
|
+
timeout, cancellation, termination, or process death may omit them because the
|
|
153
|
+
parent kills the worker before it can reply.
|
|
154
|
+
|
|
122
155
|
## Transcript and Observer UI
|
|
123
156
|
|
|
124
157
|
The CodeMode Transcript gives all four tools semantic collapsed and expanded
|
|
125
158
|
rendering. Collapsed rows prioritize Cell lifecycle, a short Session ID, Cell
|
|
126
|
-
Ordinal, returned-value shape, nested-tool count, and elapsed
|
|
127
|
-
rows show the full Session ID, explicit call arguments,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
presentation.
|
|
159
|
+
Ordinal, returned-value shape, Console-call count, nested-tool count, and elapsed
|
|
160
|
+
time. Expanded rows show the full Session ID, explicit call arguments,
|
|
161
|
+
TypeScript source, bounded Console output before structured returned data or the
|
|
162
|
+
error, and bounded nested-tool names, outcomes, and durations. Nested arguments
|
|
163
|
+
and raw nested outputs are never copied into the presentation.
|
|
131
164
|
|
|
132
165
|
Status always uses a symbol and text together:
|
|
133
166
|
|
|
@@ -138,7 +171,8 @@ Status always uses a symbol and text together:
|
|
|
138
171
|
|
|
139
172
|
Awaited Cells publish a presentation update immediately and once per second.
|
|
140
173
|
Collapsed calls show one highlighted line inline or the first eight highlighted
|
|
141
|
-
lines of a multi-line Cell
|
|
174
|
+
lines of a multi-line Cell, truncating long lines to the viewport width. Expanded
|
|
175
|
+
calls wrap long lines and show the complete TypeScript source.
|
|
142
176
|
Returned-data display uses Pi's 2,000-line/50-KB limit; complete oversized data
|
|
143
177
|
is written to a private Result Spill while the model-facing result remains
|
|
144
178
|
unchanged. Result Spill files last for the live Pi session. Replayed history
|
|
@@ -255,13 +289,16 @@ generated helper source out of ordinary source locations. Every operating-system
|
|
|
255
289
|
permission class is denied: filesystem read/write, network, environment, system
|
|
256
290
|
information, subprocesses, FFI, and remote imports.
|
|
257
291
|
|
|
258
|
-
Guest code receives ECMAScript built-ins, a read-only `tools` object,
|
|
259
|
-
frozen `Deno.version` identity. Raw
|
|
260
|
-
|
|
261
|
-
withheld.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
292
|
+
Guest code receives ECMAScript built-ins, a read-only `tools` object, a frozen
|
|
293
|
+
five-method Console facade, and only a frozen `Deno.version` identity. Raw
|
|
294
|
+
process and standard-stream access, `Worker`, timers, filesystem/network APIs,
|
|
295
|
+
and module loading are withheld. Console calls never write to worker streams and
|
|
296
|
+
do not include output from registered Pi tool handlers. The parent watchdog
|
|
297
|
+
terminates the subprocess for timeout or an infinite loop. Deno/V8 bounds each
|
|
298
|
+
Session to a 128 MiB old-space heap and a 1 MiB stack. Protocol inputs, tool
|
|
299
|
+
results, returned data, and Console entries share one 8 MiB UTF-8 worker-message
|
|
300
|
+
limit. An oversized response becomes the bounded `serialization` failure and
|
|
301
|
+
may omit its Console entries.
|
|
265
302
|
|
|
266
303
|
Registered Pi tools still execute in Pi's parent process with their normal
|
|
267
304
|
permissions and lifecycle hooks. Cancellation aborts them through Pi's
|
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Console methods captured from a CodeMode Cell in call order. */
|
|
2
|
+
export const CODEMODE_CONSOLE_METHODS = ["log", "info", "warn", "error", "debug"] as const;
|
|
3
|
+
|
|
4
|
+
/** One supported CodeMode Cell Console method. */
|
|
5
|
+
export type CodeModeConsoleMethod = (typeof CODEMODE_CONSOLE_METHODS)[number];
|
|
6
|
+
|
|
7
|
+
/** One captured CodeMode Cell Console call without a synthetic trailing newline. */
|
|
8
|
+
export type CodeModeConsoleEntry = {
|
|
9
|
+
readonly method: CodeModeConsoleMethod;
|
|
10
|
+
readonly text: string;
|
|
11
|
+
};
|
|
@@ -12,6 +12,11 @@ import type {
|
|
|
12
12
|
CodeModeObserverSnapshot,
|
|
13
13
|
CodeModeUnexpectedFailure,
|
|
14
14
|
} from "./codemode-session-coordinator.js";
|
|
15
|
+
import {
|
|
16
|
+
boundedCodeModeElapsedMs,
|
|
17
|
+
formatCodeModeDuration,
|
|
18
|
+
shortestUniqueCodeModeSessionPrefix,
|
|
19
|
+
} from "./codemode-session-coordinator.js";
|
|
15
20
|
|
|
16
21
|
const CODEMODE_OBSERVER_UI_KEY = "codemode-observer";
|
|
17
22
|
const CODEMODE_OBSERVER_ROW_LIMIT = 8;
|
|
@@ -131,25 +136,12 @@ function shortestUniqueCodeModeSessionPrefixes(
|
|
|
131
136
|
const safe = sanitizeCodeModeObserverText(session.sessionId);
|
|
132
137
|
return safe.length === 0 ? "unknown" : safe;
|
|
133
138
|
});
|
|
134
|
-
return identifiers.map((identifier, index) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
candidateIndex !== index && candidate.startsWith(identifier.slice(0, length)),
|
|
141
|
-
)
|
|
142
|
-
) {
|
|
143
|
-
length += 1;
|
|
144
|
-
}
|
|
145
|
-
return identifier.slice(0, length);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function boundedElapsedMilliseconds(nowMs: number, startedAtMs: number): number {
|
|
150
|
-
const elapsed = Math.round(nowMs - startedAtMs);
|
|
151
|
-
if (!Number.isFinite(elapsed)) return 0;
|
|
152
|
-
return Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, elapsed));
|
|
139
|
+
return identifiers.map((identifier, index) =>
|
|
140
|
+
shortestUniqueCodeModeSessionPrefix(
|
|
141
|
+
identifier,
|
|
142
|
+
identifiers.filter((_candidate, candidateIndex) => candidateIndex !== index),
|
|
143
|
+
),
|
|
144
|
+
);
|
|
153
145
|
}
|
|
154
146
|
|
|
155
147
|
function codeModeTerminalObserverState(
|
|
@@ -182,7 +174,7 @@ function projectCodeModeObserverRow(
|
|
|
182
174
|
sessionPrefix,
|
|
183
175
|
state: "running",
|
|
184
176
|
cellOrdinal: session.current_cell.ordinal,
|
|
185
|
-
elapsedMs:
|
|
177
|
+
elapsedMs: boundedCodeModeElapsedMs(session.current_cell.started_at_ms, nowMs),
|
|
186
178
|
activeToolNames,
|
|
187
179
|
activeToolCount: Math.max(session.current_cell.active_tool_count, activeToolNames.length),
|
|
188
180
|
};
|
|
@@ -232,13 +224,6 @@ export function buildCodeModeObserverView(
|
|
|
232
224
|
};
|
|
233
225
|
}
|
|
234
226
|
|
|
235
|
-
function formatCodeModeObserverDuration(elapsedMs: number): string {
|
|
236
|
-
if (elapsedMs < 1_000) return `${elapsedMs}ms`;
|
|
237
|
-
if (elapsedMs < 60_000) return `${(elapsedMs / 1_000).toFixed(1)}s`;
|
|
238
|
-
const seconds = Math.floor(elapsedMs / 1_000);
|
|
239
|
-
return `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
227
|
function formatCodeModeObserverToolActivity(
|
|
243
228
|
row: Extract<CodeModeObserverRow, { state: "running" }>,
|
|
244
229
|
): string | undefined {
|
|
@@ -256,10 +241,6 @@ function codeModeObserverRowDetail(row: CodeModeObserverRow): string {
|
|
|
256
241
|
: `Cell ${row.cellOrdinal}`;
|
|
257
242
|
}
|
|
258
243
|
|
|
259
|
-
function joinCodeModeObserverRow(parts: readonly string[], separator: string): string {
|
|
260
|
-
return parts.join(separator);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
244
|
function renderCodeModeObserverRow(
|
|
264
245
|
row: CodeModeObserverRow,
|
|
265
246
|
width: number,
|
|
@@ -270,9 +251,7 @@ function renderCodeModeObserverRow(
|
|
|
270
251
|
const status = theme.fg(presentation.color, presentation.label);
|
|
271
252
|
const detail = theme.fg("muted", codeModeObserverRowDetail(row));
|
|
272
253
|
const duration =
|
|
273
|
-
row.state === "running"
|
|
274
|
-
? theme.fg("muted", formatCodeModeObserverDuration(row.elapsedMs))
|
|
275
|
-
: undefined;
|
|
254
|
+
row.state === "running" ? theme.fg("muted", formatCodeModeDuration(row.elapsedMs)) : undefined;
|
|
276
255
|
const toolActivity =
|
|
277
256
|
row.state === "running" ? formatCodeModeObserverToolActivity(row) : undefined;
|
|
278
257
|
const themedToolActivity =
|
|
@@ -285,13 +264,13 @@ function renderCodeModeObserverRow(
|
|
|
285
264
|
[identity, status],
|
|
286
265
|
].map((parts) => parts.filter((part): part is string => part !== undefined));
|
|
287
266
|
for (const parts of candidates) {
|
|
288
|
-
const line =
|
|
267
|
+
const line = parts.join(separator);
|
|
289
268
|
if (visibleWidth(line) <= width) return line;
|
|
290
269
|
}
|
|
291
270
|
|
|
292
271
|
const compactSeparator = theme.fg("dim", CODEMODE_OBSERVER_COMPACT_SEPARATOR_TEXT);
|
|
293
272
|
const compactIdentity = `${theme.fg(presentation.color, presentation.symbol)} ${theme.bold(row.sessionPrefix)}`;
|
|
294
|
-
const compact =
|
|
273
|
+
const compact = [compactIdentity, status].join(compactSeparator);
|
|
295
274
|
if (visibleWidth(compact) <= width) return compact;
|
|
296
275
|
return truncateToWidth(compact, width, CODEMODE_OBSERVER_ELLIPSIS);
|
|
297
276
|
}
|