jinzd-ai-cli 0.4.190 → 0.4.193

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
@@ -15,11 +15,12 @@ import {
15
15
  saveDevState,
16
16
  sessionHasMeaningfulContent,
17
17
  setupProxy
18
- } from "./chunk-JATZIZJV.js";
18
+ } from "./chunk-MI23I43Y.js";
19
19
  import {
20
20
  ToolExecutor,
21
21
  ToolRegistry,
22
22
  askUserContext,
23
+ cleanupRejectedTeeFile,
23
24
  estimateTokens,
24
25
  googleSearchContext,
25
26
  initTheme,
@@ -34,10 +35,10 @@ import {
34
35
  spawnAgentContext,
35
36
  theme,
36
37
  undoStack
37
- } from "./chunk-5CLH6XAW.js";
38
+ } from "./chunk-SI5EO3NJ.js";
38
39
  import "./chunk-T2NL5ZIA.js";
39
40
  import "./chunk-BXP6YZ2P.js";
40
- import "./chunk-NV6W7TZW.js";
41
+ import "./chunk-7PX3ZX4G.js";
41
42
  import {
42
43
  SessionManager,
43
44
  getContentText
@@ -54,10 +55,18 @@ import {
54
55
  getConfigDirUsage,
55
56
  listRecentCrashes,
56
57
  writeCrashLog
57
- } from "./chunk-UVW3WLSV.js";
58
+ } from "./chunk-PO3ZY3PN.js";
59
+ import {
60
+ ProviderRegistry
61
+ } from "./chunk-BGXQGCEO.js";
62
+ import {
63
+ getStatsSnapshot,
64
+ getTopFailingTools,
65
+ getTopUsedTools,
66
+ installFlushOnExit
67
+ } from "./chunk-SHI5UFUH.js";
58
68
  import {
59
69
  CONTENT_ONLY_STREAM_REMINDER,
60
- ProviderRegistry,
61
70
  TEE_FINAL_USER_NUDGE,
62
71
  TOOL_CALL_REMINDER,
63
72
  ThinkTagFilter,
@@ -65,24 +74,15 @@ import {
65
74
  buildRoundBudgetHint,
66
75
  buildWriteRoundReminder,
67
76
  consumeToolCallStream,
68
- detectMetaNarration,
69
- detectPseudoToolCalls,
77
+ evaluateTeeContent,
70
78
  extractWrittenFilePaths,
71
- looksLikeDocumentBody,
72
79
  runAgentLoop,
73
- stripPseudoToolCalls,
74
- stripToolCallReminder
75
- } from "./chunk-IQ7JE43O.js";
76
- import {
77
- getStatsSnapshot,
78
- getTopFailingTools,
79
- getTopUsedTools,
80
- installFlushOnExit
81
- } from "./chunk-KHS7RSGR.js";
82
- import "./chunk-HIU2SH4V.js";
80
+ stripToolCallReminder,
81
+ teeStreamErrorSummary
82
+ } from "./chunk-JOR572WG.js";
83
83
  import {
84
84
  ConfigManager
85
- } from "./chunk-X4J2DZB5.js";
85
+ } from "./chunk-VQT27CZK.js";
86
86
  import {
87
87
  AuthError,
88
88
  ProviderError,
@@ -109,7 +109,7 @@ import {
109
109
  SKILLS_DIR_NAME,
110
110
  VERSION,
111
111
  buildUserIdentityPrompt
112
- } from "./chunk-4KMDKDAK.js";
112
+ } from "./chunk-P3PTUSP4.js";
113
113
  import {
114
114
  formatGitContextForPrompt,
115
115
  getGitContext,
@@ -138,7 +138,7 @@ import { program } from "commander";
138
138
 
139
139
  // src/repl/repl.ts
140
140
  import * as readline from "readline";
141
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
141
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
142
142
  import { join as join4, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
143
143
  import chalk4 from "chalk";
144
144
 
@@ -1824,7 +1824,7 @@ No tools match "${filter}".
1824
1824
  const { join: join5 } = await import("path");
1825
1825
  const { existsSync: existsSync5 } = await import("fs");
1826
1826
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1827
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-4QBBHLU4.js");
1827
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-LEU54VQQ.js");
1828
1828
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1829
1829
  const cwd = process.cwd();
1830
1830
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2885,7 +2885,7 @@ ${hint}` : "")
2885
2885
  usage: "/test [command|filter]",
2886
2886
  async execute(args, ctx) {
2887
2887
  try {
2888
- const { executeTests } = await import("./run-tests-7ZUNEUEX.js");
2888
+ const { executeTests } = await import("./run-tests-IL4342SV.js");
2889
2889
  const argStr = args.join(" ").trim();
2890
2890
  let testArgs = {};
2891
2891
  if (argStr) {
@@ -6813,10 +6813,7 @@ Tip: You can continue the conversation by asking the AI to proceed.`
6813
6813
  genUsage = teeResult.usage;
6814
6814
  teeTokShown = teeResult.tokensShown;
6815
6815
  } catch (teeErr) {
6816
- try {
6817
- unlinkSync2(saveToFile);
6818
- } catch {
6819
- }
6816
+ cleanupRejectedTeeFile(saveToFile);
6820
6817
  const errMsg = teeErr instanceof Error ? teeErr.message : String(teeErr);
6821
6818
  process.stdout.write(theme.error(
6822
6819
  `
@@ -6827,113 +6824,69 @@ Tip: You can continue the conversation by asking the AI to proceed.`
6827
6824
  ));
6828
6825
  const errorResults = toolCalls.map((tc) => ({
6829
6826
  callId: tc.id,
6830
- 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]`,
6827
+ content: tc.name === "save_last_response" ? teeStreamErrorSummary(saveToFile, errMsg) : `[skipped: save_last_response failed]`,
6831
6828
  isError: tc.name === "save_last_response"
6832
6829
  }));
6833
- const newMsgs2 = provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent);
6834
- extraMessages.push(...newMsgs2);
6830
+ const newMsgs = provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent);
6831
+ extraMessages.push(...newMsgs);
6835
6832
  return "continue";
6836
6833
  }
6837
- const metaMatch = detectMetaNarration(genContent);
6838
- if (metaMatch) {
6839
- try {
6840
- unlinkSync2(saveToFile);
6841
- } catch {
6842
- }
6834
+ if (genUsage) accumulateUsage(usage, genUsage);
6835
+ const verdict = evaluateTeeContent(genContent, saveToFile, lastResponseStore.content);
6836
+ if (verdict.kind === "reject") {
6837
+ cleanupRejectedTeeFile(saveToFile);
6838
+ const label = verdict.reason === "meta-narration" ? "meta-narration / leaked reasoning, not document body" : "pseudo-tool-call markup with no usable document body";
6843
6839
  process.stdout.write(theme.error(
6844
6840
  `
6845
- \u2717 Rejected save: response was meta-narration / leaked reasoning, not document body (matched: ${metaMatch})
6841
+ \u2717 Rejected save: response was ${label} (matched: ${verdict.matched})
6846
6842
  ${saveToFile} was deleted; asking model to retry.
6847
6843
 
6848
6844
  `
6849
6845
  ));
6850
6846
  const errorResults = toolCalls.map((tc) => ({
6851
6847
  callId: tc.id,
6852
- 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.
6853
-
6854
- 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]`,
6848
+ content: tc.name === "save_last_response" ? verdict.summary : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6855
6849
  isError: tc.name === "save_last_response"
6856
6850
  }));
6857
- const newMsgs2 = provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent);
6858
- extraMessages.push(...newMsgs2);
6859
- if (genUsage) accumulateUsage(usage, genUsage);
6851
+ extraMessages.push(...provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent));
6860
6852
  return "continue";
