@oh-my-pi/pi-coding-agent 17.2.11 → 17.2.12
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 +13 -0
- package/dist/{CHANGELOG-d9xenpn9.md → CHANGELOG-k9ghy5sn.md} +13 -0
- package/dist/cli.js +10795 -10791
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/config/custom-models.d.ts +31 -0
- package/dist/types/config/model-config-values.d.ts +19 -0
- package/dist/types/config/model-patch.d.ts +93 -0
- package/dist/types/config/model-provider-discovery.d.ts +51 -0
- package/dist/types/config/model-registry.d.ts +5 -52
- package/dist/types/config/settings.d.ts +5 -3
- package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
- package/dist/types/discovery/agent-plugin-format.d.ts +6 -13
- package/dist/types/discovery/contained-path.d.ts +2 -3
- package/dist/types/eval/executor-base.d.ts +8 -2
- package/dist/types/eval/kernel-session-registry.d.ts +60 -0
- package/dist/types/export/share.d.ts +1 -1
- package/dist/types/lsp/diagnostics.d.ts +96 -0
- package/dist/types/lsp/index.d.ts +7 -128
- package/dist/types/lsp/servers.d.ts +72 -0
- package/dist/types/lsp/tool.d.ts +42 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
- package/dist/types/lsp/writethrough.d.ts +33 -0
- package/dist/types/mcp/transports/header-policy.d.ts +2 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
- package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
- package/dist/types/modes/theme/color.d.ts +19 -0
- package/dist/types/modes/theme/loader.d.ts +19 -0
- package/dist/types/modes/theme/schema.d.ts +175 -0
- package/dist/types/modes/theme/symbols.d.ts +28 -0
- package/dist/types/modes/theme/theme-class.d.ts +244 -0
- package/dist/types/modes/theme/theme.d.ts +9 -280
- package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +3 -1
- package/dist/types/secrets/message-transform.d.ts +40 -0
- package/dist/types/secrets/obfuscator.d.ts +0 -108
- package/dist/types/secrets/placeholder-scan.d.ts +95 -0
- package/dist/types/secrets/placeholder.d.ts +94 -0
- package/dist/types/secrets/replacement.d.ts +82 -0
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/session-handoff.d.ts +3 -3
- package/dist/types/session/session-manager.d.ts +32 -0
- package/dist/types/session/session-provider-boundary.d.ts +1 -1
- package/dist/types/session/turn-recovery.d.ts +2 -2
- package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
- package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
- package/dist/types/slash-commands/builtin-control.d.ts +2 -0
- package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
- package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
- package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/slash-commands/builtin-session.d.ts +2 -0
- package/dist/types/tools/gh-common.d.ts +69 -0
- package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
- package/dist/types/tools/gh-pr-diff.d.ts +102 -0
- package/dist/types/tools/gh-run-watch.d.ts +78 -0
- package/dist/types/tools/gh-search.d.ts +61 -0
- package/dist/types/tools/gh-types.d.ts +340 -0
- package/dist/types/tools/gh-view.d.ts +88 -0
- package/dist/types/tools/gh.d.ts +4 -201
- package/dist/types/tools/read-archive.d.ts +16 -0
- package/dist/types/tools/read-format.d.ts +104 -0
- package/dist/types/tools/read-path-resolution.d.ts +19 -0
- package/dist/types/tools/read-pdf-images.d.ts +12 -0
- package/dist/types/tools/read-renderer.d.ts +24 -0
- package/dist/types/tools/read-selector.d.ts +27 -0
- package/dist/types/tools/read-sqlite.d.ts +16 -0
- package/dist/types/tools/read-summary.d.ts +19 -0
- package/dist/types/tools/read.d.ts +1 -23
- package/dist/types/tools/shell-tokenize.d.ts +2 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/src/advisor/runtime.ts +12 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +64 -704
- package/src/config/settings.ts +7 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +7 -7
- package/src/discovery/contained-path.ts +2 -5
- package/src/edit/hashline/diff.ts +5 -1
- package/src/eval/executor-base.ts +39 -6
- package/src/eval/jl/executor.ts +82 -371
- package/src/eval/kernel-session-registry.ts +398 -0
- package/src/eval/py/executor.ts +53 -261
- package/src/eval/rb/executor.ts +36 -279
- package/src/export/share.ts +2 -1
- package/src/lsp/diagnostics.ts +516 -0
- package/src/lsp/index.ts +20 -2819
- package/src/lsp/servers.ts +296 -0
- package/src/lsp/tool.ts +1352 -0
- package/src/lsp/workspace-diagnostics.ts +170 -0
- package/src/lsp/writethrough.ts +561 -0
- package/src/mcp/transports/header-policy.ts +1 -1
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/controllers/command-controller.ts +4 -1
- package/src/modes/theme/color.ts +133 -0
- package/src/modes/theme/loader.ts +178 -0
- package/src/modes/theme/schema.ts +263 -0
- package/src/modes/theme/symbols.ts +1000 -0
- package/src/modes/theme/theme-class.ts +611 -0
- package/src/modes/theme/theme.ts +27 -2453
- package/src/modes/theme/tui-adapters.ts +279 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/secrets/index.ts +6 -12
- package/src/secrets/message-transform.ts +287 -0
- package/src/secrets/obfuscator.ts +39 -1303
- package/src/secrets/placeholder-scan.ts +506 -0
- package/src/secrets/placeholder.ts +309 -0
- package/src/secrets/replacement.ts +216 -0
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +113 -7
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-handoff.ts +39 -20
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/turn-recovery.ts +21 -12
- package/src/slash-commands/builtin-collaboration.ts +504 -0
- package/src/slash-commands/builtin-completions.ts +291 -0
- package/src/slash-commands/builtin-control.ts +78 -0
- package/src/slash-commands/builtin-lifecycle.ts +506 -0
- package/src/slash-commands/builtin-marketplace.ts +567 -0
- package/src/slash-commands/builtin-modes.ts +518 -0
- package/src/slash-commands/builtin-registry.ts +21 -3000
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/tools/gh-common.ts +288 -0
- package/src/tools/gh-pr-checkout.ts +679 -0
- package/src/tools/gh-pr-diff.ts +473 -0
- package/src/tools/gh-run-watch.ts +1015 -0
- package/src/tools/gh-search.ts +500 -0
- package/src/tools/gh-types.ts +379 -0
- package/src/tools/gh-view.ts +612 -0
- package/src/tools/gh.ts +109 -3821
- package/src/tools/read-archive.ts +209 -0
- package/src/tools/read-format.ts +593 -0
- package/src/tools/read-path-resolution.ts +36 -0
- package/src/tools/read-pdf-images.ts +250 -0
- package/src/tools/read-renderer.ts +289 -0
- package/src/tools/read-selector.ts +84 -0
- package/src/tools/read-sqlite.ts +215 -0
- package/src/tools/read-summary.ts +199 -0
- package/src/tools/read.ts +77 -1820
- package/src/tools/shell-tokenize.ts +1 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/zai.ts +12 -3
package/src/eval/jl/executor.ts
CHANGED
|
@@ -2,11 +2,21 @@ import * as path from "node:path";
|
|
|
2
2
|
import { getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import type { ToolSession } from "../../tools";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
buildManagedKernelEnv,
|
|
6
|
+
buildManagedKernelEnvPatch,
|
|
6
7
|
createCancelledKernelResult,
|
|
7
8
|
executeWithKernelBase,
|
|
8
|
-
|
|
9
|
+
getExecutionDeadlineMs,
|
|
10
|
+
isCancellationError,
|
|
11
|
+
isTimedOutCancellation,
|
|
12
|
+
waitForPromiseWithCancellation,
|
|
9
13
|
} from "../executor-base";
|
|
14
|
+
import {
|
|
15
|
+
createKernelSessionRegistry,
|
|
16
|
+
type KernelSession,
|
|
17
|
+
normalizeKernelSessionCwd,
|
|
18
|
+
requireRemainingKernelTimeoutMs,
|
|
19
|
+
} from "../kernel-session-registry";
|
|
10
20
|
import { ensurePyToolBridge, type PyToolBridgeInfo } from "../py/tool-bridge";
|
|
11
21
|
import type { EvalDisplayOutput, EvalStatusEvent } from "../types";
|
|
12
22
|
import {
|
|
@@ -58,22 +68,6 @@ export interface JuliaResult {
|
|
|
58
68
|
stdinRequested: boolean;
|
|
59
69
|
}
|
|
60
70
|
|
|
61
|
-
interface JuliaSessionOwners {
|
|
62
|
-
ownerIds: Set<string>;
|
|
63
|
-
hasFallbackOwner: boolean;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
interface JuliaSession extends JuliaSessionOwners {
|
|
67
|
-
sessionKey: string;
|
|
68
|
-
sessionId: string;
|
|
69
|
-
cwd: string;
|
|
70
|
-
kernel: JuliaKernel;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface StartingJuliaSession extends JuliaSessionOwners {
|
|
74
|
-
promise: Promise<JuliaSession>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
71
|
class JuliaExecutionCancelledError extends Error {
|
|
78
72
|
constructor(readonly timedOut: boolean) {
|
|
79
73
|
super(timedOut ? "Julia execution timed out" : "Julia execution cancelled");
|
|
@@ -81,14 +75,6 @@ class JuliaExecutionCancelledError extends Error {
|
|
|
81
75
|
}
|
|
82
76
|
}
|
|
83
77
|
|
|
84
|
-
const sessions = new Map<string, JuliaSession>();
|
|
85
|
-
const startingSessions = new Map<string, StartingJuliaSession>();
|
|
86
|
-
const resettingSessions = new Map<string, Promise<void>>();
|
|
87
|
-
|
|
88
|
-
function normalizeSessionCwd(cwd: string): string {
|
|
89
|
-
return path.resolve(cwd);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
78
|
function normalizeExplicitInterpreter(cwd: string, interpreter: string | undefined): string {
|
|
93
79
|
if (interpreter === undefined) return "";
|
|
94
80
|
const resolved = resolveExplicitJuliaRuntime(interpreter, cwd, {}).juliaPath;
|
|
@@ -99,85 +85,39 @@ function normalizeExplicitInterpreter(cwd: string, interpreter: string | undefin
|
|
|
99
85
|
}
|
|
100
86
|
}
|
|
101
87
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) return true;
|
|
111
|
-
if (
|
|
112
|
-
error &&
|
|
113
|
-
typeof error === "object" &&
|
|
114
|
-
"name" in error &&
|
|
115
|
-
(error.name === "AbortError" || error.name === "TimeoutError")
|
|
116
|
-
)
|
|
117
|
-
return true;
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function isTimedOutCancellation(error: unknown, signal?: AbortSignal): boolean {
|
|
122
|
-
if (error instanceof JuliaExecutionCancelledError) return error.timedOut;
|
|
123
|
-
if (error instanceof Error && error.name === "TimeoutError") return true;
|
|
124
|
-
if (error && typeof error === "object" && "name" in error && error.name === "TimeoutError") return true;
|
|
125
|
-
if (signal?.reason instanceof Error && signal.reason.name === "TimeoutError") return true;
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function getExecutionDeadlineMs(options?: Pick<JuliaExecutorOptions, "deadlineMs" | "timeoutMs">): number | undefined {
|
|
130
|
-
if (options?.deadlineMs !== undefined) return options.deadlineMs;
|
|
131
|
-
if (options?.timeoutMs !== undefined && options.timeoutMs > 0) return Date.now() + options.timeoutMs;
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function getRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
136
|
-
if (deadlineMs === undefined) return undefined;
|
|
137
|
-
return Math.max(0, deadlineMs - Date.now());
|
|
88
|
+
function isJuliaCancellationError(error: unknown): boolean {
|
|
89
|
+
return (
|
|
90
|
+
isCancellationError(error, JuliaExecutionCancelledError) ||
|
|
91
|
+
(!!error &&
|
|
92
|
+
typeof error === "object" &&
|
|
93
|
+
"name" in error &&
|
|
94
|
+
(error.name === "AbortError" || error.name === "TimeoutError"))
|
|
95
|
+
);
|
|
138
96
|
}
|
|
139
97
|
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
return remaining;
|
|
98
|
+
function isTimedOutJuliaCancellation(error: unknown, signal?: AbortSignal): boolean {
|
|
99
|
+
return (
|
|
100
|
+
isTimedOutCancellation(error, JuliaExecutionCancelledError, signal) ||
|
|
101
|
+
(!!error && typeof error === "object" && "name" in error && error.name === "TimeoutError")
|
|
102
|
+
);
|
|
147
103
|
}
|
|
148
104
|
|
|
149
|
-
async function
|
|
105
|
+
async function waitForJuliaPromise<T>(
|
|
150
106
|
promise: Promise<T>,
|
|
151
107
|
options: Pick<JuliaExecutorOptions, "signal" | "deadlineMs">,
|
|
152
108
|
): Promise<T> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
};
|
|
163
|
-
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
164
|
-
cleanups.push(() => options.signal?.removeEventListener("abort", onAbort));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const deadlineMs = options.deadlineMs;
|
|
168
|
-
if (typeof deadlineMs === "number" && deadlineMs > Date.now()) {
|
|
169
|
-
const timeout = setTimeout(() => {
|
|
170
|
-
reject(new JuliaExecutionCancelledError(true));
|
|
171
|
-
}, deadlineMs - Date.now());
|
|
172
|
-
timeout.unref?.();
|
|
173
|
-
cleanups.push(() => clearTimeout(timeout));
|
|
174
|
-
}
|
|
109
|
+
const deadlineMs =
|
|
110
|
+
typeof options.deadlineMs === "number" && options.deadlineMs > Date.now() ? options.deadlineMs : undefined;
|
|
111
|
+
return await waitForPromiseWithCancellation(
|
|
112
|
+
promise,
|
|
113
|
+
{ ...options, deadlineMs },
|
|
114
|
+
JuliaExecutionCancelledError,
|
|
115
|
+
isTimedOutJuliaCancellation,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
175
118
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
} finally {
|
|
179
|
-
for (const cleanup of cleanups) cleanup();
|
|
180
|
-
}
|
|
119
|
+
function requireRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
120
|
+
return requireRemainingKernelTimeoutMs(deadlineMs, JuliaExecutionCancelledError);
|
|
181
121
|
}
|
|
182
122
|
|
|
183
123
|
function formatTimeoutAnnotation(timeoutMs?: number): string | undefined {
|
|
@@ -198,55 +138,9 @@ function createCancelledJuliaResult(_timedOut: boolean, timeoutMs?: number): Jul
|
|
|
198
138
|
return createCancelledKernelResult(output);
|
|
199
139
|
}
|
|
200
140
|
|
|
201
|
-
function buildKernelEnvPatch(options: {
|
|
202
|
-
sessionFile?: string;
|
|
203
|
-
artifactsDir?: string;
|
|
204
|
-
bridge?: PyToolBridgeInfo;
|
|
205
|
-
bridgeSessionId?: string;
|
|
206
|
-
localRoots?: Record<string, string>;
|
|
207
|
-
}): Record<string, string | undefined> {
|
|
208
|
-
const patch: Record<string, string | undefined> = {};
|
|
209
|
-
if (options.sessionFile) patch.PI_SESSION_FILE = options.sessionFile;
|
|
210
|
-
if (options.artifactsDir) patch.PI_ARTIFACTS_DIR = options.artifactsDir;
|
|
211
|
-
if (options.bridge) {
|
|
212
|
-
patch.PI_TOOL_BRIDGE_URL = options.bridge.url;
|
|
213
|
-
patch.PI_TOOL_BRIDGE_TOKEN = options.bridge.token;
|
|
214
|
-
patch.PI_TOOL_BRIDGE_SESSION = options.bridgeSessionId ?? "";
|
|
215
|
-
}
|
|
216
|
-
if (options.localRoots) {
|
|
217
|
-
patch.PI_EVAL_LOCAL_ROOTS = JSON.stringify(options.localRoots);
|
|
218
|
-
}
|
|
219
|
-
return patch;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function buildKernelEnv(options: {
|
|
223
|
-
sessionFile?: string;
|
|
224
|
-
artifactsDir?: string;
|
|
225
|
-
bridge?: PyToolBridgeInfo;
|
|
226
|
-
bridgeSessionId?: string;
|
|
227
|
-
localRoots?: Record<string, string>;
|
|
228
|
-
}): Record<string, string> | undefined {
|
|
229
|
-
const patch = buildKernelEnvPatch(options);
|
|
230
|
-
const keys = Object.keys(patch);
|
|
231
|
-
if (keys.length === 0) return undefined;
|
|
232
|
-
const realEnv: Record<string, string> = {};
|
|
233
|
-
for (const key in patch) {
|
|
234
|
-
const val = patch[key];
|
|
235
|
-
if (typeof val === "string") realEnv[key] = val;
|
|
236
|
-
}
|
|
237
|
-
return realEnv;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
141
|
async function startKernel(cwd: string, options: JuliaExecutorOptions): Promise<JuliaKernel> {
|
|
241
142
|
requireRemainingTimeoutMs(options.deadlineMs);
|
|
242
|
-
const env: Record<string, string | undefined> = {};
|
|
243
|
-
const patch = buildKernelEnv(options);
|
|
244
|
-
if (patch) {
|
|
245
|
-
for (const key in patch) {
|
|
246
|
-
const value = patch[key];
|
|
247
|
-
if (typeof value === "string") env[key] = value;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
143
|
+
const env: Record<string, string | undefined> = buildManagedKernelEnv(options, { sparse: true }) ?? {};
|
|
250
144
|
return await JuliaKernel.start({
|
|
251
145
|
cwd,
|
|
252
146
|
interpreter: options.interpreter,
|
|
@@ -256,163 +150,6 @@ async function startKernel(cwd: string, options: JuliaExecutorOptions): Promise<
|
|
|
256
150
|
});
|
|
257
151
|
}
|
|
258
152
|
|
|
259
|
-
async function acquireSession(
|
|
260
|
-
sessionKey: string,
|
|
261
|
-
sessionId: string,
|
|
262
|
-
cwd: string,
|
|
263
|
-
options: JuliaExecutorOptions,
|
|
264
|
-
): Promise<JuliaSession> {
|
|
265
|
-
const existing = sessions.get(sessionKey);
|
|
266
|
-
if (existing) {
|
|
267
|
-
attachSessionOwner(existing, sessionId, options.kernelOwnerId);
|
|
268
|
-
return existing;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const inFlight = startingSessions.get(sessionKey);
|
|
272
|
-
if (inFlight) {
|
|
273
|
-
attachSessionOwner(inFlight, sessionId, options.kernelOwnerId);
|
|
274
|
-
return await waitForPromiseWithCancellation(inFlight.promise, options);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
let startingSession!: StartingJuliaSession;
|
|
278
|
-
const startPromise = (async () => {
|
|
279
|
-
const kernel = await startKernel(cwd, options);
|
|
280
|
-
const session: JuliaSession = {
|
|
281
|
-
sessionKey,
|
|
282
|
-
sessionId,
|
|
283
|
-
cwd,
|
|
284
|
-
kernel,
|
|
285
|
-
ownerIds: new Set(startingSession.ownerIds),
|
|
286
|
-
hasFallbackOwner: startingSession.hasFallbackOwner,
|
|
287
|
-
};
|
|
288
|
-
if (startingSessions.get(sessionKey) === startingSession) {
|
|
289
|
-
sessions.set(sessionKey, session);
|
|
290
|
-
}
|
|
291
|
-
return session;
|
|
292
|
-
})();
|
|
293
|
-
|
|
294
|
-
startingSession = {
|
|
295
|
-
ownerIds: new Set(),
|
|
296
|
-
hasFallbackOwner: false,
|
|
297
|
-
promise: startPromise,
|
|
298
|
-
};
|
|
299
|
-
attachSessionOwner(startingSession, sessionId, options.kernelOwnerId);
|
|
300
|
-
startingSessions.set(sessionKey, startingSession);
|
|
301
|
-
try {
|
|
302
|
-
return await waitForPromiseWithCancellation(startPromise, options);
|
|
303
|
-
} finally {
|
|
304
|
-
if (startingSessions.get(sessionKey) === startingSession) startingSessions.delete(sessionKey);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async function replaceSessionKernel(session: JuliaSession, cwd: string, options: JuliaExecutorOptions): Promise<void> {
|
|
309
|
-
logger.warn("Julia subprocess died or is unresponsive; spawning fresh process", {
|
|
310
|
-
sessionKey: session.sessionKey,
|
|
311
|
-
});
|
|
312
|
-
const oldKernel = session.kernel;
|
|
313
|
-
const remaining = getRemainingTimeoutMs(options.deadlineMs);
|
|
314
|
-
await oldKernel
|
|
315
|
-
.shutdown(remaining !== undefined ? { timeoutMs: Math.max(0, remaining) } : undefined)
|
|
316
|
-
.catch(() => undefined);
|
|
317
|
-
if (sessions.get(session.sessionKey) !== session) {
|
|
318
|
-
throw new JuliaExecutionCancelledError(false);
|
|
319
|
-
}
|
|
320
|
-
requireRemainingTimeoutMs(options.deadlineMs);
|
|
321
|
-
const nextKernel = await startKernel(cwd, options);
|
|
322
|
-
if (sessions.get(session.sessionKey) !== session) {
|
|
323
|
-
await nextKernel.shutdown().catch(() => undefined);
|
|
324
|
-
throw new JuliaExecutionCancelledError(false);
|
|
325
|
-
}
|
|
326
|
-
session.kernel = nextKernel;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async function resetSession(sessionKey: string): Promise<void> {
|
|
330
|
-
const session = sessions.get(sessionKey) ?? (await startingSessions.get(sessionKey)?.promise.catch(() => undefined));
|
|
331
|
-
if (!session) return;
|
|
332
|
-
sessions.delete(sessionKey);
|
|
333
|
-
await session.kernel.shutdown({ timeoutMs: SHUTDOWN_GRACE_MS }).catch(() => undefined);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export async function disposeAllJuliaKernelSessions(): Promise<void> {
|
|
337
|
-
const pending = [...startingSessions.values()].map(starting => starting.promise);
|
|
338
|
-
startingSessions.clear();
|
|
339
|
-
resettingSessions.clear();
|
|
340
|
-
const started = await Promise.allSettled(pending);
|
|
341
|
-
const all = [...sessions.entries()];
|
|
342
|
-
for (const result of started) {
|
|
343
|
-
if (result.status !== "fulfilled") continue;
|
|
344
|
-
if (!all.some(([, session]) => session === result.value)) {
|
|
345
|
-
all.push([result.value.sessionKey, result.value]);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
for (const [id, session] of all) {
|
|
349
|
-
if (sessions.get(id) === session) sessions.delete(id);
|
|
350
|
-
}
|
|
351
|
-
const results = await Promise.allSettled(all.map(([, session]) => session.kernel.shutdown()));
|
|
352
|
-
for (let i = 0; i < all.length; i += 1) {
|
|
353
|
-
const [id, session] = all[i];
|
|
354
|
-
const result = results[i];
|
|
355
|
-
if (result.status === "fulfilled" && result.value?.confirmed !== false) continue;
|
|
356
|
-
const reason = result.status === "rejected" ? result.reason : "not confirmed";
|
|
357
|
-
logger.warn("Julia kernel shutdown not confirmed", {
|
|
358
|
-
sessionId: session.sessionId,
|
|
359
|
-
sessionKey: id,
|
|
360
|
-
cwd: session.cwd,
|
|
361
|
-
reason,
|
|
362
|
-
});
|
|
363
|
-
if (!sessions.has(id)) sessions.set(id, session);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export async function disposeJuliaKernelSessionsByOwner(ownerId: string): Promise<void> {
|
|
368
|
-
const toShutdown: JuliaSession[] = [];
|
|
369
|
-
const startingToShutdown: StartingJuliaSession[] = [];
|
|
370
|
-
for (const session of [...sessions.values()]) {
|
|
371
|
-
if (!session.ownerIds.has(ownerId)) continue;
|
|
372
|
-
if (session.ownerIds.size === 1) {
|
|
373
|
-
toShutdown.push(session);
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
|
-
session.ownerIds.delete(ownerId);
|
|
377
|
-
}
|
|
378
|
-
for (const [sessionKey, starting] of [...startingSessions.entries()]) {
|
|
379
|
-
if (sessions.has(sessionKey) || !starting.ownerIds.has(ownerId)) continue;
|
|
380
|
-
if (starting.ownerIds.size === 1) {
|
|
381
|
-
startingSessions.delete(sessionKey);
|
|
382
|
-
startingToShutdown.push(starting);
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
starting.ownerIds.delete(ownerId);
|
|
386
|
-
}
|
|
387
|
-
for (const session of toShutdown) {
|
|
388
|
-
if (sessions.get(session.sessionKey) === session) sessions.delete(session.sessionKey);
|
|
389
|
-
}
|
|
390
|
-
const started = await Promise.allSettled(startingToShutdown.map(starting => starting.promise));
|
|
391
|
-
for (const result of started) {
|
|
392
|
-
if (result.status !== "fulfilled") continue;
|
|
393
|
-
const session = result.value;
|
|
394
|
-
if (sessions.get(session.sessionKey) === session) sessions.delete(session.sessionKey);
|
|
395
|
-
toShutdown.push(session);
|
|
396
|
-
}
|
|
397
|
-
const results = await Promise.allSettled(toShutdown.map(session => session.kernel.shutdown()));
|
|
398
|
-
for (let i = 0; i < toShutdown.length; i += 1) {
|
|
399
|
-
const session = toShutdown[i];
|
|
400
|
-
const result = results[i];
|
|
401
|
-
if (result.status === "fulfilled" && result.value?.confirmed !== false) {
|
|
402
|
-
session.ownerIds.delete(ownerId);
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
const reason = result.status === "rejected" ? result.reason : "not confirmed";
|
|
406
|
-
logger.warn("Julia kernel shutdown not confirmed", {
|
|
407
|
-
sessionId: session.sessionId,
|
|
408
|
-
sessionKey: session.sessionKey,
|
|
409
|
-
cwd: session.cwd,
|
|
410
|
-
reason,
|
|
411
|
-
});
|
|
412
|
-
if (!sessions.has(session.sessionKey)) sessions.set(session.sessionKey, session);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
153
|
async function executeWithKernel(
|
|
417
154
|
kernel: JuliaKernel,
|
|
418
155
|
code: string,
|
|
@@ -426,7 +163,7 @@ async function executeWithKernel(
|
|
|
426
163
|
errorLogLabel: "Julia",
|
|
427
164
|
isJulia: true,
|
|
428
165
|
cancelledErrorClass: JuliaExecutionCancelledError,
|
|
429
|
-
buildKernelEnvPatch,
|
|
166
|
+
buildKernelEnvPatch: opts => buildManagedKernelEnvPatch(opts, { sparse: true }),
|
|
430
167
|
formatKernelTimeoutAnnotation,
|
|
431
168
|
formatTimeoutAnnotation,
|
|
432
169
|
resolveDeadlineMs: opts => opts?.deadlineMs,
|
|
@@ -434,10 +171,7 @@ async function executeWithKernel(
|
|
|
434
171
|
}
|
|
435
172
|
|
|
436
173
|
async function ensureKernelAvailable(cwd: string, options: JuliaExecutorOptions): Promise<void> {
|
|
437
|
-
const availability = await
|
|
438
|
-
checkJuliaKernelAvailability(cwd, options.interpreter),
|
|
439
|
-
options,
|
|
440
|
-
);
|
|
174
|
+
const availability = await waitForJuliaPromise(checkJuliaKernelAvailability(cwd, options.interpreter), options);
|
|
441
175
|
if (!availability.ok) {
|
|
442
176
|
throw new Error(availability.reason ?? "Julia kernel unavailable");
|
|
443
177
|
}
|
|
@@ -454,65 +188,37 @@ async function ensureToolBridge(options: JuliaExecutorOptions): Promise<void> {
|
|
|
454
188
|
}
|
|
455
189
|
}
|
|
456
190
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const session = await acquireSession(sessionKey, sessionId, cwd, options);
|
|
489
|
-
if (options.signal?.aborted) {
|
|
490
|
-
throw new JuliaExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
|
491
|
-
}
|
|
492
|
-
if (sessions.get(session.sessionKey) !== session) {
|
|
493
|
-
throw new JuliaExecutionCancelledError(false);
|
|
494
|
-
}
|
|
495
|
-
if (!session.kernel.isAlive()) {
|
|
496
|
-
await replaceSessionKernel(session, cwd, options);
|
|
497
|
-
if (sessions.get(session.sessionKey) !== session) {
|
|
498
|
-
throw new JuliaExecutionCancelledError(false);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
const runOptions = { ...options, cwd };
|
|
502
|
-
try {
|
|
503
|
-
return await executeWithKernel(session.kernel, code, runOptions);
|
|
504
|
-
} catch (err) {
|
|
505
|
-
if (isCancellationError(err) || options.signal?.aborted) throw err;
|
|
506
|
-
if (session.kernel.isAlive()) throw err;
|
|
507
|
-
if (sessions.get(session.sessionKey) !== session) {
|
|
508
|
-
throw new JuliaExecutionCancelledError(false);
|
|
509
|
-
}
|
|
510
|
-
await replaceSessionKernel(session, cwd, options);
|
|
511
|
-
if (sessions.get(session.sessionKey) !== session) {
|
|
512
|
-
throw new JuliaExecutionCancelledError(false);
|
|
513
|
-
}
|
|
514
|
-
return await executeWithKernel(session.kernel, code, runOptions);
|
|
515
|
-
}
|
|
191
|
+
const sessionRegistry = createKernelSessionRegistry<
|
|
192
|
+
JuliaKernel,
|
|
193
|
+
JuliaExecutorOptions,
|
|
194
|
+
JuliaResult,
|
|
195
|
+
KernelSession<JuliaKernel>
|
|
196
|
+
>({
|
|
197
|
+
languageLabel: "Julia",
|
|
198
|
+
cancelledErrorClass: JuliaExecutionCancelledError,
|
|
199
|
+
buildSessionKey: (sessionId, cwd, interpreter) => {
|
|
200
|
+
const normalizedCwd = normalizeKernelSessionCwd(cwd);
|
|
201
|
+
const normalizedInterpreter = normalizeExplicitInterpreter(normalizedCwd, interpreter);
|
|
202
|
+
return `${sessionId}::${normalizedCwd}::${normalizedInterpreter}`;
|
|
203
|
+
},
|
|
204
|
+
createSession: session => session,
|
|
205
|
+
startKernel,
|
|
206
|
+
executeWithKernel,
|
|
207
|
+
waitForStartup: waitForJuliaPromise,
|
|
208
|
+
shutdownSession: (session, resetting) =>
|
|
209
|
+
resetting ? session.kernel.shutdown({ timeoutMs: SHUTDOWN_GRACE_MS }) : session.kernel.shutdown(),
|
|
210
|
+
clearResetsOnDisposeAll: true,
|
|
211
|
+
logBeforeReplacement: true,
|
|
212
|
+
isCancellation: isJuliaCancellationError,
|
|
213
|
+
isTimedOutCancellation: isTimedOutJuliaCancellation,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
export async function disposeAllJuliaKernelSessions(): Promise<void> {
|
|
217
|
+
await sessionRegistry.disposeAll();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function disposeJuliaKernelSessionsByOwner(ownerId: string): Promise<void> {
|
|
221
|
+
await sessionRegistry.disposeByOwner(ownerId);
|
|
516
222
|
}
|
|
517
223
|
|
|
518
224
|
export async function executeJuliaWithKernel(
|
|
@@ -524,8 +230,13 @@ export async function executeJuliaWithKernel(
|
|
|
524
230
|
}
|
|
525
231
|
|
|
526
232
|
export async function executeJulia(code: string, options?: JuliaExecutorOptions): Promise<JuliaResult> {
|
|
527
|
-
const cwd =
|
|
528
|
-
const deadlineMs =
|
|
233
|
+
const cwd = normalizeKernelSessionCwd(options?.cwd ?? getProjectDir());
|
|
234
|
+
const deadlineMs =
|
|
235
|
+
options?.deadlineMs !== undefined
|
|
236
|
+
? options.deadlineMs
|
|
237
|
+
: options?.timeoutMs !== undefined && options.timeoutMs > 0
|
|
238
|
+
? getExecutionDeadlineMs(options)
|
|
239
|
+
: undefined;
|
|
529
240
|
const executionOptions: JuliaExecutorOptions = {
|
|
530
241
|
...(options ?? {}),
|
|
531
242
|
cwd,
|
|
@@ -536,15 +247,15 @@ export async function executeJulia(code: string, options?: JuliaExecutorOptions)
|
|
|
536
247
|
requireRemainingTimeoutMs(deadlineMs);
|
|
537
248
|
if (executionOptions.signal?.aborted) {
|
|
538
249
|
throw new JuliaExecutionCancelledError(
|
|
539
|
-
|
|
250
|
+
isTimedOutJuliaCancellation(executionOptions.signal.reason, executionOptions.signal),
|
|
540
251
|
);
|
|
541
252
|
}
|
|
542
253
|
await ensureKernelAvailable(cwd, executionOptions);
|
|
543
254
|
await ensureToolBridge(executionOptions);
|
|
544
|
-
return await executeOnSession(code, cwd, executionOptions);
|
|
255
|
+
return await sessionRegistry.executeOnSession(code, cwd, executionOptions);
|
|
545
256
|
} catch (err) {
|
|
546
|
-
if (
|
|
547
|
-
return createCancelledJuliaResult(
|
|
257
|
+
if (isJuliaCancellationError(err) || executionOptions.signal?.aborted) {
|
|
258
|
+
return createCancelledJuliaResult(isTimedOutJuliaCancellation(err, executionOptions.signal));
|
|
548
259
|
}
|
|
549
260
|
throw err;
|
|
550
261
|
}
|