@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,401 @@
1
+ import { streamSimple, } from "../ai/index.js";
2
+ import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.js";
3
+ function defaultConvertToLlm(messages) {
4
+ return messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
5
+ }
6
+ const EMPTY_USAGE = {
7
+ input: 0,
8
+ output: 0,
9
+ cacheRead: 0,
10
+ cacheWrite: 0,
11
+ totalTokens: 0,
12
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
13
+ };
14
+ const DEFAULT_MODEL = {
15
+ id: "unknown",
16
+ name: "unknown",
17
+ api: "unknown",
18
+ provider: "unknown",
19
+ baseUrl: "",
20
+ reasoning: false,
21
+ input: [],
22
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
23
+ contextWindow: 0,
24
+ maxTokens: 0,
25
+ };
26
+ function createMutableAgentState(initialState) {
27
+ let tools = initialState?.tools?.slice() ?? [];
28
+ let messages = initialState?.messages?.slice() ?? [];
29
+ return {
30
+ systemPrompt: initialState?.systemPrompt ?? "",
31
+ model: initialState?.model ?? DEFAULT_MODEL,
32
+ thinkingLevel: initialState?.thinkingLevel ?? "off",
33
+ get tools() {
34
+ return tools;
35
+ },
36
+ set tools(nextTools) {
37
+ tools = nextTools.slice();
38
+ },
39
+ get messages() {
40
+ return messages;
41
+ },
42
+ set messages(nextMessages) {
43
+ messages = nextMessages.slice();
44
+ },
45
+ isStreaming: false,
46
+ streamingMessage: undefined,
47
+ pendingToolCalls: new Set(),
48
+ errorMessage: undefined,
49
+ };
50
+ }
51
+ class PendingMessageQueue {
52
+ messages = [];
53
+ mode;
54
+ constructor(mode) {
55
+ this.mode = mode;
56
+ }
57
+ enqueue(message) {
58
+ this.messages.push(message);
59
+ }
60
+ hasItems() {
61
+ return this.messages.length > 0;
62
+ }
63
+ drain() {
64
+ if (this.mode === "all") {
65
+ const drained = this.messages.slice();
66
+ this.messages = [];
67
+ return drained;
68
+ }
69
+ const first = this.messages[0];
70
+ if (!first) {
71
+ return [];
72
+ }
73
+ this.messages = this.messages.slice(1);
74
+ return [first];
75
+ }
76
+ clear() {
77
+ this.messages = [];
78
+ }
79
+ }
80
+ /**
81
+ * Stateful wrapper around the low-level agent loop.
82
+ *
83
+ * `Agent` owns the current transcript, emits lifecycle events, executes tools,
84
+ * and exposes queueing APIs for steering and follow-up messages.
85
+ */
86
+ export class Agent {
87
+ _state;
88
+ listeners = new Set();
89
+ steeringQueue;
90
+ followUpQueue;
91
+ convertToLlm;
92
+ transformContext;
93
+ streamFn;
94
+ getApiKey;
95
+ onPayload;
96
+ onResponse;
97
+ beforeToolCall;
98
+ afterToolCall;
99
+ prepareNextTurn;
100
+ activeRun;
101
+ /** Session identifier forwarded to providers for cache-aware backends. */
102
+ sessionId;
103
+ /** Optional per-level thinking token budgets forwarded to the stream function. */
104
+ thinkingBudgets;
105
+ /** Preferred transport forwarded to the stream function. */
106
+ transport;
107
+ /** Optional cap for provider-requested retry delays. */
108
+ maxRetryDelayMs;
109
+ /** Tool execution strategy for assistant messages that contain multiple tool calls. */
110
+ toolExecution;
111
+ constructor(options = {}) {
112
+ this._state = createMutableAgentState(options.initialState);
113
+ this.convertToLlm = options.convertToLlm ?? defaultConvertToLlm;
114
+ this.transformContext = options.transformContext;
115
+ this.streamFn = options.streamFn ?? streamSimple;
116
+ this.getApiKey = options.getApiKey;
117
+ this.onPayload = options.onPayload;
118
+ this.onResponse = options.onResponse;
119
+ this.beforeToolCall = options.beforeToolCall;
120
+ this.afterToolCall = options.afterToolCall;
121
+ this.prepareNextTurn = options.prepareNextTurn;
122
+ this.steeringQueue = new PendingMessageQueue(options.steeringMode ?? "one-at-a-time");
123
+ this.followUpQueue = new PendingMessageQueue(options.followUpMode ?? "one-at-a-time");
124
+ this.sessionId = options.sessionId;
125
+ this.thinkingBudgets = options.thinkingBudgets;
126
+ this.transport = options.transport ?? "auto";
127
+ this.maxRetryDelayMs = options.maxRetryDelayMs;
128
+ this.toolExecution = options.toolExecution ?? "parallel";
129
+ }
130
+ /**
131
+ * Subscribe to agent lifecycle events.
132
+ *
133
+ * Listener promises are awaited in subscription order and are included in
134
+ * the current run's settlement. Listeners also receive the active abort
135
+ * signal for the current run.
136
+ *
137
+ * `agent_end` is the final emitted event for a run, but the agent does not
138
+ * become idle until all awaited listeners for that event have settled.
139
+ */
140
+ subscribe(listener) {
141
+ this.listeners.add(listener);
142
+ return () => this.listeners.delete(listener);
143
+ }
144
+ /**
145
+ * Current agent state.
146
+ *
147
+ * Assigning `state.tools` or `state.messages` copies the provided top-level array.
148
+ */
149
+ get state() {
150
+ return this._state;
151
+ }
152
+ /** Controls how queued steering messages are drained. */
153
+ set steeringMode(mode) {
154
+ this.steeringQueue.mode = mode;
155
+ }
156
+ get steeringMode() {
157
+ return this.steeringQueue.mode;
158
+ }
159
+ /** Controls how queued follow-up messages are drained. */
160
+ set followUpMode(mode) {
161
+ this.followUpQueue.mode = mode;
162
+ }
163
+ get followUpMode() {
164
+ return this.followUpQueue.mode;
165
+ }
166
+ /** Queue a message to be injected after the current assistant turn finishes. */
167
+ steer(message) {
168
+ this.steeringQueue.enqueue(message);
169
+ }
170
+ /** Queue a message to run only after the agent would otherwise stop. */
171
+ followUp(message) {
172
+ this.followUpQueue.enqueue(message);
173
+ }
174
+ /** Remove all queued steering messages. */
175
+ clearSteeringQueue() {
176
+ this.steeringQueue.clear();
177
+ }
178
+ /** Remove all queued follow-up messages. */
179
+ clearFollowUpQueue() {
180
+ this.followUpQueue.clear();
181
+ }
182
+ /** Remove all queued steering and follow-up messages. */
183
+ clearAllQueues() {
184
+ this.clearSteeringQueue();
185
+ this.clearFollowUpQueue();
186
+ }
187
+ /** Returns true when either queue still contains pending messages. */
188
+ hasQueuedMessages() {
189
+ return this.steeringQueue.hasItems() || this.followUpQueue.hasItems();
190
+ }
191
+ /** Active abort signal for the current run, if any. */
192
+ get signal() {
193
+ return this.activeRun?.abortController.signal;
194
+ }
195
+ /** Abort the current run, if one is active. */
196
+ abort() {
197
+ this.activeRun?.abortController.abort();
198
+ }
199
+ /**
200
+ * Resolve when the current run and all awaited event listeners have finished.
201
+ *
202
+ * This resolves after `agent_end` listeners settle.
203
+ */
204
+ waitForIdle() {
205
+ return this.activeRun?.promise ?? Promise.resolve();
206
+ }
207
+ /** Clear transcript state, runtime state, and queued messages. */
208
+ reset() {
209
+ this._state.messages = [];
210
+ this._state.isStreaming = false;
211
+ this._state.streamingMessage = undefined;
212
+ this._state.pendingToolCalls = new Set();
213
+ this._state.errorMessage = undefined;
214
+ this.clearFollowUpQueue();
215
+ this.clearSteeringQueue();
216
+ }
217
+ async prompt(input, images) {
218
+ if (this.activeRun) {
219
+ throw new Error("Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.");
220
+ }
221
+ const messages = this.normalizePromptInput(input, images);
222
+ await this.runPromptMessages(messages);
223
+ }
224
+ /** Continue from the current transcript. The last message must be a user or tool-result message. */
225
+ async continue() {
226
+ if (this.activeRun) {
227
+ throw new Error("Agent is already processing. Wait for completion before continuing.");
228
+ }
229
+ const lastMessage = this._state.messages[this._state.messages.length - 1];
230
+ if (!lastMessage) {
231
+ throw new Error("No messages to continue from");
232
+ }
233
+ if (lastMessage.role === "assistant") {
234
+ const queuedSteering = this.steeringQueue.drain();
235
+ if (queuedSteering.length > 0) {
236
+ await this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });
237
+ return;
238
+ }
239
+ const queuedFollowUps = this.followUpQueue.drain();
240
+ if (queuedFollowUps.length > 0) {
241
+ await this.runPromptMessages(queuedFollowUps);
242
+ return;
243
+ }
244
+ throw new Error("Cannot continue from message role: assistant");
245
+ }
246
+ await this.runContinuation();
247
+ }
248
+ normalizePromptInput(input, images) {
249
+ if (Array.isArray(input)) {
250
+ return input;
251
+ }
252
+ if (typeof input !== "string") {
253
+ return [input];
254
+ }
255
+ const content = [{ type: "text", text: input }];
256
+ if (images && images.length > 0) {
257
+ content.push(...images);
258
+ }
259
+ return [{ role: "user", content, timestamp: Date.now() }];
260
+ }
261
+ async runPromptMessages(messages, options = {}) {
262
+ await this.runWithLifecycle(async (signal) => {
263
+ await runAgentLoop(messages, this.createContextSnapshot(), this.createLoopConfig(options), (event) => this.processEvents(event), signal, this.streamFn);
264
+ });
265
+ }
266
+ async runContinuation() {
267
+ await this.runWithLifecycle(async (signal) => {
268
+ await runAgentLoopContinue(this.createContextSnapshot(), this.createLoopConfig(), (event) => this.processEvents(event), signal, this.streamFn);
269
+ });
270
+ }
271
+ createContextSnapshot() {
272
+ return {
273
+ systemPrompt: this._state.systemPrompt,
274
+ messages: this._state.messages.slice(),
275
+ tools: this._state.tools.slice(),
276
+ };
277
+ }
278
+ createLoopConfig(options = {}) {
279
+ let skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;
280
+ return {
281
+ model: this._state.model,
282
+ reasoning: this._state.thinkingLevel === "off" ? undefined : this._state.thinkingLevel,
283
+ sessionId: this.sessionId,
284
+ onPayload: this.onPayload,
285
+ onResponse: this.onResponse,
286
+ transport: this.transport,
287
+ thinkingBudgets: this.thinkingBudgets,
288
+ maxRetryDelayMs: this.maxRetryDelayMs,
289
+ toolExecution: this.toolExecution,
290
+ beforeToolCall: this.beforeToolCall,
291
+ afterToolCall: this.afterToolCall,
292
+ prepareNextTurn: this.prepareNextTurn ? async () => await this.prepareNextTurn?.(this.signal) : undefined,
293
+ convertToLlm: this.convertToLlm,
294
+ transformContext: this.transformContext,
295
+ getApiKey: this.getApiKey,
296
+ getSteeringMessages: async () => {
297
+ if (skipInitialSteeringPoll) {
298
+ skipInitialSteeringPoll = false;
299
+ return [];
300
+ }
301
+ return this.steeringQueue.drain();
302
+ },
303
+ getFollowUpMessages: async () => this.followUpQueue.drain(),
304
+ };
305
+ }
306
+ async runWithLifecycle(executor) {
307
+ if (this.activeRun) {
308
+ throw new Error("Agent is already processing.");
309
+ }
310
+ const abortController = new AbortController();
311
+ let resolvePromise = () => { };
312
+ const promise = new Promise((resolve) => {
313
+ resolvePromise = resolve;
314
+ });
315
+ this.activeRun = { promise, resolve: resolvePromise, abortController };
316
+ this._state.isStreaming = true;
317
+ this._state.streamingMessage = undefined;
318
+ this._state.errorMessage = undefined;
319
+ try {
320
+ await executor(abortController.signal);
321
+ }
322
+ catch (error) {
323
+ await this.handleRunFailure(error, abortController.signal.aborted);
324
+ }
325
+ finally {
326
+ this.finishRun();
327
+ }
328
+ }
329
+ async handleRunFailure(error, aborted) {
330
+ const failureMessage = {
331
+ role: "assistant",
332
+ content: [{ type: "text", text: "" }],
333
+ api: this._state.model.api,
334
+ provider: this._state.model.provider,
335
+ model: this._state.model.id,
336
+ usage: EMPTY_USAGE,
337
+ stopReason: aborted ? "aborted" : "error",
338
+ errorMessage: error instanceof Error ? error.message : String(error),
339
+ timestamp: Date.now(),
340
+ };
341
+ await this.processEvents({ type: "message_start", message: failureMessage });
342
+ await this.processEvents({ type: "message_end", message: failureMessage });
343
+ await this.processEvents({ type: "turn_end", message: failureMessage, toolResults: [] });
344
+ await this.processEvents({ type: "agent_end", messages: [failureMessage] });
345
+ }
346
+ finishRun() {
347
+ this._state.isStreaming = false;
348
+ this._state.streamingMessage = undefined;
349
+ this._state.pendingToolCalls = new Set();
350
+ this.activeRun?.resolve();
351
+ this.activeRun = undefined;
352
+ }
353
+ /**
354
+ * Reduce internal state for a loop event, then await listeners.
355
+ *
356
+ * `agent_end` only means no further loop events will be emitted. The run is
357
+ * considered idle later, after all awaited listeners for `agent_end` finish
358
+ * and `finishRun()` clears runtime-owned state.
359
+ */
360
+ async processEvents(event) {
361
+ switch (event.type) {
362
+ case "message_start":
363
+ this._state.streamingMessage = event.message;
364
+ break;
365
+ case "message_update":
366
+ this._state.streamingMessage = event.message;
367
+ break;
368
+ case "message_end":
369
+ this._state.streamingMessage = undefined;
370
+ this._state.messages.push(event.message);
371
+ break;
372
+ case "tool_execution_start": {
373
+ const pendingToolCalls = new Set(this._state.pendingToolCalls);
374
+ pendingToolCalls.add(event.toolCallId);
375
+ this._state.pendingToolCalls = pendingToolCalls;
376
+ break;
377
+ }
378
+ case "tool_execution_end": {
379
+ const pendingToolCalls = new Set(this._state.pendingToolCalls);
380
+ pendingToolCalls.delete(event.toolCallId);
381
+ this._state.pendingToolCalls = pendingToolCalls;
382
+ break;
383
+ }
384
+ case "turn_end":
385
+ if (event.message.role === "assistant" && event.message.errorMessage) {
386
+ this._state.errorMessage = event.message.errorMessage;
387
+ }
388
+ break;
389
+ case "agent_end":
390
+ this._state.streamingMessage = undefined;
391
+ break;
392
+ }
393
+ const signal = this.activeRun?.abortController.signal;
394
+ if (!signal) {
395
+ throw new Error("Agent listener invoked outside active run");
396
+ }
397
+ for (const listener of this.listeners) {
398
+ await listener(event, signal);
399
+ }
400
+ }
401
+ }