@oh-my-pi/pi-coding-agent 16.1.13 → 16.1.15
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/CHANGELOG.md +48 -0
- package/dist/cli.js +5615 -2833
- package/dist/types/advisor/runtime.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +64 -14
- package/dist/types/eval/__tests__/julia-prelude.test.d.ts +1 -0
- package/dist/types/eval/backend-helpers.d.ts +23 -0
- package/dist/types/eval/executor-base.d.ts +118 -0
- package/dist/types/eval/index.d.ts +2 -0
- package/dist/types/eval/jl/executor.d.ts +44 -0
- package/dist/types/eval/jl/index.d.ts +11 -0
- package/dist/types/eval/jl/kernel.d.ts +28 -0
- package/dist/types/eval/jl/prelude.d.ts +1 -0
- package/dist/types/eval/jl/runtime.d.ts +22 -0
- package/dist/types/eval/kernel-base.d.ts +105 -0
- package/dist/types/eval/py/kernel.d.ts +3 -61
- package/dist/types/eval/rb/executor.d.ts +77 -0
- package/dist/types/eval/rb/index.d.ts +11 -0
- package/dist/types/eval/rb/kernel.d.ts +31 -0
- package/dist/types/eval/rb/prelude.d.ts +1 -0
- package/dist/types/eval/rb/runtime.d.ts +23 -0
- package/dist/types/eval/runtime-env.d.ts +24 -0
- package/dist/types/eval/types.d.ts +3 -3
- package/dist/types/export/share.d.ts +8 -1
- package/dist/types/extensibility/extensions/runner.d.ts +2 -15
- package/dist/types/extensibility/hooks/loader.d.ts +1 -25
- package/dist/types/extensibility/session-handler-types.d.ts +23 -0
- package/dist/types/jsonrpc/message-framing.d.ts +35 -0
- package/dist/types/mcp/transports/stdio.d.ts +12 -1
- package/dist/types/mnemopi/embed-client.d.ts +7 -24
- package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +11 -0
- package/dist/types/modes/components/selector-helpers.d.ts +53 -0
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +4 -3
- package/dist/types/modes/components/status-line/types.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +8 -1
- package/dist/types/modes/types.d.ts +0 -2
- package/dist/types/modes/utils/copy-targets.d.ts +1 -1
- package/dist/types/modes/utils/interactive-context-helpers.d.ts +14 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +54 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +3 -3
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/stt/asr-client.d.ts +3 -29
- package/dist/types/subprocess/worker-client.d.ts +149 -0
- package/dist/types/subprocess/worker-runtime.d.ts +107 -0
- package/dist/types/tiny/title-client.d.ts +14 -34
- package/dist/types/tools/eval-backends.d.ts +6 -3
- package/dist/types/tools/eval-render.d.ts +6 -5
- package/dist/types/tools/eval.d.ts +13 -15
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tts/tts-client.d.ts +3 -28
- package/dist/types/tui/code-cell.d.ts +7 -0
- package/dist/types/utils/file-display-mode.d.ts +1 -1
- package/dist/types/utils/shell-snapshot.d.ts +10 -0
- package/dist/types/web/parallel.d.ts +6 -0
- package/dist/types/web/search/providers/perplexity.d.ts +17 -3
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +114 -0
- package/src/advisor/runtime.ts +129 -1
- package/src/config/model-registry.ts +12 -4
- package/src/config/settings-schema.ts +74 -18
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +13 -107
- package/src/eval/__tests__/julia-prelude.test.ts +77 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/executor-base.ts +425 -0
- package/src/eval/index.ts +2 -0
- package/src/eval/jl/executor.ts +540 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +235 -0
- package/src/eval/jl/prelude.jl +930 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +634 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/index.ts +3 -14
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/executor.ts +43 -252
- package/src/eval/py/index.ts +9 -20
- package/src/eval/py/kernel.ts +29 -544
- package/src/eval/rb/executor.ts +504 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +230 -0
- package/src/eval/rb/prelude.rb +721 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +474 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/types.ts +3 -3
- package/src/exec/bash-executor.ts +44 -0
- package/src/export/share.ts +51 -28
- package/src/extensibility/extensions/runner.ts +4 -11
- package/src/extensibility/hooks/loader.ts +3 -21
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/lsp/client.ts +13 -109
- package/src/mcp/transports/stdio.ts +20 -4
- package/src/mnemopi/embed-client.ts +43 -198
- package/src/modes/components/agent-dashboard.ts +17 -40
- package/src/modes/components/chat-transcript-builder.ts +18 -102
- package/src/modes/components/custom-editor.test.ts +22 -0
- package/src/modes/components/custom-editor.ts +29 -1
- package/src/modes/components/extensions/extension-dashboard.ts +4 -13
- package/src/modes/components/extensions/extension-list.ts +16 -44
- package/src/modes/components/footer.ts +4 -3
- package/src/modes/components/history-search.ts +4 -16
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/settings-selector.ts +7 -5
- package/src/modes/components/status-line/component.ts +5 -1
- package/src/modes/components/status-line/context-thresholds.ts +11 -3
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +1 -0
- package/src/modes/components/tree-selector.ts +13 -18
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -9
- package/src/modes/controllers/input-controller.ts +32 -54
- package/src/modes/interactive-mode.ts +5 -7
- package/src/modes/theme/theme.ts +35 -0
- package/src/modes/types.ts +0 -2
- package/src/modes/utils/copy-targets.ts +3 -2
- package/src/modes/utils/interactive-context-helpers.ts +27 -0
- package/src/modes/utils/transcript-render-helpers.ts +157 -0
- package/src/modes/utils/ui-helpers.ts +21 -126
- package/src/prompts/system/system-prompt.md +5 -5
- package/src/prompts/tools/bash.md +2 -3
- package/src/prompts/tools/eval.md +6 -4
- package/src/prompts/tools/find.md +0 -4
- package/src/prompts/tools/read.md +1 -2
- package/src/prompts/tools/replace.md +1 -1
- package/src/sdk.ts +13 -7
- package/src/secrets/obfuscator.ts +3 -9
- package/src/session/agent-session.ts +42 -9
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/stt/asr-client.ts +35 -215
- package/src/stt/asr-worker.ts +29 -181
- package/src/subprocess/worker-client.ts +297 -0
- package/src/subprocess/worker-runtime.ts +277 -0
- package/src/task/executor.ts +4 -4
- package/src/tiny/title-client.ts +53 -219
- package/src/tiny/worker.ts +29 -180
- package/src/tools/eval-backends.ts +10 -3
- package/src/tools/eval-render.ts +17 -8
- package/src/tools/eval.ts +187 -22
- package/src/tools/index.ts +51 -28
- package/src/tts/tts-client.ts +38 -206
- package/src/tts/tts-worker.ts +23 -97
- package/src/tui/code-cell.ts +12 -1
- package/src/utils/file-display-mode.ts +2 -3
- package/src/utils/shell-snapshot.ts +63 -1
- package/src/web/parallel.ts +43 -42
- package/src/web/search/providers/parallel.ts +10 -99
- package/src/web/search/providers/perplexity.ts +18 -6
package/src/eval/py/executor.ts
CHANGED
|
@@ -2,22 +2,29 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
|
|
4
4
|
import { getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import { Settings } from "../../config/settings";
|
|
6
|
-
import { OutputSink } from "../../session/streaming-output";
|
|
7
5
|
import type { ToolSession } from "../../tools";
|
|
8
|
-
import {
|
|
9
|
-
|
|
6
|
+
import {
|
|
7
|
+
attachSessionOwner,
|
|
8
|
+
buildManagedKernelEnv,
|
|
9
|
+
buildManagedKernelEnvPatch,
|
|
10
|
+
createCancelledKernelResult,
|
|
11
|
+
executeWithKernelBase,
|
|
12
|
+
getExecutionDeadlineMs,
|
|
13
|
+
getRemainingTimeoutMs,
|
|
14
|
+
isCancellationError,
|
|
15
|
+
isTimedOutCancellation,
|
|
16
|
+
waitForPromiseWithCancellation,
|
|
17
|
+
} from "../executor-base";
|
|
10
18
|
import type { JsStatusEvent } from "../js/shared/types";
|
|
11
19
|
import {
|
|
12
20
|
checkPythonKernelAvailability,
|
|
13
21
|
type KernelDisplayOutput,
|
|
14
22
|
type KernelExecuteOptions,
|
|
15
23
|
type KernelExecuteResult,
|
|
16
|
-
type KernelRuntimeEnv,
|
|
17
24
|
PythonKernel,
|
|
18
25
|
} from "./kernel";
|
|
19
26
|
import { resolveExplicitPythonRuntime } from "./runtime";
|
|
20
|
-
import { ensurePyToolBridge
|
|
27
|
+
import { ensurePyToolBridge } from "./tool-bridge";
|
|
21
28
|
|
|
22
29
|
export type PythonKernelMode = "session" | "per-call";
|
|
23
30
|
|
|
@@ -170,22 +177,11 @@ class PythonExecutionCancelledError extends Error {
|
|
|
170
177
|
|
|
171
178
|
constructor(timedOut: boolean) {
|
|
172
179
|
super(timedOut ? "Command timed out" : "Command aborted");
|
|
173
|
-
this.name =
|
|
180
|
+
this.name = "PythonExecutionCancelledError";
|
|
174
181
|
this.timedOut = timedOut;
|
|
175
182
|
}
|
|
176
183
|
}
|
|
177
184
|
|
|
178
|
-
function getExecutionDeadlineMs(options?: Pick<PythonExecutorOptions, "deadlineMs" | "timeoutMs">): number | undefined {
|
|
179
|
-
if (options?.deadlineMs !== undefined) return options.deadlineMs;
|
|
180
|
-
if (options?.timeoutMs === undefined) return undefined;
|
|
181
|
-
return Date.now() + options.timeoutMs;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function getRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
185
|
-
if (deadlineMs === undefined) return undefined;
|
|
186
|
-
return deadlineMs - Date.now();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
185
|
function requireRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
190
186
|
const remainingMs = getRemainingTimeoutMs(deadlineMs);
|
|
191
187
|
if (remainingMs === undefined) return undefined;
|
|
@@ -195,64 +191,6 @@ function requireRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
|
195
191
|
return remainingMs;
|
|
196
192
|
}
|
|
197
193
|
|
|
198
|
-
function isCancellationError(error: unknown): boolean {
|
|
199
|
-
return (
|
|
200
|
-
error instanceof PythonExecutionCancelledError ||
|
|
201
|
-
(error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) ||
|
|
202
|
-
(error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function isTimedOutCancellation(error: unknown, signal?: AbortSignal): boolean {
|
|
207
|
-
if (error instanceof PythonExecutionCancelledError) return error.timedOut;
|
|
208
|
-
if (error instanceof DOMException) return error.name === "TimeoutError";
|
|
209
|
-
if (error instanceof Error && error.name === "TimeoutError") return true;
|
|
210
|
-
const reason = signal?.reason;
|
|
211
|
-
if (reason instanceof DOMException) return reason.name === "TimeoutError";
|
|
212
|
-
return reason instanceof Error ? reason.name === "TimeoutError" : false;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
async function waitForPromiseWithCancellation<T>(
|
|
216
|
-
promise: Promise<T>,
|
|
217
|
-
options: Pick<PythonExecutorOptions, "signal" | "deadlineMs">,
|
|
218
|
-
): Promise<T> {
|
|
219
|
-
if (options.signal?.aborted) {
|
|
220
|
-
throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
|
221
|
-
}
|
|
222
|
-
const remainingMs = getRemainingTimeoutMs(options.deadlineMs);
|
|
223
|
-
if (remainingMs !== undefined && remainingMs <= 0) {
|
|
224
|
-
throw new PythonExecutionCancelledError(true);
|
|
225
|
-
}
|
|
226
|
-
if (!options.signal && remainingMs === undefined) {
|
|
227
|
-
return await promise;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const { promise: resultPromise, resolve, reject } = Promise.withResolvers<T>();
|
|
231
|
-
const cleanups: Array<() => void> = [];
|
|
232
|
-
const finish = (cb: () => void): void => {
|
|
233
|
-
while (cleanups.length > 0) cleanups.pop()?.();
|
|
234
|
-
cb();
|
|
235
|
-
};
|
|
236
|
-
if (options.signal) {
|
|
237
|
-
const onAbort = (): void =>
|
|
238
|
-
finish(() =>
|
|
239
|
-
reject(new PythonExecutionCancelledError(isTimedOutCancellation(options.signal?.reason, options.signal))),
|
|
240
|
-
);
|
|
241
|
-
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
242
|
-
cleanups.push(() => options.signal?.removeEventListener("abort", onAbort));
|
|
243
|
-
}
|
|
244
|
-
if (remainingMs !== undefined) {
|
|
245
|
-
const timer = setTimeout(() => finish(() => reject(new PythonExecutionCancelledError(true))), remainingMs);
|
|
246
|
-
timer.unref();
|
|
247
|
-
cleanups.push(() => clearTimeout(timer));
|
|
248
|
-
}
|
|
249
|
-
promise.then(
|
|
250
|
-
value => finish(() => resolve(value)),
|
|
251
|
-
err => finish(() => reject(err)),
|
|
252
|
-
);
|
|
253
|
-
return await resultPromise;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
194
|
// ---------------------------------------------------------------------------
|
|
257
195
|
// Result formatting
|
|
258
196
|
// ---------------------------------------------------------------------------
|
|
@@ -274,95 +212,24 @@ function formatKernelTimeoutAnnotation(timeoutMs: number | undefined, kernelKill
|
|
|
274
212
|
|
|
275
213
|
function createCancelledPythonResult(timedOut: boolean, timeoutMs?: number): PythonResult {
|
|
276
214
|
const output = timedOut ? (formatTimeoutAnnotation(timeoutMs) ?? "Command timed out") : "";
|
|
277
|
-
|
|
278
|
-
const outputLines = output.length > 0 ? 1 : 0;
|
|
279
|
-
return {
|
|
280
|
-
output,
|
|
281
|
-
exitCode: undefined,
|
|
282
|
-
cancelled: true,
|
|
283
|
-
truncated: false,
|
|
284
|
-
totalLines: outputLines,
|
|
285
|
-
totalBytes: outputBytes,
|
|
286
|
-
outputLines,
|
|
287
|
-
outputBytes,
|
|
288
|
-
displayOutputs: [],
|
|
289
|
-
stdinRequested: false,
|
|
290
|
-
};
|
|
215
|
+
return createCancelledKernelResult(output);
|
|
291
216
|
}
|
|
292
217
|
|
|
293
218
|
// ---------------------------------------------------------------------------
|
|
294
219
|
// Kernel start helpers
|
|
295
220
|
// ---------------------------------------------------------------------------
|
|
296
221
|
|
|
297
|
-
const MANAGED_KERNEL_ENV_KEYS = [
|
|
298
|
-
"PI_SESSION_FILE",
|
|
299
|
-
"PI_ARTIFACTS_DIR",
|
|
300
|
-
"PI_TOOL_BRIDGE_URL",
|
|
301
|
-
"PI_TOOL_BRIDGE_TOKEN",
|
|
302
|
-
"PI_TOOL_BRIDGE_SESSION",
|
|
303
|
-
"PI_EVAL_LOCAL_ROOTS",
|
|
304
|
-
] as const;
|
|
305
|
-
|
|
306
|
-
function buildKernelEnvPatch(options: {
|
|
307
|
-
sessionFile?: string;
|
|
308
|
-
artifactsDir?: string;
|
|
309
|
-
bridgeSessionId?: string;
|
|
310
|
-
bridge?: { url: string; token: string };
|
|
311
|
-
localRoots?: Record<string, string>;
|
|
312
|
-
}): KernelRuntimeEnv {
|
|
313
|
-
const localRoots = options.localRoots;
|
|
314
|
-
return {
|
|
315
|
-
PI_SESSION_FILE: options.sessionFile ?? null,
|
|
316
|
-
PI_ARTIFACTS_DIR: options.artifactsDir ?? null,
|
|
317
|
-
PI_TOOL_BRIDGE_URL: options.bridge?.url ?? null,
|
|
318
|
-
PI_TOOL_BRIDGE_TOKEN: options.bridge?.token ?? null,
|
|
319
|
-
PI_TOOL_BRIDGE_SESSION: options.bridge && options.bridgeSessionId ? options.bridgeSessionId : null,
|
|
320
|
-
PI_EVAL_LOCAL_ROOTS: localRoots && Object.keys(localRoots).length > 0 ? JSON.stringify(localRoots) : null,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function buildKernelEnv(options: {
|
|
325
|
-
sessionFile?: string;
|
|
326
|
-
artifactsDir?: string;
|
|
327
|
-
bridgeSessionId?: string;
|
|
328
|
-
bridge?: { url: string; token: string };
|
|
329
|
-
localRoots?: Record<string, string>;
|
|
330
|
-
}): Record<string, string> | undefined {
|
|
331
|
-
const patch = buildKernelEnvPatch(options);
|
|
332
|
-
const env: Record<string, string> = {};
|
|
333
|
-
for (const key of MANAGED_KERNEL_ENV_KEYS) {
|
|
334
|
-
const value = patch[key];
|
|
335
|
-
if (value !== null) env[key] = value;
|
|
336
|
-
}
|
|
337
|
-
return Object.keys(env).length > 0 ? env : undefined;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
222
|
async function startKernel(cwd: string, options: PythonExecutorOptions): Promise<PythonKernel> {
|
|
341
223
|
requireRemainingTimeoutMs(options.deadlineMs);
|
|
342
224
|
return await PythonKernel.start({
|
|
343
225
|
cwd,
|
|
344
|
-
env:
|
|
226
|
+
env: buildManagedKernelEnv(options),
|
|
345
227
|
signal: options.signal,
|
|
346
228
|
deadlineMs: options.deadlineMs,
|
|
347
229
|
interpreter: options.interpreter,
|
|
348
230
|
});
|
|
349
231
|
}
|
|
350
232
|
|
|
351
|
-
function attachOwner(session: PythonSession, sessionId: string, ownerId: string | undefined): void {
|
|
352
|
-
if (ownerId !== undefined) {
|
|
353
|
-
if (session.hasFallbackOwner) {
|
|
354
|
-
session.ownerIds.delete(sessionId);
|
|
355
|
-
session.hasFallbackOwner = false;
|
|
356
|
-
}
|
|
357
|
-
session.ownerIds.add(ownerId);
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
if (session.hasFallbackOwner || session.ownerIds.size === 0) {
|
|
361
|
-
session.ownerIds.add(sessionId);
|
|
362
|
-
session.hasFallbackOwner = true;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
233
|
async function acquireSession(
|
|
367
234
|
sessionKey: string,
|
|
368
235
|
sessionId: string,
|
|
@@ -371,13 +238,13 @@ async function acquireSession(
|
|
|
371
238
|
): Promise<PythonSession> {
|
|
372
239
|
const existing = sessions.get(sessionKey);
|
|
373
240
|
if (existing) {
|
|
374
|
-
|
|
241
|
+
attachSessionOwner(existing, sessionId, options.kernelOwnerId);
|
|
375
242
|
return existing;
|
|
376
243
|
}
|
|
377
244
|
const starting = startingSessions.get(sessionKey);
|
|
378
245
|
if (starting) {
|
|
379
246
|
const session = await starting;
|
|
380
|
-
|
|
247
|
+
attachSessionOwner(session, sessionId, options.kernelOwnerId);
|
|
381
248
|
return session;
|
|
382
249
|
}
|
|
383
250
|
const startup = (async () => {
|
|
@@ -396,7 +263,7 @@ async function acquireSession(
|
|
|
396
263
|
startingSessions.set(sessionKey, startup);
|
|
397
264
|
try {
|
|
398
265
|
const session = await startup;
|
|
399
|
-
|
|
266
|
+
attachSessionOwner(session, sessionId, options.kernelOwnerId);
|
|
400
267
|
return session;
|
|
401
268
|
} finally {
|
|
402
269
|
if (startingSessions.get(sessionKey) === startup) startingSessions.delete(sessionKey);
|
|
@@ -507,108 +374,24 @@ async function executeWithKernel(
|
|
|
507
374
|
code: string,
|
|
508
375
|
options: PythonExecutorOptions | undefined,
|
|
509
376
|
): Promise<PythonResult> {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
377
|
+
return executeWithKernelBase<PythonExecutorOptions>({
|
|
378
|
+
kernel,
|
|
379
|
+
code,
|
|
380
|
+
options,
|
|
381
|
+
runIdPrefix: "py",
|
|
382
|
+
errorLogLabel: "Python",
|
|
383
|
+
cancelledErrorClass: PythonExecutionCancelledError,
|
|
384
|
+
buildKernelEnvPatch: buildManagedKernelEnvPatch,
|
|
385
|
+
formatKernelTimeoutAnnotation,
|
|
386
|
+
formatTimeoutAnnotation,
|
|
517
387
|
});
|
|
518
|
-
const displayOutputs: KernelDisplayOutput[] = [];
|
|
519
|
-
const deadlineMs = getExecutionDeadlineMs(options);
|
|
520
|
-
let executionTimeoutMs: number | undefined;
|
|
521
|
-
|
|
522
|
-
// Collect every display output and, for status events, stream them live so
|
|
523
|
-
// long-running bridge helpers (e.g. `agent()`) surface progress mid-cell.
|
|
524
|
-
const collectDisplay = (output: KernelDisplayOutput) => {
|
|
525
|
-
if (output.type === "status") {
|
|
526
|
-
// Timeout-control events drive the eval watchdog only; never store or
|
|
527
|
-
// render them as cell output.
|
|
528
|
-
options?.onStatus?.(output.event);
|
|
529
|
-
if (isEvalTimeoutControlEvent(output.event)) return;
|
|
530
|
-
}
|
|
531
|
-
displayOutputs.push(output);
|
|
532
|
-
};
|
|
533
|
-
const emitStatus = options?.emitStatus ?? ((event: JsStatusEvent) => collectDisplay({ type: "status", event }));
|
|
534
|
-
const runId = `py-${crypto.randomUUID()}`;
|
|
535
|
-
const unregisterBridge =
|
|
536
|
-
options?.toolSession && options?.bridgeSessionId
|
|
537
|
-
? registerPyToolBridge(options.bridgeSessionId, runId, {
|
|
538
|
-
toolSession: options.toolSession,
|
|
539
|
-
signal: options.signal,
|
|
540
|
-
emitStatus,
|
|
541
|
-
})
|
|
542
|
-
: null;
|
|
543
|
-
|
|
544
|
-
try {
|
|
545
|
-
executionTimeoutMs = requireRemainingTimeoutMs(deadlineMs);
|
|
546
|
-
const result = await kernel.execute(code, {
|
|
547
|
-
cwd: options?.cwd,
|
|
548
|
-
env: buildKernelEnvPatch(options ?? {}),
|
|
549
|
-
id: runId,
|
|
550
|
-
signal: options?.signal,
|
|
551
|
-
timeoutMs: executionTimeoutMs,
|
|
552
|
-
onChunk: text => sink.push(text),
|
|
553
|
-
onDisplay: output => collectDisplay(output),
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
if (result.cancelled) {
|
|
557
|
-
const annotation = result.timedOut
|
|
558
|
-
? formatKernelTimeoutAnnotation(executionTimeoutMs ?? options?.idleTimeoutMs, result.kernelKilled ?? false)
|
|
559
|
-
: undefined;
|
|
560
|
-
return {
|
|
561
|
-
exitCode: undefined,
|
|
562
|
-
cancelled: true,
|
|
563
|
-
displayOutputs,
|
|
564
|
-
stdinRequested: result.stdinRequested,
|
|
565
|
-
...(await sink.dump(annotation)),
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (result.stdinRequested) {
|
|
570
|
-
return {
|
|
571
|
-
exitCode: 1,
|
|
572
|
-
cancelled: false,
|
|
573
|
-
displayOutputs,
|
|
574
|
-
stdinRequested: true,
|
|
575
|
-
...(await sink.dump("Kernel requested stdin; interactive input is not supported.")),
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const exitCode = result.status === "ok" ? 0 : 1;
|
|
580
|
-
return {
|
|
581
|
-
exitCode,
|
|
582
|
-
cancelled: false,
|
|
583
|
-
displayOutputs,
|
|
584
|
-
stdinRequested: false,
|
|
585
|
-
...(await sink.dump()),
|
|
586
|
-
};
|
|
587
|
-
} catch (err) {
|
|
588
|
-
if (isCancellationError(err) || options?.signal?.aborted) {
|
|
589
|
-
const timedOut = isTimedOutCancellation(err, options?.signal);
|
|
590
|
-
return {
|
|
591
|
-
exitCode: undefined,
|
|
592
|
-
cancelled: true,
|
|
593
|
-
displayOutputs,
|
|
594
|
-
stdinRequested: false,
|
|
595
|
-
...(await sink.dump(
|
|
596
|
-
timedOut ? formatTimeoutAnnotation(executionTimeoutMs ?? options?.idleTimeoutMs) : undefined,
|
|
597
|
-
)),
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
601
|
-
logger.error("Python execution failed", { error: error.message });
|
|
602
|
-
throw error;
|
|
603
|
-
} finally {
|
|
604
|
-
unregisterBridge?.();
|
|
605
|
-
}
|
|
606
388
|
}
|
|
607
389
|
|
|
608
390
|
async function ensureKernelAvailable(cwd: string, options: PythonExecutorOptions): Promise<void> {
|
|
609
391
|
const availability = await waitForPromiseWithCancellation(
|
|
610
392
|
checkPythonKernelAvailability(cwd, options.interpreter),
|
|
611
393
|
options,
|
|
394
|
+
PythonExecutionCancelledError,
|
|
612
395
|
);
|
|
613
396
|
if (!availability.ok) {
|
|
614
397
|
throw new Error(availability.reason ?? "Python kernel unavailable");
|
|
@@ -671,7 +454,9 @@ async function executeOnSession(code: string, cwd: string, options: PythonExecut
|
|
|
671
454
|
}
|
|
672
455
|
const session = await acquireSession(sessionKey, sessionId, cwd, options);
|
|
673
456
|
if (options.signal?.aborted) {
|
|
674
|
-
throw new PythonExecutionCancelledError(
|
|
457
|
+
throw new PythonExecutionCancelledError(
|
|
458
|
+
isTimedOutCancellation(options.signal.reason, PythonExecutionCancelledError, options.signal),
|
|
459
|
+
);
|
|
675
460
|
}
|
|
676
461
|
if (sessions.get(session.sessionKey) !== session) {
|
|
677
462
|
throw new PythonExecutionCancelledError(false);
|
|
@@ -686,7 +471,7 @@ async function executeOnSession(code: string, cwd: string, options: PythonExecut
|
|
|
686
471
|
try {
|
|
687
472
|
return await executeWithKernel(session.kernel, code, runOptions);
|
|
688
473
|
} catch (err) {
|
|
689
|
-
if (isCancellationError(err) || options.signal?.aborted) throw err;
|
|
474
|
+
if (isCancellationError(err, PythonExecutionCancelledError) || options.signal?.aborted) throw err;
|
|
690
475
|
if (session.kernel.isAlive()) throw err;
|
|
691
476
|
if (sessions.get(session.sessionKey) !== session) {
|
|
692
477
|
throw new PythonExecutionCancelledError(false);
|
|
@@ -722,7 +507,11 @@ export async function executePython(code: string, options?: PythonExecutorOption
|
|
|
722
507
|
requireRemainingTimeoutMs(deadlineMs);
|
|
723
508
|
if (executionOptions.signal?.aborted) {
|
|
724
509
|
throw new PythonExecutionCancelledError(
|
|
725
|
-
isTimedOutCancellation(
|
|
510
|
+
isTimedOutCancellation(
|
|
511
|
+
executionOptions.signal.reason,
|
|
512
|
+
PythonExecutionCancelledError,
|
|
513
|
+
executionOptions.signal,
|
|
514
|
+
),
|
|
726
515
|
);
|
|
727
516
|
}
|
|
728
517
|
await ensureKernelAvailable(cwd, executionOptions);
|
|
@@ -734,8 +523,10 @@ export async function executePython(code: string, options?: PythonExecutorOption
|
|
|
734
523
|
}
|
|
735
524
|
return await executeOnSession(code, cwd, executionOptions);
|
|
736
525
|
} catch (err) {
|
|
737
|
-
if (isCancellationError(err) || executionOptions.signal?.aborted) {
|
|
738
|
-
return createCancelledPythonResult(
|
|
526
|
+
if (isCancellationError(err, PythonExecutionCancelledError) || executionOptions.signal?.aborted) {
|
|
527
|
+
return createCancelledPythonResult(
|
|
528
|
+
isTimedOutCancellation(err, PythonExecutionCancelledError, executionOptions.signal),
|
|
529
|
+
);
|
|
739
530
|
}
|
|
740
531
|
throw err;
|
|
741
532
|
}
|
package/src/eval/py/index.ts
CHANGED
|
@@ -5,24 +5,24 @@ import {
|
|
|
5
5
|
type ExecutorBackendResult,
|
|
6
6
|
resolveEvalUrlRoots,
|
|
7
7
|
} from "../backend";
|
|
8
|
+
import {
|
|
9
|
+
readSetting,
|
|
10
|
+
namespaceSessionId as sharedNamespace,
|
|
11
|
+
readInterpreterSetting as sharedReadInterpreterSetting,
|
|
12
|
+
toExecutorBackendResult,
|
|
13
|
+
} from "../backend-helpers";
|
|
8
14
|
import { executePython, type PythonExecutorOptions } from "./executor";
|
|
9
15
|
import { checkPythonKernelAvailability } from "./kernel";
|
|
10
16
|
|
|
11
17
|
const PYTHON_SESSION_PREFIX = "python:";
|
|
12
18
|
|
|
13
19
|
export function namespaceSessionId(sessionId: string): string {
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function readSetting<T>(session: ToolSession, key: string): T | undefined {
|
|
18
|
-
const settings = session.settings as { get?: (key: string) => T | undefined } | undefined;
|
|
19
|
-
return settings?.get?.(key);
|
|
20
|
+
return sharedNamespace(sessionId, PYTHON_SESSION_PREFIX);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function readInterpreterSetting(session: ToolSession): string | undefined {
|
|
23
|
-
return
|
|
24
|
+
return sharedReadInterpreterSetting(session, "python.interpreter");
|
|
24
25
|
}
|
|
25
|
-
|
|
26
26
|
export default {
|
|
27
27
|
id: "python",
|
|
28
28
|
label: "Python",
|
|
@@ -52,17 +52,6 @@ export default {
|
|
|
52
52
|
toolSession: opts.session,
|
|
53
53
|
};
|
|
54
54
|
const result = await executePython(code, executorOptions);
|
|
55
|
-
return
|
|
56
|
-
output: result.output,
|
|
57
|
-
exitCode: result.exitCode,
|
|
58
|
-
cancelled: result.cancelled,
|
|
59
|
-
truncated: result.truncated,
|
|
60
|
-
artifactId: result.artifactId,
|
|
61
|
-
totalLines: result.totalLines,
|
|
62
|
-
totalBytes: result.totalBytes,
|
|
63
|
-
outputLines: result.outputLines,
|
|
64
|
-
outputBytes: result.outputBytes,
|
|
65
|
-
displayOutputs: result.displayOutputs,
|
|
66
|
-
};
|
|
55
|
+
return toExecutorBackendResult(result);
|
|
67
56
|
},
|
|
68
57
|
} satisfies ExecutorBackend;
|