jinzd-ai-cli 0.4.113 → 0.4.115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  buildWriteRoundReminder,
15
15
  clearDevState,
16
16
  computeCost,
17
+ detectMetaNarration,
17
18
  detectPseudoToolCalls,
18
19
  detectsHallucinatedFileOp,
19
20
  extractWrittenFilePaths,
@@ -31,10 +32,10 @@ import {
31
32
  setupProxy,
32
33
  stripPseudoToolCalls,
33
34
  stripToolCallReminder
34
- } from "./chunk-TFYDLG7E.js";
35
+ } from "./chunk-FMPWML3F.js";
35
36
  import {
36
37
  ConfigManager
37
- } from "./chunk-WLZ2PWQV.js";
38
+ } from "./chunk-UZLNS3QG.js";
38
39
  import {
39
40
  ToolExecutor,
40
41
  ToolRegistry,
@@ -53,10 +54,10 @@ import {
53
54
  spawnAgentContext,
54
55
  theme,
55
56
  undoStack
56
- } from "./chunk-3GUNDGUV.js";
57
+ } from "./chunk-TJGRPTJS.js";
57
58
  import "./chunk-3BICTI5M.js";
58
59
  import "./chunk-2DXY7UGF.js";
59
- import "./chunk-SIDKPVRD.js";
60
+ import "./chunk-PEMNYHIS.js";
60
61
  import "./chunk-2ZD3YTVM.js";
