@draht/coding-agent 2026.3.25 → 2026.4.5
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 +107 -0
- package/README.md +6 -2
- package/dist/core/agent-session-runtime.d.ts +136 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +267 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session.d.ts +22 -44
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +44 -248
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +3 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +5 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +9 -9
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts +2 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +2 -2
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +16 -16
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +10 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +5 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +70 -8
- package/dist/core/footer-data-provider.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/model-registry.d.ts +21 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +90 -70
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +4 -4
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts +6 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +37 -5
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +5 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +6 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +17 -23
- package/dist/core/sdk.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +49 -10
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +8 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +90 -87
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +6 -11
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +4 -4
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +87 -74
- package/dist/modes/print-mode.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 +69 -49
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/development.md +2 -2
- package/docs/extensions.md +78 -22
- package/docs/models.md +6 -0
- package/docs/packages.md +3 -3
- package/docs/rpc.md +2 -2
- package/docs/sdk.md +170 -82
- package/docs/tree.md +1 -1
- package/examples/extensions/custom-compaction.ts +17 -4
- package/examples/extensions/handoff.ts +5 -2
- package/examples/extensions/hello.ts +18 -17
- package/examples/extensions/qna.ts +5 -2
- package/examples/extensions/rpc-demo.ts +3 -9
- package/examples/extensions/status-line.ts +0 -8
- package/examples/extensions/subagent/index.ts +1 -1
- package/examples/extensions/summarize.ts +15 -4
- package/examples/extensions/todo.ts +0 -2
- package/examples/extensions/tools.ts +0 -5
- package/examples/extensions/widget-placement.ts +4 -12
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +3 -3
- package/examples/sdk/12-full-control.ts +1 -1
- package/examples/sdk/13-session-runtime.ts +49 -0
- package/examples/sdk/README.md +5 -4
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,112 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2026.4.5] - 2026-04-05
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- add startup profiling scripts for tui/rpc (#2497)
|
|
8
|
+
- add resizable share sidebar (#2435)
|
|
9
|
+
- add JSONL export/import for sessions (#2356)
|
|
10
|
+
- add --fork session flag closes #2290
|
|
11
|
+
- expose local bash operations closes #2299
|
|
12
|
+
- allow supplying custom session ID in newSession() (#2130)
|
|
13
|
+
- refine session_directory hook closes #1729
|
|
14
|
+
- add session_directory extension event
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- reproduce missing subagent builtin extension registration
|
|
19
|
+
- fix(coding-agent): reuse initial resource loader on startup closes #2766
|
|
20
|
+
- feat(coding-agent): add defineTool helper closes #2746
|
|
21
|
+
- test(coding-agent): add session lifecycle characterization suite
|
|
22
|
+
- test(coding-agent): add queue characterization coverage
|
|
23
|
+
- fix(coding-agent): resolve theme export variables closes #2707
|
|
24
|
+
- refactor(coding-agent): add runtime host for session switching closes #2024
|
|
25
|
+
- fix(coding-agent): resolve models.json auth per request closes #1835
|
|
26
|
+
- feat(ai,coding-agent): add faux provider and ModelRegistry factories
|
|
27
|
+
- docs: add switchSession to ExtensionCommandContext in extensions.md
|
|
28
|
+
- fix(tui): await async slash command completions closes #2719
|
|
29
|
+
- expand branding sweep rules in rebase-upstream prompt
|
|
30
|
+
- rename pi to draht in process title and system prompt
|
|
31
|
+
- add AgentSession test harness with faux provider
|
|
32
|
+
- extend OSS weekend to 2026-03-30
|
|
33
|
+
- document xfce4-terminal and terminator keyboard protocol limitations (#2166)
|
|
34
|
+
- clarify compat flags for openai-compatible local servers closes #2177
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- register subagent builtin extension in DefaultResourceLoader
|
|
39
|
+
- resolve type errors and branding from upstream sync
|
|
40
|
+
- branding sweep for upstream cherry-picks
|
|
41
|
+
- emit session_shutdown in print mode closes #2576
|
|
42
|
+
- replace π with D in terminal title
|
|
43
|
+
- update changelog URL from pi-mono to draht
|
|
44
|
+
- resolve upstream sync conflicts and fix branding
|
|
45
|
+
- respect export theme backgrounds closes #2565
|
|
46
|
+
- recompute interactive bash preview width closes #2569
|
|
47
|
+
- reduce git update fetch noise closes #2548
|
|
48
|
+
- expose rpc context usage closes #2550
|
|
49
|
+
- disambiguate duplicate slash commands, fixes #1061
|
|
50
|
+
- unify source provenance, closes #1734
|
|
51
|
+
- attach source info to resources and commands, fixes #1734
|
|
52
|
+
- enforce safe auto-resized image limits closes #2055
|
|
53
|
+
- reserve stdout in print and json mode closes #2482
|
|
54
|
+
- skip no-op git package reinstalls
|
|
55
|
+
- built-in tools work like extension tools
|
|
56
|
+
- disable cli-highlight auto-detection for languageless code blocks
|
|
57
|
+
- update project npm packages
|
|
58
|
+
- reuse current pi invocation for child agents closes #2464 (#2465)
|
|
59
|
+
- export ToolCallEventResult closes #2458
|
|
60
|
+
- respect agentDir for sdk session paths closes #2457
|
|
61
|
+
- keep suspend resume alive on fg closes #2454
|
|
62
|
+
- align minimax and zai defaults (#2445)
|
|
63
|
+
- migrate to namespaced ids closes #2391
|
|
64
|
+
- queue file mutations across edit and write
|
|
65
|
+
- protect rpc stdout fixes #2388
|
|
66
|
+
- resolve waitForRetry() race when auto-retry produces tool calls (#2440)
|
|
67
|
+
- /model shows stale scoped models (#2408)
|
|
68
|
+
- suppress process warnings in CLI closes #2404
|
|
69
|
+
- show bash tool elapsed time at bottom closes #2406
|
|
70
|
+
- reload custom themes from disk watcher\n\ncloses #2417\ncloses #2003
|
|
71
|
+
- avoid blocking footer branch refresh and isolate invalid extension provider registrations closes #2418 closes #2431
|
|
72
|
+
- fix windows hanging when descendants inherit stdout/stderr handles (#2389)
|
|
73
|
+
- use native clipboard text copy on desktop closes #2347
|
|
74
|
+
- make tests keybinding-agnostic
|
|
75
|
+
- use `setBedrockProviderModule` in Bun entrypoint closes #2349 (#2350)
|
|
76
|
+
- stop updating packages on startup closes #1963
|
|
77
|
+
- refresh active model after provider updates closes #2291
|
|
78
|
+
- reload keybindings on /reload closes #2309
|
|
79
|
+
- restore Bun binary lazy provider loading closes #2314
|
|
80
|
+
- handle z.ai network_error closes #2313
|
|
81
|
+
- merge piped stdin into initial prompt closes #2315
|
|
82
|
+
- skip tmux extended-keys warning when tmux is unreachable (#2311)
|
|
83
|
+
- handle empty session titles and session info metadata (#2304)
|
|
84
|
+
- handle reftable footer branch detection (#2300)
|
|
85
|
+
- emit OSC 133 command-executed marker closes #2242
|
|
86
|
+
- make prompt snippets opt in closes #2285
|
|
87
|
+
- retry provider returned error closes #2264
|
|
88
|
+
- normalize fuzzy edit matching and run touched tests closes #2044
|
|
89
|
+
- handle slash-delimited /model refs closes #2174
|
|
90
|
+
- handle WSL clipboard image fallback\n\nCloses #1722
|
|
91
|
+
- start UI before session_start closes #2035
|
|
92
|
+
- add uninstall alias
|
|
93
|
+
- stabilize windows shell/path handling
|
|
94
|
+
- silence headless clipboard fallback errors closes #2056
|
|
95
|
+
- honor provider compat in models.json closes #2062
|
|
96
|
+
- normalize prompt cwd for bash-safe windows paths closes #2080
|
|
97
|
+
- support configurable npm wrapper command closes #2072
|
|
98
|
+
- stop skill recursion at root closes #2075
|
|
99
|
+
- expand paste markers in ctx.ui.getEditorText(), closes #2084
|
|
100
|
+
- fix startup crash when downloading fd/ripgrep on first run
|
|
101
|
+
- sync tool hooks with agent event processing closes #2113
|
|
102
|
+
- keep only ISO date in system prompt closes #2131
|
|
103
|
+
- match server_error and internal_error in retry regex (#2117)
|
|
104
|
+
- Fix EXIF orientation for JPEG and WebP images (#2105)
|
|
105
|
+
- support PI_CODING_AGENT_DIR env var in example extensions (#2009)
|
|
106
|
+
- use strict JSONL framing fixes #1911
|
|
107
|
+
- allow threshold compaction for error messages using last successful usage, fixes #1834
|
|
108
|
+
- retry sync lockfile acquisition to prevent false auth errors during parallel startup
|
|
109
|
+
|
|
3
110
|
## [2026.3.14] - 2026-03-14
|
|
4
111
|
|
|
5
112
|
### Added
|
package/README.md
CHANGED
|
@@ -373,15 +373,19 @@ See [docs/packages.md](docs/packages.md).
|
|
|
373
373
|
```typescript
|
|
374
374
|
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@draht/coding-agent";
|
|
375
375
|
|
|
376
|
+
const authStorage = AuthStorage.create();
|
|
377
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
376
378
|
const { session } = await createAgentSession({
|
|
377
379
|
sessionManager: SessionManager.inMemory(),
|
|
378
|
-
authStorage
|
|
379
|
-
modelRegistry
|
|
380
|
+
authStorage,
|
|
381
|
+
modelRegistry,
|
|
380
382
|
});
|
|
381
383
|
|
|
382
384
|
await session.prompt("What files are in the current directory?");
|
|
383
385
|
```
|
|
384
386
|
|
|
387
|
+
For advanced multi-session runtime replacement, use `createAgentSessionRuntime()` and `AgentSessionRuntimeHost`.
|
|
388
|
+
|
|
385
389
|
See [docs/sdk.md](docs/sdk.md) and [examples/sdk/](examples/sdk/).
|
|
386
390
|
|
|
387
391
|
### RPC Mode
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "@draht/agent-core";
|
|
2
|
+
import type { Model } from "@draht/ai";
|
|
3
|
+
import { AuthStorage } from "./auth-storage.js";
|
|
4
|
+
import type { SessionStartEvent, ToolDefinition } from "./extensions/index.js";
|
|
5
|
+
import { ModelRegistry } from "./model-registry.js";
|
|
6
|
+
import { type DefaultResourceLoaderOptions, type ResourceLoader } from "./resource-loader.js";
|
|
7
|
+
import { type CreateAgentSessionResult } from "./sdk.js";
|
|
8
|
+
import { SessionManager } from "./session-manager.js";
|
|
9
|
+
import { SettingsManager } from "./settings-manager.js";
|
|
10
|
+
import type { Tool } from "./tools/index.js";
|
|
11
|
+
/**
|
|
12
|
+
* Stable bootstrap inputs reused whenever the active runtime is replaced.
|
|
13
|
+
*
|
|
14
|
+
* Use this for process-level wiring that should survive `/new`, `/resume`,
|
|
15
|
+
* `/fork`, and import flows. Session-local state belongs in the session file
|
|
16
|
+
* or in settings, not here.
|
|
17
|
+
*/
|
|
18
|
+
export interface AgentSessionRuntimeBootstrap {
|
|
19
|
+
/** Agent directory used for auth, models, settings, sessions, and resource discovery. */
|
|
20
|
+
agentDir?: string;
|
|
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">;
|
|
44
|
+
}
|
|
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
|
+
/**
|
|
66
|
+
* Create one runtime instance containing an AgentSession plus the cwd-bound
|
|
67
|
+
* services it depends on.
|
|
68
|
+
*
|
|
69
|
+
* Most SDK callers should keep the returned value wrapped in an
|
|
70
|
+
* AgentSessionRuntimeHost instead of holding it directly. The host owns
|
|
71
|
+
* replacing the runtime when switching sessions across files or working
|
|
72
|
+
* directories.
|
|
73
|
+
*/
|
|
74
|
+
export declare function createAgentSessionRuntime(bootstrap: AgentSessionRuntimeBootstrap, options: CreateAgentSessionRuntimeOptions): Promise<AgentSessionRuntime>;
|
|
75
|
+
/**
|
|
76
|
+
* Stable wrapper around a replaceable AgentSession runtime.
|
|
77
|
+
*
|
|
78
|
+
* Use this when your application needs `/new`, `/resume`, `/fork`, or import
|
|
79
|
+
* behavior. After replacement, read `session` again and rebind any
|
|
80
|
+
* session-local subscriptions or extension bindings.
|
|
81
|
+
*/
|
|
82
|
+
export declare class AgentSessionRuntimeHost {
|
|
83
|
+
private readonly bootstrap;
|
|
84
|
+
private runtime;
|
|
85
|
+
constructor(bootstrap: AgentSessionRuntimeBootstrap, runtime: AgentSessionRuntime);
|
|
86
|
+
/** The currently active session instance. Re-read this after runtime replacement. */
|
|
87
|
+
get session(): import("./agent-session.js").AgentSession;
|
|
88
|
+
private emitBeforeSwitch;
|
|
89
|
+
private emitBeforeFork;
|
|
90
|
+
private replace;
|
|
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
|
+
*/
|
|
97
|
+
switchSession(sessionPath: string): Promise<{
|
|
98
|
+
cancelled: boolean;
|
|
99
|
+
}>;
|
|
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
|
+
newSession(options?: {
|
|
107
|
+
/** Optional parent session path recorded in the new session header. */
|
|
108
|
+
parentSession?: string;
|
|
109
|
+
/** Optional callback for seeding the new session manager after replacement. */
|
|
110
|
+
setup?: (sessionManager: SessionManager) => Promise<void>;
|
|
111
|
+
}): Promise<{
|
|
112
|
+
cancelled: boolean;
|
|
113
|
+
}>;
|
|
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
|
+
fork(entryId: string): Promise<{
|
|
122
|
+
cancelled: boolean;
|
|
123
|
+
selectedText?: string;
|
|
124
|
+
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Import a JSONL session file into the current session directory and replace
|
|
127
|
+
* the active runtime with the imported session.
|
|
128
|
+
*/
|
|
129
|
+
importFromJsonl(inputPath: string): Promise<{
|
|
130
|
+
cancelled: boolean;
|
|
131
|
+
}>;
|
|
132
|
+
/** Emit session shutdown for the active runtime and dispose it permanently. */
|
|
133
|
+
dispose(): Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
export {};
|
|
136
|
+
//# sourceMappingURL=agent-session-runtime.d.ts.map
|
|
@@ -0,0 +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 \"@draht/agent-core\";\nimport type { Model } from \"@draht/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"]}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { getAgentDir } from "../config.js";
|
|
4
|
+
import { AuthStorage } from "./auth-storage.js";
|
|
5
|
+
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
|
+
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
|
+
function extractUserMessageText(content) {
|
|
70
|
+
if (typeof content === "string") {
|
|
71
|
+
return content;
|
|
72
|
+
}
|
|
73
|
+
return content
|
|
74
|
+
.filter((part) => part.type === "text" && typeof part.text === "string")
|
|
75
|
+
.map((part) => part.text)
|
|
76
|
+
.join("");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Stable wrapper around a replaceable AgentSession runtime.
|
|
80
|
+
*
|
|
81
|
+
* Use this when your application needs `/new`, `/resume`, `/fork`, or import
|
|
82
|
+
* behavior. After replacement, read `session` again and rebind any
|
|
83
|
+
* session-local subscriptions or extension bindings.
|
|
84
|
+
*/
|
|
85
|
+
export class AgentSessionRuntimeHost {
|
|
86
|
+
bootstrap;
|
|
87
|
+
runtime;
|
|
88
|
+
constructor(bootstrap, runtime) {
|
|
89
|
+
this.bootstrap = bootstrap;
|
|
90
|
+
this.runtime = runtime;
|
|
91
|
+
}
|
|
92
|
+
/** The currently active session instance. Re-read this after runtime replacement. */
|
|
93
|
+
get session() {
|
|
94
|
+
return this.runtime.session;
|
|
95
|
+
}
|
|
96
|
+
async emitBeforeSwitch(reason, targetSessionFile) {
|
|
97
|
+
const runner = this.runtime.session.extensionRunner;
|
|
98
|
+
if (!runner?.hasHandlers("session_before_switch")) {
|
|
99
|
+
return { cancelled: false };
|
|
100
|
+
}
|
|
101
|
+
const result = await runner.emit({
|
|
102
|
+
type: "session_before_switch",
|
|
103
|
+
reason,
|
|
104
|
+
targetSessionFile,
|
|
105
|
+
});
|
|
106
|
+
return { cancelled: result?.cancel === true };
|
|
107
|
+
}
|
|
108
|
+
async emitBeforeFork(entryId) {
|
|
109
|
+
const runner = this.runtime.session.extensionRunner;
|
|
110
|
+
if (!runner?.hasHandlers("session_before_fork")) {
|
|
111
|
+
return { cancelled: false };
|
|
112
|
+
}
|
|
113
|
+
const result = await runner.emit({
|
|
114
|
+
type: "session_before_fork",
|
|
115
|
+
entryId,
|
|
116
|
+
});
|
|
117
|
+
return { cancelled: result?.cancel === true };
|
|
118
|
+
}
|
|
119
|
+
async replace(options) {
|
|
120
|
+
const nextRuntime = await createAgentSessionRuntime(this.bootstrap, options);
|
|
121
|
+
await emitSessionShutdownEvent(this.runtime.session.extensionRunner);
|
|
122
|
+
this.runtime.session.dispose();
|
|
123
|
+
if (process.cwd() !== nextRuntime.cwd) {
|
|
124
|
+
process.chdir(nextRuntime.cwd);
|
|
125
|
+
}
|
|
126
|
+
this.runtime = nextRuntime;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Replace the active runtime with one opened from an existing session file.
|
|
130
|
+
*
|
|
131
|
+
* Emits `session_before_switch` before replacement and returns
|
|
132
|
+
* `{ cancelled: true }` if an extension vetoes the switch.
|
|
133
|
+
*/
|
|
134
|
+
async switchSession(sessionPath) {
|
|
135
|
+
const beforeResult = await this.emitBeforeSwitch("resume", sessionPath);
|
|
136
|
+
if (beforeResult.cancelled) {
|
|
137
|
+
return beforeResult;
|
|
138
|
+
}
|
|
139
|
+
const previousSessionFile = this.runtime.session.sessionFile;
|
|
140
|
+
const sessionManager = SessionManager.open(sessionPath);
|
|
141
|
+
await this.replace({
|
|
142
|
+
cwd: sessionManager.getCwd(),
|
|
143
|
+
sessionManager,
|
|
144
|
+
sessionStartEvent: { type: "session_start", reason: "resume", previousSessionFile },
|
|
145
|
+
});
|
|
146
|
+
return { cancelled: false };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Replace the active runtime with a fresh session in the current cwd.
|
|
150
|
+
*
|
|
151
|
+
* `setup` runs after replacement against the new session manager, which lets
|
|
152
|
+
* callers seed entries before normal use begins.
|
|
153
|
+
*/
|
|
154
|
+
async newSession(options) {
|
|
155
|
+
const beforeResult = await this.emitBeforeSwitch("new");
|
|
156
|
+
if (beforeResult.cancelled) {
|
|
157
|
+
return beforeResult;
|
|
158
|
+
}
|
|
159
|
+
const previousSessionFile = this.runtime.session.sessionFile;
|
|
160
|
+
const sessionDir = this.runtime.sessionManager.getSessionDir();
|
|
161
|
+
const sessionManager = SessionManager.create(this.runtime.cwd, sessionDir);
|
|
162
|
+
if (options?.parentSession) {
|
|
163
|
+
sessionManager.newSession({ parentSession: options.parentSession });
|
|
164
|
+
}
|
|
165
|
+
await this.replace({
|
|
166
|
+
cwd: this.runtime.cwd,
|
|
167
|
+
sessionManager,
|
|
168
|
+
sessionStartEvent: { type: "session_start", reason: "new", previousSessionFile },
|
|
169
|
+
});
|
|
170
|
+
if (options?.setup) {
|
|
171
|
+
await options.setup(this.runtime.sessionManager);
|
|
172
|
+
this.runtime.session.agent.state.messages = this.runtime.sessionManager.buildSessionContext().messages;
|
|
173
|
+
}
|
|
174
|
+
return { cancelled: false };
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Replace the active runtime with a fork rooted at the given user-message
|
|
178
|
+
* entry.
|
|
179
|
+
*
|
|
180
|
+
* Returns the selected user text so UIs can restore it into the editor after
|
|
181
|
+
* the fork completes.
|
|
182
|
+
*/
|
|
183
|
+
async fork(entryId) {
|
|
184
|
+
const beforeResult = await this.emitBeforeFork(entryId);
|
|
185
|
+
if (beforeResult.cancelled) {
|
|
186
|
+
return { cancelled: true };
|
|
187
|
+
}
|
|
188
|
+
const selectedEntry = this.runtime.sessionManager.getEntry(entryId);
|
|
189
|
+
if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
|
|
190
|
+
throw new Error("Invalid entry ID for forking");
|
|
191
|
+
}
|
|
192
|
+
const previousSessionFile = this.runtime.session.sessionFile;
|
|
193
|
+
const selectedText = extractUserMessageText(selectedEntry.message.content);
|
|
194
|
+
if (this.runtime.sessionManager.isPersisted()) {
|
|
195
|
+
const currentSessionFile = this.runtime.session.sessionFile;
|
|
196
|
+
const sessionDir = this.runtime.sessionManager.getSessionDir();
|
|
197
|
+
if (!selectedEntry.parentId) {
|
|
198
|
+
const sessionManager = SessionManager.create(this.runtime.cwd, sessionDir);
|
|
199
|
+
sessionManager.newSession({ parentSession: currentSessionFile });
|
|
200
|
+
await this.replace({
|
|
201
|
+
cwd: this.runtime.cwd,
|
|
202
|
+
sessionManager,
|
|
203
|
+
sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
|
|
204
|
+
});
|
|
205
|
+
return { cancelled: false, selectedText };
|
|
206
|
+
}
|
|
207
|
+
const sourceManager = SessionManager.open(currentSessionFile, sessionDir);
|
|
208
|
+
const forkedSessionPath = sourceManager.createBranchedSession(selectedEntry.parentId);
|
|
209
|
+
const sessionManager = SessionManager.open(forkedSessionPath, sessionDir);
|
|
210
|
+
await this.replace({
|
|
211
|
+
cwd: sessionManager.getCwd(),
|
|
212
|
+
sessionManager,
|
|
213
|
+
sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
|
|
214
|
+
});
|
|
215
|
+
return { cancelled: false, selectedText };
|
|
216
|
+
}
|
|
217
|
+
const sessionManager = this.runtime.sessionManager;
|
|
218
|
+
if (!selectedEntry.parentId) {
|
|
219
|
+
sessionManager.newSession({ parentSession: this.runtime.session.sessionFile });
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
sessionManager.createBranchedSession(selectedEntry.parentId);
|
|
223
|
+
}
|
|
224
|
+
await this.replace({
|
|
225
|
+
cwd: this.runtime.cwd,
|
|
226
|
+
sessionManager,
|
|
227
|
+
sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
|
|
228
|
+
});
|
|
229
|
+
return { cancelled: false, selectedText };
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Import a JSONL session file into the current session directory and replace
|
|
233
|
+
* the active runtime with the imported session.
|
|
234
|
+
*/
|
|
235
|
+
async importFromJsonl(inputPath) {
|
|
236
|
+
const resolvedPath = resolve(inputPath);
|
|
237
|
+
if (!existsSync(resolvedPath)) {
|
|
238
|
+
throw new Error(`File not found: ${resolvedPath}`);
|
|
239
|
+
}
|
|
240
|
+
const sessionDir = this.runtime.sessionManager.getSessionDir();
|
|
241
|
+
if (!existsSync(sessionDir)) {
|
|
242
|
+
mkdirSync(sessionDir, { recursive: true });
|
|
243
|
+
}
|
|
244
|
+
const destinationPath = join(sessionDir, basename(resolvedPath));
|
|
245
|
+
const beforeResult = await this.emitBeforeSwitch("resume", destinationPath);
|
|
246
|
+
if (beforeResult.cancelled) {
|
|
247
|
+
return beforeResult;
|
|
248
|
+
}
|
|
249
|
+
const previousSessionFile = this.runtime.session.sessionFile;
|
|
250
|
+
if (resolve(destinationPath) !== resolvedPath) {
|
|
251
|
+
copyFileSync(resolvedPath, destinationPath);
|
|
252
|
+
}
|
|
253
|
+
const sessionManager = SessionManager.open(destinationPath, sessionDir);
|
|
254
|
+
await this.replace({
|
|
255
|
+
cwd: sessionManager.getCwd(),
|
|
256
|
+
sessionManager,
|
|
257
|
+
sessionStartEvent: { type: "session_start", reason: "resume", previousSessionFile },
|
|
258
|
+
});
|
|
259
|
+
return { cancelled: false };
|
|
260
|
+
}
|
|
261
|
+
/** Emit session shutdown for the active runtime and dispose it permanently. */
|
|
262
|
+
async dispose() {
|
|
263
|
+
await emitSessionShutdownEvent(this.runtime.session.extensionRunner);
|
|
264
|
+
this.runtime.session.dispose();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=agent-session-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-runtime.js","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 \"@draht/agent-core\";\nimport type { Model } from \"@draht/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"]}
|