@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,282 @@
1
+ import { join } from "node:path";
2
+ import { Agent } from "../../agent-core/index.js";
3
+ import { clampThinkingLevel, streamSimple } from "../../ai/index.js";
4
+ import { getAgentDir } from "../config.js";
5
+ import { resolvePath } from "../utils/paths.js";
6
+ import { AgentSession } from "./agent-session.js";
7
+ import { formatNoModelsAvailableMessage } from "./auth-guidance.js";
8
+ import { AuthStorage } from "./auth-storage.js";
9
+ import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
10
+ import { convertToLlm } from "./messages.js";
11
+ import { ModelRegistry } from "./model-registry.js";
12
+ import { findInitialModel } from "./model-resolver.js";
13
+ import { DefaultResourceLoader } from "./resource-loader.js";
14
+ import { getDefaultSessionDir, SessionManager } from "./session-manager.js";
15
+ import { SettingsManager } from "./settings-manager.js";
16
+ import { isInstallTelemetryEnabled } from "./telemetry.js";
17
+ import { time } from "./timings.js";
18
+ import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue, } from "./tools/index.js";
19
+ // Re-exports
20
+ export * from "./agent-session-runtime.js";
21
+ export { withFileMutationQueue,
22
+ // Tool factories (for custom cwd)
23
+ createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createEditTool, createWriteTool, createGrepTool, createFindTool, createLsTool, };
24
+ // Helper Functions
25
+ function getDefaultAgentDir() {
26
+ return getAgentDir();
27
+ }
28
+ function getAttributionHeaders(model, settingsManager) {
29
+ if (!isInstallTelemetryEnabled(settingsManager)) {
30
+ return undefined;
31
+ }
32
+ if (model.provider === "openrouter" || model.baseUrl.includes("openrouter.ai")) {
33
+ return {
34
+ "HTTP-Referer": "https://pi.dev",
35
+ "X-OpenRouter-Title": "pi",
36
+ "X-OpenRouter-Categories": "cli-agent",
37
+ };
38
+ }
39
+ if (model.provider === "cloudflare-workers-ai" ||
40
+ model.provider === "cloudflare-ai-gateway" ||
41
+ model.baseUrl.includes("api.cloudflare.com") ||
42
+ model.baseUrl.includes("gateway.ai.cloudflare.com")) {
43
+ return {
44
+ "User-Agent": "pi-coding-agent",
45
+ };
46
+ }
47
+ return undefined;
48
+ }
49
+ /**
50
+ * Create an AgentSession with the specified options.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Minimal - uses defaults
55
+ * const { session } = await createAgentSession();
56
+ *
57
+ * // With explicit model
58
+ * import { getModel } from '../../ai/index.js';
59
+ * const { session } = await createAgentSession({
60
+ * model: getModel('anthropic', 'claude-opus-4-5'),
61
+ * thinkingLevel: 'high',
62
+ * });
63
+ *
64
+ * // Continue previous session
65
+ * const { session, modelFallbackMessage } = await createAgentSession({
66
+ * continueSession: true,
67
+ * });
68
+ *
69
+ * // Full control
70
+ * const loader = new DefaultResourceLoader({
71
+ * cwd: process.cwd(),
72
+ * agentDir: getAgentDir(),
73
+ * settingsManager: SettingsManager.create(),
74
+ * });
75
+ * await loader.reload();
76
+ * const { session } = await createAgentSession({
77
+ * model: myModel,
78
+ * tools: ["read", "bash"],
79
+ * resourceLoader: loader,
80
+ * sessionManager: SessionManager.inMemory(),
81
+ * });
82
+ * ```
83
+ */
84
+ export async function createAgentSession(options = {}) {
85
+ const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());
86
+ const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();
87
+ let resourceLoader = options.resourceLoader;
88
+ // Use provided or create AuthStorage and ModelRegistry
89
+ const authPath = options.agentDir ? join(agentDir, "auth.json") : undefined;
90
+ const modelsPath = options.agentDir ? join(agentDir, "models.json") : undefined;
91
+ const authStorage = options.authStorage ?? AuthStorage.create(authPath);
92
+ const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);
93
+ const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);
94
+ const sessionManager = options.sessionManager ?? SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));
95
+ if (!resourceLoader) {
96
+ resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });
97
+ await resourceLoader.reload();
98
+ time("resourceLoader.reload");
99
+ }
100
+ // Check if session has existing data to restore
101
+ const existingSession = sessionManager.buildSessionContext();
102
+ const hasExistingSession = existingSession.messages.length > 0;
103
+ const hasThinkingEntry = sessionManager.getBranch().some((entry) => entry.type === "thinking_level_change");
104
+ let model = options.model;
105
+ let modelFallbackMessage;
106
+ // If session has data, try to restore model from it
107
+ if (!model && hasExistingSession && existingSession.model) {
108
+ const restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);
109
+ if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {
110
+ model = restoredModel;
111
+ }
112
+ if (!model) {
113
+ modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;
114
+ }
115
+ }
116
+ // If still no model, use findInitialModel (checks settings default, then provider defaults)
117
+ if (!model) {
118
+ const result = await findInitialModel({
119
+ scopedModels: [],
120
+ isContinuing: hasExistingSession,
121
+ defaultProvider: settingsManager.getDefaultProvider(),
122
+ defaultModelId: settingsManager.getDefaultModel(),
123
+ defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),
124
+ modelRegistry,
125
+ });
126
+ model = result.model;
127
+ if (!model) {
128
+ modelFallbackMessage = formatNoModelsAvailableMessage();
129
+ }
130
+ else if (modelFallbackMessage) {
131
+ modelFallbackMessage += `. Using ${model.provider}/${model.id}`;
132
+ }
133
+ }
134
+ let thinkingLevel = options.thinkingLevel;
135
+ // If session has data, restore thinking level from it
136
+ if (thinkingLevel === undefined && hasExistingSession) {
137
+ thinkingLevel = hasThinkingEntry
138
+ ? existingSession.thinkingLevel
139
+ : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
140
+ }
141
+ // Fall back to settings default
142
+ if (thinkingLevel === undefined) {
143
+ thinkingLevel = settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
144
+ }
145
+ // Clamp to model capabilities
146
+ if (!model) {
147
+ thinkingLevel = "off";
148
+ }
149
+ else {
150
+ thinkingLevel = clampThinkingLevel(model, thinkingLevel);
151
+ }
152
+ const defaultActiveToolNames = ["read", "bash", "edit", "write"];
153
+ const allowedToolNames = options.tools ?? (options.noTools === "all" ? [] : undefined);
154
+ const initialActiveToolNames = options.tools
155
+ ? [...options.tools]
156
+ : options.noTools
157
+ ? []
158
+ : defaultActiveToolNames;
159
+ let agent;
160
+ // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)
161
+ const convertToLlmWithBlockImages = (messages) => {
162
+ const converted = convertToLlm(messages);
163
+ // Check setting dynamically so mid-session changes take effect
164
+ if (!settingsManager.getBlockImages()) {
165
+ return converted;
166
+ }
167
+ // Filter out ImageContent from all messages, replacing with text placeholder
168
+ return converted.map((msg) => {
169
+ if (msg.role === "user" || msg.role === "toolResult") {
170
+ const content = msg.content;
171
+ if (Array.isArray(content)) {
172
+ const hasImages = content.some((c) => c.type === "image");
173
+ if (hasImages) {
174
+ const filteredContent = content
175
+ .map((c) => c.type === "image" ? { type: "text", text: "Image reading is disabled." } : c)
176
+ .filter((c, i, arr) =>
177
+ // Dedupe consecutive "Image reading is disabled." texts
178
+ !(c.type === "text" &&
179
+ c.text === "Image reading is disabled." &&
180
+ i > 0 &&
181
+ arr[i - 1].type === "text" &&
182
+ arr[i - 1].text === "Image reading is disabled."));
183
+ return { ...msg, content: filteredContent };
184
+ }
185
+ }
186
+ }
187
+ return msg;
188
+ });
189
+ };
190
+ const extensionRunnerRef = {};
191
+ agent = new Agent({
192
+ initialState: {
193
+ systemPrompt: "",
194
+ model,
195
+ thinkingLevel,
196
+ tools: [],
197
+ },
198
+ convertToLlm: convertToLlmWithBlockImages,
199
+ streamFn: async (model, context, options) => {
200
+ const auth = await modelRegistry.getApiKeyAndHeaders(model);
201
+ if (!auth.ok) {
202
+ throw new Error(auth.error);
203
+ }
204
+ const providerRetrySettings = settingsManager.getProviderRetrySettings();
205
+ const attributionHeaders = getAttributionHeaders(model, settingsManager);
206
+ return streamSimple(model, context, {
207
+ ...options,
208
+ apiKey: auth.apiKey,
209
+ timeoutMs: options?.timeoutMs ?? providerRetrySettings.timeoutMs,
210
+ maxRetries: options?.maxRetries ?? providerRetrySettings.maxRetries,
211
+ maxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,
212
+ headers: attributionHeaders || auth.headers || options?.headers
213
+ ? { ...attributionHeaders, ...auth.headers, ...options?.headers }
214
+ : undefined,
215
+ });
216
+ },
217
+ onPayload: async (payload, _model) => {
218
+ const runner = extensionRunnerRef.current;
219
+ if (!runner?.hasHandlers("before_provider_request")) {
220
+ return payload;
221
+ }
222
+ return runner.emitBeforeProviderRequest(payload);
223
+ },
224
+ onResponse: async (response, _model) => {
225
+ const runner = extensionRunnerRef.current;
226
+ if (!runner?.hasHandlers("after_provider_response")) {
227
+ return;
228
+ }
229
+ await runner.emit({
230
+ type: "after_provider_response",
231
+ status: response.status,
232
+ headers: response.headers,
233
+ });
234
+ },
235
+ sessionId: sessionManager.getSessionId(),
236
+ transformContext: async (messages) => {
237
+ const runner = extensionRunnerRef.current;
238
+ if (!runner)
239
+ return messages;
240
+ return runner.emitContext(messages);
241
+ },
242
+ steeringMode: settingsManager.getSteeringMode(),
243
+ followUpMode: settingsManager.getFollowUpMode(),
244
+ transport: settingsManager.getTransport(),
245
+ thinkingBudgets: settingsManager.getThinkingBudgets(),
246
+ maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,
247
+ });
248
+ // Restore messages if session has existing data
249
+ if (hasExistingSession) {
250
+ agent.state.messages = existingSession.messages;
251
+ if (!hasThinkingEntry) {
252
+ sessionManager.appendThinkingLevelChange(thinkingLevel);
253
+ }
254
+ }
255
+ else {
256
+ // Save initial model and thinking level for new sessions so they can be restored on resume
257
+ if (model) {
258
+ sessionManager.appendModelChange(model.provider, model.id);
259
+ }
260
+ sessionManager.appendThinkingLevelChange(thinkingLevel);
261
+ }
262
+ const session = new AgentSession({
263
+ agent,
264
+ sessionManager,
265
+ settingsManager,
266
+ cwd,
267
+ scopedModels: options.scopedModels,
268
+ resourceLoader,
269
+ customTools: options.customTools,
270
+ modelRegistry,
271
+ initialActiveToolNames,
272
+ allowedToolNames,
273
+ extensionRunnerRef,
274
+ sessionStartEvent: options.sessionStartEvent,
275
+ });
276
+ const extensionsResult = resourceLoader.getExtensions();
277
+ return {
278
+ session,
279
+ extensionsResult,
280
+ modelFallbackMessage,
281
+ };
282
+ }
@@ -0,0 +1,37 @@
1
+ import { existsSync } from "node:fs";
2
+ export function getMissingSessionCwdIssue(sessionManager, fallbackCwd) {
3
+ const sessionFile = sessionManager.getSessionFile();
4
+ if (!sessionFile) {
5
+ return undefined;
6
+ }
7
+ const sessionCwd = sessionManager.getCwd();
8
+ if (!sessionCwd || existsSync(sessionCwd)) {
9
+ return undefined;
10
+ }
11
+ return {
12
+ sessionFile,
13
+ sessionCwd,
14
+ fallbackCwd,
15
+ };
16
+ }
17
+ export function formatMissingSessionCwdError(issue) {
18
+ const sessionFile = issue.sessionFile ? `\nSession file: ${issue.sessionFile}` : "";
19
+ return `Stored session working directory does not exist: ${issue.sessionCwd}${sessionFile}\nCurrent working directory: ${issue.fallbackCwd}`;
20
+ }
21
+ export function formatMissingSessionCwdPrompt(issue) {
22
+ return `cwd from session file does not exist\n${issue.sessionCwd}\n\ncontinue in current cwd\n${issue.fallbackCwd}`;
23
+ }
24
+ export class MissingSessionCwdError extends Error {
25
+ issue;
26
+ constructor(issue) {
27
+ super(formatMissingSessionCwdError(issue));
28
+ this.name = "MissingSessionCwdError";
29
+ this.issue = issue;
30
+ }
31
+ }
32
+ export function assertSessionCwdExists(sessionManager, fallbackCwd) {
33
+ const issue = getMissingSessionCwdIssue(sessionManager, fallbackCwd);
34
+ if (issue) {
35
+ throw new MissingSessionCwdError(issue);
36
+ }
37
+ }