jinzd-ai-cli 0.4.287 → 0.4.289

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 (32) hide show
  1. package/dist/{batch-6OH7D5YU.js → batch-GVD4CZZQ.js} +2 -2
  2. package/dist/{chunk-UDFK5Q52.js → chunk-25VQORAZ.js} +1 -1
  3. package/dist/{chunk-5F7BGV72.js → chunk-2SQCGJ6H.js} +1 -1
  4. package/dist/{chunk-AXSWLNCU.js → chunk-AA6PZRJO.js} +104 -127
  5. package/dist/{chunk-L7RAEUUN.js → chunk-BDN6ZCPQ.js} +1 -1
  6. package/dist/{chunk-HNUT4STJ.js → chunk-BW4H53KI.js} +7 -7
  7. package/dist/{chunk-L625B2XV.js → chunk-GXB5EOFI.js} +1 -1
  8. package/dist/{chunk-3BHJKMPQ.js → chunk-I73JWHNT.js} +1 -1
  9. package/dist/{chunk-RZVGR3KD.js → chunk-NP446QJS.js} +3 -3
  10. package/dist/{chunk-CYUAHF6X.js → chunk-NWM7CTQQ.js} +1 -1
  11. package/dist/{chunk-HEUB5PV6.js → chunk-QL2QACHI.js} +1 -1
  12. package/dist/{chunk-3Y5BCGD2.js → chunk-TKG6BI6G.js} +1 -1
  13. package/dist/{chunk-ERDFOXYK.js → chunk-TOH67ARY.js} +1 -1
  14. package/dist/{chunk-EU6NMK7G.js → chunk-VIDUFFHT.js} +3 -3
  15. package/dist/{ci-WXCMI4ND.js → ci-U7X5OWOA.js} +4 -4
  16. package/dist/{ci-format-JXD2BQM6.js → ci-format-PDP5K5LR.js} +2 -2
  17. package/dist/{constants-2B7CN2ER.js → constants-GBIMYBKD.js} +1 -1
  18. package/dist/{constants-SRRV3K5Y.js → constants-MYTDRODA.js} +1 -1
  19. package/dist/{doctor-cli-LQYTDJ74.js → doctor-cli-64WRAKH3.js} +4 -4
  20. package/dist/electron-server.js +143 -238
  21. package/dist/{hub-Y62P5MIA.js → hub-FSITPTD5.js} +1 -1
  22. package/dist/index.js +18 -17
  23. package/dist/{persistent-memory-46JSUEYV.js → persistent-memory-7GPL7NSO.js} +2 -2
  24. package/dist/{persistent-memory-3N2XQKJK.js → persistent-memory-7PPH6QES.js} +2 -2
  25. package/dist/{pr-L7Y3XHGL.js → pr-T7VURNFJ.js} +4 -4
  26. package/dist/{run-tests-WC644BHH.js → run-tests-CSAB2M4A.js} +2 -2
  27. package/dist/{run-tests-3ZOMRFIX.js → run-tests-PPTDYVS7.js} +2 -2
  28. package/dist/{server-XWURZCAF.js → server-6B2JFKRN.js} +53 -117
  29. package/dist/{server-L4QHJT2I.js → server-O54U4OOB.js} +5 -5
  30. package/dist/{task-orchestrator-6TVKJYKC.js → task-orchestrator-HA4JE4TF.js} +5 -5
  31. package/dist/{usage-L2PSI2KH.js → usage-HX6MPQT2.js} +2 -2
  32. package/package.json +1 -1
@@ -50,7 +50,7 @@ import {
50
50
  touchMemoryReferences,
51
51
  updateMemoryApproval,
52
52
  updateMemoryEntry
53
- } from "./chunk-L625B2XV.js";
53
+ } from "./chunk-GXB5EOFI.js";
54
54
  import {
55
55
  DEFAULT_PATTERNS,
56
56
  redactJson,
@@ -66,7 +66,7 @@ import {
66
66
  } from "./chunk-CGLYJRFA.js";
67
67
  import {
68
68
  runTestsTool
69
- } from "./chunk-3BHJKMPQ.js";
69
+ } from "./chunk-I73JWHNT.js";
70
70
  import {
71
71
  AGENTIC_BEHAVIOR_GUIDELINE,
72
72
  APP_NAME,
@@ -100,7 +100,7 @@ import {
100
100
  SUBAGENT_MAX_ROUNDS_LIMIT,
101
101
  VERSION,
102
102
  buildUserIdentityPrompt
103
- } from "./chunk-CYUAHF6X.js";
103
+ } from "./chunk-NWM7CTQQ.js";
104
104
 