6861
6853
  }
6862
- const pseudoMatch = detectPseudoToolCalls(genContent);
6863
- if (pseudoMatch) {
6864
- const cleaned = stripPseudoToolCalls(genContent);
6865
- if (looksLikeDocumentBody(cleaned)) {
6866
- try {
6867
- writeFileSync2(saveToFile, cleaned, "utf-8");
6868
- process.stdout.write(theme.warning(
6869
- `
6870
- \u26A0 Salvaged save: stripped pseudo-tool-call markup (matched: ${pseudoMatch})
6871
- ${saveToFile} now contains the cleaned document (${cleaned.length} chars; was ${genContent.length}).
6872
-
6873
- `
6874
- ));
6875
- lastResponseStore.content = cleaned;
6876
- if (genUsage) accumulateUsage(usage, genUsage);
6877
- session.addMessage({ role: "assistant", content: cleaned, timestamp: /* @__PURE__ */ new Date() });
6878
- this.events.emit("message.after", { content: cleaned });
6879
- const lines2 = cleaned.split("\n").length;
6880
- const bytes2 = Buffer.byteLength(cleaned, "utf-8");
6881
- const okResults = toolCalls.map((tc) => ({
6882
- callId: tc.id,
6883
- content: tc.name === "save_last_response" ? `File saved (with cleanup): ${saveToFile} (${lines2} lines, ${bytes2} bytes; pseudo-tool-call markup was stripped before save)` : `[skipped: file already saved by tee streaming]`,
6884
- isError: false
6885
- }));
6886
- const newMsgs3 = provider.buildToolResultMessages(toolCalls, okResults, reasoningContent);
6887
- extraMessages.push(...newMsgs3);
6888
- if (usage.inputTokens > 0 || usage.outputTokens > 0) {
6889
- this.addSessionUsage(usage, effectiveModel);
6890
- session.addTokenUsage(usage);
6891
- if (teeShowTokens && !teeTokShown) {
6892
- this.renderer.renderUsage(usage, this.sessionTokenUsage);
6893
- }
6894
- }
6895
- return "stop";
6896
- } catch (writeErr) {
6897
- process.stderr.write(`[tee] salvage write failed: ${writeErr.message ?? writeErr}
6898
- `);
6899
- }
6900
- }
6854
+ if (verdict.kind === "salvaged" || verdict.kind === "fallback") {
6901
6855
  try {
6902
- unlinkSync2(saveToFile);
6903
- } catch {
6904
- }
6905
- process.stdout.write(theme.error(
6906
- `
6907
- \u2717 Rejected save: response was pseudo-tool-call markup with no usable document body (matched: ${pseudoMatch})
6908
- ${saveToFile} was deleted; asking model to retry.
6856
+ writeFileSync2(saveToFile, verdict.content, "utf-8");
6857
+ const banner = verdict.kind === "fallback" ? `
6858
+ \u26A0 Fallback save: fresh stream had no usable body (matched: ${verdict.matched})
6859
+ ${saveToFile} now holds the response you previously saw (${verdict.content.length} chars).
6909
6860
 
6910
- `
6911
- ));
6912
- const errorResults = toolCalls.map((tc) => ({
6913
- callId: tc.id,
6914
- content: tc.name === "save_last_response" ? `[save_last_response REJECTED] Your output was tool-call XML/JSON with no document body. ${saveToFile} was NOT saved.
6861
+ ` : `
6862
+ \u26A0 Salvaged save: stripped pseudo-tool-call markup (matched: ${verdict.matched})
6863
+ ${saveToFile} now contains the cleaned document (${verdict.content.length} chars; was ${genContent.length}).
6915
6864
 
6916
- This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting <tool_call>, <function_calls>, <invoke>, <think>, or JSON tool blocks. Produce the document body NOW: start with a markdown heading like "# \u5BA1\u8BA1\u62A5\u544A" and write the full report.` : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6917
- isError: tc.name === "save_last_response"
6918
- }));
6919
- const newMsgs2 = provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent);
6920
- extraMessages.push(...newMsgs2);
6921
- if (genUsage) accumulateUsage(usage, genUsage);
6922
- return "continue";
6865
+ `;
6866
+ process.stdout.write(theme.warning(banner));
6867
+ } catch (writeErr) {
6868
+ cleanupRejectedTeeFile(saveToFile);
6869
+ process.stderr.write(`[tee] salvage write failed: ${writeErr.message ?? writeErr}
6870
+ `);
6871
+ const errorResults = toolCalls.map((tc) => ({
6872
+ callId: tc.id,
6873
+ content: tc.name === "save_last_response" ? `[save_last_response failed] could not write the cleaned document to ${saveToFile}: ${writeErr.message ?? writeErr}. Retry.` : `[skipped: save_last_response failed]`,
6874
+ isError: tc.name === "save_last_response"
6875
+ }));
6876
+ extraMessages.push(...provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent));
6877
+ return "continue";
6878
+ }
6923
6879
  }
6924
- lastResponseStore.content = genContent;
6925
- if (genUsage) accumulateUsage(usage, genUsage);
6926
- session.addMessage({ role: "assistant", content: genContent, timestamp: /* @__PURE__ */ new Date() });
6927
- this.events.emit("message.after", { content: genContent });
6928
- const lines = genContent.split("\n").length;
6929
- const bytes = Buffer.byteLength(genContent, "utf-8");
6930
- const syntheticResults = toolCalls.map((tc) => ({
6880
+ const finalContent = verdict.content;
6881
+ lastResponseStore.content = finalContent;
6882
+ session.addMessage({ role: "assistant", content: finalContent, timestamp: /* @__PURE__ */ new Date() });
6883
+ this.events.emit("message.after", { content: finalContent });
6884
+ const okResults = toolCalls.map((tc) => ({
6931
6885
  callId: tc.id,
6932
- content: tc.name === "save_last_response" ? `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)` : `[skipped: file already saved by tee streaming]`,
6886
+ content: tc.name === "save_last_response" ? verdict.summary : `[skipped: file already saved by tee streaming]`,
6933
6887
  isError: false
6934
6888
  }));
6935
- const newMsgs = provider.buildToolResultMessages(toolCalls, syntheticResults, reasoningContent);
6936
- extraMessages.push(...newMsgs);
6889
+ extraMessages.push(...provider.buildToolResultMessages(toolCalls, okResults, reasoningContent));
6937
6890
  if (usage.inputTokens > 0 || usage.outputTokens > 0) {
6938
6891
  this.addSessionUsage(usage, effectiveModel);
6939
6892
  session.addTokenUsage(usage);
@@ -7256,7 +7209,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7256
7209
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7257
7210
  process.exit(1);
7258
7211
  }
7259
- const { startWebServer } = await import("./server-WMLZOLD5.js");
7212
+ const { startWebServer } = await import("./server-KE7X3BHW.js");
7260
7213
  await startWebServer({ port, host: options.host });
7261
7214
  });
7262
7215
  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) => {
@@ -7423,16 +7376,16 @@ program.command("sessions").description("List recent conversation sessions").opt
7423
7376
  console.log(footer + "\n");
7424
7377
  });
