@oh-my-pi/pi-coding-agent 17.2.10 → 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 +53 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
- package/dist/cli.js +10775 -10762
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -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 +9 -52
- package/dist/types/config/settings-schema.d.ts +1 -34
- 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 +136 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +33 -0
- package/dist/types/discovery/index.d.ts +1 -0
- 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/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- 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 +46 -0
- package/dist/types/mcp/types.d.ts +15 -0
- 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/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- 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/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +26 -2
- 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-events.d.ts +2 -2
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -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/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +9 -4
- 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/task/index.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 +18 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +12 -7
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +84 -704
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +53 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +75 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- 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/exec/non-interactive-env.ts +1 -0
- package/src/export/share.ts +2 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- 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/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +8 -2
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- 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/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +51 -6
- 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-events.ts +2 -2
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +146 -21
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +39 -16
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +115 -84
- 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 -2996
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- 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/jtd-to-json-schema.ts +123 -21
- 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 +82 -1824
- package/src/tools/shell-tokenize.ts +99 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/zai.ts +12 -3
package/src/eval/py/executor.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
2
|
|
|
4
3
|
import { getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
5
4
|
import type { ToolSession } from "../../tools";
|
|
6
5
|
import {
|
|
7
|
-
attachSessionOwner,
|
|
8
6
|
buildManagedKernelEnv,
|
|
9
7
|
buildManagedKernelEnvPatch,
|
|
10
8
|
createCancelledKernelResult,
|
|
@@ -13,11 +11,18 @@ import {
|
|
|
13
11
|
getRemainingTimeoutMs,
|
|
14
12
|
isCancellationError,
|
|
15
13
|
isTimedOutCancellation,
|
|
16
|
-
resolveOwnerScopedSessionKey,
|
|
17
|
-
type SessionOwners,
|
|
18
14
|
waitForPromiseWithCancellation,
|
|
19
15
|
} from "../executor-base";
|
|
20
16
|
import type { JsStatusEvent } from "../js/shared/types";
|
|
17
|
+
import {
|
|
18
|
+
createKernelSessionRegistry,
|
|
19
|
+
formatSessionKernelTimeoutAnnotation,
|
|
20
|
+
formatSessionTimeoutAnnotation,
|
|
21
|
+
type KernelSession,
|
|
22
|
+
type KernelSessionRegistryContext,
|
|
23
|
+
normalizeKernelSessionCwd,
|
|
24
|
+
requireRemainingKernelTimeoutMs,
|
|
25
|
+
} from "../kernel-session-registry";
|
|
21
26
|
import {
|
|
22
27
|
checkPythonKernelAvailability,
|
|
23
28
|
type KernelDisplayOutput,
|
|
@@ -145,27 +150,9 @@ interface SessionKernelReplacement {
|
|
|
145
150
|
promise: Promise<PythonKernel>;
|
|
146
151
|
}
|
|
147
152
|
|
|
148
|
-
interface PythonSession {
|
|
149
|
-
sessionKey: string;
|
|
150
|
-
sessionId: string;
|
|
151
|
-
cwd: string;
|
|
152
|
-
kernel: PythonKernel;
|
|
153
|
+
interface PythonSession extends KernelSession<PythonKernel> {
|
|
153
154
|
generation: number;
|
|
154
155
|
replacement?: SessionKernelReplacement;
|
|
155
|
-
ownerIds: Set<string>;
|
|
156
|
-
hasFallbackOwner: boolean;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
interface StartingPythonSession extends SessionOwners {
|
|
160
|
-
promise: Promise<PythonSession>;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const sessions = new Map<string, PythonSession>();
|
|
164
|
-
const startingSessions = new Map<string, StartingPythonSession>();
|
|
165
|
-
const resettingSessions = new Map<string, Promise<void>>();
|
|
166
|
-
|
|
167
|
-
function normalizeSessionCwd(cwd: string): string {
|
|
168
|
-
return path.resolve(cwd);
|
|
169
156
|
}
|
|
170
157
|
|
|
171
158
|
function normalizeExplicitInterpreter(cwd: string, interpreter: string | undefined): string {
|
|
@@ -178,11 +165,6 @@ function normalizeExplicitInterpreter(cwd: string, interpreter: string | undefin
|
|
|
178
165
|
}
|
|
179
166
|
}
|
|
180
167
|
|
|
181
|
-
function buildSessionKey(sessionId: string, cwd: string, interpreter: string | undefined): string {
|
|
182
|
-
const normalizedCwd = normalizeSessionCwd(cwd);
|
|
183
|
-
return `${sessionId}\0${normalizedCwd}\0${normalizeExplicitInterpreter(normalizedCwd, interpreter)}`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
168
|
// ---------------------------------------------------------------------------
|
|
187
169
|
// Cancellation plumbing
|
|
188
170
|
// ---------------------------------------------------------------------------
|
|
@@ -198,32 +180,16 @@ class PythonExecutionCancelledError extends Error {
|
|
|
198
180
|
}
|
|
199
181
|
|
|
200
182
|
function requireRemainingTimeoutMs(deadlineMs?: number): number | undefined {
|
|
201
|
-
|
|
202
|
-
if (remainingMs === undefined) return undefined;
|
|
203
|
-
if (remainingMs <= 0) {
|
|
204
|
-
throw new PythonExecutionCancelledError(true);
|
|
205
|
-
}
|
|
206
|
-
return remainingMs;
|
|
183
|
+
return requireRemainingKernelTimeoutMs(deadlineMs, PythonExecutionCancelledError);
|
|
207
184
|
}
|
|
208
185
|
|
|
209
186
|
// ---------------------------------------------------------------------------
|
|
210
187
|
// Result formatting
|
|
211
188
|
// ---------------------------------------------------------------------------
|
|
212
189
|
|
|
213
|
-
|
|
214
|
-
if (timeoutMs === undefined) return "Command timed out";
|
|
215
|
-
const secs = Math.max(1, Math.round(timeoutMs / 1000));
|
|
216
|
-
return `Command timed out after ${secs} seconds`;
|
|
217
|
-
}
|
|
190
|
+
const formatTimeoutAnnotation = formatSessionTimeoutAnnotation;
|
|
218
191
|
|
|
219
|
-
|
|
220
|
-
const secs = timeoutMs === undefined ? undefined : Math.max(1, Math.round(timeoutMs / 1000));
|
|
221
|
-
if (kernelKilled) {
|
|
222
|
-
return "eval cell timed out and the kernel was unresponsive to interrupt; the kernel has been killed and will be recreated on the next call.";
|
|
223
|
-
}
|
|
224
|
-
const duration = secs === undefined ? "the configured timeout" : `${secs}s`;
|
|
225
|
-
return `eval cell timed out after ${duration}; kernel interrupted but remains running. Reset the kernel via { reset: true } if state appears corrupted.`;
|
|
226
|
-
}
|
|
192
|
+
const formatKernelTimeoutAnnotation = formatSessionKernelTimeoutAnnotation;
|
|
227
193
|
|
|
228
194
|
function createCancelledPythonResult(timedOut: boolean, timeoutMs?: number): PythonResult {
|
|
229
195
|
const output = timedOut ? (formatTimeoutAnnotation(timeoutMs) ?? "Command timed out") : "";
|
|
@@ -245,63 +211,14 @@ async function startKernel(cwd: string, options: PythonExecutorOptions): Promise
|
|
|
245
211
|
});
|
|
246
212
|
}
|
|
247
213
|
|
|
248
|
-
async function acquireSession(
|
|
249
|
-
sessionKey: string,
|
|
250
|
-
sessionId: string,
|
|
251
|
-
cwd: string,
|
|
252
|
-
options: PythonExecutorOptions,
|
|
253
|
-
): Promise<PythonSession> {
|
|
254
|
-
const existing = sessions.get(sessionKey);
|
|
255
|
-
if (existing) {
|
|
256
|
-
attachSessionOwner(existing, sessionId, options.kernelOwnerId);
|
|
257
|
-
return existing;
|
|
258
|
-
}
|
|
259
|
-
const starting = startingSessions.get(sessionKey);
|
|
260
|
-
if (starting) {
|
|
261
|
-
attachSessionOwner(starting, sessionId, options.kernelOwnerId);
|
|
262
|
-
return await starting.promise;
|
|
263
|
-
}
|
|
264
|
-
let startingSession!: StartingPythonSession;
|
|
265
|
-
const startup = (async () => {
|
|
266
|
-
const kernel = await startKernel(cwd, options);
|
|
267
|
-
const session: PythonSession = {
|
|
268
|
-
sessionKey,
|
|
269
|
-
sessionId,
|
|
270
|
-
cwd,
|
|
271
|
-
kernel,
|
|
272
|
-
generation: 0,
|
|
273
|
-
ownerIds: new Set(startingSession.ownerIds),
|
|
274
|
-
hasFallbackOwner: startingSession.hasFallbackOwner,
|
|
275
|
-
};
|
|
276
|
-
// Publish only while this startup still owns the key: owner disposal or
|
|
277
|
-
// a concurrent dispose-all may have already reaped the starting record,
|
|
278
|
-
// and publishing here would resurrect a kernel that was just torn down.
|
|
279
|
-
if (startingSessions.get(sessionKey) === startingSession) {
|
|
280
|
-
sessions.set(sessionKey, session);
|
|
281
|
-
}
|
|
282
|
-
return session;
|
|
283
|
-
})();
|
|
284
|
-
startingSession = {
|
|
285
|
-
ownerIds: new Set(),
|
|
286
|
-
hasFallbackOwner: false,
|
|
287
|
-
promise: startup,
|
|
288
|
-
};
|
|
289
|
-
attachSessionOwner(startingSession, sessionId, options.kernelOwnerId);
|
|
290
|
-
startingSessions.set(sessionKey, startingSession);
|
|
291
|
-
try {
|
|
292
|
-
return await startup;
|
|
293
|
-
} finally {
|
|
294
|
-
if (startingSessions.get(sessionKey) === startingSession) startingSessions.delete(sessionKey);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
214
|
async function replaceSessionKernel(
|
|
299
215
|
session: PythonSession,
|
|
300
|
-
kernel: PythonKernel,
|
|
301
|
-
generation: number,
|
|
302
216
|
cwd: string,
|
|
303
217
|
options: PythonExecutorOptions,
|
|
218
|
+
context: KernelSessionRegistryContext<PythonKernel, PythonExecutorOptions, PythonSession>,
|
|
304
219
|
): Promise<PythonKernel> {
|
|
220
|
+
const kernel = session.kernel;
|
|
221
|
+
const generation = session.generation;
|
|
305
222
|
const inFlight = session.replacement;
|
|
306
223
|
if (inFlight?.generation === generation) {
|
|
307
224
|
if (
|
|
@@ -312,7 +229,11 @@ async function replaceSessionKernel(
|
|
|
312
229
|
}
|
|
313
230
|
return await waitForPromiseWithCancellation(inFlight.promise, options, PythonExecutionCancelledError);
|
|
314
231
|
}
|
|
315
|
-
if (
|
|
232
|
+
if (
|
|
233
|
+
context.sessions.get(session.sessionKey) !== session ||
|
|
234
|
+
session.generation !== generation ||
|
|
235
|
+
session.kernel !== kernel
|
|
236
|
+
) {
|
|
316
237
|
throw new PythonExecutionCancelledError(false);
|
|
317
238
|
}
|
|
318
239
|
|
|
@@ -333,7 +254,7 @@ async function replaceSessionKernel(
|
|
|
333
254
|
throw new PythonExecutionCancelledError(true);
|
|
334
255
|
}
|
|
335
256
|
if (
|
|
336
|
-
sessions.get(session.sessionKey) !== session ||
|
|
257
|
+
context.sessions.get(session.sessionKey) !== session ||
|
|
337
258
|
session.generation !== generation ||
|
|
338
259
|
session.kernel !== kernel
|
|
339
260
|
) {
|
|
@@ -345,7 +266,7 @@ async function replaceSessionKernel(
|
|
|
345
266
|
deadlineMs: undefined,
|
|
346
267
|
});
|
|
347
268
|
if (
|
|
348
|
-
sessions.get(session.sessionKey) !== session ||
|
|
269
|
+
context.sessions.get(session.sessionKey) !== session ||
|
|
349
270
|
session.generation !== generation ||
|
|
350
271
|
session.kernel !== kernel
|
|
351
272
|
) {
|
|
@@ -374,111 +295,16 @@ async function acquireLiveSessionKernel(
|
|
|
374
295
|
session: PythonSession,
|
|
375
296
|
cwd: string,
|
|
376
297
|
options: PythonExecutorOptions,
|
|
298
|
+
context: KernelSessionRegistryContext<PythonKernel, PythonExecutorOptions, PythonSession>,
|
|
377
299
|
): Promise<PythonKernel> {
|
|
378
|
-
while (sessions.get(session.sessionKey) === session) {
|
|
300
|
+
while (context.sessions.get(session.sessionKey) === session) {
|
|
379
301
|
const kernel = session.kernel;
|
|
380
|
-
const generation = session.generation;
|
|
381
302
|
if (kernel.isAlive()) return kernel;
|
|
382
|
-
await replaceSessionKernel(session,
|
|
303
|
+
await context.replaceSessionKernel(session, cwd, options);
|
|
383
304
|
}
|
|
384
305
|
throw new PythonExecutionCancelledError(false);
|
|
385
306
|
}
|
|
386
307
|
|
|
387
|
-
async function resetSession(sessionKey: string): Promise<void> {
|
|
388
|
-
const existing =
|
|
389
|
-
sessions.get(sessionKey) ?? (await startingSessions.get(sessionKey)?.promise.catch(() => undefined));
|
|
390
|
-
if (!existing) return;
|
|
391
|
-
existing.generation += 1;
|
|
392
|
-
sessions.delete(sessionKey);
|
|
393
|
-
await shutdownInvalidatedSession(existing).catch(() => undefined);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// ---------------------------------------------------------------------------
|
|
397
|
-
// Public dispose entry points
|
|
398
|
-
// ---------------------------------------------------------------------------
|
|
399
|
-
|
|
400
|
-
export async function disposeAllKernelSessions(): Promise<void> {
|
|
401
|
-
const pending = [...startingSessions.values()].map(starting => starting.promise);
|
|
402
|
-
startingSessions.clear();
|
|
403
|
-
const started = await Promise.allSettled(pending);
|
|
404
|
-
const all = [...sessions.entries()];
|
|
405
|
-
for (const result of started) {
|
|
406
|
-
if (result.status !== "fulfilled") continue;
|
|
407
|
-
if (!all.some(([, session]) => session === result.value)) {
|
|
408
|
-
all.push([result.value.sessionKey, result.value]);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
for (const [id, session] of all) {
|
|
412
|
-
session.generation += 1;
|
|
413
|
-
if (sessions.get(id) === session) sessions.delete(id);
|
|
414
|
-
}
|
|
415
|
-
const results = await Promise.allSettled(all.map(([, session]) => shutdownInvalidatedSession(session)));
|
|
416
|
-
for (let i = 0; i < all.length; i += 1) {
|
|
417
|
-
const [id, session] = all[i];
|
|
418
|
-
const result = results[i];
|
|
419
|
-
if (result.status === "fulfilled" && result.value?.confirmed !== false) continue;
|
|
420
|
-
const reason = result.status === "rejected" ? result.reason : "not confirmed";
|
|
421
|
-
logger.warn("Python kernel shutdown not confirmed", {
|
|
422
|
-
sessionId: session.sessionId,
|
|
423
|
-
sessionKey: id,
|
|
424
|
-
cwd: session.cwd,
|
|
425
|
-
reason,
|
|
426
|
-
});
|
|
427
|
-
if (!sessions.has(id)) sessions.set(id, session);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
export async function disposeKernelSessionsByOwner(ownerId: string): Promise<void> {
|
|
432
|
-
const toShutdown: PythonSession[] = [];
|
|
433
|
-
for (const session of [...sessions.values()]) {
|
|
434
|
-
if (!session.ownerIds.has(ownerId)) continue;
|
|
435
|
-
if (session.ownerIds.size === 1) {
|
|
436
|
-
toShutdown.push(session);
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
session.ownerIds.delete(ownerId);
|
|
440
|
-
}
|
|
441
|
-
const startingToShutdown: StartingPythonSession[] = [];
|
|
442
|
-
for (const [sessionKey, starting] of [...startingSessions.entries()]) {
|
|
443
|
-
if (sessions.has(sessionKey) || !starting.ownerIds.has(ownerId)) continue;
|
|
444
|
-
if (starting.ownerIds.size === 1) {
|
|
445
|
-
startingSessions.delete(sessionKey);
|
|
446
|
-
startingToShutdown.push(starting);
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
449
|
-
starting.ownerIds.delete(ownerId);
|
|
450
|
-
}
|
|
451
|
-
for (const session of toShutdown) {
|
|
452
|
-
session.generation += 1;
|
|
453
|
-
if (sessions.get(session.sessionKey) === session) sessions.delete(session.sessionKey);
|
|
454
|
-
}
|
|
455
|
-
const started = await Promise.allSettled(startingToShutdown.map(starting => starting.promise));
|
|
456
|
-
for (const result of started) {
|
|
457
|
-
if (result.status !== "fulfilled") continue;
|
|
458
|
-
const session = result.value;
|
|
459
|
-
session.generation += 1;
|
|
460
|
-
if (sessions.get(session.sessionKey) === session) sessions.delete(session.sessionKey);
|
|
461
|
-
toShutdown.push(session);
|
|
462
|
-
}
|
|
463
|
-
const results = await Promise.allSettled(toShutdown.map(session => shutdownInvalidatedSession(session)));
|
|
464
|
-
for (let i = 0; i < toShutdown.length; i += 1) {
|
|
465
|
-
const session = toShutdown[i];
|
|
466
|
-
const result = results[i];
|
|
467
|
-
if (result.status === "fulfilled" && result.value?.confirmed !== false) {
|
|
468
|
-
session.ownerIds.delete(ownerId);
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
const reason = result.status === "rejected" ? result.reason : "not confirmed";
|
|
472
|
-
logger.warn("Python kernel shutdown not confirmed", {
|
|
473
|
-
sessionId: session.sessionId,
|
|
474
|
-
sessionKey: session.sessionKey,
|
|
475
|
-
cwd: session.cwd,
|
|
476
|
-
reason,
|
|
477
|
-
});
|
|
478
|
-
if (!sessions.has(session.sessionKey)) sessions.set(session.sessionKey, session);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
308
|
// ---------------------------------------------------------------------------
|
|
483
309
|
// Execution
|
|
484
310
|
// ---------------------------------------------------------------------------
|
|
@@ -535,65 +361,31 @@ async function executePerCall(code: string, cwd: string, options: PythonExecutor
|
|
|
535
361
|
}
|
|
536
362
|
}
|
|
537
363
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
await resetPromise;
|
|
564
|
-
} finally {
|
|
565
|
-
resettingSessions.delete(sessionKey);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
} else {
|
|
569
|
-
// A reset already in progress is an internal coordination state, not a
|
|
570
|
-
// user-visible failure. Wait for it to clear, then proceed with the
|
|
571
|
-
// requested execution on the freshly-restarted kernel.
|
|
572
|
-
const inFlight = resettingSessions.get(sessionKey);
|
|
573
|
-
if (inFlight) await inFlight.catch(() => undefined);
|
|
574
|
-
}
|
|
575
|
-
const session = await acquireSession(sessionKey, sessionId, cwd, options);
|
|
576
|
-
if (options.signal?.aborted) {
|
|
577
|
-
throw new PythonExecutionCancelledError(
|
|
578
|
-
isTimedOutCancellation(options.signal.reason, PythonExecutionCancelledError, options.signal),
|
|
579
|
-
);
|
|
580
|
-
}
|
|
581
|
-
const kernel = await acquireLiveSessionKernel(session, cwd, options);
|
|
582
|
-
if (sessions.get(session.sessionKey) !== session || session.kernel !== kernel) {
|
|
583
|
-
throw new PythonExecutionCancelledError(false);
|
|
584
|
-
}
|
|
585
|
-
const runOptions = { ...options, cwd };
|
|
586
|
-
try {
|
|
587
|
-
return await executeWithKernel(kernel, code, runOptions);
|
|
588
|
-
} catch (err) {
|
|
589
|
-
if (isCancellationError(err, PythonExecutionCancelledError) || options.signal?.aborted) throw err;
|
|
590
|
-
if (kernel.isAlive()) throw err;
|
|
591
|
-
const retryKernel = await acquireLiveSessionKernel(session, cwd, options);
|
|
592
|
-
if (sessions.get(session.sessionKey) !== session || session.kernel !== retryKernel) {
|
|
593
|
-
throw new PythonExecutionCancelledError(false);
|
|
594
|
-
}
|
|
595
|
-
return await executeWithKernel(retryKernel, code, runOptions);
|
|
596
|
-
}
|
|
364
|
+
const sessionRegistry = createKernelSessionRegistry<PythonKernel, PythonExecutorOptions, PythonResult, PythonSession>({
|
|
365
|
+
languageLabel: "Python",
|
|
366
|
+
cancelledErrorClass: PythonExecutionCancelledError,
|
|
367
|
+
buildSessionKey: (sessionId, cwd, interpreter) => {
|
|
368
|
+
const normalizedCwd = normalizeKernelSessionCwd(cwd);
|
|
369
|
+
return `${sessionId}\0${normalizedCwd}\0${normalizeExplicitInterpreter(normalizedCwd, interpreter)}`;
|
|
370
|
+
},
|
|
371
|
+
createSession: session => ({ ...session, generation: 0 }),
|
|
372
|
+
startKernel,
|
|
373
|
+
executeWithKernel,
|
|
374
|
+
replaceSessionKernel,
|
|
375
|
+
acquireLiveSessionKernel,
|
|
376
|
+
invalidateSession: session => {
|
|
377
|
+
session.generation += 1;
|
|
378
|
+
},
|
|
379
|
+
shutdownSession: session => shutdownInvalidatedSession(session),
|
|
380
|
+
validateKernel: (session, kernel) => session.kernel === kernel,
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
export async function disposeAllKernelSessions(): Promise<void> {
|
|
384
|
+
await sessionRegistry.disposeAll();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export async function disposeKernelSessionsByOwner(ownerId: string): Promise<void> {
|
|
388
|
+
await sessionRegistry.disposeByOwner(ownerId);
|
|
597
389
|
}
|
|
598
390
|
|
|
599
391
|
export async function executePythonWithKernel(
|
|
@@ -605,7 +397,7 @@ export async function executePythonWithKernel(
|
|
|
605
397
|
}
|
|
606
398
|
|
|
607
399
|
export async function executePython(code: string, options?: PythonExecutorOptions): Promise<PythonResult> {
|
|
608
|
-
const cwd =
|
|
400
|
+
const cwd = normalizeKernelSessionCwd(options?.cwd ?? getProjectDir());
|
|
609
401
|
const deadlineMs = getExecutionDeadlineMs(options);
|
|
610
402
|
const executionOptions: PythonExecutorOptions = {
|
|
611
403
|
...(options ?? {}),
|
|
@@ -631,7 +423,7 @@ export async function executePython(code: string, options?: PythonExecutorOption
|
|
|
631
423
|
if (kernelMode === "per-call") {
|
|
632
424
|
return await executePerCall(code, cwd, executionOptions);
|
|
633
425
|
}
|
|
634
|
-
return await executeOnSession(code, cwd, executionOptions);
|
|
426
|
+
return await sessionRegistry.executeOnSession(code, cwd, executionOptions);
|
|
635
427
|
} catch (err) {
|
|
636
428
|
if (isCancellationError(err, PythonExecutionCancelledError) || executionOptions.signal?.aborted) {
|
|
637
429
|
return createCancelledPythonResult(
|