@aexol/spectral 0.7.1 → 0.7.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.
Files changed (219) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/agent/agents.js +1 -1
  3. package/dist/agent/index.js +199 -184
  4. package/dist/commands/serve.js +0 -3
  5. package/dist/designer/data/systems/renault/DESIGN.md +1 -1
  6. package/dist/designer/philosophies.js +668 -0
  7. package/dist/mcp/sampling-handler.js +1 -1
  8. package/dist/memory/commands/status.js +1 -1
  9. package/dist/memory/compaction.js +2 -2
  10. package/dist/memory/config.js +1 -1
  11. package/dist/memory/debug-log.js +1 -1
  12. package/dist/memory/hooks/compaction-hook.js +29 -0
  13. package/dist/memory/index.js +2 -0
  14. package/dist/memory/observer.js +2 -2
  15. package/dist/memory/project-observations-store.js +14 -0
  16. package/dist/memory/tokens.js +1 -1
  17. package/dist/memory/tools/read-project-observations.js +82 -0
  18. package/dist/memory/tools/recall-observation.js +2 -2
  19. package/dist/pi/agent-core/agent-loop.js +501 -0
  20. package/dist/pi/agent-core/agent.js +401 -0
  21. package/dist/pi/agent-core/harness/agent-harness.js +899 -0
  22. package/dist/pi/agent-core/harness/compaction/branch-summarization.js +173 -0
  23. package/dist/pi/agent-core/harness/compaction/compaction.js +532 -0
  24. package/dist/pi/agent-core/harness/compaction/utils.js +130 -0
  25. package/dist/pi/agent-core/harness/env/nodejs.js +485 -0
  26. package/dist/pi/agent-core/harness/messages.js +101 -0
  27. package/dist/pi/agent-core/harness/prompt-templates.js +229 -0
  28. package/dist/pi/agent-core/harness/session/jsonl-repo.js +100 -0
  29. package/dist/pi/agent-core/harness/session/jsonl-storage.js +230 -0
  30. package/dist/pi/agent-core/harness/session/memory-repo.js +41 -0
  31. package/dist/pi/agent-core/harness/session/memory-storage.js +113 -0
  32. package/dist/pi/agent-core/harness/session/repo-utils.js +38 -0
  33. package/dist/pi/agent-core/harness/session/session.js +196 -0
  34. package/dist/pi/agent-core/harness/session/uuid.js +49 -0
  35. package/dist/pi/agent-core/harness/skills.js +310 -0
  36. package/dist/pi/agent-core/harness/system-prompt.js +29 -0
  37. package/dist/pi/agent-core/harness/types.js +93 -0
  38. package/dist/pi/agent-core/harness/utils/shell-output.js +125 -0
  39. package/dist/pi/agent-core/harness/utils/truncate.js +289 -0
  40. package/dist/pi/agent-core/index.js +24 -0
  41. package/dist/pi/agent-core/node.js +2 -0
  42. package/dist/pi/agent-core/proxy.js +277 -0
  43. package/dist/pi/agent-core/types.js +1 -0
  44. package/dist/pi/ai/api-registry.js +43 -0
  45. package/dist/pi/ai/cli.js +120 -0
  46. package/dist/pi/ai/env-api-keys.js +169 -0
  47. package/dist/pi/ai/image-models.generated.js +441 -0
  48. package/dist/pi/ai/image-models.js +22 -0
  49. package/dist/pi/ai/images-api-registry.js +21 -0
  50. package/dist/pi/ai/images.js +13 -0
  51. package/dist/pi/ai/index.js +18 -0
  52. package/dist/pi/ai/models.generated.js +16220 -0
  53. package/dist/pi/ai/models.js +70 -0
  54. package/dist/pi/ai/oauth.js +1 -0
  55. package/dist/pi/ai/providers/anthropic.js +945 -0
  56. package/dist/pi/ai/providers/faux.js +367 -0
  57. package/dist/pi/ai/providers/github-copilot-headers.js +28 -0
  58. package/dist/pi/ai/providers/openai-completions.js +945 -0
  59. package/dist/pi/ai/providers/openai-prompt-cache.js +9 -0
  60. package/dist/pi/ai/providers/register-builtins.js +97 -0
  61. package/dist/pi/ai/providers/simple-options.js +40 -0
  62. package/dist/pi/ai/providers/transform-messages.js +183 -0
  63. package/dist/pi/ai/session-resources.js +21 -0
  64. package/dist/pi/ai/stream.js +26 -0
  65. package/dist/pi/ai/types.js +1 -0
  66. package/dist/pi/ai/utils/diagnostics.js +24 -0
  67. package/dist/pi/ai/utils/event-stream.js +80 -0
  68. package/dist/pi/ai/utils/hash.js +13 -0
  69. package/dist/pi/ai/utils/headers.js +7 -0
  70. package/dist/pi/ai/utils/json-parse.js +112 -0
  71. package/dist/pi/ai/utils/node-http-proxy.js +96 -0
  72. package/dist/pi/ai/utils/oauth/anthropic.js +334 -0
  73. package/dist/pi/ai/utils/oauth/device-code.js +54 -0
  74. package/dist/pi/ai/utils/oauth/github-copilot.js +270 -0
  75. package/dist/pi/ai/utils/oauth/index.js +121 -0
  76. package/dist/pi/ai/utils/oauth/oauth-page.js +104 -0
  77. package/dist/pi/ai/utils/oauth/openai-codex.js +384 -0
  78. package/dist/pi/ai/utils/oauth/pkce.js +30 -0
  79. package/dist/pi/ai/utils/oauth/types.js +1 -0
  80. package/dist/pi/ai/utils/overflow.js +150 -0
  81. package/dist/pi/ai/utils/sanitize-unicode.js +25 -0
  82. package/dist/pi/ai/utils/typebox-helpers.js +20 -0
  83. package/dist/pi/ai/utils/validation.js +280 -0
  84. package/dist/pi/coding-agent/bun/cli.js +7 -0
  85. package/dist/pi/coding-agent/bun/restore-sandbox-env.js +31 -0
  86. package/dist/pi/coding-agent/cli/args.js +340 -0
  87. package/dist/pi/coding-agent/cli/file-processor.js +82 -0
  88. package/dist/pi/coding-agent/cli/initial-message.js +21 -0
  89. package/dist/pi/coding-agent/cli.js +17 -0
  90. package/dist/pi/coding-agent/config.js +414 -0
  91. package/dist/pi/coding-agent/core/agent-session-runtime.js +299 -0
  92. package/dist/pi/coding-agent/core/agent-session-services.js +117 -0
  93. package/dist/pi/coding-agent/core/agent-session.js +2498 -0
  94. package/dist/pi/coding-agent/core/auth-guidance.js +20 -0
  95. package/dist/pi/coding-agent/core/auth-storage.js +441 -0
  96. package/dist/pi/coding-agent/core/bash-executor.js +110 -0
  97. package/dist/pi/coding-agent/core/compaction/branch-summarization.js +242 -0
  98. package/dist/pi/coding-agent/core/compaction/compaction.js +624 -0
  99. package/dist/pi/coding-agent/core/compaction/index.js +6 -0
  100. package/dist/pi/coding-agent/core/compaction/utils.js +152 -0
  101. package/dist/pi/coding-agent/core/defaults.js +1 -0
  102. package/dist/pi/coding-agent/core/diagnostics.js +1 -0
  103. package/dist/pi/coding-agent/core/event-bus.js +24 -0
  104. package/dist/pi/coding-agent/core/exec.js +74 -0
  105. package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +248 -0
  106. package/dist/pi/coding-agent/core/export-html/index.js +225 -0
  107. package/dist/pi/coding-agent/core/export-html/tool-renderer.js +107 -0
  108. package/dist/pi/coding-agent/core/extensions/index.js +8 -0
  109. package/dist/pi/coding-agent/core/extensions/loader.js +485 -0
  110. package/dist/pi/coding-agent/core/extensions/runner.js +824 -0
  111. package/dist/pi/coding-agent/core/extensions/types.js +44 -0
  112. package/dist/pi/coding-agent/core/extensions/wrapper.js +21 -0
  113. package/dist/pi/coding-agent/core/footer-data-provider.js +309 -0
  114. package/dist/pi/coding-agent/core/http-dispatcher.js +47 -0
  115. package/dist/pi/coding-agent/core/index.js +11 -0
  116. package/dist/pi/coding-agent/core/keybindings.js +294 -0
  117. package/dist/pi/coding-agent/core/messages.js +122 -0
  118. package/dist/pi/coding-agent/core/model-registry.js +728 -0
  119. package/dist/pi/coding-agent/core/model-resolver.js +494 -0
  120. package/dist/pi/coding-agent/core/output-guard.js +58 -0
  121. package/dist/pi/coding-agent/core/package-manager.js +2020 -0
  122. package/dist/pi/coding-agent/core/prompt-templates.js +237 -0
  123. package/dist/pi/coding-agent/core/provider-display-names.js +32 -0
  124. package/dist/pi/coding-agent/core/resolve-config-value.js +125 -0
  125. package/dist/pi/coding-agent/core/resource-loader.js +733 -0
  126. package/dist/pi/coding-agent/core/sdk.js +282 -0
  127. package/dist/pi/coding-agent/core/session-cwd.js +37 -0
  128. package/dist/pi/coding-agent/core/session-manager.js +1146 -0
  129. package/dist/pi/coding-agent/core/settings-manager.js +794 -0
  130. package/dist/pi/coding-agent/core/skills.js +386 -0
  131. package/dist/pi/coding-agent/core/slash-commands.js +24 -0
  132. package/dist/pi/coding-agent/core/source-info.js +18 -0
  133. package/dist/pi/coding-agent/core/system-prompt.js +122 -0
  134. package/dist/pi/coding-agent/core/telemetry.js +8 -0
  135. package/dist/pi/coding-agent/core/timings.js +30 -0
  136. package/dist/pi/coding-agent/core/tools/bash.js +341 -0
  137. package/dist/pi/coding-agent/core/tools/edit-diff.js +344 -0
  138. package/dist/pi/coding-agent/core/tools/edit.js +324 -0
  139. package/dist/pi/coding-agent/core/tools/file-mutation-queue.js +36 -0
  140. package/dist/pi/coding-agent/core/tools/find.js +297 -0
  141. package/dist/pi/coding-agent/core/tools/grep.js +303 -0
  142. package/dist/pi/coding-agent/core/tools/index.js +111 -0
  143. package/dist/pi/coding-agent/core/tools/ls.js +168 -0
  144. package/dist/pi/coding-agent/core/tools/output-accumulator.js +183 -0
  145. package/dist/pi/coding-agent/core/tools/path-utils.js +61 -0
  146. package/dist/pi/coding-agent/core/tools/read.js +288 -0
  147. package/dist/pi/coding-agent/core/tools/render-utils.js +48 -0
  148. package/dist/pi/coding-agent/core/tools/tool-definition-wrapper.js +33 -0
  149. package/dist/pi/coding-agent/core/tools/truncate.js +214 -0
  150. package/dist/pi/coding-agent/core/tools/write.js +212 -0
  151. package/dist/pi/coding-agent/index.js +41 -0
  152. package/dist/pi/coding-agent/main.js +5 -0
  153. package/dist/pi/coding-agent/migrations.js +280 -0
  154. package/dist/pi/coding-agent/modes/index.js +7 -0
  155. package/dist/pi/coding-agent/modes/interactive/components/diff.js +132 -0
  156. package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +35 -0
  157. package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +32 -0
  158. package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +3 -0
  159. package/dist/pi/coding-agent/modes/interactive/theme/theme.js +1023 -0
  160. package/dist/pi/coding-agent/modes/print-mode.js +130 -0
  161. package/dist/pi/coding-agent/modes/rpc/jsonl.js +48 -0
  162. package/dist/pi/coding-agent/modes/rpc/rpc-client.js +409 -0
  163. package/dist/pi/coding-agent/modes/rpc/rpc-mode.js +600 -0
  164. package/dist/pi/coding-agent/modes/rpc/rpc-types.js +7 -0
  165. package/dist/pi/coding-agent/utils/ansi.js +51 -0
  166. package/dist/pi/coding-agent/utils/changelog.js +86 -0
  167. package/dist/pi/coding-agent/utils/child-process.js +87 -0
  168. package/dist/pi/coding-agent/utils/clipboard-image.js +244 -0
  169. package/dist/pi/coding-agent/utils/clipboard-native.js +13 -0
  170. package/dist/pi/coding-agent/utils/clipboard.js +116 -0
  171. package/dist/pi/coding-agent/utils/exif-orientation.js +157 -0
  172. package/dist/pi/coding-agent/utils/frontmatter.js +25 -0
  173. package/dist/pi/coding-agent/utils/fs-watch.js +24 -0
  174. package/dist/pi/coding-agent/utils/git.js +162 -0
  175. package/dist/pi/coding-agent/utils/html.js +39 -0
  176. package/dist/pi/coding-agent/utils/image-convert.js +38 -0
  177. package/dist/pi/coding-agent/utils/image-resize.js +136 -0
  178. package/dist/pi/coding-agent/utils/mime.js +68 -0
  179. package/dist/pi/coding-agent/utils/paths.js +91 -0
  180. package/dist/pi/coding-agent/utils/photon.js +120 -0
  181. package/dist/pi/coding-agent/utils/pi-user-agent.js +4 -0
  182. package/dist/pi/coding-agent/utils/shell.js +194 -0
  183. package/dist/pi/coding-agent/utils/sleep.js +16 -0
  184. package/dist/pi/coding-agent/utils/syntax-highlight.js +117 -0
  185. package/dist/pi/coding-agent/utils/tools-manager.js +327 -0
  186. package/dist/pi/coding-agent/utils/version-check.js +81 -0
  187. package/dist/pi/coding-agent/utils/windows-self-update.js +76 -0
  188. package/dist/pi/tui/autocomplete.js +631 -0
  189. package/dist/pi/tui/components/box.js +103 -0
  190. package/dist/pi/tui/components/cancellable-loader.js +34 -0
  191. package/dist/pi/tui/components/editor.js +1915 -0
  192. package/dist/pi/tui/components/image.js +88 -0
  193. package/dist/pi/tui/components/input.js +425 -0
  194. package/dist/pi/tui/components/loader.js +68 -0
  195. package/dist/pi/tui/components/markdown.js +633 -0
  196. package/dist/pi/tui/components/select-list.js +158 -0
  197. package/dist/pi/tui/components/settings-list.js +184 -0
  198. package/dist/pi/tui/components/spacer.js +22 -0
  199. package/dist/pi/tui/components/text.js +88 -0
  200. package/dist/pi/tui/components/truncated-text.js +50 -0
  201. package/dist/pi/tui/editor-component.js +1 -0
  202. package/dist/pi/tui/fuzzy.js +109 -0
  203. package/dist/pi/tui/index.js +31 -0
  204. package/dist/pi/tui/keybindings.js +173 -0
  205. package/dist/pi/tui/keys.js +1172 -0
  206. package/dist/pi/tui/kill-ring.js +43 -0
  207. package/dist/pi/tui/stdin-buffer.js +360 -0
  208. package/dist/pi/tui/terminal-image.js +335 -0
  209. package/dist/pi/tui/terminal.js +324 -0
  210. package/dist/pi/tui/tui.js +1076 -0
  211. package/dist/pi/tui/undo-stack.js +24 -0
  212. package/dist/pi/tui/utils.js +1016 -0
  213. package/dist/relay/dispatcher.js +30 -0
  214. package/dist/server/handlers/queue.js +52 -0
  215. package/dist/server/pi-bridge.js +9 -1
  216. package/dist/server/session-stream.js +76 -111
  217. package/dist/server/storage.js +154 -2
  218. package/dist/server/title-generator.js +14 -153
  219. package/package.json +24 -6