7425
7378
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
7426
- const { runUsageCli } = await import("./usage-5LMWDGZ4.js");
7379
+ const { runUsageCli } = await import("./usage-CG7PJ3AB.js");
7427
7380
  await runUsageCli(options);
7428
7381
  });
7429
7382
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7430
- const { runDoctorCli } = await import("./doctor-cli-EUOCY7VN.js");
7383
+ const { runDoctorCli } = await import("./doctor-cli-4RKSIH7N.js");
7431
7384
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7432
7385
  });
7433
7386
  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) => {
7434
7387
  try {
7435
- const batch = await import("./batch-HF5RWUBL.js");
7388
+ const batch = await import("./batch-M6367YB4.js");
7436
7389
  switch (action) {
7437
7390
  case "submit":
7438
7391
  if (!arg) {
@@ -7475,7 +7428,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7475
7428
  }
7476
7429
  });
7477
7430
  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) => {
7478
- const { startMcpServer } = await import("./server-YMCGJOXV.js");
7431
+ const { startMcpServer } = await import("./server-Y7TCCIGA.js");
7479
7432
  await startMcpServer({
7480
7433
  allowDestructive: !!options.allowDestructive,
7481
7434
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7484,7 +7437,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7484
7437
  });
7485
7438
  });
7486
7439
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7487
- const { runCi } = await import("./ci-X24WFUDF.js");
7440
+ const { runCi } = await import("./ci-4MJ4EGKQ.js");
7488
7441
  const result = await runCi({
7489
7442
  pr: options.pr,
7490
7443
  base: options.base,
@@ -7630,7 +7583,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7630
7583
  }),