61
62
  import {
62
63
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -79,7 +80,7 @@ import {
79
80
  SKILLS_DIR_NAME,
80
81
  VERSION,
81
82
  buildUserIdentityPrompt
82
- } from "./chunk-SRU5SYZI.js";
83
+ } from "./chunk-OHUHYWBR.js";
83
84
  import {
84
85
  formatGitContextForPrompt,
85
86
  getGitContext,
@@ -411,6 +412,7 @@ var Renderer = class {
411
412
  buf = "";
412
413
  };
413
414
  let interrupted = false;
415
+ let streamErr = null;
414
416
  try {
415
417
  for await (const chunk of stream) {
416
418
  if (options?.signal?.aborted) {
@@ -435,41 +437,51 @@ var Renderer = class {
435
437
  interrupted = true;
436
438
  if (!inThinking) flushBuf();
437
439
  } else {
438
- throw err;
440
+ streamErr = err;
439
441
  }
440
442
  }
441
443
  if (interrupted) {
442
444
  process.stdout.write(theme.dim(" [interrupted]\n"));
443
445
  }
444
446
  let tokensShown = false;
445
- if (options?.showTokens) {
446
- process.stdout.write("\n");
447
- if (usage) {
448
- const sessionTotal = options.sessionTotal;
449
- const updatedTotal = sessionTotal ? { inputTokens: sessionTotal.inputTokens + usage.inputTokens, outputTokens: sessionTotal.outputTokens + usage.outputTokens } : void 0;
450
- this.renderUsage(usage, updatedTotal);
451
- tokensShown = true;
452
- } else if (fullContent.length > 0) {
453
- const est = Math.ceil(fullContent.length / 2.5);
454
- process.stdout.write(theme.dim(`\u{1F4CA} ~${est.toLocaleString()} output tokens (estimated)
447
+ if (!streamErr) {
448
+ if (options?.showTokens) {
449
+ process.stdout.write("\n");
450
+ if (usage) {
451
+ const sessionTotal = options.sessionTotal;
452
+ const updatedTotal = sessionTotal ? { inputTokens: sessionTotal.inputTokens + usage.inputTokens, outputTokens: sessionTotal.outputTokens + usage.outputTokens } : void 0;
453
+ this.renderUsage(usage, updatedTotal);
454
+ tokensShown = true;
455
+ } else if (fullContent.length > 0) {
456
+ const est = Math.ceil(fullContent.length / 2.5);
457
+ process.stdout.write(theme.dim(`\u{1F4CA} ~${est.toLocaleString()} output tokens (estimated)
455
458
 
456
459
  `));
457
- tokensShown = true;
460
+ tokensShown = true;
461
+ } else {
462
+ process.stdout.write("\n");
463
+ }
458
464
  } else {
459
- process.stdout.write("\n");
465
+ process.stdout.write("\n\n");
460
466
  }
461
467
  } else {
462
- process.stdout.write("\n\n");
468
+ process.stdout.write("\n");
463
469
  }
464
470
  if (fileStream) {
465
- await new Promise((resolve3, reject) => {
466
- fileStream.end((err) => err ? reject(err) : resolve3());
467
- });
468
- const kb = (Buffer.byteLength(fullContent, "utf-8") / 1024).toFixed(1);
469
- process.stdout.write(theme.success(` \u2705 Saved: ${options.saveToFile} (${kb} KB)
471
+ try {
472
+ await new Promise((resolve3) => {
473
+ fileStream.end(() => resolve3());
474
+ });
475
+ } catch {
476
+ }
477
+ if (!streamErr) {
478
+ const kb = (Buffer.byteLength(fullContent, "utf-8") / 1024).toFixed(1);
479
+ process.stdout.write(theme.success(` \u2705 Saved: ${options.saveToFile} (${kb} KB)
470
480
 
471
481
  `));
482
+ }
472
483
  }
484
+ if (streamErr) throw streamErr;
473
485
  return { content: fullContent, usage, tokensShown };
474
486
  }
475
487
  renderResponse(content) {
@@ -1600,7 +1612,7 @@ ${text}
1600
1612
  const { join: join6 } = await import("path");
1601
1613
  const { existsSync: existsSync6 } = await import("fs");
1602
1614
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1603
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-2Z7YP252.js");
1615
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-Y6LRE5TI.js");
1604
1616
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1605
1617
  const cwd = process.cwd();
1606
1618
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2650,7 +2662,7 @@ ${hint}` : "")
2650
2662
  usage: "/test [command|filter]",
2651
2663
  async execute(args, ctx) {
2652
2664
  try {
2653
- const { executeTests } = await import("./run-tests-BUDXHVNF.js");
2665
+ const { executeTests } = await import("./run-tests-TWE7TJ4T.js");
2654
2666
  const argStr = args.join(" ").trim();
2655
2667
  let testArgs = {};
2656
2668
  if (argStr) {
@@ -6260,10 +6272,71 @@ ${mcpBudgetNote}` : "");
6260
6272
  _extraMessages: teeExtraMessages
6261
6273
  });
6262
6274
  const teeShowTokens = this.shouldShowTokens();
6263
- const { content: genContent, usage: genUsage, tokensShown: teeTokShown } = await this.renderer.renderStream(
6264
- genStream,
6265
- { saveToFile, showTokens: teeShowTokens, sessionTotal: teeShowTokens ? { ...this.sessionTokenUsage } : void 0, signal: teeAc.signal }
6266
- );
6275
+ let genContent;
6276
+ let genUsage;
6277
+ let teeTokShown = false;
6278
+ try {
6279
+ const teeResult = await this.renderer.renderStream(
6280
+ genStream,
6281
+ { saveToFile, showTokens: teeShowTokens, sessionTotal: teeShowTokens ? { ...this.sessionTokenUsage } : void 0, signal: teeAc.signal }
6282
+ );
6283
+ genContent = teeResult.content;
6284
+ genUsage = teeResult.usage;
6285
+ teeTokShown = teeResult.tokensShown;
6286
+ } catch (teeErr) {
6287
+ try {
6288
+ unlinkSync2(saveToFile);
6289
+ } catch {
6290
+ }
6291
+ const errMsg = teeErr instanceof Error ? teeErr.message : String(teeErr);
6292
+ process.stdout.write(theme.error(
6293
+ `
6294
+ \u2717 tee stream failed: ${errMsg}
6295
+ ${saveToFile} (partial) was deleted. Asking model to retry.
6296
+
6297
+ `
6298
+ ));
6299
+ const errorResults = result.toolCalls.map((tc) => ({
6300
+ callId: tc.id,
6301
+ content: tc.name === "save_last_response" ? `[save_last_response failed] streaming was interrupted: ${errMsg}. ${saveToFile} was NOT saved. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).` : `[skipped: save_last_response failed]`,
6302
+ isError: tc.name === "save_last_response"
6303
+ }));
6304
+ const reasoningContent3 = "reasoningContent" in result ? result.reasoningContent : void 0;
6305
+ const newMsgs3 = provider.buildToolResultMessages(result.toolCalls, errorResults, reasoningContent3);
6306
+ extraMessages.push(...newMsgs3);
6307
+ continue;
6308
+ }
6309
+ const metaMatch = detectMetaNarration(genContent);
6310
+ if (metaMatch) {
6311
+ try {
6312
+ unlinkSync2(saveToFile);
6313
+ } catch {
6314
+ }
6315
+ process.stdout.write(theme.error(
6316
+ `
6317
+ \u2717 Rejected save: response was meta-narration / leaked reasoning, not document body (matched: ${metaMatch})
6318
+ ${saveToFile} was deleted; asking model to retry.
6319
+
6320
+ `
6321
+ ));
6322
+ const errorResults = result.toolCalls.map((tc) => ({
6323
+ callId: tc.id,
6324
+ content: tc.name === "save_last_response" ? `[save_last_response REJECTED] Your output was internal reasoning / meta-narration about the task (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body. ${saveToFile} was NOT saved.
6325
+
6326
+ This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading like "# \u5BA1\u8BA1\u62A5\u544A" / "# Audit Report" and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.` : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6327
+ isError: tc.name === "save_last_response"
6328
+ }));
6329
+ const reasoningContent3 = "reasoningContent" in result ? result.reasoningContent : void 0;
6330
+ const newMsgs3 = provider.buildToolResultMessages(result.toolCalls, errorResults, reasoningContent3);
6331
+ extraMessages.push(...newMsgs3);
6332
+ if (genUsage) {
6333
+ roundUsage.inputTokens += genUsage.inputTokens;
6334
+ roundUsage.outputTokens += genUsage.outputTokens;
6335
+ roundUsage.cacheCreationTokens += genUsage.cacheCreationTokens ?? 0;
6336
+ roundUsage.cacheReadTokens += genUsage.cacheReadTokens ?? 0;
6337
+ }
6338
+ continue;
6339
+ }
6267
6340
  const pseudoMatch = detectPseudoToolCalls(genContent);
6268
6341
  if (pseudoMatch) {
6269
6342
  const cleaned = stripPseudoToolCalls(genContent);
@@ -6882,11 +6955,11 @@ program.command("web").description("Start Web UI server with browser-based chat
6882
6955
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6883
6956
  process.exit(1);
6884
6957
  }
6885
- const { startWebServer } = await import("./server-MJGOR6FU.js");
6958
+ const { startWebServer } = await import("./server-RODHACCH.js");
6886
6959
  await startWebServer({ port, host: options.host });
6887
6960
  });
6888
- program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
6889
- const { AuthManager } = await import("./auth-SC6KHHI3.js");
6961
+ program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
6962
+ const { AuthManager } = await import("./auth-7KK5BOCA.js");
6890
6963
  const config = new ConfigManager();
6891
6964
  const auth = new AuthManager(config.getConfigDir());
6892
6965
  if (!action || action === "list") {
@@ -6961,6 +7034,19 @@ ${users.length} user(s) registered (auth enabled):
6961
7034
  console.log(`\u2713 Password reset for '${username}'.`);
6962
7035
  return;
6963
7036
  }
7037
+ if (action === "logout-all") {
7038
+ if (!username) {
7039
+ console.error("Usage: aicli user logout-all <username>");
7040
+ process.exit(1);
7041
+ }
7042
+ const ok = auth.logoutAll(username);
7043
+ if (!ok) {
7044
+ console.error(`Error: User '${username}' not found.`);
7045
+ process.exit(1);
7046
+ }
7047
+ console.log(`\u2713 All outstanding sessions for '${username}' revoked.`);
7048
+ return;
7049
+ }
6964
7050
  if (action === "migrate") {
6965
7051
  if (!username) {
6966
7052
  console.error("Usage: aicli user migrate <username>");
@@ -6980,7 +7066,7 @@ ${users.length} user(s) registered (auth enabled):
6980
7066
  return;
6981
7067
  }
6982
7068
  console.error(`Unknown action: ${action}`);
6983
- console.error("Available: list, create, delete, reset-password, migrate");
7069
+ console.error("Available: list, create, delete, reset-password, logout-all, migrate");
6984
7070
  process.exit(1);
6985
7071
  });
6986
7072
  program.command("sessions").description("List recent conversation sessions").action(async () => {
@@ -7005,7 +7091,7 @@ program.command("sessions").description("List recent conversation sessions").act
7005
7091
  });
7006
7092
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
7007
7093
  try {
7008
- const batch = await import("./batch-NF26OYWB.js");
7094
+ const batch = await import("./batch-SHNIUSW2.js");
7009
7095
  switch (action) {
7010
7096
  case "submit":
7011
7097
  if (!arg) {
@@ -7048,7 +7134,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7048
7134
  }
7049
7135
  });
7050
7136
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7051
- const { startMcpServer } = await import("./server-ABNZXOV2.js");
7137
+ const { startMcpServer } = await import("./server-3P5BYK74.js");
7052
7138
  await startMcpServer({
7053
7139
  allowDestructive: !!options.allowDestructive,
7054
7140
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7175,7 +7261,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7175
7261
  }),
7176
7262
  config.get("customProviders")
7177
7263
  );