105
105
  // src/web/server.ts
106
106
  import express from "express";
@@ -7262,6 +7262,67 @@ function runHook(template, vars) {
7262
7262
  }
7263
7263
  }
7264
7264
 
7265
+ // src/core/tool-gate.ts
7266
+ var gateMessages = {
7267
+ hookDenied: (toolName, reason) => `[Hook denied] ${reason ?? `PreToolUse blocked ${toolName}`}. Do not retry without asking.`,
7268
+ networkDenied: (toolName, reason) => `[Permission denied] Tool ${toolName} is blocked by networkPolicy${reason ? ` (${reason})` : ""}. Do not retry.`,
7269
+ profileDenied: (toolName, profileName, reason) => `[Permission denied] Tool ${toolName} is blocked by permission profile "${profileName}"${reason ? ` (${reason})` : ""}. Do not retry.`,
7270
+ planModeDenied: (toolName) => `[Plan Mode] ${toolName} is disabled in read-only Plan Mode. Do not retry it. Use read_file / list_dir / grep_files / glob_files to investigate, and write_todos to present your plan. If the user wants this executed, tell them to type /plan execute first.`,
7271
+ autoDenied: (reason) => `[Auto denied] ${reason}. Do not retry without asking.`,
7272
+ permissionCancelled: (toolName) => `[User cancelled] The user declined permission for ${toolName}. Do not retry without asking.`,
7273
+ operationCancelled: (toolName, level) => `[User cancelled] The user declined the ${level === "destructive" ? "destructive " : ""}${toolName} operation. Do not retry without asking.`
7274
+ };
7275
+ function needsPermissionRequestHook(input) {
7276
+ return input.networkPermission?.action === "confirm" || input.permission.action === "confirm";
7277
+ }
7278
+ function decideToolGate(call, input) {
7279
+ const { dangerLevel, permission, networkPermission } = input;
7280
+ if (input.hookDeny) {
7281
+ return { kind: "denied", source: "hook", message: gateMessages.hookDenied(call.name, input.hookDeny.reason) };
7282
+ }
7283
+ if (input.planMode && !PLAN_MODE_READONLY_TOOLS.has(call.name)) {
7284
+ return { kind: "denied", source: "plan-mode", message: gateMessages.planModeDenied(call.name) };
7285
+ }
7286
+ if (networkPermission?.action === "deny") {
7287
+ return { kind: "denied", source: "network", message: gateMessages.networkDenied(call.name, networkPermission.reason) };
7288
+ }
7289
+ if (permission.action === "deny") {
7290
+ return {
7291
+ kind: "denied",
7292
+ source: "profile",
7293
+ message: gateMessages.profileDenied(call.name, permission.profileName ?? "default", permission.reason)
7294
+ };
7295
+ }
7296
+ if (permission.action === "auto-approve" && networkPermission?.action !== "confirm") {
7297
+ return { kind: "allow", source: "profile" };
7298
+ }
7299
+ const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
7300
+ if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
7301
+ return { kind: "confirm", level: "write", source: "permission" };
7302
+ }
7303
+ if (input.sessionAutoMode && networkPermission?.action !== "confirm") {
7304
+ const classification = input.classify();
7305
+ if (classification.decision === "deny") {
7306
+ return {
7307
+ kind: "denied",
7308
+ source: "auto-mode",
7309
+ message: gateMessages.autoDenied(classification.reason),
7310
+ classification
7311
+ };
7312
+ }
7313
+ if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
7314
+ return { kind: "allow", source: "auto-mode" };
7315
+ }
7316
+ }
7317
+ if (input.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
7318
+ return { kind: "allow", source: "yolo" };
7319
+ }
7320
+ if (dangerLevel === "write" || dangerLevel === "destructive") {
7321
+ return { kind: "confirm", level: dangerLevel, source: "danger" };
7322
+ }
7323
+ return { kind: "allow", source: "safe" };
7324
+ }
7325
+
7265
7326
  // src/tools/permissions.ts