7631
7584
  config.get("customProviders")
7632
7585
  );
7633
- const { startHub } = await import("./hub-YW3KLBZM.js");
7586
+ const { startHub } = await import("./hub-XMUI5HMK.js");
7634
7587
  await startHub(
7635
7588
  {
7636
7589
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-ZN5IEPSS.js";
4
+ } from "./chunk-IFETB4PY.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-NV6W7TZW.js";
6
- import "./chunk-4KMDKDAK.js";
5
+ } from "./chunk-7PX3ZX4G.js";
6
+ import "./chunk-P3PTUSP4.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -19,12 +19,13 @@ import {
19
19
  loadDevState,
20
20
  persistToolRound,
21
21
  setupProxy
22
- } from "./chunk-JATZIZJV.js";
22
+ } from "./chunk-MI23I43Y.js";
23
23
  import {
24
24
  ToolExecutor,
25
25
  ToolRegistry,
26
26
  askUserContext,
27
27
  checkPermission,
28
+ cleanupRejectedTeeFile,
28
29
  estimateTokens,
29
30
  googleSearchContext,
30
31
  groupCallsByPhase,
@@ -37,10 +38,10 @@ import {
37
38
  spawnAgentContext,
38
39
  truncateOutput,
39
40
  undoStack
40
- } from "./chunk-5CLH6XAW.js";
41
+ } from "./chunk-SI5EO3NJ.js";
41
42
  import "./chunk-T2NL5ZIA.js";
