@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
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import {
|
|
5
|
+
attachSessionOwner,
|
|
6
|
+
type CancelledErrorClass,
|
|
7
|
+
getRemainingTimeoutMs,
|
|
8
|
+
isCancellationError,
|
|
9
|
+
isTimedOutCancellation,
|
|
10
|
+
resolveOwnerScopedSessionKey,
|
|
11
|
+
type SessionOwners,
|
|
12
|
+
} from "./executor-base";
|
|
13
|
+
|
|
14
|
+
interface KernelSessionRegistryOptions {
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
kernelOwnerId?: string;
|
|
17
|
+
interpreter?: string;
|
|
18
|
+
reset?: boolean;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
deadlineMs?: number;
|
|
21
|
+
bridge?: unknown;
|
|
22
|
+
bridgeSessionId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface RegistryKernelShutdownResult {
|
|
26
|
+
confirmed?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface RegistryKernel {
|
|
30
|
+
isAlive(): boolean;
|
|
31
|
+
shutdown(options?: { timeoutMs: number }): Promise<RegistryKernelShutdownResult>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface KernelSession<TKernel extends RegistryKernel> extends SessionOwners {
|
|
35
|
+
sessionKey: string;
|
|
36
|
+
sessionId: string;
|
|
37
|
+
cwd: string;
|
|
38
|
+
kernel: TKernel;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface StartingKernelSession<TSession> extends SessionOwners {
|
|
42
|
+
promise: Promise<TSession>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface KernelSessionRegistryContext<
|
|
46
|
+
TKernel extends RegistryKernel,
|
|
47
|
+
TOptions extends KernelSessionRegistryOptions,
|
|
48
|
+
TSession extends KernelSession<TKernel>,
|
|
49
|
+
> {
|
|
50
|
+
sessions: Map<string, TSession>;
|
|
51
|
+
startKernel: (cwd: string, options: TOptions) => Promise<TKernel>;
|
|
52
|
+
replaceSessionKernel: (session: TSession, cwd: string, options: TOptions) => Promise<TKernel>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface KernelSessionRegistryDescriptor<
|
|
56
|
+
TKernel extends RegistryKernel,
|
|
57
|
+
TOptions extends KernelSessionRegistryOptions,
|
|
58
|
+
TResult,
|
|
59
|
+
TSession extends KernelSession<TKernel>,
|
|
60
|
+
> {
|
|
61
|
+
languageLabel: string;
|
|
62
|
+
cancelledErrorClass: CancelledErrorClass;
|
|
63
|
+
buildSessionKey: (sessionId: string, cwd: string, interpreter: string | undefined) => string;
|
|
64
|
+
createSession: (session: KernelSession<TKernel>) => TSession;
|
|
65
|
+
startKernel: (cwd: string, options: TOptions) => Promise<TKernel>;
|
|
66
|
+
executeWithKernel: (kernel: TKernel, code: string, options: TOptions) => Promise<TResult>;
|
|
67
|
+
waitForStartup?: (promise: Promise<TSession>, options: TOptions) => Promise<TSession>;
|
|
68
|
+
replaceSessionKernel?: (
|
|
69
|
+
session: TSession,
|
|
70
|
+
cwd: string,
|
|
71
|
+
options: TOptions,
|
|
72
|
+
context: KernelSessionRegistryContext<TKernel, TOptions, TSession>,
|
|
73
|
+
) => Promise<TKernel>;
|
|
74
|
+
acquireLiveSessionKernel?: (
|
|
75
|
+
session: TSession,
|
|
76
|
+
cwd: string,
|
|
77
|
+
options: TOptions,
|
|
78
|
+
context: KernelSessionRegistryContext<TKernel, TOptions, TSession>,
|
|
79
|
+
) => Promise<TKernel>;
|
|
80
|
+
invalidateSession?: (session: TSession) => void;
|
|
81
|
+
shutdownSession?: (session: TSession, resetting: boolean) => Promise<RegistryKernelShutdownResult>;
|
|
82
|
+
clearResetsOnDisposeAll?: boolean;
|
|
83
|
+
logBeforeReplacement?: boolean;
|
|
84
|
+
isCancellation?: (error: unknown) => boolean;
|
|
85
|
+
isTimedOutCancellation?: (error: unknown, signal?: AbortSignal) => boolean;
|
|
86
|
+
validateKernel?: (session: TSession, kernel: TKernel) => boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface KernelSessionRegistry<TOptions extends KernelSessionRegistryOptions, TResult> {
|
|
90
|
+
disposeAll(): Promise<void>;
|
|
91
|
+
disposeByOwner(ownerId: string): Promise<void>;
|
|
92
|
+
executeOnSession(code: string, cwd: string, options: TOptions): Promise<TResult>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function normalizeKernelSessionCwd(cwd: string): string {
|
|
96
|
+
return path.resolve(cwd);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function requireRemainingKernelTimeoutMs(
|
|
100
|
+
deadlineMs: number | undefined,
|
|
101
|
+
cancelledErrorClass: CancelledErrorClass,
|
|
102
|
+
): number | undefined {
|
|
103
|
+
const remainingMs = getRemainingTimeoutMs(deadlineMs);
|
|
104
|
+
if (remainingMs === undefined) return undefined;
|
|
105
|
+
if (remainingMs <= 0) {
|
|
106
|
+
throw new cancelledErrorClass(true);
|
|
107
|
+
}
|
|
108
|
+
return remainingMs;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function formatSessionTimeoutAnnotation(timeoutMs?: number): string {
|
|
112
|
+
if (timeoutMs === undefined) return "Command timed out";
|
|
113
|
+
const secs = Math.max(1, Math.round(timeoutMs / 1000));
|
|
114
|
+
return `Command timed out after ${secs} seconds`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function formatSessionKernelTimeoutAnnotation(timeoutMs: number | undefined, kernelKilled: boolean): string {
|
|
118
|
+
const secs = timeoutMs === undefined ? undefined : Math.max(1, Math.round(timeoutMs / 1000));
|
|
119
|
+
if (kernelKilled) {
|
|
120
|
+
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.";
|
|
121
|
+
}
|
|
122
|
+
const duration = secs === undefined ? "the configured timeout" : `${secs}s`;
|
|
123
|
+
return `eval cell timed out after ${duration}; kernel interrupted but remains running. Reset the kernel via { reset: true } if state appears corrupted.`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createKernelSessionRegistry<
|
|
127
|
+
TKernel extends RegistryKernel,
|
|
128
|
+
TOptions extends KernelSessionRegistryOptions,
|
|
129
|
+
TResult,
|
|
130
|
+
TSession extends KernelSession<TKernel>,
|
|
131
|
+
>(
|
|
132
|
+
descriptor: KernelSessionRegistryDescriptor<TKernel, TOptions, TResult, TSession>,
|
|
133
|
+
): KernelSessionRegistry<TOptions, TResult> {
|
|
134
|
+
const sessions = new Map<string, TSession>();
|
|
135
|
+
const startingSessions = new Map<string, StartingKernelSession<TSession>>();
|
|
136
|
+
const resettingSessions = new Map<string, Promise<void>>();
|
|
137
|
+
|
|
138
|
+
const context: KernelSessionRegistryContext<TKernel, TOptions, TSession> = {
|
|
139
|
+
sessions,
|
|
140
|
+
startKernel: descriptor.startKernel,
|
|
141
|
+
replaceSessionKernel,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
function waitForStartup(promise: Promise<TSession>, options: TOptions): Promise<TSession> {
|
|
145
|
+
return descriptor.waitForStartup?.(promise, options) ?? promise;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isCurrent(session: TSession, kernel?: TKernel): boolean {
|
|
149
|
+
return (
|
|
150
|
+
sessions.get(session.sessionKey) === session &&
|
|
151
|
+
(kernel === undefined || descriptor.validateKernel?.(session, kernel) !== false)
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function acquireSession(
|
|
156
|
+
sessionKey: string,
|
|
157
|
+
sessionId: string,
|
|
158
|
+
cwd: string,
|
|
159
|
+
options: TOptions,
|
|
160
|
+
): Promise<TSession> {
|
|
161
|
+
const existing = sessions.get(sessionKey);
|
|
162
|
+
if (existing) {
|
|
163
|
+
attachSessionOwner(existing, sessionId, options.kernelOwnerId);
|
|
164
|
+
return existing;
|
|
165
|
+
}
|
|
166
|
+
const starting = startingSessions.get(sessionKey);
|
|
167
|
+
if (starting) {
|
|
168
|
+
attachSessionOwner(starting, sessionId, options.kernelOwnerId);
|
|
169
|
+
return await waitForStartup(starting.promise, options);
|
|
170
|
+
}
|
|
171
|
+
let startingSession!: StartingKernelSession<TSession>;
|
|
172
|
+
const startup = (async () => {
|
|
173
|
+
const kernel = await descriptor.startKernel(cwd, options);
|
|
174
|
+
const session = descriptor.createSession({
|
|
175
|
+
sessionKey,
|
|
176
|
+
sessionId,
|
|
177
|
+
cwd,
|
|
178
|
+
kernel,
|
|
179
|
+
ownerIds: new Set(startingSession.ownerIds),
|
|
180
|
+
hasFallbackOwner: startingSession.hasFallbackOwner,
|
|
181
|
+
});
|
|
182
|
+
if (startingSessions.get(sessionKey) === startingSession) {
|
|
183
|
+
sessions.set(sessionKey, session);
|
|
184
|
+
}
|
|
185
|
+
return session;
|
|
186
|
+
})();
|
|
187
|
+
startingSession = {
|
|
188
|
+
ownerIds: new Set(),
|
|
189
|
+
hasFallbackOwner: false,
|
|
190
|
+
promise: startup,
|
|
191
|
+
};
|
|
192
|
+
attachSessionOwner(startingSession, sessionId, options.kernelOwnerId);
|
|
193
|
+
startingSessions.set(sessionKey, startingSession);
|
|
194
|
+
try {
|
|
195
|
+
return await waitForStartup(startup, options);
|
|
196
|
+
} finally {
|
|
197
|
+
if (startingSessions.get(sessionKey) === startingSession) startingSessions.delete(sessionKey);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function replaceSessionKernel(session: TSession, cwd: string, options: TOptions): Promise<TKernel> {
|
|
202
|
+
if (descriptor.replaceSessionKernel) {
|
|
203
|
+
return await descriptor.replaceSessionKernel(session, cwd, options, context);
|
|
204
|
+
}
|
|
205
|
+
if (descriptor.logBeforeReplacement) {
|
|
206
|
+
logger.warn(`${descriptor.languageLabel} subprocess died or is unresponsive; spawning fresh process`, {
|
|
207
|
+
sessionKey: session.sessionKey,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const old = session.kernel;
|
|
211
|
+
const remaining = getRemainingTimeoutMs(options.deadlineMs);
|
|
212
|
+
await old
|
|
213
|
+
.shutdown(remaining !== undefined ? { timeoutMs: Math.max(0, remaining) } : undefined)
|
|
214
|
+
.catch(() => undefined);
|
|
215
|
+
if (sessions.get(session.sessionKey) !== session) {
|
|
216
|
+
throw new descriptor.cancelledErrorClass(false);
|
|
217
|
+
}
|
|
218
|
+
requireRemainingKernelTimeoutMs(options.deadlineMs, descriptor.cancelledErrorClass);
|
|
219
|
+
const next = await descriptor.startKernel(cwd, options);
|
|
220
|
+
if (sessions.get(session.sessionKey) !== session) {
|
|
221
|
+
await next.shutdown().catch(() => undefined);
|
|
222
|
+
throw new descriptor.cancelledErrorClass(false);
|
|
223
|
+
}
|
|
224
|
+
session.kernel = next;
|
|
225
|
+
return next;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function acquireLiveSessionKernel(session: TSession, cwd: string, options: TOptions): Promise<TKernel> {
|
|
229
|
+
if (descriptor.acquireLiveSessionKernel) {
|
|
230
|
+
return await descriptor.acquireLiveSessionKernel(session, cwd, options, context);
|
|
231
|
+
}
|
|
232
|
+
if (!isCurrent(session)) throw new descriptor.cancelledErrorClass(false);
|
|
233
|
+
if (!session.kernel.isAlive()) await replaceSessionKernel(session, cwd, options);
|
|
234
|
+
if (!isCurrent(session)) throw new descriptor.cancelledErrorClass(false);
|
|
235
|
+
return session.kernel;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function shutdownSession(session: TSession, resetting: boolean): Promise<RegistryKernelShutdownResult> {
|
|
239
|
+
return await (descriptor.shutdownSession?.(session, resetting) ?? session.kernel.shutdown());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function resetSession(sessionKey: string): Promise<void> {
|
|
243
|
+
const existing =
|
|
244
|
+
sessions.get(sessionKey) ?? (await startingSessions.get(sessionKey)?.promise.catch(() => undefined));
|
|
245
|
+
if (!existing) return;
|
|
246
|
+
descriptor.invalidateSession?.(existing);
|
|
247
|
+
sessions.delete(sessionKey);
|
|
248
|
+
await shutdownSession(existing, true).catch(() => undefined);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function disposeAll(): Promise<void> {
|
|
252
|
+
const pending = [...startingSessions.values()].map(starting => starting.promise);
|
|
253
|
+
startingSessions.clear();
|
|
254
|
+
if (descriptor.clearResetsOnDisposeAll) resettingSessions.clear();
|
|
255
|
+
const started = await Promise.allSettled(pending);
|
|
256
|
+
const all = [...sessions.entries()];
|
|
257
|
+
for (const result of started) {
|
|
258
|
+
if (result.status !== "fulfilled") continue;
|
|
259
|
+
if (!all.some(([, session]) => session === result.value)) {
|
|
260
|
+
all.push([result.value.sessionKey, result.value]);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
for (const [id, session] of all) {
|
|
264
|
+
descriptor.invalidateSession?.(session);
|
|
265
|
+
if (sessions.get(id) === session) sessions.delete(id);
|
|
266
|
+
}
|
|
267
|
+
const results = await Promise.allSettled(all.map(([, session]) => shutdownSession(session, false)));
|
|
268
|
+
for (let i = 0; i < all.length; i += 1) {
|
|
269
|
+
const [id, session] = all[i];
|
|
270
|
+
const result = results[i];
|
|
271
|
+
if (result.status === "fulfilled" && result.value?.confirmed !== false) continue;
|
|
272
|
+
const reason = result.status === "rejected" ? result.reason : "not confirmed";
|
|
273
|
+
logger.warn(`${descriptor.languageLabel} kernel shutdown not confirmed`, {
|
|
274
|
+
sessionId: session.sessionId,
|
|
275
|
+
sessionKey: id,
|
|
276
|
+
cwd: session.cwd,
|
|
277
|
+
reason,
|
|
278
|
+
});
|
|
279
|
+
if (!sessions.has(id)) sessions.set(id, session);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function disposeByOwner(ownerId: string): Promise<void> {
|
|
284
|
+
const toShutdown: TSession[] = [];
|
|
285
|
+
const startingToShutdown: StartingKernelSession<TSession>[] = [];
|
|
286
|
+
for (const session of [...sessions.values()]) {
|
|
287
|
+
if (!session.ownerIds.has(ownerId)) continue;
|
|
288
|
+
if (session.ownerIds.size === 1) {
|
|
289
|
+
toShutdown.push(session);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
session.ownerIds.delete(ownerId);
|
|
293
|
+
}
|
|
294
|
+
for (const [sessionKey, starting] of [...startingSessions.entries()]) {
|
|
295
|
+
if (sessions.has(sessionKey) || !starting.ownerIds.has(ownerId)) continue;
|
|
296
|
+
if (starting.ownerIds.size === 1) {
|
|
297
|
+
startingSessions.delete(sessionKey);
|
|
298
|
+
startingToShutdown.push(starting);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
starting.ownerIds.delete(ownerId);
|
|
302
|
+
}
|
|
303
|
+
for (const session of toShutdown) {
|
|
304
|
+
descriptor.invalidateSession?.(session);
|
|
305
|
+
if (sessions.get(session.sessionKey) === session) sessions.delete(session.sessionKey);
|
|
306
|
+
}
|
|
307
|
+
const started = await Promise.allSettled(startingToShutdown.map(starting => starting.promise));
|
|
308
|
+
for (const result of started) {
|
|
309
|
+
if (result.status !== "fulfilled") continue;
|
|
310
|
+
const session = result.value;
|
|
311
|
+
descriptor.invalidateSession?.(session);
|
|
312
|
+
if (sessions.get(session.sessionKey) === session) sessions.delete(session.sessionKey);
|
|
313
|
+
toShutdown.push(session);
|
|
314
|
+
}
|
|
315
|
+
const results = await Promise.allSettled(toShutdown.map(session => shutdownSession(session, false)));
|
|
316
|
+
for (let i = 0; i < toShutdown.length; i += 1) {
|
|
317
|
+
const session = toShutdown[i];
|
|
318
|
+
const result = results[i];
|
|
319
|
+
if (result.status === "fulfilled" && result.value?.confirmed !== false) {
|
|
320
|
+
session.ownerIds.delete(ownerId);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const reason = result.status === "rejected" ? result.reason : "not confirmed";
|
|
324
|
+
logger.warn(`${descriptor.languageLabel} kernel shutdown not confirmed`, {
|
|
325
|
+
sessionId: session.sessionId,
|
|
326
|
+
sessionKey: session.sessionKey,
|
|
327
|
+
cwd: session.cwd,
|
|
328
|
+
reason,
|
|
329
|
+
});
|
|
330
|
+
if (!sessions.has(session.sessionKey)) sessions.set(session.sessionKey, session);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function executeOnSession(code: string, cwd: string, options: TOptions): Promise<TResult> {
|
|
335
|
+
const sessionId = options.sessionId ?? `session:${cwd}`;
|
|
336
|
+
const sessionKey = resolveOwnerScopedSessionKey({
|
|
337
|
+
baseKey: descriptor.buildSessionKey(sessionId, cwd, options.interpreter),
|
|
338
|
+
ownerId: options.kernelOwnerId,
|
|
339
|
+
reset: options.reset === true,
|
|
340
|
+
hasSession: key => sessions.has(key) || startingSessions.has(key),
|
|
341
|
+
getOwners: key => sessions.get(key) ?? startingSessions.get(key),
|
|
342
|
+
});
|
|
343
|
+
if (options.bridge && !options.bridgeSessionId) {
|
|
344
|
+
options.bridgeSessionId = sessionId;
|
|
345
|
+
}
|
|
346
|
+
if (options.reset) {
|
|
347
|
+
const inFlight = resettingSessions.get(sessionKey);
|
|
348
|
+
if (inFlight) await inFlight.catch(() => undefined);
|
|
349
|
+
else {
|
|
350
|
+
const resetPromise = resetSession(sessionKey);
|
|
351
|
+
resettingSessions.set(
|
|
352
|
+
sessionKey,
|
|
353
|
+
resetPromise.then(() => undefined),
|
|
354
|
+
);
|
|
355
|
+
try {
|
|
356
|
+
await resetPromise;
|
|
357
|
+
} finally {
|
|
358
|
+
resettingSessions.delete(sessionKey);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
const inFlight = resettingSessions.get(sessionKey);
|
|
363
|
+
if (inFlight) await inFlight.catch(() => undefined);
|
|
364
|
+
}
|
|
365
|
+
const session = await acquireSession(sessionKey, sessionId, cwd, options);
|
|
366
|
+
if (options.signal?.aborted) {
|
|
367
|
+
const timedOut =
|
|
368
|
+
descriptor.isTimedOutCancellation?.(options.signal.reason, options.signal) ??
|
|
369
|
+
isTimedOutCancellation(options.signal.reason, descriptor.cancelledErrorClass, options.signal);
|
|
370
|
+
throw new descriptor.cancelledErrorClass(timedOut);
|
|
371
|
+
}
|
|
372
|
+
const kernel = await acquireLiveSessionKernel(session, cwd, options);
|
|
373
|
+
if (!isCurrent(session, kernel)) throw new descriptor.cancelledErrorClass(false);
|
|
374
|
+
const runOptions = { ...options, cwd };
|
|
375
|
+
try {
|
|
376
|
+
return await descriptor.executeWithKernel(kernel, code, runOptions);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (
|
|
379
|
+
descriptor.isCancellation?.(err) ||
|
|
380
|
+
isCancellationError(err, descriptor.cancelledErrorClass) ||
|
|
381
|
+
options.signal?.aborted
|
|
382
|
+
)
|
|
383
|
+
throw err;
|
|
384
|
+
if (kernel.isAlive()) throw err;
|
|
385
|
+
let retryKernel: TKernel;
|
|
386
|
+
if (descriptor.acquireLiveSessionKernel) {
|
|
387
|
+
retryKernel = await acquireLiveSessionKernel(session, cwd, options);
|
|
388
|
+
} else {
|
|
389
|
+
if (!isCurrent(session, kernel)) throw new descriptor.cancelledErrorClass(false);
|
|
390
|
+
retryKernel = await replaceSessionKernel(session, cwd, options);
|
|
391
|
+
}
|
|
392
|
+
if (!isCurrent(session, retryKernel)) throw new descriptor.cancelledErrorClass(false);
|
|
393
|
+
return await descriptor.executeWithKernel(retryKernel, code, runOptions);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return { disposeAll, disposeByOwner, executeOnSession };
|
|
398
|
+
}
|