@oh-my-pi/pi-coding-agent 13.15.3 → 13.16.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/CHANGELOG.md +30 -16
- package/package.json +7 -7
- package/src/commit/agentic/tools/analyze-file.ts +1 -0
- package/src/config/model-registry.ts +215 -57
- package/src/config/settings-schema.ts +27 -0
- package/src/extensibility/custom-tools/types.ts +3 -0
- package/src/extensibility/extensions/runner.ts +7 -0
- package/src/extensibility/extensions/types.ts +10 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/ipy/cancellation.ts +28 -0
- package/src/ipy/executor.ts +252 -77
- package/src/ipy/kernel.ts +181 -35
- package/src/ipy/modules.ts +39 -4
- package/src/modes/acp/acp-agent.ts +1 -0
- package/src/modes/components/hook-editor.ts +57 -8
- package/src/modes/components/model-selector.ts +48 -29
- package/src/modes/components/settings-defs.ts +10 -1
- package/src/modes/components/settings-selector.ts +92 -5
- package/src/modes/controllers/extension-ui-controller.ts +35 -4
- package/src/modes/controllers/input-controller.ts +4 -3
- package/src/modes/controllers/selector-controller.ts +2 -2
- package/src/modes/interactive-mode.ts +7 -2
- package/src/modes/print-mode.ts +1 -0
- package/src/modes/prompt-action-autocomplete.ts +5 -3
- package/src/modes/rpc/rpc-mode.ts +79 -30
- package/src/modes/rpc/rpc-types.ts +9 -1
- package/src/modes/theme/theme.ts +70 -0
- package/src/modes/types.ts +6 -1
- package/src/prompts/system/custom-system-prompt.md +5 -0
- package/src/prompts/system/system-prompt.md +6 -0
- package/src/prompts/tools/ask.md +1 -0
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/hashline.md +20 -5
- package/src/sdk.ts +26 -2
- package/src/session/agent-session.ts +18 -11
- package/src/system-prompt.ts +63 -2
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +2 -0
- package/src/tools/ask.ts +109 -61
- package/src/tools/ast-edit.ts +2 -16
- package/src/tools/ast-grep.ts +2 -17
- package/src/tools/browser.ts +35 -17
- package/src/tools/find.ts +1 -0
- package/src/tools/grep.ts +25 -34
- package/src/tools/index.ts +3 -0
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/python.ts +3 -2
- package/src/tools/render-utils.ts +27 -0
- package/src/tui/tree-list.ts +51 -22
package/src/ipy/executor.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface PythonExecutorOptions {
|
|
|
24
24
|
cwd?: string;
|
|
25
25
|
/** Timeout in milliseconds */
|
|
26
26
|
timeoutMs?: number;
|
|
27
|
+
/** Absolute wall-clock deadline in milliseconds since epoch */
|
|
28
|
+
deadlineMs?: number;
|
|
27
29
|
/** Callback for streaming output chunks (already sanitized) */
|
|
28
30
|
onChunk?: (chunk: string) => Promise<void> | void;
|
|
29
31
|
/** AbortSignal for cancellation */
|
|
@@ -86,6 +88,151 @@ const kernelSessions = new Map<string, KernelSession>();
|
|
|
86
88
|
let cachedPreludeDocs: PreludeHelper[] | null = null;
|
|
87
89
|
let cleanupTimer: NodeJS.Timeout | null = null;
|
|
88
90
|
|
|
91
|
+
interface KernelSessionExecutionOptions {
|
|
92
|
+
useSharedGateway?: boolean;
|
|
93
|
+
sessionFile?: string;
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
deadlineMs?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class PythonExecutionCancelledError extends Error {
|
|
99
|
+
readonly timedOut: boolean;
|
|
100
|
+
|
|
101
|
+
constructor(timedOut: boolean) {
|
|
102
|
+
super(timedOut ? "Command timed out" : "Command aborted");
|
|
103
|
+
this.name = timedOut ? "TimeoutError" : "AbortError";
|
|
104
|
+
this.timedOut = timedOut;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getExecutionDeadlineMs(options?: Pick<PythonExecutorOptions, "deadlineMs" | "timeoutMs">): number | undefined {
|
|
109
|
+
if (options?.deadlineMs !== undefined) return options.deadlineMs;
|
|
110
|
+
if (options?.timeoutMs === undefined) return undefined;
|
|
111
|
+
return Date.now() + options.timeoutMs;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
115
|
+
if (deadlineMs === undefined) return undefined;
|
|
116
|
+
return deadlineMs - Date.now();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function requireRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
120
|
+
const remainingMs = getRemainingTimeoutMs(deadlineMs);
|
|
121
|
+
if (remainingMs === undefined) return undefined;
|
|
122
|
+
if (remainingMs <= 0) {
|
|
123
|
+
throw new PythonExecutionCancelledError(true);
|
|
124
|
+
}
|
|
125
|
+
return remainingMs;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isCancellationError(error: unknown): boolean {
|
|
129
|
+
return (
|
|
130
|
+
error instanceof PythonExecutionCancelledError ||
|
|
131
|
+
(error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) ||
|
|
132
|
+
(error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isTimedOutCancellation(error: unknown, signal?: AbortSignal): boolean {
|
|
137
|
+
if (error instanceof PythonExecutionCancelledError) return error.timedOut;
|
|
138
|
+
if (error instanceof DOMException) return error.name === "TimeoutError";
|
|
139
|
+
if (error instanceof Error && error.name === "TimeoutError") return true;
|
|
140
|
+
const reason = signal?.reason;
|
|
141
|
+
if (reason instanceof DOMException) return reason.name === "TimeoutError";
|
|
142
|
+
return reason instanceof Error ? reason.name === "TimeoutError" : false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function waitForQueueTurn(
|
|
146
|
+
queue: Promise<void>,
|
|
147
|
+
options: Pick<KernelSessionExecutionOptions, "signal" | "deadlineMs">,
|
|
148
|
+
): Promise<void> {
|
|
149
|
+
if (options.signal?.aborted) {
|
|
150
|
+
throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const remainingMs = getRemainingTimeoutMs(options.deadlineMs);
|
|
154
|
+
if (remainingMs !== undefined && remainingMs <= 0) {
|
|
155
|
+
throw new PythonExecutionCancelledError(true);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!options.signal && remainingMs === undefined) {
|
|
159
|
+
await queue;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await new Promise<void>((resolve, reject) => {
|
|
164
|
+
const cleanups: Array<() => void> = [];
|
|
165
|
+
const finish = (callback: () => void) => {
|
|
166
|
+
while (cleanups.length > 0) {
|
|
167
|
+
cleanups.pop()?.();
|
|
168
|
+
}
|
|
169
|
+
callback();
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const onAbort = () => {
|
|
173
|
+
finish(() =>
|
|
174
|
+
reject(new PythonExecutionCancelledError(isTimedOutCancellation(options.signal?.reason, options.signal))),
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if (options.signal) {
|
|
179
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
180
|
+
cleanups.push(() => options.signal?.removeEventListener("abort", onAbort));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (remainingMs !== undefined) {
|
|
184
|
+
const timeout = setTimeout(() => {
|
|
185
|
+
finish(() => reject(new PythonExecutionCancelledError(true)));
|
|
186
|
+
}, remainingMs);
|
|
187
|
+
timeout.unref();
|
|
188
|
+
cleanups.push(() => clearTimeout(timeout));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
queue.then(
|
|
192
|
+
() => finish(resolve),
|
|
193
|
+
error => finish(() => reject(error)),
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function formatTimeoutAnnotation(timeoutMs?: number): string | undefined {
|
|
199
|
+
if (timeoutMs === undefined) return "Command timed out";
|
|
200
|
+
const secs = Math.max(1, Math.round(timeoutMs / 1000));
|
|
201
|
+
return `Command timed out after ${secs} seconds`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function createCancelledPythonResult(timedOut: boolean, timeoutMs?: number): PythonResult {
|
|
205
|
+
const output = timedOut ? (formatTimeoutAnnotation(timeoutMs) ?? "Command timed out") : "";
|
|
206
|
+
const outputBytes = Buffer.byteLength(output, "utf-8");
|
|
207
|
+
const outputLines = output.length > 0 ? 1 : 0;
|
|
208
|
+
return {
|
|
209
|
+
output,
|
|
210
|
+
exitCode: undefined,
|
|
211
|
+
cancelled: true,
|
|
212
|
+
truncated: false,
|
|
213
|
+
totalLines: outputLines,
|
|
214
|
+
totalBytes: outputBytes,
|
|
215
|
+
outputLines,
|
|
216
|
+
outputBytes,
|
|
217
|
+
displayOutputs: [],
|
|
218
|
+
stdinRequested: false,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function buildKernelStartOptions(
|
|
223
|
+
cwd: string,
|
|
224
|
+
env: Record<string, string> | undefined,
|
|
225
|
+
options: KernelSessionExecutionOptions,
|
|
226
|
+
) {
|
|
227
|
+
return {
|
|
228
|
+
cwd,
|
|
229
|
+
env,
|
|
230
|
+
useSharedGateway: options.useSharedGateway,
|
|
231
|
+
signal: options.signal,
|
|
232
|
+
deadlineMs: options.deadlineMs,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
89
236
|
interface PreludeCacheSource {
|
|
90
237
|
path: string;
|
|
91
238
|
hash: string;
|
|
@@ -247,13 +394,10 @@ export async function warmPythonEnvironment(
|
|
|
247
394
|
const resolvedSessionId = sessionId ?? `session:${cwd}`;
|
|
248
395
|
try {
|
|
249
396
|
const docs = await logger.timeAsync("warmPython:withKernelSession", () =>
|
|
250
|
-
withKernelSession(
|
|
251
|
-
resolvedSessionId,
|
|
252
|
-
cwd,
|
|
253
|
-
async kernel => kernel.introspectPrelude(),
|
|
397
|
+
withKernelSession(resolvedSessionId, cwd, async kernel => kernel.introspectPrelude(), {
|
|
254
398
|
useSharedGateway,
|
|
255
399
|
sessionFile,
|
|
256
|
-
),
|
|
400
|
+
}),
|
|
257
401
|
);
|
|
258
402
|
cachedPreludeDocs = docs;
|
|
259
403
|
if (!isTestEnv && docs.length > 0) {
|
|
@@ -306,21 +450,22 @@ async function recoverFromResourceExhaustion(): Promise<void> {
|
|
|
306
450
|
async function createKernelSession(
|
|
307
451
|
sessionId: string,
|
|
308
452
|
cwd: string,
|
|
309
|
-
|
|
310
|
-
sessionFile?: string,
|
|
453
|
+
options: KernelSessionExecutionOptions = {},
|
|
311
454
|
isRetry?: boolean,
|
|
312
455
|
): Promise<KernelSession> {
|
|
313
|
-
|
|
456
|
+
requireRemainingTimeoutMs(options.deadlineMs);
|
|
457
|
+
const env: Record<string, string> | undefined = options.sessionFile
|
|
458
|
+
? { PI_SESSION_FILE: options.sessionFile }
|
|
459
|
+
: undefined;
|
|
460
|
+
const startOptions = buildKernelStartOptions(cwd, env, options);
|
|
314
461
|
|
|
315
462
|
let kernel: PythonKernel;
|
|
316
463
|
try {
|
|
317
|
-
kernel = await logger.timeAsync("createKernelSession:PythonKernel.start", () =>
|
|
318
|
-
PythonKernel.start({ cwd, useSharedGateway, env }),
|
|
319
|
-
);
|
|
464
|
+
kernel = await logger.timeAsync("createKernelSession:PythonKernel.start", () => PythonKernel.start(startOptions));
|
|
320
465
|
} catch (err) {
|
|
321
466
|
if (!isRetry && isResourceExhaustionError(err)) {
|
|
322
467
|
await recoverFromResourceExhaustion();
|
|
323
|
-
return createKernelSession(sessionId, cwd,
|
|
468
|
+
return createKernelSession(sessionId, cwd, options, true);
|
|
324
469
|
}
|
|
325
470
|
throw err;
|
|
326
471
|
}
|
|
@@ -347,20 +492,23 @@ async function createKernelSession(
|
|
|
347
492
|
async function restartKernelSession(
|
|
348
493
|
session: KernelSession,
|
|
349
494
|
cwd: string,
|
|
350
|
-
|
|
351
|
-
sessionFile?: string,
|
|
495
|
+
options: KernelSessionExecutionOptions = {},
|
|
352
496
|
): Promise<void> {
|
|
353
497
|
session.restartCount += 1;
|
|
354
498
|
if (session.restartCount > 1) {
|
|
355
499
|
throw new Error("Python kernel restarted too many times in this session");
|
|
356
500
|
}
|
|
501
|
+
requireRemainingTimeoutMs(options.deadlineMs);
|
|
357
502
|
try {
|
|
358
503
|
await session.kernel.shutdown();
|
|
359
504
|
} catch (err) {
|
|
360
505
|
logger.warn("Failed to shutdown crashed kernel", { error: err instanceof Error ? err.message : String(err) });
|
|
361
506
|
}
|
|
362
|
-
const env: Record<string, string> | undefined = sessionFile
|
|
363
|
-
|
|
507
|
+
const env: Record<string, string> | undefined = options.sessionFile
|
|
508
|
+
? { PI_SESSION_FILE: options.sessionFile }
|
|
509
|
+
: undefined;
|
|
510
|
+
const startOptions = buildKernelStartOptions(cwd, env, options);
|
|
511
|
+
const kernel = await PythonKernel.start(startOptions);
|
|
364
512
|
session.kernel = kernel;
|
|
365
513
|
session.dead = false;
|
|
366
514
|
session.lastUsedAt = Date.now();
|
|
@@ -382,23 +530,18 @@ async function withKernelSession<T>(
|
|
|
382
530
|
sessionId: string,
|
|
383
531
|
cwd: string,
|
|
384
532
|
handler: (kernel: PythonKernel) => Promise<T>,
|
|
385
|
-
|
|
386
|
-
sessionFile?: string,
|
|
533
|
+
options: KernelSessionExecutionOptions = {},
|
|
387
534
|
): Promise<T> {
|
|
388
535
|
let session = kernelSessions.get(sessionId);
|
|
389
536
|
if (!session) {
|
|
390
|
-
// Evict oldest session if at capacity
|
|
391
537
|
if (kernelSessions.size >= MAX_KERNEL_SESSIONS) {
|
|
392
538
|
await evictOldestSession();
|
|
393
539
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
useSharedGateway,
|
|
400
|
-
sessionFile,
|
|
401
|
-
);
|
|
540
|
+
requireRemainingTimeoutMs(options.deadlineMs);
|
|
541
|
+
if (options.signal?.aborted) {
|
|
542
|
+
throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
|
543
|
+
}
|
|
544
|
+
session = await logger.timeAsync("kernel:createKernelSession", createKernelSession, sessionId, cwd, options);
|
|
402
545
|
kernelSessions.set(sessionId, session);
|
|
403
546
|
startCleanupTimer();
|
|
404
547
|
}
|
|
@@ -406,14 +549,7 @@ async function withKernelSession<T>(
|
|
|
406
549
|
const run = async (): Promise<T> => {
|
|
407
550
|
session!.lastUsedAt = Date.now();
|
|
408
551
|
if (session!.dead || !session!.kernel.isAlive()) {
|
|
409
|
-
await logger.timeAsync(
|
|
410
|
-
"kernel:restartKernelSession",
|
|
411
|
-
restartKernelSession,
|
|
412
|
-
session!,
|
|
413
|
-
cwd,
|
|
414
|
-
useSharedGateway,
|
|
415
|
-
sessionFile,
|
|
416
|
-
);
|
|
552
|
+
await logger.timeAsync("kernel:restartKernelSession", restartKernelSession, session!, cwd, options);
|
|
417
553
|
}
|
|
418
554
|
try {
|
|
419
555
|
const result = await logger.timeAsync("kernel:withSession:handler", handler, session!.kernel);
|
|
@@ -423,26 +559,34 @@ async function withKernelSession<T>(
|
|
|
423
559
|
if (!session!.dead && session!.kernel.isAlive()) {
|
|
424
560
|
throw err;
|
|
425
561
|
}
|
|
426
|
-
await logger.timeAsync(
|
|
427
|
-
"kernel:restartKernelSession",
|
|
428
|
-
restartKernelSession,
|
|
429
|
-
session!,
|
|
430
|
-
cwd,
|
|
431
|
-
useSharedGateway,
|
|
432
|
-
sessionFile,
|
|
433
|
-
);
|
|
562
|
+
await logger.timeAsync("kernel:restartKernelSession", restartKernelSession, session!, cwd, options);
|
|
434
563
|
const result = await logger.timeAsync("kernel:postRestart:handler", handler, session!.kernel);
|
|
435
564
|
session!.restartCount = 0;
|
|
436
565
|
return result;
|
|
437
566
|
}
|
|
438
567
|
};
|
|
439
568
|
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
);
|
|
445
|
-
|
|
569
|
+
const queue = session.queue;
|
|
570
|
+
let releaseTurn: (() => void) | undefined;
|
|
571
|
+
const turn = new Promise<void>(resolve => {
|
|
572
|
+
releaseTurn = resolve;
|
|
573
|
+
});
|
|
574
|
+
session.queue = queue
|
|
575
|
+
.then(
|
|
576
|
+
() => turn,
|
|
577
|
+
() => turn,
|
|
578
|
+
)
|
|
579
|
+
.then(
|
|
580
|
+
() => undefined,
|
|
581
|
+
() => undefined,
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
await waitForQueueTurn(queue, options);
|
|
586
|
+
return await run();
|
|
587
|
+
} finally {
|
|
588
|
+
releaseTurn?.();
|
|
589
|
+
}
|
|
446
590
|
}
|
|
447
591
|
|
|
448
592
|
async function executeWithKernel(
|
|
@@ -456,19 +600,20 @@ async function executeWithKernel(
|
|
|
456
600
|
artifactId: options?.artifactId,
|
|
457
601
|
});
|
|
458
602
|
const displayOutputs: KernelDisplayOutput[] = [];
|
|
603
|
+
const deadlineMs = getExecutionDeadlineMs(options);
|
|
604
|
+
let executionTimeoutMs: number | undefined;
|
|
459
605
|
|
|
460
606
|
try {
|
|
607
|
+
executionTimeoutMs = requireRemainingTimeoutMs(deadlineMs);
|
|
461
608
|
const result = await kernel.execute(code, {
|
|
462
609
|
signal: options?.signal,
|
|
463
|
-
timeoutMs:
|
|
610
|
+
timeoutMs: executionTimeoutMs,
|
|
464
611
|
onChunk: text => sink.push(text),
|
|
465
612
|
onDisplay: output => void displayOutputs.push(output),
|
|
466
613
|
});
|
|
467
614
|
|
|
468
615
|
if (result.cancelled) {
|
|
469
|
-
const
|
|
470
|
-
const annotation =
|
|
471
|
-
result.timedOut && secs !== undefined ? `Command timed out after ${secs} seconds` : undefined;
|
|
616
|
+
const annotation = result.timedOut ? formatTimeoutAnnotation(executionTimeoutMs) : undefined;
|
|
472
617
|
return {
|
|
473
618
|
exitCode: undefined,
|
|
474
619
|
cancelled: true,
|
|
@@ -497,6 +642,16 @@ async function executeWithKernel(
|
|
|
497
642
|
...(await sink.dump()),
|
|
498
643
|
};
|
|
499
644
|
} catch (err) {
|
|
645
|
+
if (isCancellationError(err) || options?.signal?.aborted) {
|
|
646
|
+
const timedOut = isTimedOutCancellation(err, options?.signal);
|
|
647
|
+
return {
|
|
648
|
+
exitCode: undefined,
|
|
649
|
+
cancelled: true,
|
|
650
|
+
displayOutputs,
|
|
651
|
+
stdinRequested: false,
|
|
652
|
+
...(await sink.dump(timedOut ? formatTimeoutAnnotation(executionTimeoutMs) : undefined)),
|
|
653
|
+
};
|
|
654
|
+
}
|
|
500
655
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
501
656
|
logger.error("Python execution failed", { error: error.message });
|
|
502
657
|
throw error;
|
|
@@ -513,34 +668,54 @@ export async function executePythonWithKernel(
|
|
|
513
668
|
|
|
514
669
|
export async function executePython(code: string, options?: PythonExecutorOptions): Promise<PythonResult> {
|
|
515
670
|
const cwd = options?.cwd ?? getProjectDir();
|
|
516
|
-
|
|
671
|
+
const deadlineMs = getExecutionDeadlineMs(options);
|
|
672
|
+
const executionOptions: PythonExecutorOptions = {
|
|
673
|
+
...(options ?? {}),
|
|
674
|
+
deadlineMs,
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
try {
|
|
678
|
+
requireRemainingTimeoutMs(deadlineMs);
|
|
679
|
+
if (executionOptions.signal?.aborted) {
|
|
680
|
+
throw new PythonExecutionCancelledError(
|
|
681
|
+
isTimedOutCancellation(executionOptions.signal.reason, executionOptions.signal),
|
|
682
|
+
);
|
|
683
|
+
}
|
|
517
684
|
|
|
518
|
-
|
|
519
|
-
const useSharedGateway = options?.useSharedGateway;
|
|
520
|
-
const sessionFile = options?.sessionFile;
|
|
685
|
+
await ensureKernelAvailable(cwd);
|
|
521
686
|
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
687
|
+
const kernelMode = executionOptions.kernelMode ?? "session";
|
|
688
|
+
const sessionFile = executionOptions.sessionFile;
|
|
689
|
+
|
|
690
|
+
if (kernelMode === "per-call") {
|
|
691
|
+
const env: Record<string, string> | undefined = sessionFile ? { PI_SESSION_FILE: sessionFile } : undefined;
|
|
692
|
+
requireRemainingTimeoutMs(deadlineMs);
|
|
693
|
+
const startOptions = buildKernelStartOptions(cwd, env, executionOptions);
|
|
694
|
+
const kernel = await PythonKernel.start(startOptions);
|
|
695
|
+
try {
|
|
696
|
+
return await executeWithKernel(kernel, code, executionOptions);
|
|
697
|
+
} finally {
|
|
698
|
+
await kernel.shutdown();
|
|
699
|
+
}
|
|
529
700
|
}
|
|
530
|
-
}
|
|
531
701
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
702
|
+
const sessionId = executionOptions.sessionId ?? `session:${cwd}`;
|
|
703
|
+
if (executionOptions.reset) {
|
|
704
|
+
const existing = kernelSessions.get(sessionId);
|
|
705
|
+
if (existing) {
|
|
706
|
+
await disposeKernelSession(existing);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return await withKernelSession(
|
|
710
|
+
sessionId,
|
|
711
|
+
cwd,
|
|
712
|
+
async kernel => executeWithKernel(kernel, code, executionOptions),
|
|
713
|
+
executionOptions,
|
|
714
|
+
);
|
|
715
|
+
} catch (err) {
|
|
716
|
+
if (isCancellationError(err) || executionOptions.signal?.aborted) {
|
|
717
|
+
return createCancelledPythonResult(isTimedOutCancellation(err, executionOptions.signal));
|
|
537
718
|
}
|
|
719
|
+
throw err;
|
|
538
720
|
}
|
|
539
|
-
return await withKernelSession(
|
|
540
|
-
sessionId,
|
|
541
|
-
cwd,
|
|
542
|
-
async kernel => executeWithKernel(kernel, code, options),
|
|
543
|
-
useSharedGateway,
|
|
544
|
-
sessionFile,
|
|
545
|
-
);
|
|
546
721
|
}
|