42
43
  import "./chunk-BXP6YZ2P.js";
43
- import "./chunk-NV6W7TZW.js";
44
+ import "./chunk-7PX3ZX4G.js";
44
45
  import {
45
46
  SessionManager,
46
47
  getContentText
@@ -49,29 +50,27 @@ import {
49
50
  computeCost,
50
51
  formatCost
51
52
  } from "./chunk-V37XOYOE.js";
53
+ import {
54
+ ProviderRegistry
55
+ } from "./chunk-BGXQGCEO.js";
56
+ import {
57
+ runTool
58
+ } from "./chunk-SHI5UFUH.js";
52
59
  import {
53
60
  CONTENT_ONLY_STREAM_REMINDER,
54
- ProviderRegistry,
55
61
  TEE_FINAL_USER_NUDGE,
56
62
  TOOL_CALL_REMINDER,
57
63
  buildRoundBudgetHint,
58
64
  consumeToolCallStream,
59
- detectMetaNarration,
60
- detectPseudoToolCalls,
61
- looksLikeDocumentBody,
65
+ evaluateTeeContent,
66
+ getDangerLevel,
62
67
  runAgentLoop,
63
- stripPseudoToolCalls,
64
- stripToolCallReminder
65
- } from "./chunk-IQ7JE43O.js";
66
- import {
67
- runTool
68
- } from "./chunk-KHS7RSGR.js";
69
- import {
70
- getDangerLevel
71
- } from "./chunk-HIU2SH4V.js";
68
+ stripToolCallReminder,
69
+ teeStreamErrorSummary
70
+ } from "./chunk-JOR572WG.js";
72
71
  import {
73
72
  ConfigManager
74
- } from "./chunk-X4J2DZB5.js";
73
+ } from "./chunk-VQT27CZK.js";
75
74
  import "./chunk-TZQHYZKT.js";
