min-agent 0.5.0 → 0.6.0

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 (156) hide show
  1. package/README.md +32 -18
  2. package/dist/cli.js +538 -12
  3. package/docs/API.md +25 -8
  4. package/package.json +6 -6
  5. package/skills/self-config/SKILL.md +7 -3
  6. package/skills/self-config/reference.md +8 -4
  7. package/dist/agent.js +0 -1342
  8. package/dist/assistant-stream.js +0 -131
  9. package/dist/cli/commands/chat.js +0 -13
  10. package/dist/cli/commands/exec.js +0 -35
  11. package/dist/cli/commands/history.js +0 -58
  12. package/dist/cli/commands/index.js +0 -241
  13. package/dist/cli/commands/init.js +0 -18
  14. package/dist/cli/commands/mcp.js +0 -173
  15. package/dist/cli/commands/memory.js +0 -87
  16. package/dist/cli/commands/models.js +0 -21
  17. package/dist/cli/commands/permission.js +0 -12
  18. package/dist/cli/commands/rules.js +0 -33
  19. package/dist/cli/commands/sandbox.js +0 -13
  20. package/dist/cli/commands/serve.js +0 -9
  21. package/dist/cli/commands/setup.js +0 -4
  22. package/dist/cli/commands/shared.js +0 -16
  23. package/dist/cli/commands/skills.js +0 -119
  24. package/dist/cli/commands/think.js +0 -12
  25. package/dist/cli/commands/update.js +0 -7
  26. package/dist/cli/commands/write-config.js +0 -52
  27. package/dist/cli/errors.js +0 -36
  28. package/dist/cli/exec-prompt.js +0 -26
  29. package/dist/cli/option-helpers.js +0 -65
  30. package/dist/cli/program.js +0 -217
  31. package/dist/clipboard.js +0 -142
  32. package/dist/code-mode.js +0 -184
  33. package/dist/compaction.js +0 -644
  34. package/dist/config.js +0 -470
  35. package/dist/confirm.js +0 -172
  36. package/dist/context-window.js +0 -246
  37. package/dist/doom-loop.js +0 -278
  38. package/dist/fetch-timeout.js +0 -152
  39. package/dist/http-approvals.js +0 -60
  40. package/dist/http.js +0 -119
  41. package/dist/instructions.js +0 -196
  42. package/dist/logger.js +0 -95
  43. package/dist/markdown.js +0 -259
  44. package/dist/mcp.js +0 -1036
  45. package/dist/memory-cli.js +0 -33
  46. package/dist/memory.js +0 -295
  47. package/dist/model-catalog.js +0 -285
  48. package/dist/output.js +0 -76
  49. package/dist/paste-handler.js +0 -41
  50. package/dist/permission-cli.js +0 -40
  51. package/dist/plugins.js +0 -131
  52. package/dist/pricing.js +0 -119
  53. package/dist/provider.js +0 -63
  54. package/dist/question-format.js +0 -60
  55. package/dist/reasoning-stream.js +0 -158
  56. package/dist/sandbox-cli.js +0 -79
  57. package/dist/sandbox.js +0 -403
  58. package/dist/save-throttle.js +0 -45
  59. package/dist/scope.js +0 -23
  60. package/dist/serve/common.js +0 -425
  61. package/dist/serve/routes-chat.js +0 -367
  62. package/dist/serve/routes-mcp.js +0 -212
  63. package/dist/serve/routes-memory.js +0 -95
  64. package/dist/serve/routes-meta.js +0 -233
  65. package/dist/serve/routes-sessions.js +0 -61
  66. package/dist/serve/routes-skills.js +0 -70
  67. package/dist/serve.js +0 -138
  68. package/dist/sessions.js +0 -265
  69. package/dist/skills.js +0 -599
  70. package/dist/structured-output.js +0 -29
  71. package/dist/synthetic.js +0 -7
  72. package/dist/think-cli.js +0 -36
  73. package/dist/thinking-wire.js +0 -228
  74. package/dist/thinking.js +0 -142
  75. package/dist/title-gen.js +0 -55
  76. package/dist/token-display.js +0 -39
  77. package/dist/tool-display.js +0 -178
  78. package/dist/tool-output.js +0 -126
  79. package/dist/tools/apply_patch.js +0 -265
  80. package/dist/tools/atomic-file.js +0 -35
  81. package/dist/tools/backend.js +0 -61
  82. package/dist/tools/bash.js +0 -231
  83. package/dist/tools/code_search.js +0 -98
  84. package/dist/tools/edit.js +0 -68
  85. package/dist/tools/explore.js +0 -232
  86. package/dist/tools/glob.js +0 -40
  87. package/dist/tools/grep.js +0 -174
  88. package/dist/tools/index.js +0 -34
  89. package/dist/tools/question.js +0 -54
  90. package/dist/tools/read.js +0 -109
  91. package/dist/tools/search-searxng.js +0 -223
  92. package/dist/tools/search-serper.js +0 -189
  93. package/dist/tools/task.js +0 -165
  94. package/dist/tools/todo.js +0 -213
  95. package/dist/tools/web_fetch.js +0 -195
  96. package/dist/tools/web_search.js +0 -228
  97. package/dist/tools/write.js +0 -55
  98. package/dist/tui/App.js +0 -126
  99. package/dist/tui/ConfirmBar.js +0 -66
  100. package/dist/tui/InputBar.js +0 -616
  101. package/dist/tui/MessageList.js +0 -718
  102. package/dist/tui/ModelPicker.js +0 -113
  103. package/dist/tui/QuestionBar.js +0 -136
  104. package/dist/tui/SessionPicker.js +0 -79
  105. package/dist/tui/Spinner.js +0 -6
  106. package/dist/tui/StatusBar.js +0 -22
  107. package/dist/tui/ThinkPicker.js +0 -77
  108. package/dist/tui/agent-runner.js +0 -223
  109. package/dist/tui/bracketed-paste.js +0 -37
  110. package/dist/tui/caret-pos.js +0 -179
  111. package/dist/tui/caret.js +0 -69
  112. package/dist/tui/click-count.js +0 -13
  113. package/dist/tui/diff-view.js +0 -61
  114. package/dist/tui/drag-state.js +0 -49
  115. package/dist/tui/hydrate.js +0 -129
  116. package/dist/tui/index.js +0 -258
  117. package/dist/tui/input-history.js +0 -125
  118. package/dist/tui/layout.js +0 -105
  119. package/dist/tui/mouse.js +0 -46
  120. package/dist/tui/overlay-input.js +0 -12
  121. package/dist/tui/paste-draft.js +0 -173
  122. package/dist/tui/prompt-queue.js +0 -24
  123. package/dist/tui/selection.js +0 -232
  124. package/dist/tui/session-switch.js +0 -28
  125. package/dist/tui/slash-commands.js +0 -123
  126. package/dist/tui/slash-handler.js +0 -589
  127. package/dist/tui/text-width.js +0 -113
  128. package/dist/tui/theme.js +0 -12
  129. package/dist/tui/token-info.js +0 -7
  130. package/dist/tui/tool-children.js +0 -19
  131. package/dist/tui/types.js +0 -1
  132. package/dist/tui/undo-stack.js +0 -14
  133. package/dist/tui/use-sgr-mouse.js +0 -29
  134. package/dist/tui-chat.js +0 -456
  135. package/dist/updater.js +0 -116
  136. package/dist/xml-search.js +0 -194
  137. package/docs/superpowers/plans/2026-08-16-batch1-tui-improvements.md +0 -1510
  138. package/docs/superpowers/plans/2026-08-16-batch2-cli-tools-api.md +0 -2105
  139. package/docs/superpowers/plans/2026-08-16-batch3-config-engineering.md +0 -1595
  140. package/docs/superpowers/plans/2026-08-16-input-caret.md +0 -782
  141. package/docs/superpowers/plans/2026-08-20-tui-completeness.md +0 -873
  142. package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +0 -631
  143. package/docs/superpowers/plans/2026-08-23-input-paste-attachments.md +0 -475
  144. package/docs/superpowers/plans/2026-08-23-thinking-wire-profile.md +0 -450
  145. package/docs/superpowers/specs/2026-08-16-batch1-tui-improvements-design.md +0 -183
  146. package/docs/superpowers/specs/2026-08-16-batch2-cli-tools-api-design.md +0 -220
  147. package/docs/superpowers/specs/2026-08-16-batch3-config-engineering-design.md +0 -196
  148. package/docs/superpowers/specs/2026-08-16-input-caret-design.md +0 -63
  149. package/docs/superpowers/specs/2026-08-17-mouse-selection-design.md +0 -116
  150. package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +0 -47
  151. package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +0 -37
  152. package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +0 -68
  153. package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +0 -273
  154. package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +0 -165
  155. package/docs/superpowers/specs/2026-08-23-input-paste-attachments-design.md +0 -174
  156. package/docs/superpowers/specs/2026-08-23-thinking-wire-profile-design.md +0 -140