7178
- const { startHub } = await import("./hub-KRNH76Y3.js");
7264
+ const { startHub } = await import("./hub-E3WMJGYK.js");
7179
7265
  await startHub(
7180
7266
  {
7181
7267
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-2WAEM5B6.js";
4
+ } from "./chunk-KQZU2VS5.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-SIDKPVRD.js";
6
- import "./chunk-SRU5SYZI.js";
5
+ } from "./chunk-PEMNYHIS.js";
6
+ import "./chunk-OHUHYWBR.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -3,14 +3,14 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-3GUNDGUV.js";
6
+ } from "./chunk-TJGRPTJS.js";
7
7
  import "./chunk-3BICTI5M.js";
8
8
  import "./chunk-2DXY7UGF.js";
9
- import "./chunk-SIDKPVRD.js";
9
+ import "./chunk-PEMNYHIS.js";
10
10
  import "./chunk-2ZD3YTVM.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-SRU5SYZI.js";
13
+ } from "./chunk-OHUHYWBR.js";
14
14
  import "./chunk-4BKXL7SM.js";
15
15
  import "./chunk-7ZJN4KLV.js";
16
16
  import "./chunk-KHYD3WXE.js";
@@ -23,6 +23,14 @@ import { createInterface } from "readline";
23
23
  import { resolve } from "path";