76
75
  import {
77
76
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -91,7 +90,7 @@ import {
91
90
  SKILLS_DIR_NAME,
92
91
  VERSION,
93
92
  buildUserIdentityPrompt
94
- } from "./chunk-4KMDKDAK.js";
93
+ } from "./chunk-P3PTUSP4.js";
95
94
  import {
96
95
  formatGitContextForPrompt,
97
96
  getGitContext,
@@ -514,9 +513,23 @@ function loadMemoryContent(configDir) {
514
513
  }
515
514
 
516
515
  // src/web/session-handler.ts
517
- import { existsSync as existsSync3, readFileSync as readFileSync3, appendFileSync, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream, unlinkSync } from "fs";
516
+ import { existsSync as existsSync3, readFileSync as readFileSync3, appendFileSync, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
518
517
  import { join as join2, resolve, dirname } from "path";
519
518
  import { execSync } from "child_process";
519
+ function lastAssistantText(messages) {
520
+ for (let i = messages.length - 1; i >= 0; i--) {
521
+ const m = messages[i];
522
+ if (!m || m.role !== "assistant") continue;
523
+ const c = m.content;
524
+ if (typeof c === "string") return c;
525
+ if (Array.isArray(c)) {
526
+ const text = c.filter((p) => p && p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
527
+ if (text) return text;
528
+ }
529
+ return void 0;
530
+ }
531
+ return void 0;
532
+ }
520
533
  var SessionHandler = class _SessionHandler {
521
534
  ws;
522
535
  config;
@@ -1326,53 +1339,18 @@ ${summaryContent}`,
1326
1339
  await new Promise((resolve3, reject) => {
1327
1340
  fileStream.end((err) => err ? reject(err) : resolve3());
1328
1341
  });
1329
- const metaMatch = detectMetaNarration(fullContent);
1330
- if (metaMatch) {
1331
- try {
1332
- unlinkSync(saveToFile);
1333
- } catch {
1334
- }
1342
+ const verdict = evaluateTeeContent(fullContent, saveToFile, lastAssistantText(apiMessages));
1343
+ if (verdict.kind === "reject") {
1344
+ cleanupRejectedTeeFile(saveToFile);
1335
1345
  isError = true;
1336
- 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.
1337
-
1338
- 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.`;
1339
- if (teeUsage) {
1340
- roundUsage.inputTokens += teeUsage.inputTokens;
1341
- roundUsage.outputTokens += teeUsage.outputTokens;
1342
- roundUsage.cacheCreationTokens += teeUsage.cacheCreationTokens ?? 0;
1343
- roundUsage.cacheReadTokens += teeUsage.cacheReadTokens ?? 0;
1344
- }
1345
- this.send({
1346
- type: "tool_call_result",
1347
- callId: call.id,
1348
- toolName: call.name,
1349
- content: summary,
1350
- isError: true
1351
- });
1352
- return { content: "", summary, isError: true };
1353
- }
1354
- const pseudoMatch = detectPseudoToolCalls(fullContent);
1355
- if (pseudoMatch) {
1356
- const cleaned = stripPseudoToolCalls(fullContent);
1357
- if (looksLikeDocumentBody(cleaned)) {
1358
- writeFileSync(saveToFile, cleaned, "utf-8");
1359
- fullContent = cleaned;
1360
- const lines = cleaned.split("\n").length;
1361
- const bytes = Buffer.byteLength(cleaned, "utf-8");
1362
- summary = `File saved (with cleanup): ${saveToFile} (${lines} lines, ${bytes} bytes; pseudo-tool-call markup matching ${pseudoMatch} was stripped before save)`;
1363
- undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
1364
- } else {
1365
- try {
1366
- unlinkSync(saveToFile);
1367
- } catch {
1368
- }
1369
- isError = true;
1370
- summary = `[save_last_response REJECTED] Your output was tool-call markup with no usable document body (matched: ${pseudoMatch}). ${saveToFile} was NOT saved. This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting <tool_call>, <function_calls>, <invoke>, <think>, or JSON tool blocks. Produce the document body NOW: start with a markdown heading and write the full content.`;
1371
- }
1346
+ summary = verdict.summary;
1347
+ fullContent = "";
1372
1348
  } else {
1373
- const lines = fullContent.split("\n").length;
1374
- const bytes = Buffer.byteLength(fullContent, "utf-8");
1375
- summary = `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`;
1349
+ if (verdict.kind === "salvaged" || verdict.kind === "fallback") {
1350
+ writeFileSync(saveToFile, verdict.content, "utf-8");
1351
+ fullContent = verdict.content;
1352
+ }
1353
+ summary = verdict.summary;
1376
1354
  undoStack.push(saveToFile, `save_last_response: ${saveToFile}`);
1377
1355
  }
1378
1356
  if (teeUsage) {
@@ -1388,13 +1366,10 @@ This fresh stream has NO tools. Produce ONLY the document body: start with a mar
1388
1366
  } catch {
1389
1367
  }
1390
1368
  }
1391
- try {
1392
- unlinkSync(saveToFile);
1393
- } catch {
1394
- }
1369
+ cleanupRejectedTeeFile(saveToFile);
1395
1370
  isError = true;
1396
1371
  const msg = err instanceof Error ? err.message : String(err);
1397
- 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).`;
1372
+ summary = teeStreamErrorSummary(saveToFile, msg);
1398
1373
  }
1399
1374
  this.send({
1400
1375
  type: "tool_call_result",
@@ -2437,7 +2412,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2437
2412
  case "test": {
2438
2413
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2439
2414
  try {
2440
- const { executeTests } = await import("./run-tests-7ZUNEUEX.js");
2415
+ const { executeTests } = await import("./run-tests-IL4342SV.js");
2441
2416
  const argStr = args.join(" ").trim();
2442
2417
  let testArgs = {};
2443
2418
  if (argStr) {
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-5CLH6XAW.js";
4
+ } from "./chunk-SI5EO3NJ.js";
5
5
  import "./chunk-T2NL5ZIA.js";
6
6
  import "./chunk-BXP6YZ2P.js";
7
- import "./chunk-NV6W7TZW.js";
7
+ import "./chunk-7PX3ZX4G.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-KHS7RSGR.js";
10
+ } from "./chunk-SHI5UFUH.js";
11
11
  import {
12
12
  getDangerLevel,
13
13
  schemaToJsonSchema
14
- } from "./chunk-HIU2SH4V.js";
14
+ } from "./chunk-JOR572WG.js";
15
15
  import "./chunk-TZQHYZKT.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-4KMDKDAK.js";
18
+ } from "./chunk-P3PTUSP4.js";
19
19
  import "./chunk-4BKXL7SM.js";
20
20
  import "./chunk-TB4W4Y4T.js";
21
21
  import "./chunk-KHYD3WXE.js";