@andyqiu/codeforge 0.5.6 → 0.5.8

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 (2) hide show
  1. package/dist/index.js +760 -783
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -750,81 +750,22 @@ var init_kh_client = __esm(() => {
750
750
  _warnedKeys = new Set;
751
751
  });
752
752
 
753
- // lib/autonomy.ts
754
- var exports_autonomy = {};
755
- __export(exports_autonomy, {
753
+ // lib/worktree-ops.ts
754
+ var exports_worktree_ops = {};
755
+ __export(exports_worktree_ops, {
756
756
  worktreeHasChanges: () => worktreeHasChanges,
757
757
  tryMerge: () => tryMerge,
758
- renderDecision: () => renderDecision,
759
758
  removeWorktree: () => removeWorktree,
760
759
  mergeCommit: () => mergeCommit,
761
760
  mergeAbort: () => mergeAbort,
762
761
  listWorktrees: () => listWorktrees,
763
762
  getMergeConflicts: () => getMergeConflicts,
764
- evaluate: () => evaluate,
765
763
  ensureWorktree: () => ensureWorktree,
766
- commitWorktreeIfDirty: () => commitWorktreeIfDirty,
767
- classifyTool: () => classifyTool,
768
- DEFAULT_POLICIES: () => DEFAULT_POLICIES
764
+ commitWorktreeIfDirty: () => commitWorktreeIfDirty
769
765
  });
770
766
  import { execFile as execFile2 } from "node:child_process";
771
767
  import { promises as fs8 } from "node:fs";
772
768
  import * as path11 from "node:path";
773
- function evaluate(mode, intent, opts = {}) {
774
- const ignore = new Set(opts.ignore_risks ?? []);
775
- const allRisks = [...RISK_PATTERNS, ...opts.extra_risks ?? []];
776
- const haystack = buildHaystack(intent.args);
777
- const hits = allRisks.filter((p) => !ignore.has(p.tag)).filter((p) => !p.kinds || p.kinds.includes(intent.kind)).filter((p) => p.re.test(haystack)).map((p) => p.tag);
778
- let effective = mode;
779
- let downgraded = false;
780
- let reason;
781
- if (hits.length > 0 && mode === "full") {
782
- effective = "semi";
783
- downgraded = true;
784
- reason = `检测到风险操作 [${hits.join(", ")}],自动从 full 降级到 semi`;
785
- } else if (hits.length > 0 && mode !== "step") {}
786
- const policy = mergePolicy(effective, opts.policies);
787
- const baseAction = policy[intent.kind] ?? "confirm";
788
- const action = hits.length > 0 && effective !== "step" ? "confirm" : baseAction;
789
- return {
790
- effective_mode: effective,
791
- action,
792
- downgraded,
793
- reason,
794
- detected_risks: hits
795
- };
796
- }
797
- function mergePolicy(mode, override) {
798
- const base = DEFAULT_POLICIES[mode];
799
- if (!override?.[mode])
800
- return base;
801
- return { ...base, ...override[mode] };
802
- }
803
- function buildHaystack(args) {
804
- if (typeof args === "string")
805
- return args;
806
- try {
807
- return JSON.stringify(args);
808
- } catch {
809
- return String(args);
810
- }
811
- }
812
- function classifyTool(tool) {
813
- const lower = tool.toLowerCase();
814
- if (lower in TOOL_KIND_MAP)
815
- return TOOL_KIND_MAP[lower];
816
- if (/(^|[-_])edit($|[-_])|write|patch|apply/i.test(lower))
817
- return "edit";
818
- if (/bash|shell|exec|run/i.test(lower))
819
- return "bash";
820
- if (/fetch|http|curl/i.test(lower))
821
- return "webfetch";
822
- if (/read|view/i.test(lower))
823
- return "read";
824
- if (/search|grep|glob|find|list/i.test(lower))
825
- return "search";
826
- return "other";
827
- }
828
769
  async function ensureWorktree(opts) {
829
770
  const root = path11.resolve(opts.root);
830
771
  const dir = opts.worktrees_dir ?? path11.join(root, ".git", "codeforge-worktrees");
@@ -1000,142 +941,7 @@ async function commitWorktreeIfDirty(opts) {
1000
941
  await runGit(wt, ["commit", "-m", opts.message], timeout);
1001
942
  return { committed: true };
1002
943
  }
1003
- function renderDecision(d) {
1004
- const lines = [];
1005
- lines.push(`mode: ${d.effective_mode}${d.downgraded ? " (downgraded)" : ""}`);
1006
- lines.push(`action: ${d.action}`);
1007
- if (d.detected_risks.length > 0) {
1008
- lines.push(`risks: ${d.detected_risks.join(", ")}`);
1009
- }
1010
- if (d.reason)
1011
- lines.push(`reason: ${d.reason}`);
1012
- return lines.join(`
1013
- `);
1014
- }
1015
- var DEFAULT_POLICIES, RISK_PATTERNS, TOOL_KIND_MAP;
1016
- var init_autonomy = __esm(() => {
1017
- DEFAULT_POLICIES = {
1018
- step: {
1019
- bash: "confirm",
1020
- edit: "confirm",
1021
- webfetch: "confirm",
1022
- read: "confirm",
1023
- search: "confirm",
1024
- other: "confirm"
1025
- },
1026
- semi: {
1027
- bash: "confirm",
1028
- edit: "confirm",
1029
- webfetch: "confirm",
1030
- read: "auto",
1031
- search: "auto",
1032
- other: "auto"
1033
- },
1034
- full: {
1035
- bash: "auto",
1036
- edit: "auto",
1037
- webfetch: "auto",
1038
- read: "auto",
1039
- search: "auto",
1040
- other: "auto"
1041
- },
1042
- auto: {
1043
- bash: "auto",
1044
- edit: "auto",
1045
- webfetch: "auto",
1046
- read: "auto",
1047
- search: "auto",
1048
- other: "auto"
1049
- }
1050
- };
1051
- RISK_PATTERNS = [
1052
- {
1053
- tag: "rm_rf_root",
1054
- kinds: ["bash"],
1055
- re: /rm\s+-[rRf]+[^|;`\n]*\s+(\/(?:\s|$|[^/\w*.-])|~|\$HOME(?:\b|\/)|\/(?:bin|etc|usr|var|opt|home|root|boot|lib|sbin)\b)/
1056
- },
1057
- {
1058
- tag: "rm_rf_wildcard",
1059
- kinds: ["bash"],
1060
- re: /rm\s+-[rRf]+\s+(?:\.\*|\*)/
1061
- },
1062
- {
1063
- tag: "force_push_protected",
1064
- kinds: ["bash"],
1065
- re: /git\s+push\s+(?:--force\b|-f\b)[\s\S]*\b(main|master|release(?:\/[\w.-]+)?)\b/
1066
- },
1067
- {
1068
- tag: "git_reset_hard_main",
1069
- kinds: ["bash"],
1070
- re: /git\s+reset\s+--hard\s+(origin\/)?(main|master)/
1071
- },
1072
- { tag: "sudo", kinds: ["bash"], re: /(?<![\w-])sudo(?![\w-])/ },
1073
- { tag: "su_root", kinds: ["bash"], re: /(?<![\w-])su\s+(?:-\s+)?root\b/ },
1074
- { tag: "mkfs", kinds: ["bash"], re: /\bmkfs(\.[\w]+)?\b/ },
1075
- { tag: "dd_disk", kinds: ["bash"], re: /\bdd\s+if=[^|;\s]+\s+of=\/dev\// },
1076
- { tag: "chmod_777", kinds: ["bash"], re: /chmod\s+[-+]?[0-7]?777/ },
1077
- {
1078
- tag: "curl_pipe_sh",
1079
- kinds: ["bash"],
1080
- re: /\b(?:curl|wget)\b[^|]*\|\s*(?:sh|bash|zsh)\b/
1081
- },
1082
- {
1083
- tag: "drop_database",
1084
- kinds: ["bash", "other"],
1085
- re: /\b(DROP\s+(DATABASE|TABLE)|TRUNCATE\s+TABLE|DROP\s+SCHEMA)\b/i
1086
- },
1087
- { tag: "write_secrets", re: /(\.env(?:\.\w+)?|id_[edr]sa|\.ssh\/id_|\.pem|\.p12|secret\.json)/i },
1088
- { tag: "write_etc", re: /(?:^|\s|"|')\/etc\// },
1089
- { tag: "write_usr", re: /(?:^|\s|"|')\/usr\// },
1090
- { tag: "write_root_home", re: /(?:^|\s|"|')(\/root|\/home\/root)\// },
1091
- {
1092
- tag: "internal_url",
1093
- kinds: ["webfetch"],
1094
- re: /https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|169\.254\.|10\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.)/i
1095
- },
1096
- {
1097
- tag: "kubectl_delete",
1098
- kinds: ["bash"],
1099
- re: /kubectl\s+(?:delete|destroy)\s+/
1100
- },
1101
- {
1102
- tag: "terraform_destroy",
1103
- kinds: ["bash"],
1104
- re: /terraform\s+destroy/
1105
- }
1106
- ];
1107
- TOOL_KIND_MAP = {
1108
- bash: "bash",
1109
- shell: "bash",
1110
- exec: "bash",
1111
- edit: "edit",
1112
- write: "edit",
1113
- "ast-edit": "edit",
1114
- "pending-changes-apply": "edit",
1115
- webfetch: "webfetch",
1116
- fetch: "webfetch",
1117
- read: "read",
1118
- cat: "read",
1119
- glob: "search",
1120
- grep: "search",
1121
- search: "search",
1122
- "repo-map": "search",
1123
- smart_search: "search",
1124
- list_recent: "search",
1125
- get_working_memory: "search",
1126
- web_search: "search",
1127
- save_chat_insight: "other",
1128
- add_knowledge: "other",
1129
- upload_document: "other",
1130
- upload_document_from_url: "other",
1131
- update_knowledge: "other",
1132
- flag_outdated: "other",
1133
- delete_knowledge: "other",
1134
- update_working_memory: "other",
1135
- update_user_preference: "other",
1136
- generate_image: "other"
1137
- };
1138
- });
944
+ var init_worktree_ops = () => {};
1139
945
 
1140
946
  // lib/decision-parser.ts
1141
947
  var exports_decision_parser = {};
@@ -1263,17 +1069,17 @@ var require_visit = __commonJS((exports) => {
1263
1069
  visit.BREAK = BREAK;
1264
1070
  visit.SKIP = SKIP;
1265
1071
  visit.REMOVE = REMOVE;
1266
- function visit_(key, node, visitor, path17) {
1267
- const ctrl = callVisitor(key, node, visitor, path17);
1072
+ function visit_(key, node, visitor, path18) {
1073
+ const ctrl = callVisitor(key, node, visitor, path18);
1268
1074
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1269
- replaceNode(key, path17, ctrl);
1270
- return visit_(key, ctrl, visitor, path17);
1075
+ replaceNode(key, path18, ctrl);
1076
+ return visit_(key, ctrl, visitor, path18);
1271
1077
  }
1272
1078
  if (typeof ctrl !== "symbol") {
1273
1079
  if (identity.isCollection(node)) {
1274
- path17 = Object.freeze(path17.concat(node));
1080
+ path18 = Object.freeze(path18.concat(node));
1275
1081
  for (let i = 0;i < node.items.length; ++i) {
1276
- const ci = visit_(i, node.items[i], visitor, path17);
1082
+ const ci = visit_(i, node.items[i], visitor, path18);
1277
1083
  if (typeof ci === "number")
1278
1084
  i = ci - 1;
1279
1085
  else if (ci === BREAK)
@@ -1284,13 +1090,13 @@ var require_visit = __commonJS((exports) => {
1284
1090
  }
1285
1091
  }
1286
1092
  } else if (identity.isPair(node)) {
1287
- path17 = Object.freeze(path17.concat(node));
1288
- const ck = visit_("key", node.key, visitor, path17);
1093
+ path18 = Object.freeze(path18.concat(node));
1094
+ const ck = visit_("key", node.key, visitor, path18);
1289
1095
  if (ck === BREAK)
1290
1096
  return BREAK;
1291
1097
  else if (ck === REMOVE)
1292
1098
  node.key = null;
1293
- const cv = visit_("value", node.value, visitor, path17);
1099
+ const cv = visit_("value", node.value, visitor, path18);
1294
1100
  if (cv === BREAK)
1295
1101
  return BREAK;
1296
1102
  else if (cv === REMOVE)
@@ -1311,17 +1117,17 @@ var require_visit = __commonJS((exports) => {
1311
1117
  visitAsync.BREAK = BREAK;
1312
1118
  visitAsync.SKIP = SKIP;
1313
1119
  visitAsync.REMOVE = REMOVE;
1314
- async function visitAsync_(key, node, visitor, path17) {
1315
- const ctrl = await callVisitor(key, node, visitor, path17);
1120
+ async function visitAsync_(key, node, visitor, path18) {
1121
+ const ctrl = await callVisitor(key, node, visitor, path18);
1316
1122
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
1317
- replaceNode(key, path17, ctrl);
1318
- return visitAsync_(key, ctrl, visitor, path17);
1123
+ replaceNode(key, path18, ctrl);
1124
+ return visitAsync_(key, ctrl, visitor, path18);
1319
1125
  }
1320
1126
  if (typeof ctrl !== "symbol") {
1321
1127
  if (identity.isCollection(node)) {
1322
- path17 = Object.freeze(path17.concat(node));
1128
+ path18 = Object.freeze(path18.concat(node));
1323
1129
  for (let i = 0;i < node.items.length; ++i) {
1324
- const ci = await visitAsync_(i, node.items[i], visitor, path17);
1130
+ const ci = await visitAsync_(i, node.items[i], visitor, path18);
1325
1131
  if (typeof ci === "number")
1326
1132
  i = ci - 1;
1327
1133
  else if (ci === BREAK)
@@ -1332,13 +1138,13 @@ var require_visit = __commonJS((exports) => {
1332
1138
  }
1333
1139
  }
1334
1140
  } else if (identity.isPair(node)) {
1335
- path17 = Object.freeze(path17.concat(node));
1336
- const ck = await visitAsync_("key", node.key, visitor, path17);
1141
+ path18 = Object.freeze(path18.concat(node));
1142
+ const ck = await visitAsync_("key", node.key, visitor, path18);
1337
1143
  if (ck === BREAK)
1338
1144
  return BREAK;
1339
1145
  else if (ck === REMOVE)
1340
1146
  node.key = null;
1341
- const cv = await visitAsync_("value", node.value, visitor, path17);
1147
+ const cv = await visitAsync_("value", node.value, visitor, path18);
1342
1148
  if (cv === BREAK)
1343
1149
  return BREAK;
1344
1150
  else if (cv === REMOVE)
@@ -1365,23 +1171,23 @@ var require_visit = __commonJS((exports) => {
1365
1171
  }
1366
1172
  return visitor;
1367
1173
  }
1368
- function callVisitor(key, node, visitor, path17) {
1174
+ function callVisitor(key, node, visitor, path18) {
1369
1175
  if (typeof visitor === "function")
1370
- return visitor(key, node, path17);
1176
+ return visitor(key, node, path18);
1371
1177
  if (identity.isMap(node))
1372
- return visitor.Map?.(key, node, path17);
1178
+ return visitor.Map?.(key, node, path18);
1373
1179
  if (identity.isSeq(node))
1374
- return visitor.Seq?.(key, node, path17);
1180
+ return visitor.Seq?.(key, node, path18);
1375
1181
  if (identity.isPair(node))
1376
- return visitor.Pair?.(key, node, path17);
1182
+ return visitor.Pair?.(key, node, path18);
1377
1183
  if (identity.isScalar(node))
1378
- return visitor.Scalar?.(key, node, path17);
1184
+ return visitor.Scalar?.(key, node, path18);
1379
1185
  if (identity.isAlias(node))
1380
- return visitor.Alias?.(key, node, path17);
1186
+ return visitor.Alias?.(key, node, path18);
1381
1187
  return;
1382
1188
  }
1383
- function replaceNode(key, path17, node) {
1384
- const parent = path17[path17.length - 1];
1189
+ function replaceNode(key, path18, node) {
1190
+ const parent = path18[path18.length - 1];
1385
1191
  if (identity.isCollection(parent)) {
1386
1192
  parent.items[key] = node;
1387
1193
  } else if (identity.isPair(parent)) {
@@ -1940,10 +1746,10 @@ var require_Collection = __commonJS((exports) => {
1940
1746
  var createNode = require_createNode();
1941
1747
  var identity = require_identity();
1942
1748
  var Node = require_Node();
1943
- function collectionFromPath(schema, path17, value) {
1749
+ function collectionFromPath(schema, path18, value) {
1944
1750
  let v = value;
1945
- for (let i = path17.length - 1;i >= 0; --i) {
1946
- const k = path17[i];
1751
+ for (let i = path18.length - 1;i >= 0; --i) {
1752
+ const k = path18[i];
1947
1753
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
1948
1754
  const a = [];
1949
1755
  a[k] = v;
@@ -1962,7 +1768,7 @@ var require_Collection = __commonJS((exports) => {
1962
1768
  sourceObjects: new Map
1963
1769
  });
1964
1770
  }
1965
- var isEmptyPath = (path17) => path17 == null || typeof path17 === "object" && !!path17[Symbol.iterator]().next().done;
1771
+ var isEmptyPath = (path18) => path18 == null || typeof path18 === "object" && !!path18[Symbol.iterator]().next().done;
1966
1772
 
1967
1773
  class Collection extends Node.NodeBase {
1968
1774
  constructor(type, schema) {
@@ -1983,11 +1789,11 @@ var require_Collection = __commonJS((exports) => {
1983
1789
  copy.range = this.range.slice();
1984
1790
  return copy;
1985
1791
  }
1986
- addIn(path17, value) {
1987
- if (isEmptyPath(path17))
1792
+ addIn(path18, value) {
1793
+ if (isEmptyPath(path18))
1988
1794
  this.add(value);
1989
1795
  else {
1990
- const [key, ...rest] = path17;
1796
+ const [key, ...rest] = path18;
1991
1797
  const node = this.get(key, true);
1992
1798
  if (identity.isCollection(node))
1993
1799
  node.addIn(rest, value);
@@ -1997,8 +1803,8 @@ var require_Collection = __commonJS((exports) => {
1997
1803
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
1998
1804
  }
1999
1805
  }
2000
- deleteIn(path17) {
2001
- const [key, ...rest] = path17;
1806
+ deleteIn(path18) {
1807
+ const [key, ...rest] = path18;
2002
1808
  if (rest.length === 0)
2003
1809
  return this.delete(key);
2004
1810
  const node = this.get(key, true);
@@ -2007,8 +1813,8 @@ var require_Collection = __commonJS((exports) => {
2007
1813
  else
2008
1814
  throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
2009
1815
  }
2010
- getIn(path17, keepScalar) {
2011
- const [key, ...rest] = path17;
1816
+ getIn(path18, keepScalar) {
1817
+ const [key, ...rest] = path18;
2012
1818
  const node = this.get(key, true);
2013
1819
  if (rest.length === 0)
2014
1820
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -2023,15 +1829,15 @@ var require_Collection = __commonJS((exports) => {
2023
1829
  return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag;
2024
1830
  });
2025
1831
  }
2026
- hasIn(path17) {
2027
- const [key, ...rest] = path17;
1832
+ hasIn(path18) {
1833
+ const [key, ...rest] = path18;
2028
1834
  if (rest.length === 0)
2029
1835
  return this.has(key);
2030
1836
  const node = this.get(key, true);
2031
1837
  return identity.isCollection(node) ? node.hasIn(rest) : false;
2032
1838
  }
2033
- setIn(path17, value) {
2034
- const [key, ...rest] = path17;
1839
+ setIn(path18, value) {
1840
+ const [key, ...rest] = path18;
2035
1841
  if (rest.length === 0) {
2036
1842
  this.set(key, value);
2037
1843
  } else {
@@ -2837,7 +2643,7 @@ var require_merge = __commonJS((exports) => {
2837
2643
 
2838
2644
  // node_modules/yaml/dist/nodes/addPairToJSMap.js
2839
2645
  var require_addPairToJSMap = __commonJS((exports) => {
2840
- var log5 = require_log();
2646
+ var log6 = require_log();
2841
2647
  var merge = require_merge();
2842
2648
  var stringify = require_stringify();
2843
2649
  var identity = require_identity();
@@ -2886,7 +2692,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
2886
2692
  let jsonStr = JSON.stringify(strKey);
2887
2693
  if (jsonStr.length > 40)
2888
2694
  jsonStr = jsonStr.substring(0, 36) + '..."';
2889
- log5.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
2695
+ log6.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
2890
2696
  ctx.mapKeyWarned = true;
2891
2697
  }
2892
2698
  return strKey;
@@ -4424,9 +4230,9 @@ var require_Document = __commonJS((exports) => {
4424
4230
  if (assertCollection(this.contents))
4425
4231
  this.contents.add(value);
4426
4232
  }
4427
- addIn(path17, value) {
4233
+ addIn(path18, value) {
4428
4234
  if (assertCollection(this.contents))
4429
- this.contents.addIn(path17, value);
4235
+ this.contents.addIn(path18, value);
4430
4236
  }
4431
4237
  createAlias(node, name) {
4432
4238
  if (!node.anchor) {
@@ -4475,30 +4281,30 @@ var require_Document = __commonJS((exports) => {
4475
4281
  delete(key) {
4476
4282
  return assertCollection(this.contents) ? this.contents.delete(key) : false;
4477
4283
  }
4478
- deleteIn(path17) {
4479
- if (Collection.isEmptyPath(path17)) {
4284
+ deleteIn(path18) {
4285
+ if (Collection.isEmptyPath(path18)) {
4480
4286
  if (this.contents == null)
4481
4287
  return false;
4482
4288
  this.contents = null;
4483
4289
  return true;
4484
4290
  }
4485
- return assertCollection(this.contents) ? this.contents.deleteIn(path17) : false;
4291
+ return assertCollection(this.contents) ? this.contents.deleteIn(path18) : false;
4486
4292
  }
4487
4293
  get(key, keepScalar) {
4488
4294
  return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : undefined;
4489
4295
  }
4490
- getIn(path17, keepScalar) {
4491
- if (Collection.isEmptyPath(path17))
4296
+ getIn(path18, keepScalar) {
4297
+ if (Collection.isEmptyPath(path18))
4492
4298
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
4493
- return identity.isCollection(this.contents) ? this.contents.getIn(path17, keepScalar) : undefined;
4299
+ return identity.isCollection(this.contents) ? this.contents.getIn(path18, keepScalar) : undefined;
4494
4300
  }
4495
4301
  has(key) {
4496
4302
  return identity.isCollection(this.contents) ? this.contents.has(key) : false;
4497
4303
  }
4498
- hasIn(path17) {
4499
- if (Collection.isEmptyPath(path17))
4304
+ hasIn(path18) {
4305
+ if (Collection.isEmptyPath(path18))
4500
4306
  return this.contents !== undefined;
4501
- return identity.isCollection(this.contents) ? this.contents.hasIn(path17) : false;
4307
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path18) : false;
4502
4308
  }
4503
4309
  set(key, value) {
4504
4310
  if (this.contents == null) {
@@ -4507,13 +4313,13 @@ var require_Document = __commonJS((exports) => {
4507
4313
  this.contents.set(key, value);
4508
4314
  }
4509
4315
  }
4510
- setIn(path17, value) {
4511
- if (Collection.isEmptyPath(path17)) {
4316
+ setIn(path18, value) {
4317
+ if (Collection.isEmptyPath(path18)) {
4512
4318
  this.contents = value;
4513
4319
  } else if (this.contents == null) {
4514
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path17), value);
4320
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path18), value);
4515
4321
  } else if (assertCollection(this.contents)) {
4516
- this.contents.setIn(path17, value);
4322
+ this.contents.setIn(path18, value);
4517
4323
  }
4518
4324
  }
4519
4325
  setSchema(version, options = {}) {
@@ -6408,9 +6214,9 @@ var require_cst_visit = __commonJS((exports) => {
6408
6214
  visit.BREAK = BREAK;
6409
6215
  visit.SKIP = SKIP;
6410
6216
  visit.REMOVE = REMOVE;
6411
- visit.itemAtPath = (cst, path17) => {
6217
+ visit.itemAtPath = (cst, path18) => {
6412
6218
  let item = cst;
6413
- for (const [field, index] of path17) {
6219
+ for (const [field, index] of path18) {
6414
6220
  const tok = item?.[field];
6415
6221
  if (tok && "items" in tok) {
6416
6222
  item = tok.items[index];
@@ -6419,23 +6225,23 @@ var require_cst_visit = __commonJS((exports) => {
6419
6225
  }
6420
6226
  return item;
6421
6227
  };
6422
- visit.parentCollection = (cst, path17) => {
6423
- const parent = visit.itemAtPath(cst, path17.slice(0, -1));
6424
- const field = path17[path17.length - 1][0];
6228
+ visit.parentCollection = (cst, path18) => {
6229
+ const parent = visit.itemAtPath(cst, path18.slice(0, -1));
6230
+ const field = path18[path18.length - 1][0];
6425
6231
  const coll = parent?.[field];
6426
6232
  if (coll && "items" in coll)
6427
6233
  return coll;
6428
6234
  throw new Error("Parent collection not found");
6429
6235
  };
6430
- function _visit(path17, item, visitor) {
6431
- let ctrl = visitor(item, path17);
6236
+ function _visit(path18, item, visitor) {
6237
+ let ctrl = visitor(item, path18);
6432
6238
  if (typeof ctrl === "symbol")
6433
6239
  return ctrl;
6434
6240
  for (const field of ["key", "value"]) {
6435
6241
  const token = item[field];
6436
6242
  if (token && "items" in token) {
6437
6243
  for (let i = 0;i < token.items.length; ++i) {
6438
- const ci = _visit(Object.freeze(path17.concat([[field, i]])), token.items[i], visitor);
6244
+ const ci = _visit(Object.freeze(path18.concat([[field, i]])), token.items[i], visitor);
6439
6245
  if (typeof ci === "number")
6440
6246
  i = ci - 1;
6441
6247
  else if (ci === BREAK)
@@ -6446,10 +6252,10 @@ var require_cst_visit = __commonJS((exports) => {
6446
6252
  }
6447
6253
  }
6448
6254
  if (typeof ctrl === "function" && field === "key")
6449
- ctrl = ctrl(item, path17);
6255
+ ctrl = ctrl(item, path18);
6450
6256
  }
6451
6257
  }
6452
- return typeof ctrl === "function" ? ctrl(item, path17) : ctrl;
6258
+ return typeof ctrl === "function" ? ctrl(item, path18) : ctrl;
6453
6259
  }
6454
6260
  exports.visit = visit;
6455
6261
  });
@@ -7718,14 +7524,14 @@ var require_parser = __commonJS((exports) => {
7718
7524
  case "scalar":
7719
7525
  case "single-quoted-scalar":
7720
7526
  case "double-quoted-scalar": {
7721
- const fs14 = this.flowScalar(this.type);
7527
+ const fs15 = this.flowScalar(this.type);
7722
7528
  if (atNextItem || it.value) {
7723
- map.items.push({ start, key: fs14, sep: [] });
7529
+ map.items.push({ start, key: fs15, sep: [] });
7724
7530
  this.onKeyLine = true;
7725
7531
  } else if (it.sep) {
7726
- this.stack.push(fs14);
7532
+ this.stack.push(fs15);
7727
7533
  } else {
7728
- Object.assign(it, { key: fs14, sep: [] });
7534
+ Object.assign(it, { key: fs15, sep: [] });
7729
7535
  this.onKeyLine = true;
7730
7536
  }
7731
7537
  return;
@@ -7853,13 +7659,13 @@ var require_parser = __commonJS((exports) => {
7853
7659
  case "scalar":
7854
7660
  case "single-quoted-scalar":
7855
7661
  case "double-quoted-scalar": {
7856
- const fs14 = this.flowScalar(this.type);
7662
+ const fs15 = this.flowScalar(this.type);
7857
7663
  if (!it || it.value)
7858
- fc.items.push({ start: [], key: fs14, sep: [] });
7664
+ fc.items.push({ start: [], key: fs15, sep: [] });
7859
7665
  else if (it.sep)
7860
- this.stack.push(fs14);
7666
+ this.stack.push(fs15);
7861
7667
  else
7862
- Object.assign(it, { key: fs14, sep: [] });
7668
+ Object.assign(it, { key: fs15, sep: [] });
7863
7669
  return;
7864
7670
  }
7865
7671
  case "flow-map-end":
@@ -8023,7 +7829,7 @@ var require_public_api = __commonJS((exports) => {
8023
7829
  var composer = require_composer();
8024
7830
  var Document = require_Document();
8025
7831
  var errors = require_errors();
8026
- var log5 = require_log();
7832
+ var log6 = require_log();
8027
7833
  var identity = require_identity();
8028
7834
  var lineCounter = require_line_counter();
8029
7835
  var parser = require_parser();
@@ -8075,7 +7881,7 @@ var require_public_api = __commonJS((exports) => {
8075
7881
  const doc = parseDocument(src, options);
8076
7882
  if (!doc)
8077
7883
  return null;
8078
- doc.warnings.forEach((warning) => log5.warn(doc.options.logLevel, warning));
7884
+ doc.warnings.forEach((warning) => log6.warn(doc.options.logLevel, warning));
8079
7885
  if (doc.errors.length > 0) {
8080
7886
  if (doc.options.logLevel !== "silent")
8081
7887
  throw doc.errors[0];
@@ -8241,7 +8047,7 @@ async function runAutoFeedback(cfg, ctx) {
8241
8047
  if (cmds.length === 0) {
8242
8048
  throw new Error("auto-feedback: 至少需要 test_cmd 或 lint_cmd 之一");
8243
8049
  }
8244
- const log13 = ctx.log ?? (() => {});
8050
+ const log14 = ctx.log ?? (() => {});
8245
8051
  const attempt = async () => {
8246
8052
  let last = {
8247
8053
  cmd: "",
@@ -8278,7 +8084,7 @@ ${excerpt}
8278
8084
 
8279
8085
  请基于错误信息修复 pending-changes(用 ast-edit / pending-changes.stage),完成后我会重新跑测试验证。`;
8280
8086
  const result2 = await ctx.dispatchToAgent("coder", prompt);
8281
- log13("info", `auto-feedback round ${round} dispatch summary: ${result2.summary.slice(0, 200)}`);
8087
+ log14("info", `auto-feedback round ${round} dispatch summary: ${result2.summary.slice(0, 200)}`);
8282
8088
  };
8283
8089
  const debugResult = await runWithAutoDebug({
8284
8090
  attempt,
@@ -8299,7 +8105,7 @@ ${excerpt}
8299
8105
  history: debugResult.history
8300
8106
  };
8301
8107
  if (!debugResult.ok && debugResult.stopReason === "max-rounds") {
8302
- log13("warn", `auto-feedback 达 max_retries=${cfg.max_retries},escalate to ${cfg.escalate_to}`);
8108
+ log14("warn", `auto-feedback 达 max_retries=${cfg.max_retries},escalate to ${cfg.escalate_to}`);
8303
8109
  const lastFail = debugResult.history[debugResult.history.length - 1];
8304
8110
  const excerpt = lastFail?.result ? extractErrorExcerpt(lastFail.result, cfg.error_excerpt_lines) : "(无失败结果)";
8305
8111
  const escalatePrompt = `coder 连续 ${cfg.max_retries} 轮自纠失败,错误片段:
@@ -13061,7 +12867,7 @@ function toEntry(r) {
13061
12867
  import { z as z27 } from "zod";
13062
12868
 
13063
12869
  // lib/session-worktree.ts
13064
- init_autonomy();
12870
+ init_worktree_ops();
13065
12871
  init_runtime_paths();
13066
12872
  import { execFile as execFile3 } from "node:child_process";
13067
12873
  import { promises as fs10 } from "node:fs";
@@ -13312,6 +13118,18 @@ async function mergeSessionBack(opts) {
13312
13118
  });
13313
13119
  throw new Error(`mergeSessionBack: squash merge 失败(已 reset 主仓兜底): ${err.message}`);
13314
13120
  }
13121
+ const hasDevOnce = await packageHasScript(mainRoot, "dev:once");
13122
+ if (hasDevOnce) {
13123
+ try {
13124
+ await runCmd("npm", ["run", "dev:once"], mainRoot);
13125
+ } catch (err) {
13126
+ await runGit2(mainRoot, ["reset", "--hard", "HEAD"]).catch(() => {});
13127
+ const msg = err instanceof Error ? err.message : String(err);
13128
+ throw new Error(`dev:once 失败已 reset 主仓: ${msg}`);
13129
+ }
13130
+ } else {
13131
+ console.log(`[session-worktree] skip dev:once: not configured in ${mainRoot}/package.json`);
13132
+ }
13315
13133
  const squashedRaw = await runGit2(wt, ["log", "--format=%s", `${baseSha}..HEAD`]);
13316
13134
  const squashedCommits = squashedRaw.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
13317
13135
  const message = opts.commitMessage ?? buildMergeMessage(opts.sessionId, branch, baseSha, squashedCommits);
@@ -13416,6 +13234,29 @@ function runGit2(cwd, args, timeoutMs = 1e4) {
13416
13234
  });
13417
13235
  });
13418
13236
  }
13237
+ async function packageHasScript(mainRoot, scriptName) {
13238
+ try {
13239
+ const pkgPath = path13.join(mainRoot, "package.json");
13240
+ const raw = await fs10.readFile(pkgPath, "utf8");
13241
+ const pkg = JSON.parse(raw);
13242
+ if (!pkg.scripts || typeof pkg.scripts !== "object")
13243
+ return false;
13244
+ return typeof pkg.scripts[scriptName] === "string";
13245
+ } catch {
13246
+ return false;
13247
+ }
13248
+ }
13249
+ function runCmd(cmd, args, cwd, timeoutMs = 120000) {
13250
+ return new Promise((resolve11, reject) => {
13251
+ execFile3(cmd, args, { cwd, timeout: timeoutMs, windowsHide: true, encoding: "utf8" }, (err, stdout, stderr) => {
13252
+ if (err) {
13253
+ reject(new Error(`${cmd} ${args.join(" ")} (cwd=${cwd}) 失败: ${stderr?.trim() || err.message}`));
13254
+ return;
13255
+ }
13256
+ resolve11(stdout);
13257
+ });
13258
+ });
13259
+ }
13419
13260
  function buildMergeMessage(sessionId, branch, baseSha, squashed) {
13420
13261
  const subject = `session(${sessionId}): merge ${branch}`;
13421
13262
  const body = squashed.length > 0 ? `
@@ -13458,7 +13299,7 @@ async function pruneOrphanWorktrees(mainRoot) {
13458
13299
  let skipped = 0;
13459
13300
  let gitWorktrees = [];
13460
13301
  try {
13461
- const mod = await Promise.resolve().then(() => (init_autonomy(), exports_autonomy));
13302
+ const mod = await Promise.resolve().then(() => (init_worktree_ops(), exports_worktree_ops));
13462
13303
  gitWorktrees = await mod.listWorktrees({ root: resolved });
13463
13304
  } catch {
13464
13305
  gitWorktrees = [];
@@ -14698,6 +14539,212 @@ async function sendParentNotice(client, sessionID, text, opts = {}) {
14698
14539
  // lib/spawner-production.ts
14699
14540
  init_decision_parser();
14700
14541
 
14542
+ // lib/parent-map-store.ts
14543
+ init_runtime_paths();
14544
+ import { promises as fs13 } from "node:fs";
14545
+ import * as path16 from "node:path";
14546
+ var PARENT_MAP_VERSION = 1;
14547
+ var PARENT_MAP_LOCK_TIMEOUT_MS = 2000;
14548
+ var PARENT_MAP_MAX_ENTRIES = 256;
14549
+ function parentMapDir(mainRoot) {
14550
+ return path16.join(runtimeDir(path16.resolve(mainRoot), { ensure: false }), "session-worktrees");
14551
+ }
14552
+ function parentMapPath(mainRoot) {
14553
+ return path16.join(parentMapDir(mainRoot), "parent-map.json");
14554
+ }
14555
+ function parentMapLockPath(mainRoot) {
14556
+ return path16.join(parentMapDir(mainRoot), "parent-map.lock");
14557
+ }
14558
+ async function readParentMapFile(mainRoot) {
14559
+ const file = parentMapPath(mainRoot);
14560
+ try {
14561
+ const raw = await fs13.readFile(file, "utf8");
14562
+ const parsed = JSON.parse(raw);
14563
+ if (parsed.version !== PARENT_MAP_VERSION || !Array.isArray(parsed.entries)) {
14564
+ return { version: PARENT_MAP_VERSION, entries: [] };
14565
+ }
14566
+ return parsed;
14567
+ } catch (err) {
14568
+ const e = err;
14569
+ if (e.code === "ENOENT")
14570
+ return { version: PARENT_MAP_VERSION, entries: [] };
14571
+ return { version: PARENT_MAP_VERSION, entries: [] };
14572
+ }
14573
+ }
14574
+ async function writeParentMapFile(mainRoot, payload) {
14575
+ const file = parentMapPath(mainRoot);
14576
+ await fs13.mkdir(path16.dirname(file), { recursive: true });
14577
+ const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
14578
+ await fs13.writeFile(tmp, JSON.stringify(payload, null, 2), "utf8");
14579
+ await fs13.rename(tmp, file);
14580
+ }
14581
+ function capEntriesByTsDesc(entries) {
14582
+ if (entries.length <= PARENT_MAP_MAX_ENTRIES)
14583
+ return entries;
14584
+ return [...entries].sort((a, b) => b.ts - a.ts).slice(0, PARENT_MAP_MAX_ENTRIES);
14585
+ }
14586
+ async function loadParentMap(mainRoot) {
14587
+ const out = new Map;
14588
+ let file;
14589
+ try {
14590
+ file = await readParentMapFile(mainRoot);
14591
+ } catch {
14592
+ return out;
14593
+ }
14594
+ const valid = [];
14595
+ for (const e of file.entries) {
14596
+ if (!e || typeof e.childID !== "string" || typeof e.parentID !== "string")
14597
+ continue;
14598
+ if (!e.childID || !e.parentID)
14599
+ continue;
14600
+ const ts = typeof e.ts === "number" ? e.ts : Date.now();
14601
+ valid.push({ childID: e.childID, parentID: e.parentID, ts });
14602
+ }
14603
+ const capped = capEntriesByTsDesc(valid);
14604
+ for (const e of capped) {
14605
+ out.set(e.childID, { parentID: e.parentID, ts: e.ts });
14606
+ }
14607
+ return out;
14608
+ }
14609
+ async function mutateParentMap(mainRoot, mutator, opts = {}) {
14610
+ const lockPath = parentMapLockPath(mainRoot);
14611
+ await fs13.mkdir(path16.dirname(lockPath), { recursive: true });
14612
+ const lockOpts = {
14613
+ timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
14614
+ ...opts
14615
+ };
14616
+ await withFileLock(lockPath, async () => {
14617
+ const current = await readParentMapFile(mainRoot);
14618
+ const input = current.entries.slice();
14619
+ const next = await mutator(input);
14620
+ if (!Array.isArray(next))
14621
+ return;
14622
+ const capped = capEntriesByTsDesc(next);
14623
+ await writeParentMapFile(mainRoot, { version: PARENT_MAP_VERSION, entries: capped });
14624
+ }, lockOpts);
14625
+ }
14626
+ async function appendParentEntry(mainRoot, childID, parentID, ts, opts = {}) {
14627
+ if (!childID || !parentID)
14628
+ return;
14629
+ const lockPath = parentMapLockPath(mainRoot);
14630
+ await fs13.mkdir(path16.dirname(lockPath), { recursive: true });
14631
+ const lockOpts = {
14632
+ timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
14633
+ ...opts
14634
+ };
14635
+ await withFileLock(lockPath, async () => {
14636
+ const current = await readParentMapFile(mainRoot);
14637
+ const idx = current.entries.findIndex((e) => e.childID === childID);
14638
+ if (idx >= 0) {
14639
+ current.entries[idx] = { childID, parentID, ts };
14640
+ } else {
14641
+ current.entries.push({ childID, parentID, ts });
14642
+ }
14643
+ const capped = capEntriesByTsDesc(current.entries);
14644
+ await writeParentMapFile(mainRoot, { version: PARENT_MAP_VERSION, entries: capped });
14645
+ }, lockOpts);
14646
+ }
14647
+
14648
+ // lib/parent-map-runtime.ts
14649
+ var log4 = makePluginLogger("parent-map-runtime");
14650
+ var SESSION_PARENT_MAP_TTL_MS = 30 * 60000;
14651
+ var SESSION_PARENT_MAP_MAX_SIZE = PARENT_MAP_MAX_ENTRIES;
14652
+ var sessionParentMap = new Map;
14653
+ var _persistRoot = null;
14654
+ function _setPersistRootForTests(root) {
14655
+ _persistRoot = root;
14656
+ }
14657
+ function _bulkInjectSessionParentMap(entries) {
14658
+ for (const e of entries) {
14659
+ if (!e.childID || !e.parentID)
14660
+ continue;
14661
+ sessionParentMap.set(e.childID, { parentID: e.parentID, ts: e.ts });
14662
+ }
14663
+ }
14664
+ function _capSessionParentMap() {
14665
+ if (sessionParentMap.size <= SESSION_PARENT_MAP_MAX_SIZE)
14666
+ return 0;
14667
+ const sorted = [...sessionParentMap.entries()].sort((a, b) => b[1].ts - a[1].ts);
14668
+ const keep = new Set(sorted.slice(0, SESSION_PARENT_MAP_MAX_SIZE).map(([k]) => k));
14669
+ let removed = 0;
14670
+ for (const k of [...sessionParentMap.keys()]) {
14671
+ if (!keep.has(k)) {
14672
+ sessionParentMap.delete(k);
14673
+ removed++;
14674
+ }
14675
+ }
14676
+ return removed;
14677
+ }
14678
+ function recordSessionParent(childID, parentID, now = Date.now()) {
14679
+ if (!childID || !parentID)
14680
+ return;
14681
+ if (sessionParentMap.has(childID)) {
14682
+ sessionParentMap.set(childID, { parentID, ts: now });
14683
+ } else {
14684
+ if (sessionParentMap.size >= SESSION_PARENT_MAP_MAX_SIZE) {
14685
+ let oldestKey = null;
14686
+ let oldestTs = Number.POSITIVE_INFINITY;
14687
+ for (const [k, v] of sessionParentMap.entries()) {
14688
+ if (v.ts < oldestTs) {
14689
+ oldestTs = v.ts;
14690
+ oldestKey = k;
14691
+ }
14692
+ }
14693
+ if (oldestKey !== null)
14694
+ sessionParentMap.delete(oldestKey);
14695
+ }
14696
+ sessionParentMap.set(childID, { parentID, ts: now });
14697
+ }
14698
+ if (_persistRoot) {
14699
+ appendParentEntry(_persistRoot, childID, parentID, now).catch((err) => {
14700
+ log4.warn("appendParentEntry 失败(已隔离)", {
14701
+ error: err instanceof Error ? err.message : String(err),
14702
+ childID
14703
+ });
14704
+ });
14705
+ }
14706
+ }
14707
+ function lookupParentSessionId(childID, now = Date.now()) {
14708
+ const entry = sessionParentMap.get(childID);
14709
+ if (!entry)
14710
+ return;
14711
+ if (now - entry.ts > SESSION_PARENT_MAP_TTL_MS) {
14712
+ sessionParentMap.delete(childID);
14713
+ return;
14714
+ }
14715
+ return entry.parentID;
14716
+ }
14717
+ function deleteSessionParent(childID) {
14718
+ sessionParentMap.delete(childID);
14719
+ if (_persistRoot) {
14720
+ mutateParentMap(_persistRoot, (entries) => entries.filter((e) => e.childID !== childID)).catch((err) => {
14721
+ log4.warn("mutateParentMap (delete) 失败(已隔离)", {
14722
+ error: err instanceof Error ? err.message : String(err),
14723
+ childID
14724
+ });
14725
+ });
14726
+ }
14727
+ }
14728
+ function sweepExpiredSessionParents(now = Date.now()) {
14729
+ let removed = 0;
14730
+ for (const [k, v] of [...sessionParentMap.entries()]) {
14731
+ if (now - v.ts > SESSION_PARENT_MAP_TTL_MS) {
14732
+ sessionParentMap.delete(k);
14733
+ removed++;
14734
+ }
14735
+ }
14736
+ if (_persistRoot) {
14737
+ mutateParentMap(_persistRoot, (entries) => entries.filter((e) => now - e.ts <= SESSION_PARENT_MAP_TTL_MS)).catch((err) => {
14738
+ log4.warn("mutateParentMap (sweep) 失败(已隔离)", {
14739
+ error: err instanceof Error ? err.message : String(err),
14740
+ removed
14741
+ });
14742
+ });
14743
+ }
14744
+ return removed;
14745
+ }
14746
+
14747
+ // lib/spawner-production.ts
14701
14748
  class ProductionSpawner {
14702
14749
  opts;
14703
14750
  constructor(opts) {
@@ -14713,7 +14760,7 @@ class ProductionSpawner {
14713
14760
  title: `[merge-review] sess=${args.sessionId.slice(0, 8)} r=${args.round}/${args.maxRounds}`,
14714
14761
  ...args.signal ? { signal: args.signal } : {},
14715
14762
  timeoutMs: this.opts.reviewerTimeoutMs ?? 600000
14716
- });
14763
+ }, args.sessionId);
14717
14764
  } catch (err) {
14718
14765
  throw err;
14719
14766
  }
@@ -14745,7 +14792,7 @@ ${r.text.slice(0, 800)}`
14745
14792
  title: `[merge-fix] sess=${args.sessionId.slice(0, 8)}`,
14746
14793
  ...args.signal ? { signal: args.signal } : {},
14747
14794
  timeoutMs: this.opts.coderTimeoutMs ?? 1800000
14748
- });
14795
+ }, args.sessionId);
14749
14796
  } catch (err) {
14750
14797
  throw err;
14751
14798
  }
@@ -14759,7 +14806,7 @@ ${r.text.slice(0, 800)}`
14759
14806
  }
14760
14807
  return { ok: true, summary: r.text || "(coder 无文本输出)" };
14761
14808
  }
14762
- async runSubagent(opts) {
14809
+ async runSubagent(opts, parentSessionIdOverride) {
14763
14810
  let childId;
14764
14811
  try {
14765
14812
  const created = await this.opts.client.session.create({
@@ -14770,6 +14817,17 @@ ${r.text.slice(0, 800)}`
14770
14817
  throw new Error(`session.create 失败: ${describe5(created.error) || "no id"}`);
14771
14818
  }
14772
14819
  childId = created.data.id;
14820
+ const parentSessionId = parentSessionIdOverride ?? this.opts.parentSessionId ?? "";
14821
+ if (parentSessionId) {
14822
+ try {
14823
+ recordSessionParent(childId, parentSessionId);
14824
+ } catch (err) {
14825
+ this.opts.log?.("warn", `[spawner] recordSessionParent 失败 child=${childId}`, {
14826
+ err: describe5(err),
14827
+ parentSessionId
14828
+ });
14829
+ }
14830
+ }
14773
14831
  const promptPromise = Promise.resolve(this.opts.client.session.prompt({
14774
14832
  path: { id: childId },
14775
14833
  body: {
@@ -14808,7 +14866,7 @@ async function raceAbortTimeout(p, signal, timeoutMs, label) {
14808
14866
  e.name = "AbortError";
14809
14867
  throw e;
14810
14868
  }
14811
- return await new Promise((resolve13, reject) => {
14869
+ return await new Promise((resolve14, reject) => {
14812
14870
  let settled = false;
14813
14871
  const timer = setTimeout(() => {
14814
14872
  if (settled)
@@ -14833,7 +14891,7 @@ async function raceAbortTimeout(p, signal, timeoutMs, label) {
14833
14891
  settled = true;
14834
14892
  clearTimeout(timer);
14835
14893
  signal?.removeEventListener("abort", onAbort);
14836
- resolve13(v);
14894
+ resolve14(v);
14837
14895
  }, (e) => {
14838
14896
  if (settled)
14839
14897
  return;
@@ -14893,8 +14951,8 @@ function clip4(s, max) {
14893
14951
  }
14894
14952
 
14895
14953
  // lib/codeforge-runtime.ts
14896
- import { promises as fs13 } from "node:fs";
14897
- import * as path16 from "node:path";
14954
+ import { promises as fs14 } from "node:fs";
14955
+ import * as path17 from "node:path";
14898
14956
  var DEFAULT_RUNTIME = {
14899
14957
  autonomy: {
14900
14958
  default_mode: "semi",
@@ -14938,10 +14996,10 @@ function loadRuntimeSync(opts = {}) {
14938
14996
  }
14939
14997
  async function loadRuntime(opts = {}) {
14940
14998
  const root = opts.root ?? process.cwd();
14941
- const abs = path16.resolve(root, opts.file ?? CONFIG_FILE);
14999
+ const abs = path17.resolve(root, opts.file ?? CONFIG_FILE);
14942
15000
  let raw;
14943
15001
  try {
14944
- raw = await fs13.readFile(abs, "utf8");
15002
+ raw = await fs14.readFile(abs, "utf8");
14945
15003
  } catch (e) {
14946
15004
  const code = e.code;
14947
15005
  if (code === "ENOENT") {
@@ -15168,9 +15226,9 @@ function normalizeVariant(raw) {
15168
15226
  return "default";
15169
15227
  return raw;
15170
15228
  }
15171
- async function showToast(client, payload, log4) {
15229
+ async function showToast(client, payload, log5) {
15172
15230
  if (typeof client?.tui?.showToast !== "function") {
15173
- log4?.debug?.("tui.showToast 不可用,noop");
15231
+ log5?.debug?.("tui.showToast 不可用,noop");
15174
15232
  return false;
15175
15233
  }
15176
15234
  try {
@@ -15184,13 +15242,13 @@ async function showToast(client, payload, log4) {
15184
15242
  });
15185
15243
  return true;
15186
15244
  } catch (err) {
15187
- log4?.warn("tui.showToast 抛错(已隔离)", {
15245
+ log5?.warn("tui.showToast 抛错(已隔离)", {
15188
15246
  error: err instanceof Error ? err.message : String(err)
15189
15247
  });
15190
15248
  return false;
15191
15249
  }
15192
15250
  }
15193
- var log4 = makePluginLogger(PLUGIN_NAME6);
15251
+ var log5 = makePluginLogger(PLUGIN_NAME6);
15194
15252
  var toolHeartbeatServer = async (ctx) => {
15195
15253
  logLifecycle(PLUGIN_NAME6, "activate", {
15196
15254
  directory: ctx.directory,
@@ -15206,7 +15264,7 @@ var toolHeartbeatServer = async (ctx) => {
15206
15264
  const alert = buildAlert(r);
15207
15265
  if (!alert)
15208
15266
  continue;
15209
- const sent = await showToast(client, { message: alert.message, variant: alert.variant }, log4);
15267
+ const sent = await showToast(client, { message: alert.message, variant: alert.variant }, log5);
15210
15268
  r.alertsSent.add(alert.threshold);
15211
15269
  safeWriteLog(PLUGIN_NAME6, {
15212
15270
  hook: "interval",
@@ -15949,7 +16007,7 @@ var handler7 = codeforgeToolsServer;
15949
16007
 
15950
16008
  // plugins/discover-spec-suggest.ts
15951
16009
  import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "node:fs";
15952
- import { join as join13 } from "node:path";
16010
+ import { join as join14 } from "node:path";
15953
16011
 
15954
16012
  // lib/handoff-schema.ts
15955
16013
  import { z as z31 } from "zod";
@@ -16116,9 +16174,9 @@ function validateHandoff(rawYaml, fileSize) {
16116
16174
  const result = HandoffSchema.safeParse(parsed);
16117
16175
  if (!result.success) {
16118
16176
  const first = result.error.issues[0];
16119
- const path17 = first?.path?.join(".") ?? "(root)";
16177
+ const path18 = first?.path?.join(".") ?? "(root)";
16120
16178
  const msg = first?.message ?? "unknown";
16121
- return { ok: false, reason: `schema 校验失败:${path17}: ${msg}` };
16179
+ return { ok: false, reason: `schema 校验失败:${path18}: ${msg}` };
16122
16180
  }
16123
16181
  return { ok: true, data: result.data, schemaVersion: result.data.schema_version };
16124
16182
  }
@@ -16135,7 +16193,7 @@ var SESSION_TTL_MS = 24 * 60 * 60 * 1000;
16135
16193
  var MATCH_THRESHOLD = 0.15;
16136
16194
  var MAX_CANDIDATES = 3;
16137
16195
  var NUDGE_MAX_LEN = 1500;
16138
- var SPECS_REL_DIR = join13(".codeforge", "specs");
16196
+ var SPECS_REL_DIR = join14(".codeforge", "specs");
16139
16197
  var sessionMap = new Map;
16140
16198
  function pruneIfOversize() {
16141
16199
  while (sessionMap.size > SESSION_CAP) {
@@ -16241,18 +16299,18 @@ function loadSpecs(rootDir, opts = {}) {
16241
16299
  const dirReader = opts.dirReader ?? defaultDirReader;
16242
16300
  const dirExists = opts.dirExists ?? defaultDirExists;
16243
16301
  const statReader = opts.statReader ?? defaultStatReader;
16244
- const log5 = makePluginLogger(PLUGIN_NAME8);
16245
- const specsRoot = join13(rootDir, SPECS_REL_DIR);
16302
+ const log6 = makePluginLogger(PLUGIN_NAME8);
16303
+ const specsRoot = join14(rootDir, SPECS_REL_DIR);
16246
16304
  const records = [];
16247
16305
  if (!dirExists(specsRoot)) {
16248
- log5.info(`specs 目录不存在,plugin 将 no-op`, { specsRoot });
16306
+ log6.info(`specs 目录不存在,plugin 将 no-op`, { specsRoot });
16249
16307
  return records;
16250
16308
  }
16251
16309
  let entries;
16252
16310
  try {
16253
16311
  entries = dirReader(specsRoot);
16254
16312
  } catch (err) {
16255
- log5.warn(`specs 目录读取失败`, {
16313
+ log6.warn(`specs 目录读取失败`, {
16256
16314
  specsRoot,
16257
16315
  error: err instanceof Error ? err.message : String(err)
16258
16316
  });
@@ -16260,15 +16318,15 @@ function loadSpecs(rootDir, opts = {}) {
16260
16318
  }
16261
16319
  for (const entry of entries) {
16262
16320
  if (!isValidSlug(entry)) {
16263
- log5.info(`跳过非合法 slug 命名的条目`, { entry });
16321
+ log6.info(`跳过非合法 slug 命名的条目`, { entry });
16264
16322
  continue;
16265
16323
  }
16266
- const specDir = join13(specsRoot, entry);
16324
+ const specDir = join14(specsRoot, entry);
16267
16325
  let dirStat;
16268
16326
  try {
16269
16327
  dirStat = statReader(specDir);
16270
16328
  } catch (err) {
16271
- log5.warn(`spec 目录 stat 失败`, {
16329
+ log6.warn(`spec 目录 stat 失败`, {
16272
16330
  specDir,
16273
16331
  error: err instanceof Error ? err.message : String(err)
16274
16332
  });
@@ -16276,7 +16334,7 @@ function loadSpecs(rootDir, opts = {}) {
16276
16334
  }
16277
16335
  if (!dirStat.isDirectory)
16278
16336
  continue;
16279
- const handoffPath = join13(specDir, "handoff.yaml");
16337
+ const handoffPath = join14(specDir, "handoff.yaml");
16280
16338
  let fileStat;
16281
16339
  try {
16282
16340
  fileStat = statReader(handoffPath);
@@ -16286,7 +16344,7 @@ function loadSpecs(rootDir, opts = {}) {
16286
16344
  if (fileStat.isDirectory)
16287
16345
  continue;
16288
16346
  if (fileStat.size > MAX_HANDOFF_SIZE) {
16289
- log5.warn(`handoff.yaml 超过 ${MAX_HANDOFF_SIZE} 字节上限,已跳过`, {
16347
+ log6.warn(`handoff.yaml 超过 ${MAX_HANDOFF_SIZE} 字节上限,已跳过`, {
16290
16348
  handoffPath,
16291
16349
  size: fileStat.size
16292
16350
  });
@@ -16296,7 +16354,7 @@ function loadSpecs(rootDir, opts = {}) {
16296
16354
  try {
16297
16355
  rawYaml = reader(handoffPath);
16298
16356
  } catch (err) {
16299
- log5.warn(`handoff.yaml 读取失败`, {
16357
+ log6.warn(`handoff.yaml 读取失败`, {
16300
16358
  handoffPath,
16301
16359
  error: err instanceof Error ? err.message : String(err)
16302
16360
  });
@@ -16304,7 +16362,7 @@ function loadSpecs(rootDir, opts = {}) {
16304
16362
  }
16305
16363
  const result = validateHandoff(rawYaml, fileStat.size);
16306
16364
  if (!result.ok) {
16307
- log5.warn(`handoff.yaml 校验失败,已跳过`, {
16365
+ log6.warn(`handoff.yaml 校验失败,已跳过`, {
16308
16366
  handoffPath,
16309
16367
  reason: result.reason
16310
16368
  });
@@ -16312,7 +16370,7 @@ function loadSpecs(rootDir, opts = {}) {
16312
16370
  }
16313
16371
  const handoff = result.data;
16314
16372
  if (handoff.slug !== entry) {
16315
- log5.warn(`handoff.slug 与目录名不一致,以目录名为准`, {
16373
+ log6.warn(`handoff.slug 与目录名不一致,以目录名为准`, {
16316
16374
  dir: entry,
16317
16375
  handoffSlug: handoff.slug
16318
16376
  });
@@ -16335,7 +16393,7 @@ function loadSpecs(rootDir, opts = {}) {
16335
16393
  handoff
16336
16394
  });
16337
16395
  }
16338
- log5.info(`specs 加载完成`, {
16396
+ log6.info(`specs 加载完成`, {
16339
16397
  specsRoot,
16340
16398
  loaded: records.length,
16341
16399
  total_entries: entries.length
@@ -16369,14 +16427,14 @@ function renderCandidatesNudge(matched) {
16369
16427
  return body.slice(0, NUDGE_MAX_LEN - 4) + `
16370
16428
  …`;
16371
16429
  }
16372
- var log5 = makePluginLogger(PLUGIN_NAME8);
16430
+ var log6 = makePluginLogger(PLUGIN_NAME8);
16373
16431
  var discoverSpecSuggestServer = async (ctx) => {
16374
16432
  try {
16375
16433
  const loaded = loadSpecs(ctx.directory ?? process.cwd());
16376
16434
  specIndex.length = 0;
16377
16435
  specIndex.push(...loaded);
16378
16436
  } catch (err) {
16379
- log5.warn(`loadSpecs 失败(plugin 将 no-op)`, {
16437
+ log6.warn(`loadSpecs 失败(plugin 将 no-op)`, {
16380
16438
  error: err instanceof Error ? err.message : String(err)
16381
16439
  });
16382
16440
  }
@@ -16682,9 +16740,9 @@ var CODEFORGE_CONSTRAINTS = [
16682
16740
  priority: 8
16683
16741
  }
16684
16742
  ];
16685
- async function seedConstraints(client, log6) {
16743
+ async function seedConstraints(client, log7) {
16686
16744
  if (!client.hasTransport()) {
16687
- log6?.debug?.(`[${PLUGIN_NAME9}] seedConstraints: transport 不可用,跳过`, {});
16745
+ log7?.debug?.(`[${PLUGIN_NAME9}] seedConstraints: transport 不可用,跳过`, {});
16688
16746
  return { ok: false, reason: "transport_unavailable" };
16689
16747
  }
16690
16748
  try {
@@ -16698,7 +16756,7 @@ async function seedConstraints(client, log6) {
16698
16756
  });
16699
16757
  if (result && typeof result === "object" && "ok" in result && result.ok === false) {
16700
16758
  const r = result;
16701
- log6?.warn(`[${PLUGIN_NAME9}] seedConstraints 降级`, {
16759
+ log7?.warn(`[${PLUGIN_NAME9}] seedConstraints 降级`, {
16702
16760
  reason: r.reason,
16703
16761
  message: r.message
16704
16762
  });
@@ -16708,15 +16766,15 @@ async function seedConstraints(client, log6) {
16708
16766
  message: r.message
16709
16767
  };
16710
16768
  }
16711
- log6?.info(`[${PLUGIN_NAME9}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
16769
+ log7?.info(`[${PLUGIN_NAME9}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
16712
16770
  return { ok: true, itemsWritten: CODEFORGE_CONSTRAINTS.length };
16713
16771
  } catch (err) {
16714
16772
  const message = err instanceof Error ? err.message : String(err);
16715
- log6?.warn(`[${PLUGIN_NAME9}] seedConstraints 失败(已静默)`, { error: message });
16773
+ log7?.warn(`[${PLUGIN_NAME9}] seedConstraints 失败(已静默)`, { error: message });
16716
16774
  return { ok: false, reason: "exception", message };
16717
16775
  }
16718
16776
  }
16719
- function createSystemInjectedHook(client, sessionId, log6) {
16777
+ function createSystemInjectedHook(client, sessionId, log7) {
16720
16778
  const section = `topic:auto-context-${sessionId ?? "global"}`;
16721
16779
  return async (markdown) => {
16722
16780
  try {
@@ -16727,7 +16785,7 @@ function createSystemInjectedHook(client, sessionId, log6) {
16727
16785
  });
16728
16786
  if (result && typeof result === "object" && "ok" in result && result.ok === false) {
16729
16787
  const r = result;
16730
- log6?.warn(`[${PLUGIN_NAME9}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
16788
+ log7?.warn(`[${PLUGIN_NAME9}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
16731
16789
  sessionId,
16732
16790
  section,
16733
16791
  preview: markdown.slice(0, 200),
@@ -16736,12 +16794,12 @@ function createSystemInjectedHook(client, sessionId, log6) {
16736
16794
  });
16737
16795
  return;
16738
16796
  }
16739
- log6?.info(`[${PLUGIN_NAME9}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
16797
+ log7?.info(`[${PLUGIN_NAME9}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
16740
16798
  sessionId,
16741
16799
  section
16742
16800
  });
16743
16801
  } catch (err) {
16744
- log6?.warn(`[${PLUGIN_NAME9}] system-injected 抛异常,降级到 observe-only`, {
16802
+ log7?.warn(`[${PLUGIN_NAME9}] system-injected 抛异常,降级到 observe-only`, {
16745
16803
  sessionId,
16746
16804
  section,
16747
16805
  preview: markdown.slice(0, 200),
@@ -16758,7 +16816,7 @@ function inflightKey(sessionId, query) {
16758
16816
  async function runKhSearchAndInject(args) {
16759
16817
  const { query, ctx, opts, mode } = args;
16760
16818
  const cfg = opts.config ?? DEFAULT_CONFIG4;
16761
- const log6 = ctx.log;
16819
+ const log7 = ctx.log;
16762
16820
  const startedAt = Date.now();
16763
16821
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
16764
16822
  let timer = null;
@@ -16784,7 +16842,7 @@ async function runKhSearchAndInject(args) {
16784
16842
  });
16785
16843
  result = await racer(searchPromise, cfg.timeoutMs);
16786
16844
  } catch (err) {
16787
- log6?.warn(`[${PLUGIN_NAME9}] client.search threw (sync or async), return null`, {
16845
+ log7?.warn(`[${PLUGIN_NAME9}] client.search threw (sync or async), return null`, {
16788
16846
  query,
16789
16847
  elapsedMs: Date.now() - startedAt,
16790
16848
  sessionId: ctx.sessionId,
@@ -16794,7 +16852,7 @@ async function runKhSearchAndInject(args) {
16794
16852
  return null;
16795
16853
  }
16796
16854
  if (result === "__timeout__") {
16797
- log6?.warn(`[${PLUGIN_NAME9}] timeout`, {
16855
+ log7?.warn(`[${PLUGIN_NAME9}] timeout`, {
16798
16856
  query,
16799
16857
  ms: cfg.timeoutMs,
16800
16858
  elapsedMs: Date.now() - startedAt,
@@ -16804,7 +16862,7 @@ async function runKhSearchAndInject(args) {
16804
16862
  return null;
16805
16863
  }
16806
16864
  if (!result.ok) {
16807
- log6?.warn(`[${PLUGIN_NAME9}] kh degraded`, {
16865
+ log7?.warn(`[${PLUGIN_NAME9}] kh degraded`, {
16808
16866
  reason: result.reason,
16809
16867
  query,
16810
16868
  elapsedMs: Date.now() - startedAt,
@@ -16816,7 +16874,7 @@ async function runKhSearchAndInject(args) {
16816
16874
  const filtered = result.insights.filter((i) => (i.confidence ?? 0) >= cfg.minConfidence);
16817
16875
  if (filtered.length === 0) {
16818
16876
  opts.cache.record(query, []);
16819
- log6?.debug?.(`[${PLUGIN_NAME9}] no candidate above threshold`, {
16877
+ log7?.debug?.(`[${PLUGIN_NAME9}] no candidate above threshold`, {
16820
16878
  query,
16821
16879
  rawCount: result.insights.length,
16822
16880
  elapsedMs: Date.now() - startedAt,
@@ -16829,7 +16887,7 @@ async function runKhSearchAndInject(args) {
16829
16887
  try {
16830
16888
  await ctx.injectContext(payload.markdown);
16831
16889
  } catch (err) {
16832
- log6?.warn(`[${PLUGIN_NAME9}] injectContext threw`, {
16890
+ log7?.warn(`[${PLUGIN_NAME9}] injectContext threw`, {
16833
16891
  error: err instanceof Error ? err.message : String(err),
16834
16892
  query,
16835
16893
  sessionId: ctx.sessionId
@@ -16837,7 +16895,7 @@ async function runKhSearchAndInject(args) {
16837
16895
  }
16838
16896
  }
16839
16897
  opts.cache.record(query, filtered);
16840
- log6?.info(`[${PLUGIN_NAME9}] inject complete (${mode})`, {
16898
+ log7?.info(`[${PLUGIN_NAME9}] inject complete (${mode})`, {
16841
16899
  query,
16842
16900
  mode,
16843
16901
  candidateCount: filtered.length,
@@ -16850,21 +16908,21 @@ async function handleMessage2(raw, opts) {
16850
16908
  const cfg = opts.config ?? DEFAULT_CONFIG4;
16851
16909
  const mode = opts.mode ?? INJECTION_MODE;
16852
16910
  const ctx = raw ?? {};
16853
- const log6 = ctx.log;
16911
+ const log7 = ctx.log;
16854
16912
  const text = (ctx.content ?? "").trim();
16855
16913
  if (!shouldInject(text, cfg)) {
16856
- log6?.debug?.(`[${PLUGIN_NAME9}] skip (filter)`, { textLen: text.length });
16914
+ log7?.debug?.(`[${PLUGIN_NAME9}] skip (filter)`, { textLen: text.length });
16857
16915
  return;
16858
16916
  }
16859
16917
  const query = extractQuery(text);
16860
16918
  if (!query)
16861
16919
  return;
16862
16920
  if (opts.cache.shouldSkip(query)) {
16863
- log6?.debug?.(`[${PLUGIN_NAME9}] cache hit, skip`, { query });
16921
+ log7?.debug?.(`[${PLUGIN_NAME9}] cache hit, skip`, { query });
16864
16922
  return;
16865
16923
  }
16866
16924
  if (inflight2.size >= INFLIGHT_CAP) {
16867
- log6?.warn(`[${PLUGIN_NAME9}] inflight cap reached, skip`, {
16925
+ log7?.warn(`[${PLUGIN_NAME9}] inflight cap reached, skip`, {
16868
16926
  query,
16869
16927
  inflightSize: inflight2.size,
16870
16928
  cap: INFLIGHT_CAP,
@@ -16875,7 +16933,7 @@ async function handleMessage2(raw, opts) {
16875
16933
  const key = inflightKey(ctx.sessionId, query);
16876
16934
  inflight2.add(key);
16877
16935
  runKhSearchAndInject({ query, ctx, opts, mode }).catch((err) => {
16878
- log6?.warn(`[${PLUGIN_NAME9}] runKhSearchAndInject 顶层兜底捕获`, {
16936
+ log7?.warn(`[${PLUGIN_NAME9}] runKhSearchAndInject 顶层兜底捕获`, {
16879
16937
  error: err instanceof Error ? err.message : String(err),
16880
16938
  query,
16881
16939
  sessionId: ctx.sessionId
@@ -16888,7 +16946,7 @@ logLifecycle(PLUGIN_NAME9, "import");
16888
16946
  var sharedClient2 = new KhClient;
16889
16947
  var sharedCache = new QueryCache(DEFAULT_CONFIG4.cacheTtlMs);
16890
16948
  var khAutoContextServer = async (ctx) => {
16891
- const log6 = makePluginLogger(PLUGIN_NAME9);
16949
+ const log7 = makePluginLogger(PLUGIN_NAME9);
16892
16950
  const runtimeMode = resolveInjectionMode(sharedClient2);
16893
16951
  logLifecycle(PLUGIN_NAME9, "activate", {
16894
16952
  directory: ctx.directory,
@@ -16897,8 +16955,8 @@ var khAutoContextServer = async (ctx) => {
16897
16955
  mode: runtimeMode,
16898
16956
  transport_available: sharedClient2.hasTransport()
16899
16957
  });
16900
- seedConstraints(sharedClient2, log6).catch((err) => {
16901
- log6.warn("seedConstraints 顶层兜底捕获", {
16958
+ seedConstraints(sharedClient2, log7).catch((err) => {
16959
+ log7.warn("seedConstraints 顶层兜底捕获", {
16902
16960
  error: err instanceof Error ? err.message : String(err)
16903
16961
  });
16904
16962
  });
@@ -16908,8 +16966,8 @@ var khAutoContextServer = async (ctx) => {
16908
16966
  const text = extractUserText(output);
16909
16967
  if (!text)
16910
16968
  return;
16911
- const injectContext = runtimeMode === "system-injected" ? createSystemInjectedHook(sharedClient2, input.sessionID, log6) : async (markdown) => {
16912
- log6.info(`KH context candidate (${markdown.length} chars)`, {
16969
+ const injectContext = runtimeMode === "system-injected" ? createSystemInjectedHook(sharedClient2, input.sessionID, log7) : async (markdown) => {
16970
+ log7.info(`KH context candidate (${markdown.length} chars)`, {
16913
16971
  sessionID: input.sessionID,
16914
16972
  mode: runtimeMode,
16915
16973
  preview: markdown.slice(0, 200)
@@ -16919,7 +16977,7 @@ var khAutoContextServer = async (ctx) => {
16919
16977
  content: text,
16920
16978
  sessionId: input.sessionID,
16921
16979
  injectContext,
16922
- log: log6
16980
+ log: log7
16923
16981
  }, { client: sharedClient2, cache: sharedCache, mode: runtimeMode });
16924
16982
  });
16925
16983
  },
@@ -16933,7 +16991,7 @@ var khAutoContextServer = async (ctx) => {
16933
16991
  if (typeof sid !== "string" || !sid)
16934
16992
  return;
16935
16993
  sharedKhCache.onSessionEnd(sid);
16936
- log6.debug?.(`[${PLUGIN_NAME9}] session.idle: cleared shared cache`, {
16994
+ log7.debug?.(`[${PLUGIN_NAME9}] session.idle: cleared shared cache`, {
16937
16995
  sessionID: sid
16938
16996
  });
16939
16997
  });
@@ -17165,7 +17223,7 @@ function hasRecentSmartSearch(messages, recentRounds) {
17165
17223
  }
17166
17224
  return false;
17167
17225
  }
17168
- function evaluate2(input) {
17226
+ function evaluate(input) {
17169
17227
  const messages = Array.isArray(input.messages) ? input.messages : [];
17170
17228
  const sessionId = input.sessionId ?? "unknown";
17171
17229
  const threshold = input.threshold ?? DEFAULT_THRESHOLD;
@@ -17205,15 +17263,15 @@ function evaluate2(input) {
17205
17263
  lastTriggerAt.set(sessionId, now);
17206
17264
  return { triggered: true, reason, sessionId };
17207
17265
  }
17208
- function handleObserve(raw, log6) {
17266
+ function handleObserve(raw, log7) {
17209
17267
  const ctx = raw ?? {};
17210
17268
  if (!Array.isArray(ctx.messages))
17211
17269
  return null;
17212
17270
  try {
17213
- const result = evaluate2(ctx);
17271
+ const result = evaluate(ctx);
17214
17272
  if (result.triggered && result.reason) {
17215
17273
  const reasonStr = formatReason(result.reason);
17216
- log6?.info(`[${PLUGIN_NAME10}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
17274
+ log7?.info(`[${PLUGIN_NAME10}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
17217
17275
  sessionId: result.sessionId,
17218
17276
  reason: result.reason,
17219
17277
  suggestion: "调用 smart_search 查项目历史;完成后用 save_chat_insight 沉淀"
@@ -17221,7 +17279,7 @@ function handleObserve(raw, log6) {
17221
17279
  }
17222
17280
  return result;
17223
17281
  } catch (err) {
17224
- log6?.warn(`[${PLUGIN_NAME10}] evaluate 异常(已隔离)`, {
17282
+ log7?.warn(`[${PLUGIN_NAME10}] evaluate 异常(已隔离)`, {
17225
17283
  error: err instanceof Error ? err.message : String(err)
17226
17284
  });
17227
17285
  return null;
@@ -17237,7 +17295,7 @@ function formatReason(r) {
17237
17295
  return `no-search-in-recent-rounds (last ${r.rounds})`;
17238
17296
  }
17239
17297
  }
17240
- var log6 = makePluginLogger(PLUGIN_NAME10);
17298
+ var log7 = makePluginLogger(PLUGIN_NAME10);
17241
17299
  var khReminderServer = async (ctx) => {
17242
17300
  logLifecycle(PLUGIN_NAME10, "activate", {
17243
17301
  directory: ctx.directory,
@@ -17263,7 +17321,7 @@ var khReminderServer = async (ctx) => {
17263
17321
  `);
17264
17322
  return { role, content };
17265
17323
  });
17266
- const result = handleObserve({ messages: flat, sessionId }, log6);
17324
+ const result = handleObserve({ messages: flat, sessionId }, log7);
17267
17325
  if (!result)
17268
17326
  return;
17269
17327
  safeWriteLog(PLUGIN_NAME10, {
@@ -17282,14 +17340,14 @@ var khReminderServer = async (ctx) => {
17282
17340
  var handler10 = khReminderServer;
17283
17341
 
17284
17342
  // lib/memories.ts
17285
- import { promises as fs14 } from "node:fs";
17286
- import * as path17 from "node:path";
17343
+ import { promises as fs15 } from "node:fs";
17344
+ import * as path18 from "node:path";
17287
17345
  import * as os5 from "node:os";
17288
17346
  function resolveConfig(c) {
17289
17347
  return {
17290
17348
  projectRoot: c.projectRoot,
17291
17349
  homeDir: c.homeDir ?? os5.homedir(),
17292
- projectName: c.projectName ?? path17.basename(c.projectRoot),
17350
+ projectName: c.projectName ?? path18.basename(c.projectRoot),
17293
17351
  kh: c.kh,
17294
17352
  now: c.now ?? Date.now,
17295
17353
  log: c.log ?? (() => {}),
@@ -17298,13 +17356,13 @@ function resolveConfig(c) {
17298
17356
  }
17299
17357
  function fileFor(scope, cfg) {
17300
17358
  if (scope === "project") {
17301
- return path17.join(cfg.projectRoot, ".codeforge", "memories.json");
17359
+ return path18.join(cfg.projectRoot, ".codeforge", "memories.json");
17302
17360
  }
17303
- return path17.join(cfg.homeDir, ".codeforge", "memories.json");
17361
+ return path18.join(cfg.homeDir, ".codeforge", "memories.json");
17304
17362
  }
17305
17363
  async function readBank(p) {
17306
17364
  try {
17307
- const raw = await fs14.readFile(p, "utf8");
17365
+ const raw = await fs15.readFile(p, "utf8");
17308
17366
  const arr = JSON.parse(raw);
17309
17367
  if (!Array.isArray(arr))
17310
17368
  return [];
@@ -17314,10 +17372,10 @@ async function readBank(p) {
17314
17372
  }
17315
17373
  }
17316
17374
  async function writeBank(p, items) {
17317
- await fs14.mkdir(path17.dirname(p), { recursive: true });
17375
+ await fs15.mkdir(path18.dirname(p), { recursive: true });
17318
17376
  const tmp = `${p}.tmp`;
17319
- await fs14.writeFile(tmp, JSON.stringify(items, null, 2), "utf8");
17320
- await fs14.rename(tmp, p);
17377
+ await fs15.writeFile(tmp, JSON.stringify(items, null, 2), "utf8");
17378
+ await fs15.rename(tmp, p);
17321
17379
  }
17322
17380
  function isMemory(x) {
17323
17381
  if (!x || typeof x !== "object")
@@ -17569,23 +17627,23 @@ async function handleMessage3(raw, opts) {
17569
17627
  const cfg = opts.cfg ?? DEFAULT_CONFIG5;
17570
17628
  const cache2 = opts.cache ?? new QueryCache2(cfg.cacheTtlMs);
17571
17629
  const ctx = raw ?? {};
17572
- const log7 = ctx.log;
17630
+ const log8 = ctx.log;
17573
17631
  const text = (ctx.content ?? "").trim();
17574
17632
  if (!text)
17575
17633
  return { kind: "noop", reason: "empty", mode: INJECTION_MODE2 };
17576
17634
  const dir = parseDirective(text);
17577
17635
  if (dir.kind !== "none") {
17578
- return await handleDirective(dir, ctx, opts.memCfg, log7);
17636
+ return await handleDirective(dir, ctx, opts.memCfg, log8);
17579
17637
  }
17580
17638
  if (!shouldRecall(text, cfg)) {
17581
- log7?.debug?.(`[${PLUGIN_NAME11}] skip (filter)`, { textLen: text.length });
17639
+ log8?.debug?.(`[${PLUGIN_NAME11}] skip (filter)`, { textLen: text.length });
17582
17640
  return { kind: "noop", reason: "filtered", mode: INJECTION_MODE2 };
17583
17641
  }
17584
17642
  const query = extractQuery2(text);
17585
17643
  if (!query)
17586
17644
  return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE2 };
17587
17645
  if (cache2.shouldSkip(query)) {
17588
- log7?.debug?.(`[${PLUGIN_NAME11}] cache hit`, { query });
17646
+ log8?.debug?.(`[${PLUGIN_NAME11}] cache hit`, { query });
17589
17647
  return { kind: "noop", reason: "cache", mode: INJECTION_MODE2 };
17590
17648
  }
17591
17649
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
@@ -17610,7 +17668,7 @@ async function handleMessage3(raw, opts) {
17610
17668
  }, opts.memCfg);
17611
17669
  const result = await racer(injectPromise, cfg.timeoutMs);
17612
17670
  if (result === "__timeout__") {
17613
- log7?.warn(`[${PLUGIN_NAME11}] timeout`, { query, ms: cfg.timeoutMs });
17671
+ log8?.warn(`[${PLUGIN_NAME11}] timeout`, { query, ms: cfg.timeoutMs });
17614
17672
  cache2.record(query, 0);
17615
17673
  return { kind: "noop", reason: "timeout", mode: INJECTION_MODE2 };
17616
17674
  }
@@ -17622,29 +17680,29 @@ async function handleMessage3(raw, opts) {
17622
17680
  try {
17623
17681
  await ctx.injectContext(result.text);
17624
17682
  } catch (err) {
17625
- log7?.warn(`[${PLUGIN_NAME11}] injectContext threw`, {
17683
+ log8?.warn(`[${PLUGIN_NAME11}] injectContext threw`, {
17626
17684
  error: err instanceof Error ? err.message : String(err)
17627
17685
  });
17628
17686
  }
17629
17687
  }
17630
17688
  cache2.record(query, result.recalled);
17631
- log7?.info(`[${PLUGIN_NAME11}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
17689
+ log8?.info(`[${PLUGIN_NAME11}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
17632
17690
  return { kind: "injected", payload: result, mode: INJECTION_MODE2 };
17633
17691
  }
17634
- async function handleDirective(dir, ctx, memCfg, log7) {
17692
+ async function handleDirective(dir, ctx, memCfg, log8) {
17635
17693
  if (dir.kind === "remember") {
17636
17694
  const r = await addMemory({ scope: dir.scope, content: dir.content, source: "directive" }, memCfg);
17637
17695
  if (r.ok) {
17638
17696
  const target = r.written_to === "kh" ? "KH" : "本地";
17639
17697
  await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
17640
- log7?.info(`[${PLUGIN_NAME11}] /remember ok`, {
17698
+ log8?.info(`[${PLUGIN_NAME11}] /remember ok`, {
17641
17699
  scope: dir.scope,
17642
17700
  id: r.id,
17643
17701
  mode: INJECTION_MODE2
17644
17702
  });
17645
17703
  } else {
17646
17704
  await safeReply(ctx, `❌ 记忆失败:${r.error ?? "unknown"}`);
17647
- log7?.warn(`[${PLUGIN_NAME11}] /remember failed`, { error: r.error });
17705
+ log8?.warn(`[${PLUGIN_NAME11}] /remember failed`, { error: r.error });
17648
17706
  }
17649
17707
  return { kind: "remembered", result: r, mode: INJECTION_MODE2 };
17650
17708
  }
@@ -17681,7 +17739,7 @@ function buildMemCfg(directory) {
17681
17739
  return { projectRoot: directory };
17682
17740
  }
17683
17741
  var memoriesContextServer = async (ctx) => {
17684
- const log7 = makePluginLogger(PLUGIN_NAME11);
17742
+ const log8 = makePluginLogger(PLUGIN_NAME11);
17685
17743
  const memCfg = buildMemCfg(ctx.directory);
17686
17744
  logLifecycle(PLUGIN_NAME11, "activate", {
17687
17745
  directory: ctx.directory,
@@ -17698,19 +17756,19 @@ var memoriesContextServer = async (ctx) => {
17698
17756
  content: text,
17699
17757
  sessionId: input.sessionID,
17700
17758
  injectContext: async (markdown) => {
17701
- log7.info(`memories context candidate (${markdown.length} chars)`, {
17759
+ log8.info(`memories context candidate (${markdown.length} chars)`, {
17702
17760
  sessionID: input.sessionID,
17703
17761
  mode: INJECTION_MODE2,
17704
17762
  preview: markdown.slice(0, 200)
17705
17763
  });
17706
17764
  },
17707
17765
  reply: async (msg) => {
17708
- log7.info(`directive reply (observe-only): ${msg}`, {
17766
+ log8.info(`directive reply (observe-only): ${msg}`, {
17709
17767
  sessionID: input.sessionID,
17710
17768
  mode: INJECTION_MODE2
17711
17769
  });
17712
17770
  },
17713
- log: log7
17771
+ log: log8
17714
17772
  }, { cache: sharedCache2, memCfg });
17715
17773
  });
17716
17774
  }
@@ -17785,7 +17843,7 @@ fallback 链已用尽:${meta.chain.join(" → ")}`;
17785
17843
  完整链:${meta.chain.join(" → ")}`;
17786
17844
  }
17787
17845
  var modelFallbackServer = async (ctx) => {
17788
- const log7 = makePluginLogger(PLUGIN_NAME12);
17846
+ const log8 = makePluginLogger(PLUGIN_NAME12);
17789
17847
  loadOnce(ctx.directory ?? process.cwd());
17790
17848
  logLifecycle(PLUGIN_NAME12, "activate", {
17791
17849
  directory: ctx.directory,
@@ -17844,7 +17902,7 @@ var modelFallbackServer = async (ctx) => {
17844
17902
  const model = props.model ?? "unknown/unknown";
17845
17903
  const meta = buildFallbackMeta(state.config, agent, model);
17846
17904
  const suggestion = meta ? buildSuggestion(meta, String(message)) : `⚠️ ${agent}/${model} 失败:${message}`;
17847
- log7.warn(`[${PLUGIN_NAME12}] ${suggestion}`);
17905
+ log8.warn(`[${PLUGIN_NAME12}] ${suggestion}`);
17848
17906
  safeWriteLog(PLUGIN_NAME12, {
17849
17907
  hook: "event.error",
17850
17908
  eventType: e.type,
@@ -17864,100 +17922,13 @@ import { promises as fsPromises } from "node:fs";
17864
17922
  import * as path19 from "node:path";
17865
17923
  init_runtime_paths();
17866
17924
  init_global_config();
17867
-
17868
- // lib/parent-map-store.ts
17869
- init_runtime_paths();
17870
- import { promises as fs15 } from "node:fs";
17871
- import * as path18 from "node:path";
17872
- var PARENT_MAP_VERSION = 1;
17873
- var PARENT_MAP_LOCK_TIMEOUT_MS = 2000;
17874
- function parentMapDir(mainRoot) {
17875
- return path18.join(runtimeDir(path18.resolve(mainRoot), { ensure: false }), "session-worktrees");
17876
- }
17877
- function parentMapPath(mainRoot) {
17878
- return path18.join(parentMapDir(mainRoot), "parent-map.json");
17879
- }
17880
- function parentMapLockPath(mainRoot) {
17881
- return path18.join(parentMapDir(mainRoot), "parent-map.lock");
17882
- }
17883
- async function readParentMapFile(mainRoot) {
17884
- const file = parentMapPath(mainRoot);
17885
- try {
17886
- const raw = await fs15.readFile(file, "utf8");
17887
- const parsed = JSON.parse(raw);
17888
- if (parsed.version !== PARENT_MAP_VERSION || !Array.isArray(parsed.entries)) {
17889
- return { version: PARENT_MAP_VERSION, entries: [] };
17890
- }
17891
- return parsed;
17892
- } catch (err) {
17893
- const e = err;
17894
- if (e.code === "ENOENT")
17895
- return { version: PARENT_MAP_VERSION, entries: [] };
17896
- return { version: PARENT_MAP_VERSION, entries: [] };
17897
- }
17898
- }
17899
- async function writeParentMapFile(mainRoot, payload) {
17900
- const file = parentMapPath(mainRoot);
17901
- await fs15.mkdir(path18.dirname(file), { recursive: true });
17902
- const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
17903
- await fs15.writeFile(tmp, JSON.stringify(payload, null, 2), "utf8");
17904
- await fs15.rename(tmp, file);
17905
- }
17906
- async function loadParentMap(mainRoot) {
17907
- const out = new Map;
17908
- let file;
17909
- try {
17910
- file = await readParentMapFile(mainRoot);
17911
- } catch {
17912
- return out;
17913
- }
17914
- for (const e of file.entries) {
17915
- if (!e || typeof e.childID !== "string" || typeof e.parentID !== "string")
17916
- continue;
17917
- if (!e.childID || !e.parentID)
17918
- continue;
17919
- const ts = typeof e.ts === "number" ? e.ts : Date.now();
17920
- out.set(e.childID, { parentID: e.parentID, ts });
17921
- }
17922
- return out;
17923
- }
17924
- async function writeParentMap(mainRoot, snapshot, opts = {}) {
17925
- const lockPath = parentMapLockPath(mainRoot);
17926
- await fs15.mkdir(path18.dirname(lockPath), { recursive: true });
17927
- const lockOpts = {
17928
- timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
17929
- ...opts
17930
- };
17931
- await withFileLock(lockPath, async () => {
17932
- const entries = [];
17933
- for (const [childID, v] of snapshot.entries()) {
17934
- entries.push({ childID, parentID: v.parentID, ts: v.ts });
17935
- }
17936
- await writeParentMapFile(mainRoot, { version: PARENT_MAP_VERSION, entries });
17937
- }, lockOpts);
17938
- }
17939
- async function appendParentEntry(mainRoot, childID, parentID, ts, opts = {}) {
17940
- if (!childID || !parentID)
17941
- return;
17942
- const lockPath = parentMapLockPath(mainRoot);
17943
- await fs15.mkdir(path18.dirname(lockPath), { recursive: true });
17944
- const lockOpts = {
17945
- timeoutMs: opts.timeoutMs ?? PARENT_MAP_LOCK_TIMEOUT_MS,
17946
- ...opts
17947
- };
17948
- await withFileLock(lockPath, async () => {
17949
- const current = await readParentMapFile(mainRoot);
17950
- const idx = current.entries.findIndex((e) => e.childID === childID);
17951
- if (idx >= 0) {
17952
- current.entries[idx] = { childID, parentID, ts };
17953
- } else {
17954
- current.entries.push({ childID, parentID, ts });
17955
- }
17956
- await writeParentMapFile(mainRoot, current);
17957
- }, lockOpts);
17958
- }
17959
-
17960
- // plugins/subtask-heartbeat.ts
17925
+ var recordSessionParent2 = recordSessionParent;
17926
+ var lookupParentSessionId2 = lookupParentSessionId;
17927
+ var deleteSessionParent2 = deleteSessionParent;
17928
+ var sweepExpiredSessionParents2 = sweepExpiredSessionParents;
17929
+ var _bulkInjectSessionParentMap2 = _bulkInjectSessionParentMap;
17930
+ var _capSessionParentMap2 = _capSessionParentMap;
17931
+ var _setPersistRootForTests2 = _setPersistRootForTests;
17961
17932
  var PLUGIN_NAME13 = "subtask-heartbeat";
17962
17933
  logLifecycle(PLUGIN_NAME13, "import", {});
17963
17934
  var HEARTBEAT_INTERVAL_MS2 = 30000;
@@ -17968,94 +17939,13 @@ var PENDING_TASK_TTL_MS = 60000;
17968
17939
  var PENDING_TASK_MAX_PARENTS = 64;
17969
17940
  var PENDING_TASK_MAX_PER_PARENT = 16;
17970
17941
  var DESCRIPTION_MAX_LEN = 60;
17971
- var SESSION_PARENT_MAP_TTL_MS = 30 * 60000;
17972
- var SESSION_PARENT_MAP_MAX_SIZE = 256;
17973
17942
  var PARENT_PARSE_FAIL_MAX_LOG = 10;
17974
17943
  var inflight3 = new Map;
17975
17944
  var pendingTask = new Map;
17976
- var sessionParentMap = new Map;
17977
17945
  var _parentParseFailLogged = 0;
17978
- var _persistRoot = null;
17979
- function _bulkInjectSessionParentMap(entries) {
17980
- for (const e of entries) {
17981
- if (!e.childID || !e.parentID)
17982
- continue;
17983
- sessionParentMap.set(e.childID, { parentID: e.parentID, ts: e.ts });
17984
- }
17985
- }
17986
17946
  function _snapshotInflight() {
17987
17947
  return [...inflight3.values()].map((r) => ({ ...r }));
17988
17948
  }
17989
- function recordSessionParent(childID, parentID, now = Date.now()) {
17990
- if (!childID || !parentID)
17991
- return;
17992
- if (sessionParentMap.has(childID)) {
17993
- sessionParentMap.set(childID, { parentID, ts: now });
17994
- } else {
17995
- if (sessionParentMap.size >= SESSION_PARENT_MAP_MAX_SIZE) {
17996
- let oldestKey = null;
17997
- let oldestTs = Number.POSITIVE_INFINITY;
17998
- for (const [k, v] of sessionParentMap.entries()) {
17999
- if (v.ts < oldestTs) {
18000
- oldestTs = v.ts;
18001
- oldestKey = k;
18002
- }
18003
- }
18004
- if (oldestKey !== null)
18005
- sessionParentMap.delete(oldestKey);
18006
- }
18007
- sessionParentMap.set(childID, { parentID, ts: now });
18008
- }
18009
- if (_persistRoot) {
18010
- appendParentEntry(_persistRoot, childID, parentID, now).catch((err) => {
18011
- log7.warn("appendParentEntry 失败(已隔离)", {
18012
- error: err instanceof Error ? err.message : String(err),
18013
- childID
18014
- });
18015
- });
18016
- }
18017
- }
18018
- function lookupParentSessionId(childID, now = Date.now()) {
18019
- const entry = sessionParentMap.get(childID);
18020
- if (!entry)
18021
- return;
18022
- if (now - entry.ts > SESSION_PARENT_MAP_TTL_MS) {
18023
- sessionParentMap.delete(childID);
18024
- return;
18025
- }
18026
- return entry.parentID;
18027
- }
18028
- function deleteSessionParent(childID) {
18029
- sessionParentMap.delete(childID);
18030
- if (_persistRoot) {
18031
- const snapshot = new Map(sessionParentMap);
18032
- writeParentMap(_persistRoot, snapshot).catch((err) => {
18033
- log7.warn("writeParentMap (delete) 失败(已隔离)", {
18034
- error: err instanceof Error ? err.message : String(err),
18035
- childID
18036
- });
18037
- });
18038
- }
18039
- }
18040
- function sweepExpiredSessionParents(now = Date.now()) {
18041
- let removed = 0;
18042
- for (const [k, v] of [...sessionParentMap.entries()]) {
18043
- if (now - v.ts > SESSION_PARENT_MAP_TTL_MS) {
18044
- sessionParentMap.delete(k);
18045
- removed++;
18046
- }
18047
- }
18048
- if (removed > 0 && _persistRoot) {
18049
- const snapshot = new Map(sessionParentMap);
18050
- writeParentMap(_persistRoot, snapshot).catch((err) => {
18051
- log7.warn("writeParentMap (sweep) 失败(已隔离)", {
18052
- error: err instanceof Error ? err.message : String(err),
18053
- removed
18054
- });
18055
- });
18056
- }
18057
- return removed;
18058
- }
18059
17949
  function detectUnparsedParentID(event) {
18060
17950
  if (!event || typeof event !== "object")
18061
17951
  return false;
@@ -18329,33 +18219,13 @@ function buildAfterLogLine(toolName, ok, durationMs, now = Date.now()) {
18329
18219
  duration_ms: durationMs
18330
18220
  });
18331
18221
  }
18332
- function buildSuccessNotice(r, logPath, now = Date.now()) {
18333
- const agent = r.agent ? titleCase(r.agent) : "subagent";
18334
- const elapsed = fmtElapsed(now - r.startedAt);
18335
- return [`✅ ${agent} 完成(${elapsed})`, `\uD83D\uDCC4 完整日志: ${logPath}`].join(`
18336
- `);
18337
- }
18338
- function buildFailureNotice(r, endedType, logPath, worktreePath, now = Date.now()) {
18339
- const agent = r.agent ? titleCase(r.agent) : "subagent";
18340
- const elapsed = fmtElapsed(now - r.startedAt);
18341
- const verb = endedType === "session.deleted" ? "被取消" : "失败";
18342
- const lines = [
18343
- `❌ ${agent} ${verb}(${endedType}, ${elapsed})`,
18344
- `\uD83D\uDCC4 完整日志: ${logPath}`
18345
- ];
18346
- if (worktreePath)
18347
- lines.push(`\uD83D\uDD0D worktree 保留: ${worktreePath}`);
18348
- lines.push(`\uD83D\uDCA1 排查: cat ${logPath} | tail -50`);
18349
- return lines.join(`
18350
- `);
18351
- }
18352
- async function appendSubagentLog(filePath, line, log7) {
18222
+ async function appendSubagentLog(filePath, line, log8) {
18353
18223
  try {
18354
18224
  await fsPromises.mkdir(path19.dirname(filePath), { recursive: true });
18355
18225
  await fsPromises.appendFile(filePath, line + `
18356
18226
  `, "utf8");
18357
18227
  } catch (err) {
18358
- log7?.debug?.("appendSubagentLog 失败(已隔离)", {
18228
+ log8?.debug?.("appendSubagentLog 失败(已隔离)", {
18359
18229
  error: err instanceof Error ? err.message : String(err),
18360
18230
  file: filePath
18361
18231
  });
@@ -18380,9 +18250,9 @@ function normalizeVariant2(raw) {
18380
18250
  return "default";
18381
18251
  return raw;
18382
18252
  }
18383
- async function showToast2(client, payload, log7) {
18253
+ async function showToast2(client, payload, log8) {
18384
18254
  if (typeof client?.tui?.showToast !== "function") {
18385
- log7?.debug?.("tui.showToast 不可用,noop");
18255
+ log8?.debug?.("tui.showToast 不可用,noop");
18386
18256
  return false;
18387
18257
  }
18388
18258
  try {
@@ -18396,13 +18266,13 @@ async function showToast2(client, payload, log7) {
18396
18266
  });
18397
18267
  return true;
18398
18268
  } catch (err) {
18399
- log7?.warn("tui.showToast 抛错(已隔离)", {
18269
+ log8?.warn("tui.showToast 抛错(已隔离)", {
18400
18270
  error: err instanceof Error ? err.message : String(err)
18401
18271
  });
18402
18272
  return false;
18403
18273
  }
18404
18274
  }
18405
- var log7 = makePluginLogger(PLUGIN_NAME13);
18275
+ var log8 = makePluginLogger(PLUGIN_NAME13);
18406
18276
  var subtaskHeartbeatServer = async (ctx) => {
18407
18277
  logLifecycle(PLUGIN_NAME13, "activate", {
18408
18278
  directory: ctx.directory,
@@ -18410,7 +18280,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18410
18280
  });
18411
18281
  const client = ctx.client;
18412
18282
  const cwd = ctx.directory;
18413
- _persistRoot = cwd;
18283
+ _setPersistRootForTests2(cwd);
18414
18284
  try {
18415
18285
  const restored = await loadParentMap(cwd);
18416
18286
  if (restored.size > 0) {
@@ -18419,15 +18289,17 @@ var subtaskHeartbeatServer = async (ctx) => {
18419
18289
  parentID: v.parentID,
18420
18290
  ts: v.ts
18421
18291
  }));
18422
- _bulkInjectSessionParentMap(entries);
18292
+ _bulkInjectSessionParentMap2(entries);
18293
+ const cappedOut = _capSessionParentMap2();
18423
18294
  safeWriteLog(PLUGIN_NAME13, {
18424
18295
  hook: "activate",
18425
18296
  type: "parent-map.restore",
18426
- restored: restored.size
18297
+ restored: restored.size,
18298
+ capped: cappedOut
18427
18299
  });
18428
18300
  }
18429
18301
  } catch (err) {
18430
- log7.warn("loadParentMap 失败(已隔离),降级为空表", {
18302
+ log8.warn("loadParentMap 失败(已隔离),降级为空表", {
18431
18303
  error: err instanceof Error ? err.message : String(err)
18432
18304
  });
18433
18305
  }
@@ -18437,7 +18309,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18437
18309
  if (swept > 0) {
18438
18310
  safeWriteLog(PLUGIN_NAME13, { hook: "interval", pending_task_swept: swept });
18439
18311
  }
18440
- const sweptParents = sweepExpiredSessionParents();
18312
+ const sweptParents = sweepExpiredSessionParents2();
18441
18313
  if (sweptParents > 0) {
18442
18314
  safeWriteLog(PLUGIN_NAME13, { hook: "interval", session_parent_swept: sweptParents });
18443
18315
  }
@@ -18446,7 +18318,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18446
18318
  return;
18447
18319
  for (const r of beats) {
18448
18320
  const t = buildHeartbeatToast(r);
18449
- const sent = await showToast2(client, t, log7);
18321
+ const sent = await showToast2(client, t, log8);
18450
18322
  safeWriteLog(PLUGIN_NAME13, {
18451
18323
  hook: "interval",
18452
18324
  child: r.childID,
@@ -18468,7 +18340,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18468
18340
  try {
18469
18341
  if (detectUnparsedParentID(event) && _parentParseFailLogged < PARENT_PARSE_FAIL_MAX_LOG) {
18470
18342
  _parentParseFailLogged++;
18471
- log7.warn("session.created 含 parentID 关键字但 extractCreatedChild 解析失败,可能 SDK schema 变更", {
18343
+ log8.warn("session.created 含 parentID 关键字但 extractCreatedChild 解析失败,可能 SDK schema 变更", {
18472
18344
  sample_count: _parentParseFailLogged,
18473
18345
  hint: "如频繁出现请检查 plugins/subtask-heartbeat.ts::extractCreatedChild 是否适配新 SDK"
18474
18346
  });
@@ -18482,9 +18354,9 @@ var subtaskHeartbeatServer = async (ctx) => {
18482
18354
  const created = extractCreatedChild(event);
18483
18355
  if (created) {
18484
18356
  try {
18485
- recordSessionParent(created.childID, created.parentID);
18357
+ recordSessionParent2(created.childID, created.parentID);
18486
18358
  } catch (err) {
18487
- log7.warn("recordSessionParent 抛错(已隔离)", {
18359
+ log8.warn("recordSessionParent 抛错(已隔离)", {
18488
18360
  error: err instanceof Error ? err.message : String(err)
18489
18361
  });
18490
18362
  }
@@ -18505,7 +18377,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18505
18377
  description_len: record.description?.length ?? 0
18506
18378
  });
18507
18379
  const startToast = buildStartToast(record);
18508
- const sent = await showToast2(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log7);
18380
+ const sent = await showToast2(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log8);
18509
18381
  safeWriteLog(PLUGIN_NAME13, {
18510
18382
  hook: "event",
18511
18383
  type: "session.created.toast",
@@ -18519,13 +18391,13 @@ var subtaskHeartbeatServer = async (ctx) => {
18519
18391
  if (ended) {
18520
18392
  if (ended.type === "session.deleted") {
18521
18393
  try {
18522
- deleteSessionParent(ended.sessionID);
18394
+ deleteSessionParent2(ended.sessionID);
18523
18395
  } catch {}
18524
18396
  }
18525
18397
  const r = clearInflight2(ended.sessionID);
18526
18398
  if (r) {
18527
18399
  const t = buildEndToast(r, ended.type);
18528
- const sent = await showToast2(client, t, log7);
18400
+ const sent = await showToast2(client, t, log8);
18529
18401
  safeWriteLog(PLUGIN_NAME13, {
18530
18402
  hook: "event",
18531
18403
  type: ended.type,
@@ -18534,32 +18406,10 @@ var subtaskHeartbeatServer = async (ctx) => {
18534
18406
  toast_sent: sent,
18535
18407
  end_toast_message: t.message
18536
18408
  });
18537
- const logPath = subagentLogPath(cwd, r.parentID, r.childID);
18538
- const isSuccess = ended.type === "session.idle";
18539
- let text;
18540
- if (isSuccess) {
18541
- text = buildSuccessNotice(r, logPath);
18542
- } else {
18543
- let worktreePath = null;
18544
- try {
18545
- const entry = await getSessionWorktree(r.childID, cwd);
18546
- worktreePath = entry?.worktreePath ?? null;
18547
- } catch {}
18548
- text = buildFailureNotice(r, ended.type, logPath, worktreePath);
18409
+ if (ended.type === "session.deleted") {
18410
+ const logFile = subagentLogPath(cwd, r.parentID, r.childID);
18411
+ fsPromises.unlink(logFile).catch(() => {});
18549
18412
  }
18550
- const ocClient = ctx.client;
18551
- const noticeSent = await sendParentNotice(ocClient, r.parentID, text, {
18552
- directory: cwd,
18553
- log: (lvl, msg, data) => log7[lvl === "info" ? "info" : "warn"](msg, data)
18554
- });
18555
- safeWriteLog(PLUGIN_NAME13, {
18556
- hook: "event",
18557
- type: `${ended.type}.notice`,
18558
- child: r.childID,
18559
- parent: r.parentID,
18560
- notice_sent: noticeSent,
18561
- success: isSuccess
18562
- });
18563
18413
  }
18564
18414
  }
18565
18415
  });
@@ -18605,7 +18455,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18605
18455
  const args = output?.args ?? null;
18606
18456
  const line = buildBeforeLogLine(input.tool, args);
18607
18457
  const file = subagentLogPath(cwd, rec.parentID, rec.childID);
18608
- appendSubagentLog(file, line, log7);
18458
+ appendSubagentLog(file, line, log8);
18609
18459
  }
18610
18460
  }
18611
18461
  });
@@ -18630,7 +18480,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18630
18480
  if (isLogPersistenceEnabled(cwd)) {
18631
18481
  const line = buildAfterLogLine(input.tool, true, durationMs);
18632
18482
  const file = subagentLogPath(cwd, rec.parentID, rec.childID);
18633
- appendSubagentLog(file, line, log7);
18483
+ appendSubagentLog(file, line, log8);
18634
18484
  }
18635
18485
  });
18636
18486
  }
@@ -18672,7 +18522,7 @@ function formatInflightMarkdown(snapshot, now = Date.now()) {
18672
18522
  `);
18673
18523
  }
18674
18524
  var parallelStatusServer = async (ctx) => {
18675
- const log8 = makePluginLogger(PLUGIN_NAME14);
18525
+ const log9 = makePluginLogger(PLUGIN_NAME14);
18676
18526
  logLifecycle(PLUGIN_NAME14, "activate", { directory: ctx.directory });
18677
18527
  return {
18678
18528
  "command.execute.before": async (input, output) => {
@@ -18692,9 +18542,9 @@ var parallelStatusServer = async (ctx) => {
18692
18542
  synthetic: false
18693
18543
  });
18694
18544
  }
18695
- log8.info(`[${PLUGIN_NAME14}] 已回写 ${snapshot.length} 条 inflight`);
18545
+ log9.info(`[${PLUGIN_NAME14}] 已回写 ${snapshot.length} 条 inflight`);
18696
18546
  } catch (err) {
18697
- log8.error(`[${PLUGIN_NAME14}] command.execute.before 异常(已隔离)`, {
18547
+ log9.error(`[${PLUGIN_NAME14}] command.execute.before 异常(已隔离)`, {
18698
18548
  error: err instanceof Error ? err.message : String(err)
18699
18549
  });
18700
18550
  }
@@ -18770,7 +18620,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18770
18620
  const result = new Map;
18771
18621
  const safeSet = new Set(PARALLEL_SAFE_TOOLS);
18772
18622
  const unionTools = new Set;
18773
- const log8 = makePluginLogger(PLUGIN_NAME15);
18623
+ const log9 = makePluginLogger(PLUGIN_NAME15);
18774
18624
  for (const dir of candidateDirs) {
18775
18625
  if (!dirExists(dir))
18776
18626
  continue;
@@ -18778,7 +18628,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18778
18628
  try {
18779
18629
  entries = dirReader(dir);
18780
18630
  } catch (err) {
18781
- log8.warn(`agents 目录读取失败(已跳过)`, {
18631
+ log9.warn(`agents 目录读取失败(已跳过)`, {
18782
18632
  dir,
18783
18633
  error: err instanceof Error ? err.message : String(err)
18784
18634
  });
@@ -18792,7 +18642,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18792
18642
  try {
18793
18643
  content = reader(path20);
18794
18644
  } catch (err) {
18795
- log8.warn(`agent.md 读取失败(已跳过)`, {
18645
+ log9.warn(`agent.md 读取失败(已跳过)`, {
18796
18646
  path: path20,
18797
18647
  error: err instanceof Error ? err.message : String(err)
18798
18648
  });
@@ -18800,7 +18650,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
18800
18650
  }
18801
18651
  const parsed = parseAgentFrontmatter(content);
18802
18652
  if (!parsed) {
18803
- log8.warn(`agent frontmatter 解析失败(已跳过)`, { path: path20 });
18653
+ log9.warn(`agent frontmatter 解析失败(已跳过)`, { path: path20 });
18804
18654
  continue;
18805
18655
  }
18806
18656
  if (result.has(parsed.name))
@@ -18878,7 +18728,7 @@ This directive is re-injected every turn — it is not optional advice.
18878
18728
  return body.slice(0, NUDGE_MAX_LEN2 - 4) + `
18879
18729
  …`;
18880
18730
  }
18881
- var log8 = makePluginLogger(PLUGIN_NAME15);
18731
+ var log9 = makePluginLogger(PLUGIN_NAME15);
18882
18732
  var parallelToolNudgeServer = async (ctx) => {
18883
18733
  try {
18884
18734
  const loaded = loadAgentToolsMap(ctx.directory ?? process.cwd());
@@ -18886,14 +18736,14 @@ var parallelToolNudgeServer = async (ctx) => {
18886
18736
  for (const [k, v] of loaded.entries())
18887
18737
  agentToolsMap.set(k, v);
18888
18738
  } catch (err) {
18889
- log8.warn(`loadAgentToolsMap 失败(plugin 将 no-op)`, {
18739
+ log9.warn(`loadAgentToolsMap 失败(plugin 将 no-op)`, {
18890
18740
  error: err instanceof Error ? err.message : String(err)
18891
18741
  });
18892
18742
  }
18893
18743
  const realAgentCount = Math.max(0, agentToolsMap.size - (agentToolsMap.has(DEFAULT_AGENT_KEY) ? 1 : 0));
18894
18744
  if (realAgentCount === 0) {
18895
18745
  agentToolsMap.clear();
18896
- log8.warn(`0 real agents loaded; plugin will be no-op for this session`, {
18746
+ log9.warn(`0 real agents loaded; plugin will be no-op for this session`, {
18897
18747
  directory: ctx.directory
18898
18748
  });
18899
18749
  }
@@ -19486,7 +19336,7 @@ function renderPrompt(plan) {
19486
19336
  return lines.join(`
19487
19337
  `);
19488
19338
  }
19489
- var log9 = makePluginLogger(PLUGIN_NAME17);
19339
+ var log10 = makePluginLogger(PLUGIN_NAME17);
19490
19340
  var _lastInjection = null;
19491
19341
  var sessionRecoveryServer = async (ctx) => {
19492
19342
  logLifecycle(PLUGIN_NAME17, "activate", { directory: ctx.directory });
@@ -19502,7 +19352,7 @@ var sessionRecoveryServer = async (ctx) => {
19502
19352
  const root = typeof e.properties?.["root"] === "string" ? e.properties["root"] : ctx.directory ?? process.cwd();
19503
19353
  const r = await processSessionStart(sid, {
19504
19354
  root,
19505
- log: log9,
19355
+ log: log10,
19506
19356
  injectRecovery: (inj) => {
19507
19357
  _lastInjection = inj;
19508
19358
  }
@@ -19517,7 +19367,7 @@ var sessionRecoveryServer = async (ctx) => {
19517
19367
  pending_blocks_count: r.pendingBlocks?.length ?? 0
19518
19368
  });
19519
19369
  if (r.injected && r.plan) {
19520
- log9.info(`[${PLUGIN_NAME17}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason}, pending_blocks=${r.pendingBlocks?.length ?? 0})`);
19370
+ log10.info(`[${PLUGIN_NAME17}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason}, pending_blocks=${r.pendingBlocks?.length ?? 0})`);
19521
19371
  }
19522
19372
  });
19523
19373
  }
@@ -19530,7 +19380,7 @@ import { promises as fs18 } from "node:fs";
19530
19380
  import * as path22 from "node:path";
19531
19381
 
19532
19382
  // lib/parallel-merge.ts
19533
- init_autonomy();
19383
+ init_worktree_ops();
19534
19384
  function resolveMergeFns(deps) {
19535
19385
  return {
19536
19386
  commitWorktreeIfDirtyFn: deps.commitWorktreeIfDirty ?? commitWorktreeIfDirty,
@@ -19541,7 +19391,7 @@ function resolveMergeFns(deps) {
19541
19391
  worktreeHasChangesFn: deps.worktreeHasChanges ?? worktreeHasChanges
19542
19392
  };
19543
19393
  }
19544
- async function mergeOneAttempt(r, opts, mergeFns, log10) {
19394
+ async function mergeOneAttempt(r, opts, mergeFns, log11) {
19545
19395
  const t0 = Date.now();
19546
19396
  const root = opts.mergeRoot;
19547
19397
  const {
@@ -19623,7 +19473,7 @@ async function mergeOneAttempt(r, opts, mergeFns, log10) {
19623
19473
  await mergeAbortFn({ root });
19624
19474
  } catch (abortErr) {
19625
19475
  abortFailed = true;
19626
- log10("error", `[parallel-merge] mergeAbort 失败(仓库可能锁死)`, {
19476
+ log11("error", `[parallel-merge] mergeAbort 失败(仓库可能锁死)`, {
19627
19477
  id: r.id,
19628
19478
  error: describe6(abortErr)
19629
19479
  });
@@ -19639,13 +19489,13 @@ async function mergeOneAttempt(r, opts, mergeFns, log10) {
19639
19489
  }
19640
19490
  attempt.ok = true;
19641
19491
  attempt.durationMs = Date.now() - t0;
19642
- await safeRemoveWorktree(removeWorktreeFn, root, wt, log10, r.id);
19492
+ await safeRemoveWorktree(removeWorktreeFn, root, wt, log11, r.id);
19643
19493
  return { attempt };
19644
19494
  } else {
19645
19495
  attempt.ok = true;
19646
19496
  attempt.skippedReason = "no_changes";
19647
19497
  attempt.durationMs = Date.now() - t0;
19648
- await safeRemoveWorktree(removeWorktreeFn, root, wt, log10, r.id);
19498
+ await safeRemoveWorktree(removeWorktreeFn, root, wt, log11, r.id);
19649
19499
  return { attempt };
19650
19500
  }
19651
19501
  } catch (err) {
@@ -19659,7 +19509,7 @@ async function mergeOneAttempt(r, opts, mergeFns, log10) {
19659
19509
  }
19660
19510
  }
19661
19511
  async function mergeWorktrees(results, opts, deps) {
19662
- const log10 = deps.log ?? (() => {});
19512
+ const log11 = deps.log ?? (() => {});
19663
19513
  const root = opts.mergeRoot;
19664
19514
  const mergeFns = resolveMergeFns(deps);
19665
19515
  const report = {
@@ -19701,10 +19551,10 @@ async function mergeWorktrees(results, opts, deps) {
19701
19551
  };
19702
19552
  report.attempts.push(attempt);
19703
19553
  report.skipped++;
19704
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
19554
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
19705
19555
  }
19706
19556
  for (const r of mergeable) {
19707
- const { attempt, pendingItem } = await mergeOneAttempt(r, { mergeRoot: root }, mergeFns, log10);
19557
+ const { attempt, pendingItem } = await mergeOneAttempt(r, { mergeRoot: root }, mergeFns, log11);
19708
19558
  report.attempts.push(attempt);
19709
19559
  if (attempt.ok && !attempt.skippedReason)
19710
19560
  report.merged++;
@@ -19714,12 +19564,12 @@ async function mergeWorktrees(results, opts, deps) {
19714
19564
  report.conflicted++;
19715
19565
  if (pendingItem)
19716
19566
  report.pendingWorktrees.push(pendingItem);
19717
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
19567
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
19718
19568
  }
19719
19569
  return report;
19720
19570
  }
19721
19571
  function createMergeQueue(opts, deps = {}) {
19722
- const log10 = deps.log ?? (() => {});
19572
+ const log11 = deps.log ?? (() => {});
19723
19573
  const mergeFns = {
19724
19574
  commitWorktreeIfDirtyFn: deps.commitWorktreeIfDirtyFn ?? commitWorktreeIfDirty,
19725
19575
  tryMergeFn: deps.tryMergeFn ?? tryMerge,
@@ -19760,10 +19610,10 @@ function createMergeQueue(opts, deps = {}) {
19760
19610
  conflicts: []
19761
19611
  });
19762
19612
  }
19763
- await fireMergeAttempt(opts.onMergeAttempt, attempt2, idx++, log10);
19613
+ await fireMergeAttempt(opts.onMergeAttempt, attempt2, idx++, log11);
19764
19614
  return;
19765
19615
  }
19766
- const { attempt, abortFailed, pendingItem } = await mergeOneAttempt(result, { mergeRoot: opts.mergeRoot, summaryCharLimit: opts.summaryCharLimit }, mergeFns, log10);
19616
+ const { attempt, abortFailed, pendingItem } = await mergeOneAttempt(result, { mergeRoot: opts.mergeRoot, summaryCharLimit: opts.summaryCharLimit }, mergeFns, log11);
19767
19617
  report.attempts.push(attempt);
19768
19618
  if (attempt.ok && !attempt.skippedReason)
19769
19619
  report.merged++;
@@ -19775,11 +19625,11 @@ function createMergeQueue(opts, deps = {}) {
19775
19625
  report.pendingWorktrees.push(pendingItem);
19776
19626
  if (abortFailed) {
19777
19627
  queueAborted = true;
19778
- log10("error", `[parallel-merge] queue aborted (mergeAbort failed)`, {
19628
+ log11("error", `[parallel-merge] queue aborted (mergeAbort failed)`, {
19779
19629
  id: result.id
19780
19630
  });
19781
19631
  }
19782
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
19632
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
19783
19633
  });
19784
19634
  },
19785
19635
  async flushAndReport() {
@@ -19788,23 +19638,23 @@ function createMergeQueue(opts, deps = {}) {
19788
19638
  }
19789
19639
  };
19790
19640
  }
19791
- async function safeRemoveWorktree(fn, root, wt, log10, subtaskId) {
19641
+ async function safeRemoveWorktree(fn, root, wt, log11, subtaskId) {
19792
19642
  try {
19793
19643
  await fn({ root, worktree_path: wt, force: true });
19794
19644
  } catch (err) {
19795
- log10("warn", `[parallel] removeWorktree 失败 ${subtaskId}`, {
19645
+ log11("warn", `[parallel] removeWorktree 失败 ${subtaskId}`, {
19796
19646
  error: describe6(err),
19797
19647
  worktree: wt
19798
19648
  });
19799
19649
  }
19800
19650
  }
19801
- async function fireMergeAttempt(cb, attempt, idx, log10) {
19651
+ async function fireMergeAttempt(cb, attempt, idx, log11) {
19802
19652
  if (!cb)
19803
19653
  return;
19804
19654
  try {
19805
19655
  await cb(attempt, idx);
19806
19656
  } catch (err) {
19807
- log10("warn", `[parallel] onMergeAttempt 抛错(已隔离)`, {
19657
+ log11("warn", `[parallel] onMergeAttempt 抛错(已隔离)`, {
19808
19658
  id: attempt.subtaskId,
19809
19659
  error: describe6(err)
19810
19660
  });
@@ -19830,7 +19680,7 @@ async function schedule(opts) {
19830
19680
  }
19831
19681
  const now = opts.deps.now ?? Date.now;
19832
19682
  const start = now();
19833
- const log10 = opts.deps.log ?? (() => {});
19683
+ const log11 = opts.deps.log ?? (() => {});
19834
19684
  const concurrency = Math.max(1, opts.maxConcurrency ?? 4);
19835
19685
  const totalTimeout = opts.totalTimeout_ms ?? 30 * 60000;
19836
19686
  const limit = Math.max(1, opts.summaryCharLimit ?? 500);
@@ -19863,7 +19713,7 @@ async function schedule(opts) {
19863
19713
  mergeAbortFn: opts.deps.mergeAbort,
19864
19714
  removeWorktreeFn: opts.deps.removeWorktree,
19865
19715
  worktreeHasChangesFn: opts.deps.worktreeHasChanges,
19866
- log: log10
19716
+ log: log11
19867
19717
  });
19868
19718
  }
19869
19719
  const fireFinish = async (i, res) => {
@@ -19871,7 +19721,7 @@ async function schedule(opts) {
19871
19721
  try {
19872
19722
  queue.enqueue(res);
19873
19723
  } catch (err) {
19874
- log10("warn", `[parallel] queue.enqueue 抛错(已隔离)`, {
19724
+ log11("warn", `[parallel] queue.enqueue 抛错(已隔离)`, {
19875
19725
  id: res.id,
19876
19726
  error: describe7(err)
19877
19727
  });
@@ -19882,7 +19732,7 @@ async function schedule(opts) {
19882
19732
  try {
19883
19733
  await opts.onSubtaskFinish(res, i);
19884
19734
  } catch (err) {
19885
- log10("warn", `[parallel] onSubtaskFinish 抛错(已隔离)`, {
19735
+ log11("warn", `[parallel] onSubtaskFinish 抛错(已隔离)`, {
19886
19736
  id: res.id,
19887
19737
  error: describe7(err)
19888
19738
  });
@@ -19912,7 +19762,7 @@ async function schedule(opts) {
19912
19762
  try {
19913
19763
  await opts.onSubtaskStart(spec, i);
19914
19764
  } catch (err) {
19915
- log10("warn", `[parallel] onSubtaskStart 抛错(已隔离)`, {
19765
+ log11("warn", `[parallel] onSubtaskStart 抛错(已隔离)`, {
19916
19766
  id: spec.id,
19917
19767
  error: describe7(err)
19918
19768
  });
@@ -19963,7 +19813,7 @@ async function schedule(opts) {
19963
19813
  try {
19964
19814
  await alloc.cleanup();
19965
19815
  } catch (err) {
19966
- log10("warn", `[parallel] worktree 清理失败 ${spec.id}`, {
19816
+ log11("warn", `[parallel] worktree 清理失败 ${spec.id}`, {
19967
19817
  error: describe7(err)
19968
19818
  });
19969
19819
  }
@@ -20121,7 +19971,7 @@ function pickStatus(r, taskAborted, globalAborted) {
20121
19971
  }
20122
19972
 
20123
19973
  // plugins/subtasks.ts
20124
- init_autonomy();
19974
+ init_worktree_ops();
20125
19975
 
20126
19976
  // lib/decompose.ts
20127
19977
  var DEFAULT_MAX_SUBTASKS = 8;
@@ -20143,9 +19993,9 @@ function buildSystemPrompt(maxSubtasks) {
20143
19993
  `);
20144
19994
  }
20145
19995
  async function decomposeTask(description29, opts) {
20146
- const log10 = opts.log ?? (() => {});
19996
+ const log11 = opts.log ?? (() => {});
20147
19997
  if (opts.mockResponse) {
20148
- return validateAndFinalize(opts.mockResponse, undefined, log10, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
19998
+ return validateAndFinalize(opts.mockResponse, undefined, log11, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
20149
19999
  }
20150
20000
  const maxSubtasks = opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS;
20151
20001
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
@@ -20156,7 +20006,7 @@ async function decomposeTask(description29, opts) {
20156
20006
  query: opts.directory ? { directory: opts.directory } : undefined
20157
20007
  });
20158
20008
  if (created.error || !created.data?.id) {
20159
- log10("warn", "[decompose] session.create 失败", { error: describe8(created.error) });
20009
+ log11("warn", "[decompose] session.create 失败", { error: describe8(created.error) });
20160
20010
  return {
20161
20011
  ok: false,
20162
20012
  subtasks: [],
@@ -20178,22 +20028,22 @@ async function decomposeTask(description29, opts) {
20178
20028
  sleep2(timeoutMs).then(() => ({ kind: "timeout" }))
20179
20029
  ]);
20180
20030
  if (raced.kind === "timeout") {
20181
- log10("warn", "[decompose] LLM 调用超时", { timeoutMs });
20031
+ log11("warn", "[decompose] LLM 调用超时", { timeoutMs });
20182
20032
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
20183
20033
  }
20184
20034
  const r = raced.value;
20185
20035
  if (r.error || !r.data) {
20186
- log10("warn", "[decompose] session.prompt 返回错误", { error: describe8(r.error) });
20036
+ log11("warn", "[decompose] session.prompt 返回错误", { error: describe8(r.error) });
20187
20037
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
20188
20038
  }
20189
20039
  const rawText = pickLastText2(r.data.parts ?? []);
20190
20040
  if (!rawText) {
20191
- log10("warn", "[decompose] LLM 输出为空");
20041
+ log11("warn", "[decompose] LLM 输出为空");
20192
20042
  return { ok: false, subtasks: [], reason: "parse_failed", raw: "" };
20193
20043
  }
20194
20044
  const parsed = extractJson(rawText);
20195
20045
  if (!parsed) {
20196
- log10("warn", "[decompose] JSON 解析失败", { raw: clip6(rawText, 200) });
20046
+ log11("warn", "[decompose] JSON 解析失败", { raw: clip6(rawText, 200) });
20197
20047
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
20198
20048
  }
20199
20049
  if (parsed && typeof parsed === "object" && parsed.single_task === true) {
@@ -20201,7 +20051,7 @@ async function decomposeTask(description29, opts) {
20201
20051
  }
20202
20052
  const subtasksRaw = parsed.subtasks;
20203
20053
  if (!Array.isArray(subtasksRaw)) {
20204
- log10("warn", "[decompose] JSON 缺 subtasks 数组");
20054
+ log11("warn", "[decompose] JSON 缺 subtasks 数组");
20205
20055
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
20206
20056
  }
20207
20057
  const normalized = [];
@@ -20217,12 +20067,12 @@ async function decomposeTask(description29, opts) {
20217
20067
  normalized.push({ description: desc, hintFiles });
20218
20068
  }
20219
20069
  if (normalized.length > maxSubtasks) {
20220
- log10("warn", "[decompose] LLM 返回子任务超过上限", { count: normalized.length, maxSubtasks });
20070
+ log11("warn", "[decompose] LLM 返回子任务超过上限", { count: normalized.length, maxSubtasks });
20221
20071
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
20222
20072
  }
20223
- return validateAndFinalize(normalized, rawText, log10, maxSubtasks);
20073
+ return validateAndFinalize(normalized, rawText, log11, maxSubtasks);
20224
20074
  } catch (err) {
20225
- log10("warn", "[decompose] 抛错", { error: describe8(err) });
20075
+ log11("warn", "[decompose] 抛错", { error: describe8(err) });
20226
20076
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
20227
20077
  } finally {
20228
20078
  if (childSessionId) {
@@ -20232,12 +20082,12 @@ async function decomposeTask(description29, opts) {
20232
20082
  query: opts.directory ? { directory: opts.directory } : undefined
20233
20083
  });
20234
20084
  } catch (err) {
20235
- log10("warn", "[decompose] session.delete 失败", { error: describe8(err) });
20085
+ log11("warn", "[decompose] session.delete 失败", { error: describe8(err) });
20236
20086
  }
20237
20087
  }
20238
20088
  }
20239
20089
  }
20240
- function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
20090
+ function validateAndFinalize(subtasks, raw, log11, maxSubtasks) {
20241
20091
  if (subtasks.length < 2) {
20242
20092
  return { ok: false, subtasks: [], reason: "single_task", raw };
20243
20093
  }
@@ -20247,7 +20097,7 @@ function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
20247
20097
  }
20248
20098
  }
20249
20099
  if (subtasks.length > maxSubtasks) {
20250
- log10("warn", "[decompose] LLM 返回子任务超过上限", { count: subtasks.length, maxSubtasks });
20100
+ log11("warn", "[decompose] LLM 返回子任务超过上限", { count: subtasks.length, maxSubtasks });
20251
20101
  return { ok: false, subtasks: [], reason: "parse_failed", raw };
20252
20102
  }
20253
20103
  for (let i = 0;i < subtasks.length; i++) {
@@ -20261,7 +20111,7 @@ function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
20261
20111
  continue;
20262
20112
  for (const f of b) {
20263
20113
  if (setA.has(f.trim())) {
20264
- log10("info", "[decompose] hintFiles 有交集,降级 single_task", {
20114
+ log11("info", "[decompose] hintFiles 有交集,降级 single_task", {
20265
20115
  a: subtasks[i].description,
20266
20116
  b: subtasks[j].description,
20267
20117
  file: f
@@ -20416,7 +20266,7 @@ var mockRunner = async (spec) => {
20416
20266
  };
20417
20267
  async function handleParallelCommand(raw) {
20418
20268
  const ctx = raw ?? {};
20419
- const log10 = ctx.log;
20269
+ const log11 = ctx.log;
20420
20270
  const args = ctx.args ?? {};
20421
20271
  const parentId = (args.parentId ?? `task-${Date.now().toString(36)}`).replace(/[^a-zA-Z0-9._-]/g, "-");
20422
20272
  const rawDescription = typeof args.description === "string" ? args.description.trim() : "";
@@ -20430,7 +20280,7 @@ async function handleParallelCommand(raw) {
20430
20280
  specs = splitDescriptions(args.description, { parentId });
20431
20281
  }
20432
20282
  if (specs.length === 0) {
20433
- log10?.warn(`[${PLUGIN_NAME18}] /parallel 缺有效子任务`);
20283
+ log11?.warn(`[${PLUGIN_NAME18}] /parallel 缺有效子任务`);
20434
20284
  await safeReply2(ctx, "⚠ /parallel 需要至少 1 个子任务(用 ; 或换行分隔)");
20435
20285
  return { ok: false, reason: "no_subtasks" };
20436
20286
  }
@@ -20456,7 +20306,7 @@ async function handleParallelCommand(raw) {
20456
20306
  if (!gitOk) {
20457
20307
  autoMerge = false;
20458
20308
  downgradedAutoMerge = true;
20459
- log10?.warn("[subtasks] mergeRoot 非 git 仓库,降级 autoMerge=false");
20309
+ log11?.warn("[subtasks] mergeRoot 非 git 仓库,降级 autoMerge=false");
20460
20310
  const canNoticeEarly = Boolean(ctx.client && ctx.parentSessionID);
20461
20311
  if (canNoticeEarly) {
20462
20312
  await sendParentNotice(ctx.client, ctx.parentSessionID, "ℹ 当前目录不是 git 仓库,worktree 隔离已自动关闭,以单目录模式运行", { directory: ctx.directory });
@@ -20465,17 +20315,17 @@ async function handleParallelCommand(raw) {
20465
20315
  }
20466
20316
  const canNotice = Boolean(ctx.client && ctx.parentSessionID);
20467
20317
  if (specs.length === 1 && rawDescription.length >= 30 && ctx.client) {
20468
- log10?.info(`[${PLUGIN_NAME18}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
20318
+ log11?.info(`[${PLUGIN_NAME18}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
20469
20319
  const dec = await decomposeTask(rawDescription, {
20470
20320
  client: ctx.client,
20471
20321
  directory: ctx.directory,
20472
20322
  log: (lvl, msg, data) => {
20473
20323
  if (lvl === "error")
20474
- log10?.error(msg, data);
20324
+ log11?.error(msg, data);
20475
20325
  else if (lvl === "warn")
20476
- log10?.warn(msg, data);
20326
+ log11?.warn(msg, data);
20477
20327
  else
20478
- log10?.info(msg, data);
20328
+ log11?.info(msg, data);
20479
20329
  },
20480
20330
  mockResponse: ctx.decomposeMockResponse
20481
20331
  });
@@ -20486,7 +20336,7 @@ async function handleParallelCommand(raw) {
20486
20336
  timeout_ms: undefined,
20487
20337
  args: d.hintFiles && d.hintFiles.length > 0 ? { hintFiles: d.hintFiles } : undefined
20488
20338
  }));
20489
- log10?.info(`[${PLUGIN_NAME18}] AI 拆分成功 → ${specs.length} 个子任务`);
20339
+ log11?.info(`[${PLUGIN_NAME18}] AI 拆分成功 → ${specs.length} 个子任务`);
20490
20340
  if (canNotice) {
20491
20341
  const lines = [
20492
20342
  `\uD83E\uDD16 AI 已自动拆为 ${specs.length} 个并行子任务:`,
@@ -20498,7 +20348,7 @@ async function handleParallelCommand(raw) {
20498
20348
  });
20499
20349
  }
20500
20350
  } else if (dec.reason === "llm_unavailable" || dec.reason === "parse_failed") {
20501
- log10?.warn(`[${PLUGIN_NAME18}] AI 拆分失败(${dec.reason}),按单任务执行`);
20351
+ log11?.warn(`[${PLUGIN_NAME18}] AI 拆分失败(${dec.reason}),按单任务执行`);
20502
20352
  }
20503
20353
  }
20504
20354
  if (canNotice) {
@@ -20513,11 +20363,11 @@ async function handleParallelCommand(raw) {
20513
20363
  directory: ctx.directory,
20514
20364
  log: (lvl, msg, data) => {
20515
20365
  if (lvl === "error")
20516
- log10?.error(msg, data);
20366
+ log11?.error(msg, data);
20517
20367
  else if (lvl === "warn")
20518
- log10?.warn(msg, data);
20368
+ log11?.warn(msg, data);
20519
20369
  else
20520
- log10?.info(msg, data);
20370
+ log11?.info(msg, data);
20521
20371
  }
20522
20372
  });
20523
20373
  }
@@ -20561,11 +20411,11 @@ async function handleParallelCommand(raw) {
20561
20411
  allocateWorktree: downgradedAutoMerge ? undefined : ctx.allocateWorktree,
20562
20412
  log: (lvl, msg, data) => {
20563
20413
  if (lvl === "error")
20564
- log10?.error(msg, data);
20414
+ log11?.error(msg, data);
20565
20415
  else if (lvl === "warn")
20566
- log10?.warn(msg, data);
20416
+ log11?.warn(msg, data);
20567
20417
  else
20568
- log10?.info(msg, data);
20418
+ log11?.info(msg, data);
20569
20419
  },
20570
20420
  tryMerge: ctx.tryMerge,
20571
20421
  mergeCommit: ctx.mergeCommit,
@@ -20577,7 +20427,7 @@ async function handleParallelCommand(raw) {
20577
20427
  });
20578
20428
  } catch (err) {
20579
20429
  const msg = err instanceof Error ? err.message : String(err);
20580
- log10?.error(`[${PLUGIN_NAME18}] schedule 抛错`, { error: msg });
20430
+ log11?.error(`[${PLUGIN_NAME18}] schedule 抛错`, { error: msg });
20581
20431
  await safeReply2(ctx, `❌ 并发调度失败:${msg}`);
20582
20432
  return { ok: false, reason: msg };
20583
20433
  }
@@ -20595,7 +20445,7 @@ async function handleParallelCommand(raw) {
20595
20445
  }
20596
20446
  await safeReply2(ctx, summaryLines.join(`
20597
20447
  `));
20598
- log10?.info(`[${PLUGIN_NAME18}] schedule 完成`, {
20448
+ log11?.info(`[${PLUGIN_NAME18}] schedule 完成`, {
20599
20449
  parentId,
20600
20450
  success: result.digest.success,
20601
20451
  failed: result.digest.failed,
@@ -20608,7 +20458,7 @@ async function handleParallelCommand(raw) {
20608
20458
  try {
20609
20459
  await ctx.onCompleted(result);
20610
20460
  } catch (err) {
20611
- log10?.warn(`[${PLUGIN_NAME18}] onCompleted hook 抛错(已隔离)`, {
20461
+ log11?.warn(`[${PLUGIN_NAME18}] onCompleted hook 抛错(已隔离)`, {
20612
20462
  error: err instanceof Error ? err.message : String(err)
20613
20463
  });
20614
20464
  }
@@ -20663,7 +20513,7 @@ async function writeLog(level, msg, data) {
20663
20513
  }
20664
20514
  logLifecycle(PLUGIN_NAME18, "import");
20665
20515
  var subtasksServer = async (ctx) => {
20666
- const log10 = makePluginLogger(PLUGIN_NAME18);
20516
+ const log11 = makePluginLogger(PLUGIN_NAME18);
20667
20517
  const client = ctx?.client ?? undefined;
20668
20518
  logLifecycle(PLUGIN_NAME18, "activate", {
20669
20519
  directory: ctx.directory,
@@ -20690,7 +20540,7 @@ var subtasksServer = async (ctx) => {
20690
20540
  const gitOk = await isGitRepo({ root: repoRoot }).catch(() => false);
20691
20541
  if (!gitOk) {
20692
20542
  autoMerge = false;
20693
- log10?.warn("[subtasks] worktree_isolation=true 但非 git 仓库,自动降级 autoMerge=false");
20543
+ log11?.warn("[subtasks] worktree_isolation=true 但非 git 仓库,自动降级 autoMerge=false");
20694
20544
  const canNotice = Boolean(client);
20695
20545
  if (canNotice) {
20696
20546
  await sendParentNotice(client, input.sessionID, "ℹ 当前目录不是 git 仓库,worktree 隔离已自动关闭,以单目录模式运行", { directory: ctx.directory });
@@ -20719,7 +20569,7 @@ var subtasksServer = async (ctx) => {
20719
20569
  replyLines.push(s);
20720
20570
  return Promise.resolve();
20721
20571
  },
20722
- log: log10,
20572
+ log: log11,
20723
20573
  client,
20724
20574
  parentSessionID: input.sessionID,
20725
20575
  directory: ctx.directory,
@@ -20732,11 +20582,11 @@ var subtasksServer = async (ctx) => {
20732
20582
  perTaskTimeoutMs: 5 * 60000,
20733
20583
  log: (lvl, msg, data) => {
20734
20584
  if (lvl === "error")
20735
- log10.error(msg, data);
20585
+ log11.error(msg, data);
20736
20586
  else if (lvl === "warn")
20737
- log10.warn(msg, data);
20587
+ log11.warn(msg, data);
20738
20588
  else
20739
- log10.info(msg, data);
20589
+ log11.info(msg, data);
20740
20590
  }
20741
20591
  }) : undefined
20742
20592
  };
@@ -20762,7 +20612,7 @@ var subtasksServer = async (ctx) => {
20762
20612
  });
20763
20613
  }
20764
20614
  } catch (err) {
20765
- log10.error(`[${PLUGIN_NAME18}] command.execute.before 异常(已隔离)`, {
20615
+ log11.error(`[${PLUGIN_NAME18}] command.execute.before 异常(已隔离)`, {
20766
20616
  error: err instanceof Error ? err.message : String(err)
20767
20617
  });
20768
20618
  }
@@ -21056,7 +20906,7 @@ function describeError(err) {
21056
20906
  return String(err);
21057
20907
  }
21058
20908
  }
21059
- var log10 = makePluginLogger(PLUGIN_NAME19);
20909
+ var log11 = makePluginLogger(PLUGIN_NAME19);
21060
20910
  var lru = new FingerprintLRU2;
21061
20911
  var _lastNotification = null;
21062
20912
  var terminalMonitorServer = async (ctx) => {
@@ -21075,7 +20925,7 @@ var terminalMonitorServer = async (ctx) => {
21075
20925
  const ev = { type: e.type, ...e.properties ?? {} };
21076
20926
  const r = await processTerminalEvent(ev, {
21077
20927
  lru,
21078
- log: log10,
20928
+ log: log11,
21079
20929
  notifyAgent: (msg) => {
21080
20930
  _lastNotification = msg;
21081
20931
  }
@@ -21088,7 +20938,7 @@ var terminalMonitorServer = async (ctx) => {
21088
20938
  findings: r.notification?.findings.length ?? 0
21089
20939
  });
21090
20940
  if (r.notified && r.notification) {
21091
- log10.info(`[${PLUGIN_NAME19}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
20941
+ log11.info(`[${PLUGIN_NAME19}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
21092
20942
  }
21093
20943
  });
21094
20944
  }
@@ -21099,7 +20949,7 @@ var handler19 = terminalMonitorServer;
21099
20949
  // plugins/token-manager.ts
21100
20950
  var PLUGIN_NAME20 = "token-manager";
21101
20951
  logLifecycle(PLUGIN_NAME20, "import", {});
21102
- async function handleMessageBefore(raw, log11, defaults) {
20952
+ async function handleMessageBefore(raw, log12, defaults) {
21103
20953
  const ctx = raw ?? {};
21104
20954
  if (!Array.isArray(ctx.messages) || ctx.messages.length === 0)
21105
20955
  return null;
@@ -21118,17 +20968,17 @@ async function handleMessageBefore(raw, log11, defaults) {
21118
20968
  };
21119
20969
  if (r.compressed) {
21120
20970
  ctx.messages = r.messages;
21121
- log11?.info(`[${PLUGIN_NAME20}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
20971
+ log12?.info(`[${PLUGIN_NAME20}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
21122
20972
  }
21123
20973
  return r;
21124
20974
  } catch (err) {
21125
- log11?.warn(`[${PLUGIN_NAME20}] 压缩异常(已隔离)`, {
20975
+ log12?.warn(`[${PLUGIN_NAME20}] 压缩异常(已隔离)`, {
21126
20976
  error: err instanceof Error ? err.message : String(err)
21127
20977
  });
21128
20978
  return null;
21129
20979
  }
21130
20980
  }
21131
- var log11 = makePluginLogger(PLUGIN_NAME20);
20981
+ var log12 = makePluginLogger(PLUGIN_NAME20);
21132
20982
  var tokenManagerServer = async (ctx) => {
21133
20983
  const rt = loadRuntimeSync();
21134
20984
  const threshold = rt.runtime.context.condenser_threshold_ratio;
@@ -21151,7 +21001,7 @@ var tokenManagerServer = async (ctx) => {
21151
21001
  `);
21152
21002
  return { role, content };
21153
21003
  });
21154
- const r = await handleMessageBefore({ messages: flat }, log11, { threshold });
21004
+ const r = await handleMessageBefore({ messages: flat }, log12, { threshold });
21155
21005
  if (!r)
21156
21006
  return;
21157
21007
  safeWriteLog(PLUGIN_NAME20, {
@@ -21165,7 +21015,7 @@ var tokenManagerServer = async (ctx) => {
21165
21015
  reason: r.reason
21166
21016
  });
21167
21017
  if (r.compressed) {
21168
- log11.warn(`[${PLUGIN_NAME20}] advise condense: ${r.before.count}→${r.after.count} msgs / ${r.before.tokens}→${r.after.tokens} tokens (observe-only, no write-back to avoid opencode message schema mismatch)`);
21018
+ log12.warn(`[${PLUGIN_NAME20}] advise condense: ${r.before.count}→${r.after.count} msgs / ${r.before.tokens}→${r.after.tokens} tokens (observe-only, no write-back to avoid opencode message schema mismatch)`);
21169
21019
  }
21170
21020
  });
21171
21021
  }
@@ -21176,7 +21026,150 @@ var handler20 = tokenManagerServer;
21176
21026
  // plugins/tool-policy.ts
21177
21027
  import { promises as fs19 } from "node:fs";
21178
21028
  import * as path24 from "node:path";
21179
- init_autonomy();
21029
+
21030
+ // lib/tool-risk.ts
21031
+ var RISK_PATTERNS = [
21032
+ {
21033
+ tag: "rm_rf_root",
21034
+ kinds: ["bash"],
21035
+ re: /rm\s+-[rRf]+[^|;`\n]*\s+(\/(?:\s|$|[^/\w*.-])|~|\$HOME(?:\b|\/)|\/(?:bin|etc|usr|var|opt|home|root|boot|lib|sbin)\b)/
21036
+ },
21037
+ {
21038
+ tag: "rm_rf_wildcard",
21039
+ kinds: ["bash"],
21040
+ re: /rm\s+-[rRf]+\s+(?:\.\*|\*)/
21041
+ },
21042
+ {
21043
+ tag: "force_push_protected",
21044
+ kinds: ["bash"],
21045
+ re: /git\s+push\s+(?:--force\b|-f\b)[\s\S]*\b(main|master|release(?:\/[\w.-]+)?)\b/
21046
+ },
21047
+ {
21048
+ tag: "git_reset_hard_main",
21049
+ kinds: ["bash"],
21050
+ re: /git\s+reset\s+--hard\s+(origin\/)?(main|master)/
21051
+ },
21052
+ { tag: "sudo", kinds: ["bash"], re: /(?<![\w-])sudo(?![\w-])/ },
21053
+ { tag: "su_root", kinds: ["bash"], re: /(?<![\w-])su\s+(?:-\s+)?root\b/ },
21054
+ { tag: "mkfs", kinds: ["bash"], re: /\bmkfs(\.[\w]+)?\b/ },
21055
+ { tag: "dd_disk", kinds: ["bash"], re: /\bdd\s+if=[^|;\s]+\s+of=\/dev\// },
21056
+ { tag: "chmod_777", kinds: ["bash"], re: /chmod\s+[-+]?[0-7]?777/ },
21057
+ {
21058
+ tag: "curl_pipe_sh",
21059
+ kinds: ["bash"],
21060
+ re: /\b(?:curl|wget)\b[^|]*\|\s*(?:sh|bash|zsh)\b/
21061
+ },
21062
+ {
21063
+ tag: "drop_database",
21064
+ kinds: ["bash", "other"],
21065
+ re: /\b(DROP\s+(DATABASE|TABLE)|TRUNCATE\s+TABLE|DROP\s+SCHEMA)\b/i
21066
+ },
21067
+ { tag: "write_secrets", re: /(\.env(?:\.\w+)?|id_[edr]sa|\.ssh\/id_|\.pem|\.p12|secret\.json)/i },
21068
+ {
21069
+ tag: "write_etc",
21070
+ kinds: ["bash", "edit"],
21071
+ re: /(?:^|\s|"|')\/etc\//
21072
+ },
21073
+ {
21074
+ tag: "write_usr",
21075
+ kinds: ["bash", "edit"],
21076
+ re: /(?:^|\s|"|')\/usr\//
21077
+ },
21078
+ {
21079
+ tag: "write_root_home",
21080
+ kinds: ["bash", "edit"],
21081
+ re: /(?:^|\s|"|')(\/root|\/home\/root)\//
21082
+ },
21083
+ {
21084
+ tag: "internal_url",
21085
+ kinds: ["webfetch"],
21086
+ re: /https?:\/\/(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3})(?::\d+)?(?:[/?#]|$)/i
21087
+ },
21088
+ {
21089
+ tag: "kubectl_delete",
21090
+ kinds: ["bash"],
21091
+ re: /kubectl\s+(?:delete|destroy)\s+/
21092
+ },
21093
+ {
21094
+ tag: "terraform_destroy",
21095
+ kinds: ["bash"],
21096
+ re: /terraform\s+destroy/
21097
+ }
21098
+ ];
21099
+ var TOOL_KIND_MAP = {
21100
+ bash: "bash",
21101
+ shell: "bash",
21102
+ exec: "bash",
21103
+ edit: "edit",
21104
+ write: "edit",
21105
+ "ast-edit": "edit",
21106
+ "pending-changes-apply": "edit",
21107
+ webfetch: "webfetch",
21108
+ fetch: "webfetch",
21109
+ read: "read",
21110
+ cat: "read",
21111
+ glob: "search",
21112
+ grep: "search",
21113
+ search: "search",
21114
+ "repo-map": "search",
21115
+ smart_search: "search",
21116
+ list_recent: "search",
21117
+ get_working_memory: "search",
21118
+ web_search: "search",
21119
+ save_chat_insight: "other",
21120
+ add_knowledge: "other",
21121
+ upload_document: "other",
21122
+ upload_document_from_url: "other",
21123
+ update_knowledge: "other",
21124
+ flag_outdated: "other",
21125
+ delete_knowledge: "other",
21126
+ update_working_memory: "other",
21127
+ update_user_preference: "other",
21128
+ generate_image: "other"
21129
+ };
21130
+ function classifyTool(tool2) {
21131
+ const lower = tool2.toLowerCase();
21132
+ if (lower in TOOL_KIND_MAP)
21133
+ return TOOL_KIND_MAP[lower];
21134
+ if (/(^|[-_])edit($|[-_])|write|patch|apply/i.test(lower))
21135
+ return "edit";
21136
+ if (/bash|shell|exec|run/i.test(lower))
21137
+ return "bash";
21138
+ if (/fetch|http|curl/i.test(lower))
21139
+ return "webfetch";
21140
+ if (/read|view/i.test(lower))
21141
+ return "read";
21142
+ if (/search|grep|glob|find|list/i.test(lower))
21143
+ return "search";
21144
+ return "other";
21145
+ }
21146
+ function evaluateRisk(tool2, args) {
21147
+ const kind = classifyTool(tool2);
21148
+ const haystack = buildHaystack(args);
21149
+ const hits = [];
21150
+ for (const pattern of RISK_PATTERNS) {
21151
+ if (pattern.kinds && !pattern.kinds.includes(kind))
21152
+ continue;
21153
+ const m = haystack.match(pattern.re);
21154
+ if (m) {
21155
+ hits.push({
21156
+ label: pattern.tag,
21157
+ pattern: pattern.re.source,
21158
+ matched_on: m[0]
21159
+ });
21160
+ }
21161
+ }
21162
+ return hits;
21163
+ }
21164
+ function buildHaystack(args) {
21165
+ if (typeof args === "string")
21166
+ return args;
21167
+ try {
21168
+ return JSON.stringify(args);
21169
+ } catch {
21170
+ return String(args);
21171
+ }
21172
+ }
21180
21173
 
21181
21174
  // lib/file-regex-acl.ts
21182
21175
  import * as path23 from "node:path";
@@ -21315,28 +21308,18 @@ function decideToolCall(ctx, cfg = {}, currentAgent) {
21315
21308
  return {
21316
21309
  action: "deny",
21317
21310
  reasons: [
21318
- `[ADR:prevent-subagent-self-apply] subagent '${currentAgent}' 禁止调 pending_changes.${action2}` + ` — apply 必须由 codeforge orchestrator 或用户拍板`,
21311
+ `[ADR:worktree-session-isolation] subagent '${currentAgent}' 禁止调 pending_changes.${action2}` + ` — apply 必须由 codeforge orchestrator 或用户拍板`,
21319
21312
  `替代路径:stage 完成后回报 codeforge,由其决定 apply 后通过 task_id 复用启动你跑测试`
21320
21313
  ],
21321
- autonomy: {
21322
- effective_mode: ctx.mode ?? cfg.defaultMode ?? DEFAULT_RUNTIME.autonomy.default_mode,
21323
- action: "confirm",
21324
- downgraded: false,
21325
- detected_risks: ["subagent_apply_blocked"],
21326
- reason: `subagent '${currentAgent}' 自 apply 越权 (ADR:prevent-subagent-self-apply)`
21327
- }
21314
+ risks: []
21328
21315
  };
21329
21316
  }
21330
- const fallbackMode = cfg.defaultMode ?? DEFAULT_RUNTIME.autonomy.default_mode;
21331
- const mode = ctx.mode ?? fallbackMode;
21332
- const a = evaluate(mode, {
21333
- tool: ctx.tool,
21334
- kind: ctx.kind ?? "other",
21335
- args: ctx.args ?? {}
21336
- });
21317
+ const risks = evaluateRisk(ctx.tool, ctx.args ?? {});
21337
21318
  const reasons = [];
21338
- if (a.action === "confirm")
21339
- reasons.push(`autonomy: ${a.reason}`);
21319
+ if (risks.length > 0) {
21320
+ const labels = risks.map((r) => r.label).join(", ");
21321
+ reasons.push(`risk: 命中高危规则 [${labels}]`);
21322
+ }
21340
21323
  let aclResults;
21341
21324
  let worstAcl = "allow";
21342
21325
  if (ctx.files?.length) {
@@ -21350,10 +21333,8 @@ function decideToolCall(ctx, cfg = {}, currentAgent) {
21350
21333
  return d;
21351
21334
  });
21352
21335
  }
21353
- let action = a.action === "confirm" ? "confirm" : "allow";
21354
- if (worstAcl === "deny")
21355
- action = "deny";
21356
- return { action, reasons, autonomy: a, acl: aclResults };
21336
+ const action = risks.length > 0 || worstAcl === "deny" ? "deny" : "allow";
21337
+ return { action, reasons, risks, acl: aclResults };
21357
21338
  }
21358
21339
  var POLICY_PATH = path24.join(".codeforge", "policy.json");
21359
21340
  async function loadPolicy(root = process.cwd()) {
@@ -21367,27 +21348,20 @@ async function loadPolicy(root = process.cwd()) {
21367
21348
  }
21368
21349
  }
21369
21350
  function classifyToolKind(toolName) {
21370
- const t = toolName.toLowerCase();
21371
- if (t === "bash" || t === "shell" || t === "execute")
21372
- return "bash";
21373
- if (t === "edit" || t === "write" || t === "patch" || t === "ast_edit")
21374
- return "edit";
21375
- if (t === "webfetch" || t === "fetch" || t === "browser")
21376
- return "webfetch";
21377
- return "other";
21351
+ return classifyTool(toolName);
21378
21352
  }
21379
- async function resolveCurrentAgent(client, sessionID, log12) {
21353
+ async function resolveCurrentAgent(client, sessionID, log13) {
21380
21354
  try {
21381
21355
  const sessionApi = client?.session;
21382
21356
  if (!sessionApi || typeof sessionApi.get !== "function") {
21383
- log12.warn(`client.session.get unavailable`, { sessionID });
21357
+ log13.warn(`client.session.get unavailable`, { sessionID });
21384
21358
  return;
21385
21359
  }
21386
21360
  const res = await sessionApi.get({ path: { id: sessionID } });
21387
21361
  const data = res?.data ?? res;
21388
21362
  const rawAgent = data?.agent;
21389
21363
  if (typeof rawAgent !== "string" || rawAgent === "") {
21390
- log12.warn(`client.session.get returned no string agent (保守放行)`, {
21364
+ log13.warn(`client.session.get returned no string agent (保守放行)`, {
21391
21365
  sessionID,
21392
21366
  dataKeys: data && typeof data === "object" ? Object.keys(data) : null
21393
21367
  });
@@ -21395,24 +21369,20 @@ async function resolveCurrentAgent(client, sessionID, log12) {
21395
21369
  }
21396
21370
  return rawAgent;
21397
21371
  } catch (err) {
21398
- log12.warn(`client.session.get failed (保守放行)`, {
21372
+ log13.warn(`client.session.get failed (保守放行)`, {
21399
21373
  sessionID,
21400
21374
  error: err instanceof Error ? err.message : String(err)
21401
21375
  });
21402
21376
  return;
21403
21377
  }
21404
21378
  }
21405
- var log12 = makePluginLogger(PLUGIN_NAME21);
21379
+ var log13 = makePluginLogger(PLUGIN_NAME21);
21406
21380
  var toolPolicyServer = async (ctx) => {
21407
21381
  const directory = ctx.directory ?? process.cwd();
21408
21382
  const cfg = await loadPolicy(directory);
21409
- const rt = loadRuntimeSync();
21410
- cfg.defaultMode = rt.runtime.autonomy.default_mode;
21411
21383
  logLifecycle(PLUGIN_NAME21, "activate", {
21412
21384
  directory,
21413
- acl_loaded: !!cfg.acl,
21414
- default_mode: cfg.defaultMode,
21415
- config_source: rt.ok ? "codeforge.json" : "built-in"
21385
+ acl_loaded: !!cfg.acl
21416
21386
  });
21417
21387
  return {
21418
21388
  "tool.execute.before": async (input, output) => {
@@ -21423,7 +21393,7 @@ var toolPolicyServer = async (ctx) => {
21423
21393
  const needsAgentDetection = toolName === "pending_changes" && (argsObj.action === "apply" || argsObj.action === "apply_all");
21424
21394
  let currentAgent = undefined;
21425
21395
  if (needsAgentDetection) {
21426
- currentAgent = await resolveCurrentAgent(ctx.client, input.sessionID, log12);
21396
+ currentAgent = await resolveCurrentAgent(ctx.client, input.sessionID, log13);
21427
21397
  }
21428
21398
  const files = [];
21429
21399
  const filePath = argsObj["path"] ?? argsObj["filePath"] ?? argsObj["file"];
@@ -21435,7 +21405,6 @@ var toolPolicyServer = async (ctx) => {
21435
21405
  tool: toolName,
21436
21406
  kind: classifyToolKind(toolName),
21437
21407
  args: argsObj,
21438
- mode: cfg.defaultMode,
21439
21408
  files: files.length ? files : undefined
21440
21409
  }, cfg, currentAgent);
21441
21410
  safeWriteLog(PLUGIN_NAME21, {
@@ -21445,18 +21414,18 @@ var toolPolicyServer = async (ctx) => {
21445
21414
  sessionID: input.sessionID,
21446
21415
  action: decision.action,
21447
21416
  reasons: decision.reasons,
21417
+ risks: decision.risks.map((r) => r.label),
21448
21418
  currentAgent
21449
21419
  });
21450
21420
  if (decision.action === "deny") {
21451
- log12.warn(`[${PLUGIN_NAME21}] DENY ${toolName}`, {
21421
+ log13.warn(`[${PLUGIN_NAME21}] DENY ${toolName}`, {
21452
21422
  action: argsObj.action,
21453
21423
  currentAgent,
21454
- reasons: decision.reasons
21424
+ reasons: decision.reasons,
21425
+ risks: decision.risks.map((r) => r.label)
21455
21426
  });
21456
21427
  denied = new DeniedError(`[tool-policy] DENIED: ${decision.reasons.join(" / ")}`);
21457
21428
  return;
21458
- } else if (decision.action === "confirm") {
21459
- log12.info(`[${PLUGIN_NAME21}] CONFIRM ${toolName}: ${decision.reasons.join("; ")}`);
21460
21429
  }
21461
21430
  });
21462
21431
  if (denied)
@@ -21494,7 +21463,7 @@ import * as zlib from "node:zlib";
21494
21463
  // lib/version-injected.ts
21495
21464
  function getInjectedVersion() {
21496
21465
  try {
21497
- const v = "0.5.6";
21466
+ const v = "0.5.8";
21498
21467
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
21499
21468
  return v;
21500
21469
  }
@@ -22691,35 +22660,35 @@ async function ensureRegistry(workflowsDir = "workflows") {
22691
22660
  }
22692
22661
  async function handleCommandInvoked(raw, workflowsDir = "workflows") {
22693
22662
  const ctx = raw ?? {};
22694
- const log13 = ctx.log ?? fallbackLog2;
22663
+ const log14 = ctx.log ?? fallbackLog2;
22695
22664
  const command = typeof ctx.command === "string" ? ctx.command : null;
22696
22665
  if (!command) {
22697
- log13.warn(`[${PLUGIN_NAME23}] command.invoked 缺 command 字段`, ctx);
22666
+ log14.warn(`[${PLUGIN_NAME23}] command.invoked 缺 command 字段`, ctx);
22698
22667
  return null;
22699
22668
  }
22700
22669
  const reg = await ensureRegistry(workflowsDir);
22701
22670
  if (reg.errors.length) {
22702
- log13.warn(`[${PLUGIN_NAME23}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
22671
+ log14.warn(`[${PLUGIN_NAME23}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
22703
22672
  }
22704
22673
  const wf = reg.workflows.find((w) => matchesTrigger(w, command));
22705
22674
  if (!wf) {
22706
- log13.info(`[${PLUGIN_NAME23}] no workflow matches "${command}"`);
22675
+ log14.info(`[${PLUGIN_NAME23}] no workflow matches "${command}"`);
22707
22676
  return null;
22708
22677
  }
22709
- log13.info(`[${PLUGIN_NAME23}] dispatch "${command}" → workflow "${wf.name}"`);
22678
+ log14.info(`[${PLUGIN_NAME23}] dispatch "${command}" → workflow "${wf.name}"`);
22710
22679
  try {
22711
22680
  const result = await run(wf, {
22712
22681
  mode: ctx.adapter ? "real" : "dry_run",
22713
22682
  autonomy: ctx.autonomy ?? "semi",
22714
22683
  adapter: ctx.adapter
22715
22684
  });
22716
- log13.info(`[${PLUGIN_NAME23}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
22685
+ log14.info(`[${PLUGIN_NAME23}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
22717
22686
  steps: result.plan.steps.length,
22718
22687
  results: result.results.length
22719
22688
  });
22720
22689
  return result;
22721
22690
  } catch (err) {
22722
- log13.error(`[${PLUGIN_NAME23}] workflow "${wf.name}" 执行失败`, {
22691
+ log14.error(`[${PLUGIN_NAME23}] workflow "${wf.name}" 执行失败`, {
22723
22692
  error: err instanceof Error ? err.message : String(err)
22724
22693
  });
22725
22694
  throw err;
@@ -22852,9 +22821,17 @@ function isWriteOperation(toolName, argsObj, mainRoot) {
22852
22821
  return true;
22853
22822
  return false;
22854
22823
  }
22855
- var log13 = makePluginLogger(PLUGIN_NAME24);
22824
+ var log14 = makePluginLogger(PLUGIN_NAME24);
22825
+ function resolveMainRoot2(rawDir) {
22826
+ const worktreeMarker = "/.git/codeforge-worktrees/";
22827
+ const idx = rawDir.indexOf(worktreeMarker);
22828
+ if (idx !== -1) {
22829
+ return rawDir.slice(0, idx);
22830
+ }
22831
+ return rawDir;
22832
+ }
22856
22833
  var sessionWorktreeGuardPlugin = async (ctx) => {
22857
- const mainRoot = ctx.directory ?? process.cwd();
22834
+ const mainRoot = resolveMainRoot2(ctx.directory ?? process.cwd());
22858
22835
  logLifecycle(PLUGIN_NAME24, "activate", {
22859
22836
  mainRoot,
22860
22837
  CODEFORGE_SESSION_ID: process.env["CODEFORGE_SESSION_ID"] ?? "(not set)"
@@ -22872,7 +22849,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22872
22849
  try {
22873
22850
  entry = await getSessionWorktree(sessionId, mainRoot);
22874
22851
  } catch (err) {
22875
- log13.warn(`getSessionWorktree failed (跳过本次检查)`, {
22852
+ log14.warn(`getSessionWorktree failed (跳过本次检查)`, {
22876
22853
  sessionId,
22877
22854
  mainRoot,
22878
22855
  error: err instanceof Error ? err.message : String(err)
@@ -22884,12 +22861,12 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22884
22861
  return;
22885
22862
  }
22886
22863
  try {
22887
- const parentId = lookupParentSessionId(sessionId);
22864
+ const parentId = lookupParentSessionId2(sessionId);
22888
22865
  if (parentId) {
22889
22866
  const parentEntry = await getSessionWorktree(parentId, mainRoot);
22890
22867
  if (parentEntry && parentEntry.status === "active") {
22891
22868
  entry = parentEntry;
22892
- log13.debug?.(`[child-inherit] session ${sessionId} 继承父 ${parentId} 的 worktree`, { parentSessionId: parentId, worktreePath: parentEntry.worktreePath });
22869
+ log14.debug?.(`[child-inherit] session ${sessionId} 继承父 ${parentId} 的 worktree`, { parentSessionId: parentId, worktreePath: parentEntry.worktreePath });
22893
22870
  safeWriteLog(PLUGIN_NAME24, {
22894
22871
  hook: "tool.execute.before",
22895
22872
  tool: toolName,
@@ -22902,13 +22879,13 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22902
22879
  }
22903
22880
  }
22904
22881
  } catch (lookupErr) {
22905
- log13.debug?.("[child-inherit] lookupParentSessionId 抛错(已隔离,退回 lazy-bind)", { error: lookupErr instanceof Error ? lookupErr.message : String(lookupErr) });
22882
+ log14.debug?.("[child-inherit] lookupParentSessionId 抛错(已隔离,退回 lazy-bind)", { error: lookupErr instanceof Error ? lookupErr.message : String(lookupErr) });
22906
22883
  }
22907
22884
  }
22908
22885
  if (!entry) {
22909
22886
  try {
22910
22887
  entry = await bindSessionWorktree({ sessionId, mainRoot });
22911
- log13.info(`[lazy-bind] auto-created worktree for session ${sessionId}`, { branch: entry.branch, path: entry.worktreePath });
22888
+ log14.info(`[lazy-bind] auto-created worktree for session ${sessionId}`, { branch: entry.branch, path: entry.worktreePath });
22912
22889
  safeWriteLog(PLUGIN_NAME24, {
22913
22890
  hook: "tool.execute.before",
22914
22891
  tool: toolName,
@@ -22918,7 +22895,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22918
22895
  worktreePath: entry.worktreePath
22919
22896
  });
22920
22897
  } catch (err) {
22921
- log13.warn(`[lazy-bind] failed (落到主仓写)`, { sessionId, error: err instanceof Error ? err.message : String(err) });
22898
+ log14.warn(`[lazy-bind] failed (落到主仓写)`, { sessionId, error: err instanceof Error ? err.message : String(err) });
22922
22899
  safeWriteLog(PLUGIN_NAME24, {
22923
22900
  hook: "tool.execute.before",
22924
22901
  tool: toolName,
@@ -22936,7 +22913,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22936
22913
  const caller = input.agent;
22937
22914
  if (caller !== undefined && caller !== "codeforge") {
22938
22915
  const reason = `[session-worktree-guard] DENIED: session_merge action=merge 仅 codeforge orchestrator 或用户可调;当前 caller=${caller}`;
22939
- log13.warn(reason, {
22916
+ log14.warn(reason, {
22940
22917
  sessionId,
22941
22918
  tool: toolName,
22942
22919
  action,
@@ -22978,7 +22955,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
22978
22955
  const reasonBase = `[session-worktree-guard] DENIED: 当前 session 要求先调用 plan_read(plan_id="${entry.requiredPlanId}") 再执行写操作`;
22979
22956
  const reason = inherited ? `${reasonBase}
22980
22957
  [gate-deny] child session=${sessionId} 继承父 entry 但父 session planReadOk=false,父 session=${entry.sessionId} 需先调 plan_read(plan_id="${entry.requiredPlanId}")` : reasonBase;
22981
- log13.warn(reason, {
22958
+ log14.warn(reason, {
22982
22959
  tool: toolName,
22983
22960
  sessionId,
22984
22961
  requiredPlanId: entry.requiredPlanId,
@@ -23001,7 +22978,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23001
22978
  if (typeof command === "string" && commandContainsMainRoot(command, mainRoot) && detectBashWriteIntent(command, mainRoot)) {
23002
22979
  const snippet = command.length > 60 ? command.slice(0, 60) + "…" : command;
23003
22980
  const reason = `[session-worktree-guard] DENIED: bash.command 含主仓绝对路径写操作 (${snippet}),请在当前 session worktree (${worktreePath}) 内操作`;
23004
- log13.warn(reason, { sessionId, command: command.slice(0, 200) });
22981
+ log14.warn(reason, { sessionId, command: command.slice(0, 200) });
23005
22982
  safeWriteLog(PLUGIN_NAME24, {
23006
22983
  hook: "tool.execute.before",
23007
22984
  tool: toolName,
@@ -23019,7 +22996,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23019
22996
  if (typeof filePath === "string") {
23020
22997
  const newPath = rewritePath(filePath, mainRoot, worktreePath);
23021
22998
  if (newPath !== null) {
23022
- log13.info(`rewrote ${toolName}.filePath: ${filePath} → ${newPath}`);
22999
+ log14.info(`rewrote ${toolName}.filePath: ${filePath} → ${newPath}`);
23023
23000
  safeWriteLog(PLUGIN_NAME24, {
23024
23001
  hook: "tool.execute.before",
23025
23002
  tool: toolName,
@@ -23037,7 +23014,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23037
23014
  if (typeof target === "string") {
23038
23015
  const newTarget = rewritePath(target, mainRoot, worktreePath);
23039
23016
  if (newTarget !== null) {
23040
- log13.info(`rewrote ast_edit.target: ${target} → ${newTarget}`);
23017
+ log14.info(`rewrote ast_edit.target: ${target} → ${newTarget}`);
23041
23018
  safeWriteLog(PLUGIN_NAME24, {
23042
23019
  hook: "tool.execute.before",
23043
23020
  tool: toolName,
@@ -23053,7 +23030,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23053
23030
  if (typeof root === "string") {
23054
23031
  const newRoot = rewritePath(root, mainRoot, worktreePath);
23055
23032
  if (newRoot !== null) {
23056
- log13.info(`rewrote ast_edit.root: ${root} → ${newRoot}`);
23033
+ log14.info(`rewrote ast_edit.root: ${root} → ${newRoot}`);
23057
23034
  safeWriteLog(PLUGIN_NAME24, {
23058
23035
  hook: "tool.execute.before",
23059
23036
  tool: toolName,
@@ -23071,7 +23048,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23071
23048
  if (typeof workdir === "string") {
23072
23049
  const newWorkdir = rewritePath(workdir, mainRoot, worktreePath);
23073
23050
  if (newWorkdir !== null) {
23074
- log13.info(`rewrote bash.workdir: ${workdir} → ${newWorkdir}`);
23051
+ log14.info(`rewrote bash.workdir: ${workdir} → ${newWorkdir}`);
23075
23052
  safeWriteLog(PLUGIN_NAME24, {
23076
23053
  hook: "tool.execute.before",
23077
23054
  tool: toolName,
@@ -23099,7 +23076,7 @@ var IDLE_TOAST_THROTTLE_MS = 60 * 60000;
23099
23076
  var IDLE_TOAST_DURATION_MS = 8000;
23100
23077
  var IDLE_TOAST_REMINDER_INTERVAL_MS = 30 * 60000;
23101
23078
  var lastIdleToastAt = new Map;
23102
- var log14 = makePluginLogger(PLUGIN_NAME25);
23079
+ var log15 = makePluginLogger(PLUGIN_NAME25);
23103
23080
  var worktreeLifecyclePlugin = async (ctx) => {
23104
23081
  const mainRoot = ctx.directory;
23105
23082
  logLifecycle(PLUGIN_NAME25, "activate", {
@@ -23114,7 +23091,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
23114
23091
  safeAsync(PLUGIN_NAME25, "activate.pruneOrphan", async () => {
23115
23092
  const result = await pruneOrphanWorktrees(mainRoot);
23116
23093
  if (result.cleaned.length > 0 || result.failed.length > 0) {
23117
- log14.info(`[pruneOrphan] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
23094
+ log15.info(`[pruneOrphan] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
23118
23095
  safeWriteLog(PLUGIN_NAME25, {
23119
23096
  hook: "activate.pruneOrphan",
23120
23097
  cleaned: result.cleaned,
@@ -23161,7 +23138,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
23161
23138
  }
23162
23139
  }
23163
23140
  } catch (err) {
23164
- log14.warn(`[lifecycle] empty-worktree fast-path 检测失败 (回退到常规路径)`, {
23141
+ log15.warn(`[lifecycle] empty-worktree fast-path 检测失败 (回退到常规路径)`, {
23165
23142
  sessionId: ended.sessionID,
23166
23143
  error: err instanceof Error ? err.message : String(err)
23167
23144
  });
@@ -23176,7 +23153,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
23176
23153
  });
23177
23154
  }
23178
23155
  } catch (err) {
23179
- log14.warn(`[lifecycle] checkpointCommit failed (继续 discard)`, {
23156
+ log15.warn(`[lifecycle] checkpointCommit failed (继续 discard)`, {
23180
23157
  sessionId: ended.sessionID,
23181
23158
  error: err instanceof Error ? err.message : String(err)
23182
23159
  });
@@ -23193,7 +23170,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
23193
23170
  worktreePath: entry.worktreePath
23194
23171
  });
23195
23172
  } catch (err) {
23196
- log14.warn(`[lifecycle] discardSession failed`, {
23173
+ log15.warn(`[lifecycle] discardSession failed`, {
23197
23174
  sessionId: ended.sessionID,
23198
23175
  error: err instanceof Error ? err.message : String(err)
23199
23176
  });
@@ -23216,7 +23193,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
23216
23193
  lastIdleToastAt.set(ended.sessionID, now);
23217
23194
  const idleMin = Math.round(idleMs / 60000);
23218
23195
  const msg = `\uD83D\uDCA4 Session ${ended.sessionID.slice(0, 8)} worktree 已空闲 ${idleMin}min,` + `用 /merge 收尾或 /discard-session 放弃`;
23219
- const sent = await showToast2(client, { message: msg, variant: "default", duration: IDLE_TOAST_DURATION_MS, title: "CodeForge" }, log14);
23196
+ const sent = await showToast2(client, { message: msg, variant: "default", duration: IDLE_TOAST_DURATION_MS, title: "CodeForge" }, log15);
23220
23197
  safeWriteLog(PLUGIN_NAME25, {
23221
23198
  hook: "event",
23222
23199
  type: ended.type,
@@ -23234,7 +23211,7 @@ var handler25 = worktreeLifecyclePlugin;
23234
23211
 
23235
23212
  // src/index.ts
23236
23213
  var PLUGIN_ID = "codeforge";
23237
- var log15 = makePluginLogger(PLUGIN_ID);
23214
+ var log16 = makePluginLogger(PLUGIN_ID);
23238
23215
  logLifecycle(PLUGIN_ID, "import", { entry: "src/index.ts" });
23239
23216
  var HANDLERS = [
23240
23217
  { name: "agent-router", init: handler },
@@ -23271,7 +23248,7 @@ function makeSerialHook(hookName, fns) {
23271
23248
  } catch (err) {
23272
23249
  if (isDeniedError(err))
23273
23250
  throw err;
23274
- log15.warn(`[${PLUGIN_ID}] ${hookName} handler 异常(已隔离)`, {
23251
+ log16.warn(`[${PLUGIN_ID}] ${hookName} handler 异常(已隔离)`, {
23275
23252
  error: err instanceof Error ? err.message : String(err)
23276
23253
  });
23277
23254
  }
@@ -23284,7 +23261,7 @@ function createCodeforgeServer(opts) {
23284
23261
  const yieldResult = shouldYieldToLocalPlugin({ directory: input.directory });
23285
23262
  if (yieldResult.yield) {
23286
23263
  const msg = formatYieldLog(yieldResult);
23287
- log15.info(msg, { reason: yieldResult.reason, markerPath: yieldResult.markerPath });
23264
+ log16.info(msg, { reason: yieldResult.reason, markerPath: yieldResult.markerPath });
23288
23265
  logLifecycle(PLUGIN_ID, "activate", {
23289
23266
  yield_to_local: true,
23290
23267
  yield_reason: yieldResult.reason,
@@ -23302,7 +23279,7 @@ function createCodeforgeServer(opts) {
23302
23279
  if (r.status === "fulfilled" && r.value && typeof r.value === "object") {
23303
23280
  hooksList.push(r.value);
23304
23281
  } else if (r.status === "rejected") {
23305
- log15.warn(`[${PLUGIN_ID}] handler ${HANDLERS[i].name} init failed (隔离,其他 handler 继续)`, { error: r.reason instanceof Error ? r.reason.message : String(r.reason) });
23282
+ log16.warn(`[${PLUGIN_ID}] handler ${HANDLERS[i].name} init failed (隔离,其他 handler 继续)`, { error: r.reason instanceof Error ? r.reason.message : String(r.reason) });
23306
23283
  }
23307
23284
  });
23308
23285
  logLifecycle(PLUGIN_ID, "activate", {
@@ -23357,7 +23334,7 @@ function createCodeforgeServer(opts) {
23357
23334
  } catch (err) {
23358
23335
  if (isDeniedError(err))
23359
23336
  throw err;
23360
- log15.warn(`[${PLUGIN_ID}] event handler 异常(已隔离)`, {
23337
+ log16.warn(`[${PLUGIN_ID}] event handler 异常(已隔离)`, {
23361
23338
  error: err instanceof Error ? err.message : String(err)
23362
23339
  });
23363
23340
  }