@@ -0,0 +1,299 @@
1
+ import { copyFileSync, existsSync, mkdirSync } from "node:fs";
2
+ import { basename, join, resolve } from "node:path";
3
+ import { resolvePath } from "../utils/paths.js";
4
+ import { emitSessionShutdownEvent } from "./extensions/runner.js";
5
+ import { assertSessionCwdExists } from "./session-cwd.js";
6
+ import { SessionManager } from "./session-manager.js";
7
+ /**
8
+ * Thrown when /import references a JSONL file path that does not exist.
9
+ */
10
+ export class SessionImportFileNotFoundError extends Error {
11
+ filePath;
12
+ constructor(filePath) {
13
+ super(`File not found: ${filePath}`);
14
+ this.name = "SessionImportFileNotFoundError";
15
+ this.filePath = filePath;
16
+ }
17
+ }
18
+ function extractUserMessageText(content) {
19
+ if (typeof content === "string") {
20
+ return content;
21
+ }
22
+ return content
23
+ .filter((part) => part.type === "text" && typeof part.text === "string")
24
+ .map((part) => part.text)
25
+ .join("");
26
+ }
27
+ /**
28
+ * Owns the current AgentSession plus its cwd-bound services.
29
+ *
30
+ * Session replacement methods tear down the current runtime first, then create
31
+ * and apply the next runtime. If creation fails, the error is propagated to the
32
+ * caller. The caller is responsible for user-facing error handling.
33
+ */
34
+ export class AgentSessionRuntime {
35
+ rebindSession;
36
+ beforeSessionInvalidate;
37
+ _session;
38
+ _services;
39
+ createRuntime;
40
+ _diagnostics;
41
+ _modelFallbackMessage;
42
+ constructor(_session, _services, createRuntime, _diagnostics = [], _modelFallbackMessage) {
43
+ this._session = _session;
44
+ this._services = _services;
45
+ this.createRuntime = createRuntime;
46
+ this._diagnostics = _diagnostics;
47
+ this._modelFallbackMessage = _modelFallbackMessage;
48
+ }
49
+ get services() {
50
+ return this._services;
51
+ }
52
+ get session() {
53
+ return this._session;
54
+ }
55
+ get cwd() {
56
+ return this._services.cwd;
57
+ }
58
+ get diagnostics() {
59
+ return this._diagnostics;
60
+ }
61
+ get modelFallbackMessage() {
62
+ return this._modelFallbackMessage;
63
+ }
64
+ setRebindSession(rebindSession) {
65
+ this.rebindSession = rebindSession;
66
+ }
67
+ /**
68
+ * Set a synchronous callback that runs after `session_shutdown` handlers finish
69
+ * but before the current session is invalidated.
70
+ *
71
+ * This is for host-owned UI teardown that must not yield to the event loop,
72
+ * such as detaching extension-provided TUI components before the old extension
73
+ * context becomes stale.
74
+ */
75
+ setBeforeSessionInvalidate(beforeSessionInvalidate) {
76
+ this.beforeSessionInvalidate = beforeSessionInvalidate;
77
+ }
78
+ async emitBeforeSwitch(reason, targetSessionFile) {
79
+ const runner = this.session.extensionRunner;
80
+ if (!runner.hasHandlers("session_before_switch")) {
81
+ return { cancelled: false };
82
+ }
83
+ const result = await runner.emit({
84
+ type: "session_before_switch",
85
+ reason,
86
+ targetSessionFile,
87
+ });
88
+ return { cancelled: result?.cancel === true };
89
+ }
90
+ async emitBeforeFork(entryId, options) {
91
+ const runner = this.session.extensionRunner;
92
+ if (!runner.hasHandlers("session_before_fork")) {
93
+ return { cancelled: false };
94
+ }
95
+ const result = await runner.emit({
96
+ type: "session_before_fork",
97
+ entryId,
98
+ ...options,
99
+ });
100
+ return { cancelled: result?.cancel === true };
101
+ }
102
+ async teardownCurrent(reason, targetSessionFile) {
103
+ await emitSessionShutdownEvent(this.session.extensionRunner, {
104
+ type: "session_shutdown",
105
+ reason,
106
+ targetSessionFile,
107
+ });
108
+ this.beforeSessionInvalidate?.();
109
+ this.session.dispose();
110
+ }
111
+ apply(result) {
112
+ this._session = result.session;
113
+ this._services = result.services;
114
+ this._diagnostics = result.diagnostics;
115
+ this._modelFallbackMessage = result.modelFallbackMessage;
116
+ }
117
+ async finishSessionReplacement(withSession) {
118
+ if (this.rebindSession) {
119
+ await this.rebindSession(this.session);
120
+ }
121
+ if (withSession) {
122
+ await withSession(this.session.createReplacedSessionContext());
123
+ }
124
+ }
125
+ async switchSession(sessionPath, options) {
126
+ const beforeResult = await this.emitBeforeSwitch("resume", sessionPath);
127
+ if (beforeResult.cancelled) {
128
+ return beforeResult;
129
+ }
130
+ const previousSessionFile = this.session.sessionFile;
131
+ const sessionManager = SessionManager.open(sessionPath, undefined, options?.cwdOverride);
132
+ assertSessionCwdExists(sessionManager, this.cwd);
133
+ await this.teardownCurrent("resume", sessionManager.getSessionFile());
134
+ this.apply(await this.createRuntime({
135
+ cwd: sessionManager.getCwd(),
136
+ agentDir: this.services.agentDir,
137
+ sessionManager,
138
+ sessionStartEvent: { type: "session_start", reason: "resume", previousSessionFile },
139
+ }));
140
+ await this.finishSessionReplacement(options?.withSession);
141
+ return { cancelled: false };
142
+ }
143
+ async newSession(options) {
144
+ const beforeResult = await this.emitBeforeSwitch("new");
145
+ if (beforeResult.cancelled) {
146
+ return beforeResult;
147
+ }
148
+ const previousSessionFile = this.session.sessionFile;
149
+ const sessionDir = this.session.sessionManager.getSessionDir();
150
+ const sessionManager = SessionManager.create(this.cwd, sessionDir);
151
+ if (options?.parentSession) {
152
+ sessionManager.newSession({ parentSession: options.parentSession });
153
+ }
154
+ await this.teardownCurrent("new", sessionManager.getSessionFile());
155
+ this.apply(await this.createRuntime({
156
+ cwd: this.cwd,
157
+ agentDir: this.services.agentDir,
158
+ sessionManager,
159
+ sessionStartEvent: { type: "session_start", reason: "new", previousSessionFile },
160
+ }));
161
+ if (options?.setup) {
162
+ await options.setup(this.session.sessionManager);
163
+ this.session.agent.state.messages = this.session.sessionManager.buildSessionContext().messages;
164
+ }
165
+ await this.finishSessionReplacement(options?.withSession);
166
+ return { cancelled: false };
167
+ }
168
+ async fork(entryId, options) {
169
+ const position = options?.position ?? "before";
170
+ const beforeResult = await this.emitBeforeFork(entryId, { position });
171
+ if (beforeResult.cancelled) {
172
+ return { cancelled: true };
173
+ }
174
+ let targetLeafId;
175
+ let selectedText;
176
+ const selectedEntry = this.session.sessionManager.getEntry(entryId);
177
+ if (!selectedEntry) {
178
+ throw new Error("Invalid entry ID for forking");
179
+ }
180
+ if (position === "at") {
181
+ targetLeafId = selectedEntry.id;
182
+ }
183
+ else {
184
+ if (selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
185
+ throw new Error("Invalid entry ID for forking");
186
+ }
187
+ targetLeafId = selectedEntry.parentId;
188
+ selectedText = extractUserMessageText(selectedEntry.message.content);
189
+ }
190
+ const previousSessionFile = this.session.sessionFile;
191
+ if (this.session.sessionManager.isPersisted()) {
192
+ const currentSessionFile = this.session.sessionFile;
193
+ if (!currentSessionFile) {
194
+ throw new Error("Persisted session is missing a session file");
195
+ }
196
+ const sessionDir = this.session.sessionManager.getSessionDir();
197
+ if (!targetLeafId) {
198
+ const sessionManager = SessionManager.create(this.cwd, sessionDir);
199
+ sessionManager.newSession({ parentSession: currentSessionFile });
200
+ await this.teardownCurrent("fork", sessionManager.getSessionFile());
201
+ this.apply(await this.createRuntime({
202
+ cwd: this.cwd,
203
+ agentDir: this.services.agentDir,
204
+ sessionManager,
205
+ sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
206
+ }));
207
+ await this.finishSessionReplacement(options?.withSession);
208
+ return { cancelled: false, selectedText };
209
+ }
210
+ const sessionManager = SessionManager.open(currentSessionFile, sessionDir);
211
+ const forkedSessionPath = sessionManager.createBranchedSession(targetLeafId);
212
+ if (!forkedSessionPath) {
213
+ throw new Error("Failed to create forked session");
214
+ }
215
+ await this.teardownCurrent("fork", sessionManager.getSessionFile());
216
+ this.apply(await this.createRuntime({
217
+ cwd: sessionManager.getCwd(),
218
+ agentDir: this.services.agentDir,
219
+ sessionManager,
220
+ sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
221
+ }));
222
+ await this.finishSessionReplacement(options?.withSession);
223
+ return { cancelled: false, selectedText };
224
+ }
225
+ const sessionManager = this.session.sessionManager;
226
+ if (!targetLeafId) {
227
+ sessionManager.newSession({ parentSession: this.session.sessionFile });
228
+ }
229
+ else {
230
+ sessionManager.createBranchedSession(targetLeafId);
231
+ }
232
+ await this.teardownCurrent("fork", sessionManager.getSessionFile());
233
+ this.apply(await this.createRuntime({
234
+ cwd: this.cwd,
235
+ agentDir: this.services.agentDir,
236
+ sessionManager,
237
+ sessionStartEvent: { type: "session_start", reason: "fork", previousSessionFile },
238
+ }));
239
+ await this.finishSessionReplacement(options?.withSession);
240
+ return { cancelled: false, selectedText };
241
+ }
242
+ /**
243
+ * Import a session JSONL file and switch runtime state to the imported session.
244
+ *
245
+ * @returns `{ cancelled: true }` when cancelled by `session_before_switch`, otherwise `{ cancelled: false }`.
246
+ * @throws {SessionImportFileNotFoundError} When the input path does not exist.
247
+ * @throws {MissingSessionCwdError} When the imported session cwd cannot be resolved and no override is provided.
248
+ */
249
+ async importFromJsonl(inputPath, cwdOverride) {
250
+ const resolvedPath = resolvePath(inputPath);
251
+ if (!existsSync(resolvedPath)) {
252
+ throw new SessionImportFileNotFoundError(resolvedPath);
253
+ }
254
+ const sessionDir = this.session.sessionManager.getSessionDir();
255
+ if (!existsSync(sessionDir)) {
256
+ mkdirSync(sessionDir, { recursive: true });
257
+ }
258
+ const destinationPath = join(sessionDir, basename(resolvedPath));
259
+ const beforeResult = await this.emitBeforeSwitch("resume", destinationPath);
260
+ if (beforeResult.cancelled) {
261
+ return beforeResult;
262
+ }
263
+ const previousSessionFile = this.session.sessionFile;
264
+ if (resolve(destinationPath) !== resolvedPath) {
265
+ copyFileSync(resolvedPath, destinationPath);
266
+ }
267
+ const sessionManager = SessionManager.open(destinationPath, sessionDir, cwdOverride);
268
+ assertSessionCwdExists(sessionManager, this.cwd);
269
+ await this.teardownCurrent("resume", sessionManager.getSessionFile());
270
+ this.apply(await this.createRuntime({
271
+ cwd: sessionManager.getCwd(),
272
+ agentDir: this.services.agentDir,
273
+ sessionManager,
274
+ sessionStartEvent: { type: "session_start", reason: "resume", previousSessionFile },
275
+ }));
276
+ await this.finishSessionReplacement();
277
+ return { cancelled: false };
278
+ }
279
+ async dispose() {
280
+ await emitSessionShutdownEvent(this.session.extensionRunner, {
281
+ type: "session_shutdown",
282
+ reason: "quit",
283
+ });
284
+ this.beforeSessionInvalidate?.();
285
+ this.session.dispose();
286
+ }
287
+ }
288
+ /**
289
+ * Create the initial runtime from a runtime factory and initial session target.
290
+ *
291
+ * The same factory is stored on the returned AgentSessionRuntime and reused for
292
+ * later /new, /resume, /fork, and import flows.
293
+ */
294
+ export async function createAgentSessionRuntime(createRuntime, options) {
295
+ assertSessionCwdExists(options.sessionManager, options.cwd);
296
+ const result = await createRuntime(options);
297
+ return new AgentSessionRuntime(result.session, result.services, createRuntime, result.diagnostics, result.modelFallbackMessage);
298
+ }
299
+ export { createAgentSessionFromServices, createAgentSessionServices, } from "./agent-session-services.js";
@@ -0,0 +1,117 @@
1
+ import { join } from "node:path";
2
+ import { getAgentDir } from "../config.js";
3
+ import { resolvePath } from "../utils/paths.js";
4
+ import { AuthStorage } from "./auth-storage.js";
5
+ import { ModelRegistry } from "./model-registry.js";
6
+ import { DefaultResourceLoader } from "./resource-loader.js";
7
+ import { createAgentSession } from "./sdk.js";
8
+ import { SettingsManager } from "./settings-manager.js";
9
+ function applyExtensionFlagValues(resourceLoader, extensionFlagValues) {
10
+ if (!extensionFlagValues) {
11
+ return [];
12
+ }
13
+ const diagnostics = [];
14
+ const extensionsResult = resourceLoader.getExtensions();
15
+ const registeredFlags = new Map();
16
+ for (const extension of extensionsResult.extensions) {
17
+ for (const [name, flag] of extension.flags) {
18
+ registeredFlags.set(name, { type: flag.type });
19
+ }
20
+ }
21
+ const unknownFlags = [];
22
+ for (const [name, value] of extensionFlagValues) {
23
+ const flag = registeredFlags.get(name);
24
+ if (!flag) {
25
+ unknownFlags.push(name);
26
+ continue;
27
+ }
28
+ if (flag.type === "boolean") {
29
+ extensionsResult.runtime.flagValues.set(name, true);
30
+ continue;
31
+ }
32
+ if (typeof value === "string") {
33
+ extensionsResult.runtime.flagValues.set(name, value);
34
+ continue;
35
+ }
36
+ diagnostics.push({
37
+ type: "error",
38
+ message: `Extension flag "--${name}" requires a value`,
39
+ });
40
+ }
41
+ if (unknownFlags.length > 0) {
42
+ diagnostics.push({
43
+ type: "error",
44
+ message: `Unknown option${unknownFlags.length === 1 ? "" : "s"}: ${unknownFlags.map((name) => `--${name}`).join(", ")}`,
45
+ });
46
+ }
47
+ return diagnostics;
48
+ }
49
+ /**
50
+ * Create cwd-bound runtime services.
51
+ *
52
+ * Returns services plus diagnostics. It does not create an AgentSession.
53
+ */
54
+ export async function createAgentSessionServices(options) {
55
+ const cwd = resolvePath(options.cwd);
56
+ const agentDir = options.agentDir ? resolvePath(options.agentDir) : getAgentDir();
57
+ const authStorage = options.authStorage ?? AuthStorage.create(join(agentDir, "auth.json"));
58
+ const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);
59
+ const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, join(agentDir, "models.json"));
60
+ const resourceLoader = new DefaultResourceLoader({
61
+ ...(options.resourceLoaderOptions ?? {}),
62
+ cwd,
63
+ agentDir,
64
+ settingsManager,
65
+ });
66
+ await resourceLoader.reload();
67
+ const diagnostics = [];
68
+ const extensionsResult = resourceLoader.getExtensions();
69
+ for (const { name, config, extensionPath } of extensionsResult.runtime.pendingProviderRegistrations) {
70
+ try {
71
+ modelRegistry.registerProvider(name, config);
72
+ }
73
+ catch (error) {
74
+ const message = error instanceof Error ? error.message : String(error);
75
+ diagnostics.push({
76
+ type: "error",
77
+ message: `Extension "${extensionPath}" error: ${message}`,
78
+ });
79
+ }
80
+ }
81
+ extensionsResult.runtime.pendingProviderRegistrations = [];
82
+ diagnostics.push(...applyExtensionFlagValues(resourceLoader, options.extensionFlagValues));
83
+ return {
84
+ cwd,
85
+ agentDir,
86
+ authStorage,
87
+ settingsManager,
88
+ modelRegistry,
89
+ resourceLoader,
90
+ diagnostics,
91
+ };
92
+ }
93
+ /**
94
+ * Create an AgentSession from previously created services.
95
+ *
96
+ * This keeps session creation separate from service creation so callers can
97
+ * resolve model, thinking, tools, and other session inputs against the target
98
+ * cwd before constructing the session.
99
+ */
100
+ export async function createAgentSessionFromServices(options) {
101
+ return createAgentSession({
102
+ cwd: options.services.cwd,
103
+ agentDir: options.services.agentDir,
104
+ authStorage: options.services.authStorage,
105
+ settingsManager: options.services.settingsManager,
106
+ modelRegistry: options.services.modelRegistry,
107
+ resourceLoader: options.services.resourceLoader,
108
+ sessionManager: options.sessionManager,
109
+ model: options.model,
110
+ thinkingLevel: options.thinkingLevel,
111
+ scopedModels: options.scopedModels,
112
+ tools: options.tools,
113
+ noTools: options.noTools,
114
+ customTools: options.customTools,
115
+ sessionStartEvent: options.sessionStartEvent,
116
+ });
117
+ }