7266
7327
  import { existsSync as existsSync9, realpathSync } from "fs";
7267
7328
  import { basename as basename2, dirname as dirname3, isAbsolute, resolve as resolve3 } from "path";
@@ -7968,6 +8029,14 @@ var ToolExecutor = class {
7968
8029
  sessionAutoApprove = false;
7969
8030
  /** Session-level Auto Mode: rule-based low-risk auto approval. */
7970
8031
  sessionAutoMode = false;
8032
+ /**
8033
+ * Plan Mode(只读规划模式)。由宿主在每轮开始前同步。
8034
+ *
8035
+ * 必须在**执行层**也知道它:Plan Mode 此前只过滤"给模型看的工具清单",模型一旦吐出
8036
+ * 白名单外的调用,executor 一无所知照常执行,而 `alwaysYolo` 下 write 级还会被直接
8037
+ * 放行——只读保护形同虚设(审计 P2-01)。
8038
+ */
8039
+ planMode = false;
7971
8040
  /**
7972
8041
  * Logical session key used to scope per-session state in stateful tools
7973
8042
  * (currently only `bash`'s persistent cwd). Web mode sets this per-tab so
@@ -8056,13 +8125,6 @@ var ToolExecutor = class {
8056
8125
  { configDir: this.hookConfigDir, onSummary: (msg) => console.log(theme.dim(` [hook] ${msg}`)) }
8057
8126
  );
8058
8127
  const preDeny = preToolDecisions.find((d) => d.action === "deny");
8059
- if (preDeny) {
8060
- return {
8061
- callId: call.id,
8062
- content: `[Hook denied] ${preDeny.reason ?? `PreToolUse blocked ${call.name}`}. Do not retry without asking.`,
8063
- isError: true
8064
- };
8065
- }
8066
8128
  for (const d of preToolDecisions) {
8067
8129
  const warnings = [d.warning, ...d.warnings ?? []].filter(Boolean);
8068
8130
  for (const w of warnings) console.log(theme.warning(` \u26A0 Hook warning: ${w}`));
@@ -8082,7 +8144,20 @@ var ToolExecutor = class {
8082
8144
  }
8083
8145
  );
8084
8146
  const networkPermission = checkNetworkPolicy(call.name, call.arguments, this.networkPolicy);
8085
- if (networkPermission?.action === "confirm" || permission.action === "confirm") {
8147
+ const gate = decideToolGate(call, {
8148
+ dangerLevel,
8149
+ permission,
8150
+ networkPermission,
8151
+ hookDeny: preDeny ? { reason: preDeny.reason } : void 0,
8152
+ planMode: this.planMode,
8153
+ sessionAutoMode: this.sessionAutoMode,
8154
+ sessionAutoApprove: this.sessionAutoApprove,
8155
+ classify: () => defaultActionClassifier.classify(call, dangerLevel, {
8156
+ workspaceRoot: this.workspaceRoot,
8157
+ tempDirs: [tmpdir()]
8158
+ })
8159
+ });
8160
+ if (needsPermissionRequestHook({ permission, networkPermission })) {
8086
8161
  runLifecycleHooks(
8087
8162
  this.hookConfig,
8088
8163
  "PermissionRequest",
@@ -8090,131 +8165,29 @@ var ToolExecutor = class {
8090
8165
  { configDir: this.hookConfigDir, onSummary: (msg) => console.log(theme.dim(" [hook] " + msg)) }
8091
8166
  );
8092
8167
  }
8093
- if (networkPermission?.action === "deny") {
8094
- const reason = networkPermission.reason ? ` (${networkPermission.reason})` : "";
8095
- return {
8096
- callId: call.id,
8097
- content: `[Permission denied] Tool ${call.name} is blocked by networkPolicy${reason}. Do not retry.`,
8098
- isError: true
8099
- };
8100
- }
8101
- if (permission.action === "deny") {
8102
- const reason = permission.reason ? ` (${permission.reason})` : "";
8103
- return {
8104
- callId: call.id,
8105
- content: `[Permission denied] Tool ${call.name} is blocked by permission profile "${permission.profileName}"${reason}. Do not retry.`,
8106
- isError: true
8107
- };
8108
- }
8109
- if (permission.action === "auto-approve" && networkPermission?.action !== "confirm") {
8110
- if (!toolCallAlreadyPrinted) this.printToolCall(call);
8111
- try {
8112
- const rawContent = await runTool(tool, call.arguments, call.name);
8113
- const content = truncateOutput(rawContent, call.name);
8114
- const wasTruncated = content !== rawContent;
8115
- this.printToolResult(call.name, rawContent, false, wasTruncated);
8116
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
8117
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
8118
- return { callId: call.id, content, isError: false };
8119
- } catch (err) {
8120
- const message = err instanceof Error ? err.message : err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
8121
- this.printToolResult(call.name, message, true, false);
8122
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
8123
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
8124
- return { callId: call.id, content: message, isError: true };
8125
- }
8126
- }
8127
- const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
8128
- if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
8129
- this.printToolCall(call);
8130
- toolCallAlreadyPrinted = true;
8131
- const confirmed = await this.confirm(call, "write");
8132
- if (!confirmed) {
8133
- return {
8134
- callId: call.id,
8135
- content: `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`,
8136
- isError: true
8137
- };
8138
- }
8168
+ if (gate.kind === "denied") {
8169
+ if (gate.source === "auto-mode") recordRecentlyDeniedAutoAction(call, gate.classification);
8170
+ return { callId: call.id, content: gate.message, isError: true };
8139
8171
  }
8140
- if (this.sessionAutoMode && networkPermission?.action !== "confirm") {
8141
- const classification = defaultActionClassifier.classify(call, dangerLevel, {
8142
- workspaceRoot: this.workspaceRoot,
8143
- tempDirs: [tmpdir()]
8144
- });
8145
- if (classification.decision === "deny") {
8146
- recordRecentlyDeniedAutoAction(call, classification);
8147
- return {
8148
- callId: call.id,
8149
- content: `[Auto denied] ${classification.reason}. Do not retry without asking.`,
8150
- isError: true
8151
- };
8152
- }
8153
- if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
8172
+ if (gate.kind === "confirm") {
8173
+ if (gate.source === "danger" && gate.level === "write") {
8154
8174
  this.printToolCall(call);
8155
- if (dangerLevel === "write") this.printDiffPreview(call);
8156
- console.log(theme.warning(` \u26A1 Auto-approved (/auto: ${classification.ruleId})`));
8157
- try {
8158
- const rawContent = await runTool(tool, call.arguments, call.name);
8159
- const content = truncateOutput(rawContent, call.name);
8160
- const wasTruncated = content !== rawContent;
8161
- this.printToolResult(call.name, rawContent, false, wasTruncated);
8162
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
8163
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
8164
- return { callId: call.id, content, isError: false };
8165
- } catch (err) {
8166
- const message = err instanceof Error ? err.message : err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
8167
- this.printToolResult(call.name, message, true, false);
8168
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
8169
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
8170
- return { callId: call.id, content: message, isError: true };
8171
- }
8172
- }
8173
- }
8174
- if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
8175
- this.printToolCall(call);
8176
- if (dangerLevel === "write") this.printDiffPreview(call);
8177
- console.log(theme.warning(" \u26A1 Auto-approved (session /yolo mode)"));
8178
- try {
8179
- const rawContent = await runTool(tool, call.arguments, call.name);
8180
- const content = truncateOutput(rawContent, call.name);
8181
- const wasTruncated = content !== rawContent;
8182
- this.printToolResult(call.name, rawContent, false, wasTruncated);
8183
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
8184
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
8185
- return { callId: call.id, content, isError: false };
8186
- } catch (err) {
8187
- const message = err instanceof Error ? err.message : err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
8188
- this.printToolResult(call.name, message, true, false);
8189
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
8190
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
8191
- return { callId: call.id, content: message, isError: true };
8192
- }
8193
- }
8194
- if (dangerLevel === "write") {
8195
- this.printToolCall(call);
8196
- this.printDiffPreview(call);
8197
- const confirmed = await this.confirm(call, dangerLevel);
8198
- if (!confirmed) {
8199
- return {
8200
- callId: call.id,
8201
- content: `[User cancelled] The user declined the ${call.name} operation. Do not retry without asking.`,
8202
- isError: true
8203
- };
8175
+ this.printDiffPreview(call);
8176
+ toolCallAlreadyPrinted = true;
8177
+ } else if (gate.source === "permission") {
8178
+ this.printToolCall(call);
8179
+ toolCallAlreadyPrinted = true;
8204
8180
  }
8205
- } else if (dangerLevel === "destructive") {
8206
- const confirmed = await this.confirm(call, dangerLevel);
8181
+ const confirmed = await this.confirm(call, gate.level);
8207
8182
  if (!confirmed) {
8208
8183
  return {
8209
8184
  callId: call.id,
8210
- content: `[User cancelled] The user declined the destructive ${call.name} operation. Do not retry without asking.`,
8185
+ content: gate.source === "permission" ? gateMessages.permissionCancelled(call.name) : gateMessages.operationCancelled(call.name, gate.level),
8211
8186
  isError: true
8212
8187
  };
8213
8188
  }
8214
- this.printToolCall(call);
8215
- } else {
8216
- this.printToolCall(call);
8217
8189
  }
8190
+ if (!toolCallAlreadyPrinted) this.printToolCall(call);
8218
8191
  try {
8219
8192
  const rawContent = await runTool(tool, call.arguments, call.name);
8220
8193
  const content = truncateOutput(rawContent, call.name);
@@ -13843,6 +13816,14 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13843
13816
  sessionAutoApprove = false;
13844
13817
  /** Session-level Auto Mode: rule-based low-risk auto approval. */
13845
13818
  sessionAutoMode = false;
13819
+ /**
13820
+ * Plan Mode(只读规划模式)。由宿主在每轮开始前同步。
13821
+ *
13822
+ * 必须在**执行层**也知道它:Plan Mode 此前只过滤"给模型看的工具清单",模型一旦吐出
13823
+ * 白名单外的调用,executor 一无所知照常执行,而 `alwaysYolo` 下 write 级还会被直接
13824
+ * 放行——只读保护形同虚设(审计 P2-01)。
13825
+ */
13826
+ planMode = false;
13846
13827
  /** Track tool start times for duration calculation */
13847
13828
  toolStartTimes = /* @__PURE__ */ new Map();
13848
13829
  /**
@@ -14048,15 +14029,6 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
14048
14029
  { configDir: this.hookConfigDir, onSummary: (message) => this.send({ type: "info", message: `[hook] ${message}` }) }
14049
14030
  );
14050
14031
  const preDeny = preToolDecisions.find((d) => d.action === "deny");
14051
- if (preDeny) {
14052
- const result = {
14053
- callId: call.id,
14054
- content: `[Hook denied] ${preDeny.reason ?? `PreToolUse blocked ${call.name}`}. Do not retry without asking.`,
14055
- isError: true
14056
- };
14057
- this.sendToolCallResult(call, result.content, true);
14058
- return result;
14059
- }
14060
14032
  for (const d of preToolDecisions) {
14061
14033
  const warnings = [d.warning, ...d.warnings ?? []].filter(Boolean);
14062
14034
  for (const warning of warnings) this.send({ type: "info", message: `[hook] ${warning}` });
@@ -14076,7 +14048,20 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
14076
14048
  }
14077
14049
  );
14078
14050
  const networkPermission = checkNetworkPolicy(call.name, call.arguments, this.networkPolicy);
14079
- if (networkPermission?.action === "confirm" || permission.action === "confirm") {
14051
+ const gate = decideToolGate(call, {
14052
+ dangerLevel,
14053
+ permission,
14054
+ networkPermission,
14055
+ hookDeny: preDeny ? { reason: preDeny.reason } : void 0,
14056
+ planMode: this.planMode,
14057
+ sessionAutoMode: this.sessionAutoMode,
14058
+ sessionAutoApprove: this.sessionAutoApprove,
14059
+ classify: () => defaultActionClassifier.classify(call, dangerLevel, {
14060
+ workspaceRoot: this.workspaceRoot,
14061
+ tempDirs: [tmpdir2()]
14062
+ })
14063
+ });
14064
+ if (needsPermissionRequestHook({ permission, networkPermission })) {
14080
14065
  runLifecycleHooks(
14081
14066
  this.hookConfig,
14082
14067
  "PermissionRequest",
@@ -14084,105 +14069,24 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
14084
14069
  { configDir: this.hookConfigDir, onSummary: (message) => this.send({ type: "info", message: "[hook] " + message }) }
14085
14070
  );
14086
14071
  }
14087
- if (networkPermission?.action === "deny") {
14088
- const reason = networkPermission.reason ? ` (${networkPermission.reason})` : "";
14089
- return {
14090
- callId: call.id,
14091
- content: `[Permission denied] Tool ${call.name} is blocked by networkPolicy${reason}. Do not retry.`,
14092
- isError: true
14093
- };
14094
- }
14095
- if (permission.action === "deny") {
14096
- const reason = permission.reason ? ` (${permission.reason})` : "";
14097
- return {
14098
- callId: call.id,
14099
- content: `[Permission denied] Tool ${call.name} is blocked by permission profile "${permission.profileName}"${reason}. Do not retry.`,
14100
- isError: true
14101
- };
14102
- }
14103
- if (permission.action === "auto-approve" && networkPermission?.action !== "confirm") {
14104
- this.sendToolCallStart(call);
14105
- try {
14106
- const rawContent = await runTool(tool, call.arguments, call.name);
14107
- const content = truncateOutput(rawContent, call.name);
14108
- this.sendToolCallResult(call, rawContent, false);
14109
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
14110
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
14111
- return { callId: call.id, content, isError: false };
14112
- } catch (err) {
14113
- const message = err instanceof Error ? err.message : err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
14114
- this.sendToolCallResult(call, message, true);
14115
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
14116
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
14117
- return { callId: call.id, content: message, isError: true };
14118
- }
14119
- }
14120
- const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
14121
- if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
14122
- this.sendToolCallStart(call);
14123
- toolCallStarted = true;
14124
- const confirmed = await this.confirm(call, "write");
14125
- if (!confirmed) {
14126
- const rejectionMsg = `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`;
14127
- this.sendToolCallResult(call, rejectionMsg, true);
14128
- return { callId: call.id, content: rejectionMsg, isError: true };
14129
- }
14072
+ if (gate.kind === "denied") {
14073
+ if (gate.source === "auto-mode") recordRecentlyDeniedAutoAction(call, gate.classification);
14074
+ this.sendToolCallResult(call, gate.message, true);
14075
+ return { callId: call.id, content: gate.message, isError: true };
14130
14076
  }
14131
- if (!toolCallStarted) this.sendToolCallStart(call);
14132
- if (this.sessionAutoMode && networkPermission?.action !== "confirm") {
14133
- const classification = defaultActionClassifier.classify(call, dangerLevel, {
14134
- workspaceRoot: this.workspaceRoot,
14135
- tempDirs: [tmpdir2()]
14136
- });
14137
- if (classification.decision === "deny") {
14138
- recordRecentlyDeniedAutoAction(call, classification);
14139
- const rejectionMsg = `[Auto denied] ${classification.reason}. Do not retry without asking.`;
14140
- this.sendToolCallResult(call, rejectionMsg, true);
14141
- return { callId: call.id, content: rejectionMsg, isError: true };
14077
+ if (gate.kind === "confirm") {
14078
+ if (!toolCallStarted) {
14079
+ this.sendToolCallStart(call);
14080
+ toolCallStarted = true;
14142
14081
  }
14143
- if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
14144
- this.send({ type: "info", message: `\u26A1 Auto-approved (/auto: ${classification.ruleId})` });
14145
- try {
14146
- const rawContent = await runTool(tool, call.arguments, call.name);
14147
- const content = truncateOutput(rawContent, call.name);
14148
- this.sendToolCallResult(call, rawContent, false);
14149
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
14150
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
14151
- return { callId: call.id, content, isError: false };
14152
- } catch (err) {
14153
- const message = err instanceof Error ? err.message : err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
14154
- this.sendToolCallResult(call, message, true);
14155
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
14156
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
14157
- return { callId: call.id, content: message, isError: true };
14158
- }
14159
- }
14160
- }
14161
- if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
14162
- try {
14163
- const rawContent = await runTool(tool, call.arguments, call.name);
14164
- const content = truncateOutput(rawContent, call.name);
14165
- this.sendToolCallResult(call, rawContent, false);
14166
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
14167
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
14168
- return { callId: call.id, content, isError: false };
14169
- } catch (err) {
14170
- const message = err instanceof Error ? err.message : err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
14171
- this.sendToolCallResult(call, message, true);
14172
- runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
14173
- runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
14174
- return { callId: call.id, content: message, isError: true };
14175
- }
14176
- }
14177
- if (dangerLevel === "write" || dangerLevel === "destructive") {
14178
- const confirmed = await this.confirm(call, dangerLevel);
14082
+ const confirmed = await this.confirm(call, gate.level);
14179
14083
  if (!confirmed) {
14180
- const label = dangerLevel === "destructive" ? "destructive " : "";
14181
- const rejectionMsg = `[User cancelled] The user declined the ${label}${call.name} operation. Do not retry without asking.`;
14084
+ const rejectionMsg = gate.source === "permission" ? gateMessages.permissionCancelled(call.name) : gateMessages.operationCancelled(call.name, gate.level);
14182
14085
  this.sendToolCallResult(call, rejectionMsg, true);
14183
14086
  return { callId: call.id, content: rejectionMsg, isError: true };
14184
14087
  }
14185
14088
  }
14089
+ if (!toolCallStarted) this.sendToolCallStart(call);
14186
14090
  try {
14187
14091
  const rawContent = await runTool(tool, call.arguments, call.name);
14188
14092
  const content = truncateOutput(rawContent, call.name);
@@ -16997,7 +16901,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
16997
16901
  Cost: ${formatCost(cost)}` : "";
16998
16902
  let memoryLine = "";
16999
16903
  try {
17000
- const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-3N2XQKJK.js");
16904
+ const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-7PPH6QES.js");
17001
16905
  const pending = countPendingMemories2(ctx.config.getConfigDir());
17002
16906
  if (pending > 0) memoryLine = `
17003
16907
  Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
@@ -17980,7 +17884,7 @@ async function handleTest(args, ctx) {
17980
17884
  const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
17981
17885
  testArgs = isCommand ? { command: argStr } : { filter: argStr };
17982
17886
  }
17983
- const runTests = ctx.runTests ?? (await import("./run-tests-3ZOMRFIX.js")).executeTests;
17887
+ const runTests = ctx.runTests ?? (await import("./run-tests-PPTDYVS7.js")).executeTests;
17984
17888
  const report = await runTests(testArgs);
17985
17889
  ctx.send({ type: "info", message: report });
17986
17890
  } catch (err) {
@@ -18370,7 +18274,7 @@ async function handleMemory(args, ctx) {
18370
18274
  }
18371
18275
  if (intent.kind === "status") {
18372
18276
  const { getChatIndexStatus: getChatIndexStatus2 } = await import("./chat-index-DGUOUUQW.js");
18373
- const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-3N2XQKJK.js");
18277
+ const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-7PPH6QES.js");
18374
18278
  const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
18375
18279
  const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
18376
18280
  const view = buildMemoryStatusView({
@@ -18393,7 +18297,7 @@ async function handleMemory(args, ctx) {
18393
18297
  return;
18394
18298
  }
18395
18299
  if (intent.kind === "recall") {
18396
- const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-3N2XQKJK.js");
18300
+ const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-7PPH6QES.js");
18397
18301
  const { searchChatMemory: searchChatMemory2 } = await import("./chat-index-DGUOUUQW.js");
18398
18302
  const persistentHits = searchPersistentMemories2(configDir, intent.query, process.cwd(), 3);
18399
18303
  if (persistentHits.length > 0) {
@@ -18514,7 +18418,7 @@ async function handleMemory(args, ctx) {
18514
18418
  ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
18515
18419
  } else if (intent.kind === "export") {
18516
18420
  if (args[1] === "md") {
18517
- const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-3N2XQKJK.js");
18421
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-7PPH6QES.js");
18518
18422
  ctx.send({
18519
18423
  type: "export_data",
18520
18424
  format: "md",
@@ -19519,7 +19423,7 @@ No tools match "${filter}".
19519
19423
  const { join: join29 } = await import("path");
19520
19424
  const { existsSync: existsSync33 } = await import("fs");
19521
19425
  const { getGitRoot: getGitRoot2 } = await import("./git-context-VNCA2GU7.js");
19522
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-SRRV3K5Y.js");
19426
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-MYTDRODA.js");
19523
19427
  const { approveProject, hashMcpFile } = await import("./project-trust-MUG325AW.js");
19524
19428
  const cwd = process.cwd();
19525
19429
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -20434,7 +20338,7 @@ function createReviewCommands() {
20434
20338
  usage: "/test [command|filter]",
20435
20339
  async execute(args, ctx) {
20436
20340
  try {
20437
- const { executeTests } = await import("./run-tests-3ZOMRFIX.js");
20341
+ const { executeTests } = await import("./run-tests-PPTDYVS7.js");
20438
20342
  const argStr = args.join(" ").trim();
20439
20343
  let testArgs = {};
20440
20344
  if (argStr) {
@@ -22931,7 +22835,7 @@ var SessionHandler = class {
22931
22835
  ...simpleReasoning ? { reasoningContent: simpleReasoning } : {}
22932
22836
  });
22933
22837
  } catch (err) {
22934
- if (err.name === "AbortError") {
22838
+ if (isAbortError(err)) {
22935
22839
  this.send({ type: "info", message: "[interrupted]" });
22936
22840
  } else {
22937
22841
  throw err;
@@ -22948,7 +22852,7 @@ var SessionHandler = class {
22948
22852
  const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
22949
22853
  const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
22950
22854
  const { stable: toolStable, volatile: toolVolatile } = this.buildSystemPrompt();
22951
- const roundBudgetHint = buildRoundBudgetHint({ maxToolRounds, autoPauseInterval });
22855
+ const roundBudgetHint = buildRoundBudgetHint({ maxToolRounds, autoPauseInterval, planMode: this.planMode });
22952
22856
  const systemPrompt = toolStable + TOOL_CALL_REMINDER + roundBudgetHint + (mcpBudgetNote ? `
22953
22857
 
22954
22858
  ${mcpBudgetNote}` : "");
@@ -23251,7 +23155,7 @@ ${summaryContent}`,
23251
23155
  this.send({ type: "info", message: "[interrupted]" });
23252
23156
  }
23253
23157
  } catch (err) {
23254
- if (err.name === "AbortError") {
23158
+ if (isAbortError(err)) {
23255
23159
  this.send({ type: "info", message: "[interrupted]" });
23256
23160
  } else {
23257
23161
  throw err;
@@ -23864,6 +23768,7 @@ Backup saved: ${result.backupPath}` : "";
23864
23768
  };
23865
23769
  }
23866
23770
  getFilteredToolDefs() {
23771
+ this.toolExecutor.planMode = this.planMode;
23867
23772
  if (this.planMode) {
23868
23773
  return {
23869
23774
  toolDefs: this.toolRegistry.getDefinitions().filter((t) => PLAN_MODE_READONLY_TOOLS.has(t.name)),
@@ -158,7 +158,7 @@ ${content}`);
158
158
  }
159
159
  }
160
160
  async function runTaskMode(config, providers, configManager, topic) {
161
- const { TaskOrchestrator } = await import("./task-orchestrator-6TVKJYKC.js");
161
+ const { TaskOrchestrator } = await import("./task-orchestrator-HA4JE4TF.js");
162
162
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
163
163
  let interrupted = false;
164
164
  const onSigint = () => {