24
24
  import { realpathSync } from "fs";
25
25
  var STDIN_TOOLS = /* @__PURE__ */ new Set(["ask_user", "spawn_agent"]);
26
+ function looksLikePath(s) {
27
+ if (s.startsWith("/")) return true;
28
+ if (s.startsWith("\\\\")) return true;
29
+ if (/^[a-zA-Z]:[\\/]/.test(s)) return true;
30
+ if (s.startsWith("../") || s.startsWith("..\\")) return true;
31
+ if (s === "..") return true;
32
+ return false;
33
+ }
26
34
  var MCP_ALWAYS_DESTRUCTIVE = /* @__PURE__ */ new Set([
27
35
  "bash",
28
36
  "run_interactive",
@@ -237,19 +245,30 @@ var McpServer = class {
237
245
  validatePathArgs(toolName, args) {
238
246
  if (this.opts.allowOutsideCwd) return void 0;
239
247
  const keys = TOOL_PATH_ARGS[toolName];
240
- if (!keys) return void 0;
241
- for (const key of keys) {
242
- const value = args[key];
243
- if (value === void 0 || value === null) continue;
244
- const list = Array.isArray(value) ? value : [value];
245
- for (const entry of list) {
246
- if (typeof entry !== "string" || entry.length === 0) continue;
247
- const abs = resolve(this.sandboxRoot, entry);
248
- if (!this.isInsideSandbox(abs)) {
249
- return `Path '${entry}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
248
+ if (keys) {
249
+ for (const key of keys) {
250
+ const value = args[key];
251
+ if (value === void 0 || value === null) continue;
252
+ const list = Array.isArray(value) ? value : [value];
253
+ for (const entry of list) {
254
+ if (typeof entry !== "string" || entry.length === 0) continue;
255
+ const abs = resolve(this.sandboxRoot, entry);
256
+ if (!this.isInsideSandbox(abs)) {
257
+ return `Path '${entry}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
258
+ }
250
259
  }
251
260
  }
252
261
  }
262
+ const knownKeys = new Set(keys ?? []);
263
+ for (const [k, v] of Object.entries(args)) {
264
+ if (knownKeys.has(k)) continue;
265
+ if (typeof v !== "string" || v.length === 0) continue;
266
+ if (!looksLikePath(v)) continue;
267
+ const abs = resolve(this.sandboxRoot, v);
268
+ if (!this.isInsideSandbox(abs)) {
269
+ return `Path-like argument '${k}=${v}' escapes sandbox root '${this.sandboxRoot}'. Pass --allow-outside-cwd to permit.`;
270
+ }
271
+ }
253
272
  return void 0;
254
273
  }
255
274
  isInsideSandbox(abs) {
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- AuthManager
4
- } from "./chunk-BYNY5JPB.js";
3
+ AuthManager,
4
+ TOKEN_EXPIRY_MS
5
+ } from "./chunk-O7NM4WTS.js";
5
6
  import {
6
7
  CONTENT_ONLY_STREAM_REMINDER,
7
8
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -13,6 +14,7 @@ import {
13
14
  TOOL_CALL_REMINDER,
14
15
  autoTrimSessionIfNeeded,
15
16
  computeCost,
17
+ detectMetaNarration,
16
18
  detectPseudoToolCalls,
17
19
  detectsHallucinatedFileOp,
18
20
  formatCost,
@@ -24,10 +26,10 @@ import {
24
26
  setupProxy,
25
27
  stripPseudoToolCalls,
26
28
  stripToolCallReminder
27
- } from "./chunk-TFYDLG7E.js";
29
+ } from "./chunk-FMPWML3F.js";
28
30
  import {
29
31
  ConfigManager
30
- } from "./chunk-WLZ2PWQV.js";
32
+ } from "./chunk-UZLNS3QG.js";
31
33
  import {
32
34
  ToolExecutor,
33
35
  ToolRegistry,
@@ -45,10 +47,10 @@ import {
45
47
  spawnAgentContext,
46
48
  truncateOutput,
47
49
  undoStack
48
- } from "./chunk-3GUNDGUV.js";
50
+ } from "./chunk-TJGRPTJS.js";
49
51
  import "./chunk-3BICTI5M.js";
50
52
  import "./chunk-2DXY7UGF.js";
51
- import "./chunk-SIDKPVRD.js";
53
+ import "./chunk-PEMNYHIS.js";
52
54
  import "./chunk-2ZD3YTVM.js";
53
55
  import {
54
56
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -68,7 +70,7 @@ import {
68
70
  SKILLS_DIR_NAME,
69
71
  VERSION,
70
72
  buildUserIdentityPrompt
71
- } from "./chunk-SRU5SYZI.js";
73
+ } from "./chunk-OHUHYWBR.js";
72
74
  import {
73
75
  formatGitContextForPrompt,
74
76
  getGitContext,
@@ -539,6 +541,7 @@ var SessionHandler = class _SessionHandler {
539
541
  this.mcpManager = shared.mcpManager;
540
542
  this.skillManager = shared.skillManager;
541
543
  this.toolExecutor = new ToolExecutorWeb(shared.toolRegistry, ws);
544
+ this.toolExecutor.setSessionKey(`web-${Math.random().toString(36).slice(2)}-${Date.now()}`);
542
545
  this.currentProvider = this.config.get("defaultProvider");
543
546
  const allDefaultModels = this.config.get("defaultModels");
544
547
  try {
@@ -1293,6 +1296,31 @@ ${summaryResult.content}`,
1293
1296
  await new Promise((resolve3, reject) => {
1294
1297
  fileStream.end((err) => err ? reject(err) : resolve3());
1295
1298
  });
1299
+ const metaMatch = detectMetaNarration(fullContent);
1300
+ if (metaMatch) {
1301
+ try {
1302
+ unlinkSync(saveToFile);
1303
+ } catch {
1304
+ }
1305
+ isError = true;
1306
+ summary = `[save_last_response REJECTED] Your output was internal reasoning / meta-narration (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body (matched: ${metaMatch}). ${saveToFile} was NOT saved.
1307
+
1308
+ This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.`;
1309
+ if (teeUsage) {
1310
+ roundUsage.inputTokens += teeUsage.inputTokens;
1311
+ roundUsage.outputTokens += teeUsage.outputTokens;
1312
+ roundUsage.cacheCreationTokens += teeUsage.cacheCreationTokens ?? 0;
1313
+ roundUsage.cacheReadTokens += teeUsage.cacheReadTokens ?? 0;
1314
+ }
1315
+ this.send({
1316
+ type: "tool_call_result",
1317
+ callId: call.id,
1318
+ result: summary,
1319
+ isError: true,
1320
+ endTime: Date.now()
1321
+ });
1322
+ return { content: "", summary, isError: true };
1323
+ }
1296
1324
  const pseudoMatch = detectPseudoToolCalls(fullContent);
1297
1325
  if (pseudoMatch) {
1298
1326
  const cleaned = stripPseudoToolCalls(fullContent);
@@ -1330,9 +1358,13 @@ ${summaryResult.content}`,
1330
1358
  } catch {
1331
1359
  }
1332
1360
  }
1361
+ try {
1362
+ unlinkSync(saveToFile);
1363
+ } catch {
1364
+ }
1333
1365
  isError = true;
1334
1366
  const msg = err instanceof Error ? err.message : String(err);
1335
- summary = `[save_last_response failed] ${msg}`;
1367
+ summary = `[save_last_response failed] streaming was interrupted: ${msg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
1336
1368
  }
1337
1369
  this.send({
1338
1370
  type: "tool_call_result",
@@ -2411,7 +2443,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2411
2443
  case "test": {
2412
2444
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2413
2445
  try {
2414
- const { executeTests } = await import("./run-tests-BUDXHVNF.js");
2446
+ const { executeTests } = await import("./run-tests-TWE7TJ4T.js");
2415
2447
  const argStr = args.join(" ").trim();
2416
2448
  let testArgs = {};
2417
2449
  if (argStr) {
@@ -3467,6 +3499,27 @@ async function startWebServer(options = {}) {
3467
3499
  skillManager
3468
3500
  };
3469
3501
  const app = express();
3502
+ app.use((_req, res, next) => {
3503
+ res.setHeader(
3504
+ "Content-Security-Policy",
3505
+ [
3506
+ "default-src 'self'",
3507
+ "script-src 'self'",
3508
+ "style-src 'self' 'unsafe-inline'",
3509
+ "img-src 'self' data: blob:",
3510
+ "font-src 'self' data:",
3511
+ "connect-src 'self' ws: wss:",
3512
+ "frame-ancestors 'none'",
3513
+ "base-uri 'self'",
3514
+ "form-action 'self'"
3515
+ ].join("; ")
3516
+ );
3517
+ res.setHeader("X-Content-Type-Options", "nosniff");
3518
+ res.setHeader("Referrer-Policy", "no-referrer");
3519
+ res.setHeader("X-Frame-Options", "DENY");
3520
+ res.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()");
3521
+ next();
3522
+ });
3470
3523
  const server = createServer(app);
3471
3524
  const WS_MAX_PAYLOAD = 1 * 1024 * 1024;
3472
3525
  const WS_MSG_RATE_PER_SEC = 30;
@@ -3551,7 +3604,7 @@ async function startWebServer(options = {}) {
3551
3604
  }
3552
3605
  const token = authManager.login(username, password);
3553
3606
  console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
3554
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
3607
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
3555
3608
  res.json({ success: true, username });
3556
3609
  });
3557
3610
  app.post("/api/auth/login", (req, res) => {
@@ -3565,7 +3618,7 @@ async function startWebServer(options = {}) {
3565
3618
  res.status(401).json({ error: "Invalid username or password" });
3566
3619
  return;
3567
3620
  }
3568
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
3621
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
3569
3622
  res.json({ success: true, username });
3570
3623
  });
3571
3624
  app.post("/api/auth/logout", (_req, res) => {
@@ -4,14 +4,14 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-3GUNDGUV.js";
7
+ } from "./chunk-TJGRPTJS.js";
8
8
  import "./chunk-3BICTI5M.js";
9
9
  import "./chunk-2DXY7UGF.js";
10
- import "./chunk-SIDKPVRD.js";
10
+ import "./chunk-PEMNYHIS.js";
11
11
  import "./chunk-2ZD3YTVM.js";
12
12
  import {
13
13
  SUBAGENT_ALLOWED_TOOLS
14
- } from "./chunk-SRU5SYZI.js";
14
+ } from "./chunk-OHUHYWBR.js";
15
15
  import "./chunk-4BKXL7SM.js";
16
16
  import "./chunk-7ZJN4KLV.js";
17
17
  import "./chunk-KHYD3WXE.js";