@codex-infinity/pi-infinity 0.64.3 → 0.65.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -34
- package/README.md +1 -1
- package/dist/cli/args.d.ts +7 -4
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +37 -15
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +51 -104
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +103 -141
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +86 -0
- package/dist/core/agent-session-services.d.ts.map +1 -0
- package/dist/core/agent-session-services.js +116 -0
- package/dist/core/agent-session-services.js.map +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -13
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +4 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -14
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/package-manager.d.ts +20 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +32 -0
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +21 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +2 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +202 -457
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +20 -0
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +56 -29
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts +2 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +4 -0
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +1 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +23 -15
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts +4 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +234 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/docs/extensions.md +2 -26
- package/docs/sdk.md +97 -37
- package/docs/settings.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/13-session-runtime.ts +30 -12
- package/examples/sdk/README.md +2 -0
- package/package.json +4 -4
|
@@ -1,136 +1,83 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
import { ModelRegistry } from "./model-registry.js";
|
|
6
|
-
import { type DefaultResourceLoaderOptions, type ResourceLoader } from "./resource-loader.js";
|
|
7
|
-
import { type CreateAgentSessionResult } from "./sdk.js";
|
|
1
|
+
import type { AgentSession } from "./agent-session.js";
|
|
2
|
+
import type { AgentSessionRuntimeDiagnostic, AgentSessionServices } from "./agent-session-services.js";
|
|
3
|
+
import type { SessionStartEvent } from "./extensions/index.js";
|
|
4
|
+
import type { CreateAgentSessionResult } from "./sdk.js";
|
|
8
5
|
import { SessionManager } from "./session-manager.js";
|
|
9
|
-
import { SettingsManager } from "./settings-manager.js";
|
|
10
|
-
import type { Tool } from "./tools/index.js";
|
|
11
6
|
/**
|
|
12
|
-
*
|
|
7
|
+
* Result returned by runtime creation.
|
|
13
8
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* or in settings, not here.
|
|
9
|
+
* The caller gets the created session, its cwd-bound services, and all
|
|
10
|
+
* diagnostics collected during setup.
|
|
17
11
|
*/
|
|
18
|
-
export interface
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/** Optional shared auth storage. If omitted, file-backed storage under agentDir is used. */
|
|
22
|
-
authStorage?: AuthStorage;
|
|
23
|
-
/** Initial model for the first created session runtime. */
|
|
24
|
-
model?: Model<any>;
|
|
25
|
-
/** Initial thinking level for the first created session runtime. */
|
|
26
|
-
thinkingLevel?: ThinkingLevel;
|
|
27
|
-
/** Optional scoped model list for model cycling and selection. */
|
|
28
|
-
scopedModels?: Array<{
|
|
29
|
-
model: Model<any>;
|
|
30
|
-
thinkingLevel?: ThinkingLevel;
|
|
31
|
-
}>;
|
|
32
|
-
/** Built-in tool set override. */
|
|
33
|
-
tools?: Tool[];
|
|
34
|
-
/** Additional custom tools registered directly through the SDK. */
|
|
35
|
-
customTools?: ToolDefinition[];
|
|
36
|
-
/**
|
|
37
|
-
* Resource loader input used for each created runtime.
|
|
38
|
-
*
|
|
39
|
-
* Pass either a factory that creates a fully custom ResourceLoader for the
|
|
40
|
-
* target cwd, or DefaultResourceLoader options without cwd/agentDir/
|
|
41
|
-
* settingsManager, which are supplied by the runtime.
|
|
42
|
-
*/
|
|
43
|
-
resourceLoader?: ((cwd: string, agentDir: string) => Promise<ResourceLoader>) | Omit<DefaultResourceLoaderOptions, "cwd" | "agentDir" | "settingsManager">;
|
|
12
|
+
export interface CreateAgentSessionRuntimeResult extends CreateAgentSessionResult {
|
|
13
|
+
services: AgentSessionServices;
|
|
14
|
+
diagnostics: AgentSessionRuntimeDiagnostic[];
|
|
44
15
|
}
|
|
45
|
-
/** Options for creating one concrete runtime instance. */
|
|
46
|
-
export interface CreateAgentSessionRuntimeOptions {
|
|
47
|
-
/** Working directory for this runtime instance. */
|
|
48
|
-
cwd: string;
|
|
49
|
-
/** Optional preselected session manager. If omitted, normal session resolution applies. */
|
|
50
|
-
sessionManager?: SessionManager;
|
|
51
|
-
/** Optional preloaded resource loader to reuse instead of creating and reloading one. */
|
|
52
|
-
resourceLoader?: ResourceLoader;
|
|
53
|
-
/** Optional session_start metadata to emit when the runtime binds extensions. */
|
|
54
|
-
sessionStartEvent?: SessionStartEvent;
|
|
55
|
-
}
|
|
56
|
-
type AgentSessionRuntime = CreateAgentSessionResult & {
|
|
57
|
-
cwd: string;
|
|
58
|
-
agentDir: string;
|
|
59
|
-
authStorage: AuthStorage;
|
|
60
|
-
modelRegistry: ModelRegistry;
|
|
61
|
-
settingsManager: SettingsManager;
|
|
62
|
-
resourceLoader: ResourceLoader;
|
|
63
|
-
sessionManager: SessionManager;
|
|
64
|
-
};
|
|
65
16
|
/**
|
|
66
|
-
*
|
|
67
|
-
* services it depends on.
|
|
17
|
+
* Creates a full runtime for a target cwd and session manager.
|
|
68
18
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* directories.
|
|
19
|
+
* The factory closes over process-global fixed inputs, recreates cwd-bound
|
|
20
|
+
* services for the effective cwd, resolves session options against those
|
|
21
|
+
* services, and finally creates the AgentSession.
|
|
73
22
|
*/
|
|
74
|
-
export
|
|
23
|
+
export type CreateAgentSessionRuntimeFactory = (options: {
|
|
24
|
+
cwd: string;
|
|
25
|
+
agentDir: string;
|
|
26
|
+
sessionManager: SessionManager;
|
|
27
|
+
sessionStartEvent?: SessionStartEvent;
|
|
28
|
+
}) => Promise<CreateAgentSessionRuntimeResult>;
|
|
75
29
|
/**
|
|
76
|
-
*
|
|
30
|
+
* Owns the current AgentSession plus its cwd-bound services.
|
|
77
31
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
32
|
+
* Session replacement methods tear down the current runtime first, then create
|
|
33
|
+
* and apply the next runtime. If creation fails, the error is propagated to the
|
|
34
|
+
* caller. The caller is responsible for user-facing error handling.
|
|
81
35
|
*/
|
|
82
|
-
export declare class
|
|
83
|
-
private
|
|
84
|
-
private
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
36
|
+
export declare class AgentSessionRuntime {
|
|
37
|
+
private _session;
|
|
38
|
+
private _services;
|
|
39
|
+
private readonly createRuntime;
|
|
40
|
+
private _diagnostics;
|
|
41
|
+
private _modelFallbackMessage?;
|
|
42
|
+
constructor(_session: AgentSession, _services: AgentSessionServices, createRuntime: CreateAgentSessionRuntimeFactory, _diagnostics?: AgentSessionRuntimeDiagnostic[], _modelFallbackMessage?: string | undefined);
|
|
43
|
+
get services(): AgentSessionServices;
|
|
44
|
+
get session(): AgentSession;
|
|
45
|
+
get cwd(): string;
|
|
46
|
+
get diagnostics(): readonly AgentSessionRuntimeDiagnostic[];
|
|
47
|
+
get modelFallbackMessage(): string | undefined;
|
|
88
48
|
private emitBeforeSwitch;
|
|
89
49
|
private emitBeforeFork;
|
|
90
|
-
private
|
|
91
|
-
|
|
92
|
-
* Replace the active runtime with one opened from an existing session file.
|
|
93
|
-
*
|
|
94
|
-
* Emits `session_before_switch` before replacement and returns
|
|
95
|
-
* `{ cancelled: true }` if an extension vetoes the switch.
|
|
96
|
-
*/
|
|
50
|
+
private teardownCurrent;
|
|
51
|
+
private apply;
|
|
97
52
|
switchSession(sessionPath: string): Promise<{
|
|
98
53
|
cancelled: boolean;
|
|
99
54
|
}>;
|
|
100
|
-
/**
|
|
101
|
-
* Replace the active runtime with a fresh session in the current cwd.
|
|
102
|
-
*
|
|
103
|
-
* `setup` runs after replacement against the new session manager, which lets
|
|
104
|
-
* callers seed entries before normal use begins.
|
|
105
|
-
*/
|
|
106
55
|
newSession(options?: {
|
|
107
|
-
/** Optional parent session path recorded in the new session header. */
|
|
108
56
|
parentSession?: string;
|
|
109
|
-
/** Optional callback for seeding the new session manager after replacement. */
|
|
110
57
|
setup?: (sessionManager: SessionManager) => Promise<void>;
|
|
111
58
|
}): Promise<{
|
|
112
59
|
cancelled: boolean;
|
|
113
60
|
}>;
|
|
114
|
-
/**
|
|
115
|
-
* Replace the active runtime with a fork rooted at the given user-message
|
|
116
|
-
* entry.
|
|
117
|
-
*
|
|
118
|
-
* Returns the selected user text so UIs can restore it into the editor after
|
|
119
|
-
* the fork completes.
|
|
120
|
-
*/
|
|
121
61
|
fork(entryId: string): Promise<{
|
|
122
62
|
cancelled: boolean;
|
|
123
63
|
selectedText?: string;
|
|
124
64
|
}>;
|
|
125
|
-
/**
|
|
126
|
-
* Import a JSONL session file into the current session directory and replace
|
|
127
|
-
* the active runtime with the imported session.
|
|
128
|
-
*/
|
|
129
65
|
importFromJsonl(inputPath: string): Promise<{
|
|
130
66
|
cancelled: boolean;
|
|
131
67
|
}>;
|
|
132
|
-
/** Emit session shutdown for the active runtime and dispose it permanently. */
|
|
133
68
|
dispose(): Promise<void>;
|
|
134
69
|
}
|
|
135
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Create the initial runtime from a runtime factory and initial session target.
|
|
72
|
+
*
|
|
73
|
+
* The same factory is stored on the returned AgentSessionRuntime and reused for
|
|
74
|
+
* later /new, /resume, /fork, and import flows.
|
|
75
|
+
*/
|
|
76
|
+
export declare function createAgentSessionRuntime(createRuntime: CreateAgentSessionRuntimeFactory, options: {
|
|
77
|
+
cwd: string;
|
|
78
|
+
agentDir: string;
|
|
79
|
+
sessionManager: SessionManager;
|
|
80
|
+
sessionStartEvent?: SessionStartEvent;
|
|
81
|
+
}): Promise<AgentSessionRuntime>;
|
|
82
|
+
export { type AgentSessionRuntimeDiagnostic, type AgentSessionServices, type CreateAgentSessionFromServicesOptions, type CreateAgentSessionServicesOptions, createAgentSessionFromServices, createAgentSessionServices, } from "./agent-session-services.js";
|
|
136
83
|
//# sourceMappingURL=agent-session-runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-session-runtime.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { copyFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { basename, join, resolve } from \"node:path\";\nimport type { ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport type { Model } from \"@mariozechner/pi-ai\";\nimport { getAgentDir } from \"../config.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport type { SessionStartEvent, ToolDefinition } from \"./extensions/index.js\";\nimport { emitSessionShutdownEvent } from \"./extensions/runner.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { DefaultResourceLoader, type DefaultResourceLoaderOptions, type ResourceLoader } from \"./resource-loader.js\";\nimport { type CreateAgentSessionResult, createAgentSession } from \"./sdk.js\";\nimport { SessionManager } from \"./session-manager.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\nimport type { Tool } from \"./tools/index.js\";\n\n/**\n * Stable bootstrap inputs reused whenever the active runtime is replaced.\n *\n * Use this for process-level wiring that should survive `/new`, `/resume`,\n * `/fork`, and import flows. Session-local state belongs in the session file\n * or in settings, not here.\n */\nexport interface AgentSessionRuntimeBootstrap {\n\t/** Agent directory used for auth, models, settings, sessions, and resource discovery. */\n\tagentDir?: string;\n\t/** Optional shared auth storage. If omitted, file-backed storage under agentDir is used. */\n\tauthStorage?: AuthStorage;\n\t/** Initial model for the first created session runtime. */\n\tmodel?: Model<any>;\n\t/** Initial thinking level for the first created session runtime. */\n\tthinkingLevel?: ThinkingLevel;\n\t/** Optional scoped model list for model cycling and selection. */\n\tscopedModels?: Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;\n\t/** Built-in tool set override. */\n\ttools?: Tool[];\n\t/** Additional custom tools registered directly through the SDK. */\n\tcustomTools?: ToolDefinition[];\n\t/**\n\t * Resource loader input used for each created runtime.\n\t *\n\t * Pass either a factory that creates a fully custom ResourceLoader for the\n\t * target cwd, or DefaultResourceLoader options without cwd/agentDir/\n\t * settingsManager, which are supplied by the runtime.\n\t */\n\tresourceLoader?:\n\t\t| ((cwd: string, agentDir: string) => Promise<ResourceLoader>)\n\t\t| Omit<DefaultResourceLoaderOptions, \"cwd\" | \"agentDir\" | \"settingsManager\">;\n}\n\n/** Options for creating one concrete runtime instance. */\nexport interface CreateAgentSessionRuntimeOptions {\n\t/** Working directory for this runtime instance. */\n\tcwd: string;\n\t/** Optional preselected session manager. If omitted, normal session resolution applies. */\n\tsessionManager?: SessionManager;\n\t/** Optional preloaded resource loader to reuse instead of creating and reloading one. */\n\tresourceLoader?: ResourceLoader;\n\t/** Optional session_start metadata to emit when the runtime binds extensions. */\n\tsessionStartEvent?: SessionStartEvent;\n}\n\ntype AgentSessionRuntime = CreateAgentSessionResult & {\n\tcwd: string;\n\tagentDir: string;\n\tauthStorage: AuthStorage;\n\tmodelRegistry: ModelRegistry;\n\tsettingsManager: SettingsManager;\n\tresourceLoader: ResourceLoader;\n\tsessionManager: SessionManager;\n};\n\n/**\n * Create one runtime instance containing an AgentSession plus the cwd-bound\n * services it depends on.\n *\n * Most SDK callers should keep the returned value wrapped in an\n * AgentSessionRuntimeHost instead of holding it directly. The host owns\n * replacing the runtime when switching sessions across files or working\n * directories.\n */\nexport async function createAgentSessionRuntime(\n\tbootstrap: AgentSessionRuntimeBootstrap,\n\toptions: CreateAgentSessionRuntimeOptions,\n): Promise<AgentSessionRuntime> {\n\tconst cwd = options.cwd;\n\tconst agentDir = bootstrap.agentDir ?? getAgentDir();\n\tconst authStorage = bootstrap.authStorage ?? AuthStorage.create(join(agentDir, \"auth.json\"));\n\tconst settingsManager = SettingsManager.create(cwd, agentDir);\n\tconst modelRegistry = ModelRegistry.create(authStorage, join(agentDir, \"models.json\"));\n\tconst resourceLoader =\n\t\toptions.resourceLoader ??\n\t\t(typeof bootstrap.resourceLoader === \"function\"\n\t\t\t? await bootstrap.resourceLoader(cwd, agentDir)\n\t\t\t: new DefaultResourceLoader({\n\t\t\t\t\t...(bootstrap.resourceLoader ?? {}),\n\t\t\t\t\tcwd,\n\t\t\t\t\tagentDir,\n\t\t\t\t\tsettingsManager,\n\t\t\t\t}));\n\tif (!options.resourceLoader) {\n\t\tawait resourceLoader.reload();\n\t}\n\n\tconst extensionsResult = resourceLoader.getExtensions();\n\tfor (const { name, config } of extensionsResult.runtime.pendingProviderRegistrations) {\n\t\tmodelRegistry.registerProvider(name, config);\n\t}\n\textensionsResult.runtime.pendingProviderRegistrations = [];\n\n\tconst created = await createAgentSession({\n\t\tcwd,\n\t\tagentDir,\n\t\tauthStorage,\n\t\tmodelRegistry,\n\t\tsettingsManager,\n\t\tresourceLoader,\n\t\tsessionManager: options.sessionManager,\n\t\tmodel: bootstrap.model,\n\t\tthinkingLevel: bootstrap.thinkingLevel,\n\t\tscopedModels: bootstrap.scopedModels,\n\t\ttools: bootstrap.tools,\n\t\tcustomTools: bootstrap.customTools,\n\t\tsessionStartEvent: options.sessionStartEvent,\n\t});\n\n\treturn {\n\t\t...created,\n\t\tcwd,\n\t\tagentDir,\n\t\tauthStorage,\n\t\tmodelRegistry,\n\t\tsettingsManager,\n\t\tresourceLoader,\n\t\tsessionManager: created.session.sessionManager,\n\t};\n}\n\nfunction extractUserMessageText(content: string | Array<{ type: string; text?: string }>): string {\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\n\treturn content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\");\n}\n\n/**\n * Stable wrapper around a replaceable AgentSession runtime.\n *\n * Use this when your application needs `/new`, `/resume`, `/fork`, or import\n * behavior. After replacement, read `session` again and rebind any\n * session-local subscriptions or extension bindings.\n */\nexport class AgentSessionRuntimeHost {\n\tconstructor(\n\t\tprivate readonly bootstrap: AgentSessionRuntimeBootstrap,\n\t\tprivate runtime: AgentSessionRuntime,\n\t) {}\n\n\t/** The currently active session instance. Re-read this after runtime replacement. */\n\tget session() {\n\t\treturn this.runtime.session;\n\t}\n\n\tprivate async emitBeforeSwitch(\n\t\treason: \"new\" | \"resume\",\n\t\ttargetSessionFile?: string,\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.runtime.session.extensionRunner;\n\t\tif (!runner?.hasHandlers(\"session_before_switch\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_switch\",\n\t\t\treason,\n\t\t\ttargetSessionFile,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async emitBeforeFork(entryId: string): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.runtime.session.extensionRunner;\n\t\tif (!runner?.hasHandlers(\"session_before_fork\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_fork\",\n\t\t\tentryId,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async replace(options: CreateAgentSessionRuntimeOptions): Promise<void> {\n\t\tconst nextRuntime = await createAgentSessionRuntime(this.bootstrap, options);\n\t\tawait emitSessionShutdownEvent(this.runtime.session.extensionRunner);\n\t\tthis.runtime.session.dispose();\n\t\tif (process.cwd() !== nextRuntime.cwd) {\n\t\t\tprocess.chdir(nextRuntime.cwd);\n\t\t}\n\t\tthis.runtime = nextRuntime;\n\t}\n\n\t/**\n\t * Replace the active runtime with one opened from an existing session file.\n\t *\n\t * Emits `session_before_switch` before replacement and returns\n\t * `{ cancelled: true }` if an extension vetoes the switch.\n\t */\n\tasync switchSession(sessionPath: string): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", sessionPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.runtime.session.sessionFile;\n\t\tconst sessionManager = SessionManager.open(sessionPath);\n\t\tawait this.replace({\n\t\t\tcwd: sessionManager.getCwd(),\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t});\n\t\treturn { cancelled: false };\n\t}\n\n\t/**\n\t * Replace the active runtime with a fresh session in the current cwd.\n\t *\n\t * `setup` runs after replacement against the new session manager, which lets\n\t * callers seed entries before normal use begins.\n\t */\n\tasync newSession(options?: {\n\t\t/** Optional parent session path recorded in the new session header. */\n\t\tparentSession?: string;\n\t\t/** Optional callback for seeding the new session manager after replacement. */\n\t\tsetup?: (sessionManager: SessionManager) => Promise<void>;\n\t}): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"new\");\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.runtime.session.sessionFile;\n\t\tconst sessionDir = this.runtime.sessionManager.getSessionDir();\n\t\tconst sessionManager = SessionManager.create(this.runtime.cwd, sessionDir);\n\t\tif (options?.parentSession) {\n\t\t\tsessionManager.newSession({ parentSession: options.parentSession });\n\t\t}\n\t\tawait this.replace({\n\t\t\tcwd: this.runtime.cwd,\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"new\", previousSessionFile },\n\t\t});\n\t\tif (options?.setup) {\n\t\t\tawait options.setup(this.runtime.sessionManager);\n\t\t\tthis.runtime.session.agent.state.messages = this.runtime.sessionManager.buildSessionContext().messages;\n\t\t}\n\t\treturn { cancelled: false };\n\t}\n\n\t/**\n\t * Replace the active runtime with a fork rooted at the given user-message\n\t * entry.\n\t *\n\t * Returns the selected user text so UIs can restore it into the editor after\n\t * the fork completes.\n\t */\n\tasync fork(entryId: string): Promise<{ cancelled: boolean; selectedText?: string }> {\n\t\tconst beforeResult = await this.emitBeforeFork(entryId);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn { cancelled: true };\n\t\t}\n\n\t\tconst selectedEntry = this.runtime.sessionManager.getEntry(entryId);\n\t\tif (!selectedEntry || selectedEntry.type !== \"message\" || selectedEntry.message.role !== \"user\") {\n\t\t\tthrow new Error(\"Invalid entry ID for forking\");\n\t\t}\n\n\t\tconst previousSessionFile = this.runtime.session.sessionFile;\n\t\tconst selectedText = extractUserMessageText(selectedEntry.message.content);\n\t\tif (this.runtime.sessionManager.isPersisted()) {\n\t\t\tconst currentSessionFile = this.runtime.session.sessionFile!;\n\t\t\tconst sessionDir = this.runtime.sessionManager.getSessionDir();\n\t\t\tif (!selectedEntry.parentId) {\n\t\t\t\tconst sessionManager = SessionManager.create(this.runtime.cwd, sessionDir);\n\t\t\t\tsessionManager.newSession({ parentSession: currentSessionFile });\n\t\t\t\tawait this.replace({\n\t\t\t\t\tcwd: this.runtime.cwd,\n\t\t\t\t\tsessionManager,\n\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t});\n\t\t\t\treturn { cancelled: false, selectedText };\n\t\t\t}\n\n\t\t\tconst sourceManager = SessionManager.open(currentSessionFile, sessionDir);\n\t\t\tconst forkedSessionPath = sourceManager.createBranchedSession(selectedEntry.parentId)!;\n\t\t\tconst sessionManager = SessionManager.open(forkedSessionPath, sessionDir);\n\t\t\tawait this.replace({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t});\n\t\t\treturn { cancelled: false, selectedText };\n\t\t}\n\n\t\tconst sessionManager = this.runtime.sessionManager;\n\t\tif (!selectedEntry.parentId) {\n\t\t\tsessionManager.newSession({ parentSession: this.runtime.session.sessionFile });\n\t\t} else {\n\t\t\tsessionManager.createBranchedSession(selectedEntry.parentId);\n\t\t}\n\t\tawait this.replace({\n\t\t\tcwd: this.runtime.cwd,\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t});\n\t\treturn { cancelled: false, selectedText };\n\t}\n\n\t/**\n\t * Import a JSONL session file into the current session directory and replace\n\t * the active runtime with the imported session.\n\t */\n\tasync importFromJsonl(inputPath: string): Promise<{ cancelled: boolean }> {\n\t\tconst resolvedPath = resolve(inputPath);\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tthrow new Error(`File not found: ${resolvedPath}`);\n\t\t}\n\n\t\tconst sessionDir = this.runtime.sessionManager.getSessionDir();\n\t\tif (!existsSync(sessionDir)) {\n\t\t\tmkdirSync(sessionDir, { recursive: true });\n\t\t}\n\n\t\tconst destinationPath = join(sessionDir, basename(resolvedPath));\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", destinationPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.runtime.session.sessionFile;\n\t\tif (resolve(destinationPath) !== resolvedPath) {\n\t\t\tcopyFileSync(resolvedPath, destinationPath);\n\t\t}\n\n\t\tconst sessionManager = SessionManager.open(destinationPath, sessionDir);\n\t\tawait this.replace({\n\t\t\tcwd: sessionManager.getCwd(),\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t});\n\t\treturn { cancelled: false };\n\t}\n\n\t/** Emit session shutdown for the active runtime and dispose it permanently. */\n\tasync dispose(): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.runtime.session.extensionRunner);\n\t\tthis.runtime.session.dispose();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent-session-runtime.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,6BAA6B,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACvG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,+BAAgC,SAAQ,wBAAwB;IAChF,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,EAAE,6BAA6B,EAAE,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gCAAgC,GAAG,CAAC,OAAO,EAAE;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACtC,KAAK,OAAO,CAAC,+BAA+B,CAAC,CAAC;AAa/C;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAE9B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,qBAAqB,CAAC;IAL/B,YACS,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,oBAAoB,EACtB,aAAa,EAAE,gCAAgC,EACxD,YAAY,GAAE,6BAA6B,EAAO,EAClD,qBAAqB,CAAC,oBAAQ,EACnC;IAEJ,IAAI,QAAQ,IAAI,oBAAoB,CAEnC;IAED,IAAI,OAAO,IAAI,YAAY,CAE1B;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,WAAW,IAAI,SAAS,6BAA6B,EAAE,CAE1D;IAED,IAAI,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAE7C;YAEa,gBAAgB;YAiBhB,cAAc;YAad,eAAe;IAK7B,OAAO,CAAC,KAAK;IAUP,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAkBxE;IAEK,UAAU,CAAC,OAAO,CAAC,EAAE;QAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,KAAK,CAAC,EAAE,CAAC,cAAc,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1D,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CA2BlC;IAEK,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAoElF;IAEK,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAiCxE;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7B;CACD;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC9C,aAAa,EAAE,gCAAgC,EAC/C,OAAO,EAAE;IACR,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACtC,GACC,OAAO,CAAC,mBAAmB,CAAC,CAY9B;AAED,OAAO,EACN,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,EACzB,KAAK,qCAAqC,EAC1C,KAAK,iCAAiC,EACtC,8BAA8B,EAC9B,0BAA0B,GAC1B,MAAM,6BAA6B,CAAC","sourcesContent":["import { copyFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { basename, join, resolve } from \"node:path\";\nimport type { AgentSession } from \"./agent-session.js\";\nimport type { AgentSessionRuntimeDiagnostic, AgentSessionServices } from \"./agent-session-services.js\";\nimport type { SessionStartEvent } from \"./extensions/index.js\";\nimport { emitSessionShutdownEvent } from \"./extensions/runner.js\";\nimport type { CreateAgentSessionResult } from \"./sdk.js\";\nimport { SessionManager } from \"./session-manager.js\";\n\n/**\n * Result returned by runtime creation.\n *\n * The caller gets the created session, its cwd-bound services, and all\n * diagnostics collected during setup.\n */\nexport interface CreateAgentSessionRuntimeResult extends CreateAgentSessionResult {\n\tservices: AgentSessionServices;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\n/**\n * Creates a full runtime for a target cwd and session manager.\n *\n * The factory closes over process-global fixed inputs, recreates cwd-bound\n * services for the effective cwd, resolves session options against those\n * services, and finally creates the AgentSession.\n */\nexport type CreateAgentSessionRuntimeFactory = (options: {\n\tcwd: string;\n\tagentDir: string;\n\tsessionManager: SessionManager;\n\tsessionStartEvent?: SessionStartEvent;\n}) => Promise<CreateAgentSessionRuntimeResult>;\n\nfunction extractUserMessageText(content: string | Array<{ type: string; text?: string }>): string {\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\n\treturn content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\");\n}\n\n/**\n * Owns the current AgentSession plus its cwd-bound services.\n *\n * Session replacement methods tear down the current runtime first, then create\n * and apply the next runtime. If creation fails, the error is propagated to the\n * caller. The caller is responsible for user-facing error handling.\n */\nexport class AgentSessionRuntime {\n\tconstructor(\n\t\tprivate _session: AgentSession,\n\t\tprivate _services: AgentSessionServices,\n\t\tprivate readonly createRuntime: CreateAgentSessionRuntimeFactory,\n\t\tprivate _diagnostics: AgentSessionRuntimeDiagnostic[] = [],\n\t\tprivate _modelFallbackMessage?: string,\n\t) {}\n\n\tget services(): AgentSessionServices {\n\t\treturn this._services;\n\t}\n\n\tget session(): AgentSession {\n\t\treturn this._session;\n\t}\n\n\tget cwd(): string {\n\t\treturn this._services.cwd;\n\t}\n\n\tget diagnostics(): readonly AgentSessionRuntimeDiagnostic[] {\n\t\treturn this._diagnostics;\n\t}\n\n\tget modelFallbackMessage(): string | undefined {\n\t\treturn this._modelFallbackMessage;\n\t}\n\n\tprivate async emitBeforeSwitch(\n\t\treason: \"new\" | \"resume\",\n\t\ttargetSessionFile?: string,\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.session.extensionRunner;\n\t\tif (!runner?.hasHandlers(\"session_before_switch\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_switch\",\n\t\t\treason,\n\t\t\ttargetSessionFile,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async emitBeforeFork(entryId: string): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.session.extensionRunner;\n\t\tif (!runner?.hasHandlers(\"session_before_fork\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_fork\",\n\t\t\tentryId,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async teardownCurrent(): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.session.extensionRunner);\n\t\tthis.session.dispose();\n\t}\n\n\tprivate apply(result: CreateAgentSessionRuntimeResult): void {\n\t\tif (process.cwd() !== result.services.cwd) {\n\t\t\tprocess.chdir(result.services.cwd);\n\t\t}\n\t\tthis._session = result.session;\n\t\tthis._services = result.services;\n\t\tthis._diagnostics = result.diagnostics;\n\t\tthis._modelFallbackMessage = result.modelFallbackMessage;\n\t}\n\n\tasync switchSession(sessionPath: string): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", sessionPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst sessionManager = SessionManager.open(sessionPath);\n\t\tawait this.teardownCurrent();\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\treturn { cancelled: false };\n\t}\n\n\tasync newSession(options?: {\n\t\tparentSession?: string;\n\t\tsetup?: (sessionManager: SessionManager) => Promise<void>;\n\t}): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"new\");\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\tconst sessionManager = SessionManager.create(this.cwd, sessionDir);\n\t\tif (options?.parentSession) {\n\t\t\tsessionManager.newSession({ parentSession: options.parentSession });\n\t\t}\n\n\t\tawait this.teardownCurrent();\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"new\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tif (options?.setup) {\n\t\t\tawait options.setup(this.session.sessionManager);\n\t\t\tthis.session.agent.state.messages = this.session.sessionManager.buildSessionContext().messages;\n\t\t}\n\t\treturn { cancelled: false };\n\t}\n\n\tasync fork(entryId: string): Promise<{ cancelled: boolean; selectedText?: string }> {\n\t\tconst beforeResult = await this.emitBeforeFork(entryId);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn { cancelled: true };\n\t\t}\n\n\t\tconst selectedEntry = this.session.sessionManager.getEntry(entryId);\n\t\tif (!selectedEntry || selectedEntry.type !== \"message\" || selectedEntry.message.role !== \"user\") {\n\t\t\tthrow new Error(\"Invalid entry ID for forking\");\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst selectedText = extractUserMessageText(selectedEntry.message.content);\n\t\tif (this.session.sessionManager.isPersisted()) {\n\t\t\tconst currentSessionFile = this.session.sessionFile;\n\t\t\tif (!currentSessionFile) {\n\t\t\t\tthrow new Error(\"Persisted session is missing a session file\");\n\t\t\t}\n\t\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\t\tif (!selectedEntry.parentId) {\n\t\t\t\tconst sessionManager = SessionManager.create(this.cwd, sessionDir);\n\t\t\t\tsessionManager.newSession({ parentSession: currentSessionFile });\n\t\t\t\tawait this.teardownCurrent();\n\t\t\t\tthis.apply(\n\t\t\t\t\tawait this.createRuntime({\n\t\t\t\t\t\tcwd: this.cwd,\n\t\t\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\t\t\tsessionManager,\n\t\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn { cancelled: false, selectedText };\n\t\t\t}\n\n\t\t\tconst sourceManager = SessionManager.open(currentSessionFile, sessionDir);\n\t\t\tconst forkedSessionPath = sourceManager.createBranchedSession(selectedEntry.parentId);\n\t\t\tif (!forkedSessionPath) {\n\t\t\t\tthrow new Error(\"Failed to create forked session\");\n\t\t\t}\n\t\t\tconst sessionManager = SessionManager.open(forkedSessionPath, sessionDir);\n\t\t\tawait this.teardownCurrent();\n\t\t\tthis.apply(\n\t\t\t\tawait this.createRuntime({\n\t\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\t\tsessionManager,\n\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn { cancelled: false, selectedText };\n\t\t}\n\n\t\tconst sessionManager = this.session.sessionManager;\n\t\tif (!selectedEntry.parentId) {\n\t\t\tsessionManager.newSession({ parentSession: this.session.sessionFile });\n\t\t} else {\n\t\t\tsessionManager.createBranchedSession(selectedEntry.parentId);\n\t\t}\n\t\tawait this.teardownCurrent();\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\treturn { cancelled: false, selectedText };\n\t}\n\n\tasync importFromJsonl(inputPath: string): Promise<{ cancelled: boolean }> {\n\t\tconst resolvedPath = resolve(inputPath);\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tthrow new Error(`File not found: ${resolvedPath}`);\n\t\t}\n\n\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\tif (!existsSync(sessionDir)) {\n\t\t\tmkdirSync(sessionDir, { recursive: true });\n\t\t}\n\n\t\tconst destinationPath = join(sessionDir, basename(resolvedPath));\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", destinationPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tif (resolve(destinationPath) !== resolvedPath) {\n\t\t\tcopyFileSync(resolvedPath, destinationPath);\n\t\t}\n\n\t\tconst sessionManager = SessionManager.open(destinationPath, sessionDir);\n\t\tawait this.teardownCurrent();\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\treturn { cancelled: false };\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.session.extensionRunner);\n\t\tthis.session.dispose();\n\t}\n}\n\n/**\n * Create the initial runtime from a runtime factory and initial session target.\n *\n * The same factory is stored on the returned AgentSessionRuntime and reused for\n * later /new, /resume, /fork, and import flows.\n */\nexport async function createAgentSessionRuntime(\n\tcreateRuntime: CreateAgentSessionRuntimeFactory,\n\toptions: {\n\t\tcwd: string;\n\t\tagentDir: string;\n\t\tsessionManager: SessionManager;\n\t\tsessionStartEvent?: SessionStartEvent;\n\t},\n): Promise<AgentSessionRuntime> {\n\tconst result = await createRuntime(options);\n\tif (process.cwd() !== result.services.cwd) {\n\t\tprocess.chdir(result.services.cwd);\n\t}\n\treturn new AgentSessionRuntime(\n\t\tresult.session,\n\t\tresult.services,\n\t\tcreateRuntime,\n\t\tresult.diagnostics,\n\t\tresult.modelFallbackMessage,\n\t);\n}\n\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.js\";\n"]}
|
|
@@ -1,71 +1,7 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
-
import { getAgentDir } from "../config.js";
|
|
4
|
-
import { AuthStorage } from "./auth-storage.js";
|
|
5
3
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
6
|
-
import { ModelRegistry } from "./model-registry.js";
|
|
7
|
-
import { DefaultResourceLoader } from "./resource-loader.js";
|
|
8
|
-
import { createAgentSession } from "./sdk.js";
|
|
9
4
|
import { SessionManager } from "./session-manager.js";
|
|
10
|
-
import { SettingsManager } from "./settings-manager.js";
|
|
11
|
-
/**
|
|
12
|
-
* Create one runtime instance containing an AgentSession plus the cwd-bound
|
|
13
|
-
* services it depends on.
|
|
14
|
-
*
|
|
15
|
-
* Most SDK callers should keep the returned value wrapped in an
|
|
16
|
-
* AgentSessionRuntimeHost instead of holding it directly. The host owns
|
|
17
|
-
* replacing the runtime when switching sessions across files or working
|
|
18
|
-
* directories.
|
|
19
|
-
*/
|
|
20
|
-
export async function createAgentSessionRuntime(bootstrap, options) {
|
|
21
|
-
const cwd = options.cwd;
|
|
22
|
-
const agentDir = bootstrap.agentDir ?? getAgentDir();
|
|
23
|
-
const authStorage = bootstrap.authStorage ?? AuthStorage.create(join(agentDir, "auth.json"));
|
|
24
|
-
const settingsManager = SettingsManager.create(cwd, agentDir);
|
|
25
|
-
const modelRegistry = ModelRegistry.create(authStorage, join(agentDir, "models.json"));
|
|
26
|
-
const resourceLoader = options.resourceLoader ??
|
|
27
|
-
(typeof bootstrap.resourceLoader === "function"
|
|
28
|
-
? await bootstrap.resourceLoader(cwd, agentDir)
|
|
29
|
-
: new DefaultResourceLoader({
|
|
30
|
-
...(bootstrap.resourceLoader ?? {}),
|
|
31
|
-
cwd,
|
|
32
|
-
agentDir,
|
|
33
|
-
settingsManager,
|
|
34
|
-
}));
|
|
35
|
-
if (!options.resourceLoader) {
|
|
36
|
-
await resourceLoader.reload();
|
|
37
|
-
}
|
|
38
|
-
const extensionsResult = resourceLoader.getExtensions();
|
|
39
|
-
for (const { name, config } of extensionsResult.runtime.pendingProviderRegistrations) {
|
|
40
|
-
modelRegistry.registerProvider(name, config);
|
|
41
|
-
}
|
|
42
|
-
extensionsResult.runtime.pendingProviderRegistrations = [];
|
|
43
|
-
const created = await createAgentSession({
|
|
44
|
-
cwd,
|
|
45
|
-
agentDir,
|
|
46
|
-
authStorage,
|
|
47
|
-
modelRegistry,
|
|
48
|
-
settingsManager,
|
|
49
|
-
resourceLoader,
|
|
50
|
-
sessionManager: options.sessionManager,
|
|
51
|
-
model: bootstrap.model,
|
|
52
|
-
thinkingLevel: bootstrap.thinkingLevel,
|
|
53
|
-
scopedModels: bootstrap.scopedModels,
|
|
54
|
-
tools: bootstrap.tools,
|
|
55
|
-
customTools: bootstrap.customTools,
|
|
56
|
-
sessionStartEvent: options.sessionStartEvent,
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
...created,
|
|
60
|
-
cwd,
|
|
61
|
-
agentDir,
|
|
62
|
-
authStorage,
|
|
63
|
-
modelRegistry,
|
|
64
|
-
settingsManager,
|
|
65
|
-
resourceLoader,
|
|
66
|
-
sessionManager: created.session.sessionManager,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
5
|
function extractUserMessageText(content) {
|
|
70
6
|
if (typeof content === "string") {
|
|
71
7
|
return content;
|
|
@@ -76,23 +12,37 @@ function extractUserMessageText(content) {
|
|
|
76
12
|
.join("");
|
|
77
13
|
}
|
|
78
14
|
/**
|
|
79
|
-
*
|
|
15
|
+
* Owns the current AgentSession plus its cwd-bound services.
|
|
80
16
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
17
|
+
* Session replacement methods tear down the current runtime first, then create
|
|
18
|
+
* and apply the next runtime. If creation fails, the error is propagated to the
|
|
19
|
+
* caller. The caller is responsible for user-facing error handling.
|
|
84
20
|
*/
|
|
85
|
-
export class
|
|
86
|
-
constructor(
|
|
87
|
-
this.
|
|
88
|
-
this.
|
|
21
|
+
export class AgentSessionRuntime {
|
|
22
|
+
constructor(_session, _services, createRuntime, _diagnostics = [], _modelFallbackMessage) {
|
|
23
|
+
this._session = _session;
|
|
24
|
+
this._services = _services;
|
|
25
|
+
this.createRuntime = createRuntime;
|
|
26
|
+
this._diagnostics = _diagnostics;
|
|
27
|
+
this._modelFallbackMessage = _modelFallbackMessage;
|
|
28
|
+
}
|
|
29
|
+
get services() {
|
|
30
|
+
return this._services;
|
|
89
31
|
}
|
|
90
|
-
/** The currently active session instance. Re-read this after runtime replacement. */
|
|
91
32
|
get session() {
|
|
92
|
-
return this.
|
|
33
|
+
return this._session;
|
|
34
|
+
}
|
|
35
|
+
get cwd() {
|
|
36
|
+
return this._services.cwd;
|
|
37
|
+
}
|
|
38
|
+
get diagnostics() {
|
|
39
|
+
return this._diagnostics;
|
|
40
|
+
}
|
|
41
|
+
get modelFallbackMessage() {
|
|
42
|
+
return this._modelFallbackMessage;
|
|
93
43
|
}
|
|
94
44
|
async emitBeforeSwitch(reason, targetSessionFile) {
|
|
95
|
-
const runner = this.
|
|
45
|
+
const runner = this.session.extensionRunner;
|
|
96
46
|
if (!runner?.hasHandlers("session_before_switch")) {
|
|
97
47
|
return { cancelled: false };
|
|
98
48
|
}
|
|
@@ -104,7 +54,7 @@ export class AgentSessionRuntimeHost {
|
|
|
104
54
|
return { cancelled: result?.cancel === true };
|
|
105
55
|
}
|
|
106
56
|
async emitBeforeFork(entryId) {
|
|
107
|
-
const runner = this.
|
|
57
|
+
const runner = this.session.extensionRunner;
|
|
108
58
|
if (!runner?.hasHandlers("session_before_fork")) {
|
|
109
59
|
return { cancelled: false };
|
|
110
60
|
}
|
|
@@ -114,128 +64,125 @@ export class AgentSessionRuntimeHost {
|
|
|
114
64
|
});
|
|
115
65
|
return { cancelled: result?.cancel === true };
|
|
116
66
|
}
|
|
117
|
-
async
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
67
|
+
async teardownCurrent() {
|
|
68
|
+
await emitSessionShutdownEvent(this.session.extensionRunner);
|
|
69
|
+
this.session.dispose();
|
|
70
|
+
}
|
|
71
|
+
apply(result) {
|
|
72
|
+
if (process.cwd() !== result.services.cwd) {
|
|
73
|
+
process.chdir(result.services.cwd);
|
|
74
|
+
}
|
|
75
|
+
this._session = result.session;
|
|
76
|
+
this._services = result.services;
|
|
77
|
+
this._diagnostics = result.diagnostics;
|
|
78
|
+
this._modelFallbackMessage = result.modelFallbackMessage;
|
|
125
79
|
}
|
|
126
|
-
/**
|
|
127
|
-
* Replace the active runtime with one opened from an existing session file.
|
|
128
|
-
*
|
|
129
|
-
* Emits `session_before_switch` before replacement and returns
|
|
130
|
-
* `{ cancelled: true }` if an extension vetoes the switch.
|
|
131
|
-
*/
|
|
132
80
|
async switchSession(sessionPath) {
|
|
133
81
|
const beforeResult = await this.emitBeforeSwitch("resume", sessionPath);
|
|
134
82
|
if (beforeResult.cancelled) {
|
|
135
83
|
return beforeResult;
|
|
136
84
|
}
|
|
137
|
-
const previousSessionFile = this.
|
|
85
|
+
const previousSessionFile = this.session.sessionFile;
|
|
138
86
|
const sessionManager = SessionManager.open(sessionPath);
|
|
139
|
-
await this.
|
|
87
|
+
await this.teardownCurrent();
|
|
88
|
+
this.apply(await this.createRuntime({
|
|
140
89
|
cwd: sessionManager.getCwd(),
|
|
90
|
+
agentDir: this.services.agentDir,
|
|
141
91
|
sessionManager,
|
|
142
92
|
sessionStartEvent: { type: "session_start", reason: "resume", previousSessionFile },
|
|
143
|
-
});
|
|
93
|
+
}));
|
|
144
94
|
return { cancelled: false };
|
|
145
95
|
}
|
|
146
|
-
/**
|
|
147
|
-
* Replace the active runtime with a fresh session in the current cwd.
|
|
148
|
-
*
|
|
149
|
-
* `setup` runs after replacement against the new session manager, which lets
|
|
150
|
-
* callers seed entries before normal use begins.
|
|
151
|
-
*/
|
|
152
96
|
async newSession(options) {
|
|
153
97
|
const beforeResult = await this.emitBeforeSwitch("new");
|
|
154
98
|
if (beforeResult.cancelled) {
|
|
155
99
|
return beforeResult;
|
|
156
100
|
}
|
|
157
|
-
const previousSessionFile = this.
|
|
158
|
-
const sessionDir = this.
|
|
159
|
-
const sessionManager = SessionManager.create(this.
|
|
101
|
+
const previousSessionFile = this.session.sessionFile;
|
|
102
|
+
const sessionDir = this.session.sessionManager.getSessionDir();
|
|
103
|
+
const sessionManager = SessionManager.create(this.cwd, sessionDir);
|
|
160
104
|
if (options?.parentSession) {
|
|
161
105
|
sessionManager.newSession({ parentSession: options.parentSession });
|
|
162
106
|
}
|
|
163
|
-
await this.
|
|
164
|
-
|
|
107
|
+
await this.teardownCurrent();
|
|
108
|
+
this.apply(await this.createRuntime({
|
|
109
|
+
cwd: this.cwd,
|
|
110
|
+
agentDir: this.services.agentDir,
|
|
165
111
|
sessionManager,
|
|
166
112
|
sessionStartEvent: { type: "session_start", reason: "new", previousSessionFile },
|
|
167
|
-
});
|
|
113
|
+
}));
|
|
168
114
|
if (options?.setup) {
|
|
169
|
-
await options.setup(this.
|
|
170
|
-
this.
|
|
115
|
+
await options.setup(this.session.sessionManager);
|
|
116
|
+
this.session.agent.state.messages = this.session.sessionManager.buildSessionContext().messages;
|
|
171
117
|
}
|
|
172
118
|
return { cancelled: false };
|
|
173
119
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Replace the active runtime with a fork rooted at the given user-message
|
|
176
|
-
* entry.
|
|
177
|
-
*
|
|
178
|
-
* Returns the selected user text so UIs can restore it into the editor after
|
|
179
|
-
* the fork completes.
|
|
180
|
-
*/
|
|
181
120
|
async fork(entryId) {
|
|
182
121
|
const beforeResult = await this.emitBeforeFork(entryId);
|
|
183
122
|
if (beforeResult.cancelled) {
|
|
184
123
|
return { cancelled: true };
|
|
185
124
|
}
|
|
186
|
-
const selectedEntry = this.
|
|
125
|
+
const selectedEntry = this.session.sessionManager.getEntry(entryId);
|
|
187
126
|
if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
|
|
188
127
|
throw new Error("Invalid entry ID for forking");
|
|
189
128
|
}
|
|
190
|
-
const previousSessionFile = this.
|
|
129
|
+
const previousSessionFile = this.session.sessionFile;
|
|
191
130
|
const selectedText = extractUserMessageText(selectedEntry.message.content);
|
|
192
|
-
if (this.
|
|
193
|
-
const currentSessionFile = this.
|
|
194
|
-
|
|
131
|
+
if (this.session.sessionManager.isPersisted()) {
|
|
132
|
+
const currentSessionFile = this.session.sessionFile;
|
|
133
|
+
if (!currentSessionFile) {
|
|
134
|
+
throw new Error("Persisted session is missing a session file");
|
|
135
|
+
}
|
|
136
|
+
const sessionDir = this.session.sessionManager.getSessionDir();
|
|
195
137
|
if (!selectedEntry.parentId) {
|
|
196
|
-
const sessionManager = SessionManager.create(this.
|
|
138
|
+
const sessionManager = SessionManager.create(this.cwd, sessionDir);
|
|
197
139
|
sessionManager.newSession({ parentSession: currentSessionFile });
|
|
198
|
-
await this.
|
|
199
|
-
|
|
140
|
+
await this.teardownCurrent();
|
|
141
|
+
this.apply(await this.createRuntime({
|
|
142
|
+
cwd: this.cwd,
|
|
143
|
+
agentDir: this.services.agentDir,
|
|
200
144
|
sessionManager,
|
|
201
145
|
sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
|
|
202
|
-
});
|
|
146
|
+
}));
|
|
203
147
|
return { cancelled: false, selectedText };
|
|
204
148
|
}
|
|
205
149
|
const sourceManager = SessionManager.open(currentSessionFile, sessionDir);
|
|
206
150
|
const forkedSessionPath = sourceManager.createBranchedSession(selectedEntry.parentId);
|
|
151
|
+
if (!forkedSessionPath) {
|
|
152
|
+
throw new Error("Failed to create forked session");
|
|
153
|
+
}
|
|
207
154
|
const sessionManager = SessionManager.open(forkedSessionPath, sessionDir);
|
|
208
|
-
await this.
|
|
155
|
+
await this.teardownCurrent();
|
|
156
|
+
this.apply(await this.createRuntime({
|
|
209
157
|
cwd: sessionManager.getCwd(),
|
|
158
|
+
agentDir: this.services.agentDir,
|
|
210
159
|
sessionManager,
|
|
211
160
|
sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
|
|
212
|
-
});
|
|
161
|
+
}));
|
|
213
162
|
return { cancelled: false, selectedText };
|
|
214
163
|
}
|
|
215
|
-
const sessionManager = this.
|
|
164
|
+
const sessionManager = this.session.sessionManager;
|
|
216
165
|
if (!selectedEntry.parentId) {
|
|
217
|
-
sessionManager.newSession({ parentSession: this.
|
|
166
|
+
sessionManager.newSession({ parentSession: this.session.sessionFile });
|
|
218
167
|
}
|
|
219
168
|
else {
|
|
220
169
|
sessionManager.createBranchedSession(selectedEntry.parentId);
|
|
221
170
|
}
|
|
222
|
-
await this.
|
|
223
|
-
|
|
171
|
+
await this.teardownCurrent();
|
|
172
|
+
this.apply(await this.createRuntime({
|
|
173
|
+
cwd: this.cwd,
|
|
174
|
+
agentDir: this.services.agentDir,
|
|
224
175
|
sessionManager,
|
|
225
176
|
sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
|
|
226
|
-
});
|
|
177
|
+
}));
|
|
227
178
|
return { cancelled: false, selectedText };
|
|
228
179
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Import a JSONL session file into the current session directory and replace
|
|
231
|
-
* the active runtime with the imported session.
|
|
232
|
-
*/
|
|
233
180
|
async importFromJsonl(inputPath) {
|
|
234
181
|
const resolvedPath = resolve(inputPath);
|
|
235
182
|
if (!existsSync(resolvedPath)) {
|
|
236
183
|
throw new Error(`File not found: ${resolvedPath}`);
|
|
237
184
|
}
|
|
238
|
-
const sessionDir = this.
|
|
185
|
+
const sessionDir = this.session.sessionManager.getSessionDir();
|
|
239
186
|
if (!existsSync(sessionDir)) {
|
|
240
187
|
mkdirSync(sessionDir, { recursive: true });
|
|
241
188
|
}
|
|
@@ -244,22 +191,37 @@ export class AgentSessionRuntimeHost {
|
|
|
244
191
|
if (beforeResult.cancelled) {
|
|
245
192
|
return beforeResult;
|
|
246
193
|
}
|
|
247
|
-
const previousSessionFile = this.
|
|
194
|
+
const previousSessionFile = this.session.sessionFile;
|
|
248
195
|
if (resolve(destinationPath) !== resolvedPath) {
|
|
249
196
|
copyFileSync(resolvedPath, destinationPath);
|
|
250
197
|
}
|
|
251
198
|
const sessionManager = SessionManager.open(destinationPath, sessionDir);
|
|
252
|
-
await this.
|
|
199
|
+
await this.teardownCurrent();
|
|
200
|
+
this.apply(await this.createRuntime({
|
|
253
201
|
cwd: sessionManager.getCwd(),
|
|
202
|
+
agentDir: this.services.agentDir,
|
|
254
203
|
sessionManager,
|
|
255
204
|
sessionStartEvent: { type: "session_start", reason: "resume", previousSessionFile },
|
|
256
|
-
});
|
|
205
|
+
}));
|
|
257
206
|
return { cancelled: false };
|
|
258
207
|
}
|
|
259
|
-
/** Emit session shutdown for the active runtime and dispose it permanently. */
|
|
260
208
|
async dispose() {
|
|
261
|
-
await emitSessionShutdownEvent(this.
|
|
262
|
-
this.
|
|
209
|
+
await emitSessionShutdownEvent(this.session.extensionRunner);
|
|
210
|
+
this.session.dispose();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Create the initial runtime from a runtime factory and initial session target.
|
|
215
|
+
*
|
|
216
|
+
* The same factory is stored on the returned AgentSessionRuntime and reused for
|
|
217
|
+
* later /new, /resume, /fork, and import flows.
|
|
218
|
+
*/
|
|
219
|
+
export async function createAgentSessionRuntime(createRuntime, options) {
|
|
220
|
+
const result = await createRuntime(options);
|
|
221
|
+
if (process.cwd() !== result.services.cwd) {
|
|
222
|
+
process.chdir(result.services.cwd);
|
|
263
223
|
}
|
|
224
|
+
return new AgentSessionRuntime(result.session, result.services, createRuntime, result.diagnostics, result.modelFallbackMessage);
|
|
264
225
|
}
|
|
226
|
+
export { createAgentSessionFromServices, createAgentSessionServices, } from "./agent-session-services.js";
|
|
265
227
|
//# sourceMappingURL=agent-session-runtime.js.map
|