@@ -1,425 +0,0 @@
1
- import { randomUUID } from "crypto";
2
- import { getSandboxSnapshot, getPermissionSnapshot, permissionModeLabel, parseThinkingEffort, parseMemoryMode, } from "../config.js";
3
- import { parseNetworkPolicy, parseSandboxMode, runWithSandboxPolicy } from "../sandbox.js";
4
- import { runWithConfirmContext, getPermissionOverride } from "../confirm.js";
5
- import { thinkingPayload } from "../thinking.js";
6
- import { memoryPayload } from "../memory.js";
7
- import { runOnceWithSystem } from "../agent.js";
8
- import { saveSession, sanitizeForStorage } from "../sessions.js";
9
- import { createSaveThrottle } from "../save-throttle.js";
10
- import { scanProject, buildCodeSystemPrompt } from "../code-mode.js";
11
- import { TokenTracker } from "../compaction.js";
12
- import { emptyTaskState } from "../tools/todo.js";
13
- export { localVersion as packageVersion } from "../updater.js";
14
- const MAX_BODY_BYTES = 2 * 1024 * 1024;
15
- const BODY_TIMEOUT_MS = 30_000;
16
- const MAX_TOOL_RESULT_SSE_CHARS = 48_000;
17
- export const MAX_CONCURRENT_CHATS = 8;
18
- export function corsEnabled() {
19
- const v = process.env.MIN_AGENT_SERVE_CORS;
20
- return v === "1" || v === "true";
21
- }
22
- export function corsHeaders() {
23
- if (corsEnabled()) {
24
- return {
25
- "Access-Control-Allow-Origin": "*",
26
- "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
27
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
28
- };
29
- }
30
- return {};
31
- }
32
- export function skillPayload(s) {
33
- return {
34
- name: s.name,
35
- description: s.description,
36
- location: s.location,
37
- enabled: s.enabled,
38
- ...(s.disabledScope ? { disabled_scope: s.disabledScope } : {}),
39
- ...(s.version ? { version: s.version } : {}),
40
- ...(s.allowedTools.length ? { allowed_tools: s.allowedTools } : {}),
41
- ...(s.requiredBins.length ? { requires_bins: s.requiredBins } : {}),
42
- ...(s.builtin ? { builtin: true } : {}),
43
- ...(s.alwaysLoad ? { always_load: true } : {}),
44
- };
45
- }
46
- export function sendJson(res, status, body) {
47
- const headers = {
48
- "Content-Type": "application/json; charset=utf-8",
49
- ...corsHeaders(),
50
- };
51
- res.writeHead(status, headers);
52
- res.end(JSON.stringify(body));
53
- }
54
- /** Decode a URL path segment without letting malformed escapes become a 500. */
55
- export function decodePathSegment(raw) {
56
- try {
57
- return decodeURIComponent(raw);
58
- }
59
- catch {
60
- return null;
61
- }
62
- }
63
- function readBody(req) {
64
- return new Promise((resolve, reject) => {
65
- const chunks = [];
66
- let total = 0;
67
- const timer = setTimeout(() => {
68
- req.destroy();
69
- reject(new Error("body_timeout"));
70
- }, BODY_TIMEOUT_MS);
71
- req.on("data", (c) => {
72
- total += c.length;
73
- if (total > MAX_BODY_BYTES) {
74
- clearTimeout(timer);
75
- req.removeAllListeners("data");
76
- req.pause();
77
- reject(new Error("body_too_large"));
78
- return;
79
- }
80
- chunks.push(c);
81
- });
82
- req.on("end", () => {
83
- clearTimeout(timer);
84
- resolve(Buffer.concat(chunks).toString("utf-8"));
85
- });
86
- req.on("error", (e) => {
87
- clearTimeout(timer);
88
- reject(e);
89
- });
90
- });
91
- }
92
- export async function readJsonBody(req) {
93
- let raw;
94
- try {
95
- raw = await readBody(req);
96
- }
97
- catch (e) {
98
- if (e?.message === "body_too_large") {
99
- return { ok: false, status: 413, error: "payload_too_large" };
100
- }
101
- if (e?.message === "body_timeout") {
102
- return { ok: false, status: 408, error: "payload_timeout" };
103
- }
104
- throw e;
105
- }
106
- try {
107
- return { ok: true, value: JSON.parse(raw) };
108
- }
109
- catch {
110
- return { ok: false, status: 400, error: "invalid_json" };
111
- }
112
- }
113
- export function sendJsonError(res, e) {
114
- if (e.status === 413) {
115
- // The request body was abandoned mid-stream; drop keep-alive so the
116
- // connection cannot be reused for a second request.
117
- res.shouldKeepAlive = false;
118
- }
119
- sendJson(res, e.status, e.status === 413 ? { error: e.error, max_bytes: MAX_BODY_BYTES } : { error: e.error });
120
- }
121
- function sseWrite(res, obj) {
122
- try {
123
- if (res.writableEnded)
124
- return;
125
- res.write(`data: ${JSON.stringify(obj)}\n\n`);
126
- }
127
- catch {
128
- /* client gone */
129
- }
130
- }
131
- function truncateForJson(v, max) {
132
- if (typeof v === "string" && v.length > max)
133
- return v.slice(0, max) + `\n… [truncated ${v.length - max} chars]`;
134
- return v;
135
- }
136
- function sseHeaders(cors) {
137
- return {
138
- "Content-Type": "text/event-stream; charset=utf-8",
139
- "Cache-Control": "no-cache, no-transform",
140
- Connection: "keep-alive",
141
- "X-Accel-Buffering": "no",
142
- ...cors,
143
- };
144
- }
145
- function createSseCallbacks(res, messages, opts = {}) {
146
- return {
147
- onAssistantDisplayDelta(delta) {
148
- sseWrite(res, { type: "assistant", text: delta });
149
- },
150
- onThinkingDelta(delta) {
151
- sseWrite(res, { type: "thinking", text: delta });
152
- },
153
- onToolCall(name, input) {
154
- sseWrite(res, { type: "tool_call", name, input: truncateForJson(input, MAX_TOOL_RESULT_SSE_CHARS) });
155
- },
156
- onToolResult(name, output) {
157
- sseWrite(res, { type: "tool_result", name, output: truncateForJson(output, MAX_TOOL_RESULT_SSE_CHARS) });
158
- },
159
- onCompaction(line) {
160
- sseWrite(res, { type: "compaction", line });
161
- },
162
- onRetryNotice(info) {
163
- sseWrite(res, {
164
- type: "notice",
165
- kind: info.kind,
166
- attempt: info.attempt,
167
- max_attempts: info.maxAttempts,
168
- delay_ms: info.delayMs,
169
- });
170
- },
171
- onStreamError(message) {
172
- sseWrite(res, { type: "error", message });
173
- },
174
- onRunFinish(info) {
175
- let saved;
176
- if (opts.sessionId && messages.length > 0) {
177
- try {
178
- saved = saveSession(messages, opts.sessionId, undefined, undefined, opts.taskState);
179
- }
180
- catch {
181
- saved = undefined;
182
- }
183
- }
184
- sseWrite(res, {
185
- type: "done",
186
- step_count: info.stepCount,
187
- usage: info.usage,
188
- context_tokens: info.contextTokens ?? 0,
189
- has_error: info.hasError,
190
- aborted: info.aborted,
191
- max_steps_reached: info.maxStepsReached,
192
- incomplete: info.incomplete ?? false,
193
- budget_exceeded: info.budgetExceeded ?? false,
194
- continues: info.continues ?? 0,
195
- stopped: info.stopped ?? false,
196
- empty_response: info.emptyResponse ?? false,
197
- empty_attempts: info.emptyAttempts ?? 0,
198
- wrap_up: info.wrapUp ?? false,
199
- session_id: saved,
200
- messages: sanitizeForStorage(messages),
201
- task_state: opts.taskState,
202
- project: opts.project,
203
- });
204
- if (!res.writableEnded)
205
- res.end();
206
- },
207
- };
208
- }
209
- function createJsonCallbacks() {
210
- const toolCalls = [];
211
- const toolResults = [];
212
- let finishInfo = null;
213
- const callbacks = {
214
- onToolCall(name, input) {
215
- toolCalls.push({ name, input: truncateForJson(input, MAX_TOOL_RESULT_SSE_CHARS) });
216
- },
217
- onToolResult(name, output) {
218
- toolResults.push({ name, output: truncateForJson(output, MAX_TOOL_RESULT_SSE_CHARS) });
219
- },
220
- onRunFinish(info) {
221
- finishInfo = info;
222
- },
223
- };
224
- return { callbacks, toolCalls, toolResults, finishInfo: () => finishInfo };
225
- }
226
- export function readSandboxTighten(body) {
227
- const policy = {};
228
- if (body.sandbox !== undefined) {
229
- const mode = parseSandboxMode(body.sandbox);
230
- if (!mode)
231
- return { ok: false, error: "invalid_sandbox", detail: "sandbox must be off, workspace, or strict" };
232
- policy.mode = mode;
233
- }
234
- if (body.network !== undefined) {
235
- const network = parseNetworkPolicy(body.network);
236
- if (!network)
237
- return { ok: false, error: "invalid_network", detail: "network must be allow or deny" };
238
- policy.network = network;
239
- }
240
- return { ok: true, policy };
241
- }
242
- export function readStringPathList(value) {
243
- if (value === undefined)
244
- return { ok: true };
245
- if (!Array.isArray(value) || value.some((p) => typeof p !== "string" || p.length === 0))
246
- return { ok: false };
247
- return { ok: true, value };
248
- }
249
- export function sandboxPayload() {
250
- const snap = getSandboxSnapshot();
251
- return {
252
- mode: snap.mode,
253
- network: snap.network,
254
- extraWriteRoots: snap.extraWriteRoots,
255
- extraReadRoots: snap.extraReadRoots,
256
- source: snap.source,
257
- label: snap.label,
258
- };
259
- }
260
- export function permissionPayload() {
261
- const override = getPermissionOverride();
262
- const snap = getPermissionSnapshot();
263
- const permission = override ?? snap.permission;
264
- const source = override ? "cli" : snap.source;
265
- return {
266
- permission,
267
- source,
268
- label: permissionModeLabel(permission),
269
- };
270
- }
271
- export { thinkingPayload, memoryPayload };
272
- export function readThinking(body) {
273
- if (body.thinking === undefined)
274
- return { ok: true };
275
- const thinking = parseThinkingEffort(body.thinking);
276
- if (!thinking)
277
- return { ok: false, error: "invalid_thinking", detail: "thinking must be off, low, medium, high, or max" };
278
- return { ok: true, thinking };
279
- }
280
- export function readMemory(body) {
281
- if (body.memory === undefined)
282
- return { ok: true };
283
- const memory = parseMemoryMode(body.memory);
284
- if (!memory)
285
- return { ok: false, error: "invalid_memory", detail: "memory must be on or off" };
286
- return { ok: true, memory };
287
- }
288
- export function normalizeMessages(body) {
289
- if (body.messages && Array.isArray(body.messages)) {
290
- if (body.messages.length === 0)
291
- return { ok: false, error: "messages must be non-empty" };
292
- return { ok: true, messages: [...body.messages] };
293
- }
294
- if (typeof body.message === "string" && body.message.length > 0) {
295
- return { ok: true, messages: [{ role: "user", content: body.message }] };
296
- }
297
- return { ok: false, error: "Provide `message` (string) or non-empty `messages` array" };
298
- }
299
- export function chatRunOptions(body) {
300
- return {
301
- temperature: typeof body.temperature === "number" ? body.temperature : undefined,
302
- maxTokens: typeof body.maxTokens === "number" ? body.maxTokens : undefined,
303
- topP: typeof body.topP === "number" ? body.topP : undefined,
304
- thinking: parseThinkingEffort(body.thinking),
305
- memory: parseMemoryMode(body.memory),
306
- providerName: typeof body.provider === "string" ? body.provider : undefined,
307
- ...(body.plan_mode === true ? { planMode: true } : {}),
308
- };
309
- }
310
- function httpConfirmContext(ctx, res, autoApprove, pendingIds) {
311
- if (autoApprove)
312
- return { autoApprove: true };
313
- return {
314
- autoApprove: false,
315
- onConfirm: async (message) => {
316
- const id = randomUUID();
317
- pendingIds.push(id);
318
- sseWrite(res, { type: "confirm", id, message });
319
- return ctx.approvalGate.waitConfirm(id);
320
- },
321
- onQuestion: async (prompt, options) => {
322
- const id = randomUUID();
323
- pendingIds.push(id);
324
- sseWrite(res, { type: "question", id, prompt, ...(options && options.length > 0 ? { options } : {}) });
325
- return ctx.approvalGate.waitQuestion(id);
326
- },
327
- };
328
- }
329
- export async function executeChat(ctx, opts) {
330
- const { res, messages, modelId, stream, sessionId, runOptions, autoApprove } = opts;
331
- const taskState = opts.taskState ?? emptyTaskState();
332
- if (!autoApprove && !stream) {
333
- sendJson(res, 400, {
334
- error: "auto_approve_requires_stream",
335
- detail: "Set stream: true when auto_approve is false, then answer via POST /v1/approvals and POST /v1/answers",
336
- });
337
- return;
338
- }
339
- const abort = new AbortController();
340
- const pendingIds = [];
341
- const onClose = () => {
342
- abort.abort();
343
- for (const id of pendingIds)
344
- ctx.approvalGate.cancel(id);
345
- };
346
- res.on("close", onClose);
347
- const tracker = new TokenTracker();
348
- const project = scanProject();
349
- const codeSystemPrompt = buildCodeSystemPrompt(project, ctx.instructions);
350
- const confirmCtx = httpConfirmContext(ctx, res, autoApprove, pendingIds);
351
- const tighten = opts.sandboxTighten;
352
- const run = (callbacks) => {
353
- // Persist mid-run so a dropped connection or crash keeps the work so far.
354
- const saveThrottle = sessionId != null
355
- ? createSaveThrottle(() => {
356
- try {
357
- saveSession(messages, sessionId, undefined, undefined, taskState);
358
- }
359
- catch { }
360
- })
361
- : null;
362
- const exec = () => runWithConfirmContext(confirmCtx, () => runOnceWithSystem(messages, codeSystemPrompt, modelId, abort.signal, callbacks, tracker, {
363
- ...runOptions,
364
- taskState,
365
- ...(saveThrottle ? { onStepPersist: () => saveThrottle.request() } : {}),
366
- }));
367
- const finish = (p) => p.finally(() => {
368
- saveThrottle?.cancel();
369
- });
370
- if (tighten && (tighten.mode || tighten.network))
371
- return finish(runWithSandboxPolicy(tighten, exec));
372
- return finish(exec());
373
- };
374
- if (stream) {
375
- res.writeHead(200, sseHeaders(corsHeaders()));
376
- res.flushHeaders?.();
377
- const callbacks = createSseCallbacks(res, messages, { sessionId, project, taskState });
378
- try {
379
- await run(callbacks);
380
- }
381
- catch (err) {
382
- const message = err instanceof Error ? err.message : String(err);
383
- if (!res.writableEnded) {
384
- sseWrite(res, { type: "fatal", message });
385
- res.end();
386
- }
387
- }
388
- return;
389
- }
390
- const { callbacks, toolCalls, toolResults, finishInfo } = createJsonCallbacks();
391
- await run(callbacks);
392
- const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
393
- let savedSession;
394
- if (sessionId && messages.length > 0) {
395
- try {
396
- savedSession = saveSession(messages, sessionId, undefined, undefined, taskState);
397
- }
398
- catch {
399
- savedSession = undefined;
400
- }
401
- }
402
- const fi = finishInfo();
403
- sendJson(res, 200, {
404
- messages: sanitizeForStorage(messages),
405
- assistant: lastAssistant ?? null,
406
- tool_calls: toolCalls,
407
- tool_results: toolResults,
408
- session_id: savedSession,
409
- step_count: fi?.stepCount ?? 0,
410
- usage: fi?.usage ?? null,
411
- context_tokens: fi?.contextTokens ?? 0,
412
- has_error: fi?.hasError ?? false,
413
- aborted: fi?.aborted ?? false,
414
- max_steps_reached: fi?.maxStepsReached ?? false,
415
- incomplete: fi?.incomplete ?? false,
416
- budget_exceeded: fi?.budgetExceeded ?? false,
417
- continues: fi?.continues ?? 0,
418
- stopped: fi?.stopped ?? false,
419
- empty_response: fi?.emptyResponse ?? false,
420
- empty_attempts: fi?.emptyAttempts ?? 0,
421
- wrap_up: fi?.wrapUp ?? false,
422
- task_state: taskState,
423
- project,
424
- });
425
- }