@andyqiu/codeforge 0.5.20 → 0.5.21

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 (3) hide show
  1. package/dist/index.js +649 -1613
  2. package/install.sh +15 -0
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8047,7 +8047,7 @@ async function runAutoFeedback(cfg, ctx) {
8047
8047
  if (cmds.length === 0) {
8048
8048
  throw new Error("auto-feedback: 至少需要 test_cmd 或 lint_cmd 之一");
8049
8049
  }
8050
- const log14 = ctx.log ?? (() => {});
8050
+ const log13 = ctx.log ?? (() => {});
8051
8051
  const attempt = async () => {
8052
8052
  let last = {
8053
8053
  cmd: "",
@@ -8084,7 +8084,7 @@ ${excerpt}
8084
8084
 
8085
8085
  请基于错误信息修复 pending-changes(用 ast-edit / pending-changes.stage),完成后我会重新跑测试验证。`;
8086
8086
  const result2 = await ctx.dispatchToAgent("coder", prompt);
8087
- log14("info", `auto-feedback round ${round} dispatch summary: ${result2.summary.slice(0, 200)}`);
8087
+ log13("info", `auto-feedback round ${round} dispatch summary: ${result2.summary.slice(0, 200)}`);
8088
8088
  };
8089
8089
  const debugResult = await runWithAutoDebug({
8090
8090
  attempt,
@@ -8105,7 +8105,7 @@ ${excerpt}
8105
8105
  history: debugResult.history
8106
8106
  };
8107
8107
  if (!debugResult.ok && debugResult.stopReason === "max-rounds") {
8108
- log14("warn", `auto-feedback 达 max_retries=${cfg.max_retries},escalate to ${cfg.escalate_to}`);
8108
+ log13("warn", `auto-feedback 达 max_retries=${cfg.max_retries},escalate to ${cfg.escalate_to}`);
8109
8109
  const lastFail = debugResult.history[debugResult.history.length - 1];
8110
8110
  const excerpt = lastFail?.result ? extractErrorExcerpt(lastFail.result, cfg.error_excerpt_lines) : "(无失败结果)";
8111
8111
  const escalatePrompt = `coder 连续 ${cfg.max_retries} 轮自纠失败,错误片段:
@@ -8825,248 +8825,6 @@ var autoCommitServer = async (ctx) => {
8825
8825
  };
8826
8826
  var handler3 = autoCommitServer;
8827
8827
 
8828
- // plugins/auto-learning.ts
8829
- init_kh_client();
8830
- import * as crypto2 from "node:crypto";
8831
- var PLUGIN_NAME4 = "auto-learning";
8832
- var DEFAULT_CONFIG2 = {
8833
- minScore: 0.5,
8834
- cooldownMs: 24 * 60 * 60 * 1000,
8835
- fingerprintLimit: 256,
8836
- events: ["bug.fixed", "workflow.completed", "session.ended"]
8837
- };
8838
- var NOISE_TITLE_RE = /^\s*(test|wip|tmp|fix typo|update|chore|debug|todo|hello)\b/i;
8839
- var TRIVIAL_KEYWORDS = ["typo", "格式化", "format", "rename", "重命名"];
8840
- function extractInsights(ctx) {
8841
- if (!ctx || typeof ctx !== "object")
8842
- return [];
8843
- const out = [];
8844
- if (ctx.bug && ctx.bug.symptom && ctx.bug.fix) {
8845
- const rootCause = ctx.bug.rootCause?.trim() ?? "";
8846
- const baseScore = rootCause ? 0.85 : 0.55;
8847
- const score = adjustScore(baseScore, ctx);
8848
- const title = clip2(`Bug 修复:${ctx.bug.symptom}`, 80);
8849
- if (!isTrivial(title)) {
8850
- out.push({
8851
- title,
8852
- content: clip2([
8853
- `**症状**:${ctx.bug.symptom}`,
8854
- rootCause ? `**根因**:${rootCause}` : "",
8855
- `**修复**:${ctx.bug.fix}`,
8856
- ctx.bug.files?.length ? `**涉及**:${ctx.bug.files.slice(0, 5).join(", ")}` : ""
8857
- ].filter(Boolean).join(`
8858
-
8859
- `), 500),
8860
- kind: "bug-fix",
8861
- category: "踩坑记录",
8862
- tags: ctx.bug.files?.slice(0, 5),
8863
- score
8864
- });
8865
- }
8866
- }
8867
- if (ctx.workflow?.learnings?.length) {
8868
- for (const lesson of ctx.workflow.learnings) {
8869
- const text = lesson.trim();
8870
- if (!text || isTrivial(text))
8871
- continue;
8872
- const score = adjustScore(0.7, ctx);
8873
- out.push({
8874
- title: clip2(`${ctx.workflow.name ?? "workflow"}:${text}`, 80),
8875
- content: clip2(text, 500),
8876
- kind: "decision",
8877
- category: "操作指南",
8878
- tags: ctx.workflow.name ? [ctx.workflow.name] : undefined,
8879
- score
8880
- });
8881
- }
8882
- }
8883
- if (ctx.summary && ctx.positiveFeedback && !ctx.rolledBack) {
8884
- const text = ctx.summary.trim();
8885
- if (text && !isTrivial(text)) {
8886
- out.push({
8887
- title: clip2(text.split(/[。.\n]/)[0] || "会话洞察", 80),
8888
- content: clip2(text, 500),
8889
- kind: "decision",
8890
- category: "其它",
8891
- score: adjustScore(0.55, ctx)
8892
- });
8893
- }
8894
- }
8895
- for (const i of out)
8896
- i.fingerprint = fingerprint(i);
8897
- return out.filter((i) => i.score > 0);
8898
- }
8899
- function adjustScore(base, ctx) {
8900
- let s = base;
8901
- if (ctx.positiveFeedback)
8902
- s += 0.1;
8903
- if (ctx.rolledBack)
8904
- s -= 0.5;
8905
- return Math.max(-1, Math.min(1, s));
8906
- }
8907
- function isTrivial(text) {
8908
- if (!text)
8909
- return true;
8910
- if (NOISE_TITLE_RE.test(text))
8911
- return true;
8912
- const lower = text.toLowerCase();
8913
- return TRIVIAL_KEYWORDS.some((k) => lower.includes(k));
8914
- }
8915
- function clip2(s, max) {
8916
- if (!s)
8917
- return "";
8918
- if (s.length <= max)
8919
- return s;
8920
- return s.slice(0, max - 1) + "…";
8921
- }
8922
- function fingerprint(i) {
8923
- const seed = `${i.kind}|${i.title}|${i.content}`;
8924
- return crypto2.createHash("sha1").update(seed).digest("hex").slice(0, 16);
8925
- }
8926
-
8927
- class FingerprintLRU {
8928
- limit;
8929
- list = [];
8930
- constructor(limit = 256) {
8931
- this.limit = limit;
8932
- }
8933
- has(fp, withinMs, now = Date.now()) {
8934
- const e = this.list.find((x) => x.fp === fp);
8935
- if (!e)
8936
- return false;
8937
- return now - e.ts < withinMs;
8938
- }
8939
- add(fp, now = Date.now()) {
8940
- this.list = this.list.filter((x) => x.fp !== fp);
8941
- this.list.unshift({ fp, ts: now });
8942
- if (this.list.length > this.limit)
8943
- this.list.length = this.limit;
8944
- }
8945
- size() {
8946
- return this.list.length;
8947
- }
8948
- }
8949
- function shouldSave(insight, lru, cfg, now = Date.now()) {
8950
- if (insight.score < cfg.minScore) {
8951
- return { decision: "skip", reason: `score ${insight.score.toFixed(2)} < ${cfg.minScore}`, insight };
8952
- }
8953
- if (insight.fingerprint && lru.has(insight.fingerprint, cfg.cooldownMs, now)) {
8954
- return { decision: "skip", reason: "duplicate within cooldown", insight };
8955
- }
8956
- return { decision: "save", reason: "ok", insight };
8957
- }
8958
- var moduleLRU = new FingerprintLRU(DEFAULT_CONFIG2.fingerprintLimit);
8959
- async function processEvent(raw, opts = {}) {
8960
- const cfg = opts.config ?? DEFAULT_CONFIG2;
8961
- const lru = opts.lru ?? moduleLRU;
8962
- const now = opts.now ?? Date.now();
8963
- const ctx = raw ?? {};
8964
- const insights = extractInsights(ctx);
8965
- const decisions = insights.map((i) => shouldSave(i, lru, cfg, now));
8966
- let saved = 0;
8967
- let skipped = 0;
8968
- for (const d of decisions) {
8969
- if (d.decision === "skip") {
8970
- skipped++;
8971
- continue;
8972
- }
8973
- if (typeof opts.saveInsight !== "function") {
8974
- if (d.insight.fingerprint)
8975
- lru.add(d.insight.fingerprint, now);
8976
- skipped++;
8977
- continue;
8978
- }
8979
- try {
8980
- const res = await opts.saveInsight({
8981
- title: d.insight.title,
8982
- content: d.insight.content,
8983
- category: d.insight.category,
8984
- tags: d.insight.tags
8985
- });
8986
- if (res?.ok) {
8987
- if (d.insight.fingerprint)
8988
- lru.add(d.insight.fingerprint, now);
8989
- saved++;
8990
- opts.log?.info(`[${PLUGIN_NAME4}] saved insight: ${d.insight.title}`, { id: res.id });
8991
- if (typeof opts.onSaved === "function") {
8992
- try {
8993
- await opts.onSaved(d.insight, { id: res.id });
8994
- } catch (err) {
8995
- opts.log?.warn(`[${PLUGIN_NAME4}] onSaved hook 抛错(已隔离)`, {
8996
- error: err instanceof Error ? err.message : String(err)
8997
- });
8998
- }
8999
- }
9000
- } else {
9001
- skipped++;
9002
- opts.log?.warn(`[${PLUGIN_NAME4}] save failed: ${res?.error ?? "unknown"}`);
9003
- }
9004
- } catch (err) {
9005
- skipped++;
9006
- opts.log?.warn(`[${PLUGIN_NAME4}] save threw(已隔离)`, {
9007
- error: err instanceof Error ? err.message : String(err)
9008
- });
9009
- }
9010
- }
9011
- return { saved, skipped, decisions };
9012
- }
9013
- function createKhSaveInsightHook(client, log3) {
9014
- if (!client.hasTransport()) {
9015
- log3?.debug?.(`[${PLUGIN_NAME4}] createKhSaveInsightHook: transport 不可用,返 undefined(走 noop)`, {});
9016
- return;
9017
- }
9018
- return async ({ title, content, category, tags }) => {
9019
- const insight = `**${title}**
9020
-
9021
- ${content}`;
9022
- try {
9023
- const r = await client.save({ insight, category, tags });
9024
- if (r.ok) {
9025
- return { ok: true, id: r.id };
9026
- }
9027
- return { ok: false, error: `${r.reason}: ${r.message}` };
9028
- } catch (err) {
9029
- const message = err instanceof Error ? err.message : String(err);
9030
- log3?.warn(`[${PLUGIN_NAME4}] saveInsight hook 抛异常(已隔离)`, { error: message });
9031
- return { ok: false, error: message };
9032
- }
9033
- };
9034
- }
9035
- logLifecycle(PLUGIN_NAME4, "import");
9036
- var TRIGGER_EVENT_TYPES2 = new Set([
9037
- "session.idle",
9038
- "session.error"
9039
- ]);
9040
- var sharedClient = new KhClient;
9041
- var autoLearningServer = async (ctx) => {
9042
- const log3 = makePluginLogger(PLUGIN_NAME4);
9043
- const saveInsight = createKhSaveInsightHook(sharedClient, log3);
9044
- logLifecycle(PLUGIN_NAME4, "activate", {
9045
- directory: ctx.directory,
9046
- triggerEventTypes: [...TRIGGER_EVENT_TYPES2],
9047
- minScore: DEFAULT_CONFIG2.minScore,
9048
- transport_available: sharedClient.hasTransport(),
9049
- save_insight_wired: typeof saveInsight === "function"
9050
- });
9051
- return {
9052
- event: async ({ event }) => {
9053
- await safeAsync(PLUGIN_NAME4, "event", async () => {
9054
- const e = event;
9055
- if (!e.type || !TRIGGER_EVENT_TYPES2.has(e.type))
9056
- return;
9057
- const r = await processEvent({
9058
- type: e.type,
9059
- ...e.properties && typeof e.properties === "object" ? e.properties : {}
9060
- }, { log: log3, saveInsight });
9061
- if (r.saved > 0 || r.skipped > 0) {
9062
- log3.info(`event ${e.type}: saved=${r.saved} skipped=${r.skipped}`);
9063
- }
9064
- });
9065
- }
9066
- };
9067
- };
9068
- var handler4 = autoLearningServer;
9069
-
9070
8828
  // plugins/channels.ts
9071
8829
  import { promises as fs2, statSync as statSync2 } from "node:fs";
9072
8830
  import * as path4 from "node:path";
@@ -9755,9 +9513,9 @@ function makeChannelEvent(event, data = {}) {
9755
9513
 
9756
9514
  // plugins/channels.ts
9757
9515
  init_global_config();
9758
- var PLUGIN_NAME5 = "channels";
9759
- logLifecycle(PLUGIN_NAME5, "import", {});
9760
- var fallbackLog = makePluginLogger(PLUGIN_NAME5);
9516
+ var PLUGIN_NAME4 = "channels";
9517
+ logLifecycle(PLUGIN_NAME4, "import", {});
9518
+ var fallbackLog = makePluginLogger(PLUGIN_NAME4);
9761
9519
  var _channelsCache = null;
9762
9520
  var _activatedDirectory;
9763
9521
  var KNOWN_TYPES = new Set([
@@ -10074,8 +9832,8 @@ function pickSeverity(eventName) {
10074
9832
  return 10;
10075
9833
  return 10;
10076
9834
  }
10077
- var log3 = makePluginLogger(PLUGIN_NAME5);
10078
- var TRIGGER_EVENT_TYPES3 = new Set([
9835
+ var log3 = makePluginLogger(PLUGIN_NAME4);
9836
+ var TRIGGER_EVENT_TYPES2 = new Set([
10079
9837
  "workflow.completed",
10080
9838
  "workflow.failed",
10081
9839
  "approval.required",
@@ -10087,26 +9845,26 @@ var TRIGGER_EVENT_TYPES3 = new Set([
10087
9845
  ]);
10088
9846
  var channelsServer = async (ctx) => {
10089
9847
  _activatedDirectory = ctx.directory;
10090
- logLifecycle(PLUGIN_NAME5, "activate", {
9848
+ logLifecycle(PLUGIN_NAME4, "activate", {
10091
9849
  directory: ctx.directory,
10092
- triggerEventTypes: [...TRIGGER_EVENT_TYPES3],
9850
+ triggerEventTypes: [...TRIGGER_EVENT_TYPES2],
10093
9851
  channelsConfigured: ensureChannels().length
10094
9852
  });
10095
9853
  return {
10096
9854
  event: async ({ event }) => {
10097
- await safeAsync(PLUGIN_NAME5, "event", async () => {
9855
+ await safeAsync(PLUGIN_NAME4, "event", async () => {
10098
9856
  const e = event;
10099
9857
  if (!e || typeof e.type !== "string")
10100
9858
  return;
10101
- if (!TRIGGER_EVENT_TYPES3.has(e.type))
9859
+ if (!TRIGGER_EVENT_TYPES2.has(e.type))
10102
9860
  return;
10103
9861
  const summary = await bridgeEvent(e.type, e.properties ?? {});
10104
9862
  if (summary.results.length > 0) {
10105
- log3.info(`[${PLUGIN_NAME5}] ${e.type} → ${summary.results.length} channels`, {
9863
+ log3.info(`[${PLUGIN_NAME4}] ${e.type} → ${summary.results.length} channels`, {
10106
9864
  any_sent: summary.any_sent,
10107
9865
  rate_limited: summary.rate_limited
10108
9866
  });
10109
- safeWriteLog(PLUGIN_NAME5, {
9867
+ safeWriteLog(PLUGIN_NAME4, {
10110
9868
  hook: "event",
10111
9869
  type: e.type,
10112
9870
  results: summary.results.length,
@@ -10118,7 +9876,7 @@ var channelsServer = async (ctx) => {
10118
9876
  }
10119
9877
  };
10120
9878
  };
10121
- var handler5 = channelsServer;
9879
+ var handler4 = channelsServer;
10122
9880
 
10123
9881
  // lib/agent-resolver.ts
10124
9882
  var chatAgentCacheReader = null;
@@ -10178,8 +9936,8 @@ async function resolveAgentForGuard(input, client, log4, opts = {}) {
10178
9936
  }
10179
9937
 
10180
9938
  // plugins/chat-agent-cache.ts
10181
- var PLUGIN_NAME6 = "chat-agent-cache";
10182
- logLifecycle(PLUGIN_NAME6, "import", {});
9939
+ var PLUGIN_NAME5 = "chat-agent-cache";
9940
+ logLifecycle(PLUGIN_NAME5, "import", {});
10183
9941
  var SESSION_CAP = 500;
10184
9942
  var SESSION_TTL_MS = 24 * 60 * 60 * 1000;
10185
9943
  var sessionAgentMap = new Map;
@@ -10216,14 +9974,14 @@ function readSessionAgent(sessionID) {
10216
9974
  }
10217
9975
  var chatAgentCachePlugin = async (ctx) => {
10218
9976
  setChatAgentCacheReader(readSessionAgent);
10219
- logLifecycle(PLUGIN_NAME6, "activate", {
9977
+ logLifecycle(PLUGIN_NAME5, "activate", {
10220
9978
  directory: ctx.directory,
10221
9979
  session_cap: SESSION_CAP,
10222
9980
  session_ttl_ms: SESSION_TTL_MS
10223
9981
  });
10224
9982
  return {
10225
9983
  "chat.params": async (input, _output) => {
10226
- await safeAsync(PLUGIN_NAME6, "chat.params", async () => {
9984
+ await safeAsync(PLUGIN_NAME5, "chat.params", async () => {
10227
9985
  const sid = input?.sessionID;
10228
9986
  const agent = input?.agent;
10229
9987
  if (typeof sid !== "string" || sid.length === 0)
@@ -10231,7 +9989,7 @@ var chatAgentCachePlugin = async (ctx) => {
10231
9989
  if (typeof agent !== "string" || agent.length === 0)
10232
9990
  return;
10233
9991
  rememberSessionAgent(sid, agent);
10234
- safeWriteLog(PLUGIN_NAME6, {
9992
+ safeWriteLog(PLUGIN_NAME5, {
10235
9993
  hook: "chat.params",
10236
9994
  sessionID: sid,
10237
9995
  agent,
@@ -10241,7 +9999,7 @@ var chatAgentCachePlugin = async (ctx) => {
10241
9999
  });
10242
10000
  },
10243
10001
  "chat.message": async (input, _output) => {
10244
- await safeAsync(PLUGIN_NAME6, "chat.message", async () => {
10002
+ await safeAsync(PLUGIN_NAME5, "chat.message", async () => {
10245
10003
  const sid = input?.sessionID;
10246
10004
  const agent = input?.agent;
10247
10005
  if (typeof sid !== "string" || sid.length === 0)
@@ -10249,7 +10007,7 @@ var chatAgentCachePlugin = async (ctx) => {
10249
10007
  if (typeof agent !== "string" || agent.length === 0)
10250
10008
  return;
10251
10009
  rememberSessionAgent(sid, agent);
10252
- safeWriteLog(PLUGIN_NAME6, {
10010
+ safeWriteLog(PLUGIN_NAME5, {
10253
10011
  hook: "chat.message",
10254
10012
  sessionID: sid,
10255
10013
  agent,
@@ -10260,7 +10018,7 @@ var chatAgentCachePlugin = async (ctx) => {
10260
10018
  }
10261
10019
  };
10262
10020
  };
10263
- var handler6 = chatAgentCachePlugin;
10021
+ var handler5 = chatAgentCachePlugin;
10264
10022
 
10265
10023
  // plugins/codeforge-tools.ts
10266
10024
  import { tool } from "@opencode-ai/plugin";
@@ -10766,10 +10524,10 @@ import * as path5 from "node:path";
10766
10524
  import { z as z16 } from "zod";
10767
10525
 
10768
10526
  // lib/ast-edit-engine.ts
10769
- import * as crypto3 from "node:crypto";
10527
+ import * as crypto2 from "node:crypto";
10770
10528
  var IDENT_RE = /^[A-Za-z_$][\w$]*$/;
10771
10529
  function sha256(s) {
10772
- return crypto3.createHash("sha256").update(s).digest("hex");
10530
+ return crypto2.createHash("sha256").update(s).digest("hex");
10773
10531
  }
10774
10532
  function detectEol(content) {
10775
10533
  let crlf = 0;
@@ -12047,7 +11805,7 @@ import { z as z20 } from "zod";
12047
11805
  // lib/browser-control.ts
12048
11806
  init_runtime_paths();
12049
11807
  import * as path9 from "node:path";
12050
- var DEFAULT_CONFIG3 = {
11808
+ var DEFAULT_CONFIG2 = {
12051
11809
  enabled: false,
12052
11810
  headless: true,
12053
11811
  allow: ["^https?://"],
@@ -12060,7 +11818,7 @@ var DEFAULT_CONFIG3 = {
12060
11818
  function defaultScreenshotDir(root = process.cwd()) {
12061
11819
  return path9.join(runtimeDir(root), "browser", "screenshots");
12062
11820
  }
12063
- function checkUrl(url, cfg = DEFAULT_CONFIG3) {
11821
+ function checkUrl(url, cfg = DEFAULT_CONFIG2) {
12064
11822
  if (typeof url !== "string" || url.trim() === "") {
12065
11823
  return { ok: false, reason: "empty_url" };
12066
11824
  }
@@ -12107,7 +11865,7 @@ class NoopBrowserController {
12107
11865
  }
12108
11866
  async close() {}
12109
11867
  }
12110
- async function tryCreatePlaywrightController(cfg = DEFAULT_CONFIG3, resolver = defaultPlaywrightResolver) {
11868
+ async function tryCreatePlaywrightController(cfg = DEFAULT_CONFIG2, resolver = defaultPlaywrightResolver) {
12111
11869
  if (!cfg.enabled)
12112
11870
  return null;
12113
11871
  let mod;
@@ -12253,7 +12011,7 @@ function describe3(err) {
12253
12011
  }
12254
12012
  }
12255
12013
  async function createBrowserController(opts = {}) {
12256
- const cfg = { ...DEFAULT_CONFIG3, ...opts.cfg };
12014
+ const cfg = { ...DEFAULT_CONFIG2, ...opts.cfg };
12257
12015
  if (!cfg.enabled) {
12258
12016
  return new NoopBrowserController("browser disabled in config");
12259
12017
  }
@@ -13018,7 +12776,7 @@ import * as path13 from "node:path";
13018
12776
 
13019
12777
  // lib/file-lock.ts
13020
12778
  import { promises as fs9 } from "node:fs";
13021
- import * as crypto4 from "node:crypto";
12779
+ import * as crypto3 from "node:crypto";
13022
12780
  import * as os4 from "node:os";
13023
12781
  import * as path12 from "node:path";
13024
12782
  async function withFileLock(lockPath, fn, opts = {}) {
@@ -13028,7 +12786,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
13028
12786
  await fs9.mkdir(path12.dirname(lockPath), { recursive: true });
13029
12787
  const deadline = Date.now() + timeoutMs;
13030
12788
  const myHost = os4.hostname();
13031
- const myToken = crypto4.randomBytes(16).toString("hex");
12789
+ const myToken = crypto3.randomBytes(16).toString("hex");
13032
12790
  let acquired = false;
13033
12791
  while (!acquired) {
13034
12792
  try {
@@ -14871,7 +14629,7 @@ function makeOpencodeRunner(opts) {
14871
14629
  const created = await opts.client.session.create({
14872
14630
  body: {
14873
14631
  parentID: opts.parentSessionID,
14874
- title: clip3(`subtask:${spec.id}`, 80)
14632
+ title: clip2(`subtask:${spec.id}`, 80)
14875
14633
  },
14876
14634
  query: opts.directory ? { directory: opts.directory } : undefined
14877
14635
  });
@@ -15071,7 +14829,7 @@ function safeStringify(v) {
15071
14829
  return String(v);
15072
14830
  }
15073
14831
  }
15074
- function clip3(s, max) {
14832
+ function clip2(s, max) {
15075
14833
  if (!s)
15076
14834
  return "";
15077
14835
  return s.length <= max ? s : s.slice(0, max - 1) + "…";
@@ -15391,7 +15149,7 @@ ${r.text.slice(0, 800)}`
15391
15149
  let childId;
15392
15150
  try {
15393
15151
  const created = await this.opts.client.session.create({
15394
- body: { title: clip4(opts.title, 80) },
15152
+ body: { title: clip3(opts.title, 80) },
15395
15153
  query: this.opts.directory ? { directory: this.opts.directory } : undefined
15396
15154
  });
15397
15155
  if (created.error || !created.data?.id) {
@@ -15534,7 +15292,7 @@ function describe5(err) {
15534
15292
  return String(err);
15535
15293
  }
15536
15294
  }
15537
- function clip4(s, max) {
15295
+ function clip3(s, max) {
15538
15296
  if (!s)
15539
15297
  return "";
15540
15298
  return s.length <= max ? s : s.slice(0, max - 1) + "…";
@@ -15747,8 +15505,8 @@ function parseRuntime(raw, abs) {
15747
15505
  }
15748
15506
 
15749
15507
  // plugins/tool-heartbeat.ts
15750
- var PLUGIN_NAME7 = "tool-heartbeat";
15751
- logLifecycle(PLUGIN_NAME7, "import", {});
15508
+ var PLUGIN_NAME6 = "tool-heartbeat";
15509
+ logLifecycle(PLUGIN_NAME6, "import", {});
15752
15510
  var HEARTBEAT_INTERVAL_MS = 15000;
15753
15511
  var ALERT_30S_MS = 30000;
15754
15512
  var ALERT_60S_MS = 60000;
@@ -15830,15 +15588,15 @@ async function showToast(client, payload, log5) {
15830
15588
  return false;
15831
15589
  }
15832
15590
  }
15833
- var log5 = makePluginLogger(PLUGIN_NAME7);
15591
+ var log5 = makePluginLogger(PLUGIN_NAME6);
15834
15592
  var toolHeartbeatServer = async (ctx) => {
15835
- logLifecycle(PLUGIN_NAME7, "activate", {
15593
+ logLifecycle(PLUGIN_NAME6, "activate", {
15836
15594
  directory: ctx.directory,
15837
15595
  intervalMs: HEARTBEAT_INTERVAL_MS
15838
15596
  });
15839
15597
  const client = ctx.client;
15840
15598
  const interval = setInterval(() => {
15841
- safeAsync(PLUGIN_NAME7, "interval", async () => {
15599
+ safeAsync(PLUGIN_NAME6, "interval", async () => {
15842
15600
  if (inflight.size === 0)
15843
15601
  return;
15844
15602
  const records = [...inflight.values()];
@@ -15848,7 +15606,7 @@ var toolHeartbeatServer = async (ctx) => {
15848
15606
  continue;
15849
15607
  const sent = await showToast(client, { message: alert.message, variant: alert.variant }, log5);
15850
15608
  r.alertsSent.add(alert.threshold);
15851
- safeWriteLog(PLUGIN_NAME7, {
15609
+ safeWriteLog(PLUGIN_NAME6, {
15852
15610
  hook: "interval",
15853
15611
  tool: r.toolName,
15854
15612
  callID: r.callID,
@@ -15866,12 +15624,12 @@ var toolHeartbeatServer = async (ctx) => {
15866
15624
  event: async () => {}
15867
15625
  };
15868
15626
  };
15869
- var handler7 = toolHeartbeatServer;
15627
+ var handler6 = toolHeartbeatServer;
15870
15628
 
15871
15629
  // plugins/codeforge-tools.ts
15872
15630
  var z31 = tool.schema;
15873
- var PLUGIN_NAME8 = "codeforge-tools";
15874
- logLifecycle(PLUGIN_NAME8, "import");
15631
+ var PLUGIN_NAME7 = "codeforge-tools";
15632
+ logLifecycle(PLUGIN_NAME7, "import");
15875
15633
  function wrap(output, metadata) {
15876
15634
  const text = typeof output === "string" ? output : JSON.stringify(output, null, 2);
15877
15635
  return metadata && Object.keys(metadata).length > 0 ? { output: text, metadata } : { output: text };
@@ -15881,7 +15639,7 @@ async function runSafe(toolName, fn) {
15881
15639
  return await fn();
15882
15640
  } catch (err) {
15883
15641
  const msg = err instanceof Error ? err.message : String(err);
15884
- safeWriteLog(PLUGIN_NAME8, {
15642
+ safeWriteLog(PLUGIN_NAME7, {
15885
15643
  level: "error",
15886
15644
  tool: toolName,
15887
15645
  error: msg
@@ -16279,7 +16037,7 @@ var codeforgeToolsServer = async (ctx) => {
16279
16037
  const rt = loadRuntimeSync({ root: ctx.directory });
16280
16038
  const browserEnabled = rt.runtime.tools.browser.enabled;
16281
16039
  const activeTools = browserEnabled ? [...CORE_TOOL_NAMES, ...BROWSER_TOOL_NAMES] : [...CORE_TOOL_NAMES];
16282
- logLifecycle(PLUGIN_NAME8, "activate", {
16040
+ logLifecycle(PLUGIN_NAME7, "activate", {
16283
16041
  directory: ctx.directory,
16284
16042
  tools: activeTools,
16285
16043
  count: activeTools.length,
@@ -16295,7 +16053,7 @@ var codeforgeToolsServer = async (ctx) => {
16295
16053
  client: ctx.client,
16296
16054
  directory: ctx.directory ?? process.cwd(),
16297
16055
  mainRoot: ctx.directory ?? process.cwd(),
16298
- log: (level, msg, data) => safeWriteLog(PLUGIN_NAME8, { level, msg, data })
16056
+ log: (level, msg, data) => safeWriteLog(PLUGIN_NAME7, { level, msg, data })
16299
16057
  });
16300
16058
  __setContext({
16301
16059
  mainRoot: ctx.directory ?? process.cwd(),
@@ -16607,7 +16365,7 @@ var codeforgeToolsServer = async (ctx) => {
16607
16365
  }
16608
16366
  };
16609
16367
  };
16610
- var handler8 = codeforgeToolsServer;
16368
+ var handler7 = codeforgeToolsServer;
16611
16369
 
16612
16370
  // plugins/discover-spec-suggest.ts
16613
16371
  import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "node:fs";
@@ -16789,8 +16547,8 @@ function isValidSlug(slug) {
16789
16547
  }
16790
16548
 
16791
16549
  // plugins/discover-spec-suggest.ts
16792
- var PLUGIN_NAME9 = "discover-spec-suggest";
16793
- logLifecycle(PLUGIN_NAME9, "import", {});
16550
+ var PLUGIN_NAME8 = "discover-spec-suggest";
16551
+ logLifecycle(PLUGIN_NAME8, "import", {});
16794
16552
  var TARGET_AGENT = "codeforge";
16795
16553
  var SESSION_CAP2 = 200;
16796
16554
  var SESSION_TTL_MS2 = 24 * 60 * 60 * 1000;
@@ -16903,7 +16661,7 @@ function loadSpecs(rootDir, opts = {}) {
16903
16661
  const dirReader = opts.dirReader ?? defaultDirReader;
16904
16662
  const dirExists = opts.dirExists ?? defaultDirExists;
16905
16663
  const statReader = opts.statReader ?? defaultStatReader;
16906
- const log6 = makePluginLogger(PLUGIN_NAME9);
16664
+ const log6 = makePluginLogger(PLUGIN_NAME8);
16907
16665
  const specsRoot = join15(rootDir, SPECS_REL_DIR);
16908
16666
  const records = [];
16909
16667
  if (!dirExists(specsRoot)) {
@@ -17031,7 +16789,7 @@ function renderCandidatesNudge(matched) {
17031
16789
  return body.slice(0, NUDGE_MAX_LEN - 4) + `
17032
16790
  …`;
17033
16791
  }
17034
- var log6 = makePluginLogger(PLUGIN_NAME9);
16792
+ var log6 = makePluginLogger(PLUGIN_NAME8);
17035
16793
  var discoverSpecSuggestServer = async (ctx) => {
17036
16794
  try {
17037
16795
  const loaded = loadSpecs(ctx.directory ?? process.cwd());
@@ -17042,7 +16800,7 @@ var discoverSpecSuggestServer = async (ctx) => {
17042
16800
  error: err instanceof Error ? err.message : String(err)
17043
16801
  });
17044
16802
  }
17045
- logLifecycle(PLUGIN_NAME9, "activate", {
16803
+ logLifecycle(PLUGIN_NAME8, "activate", {
17046
16804
  directory: ctx.directory,
17047
16805
  specs_loaded: specIndex.length,
17048
16806
  target_agent: TARGET_AGENT,
@@ -17054,7 +16812,7 @@ var discoverSpecSuggestServer = async (ctx) => {
17054
16812
  });
17055
16813
  return {
17056
16814
  "chat.message": async (input, output) => {
17057
- await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
16815
+ await safeAsync(PLUGIN_NAME8, "chat.message", async () => {
17058
16816
  if (specIndex.length === 0)
17059
16817
  return;
17060
16818
  const text = extractUserText(output);
@@ -17069,7 +16827,7 @@ var discoverSpecSuggestServer = async (ctx) => {
17069
16827
  });
17070
16828
  },
17071
16829
  "experimental.chat.system.transform": async (input, output) => {
17072
- await safeAsync(PLUGIN_NAME9, "experimental.chat.system.transform", async () => {
16830
+ await safeAsync(PLUGIN_NAME8, "experimental.chat.system.transform", async () => {
17073
16831
  if (specIndex.length === 0)
17074
16832
  return;
17075
16833
  if (!output || !Array.isArray(output.system))
@@ -17095,7 +16853,7 @@ var discoverSpecSuggestServer = async (ctx) => {
17095
16853
  if (!nudge)
17096
16854
  return;
17097
16855
  output.system.push(nudge);
17098
- safeWriteLog(PLUGIN_NAME9, {
16856
+ safeWriteLog(PLUGIN_NAME8, {
17099
16857
  hook: "experimental.chat.system.transform",
17100
16858
  sessionID: sid,
17101
16859
  agent: entry.agent,
@@ -17107,927 +16865,93 @@ var discoverSpecSuggestServer = async (ctx) => {
17107
16865
  }
17108
16866
  };
17109
16867
  };
17110
- var handler9 = discoverSpecSuggestServer;
16868
+ var handler8 = discoverSpecSuggestServer;
17111
16869
 
17112
- // plugins/kh-auto-context.ts
17113
- init_kh_client();
17114
-
17115
- // lib/kh-shared-context.ts
17116
- var DEFAULT_MAX_PER_SESSION = 20;
17117
- var DEFAULT_TTL_MS = 5 * 60 * 1000;
17118
-
17119
- class SessionScopedCache {
17120
- cache = new Map;
17121
- inflight = new Map;
17122
- generation = new Map;
17123
- ttlMs;
17124
- maxPerSession;
17125
- now;
17126
- constructor(opts = {}) {
17127
- this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
17128
- this.maxPerSession = opts.maxPerSession ?? DEFAULT_MAX_PER_SESSION;
17129
- this.now = opts.now ?? (() => Date.now());
17130
- }
17131
- async searchOrGet(args) {
17132
- const { client, sessionId, query, limit, timeoutMs } = args;
17133
- const queryHash = this.hashQuery(sessionId, query);
17134
- const inflightKey = `${sessionId}::${queryHash}`;
17135
- const sessionMap2 = this.cache.get(sessionId);
17136
- if (sessionMap2) {
17137
- const entry = sessionMap2.get(queryHash);
17138
- if (entry && this.now() - entry.cachedAt < this.ttlMs) {
17139
- sessionMap2.delete(queryHash);
17140
- sessionMap2.set(queryHash, entry);
17141
- return entry.result;
17142
- }
17143
- if (entry)
17144
- sessionMap2.delete(queryHash);
17145
- }
17146
- const pending = this.inflight.get(inflightKey);
17147
- if (pending)
17148
- return pending;
17149
- const startGen = this.generation.get(sessionId) ?? 0;
17150
- const promise = this.doSearchAndMaybeCache(client, sessionId, queryHash, query, limit, timeoutMs, startGen).finally(() => {
17151
- this.inflight.delete(inflightKey);
17152
- });
17153
- this.inflight.set(inflightKey, promise);
17154
- return promise;
17155
- }
17156
- onSessionEnd(sessionId) {
17157
- this.generation.set(sessionId, (this.generation.get(sessionId) ?? 0) + 1);
17158
- this.cache.delete(sessionId);
17159
- const prefix = `${sessionId}::`;
17160
- for (const key of this.inflight.keys()) {
17161
- if (key.startsWith(prefix)) {
17162
- this.inflight.delete(key);
17163
- }
17164
- }
17165
- }
17166
- _snapshot() {
17167
- const perSession = {};
17168
- let total = 0;
17169
- for (const [sid, map] of this.cache.entries()) {
17170
- perSession[sid] = map.size;
17171
- total += map.size;
17172
- }
17173
- const generations = {};
17174
- for (const [sid, g] of this.generation.entries()) {
17175
- generations[sid] = g;
17176
- }
17177
- return {
17178
- cacheSize: total,
17179
- inflightSize: this.inflight.size,
17180
- sessions: Array.from(this.cache.keys()),
17181
- perSession,
17182
- generations
17183
- };
17184
- }
17185
- _reset() {
17186
- this.cache.clear();
17187
- this.inflight.clear();
17188
- this.generation.clear();
16870
+ // lib/memories.ts
16871
+ import { promises as fs15 } from "node:fs";
16872
+ import * as path19 from "node:path";
16873
+ import * as os5 from "node:os";
16874
+ function resolveConfig(c) {
16875
+ return {
16876
+ projectRoot: c.projectRoot,
16877
+ homeDir: c.homeDir ?? os5.homedir(),
16878
+ projectName: c.projectName ?? path19.basename(c.projectRoot),
16879
+ kh: c.kh,
16880
+ now: c.now ?? Date.now,
16881
+ log: c.log ?? (() => {}),
16882
+ maxPerScope: c.maxPerScope ?? 1000
16883
+ };
16884
+ }
16885
+ function fileFor(scope, cfg) {
16886
+ if (scope === "project") {
16887
+ return path19.join(cfg.projectRoot, ".codeforge", "memories.json");
17189
16888
  }
17190
- hashQuery(sessionId, query) {
17191
- return `${sessionId}::${query}`;
16889
+ return path19.join(cfg.homeDir, ".codeforge", "memories.json");
16890
+ }
16891
+ async function readBank(p) {
16892
+ try {
16893
+ const raw = await fs15.readFile(p, "utf8");
16894
+ const arr = JSON.parse(raw);
16895
+ if (!Array.isArray(arr))
16896
+ return [];
16897
+ return arr.filter((x) => isMemory(x));
16898
+ } catch {
16899
+ return [];
17192
16900
  }
17193
- async doSearchAndMaybeCache(client, sessionId, queryHash, query, limit, timeoutMs, startGen) {
17194
- const result = await this.doSearch(client, query, limit, timeoutMs);
17195
- const currentGen = this.generation.get(sessionId) ?? 0;
17196
- if (currentGen === startGen && result.ok) {
17197
- this.writeCache(sessionId, queryHash, query, result);
17198
- }
17199
- return result;
16901
+ }
16902
+ async function writeBank(p, items) {
16903
+ await fs15.mkdir(path19.dirname(p), { recursive: true });
16904
+ const tmp = `${p}.tmp`;
16905
+ await fs15.writeFile(tmp, JSON.stringify(items, null, 2), "utf8");
16906
+ await fs15.rename(tmp, p);
16907
+ }
16908
+ function isMemory(x) {
16909
+ if (!x || typeof x !== "object")
16910
+ return false;
16911
+ const m = x;
16912
+ return typeof m.id === "string" && (m.scope === "project" || m.scope === "user") && typeof m.content === "string" && typeof m.created_at === "number" && typeof m.updated_at === "number";
16913
+ }
16914
+ var _seq = 0;
16915
+ function localId(now) {
16916
+ _seq = (_seq + 1) % 1e5;
16917
+ return `mem-${now}-${_seq.toString(36)}`;
16918
+ }
16919
+ async function addMemory(params, cfgRaw) {
16920
+ const cfg = resolveConfig(cfgRaw);
16921
+ if (!params.content || params.content.trim().length === 0) {
16922
+ return { ok: false, id: "", written_to: "local", error: "content 不能为空" };
17200
16923
  }
17201
- async doSearch(client, query, limit, timeoutMs) {
17202
- const callPromise = (async () => {
17203
- try {
17204
- return await client.search({ query, limit });
17205
- } catch (err) {
17206
- return {
17207
- ok: false,
17208
- reason: "kh_returned_error",
17209
- message: err instanceof Error ? err.message : String(err)
17210
- };
17211
- }
17212
- })();
17213
- if (timeoutMs === undefined || timeoutMs <= 0) {
17214
- return callPromise;
17215
- }
17216
- let timer = null;
16924
+ const now = cfg.now();
16925
+ const base = {
16926
+ scope: params.scope,
16927
+ content: params.content.trim(),
16928
+ tags: params.tags?.filter((t) => typeof t === "string") ?? [],
16929
+ project: params.scope === "project" ? cfg.projectName : undefined,
16930
+ created_at: now,
16931
+ updated_at: now,
16932
+ source: params.source ?? "manual"
16933
+ };
16934
+ if (cfg.kh) {
17217
16935
  try {
17218
- const racer = new Promise((res) => {
17219
- timer = setTimeout(() => res({
17220
- ok: false,
17221
- reason: "kh_returned_error",
17222
- message: `kh search timeout after ${timeoutMs}ms`
17223
- }), timeoutMs);
16936
+ const r = await cfg.kh.add(base);
16937
+ cfg.log("info", `[memories] add KH ${r.id}`);
16938
+ return { ok: true, id: r.id, written_to: "kh" };
16939
+ } catch (err) {
16940
+ cfg.log("warn", `[memories] KH 写入失败,降级本地`, {
16941
+ error: err instanceof Error ? err.message : String(err)
17224
16942
  });
17225
- return await Promise.race([callPromise, racer]);
17226
- } finally {
17227
- if (timer)
17228
- clearTimeout(timer);
17229
16943
  }
17230
16944
  }
17231
- writeCache(sessionId, queryHash, query, result) {
17232
- let sessionMap2 = this.cache.get(sessionId);
17233
- if (!sessionMap2) {
17234
- sessionMap2 = new Map;
17235
- this.cache.set(sessionId, sessionMap2);
17236
- }
17237
- sessionMap2.delete(queryHash);
17238
- sessionMap2.set(queryHash, { query, result, cachedAt: this.now() });
17239
- while (sessionMap2.size > this.maxPerSession) {
17240
- const oldest = sessionMap2.keys().next().value;
17241
- if (oldest === undefined)
17242
- break;
17243
- sessionMap2.delete(oldest);
17244
- }
16945
+ const file = fileFor(params.scope, cfg);
16946
+ const id = localId(now);
16947
+ const m = { id, ...base };
16948
+ const items = await readBank(file);
16949
+ items.push(m);
16950
+ if (items.length > cfg.maxPerScope) {
16951
+ items.splice(0, items.length - cfg.maxPerScope);
17245
16952
  }
17246
- }
17247
- var sharedKhCache = new SessionScopedCache;
17248
-
17249
- // plugins/kh-auto-context.ts
17250
- var PLUGIN_NAME10 = "kh-auto-context";
17251
- var INJECTION_MODE = "observe-only";
17252
- function resolveInjectionMode(client) {
17253
- return client.hasTransport() ? "system-injected" : "observe-only";
17254
- }
17255
- var DEFAULT_CONFIG4 = {
17256
- minConfidence: 0.55,
17257
- limit: 3,
17258
- minTextLength: 8,
17259
- cacheTtlMs: 5 * 60 * 1000,
17260
- timeoutMs: 1000,
17261
- skipPrefixes: ["/", "!", "@"],
17262
- skipKeywords: ["你好", "hello", "hi", "thanks", "谢谢"]
17263
- };
17264
-
17265
- class QueryCache {
17266
- ttlMs;
17267
- now;
17268
- map = new Map;
17269
- constructor(ttlMs, now = () => Date.now()) {
17270
- this.ttlMs = ttlMs;
17271
- this.now = now;
17272
- }
17273
- shouldSkip(query) {
17274
- const entry = this.map.get(query);
17275
- if (!entry)
17276
- return false;
17277
- return this.now() - entry.injectedAt < this.ttlMs;
17278
- }
17279
- record(query, insights) {
17280
- this.map.set(query, { query, injectedAt: this.now(), insights });
17281
- }
17282
- size() {
17283
- return this.map.size;
17284
- }
17285
- clear() {
17286
- this.map.clear();
17287
- }
17288
- }
17289
- function extractQuery(text) {
17290
- if (!text)
17291
- return "";
17292
- const norm = text.trim().replace(/\s+/g, " ");
17293
- const m = /^[^.。?!?!;;\n]+/.exec(norm);
17294
- let head = (m ? m[0] : norm).trim();
17295
- const fillerRe = /^(请|帮我|麻烦|我想|你能|你可以|看一?下|查一?下|how (do|can) (i|you)|can you|could you|please|help me)\s*/i;
17296
- let prev = "";
17297
- while (prev !== head) {
17298
- prev = head;
17299
- head = head.replace(fillerRe, "").trim();
17300
- }
17301
- return head.slice(0, 80);
17302
- }
17303
- function shouldInject(text, cfg = DEFAULT_CONFIG4) {
17304
- if (!text)
17305
- return false;
17306
- const t = text.trim();
17307
- if (t.length < cfg.minTextLength)
17308
- return false;
17309
- for (const p of cfg.skipPrefixes) {
17310
- if (t.startsWith(p))
17311
- return false;
17312
- }
17313
- const lower = t.toLowerCase();
17314
- for (const kw of cfg.skipKeywords) {
17315
- if (lower.includes(kw.toLowerCase()) && t.length <= kw.length + 5)
17316
- return false;
17317
- }
17318
- return true;
17319
- }
17320
- function formatInjection(query, insights, mode = INJECTION_MODE) {
17321
- const lines = [];
17322
- lines.push(`> \uD83E\uDDE0 [${mode}] 候选 ${insights.length} 条团队经验(query: "${query}")`);
17323
- lines.push("");
17324
- for (const ins of insights) {
17325
- lines.push(`**${ins.title}** \`${ins.category}\` _(${(ins.confidence * 100).toFixed(0)}%)_`);
17326
- const content = ins.content.length > 240 ? ins.content.slice(0, 240) + "…" : ins.content;
17327
- lines.push(content.replace(/\n+/g, " "));
17328
- lines.push("");
17329
- }
17330
- return { query, insights, markdown: lines.join(`
17331
- `), mode };
17332
- }
17333
- var CODEFORGE_CONSTRAINTS = [
17334
- {
17335
- content: "本会话使用 CodeForge:触发词命中(部署/怎么/为什么/之前/历史等)必须先 smart_search;写代码直接在 session worktree 内 edit/write/ast_edit(worktree 由 session-worktree-guard 隔离主仓),完成后由用户 /merge 拍板",
17336
- priority: 9
17337
- },
17338
- {
17339
- content: "API key 仅读环境变量 KNOWLEDGE_API_KEY,禁止写入任何配置文件",
17340
- priority: 9
17341
- },
17342
- {
17343
- content: "沉淀经验优先用 save_chat_insight(自动分类、去重、合并),不要直接 add_knowledge(结构化录入)",
17344
- priority: 8
17345
- }
17346
- ];
17347
- async function seedConstraints(client, log7) {
17348
- if (!client.hasTransport()) {
17349
- log7?.debug?.(`[${PLUGIN_NAME10}] seedConstraints: transport 不可用,跳过`, {});
17350
- return { ok: false, reason: "transport_unavailable" };
17351
- }
17352
- try {
17353
- const result = await client.updateWorkingMemory({
17354
- section: "constraints",
17355
- replace: true,
17356
- items: CODEFORGE_CONSTRAINTS.map((c) => ({
17357
- content: c.content,
17358
- priority: c.priority
17359
- }))
17360
- });
17361
- if (result && typeof result === "object" && "ok" in result && result.ok === false) {
17362
- const r = result;
17363
- log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 降级`, {
17364
- reason: r.reason,
17365
- message: r.message
17366
- });
17367
- return {
17368
- ok: false,
17369
- reason: r.reason === "transport_unavailable" ? "transport_unavailable" : "kh_call_failed",
17370
- message: r.message
17371
- };
17372
- }
17373
- log7?.info(`[${PLUGIN_NAME10}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
17374
- return { ok: true, itemsWritten: CODEFORGE_CONSTRAINTS.length };
17375
- } catch (err) {
17376
- const message = err instanceof Error ? err.message : String(err);
17377
- log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 失败(已静默)`, { error: message });
17378
- return { ok: false, reason: "exception", message };
17379
- }
17380
- }
17381
- function createSystemInjectedHook(client, sessionId, log7) {
17382
- const section = `topic:auto-context-${sessionId ?? "global"}`;
17383
- return async (markdown) => {
17384
- try {
17385
- const result = await client.updateWorkingMemory({
17386
- section,
17387
- replace: true,
17388
- items: [{ content: markdown, priority: 7 }]
17389
- });
17390
- if (result && typeof result === "object" && "ok" in result && result.ok === false) {
17391
- const r = result;
17392
- log7?.warn(`[${PLUGIN_NAME10}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
17393
- sessionId,
17394
- section,
17395
- preview: markdown.slice(0, 200),
17396
- reason: r.reason,
17397
- message: r.message
17398
- });
17399
- return;
17400
- }
17401
- log7?.info(`[${PLUGIN_NAME10}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
17402
- sessionId,
17403
- section
17404
- });
17405
- } catch (err) {
17406
- log7?.warn(`[${PLUGIN_NAME10}] system-injected 抛异常,降级到 observe-only`, {
17407
- sessionId,
17408
- section,
17409
- preview: markdown.slice(0, 200),
17410
- error: err instanceof Error ? err.message : String(err)
17411
- });
17412
- }
17413
- };
17414
- }
17415
- var inflight2 = new Set;
17416
- var INFLIGHT_CAP = 5;
17417
- function inflightKey(sessionId, query) {
17418
- return `${sessionId ?? "global"}:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
17419
- }
17420
- async function runKhSearchAndInject(args) {
17421
- const { query, ctx, opts, mode } = args;
17422
- const cfg = opts.config ?? DEFAULT_CONFIG4;
17423
- const log7 = ctx.log;
17424
- const startedAt = Date.now();
17425
- const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
17426
- let timer = null;
17427
- try {
17428
- return await Promise.race([
17429
- p,
17430
- new Promise((res) => {
17431
- timer = setTimeout(() => res("__timeout__"), ms);
17432
- })
17433
- ]);
17434
- } finally {
17435
- if (timer)
17436
- clearTimeout(timer);
17437
- }
17438
- });
17439
- let result;
17440
- try {
17441
- const searchPromise = sharedKhCache.searchOrGet({
17442
- client: opts.client,
17443
- sessionId: ctx.sessionId ?? "global",
17444
- query,
17445
- limit: cfg.limit
17446
- });
17447
- result = await racer(searchPromise, cfg.timeoutMs);
17448
- } catch (err) {
17449
- log7?.warn(`[${PLUGIN_NAME10}] client.search threw (sync or async), return null`, {
17450
- query,
17451
- elapsedMs: Date.now() - startedAt,
17452
- sessionId: ctx.sessionId,
17453
- error: err instanceof Error ? err.message : String(err)
17454
- });
17455
- opts.cache.record(query, []);
17456
- return null;
17457
- }
17458
- if (result === "__timeout__") {
17459
- log7?.warn(`[${PLUGIN_NAME10}] timeout`, {
17460
- query,
17461
- ms: cfg.timeoutMs,
17462
- elapsedMs: Date.now() - startedAt,
17463
- sessionId: ctx.sessionId
17464
- });
17465
- opts.cache.record(query, []);
17466
- return null;
17467
- }
17468
- if (!result.ok) {
17469
- log7?.warn(`[${PLUGIN_NAME10}] kh degraded`, {
17470
- reason: result.reason,
17471
- query,
17472
- elapsedMs: Date.now() - startedAt,
17473
- sessionId: ctx.sessionId
17474
- });
17475
- opts.cache.record(query, []);
17476
- return null;
17477
- }
17478
- const filtered = result.insights.filter((i) => (i.confidence ?? 0) >= cfg.minConfidence);
17479
- if (filtered.length === 0) {
17480
- opts.cache.record(query, []);
17481
- log7?.debug?.(`[${PLUGIN_NAME10}] no candidate above threshold`, {
17482
- query,
17483
- rawCount: result.insights.length,
17484
- elapsedMs: Date.now() - startedAt,
17485
- sessionId: ctx.sessionId
17486
- });
17487
- return null;
17488
- }
17489
- const payload = formatInjection(query, filtered, mode);
17490
- if (typeof ctx.injectContext === "function") {
17491
- try {
17492
- await ctx.injectContext(payload.markdown);
17493
- } catch (err) {
17494
- log7?.warn(`[${PLUGIN_NAME10}] injectContext threw`, {
17495
- error: err instanceof Error ? err.message : String(err),
17496
- query,
17497
- sessionId: ctx.sessionId
17498
- });
17499
- }
17500
- }
17501
- opts.cache.record(query, filtered);
17502
- log7?.info(`[${PLUGIN_NAME10}] inject complete (${mode})`, {
17503
- query,
17504
- mode,
17505
- candidateCount: filtered.length,
17506
- elapsedMs: Date.now() - startedAt,
17507
- sessionId: ctx.sessionId
17508
- });
17509
- return payload;
17510
- }
17511
- async function handleMessage2(raw, opts) {
17512
- const cfg = opts.config ?? DEFAULT_CONFIG4;
17513
- const mode = opts.mode ?? INJECTION_MODE;
17514
- const ctx = raw ?? {};
17515
- const log7 = ctx.log;
17516
- const text = (ctx.content ?? "").trim();
17517
- if (!shouldInject(text, cfg)) {
17518
- log7?.debug?.(`[${PLUGIN_NAME10}] skip (filter)`, { textLen: text.length });
17519
- return;
17520
- }
17521
- const query = extractQuery(text);
17522
- if (!query)
17523
- return;
17524
- if (opts.cache.shouldSkip(query)) {
17525
- log7?.debug?.(`[${PLUGIN_NAME10}] cache hit, skip`, { query });
17526
- return;
17527
- }
17528
- if (inflight2.size >= INFLIGHT_CAP) {
17529
- log7?.warn(`[${PLUGIN_NAME10}] inflight cap reached, skip`, {
17530
- query,
17531
- inflightSize: inflight2.size,
17532
- cap: INFLIGHT_CAP,
17533
- sessionId: ctx.sessionId
17534
- });
17535
- return;
17536
- }
17537
- const key = inflightKey(ctx.sessionId, query);
17538
- inflight2.add(key);
17539
- runKhSearchAndInject({ query, ctx, opts, mode }).catch((err) => {
17540
- log7?.warn(`[${PLUGIN_NAME10}] runKhSearchAndInject 顶层兜底捕获`, {
17541
- error: err instanceof Error ? err.message : String(err),
17542
- query,
17543
- sessionId: ctx.sessionId
17544
- });
17545
- }).finally(() => {
17546
- inflight2.delete(key);
17547
- });
17548
- }
17549
- logLifecycle(PLUGIN_NAME10, "import");
17550
- var sharedClient2 = new KhClient;
17551
- var sharedCache = new QueryCache(DEFAULT_CONFIG4.cacheTtlMs);
17552
- var khAutoContextServer = async (ctx) => {
17553
- const log7 = makePluginLogger(PLUGIN_NAME10);
17554
- const runtimeMode = resolveInjectionMode(sharedClient2);
17555
- logLifecycle(PLUGIN_NAME10, "activate", {
17556
- directory: ctx.directory,
17557
- minConfidence: DEFAULT_CONFIG4.minConfidence,
17558
- timeoutMs: DEFAULT_CONFIG4.timeoutMs,
17559
- mode: runtimeMode,
17560
- transport_available: sharedClient2.hasTransport()
17561
- });
17562
- seedConstraints(sharedClient2, log7).catch((err) => {
17563
- log7.warn("seedConstraints 顶层兜底捕获", {
17564
- error: err instanceof Error ? err.message : String(err)
17565
- });
17566
- });
17567
- return {
17568
- "chat.message": async (input, output) => {
17569
- await safeAsync(PLUGIN_NAME10, "chat.message", async () => {
17570
- const text = extractUserText(output);
17571
- if (!text)
17572
- return;
17573
- const injectContext = runtimeMode === "system-injected" ? createSystemInjectedHook(sharedClient2, input.sessionID, log7) : async (markdown) => {
17574
- log7.info(`KH context candidate (${markdown.length} chars)`, {
17575
- sessionID: input.sessionID,
17576
- mode: runtimeMode,
17577
- preview: markdown.slice(0, 200)
17578
- });
17579
- };
17580
- await handleMessage2({
17581
- content: text,
17582
- sessionId: input.sessionID,
17583
- injectContext,
17584
- log: log7
17585
- }, { client: sharedClient2, cache: sharedCache, mode: runtimeMode });
17586
- });
17587
- },
17588
- event: async ({ event }) => {
17589
- await safeAsync(PLUGIN_NAME10, "event", async () => {
17590
- const e = event;
17591
- if (e.type !== "session.idle")
17592
- return;
17593
- const props = e.properties;
17594
- const sid = props?.sessionID;
17595
- if (typeof sid !== "string" || !sid)
17596
- return;
17597
- sharedKhCache.onSessionEnd(sid);
17598
- log7.debug?.(`[${PLUGIN_NAME10}] session.idle: cleared shared cache`, {
17599
- sessionID: sid
17600
- });
17601
- });
17602
- }
17603
- };
17604
- };
17605
- var handler10 = khAutoContextServer;
17606
-
17607
- // lib/condenser.ts
17608
- var DEFAULT_CONDENSE = {
17609
- budget: 128000,
17610
- threshold: 0.7,
17611
- target: 0.4,
17612
- keepRecent: 6,
17613
- keepSystem: true,
17614
- charsPerToken: 4
17615
- };
17616
- function estimateTokens(text, charsPerToken = 4) {
17617
- if (!text)
17618
- return 0;
17619
- return Math.max(1, Math.ceil(text.length / charsPerToken));
17620
- }
17621
- function tokenizeMessages(msgs, charsPerToken = 4) {
17622
- let total = 0;
17623
- for (const m of msgs) {
17624
- total += m.tokens ?? estimateTokens(m.content, charsPerToken);
17625
- }
17626
- return total;
17627
- }
17628
- function fallbackSummarize(msgs) {
17629
- if (msgs.length === 0)
17630
- return "";
17631
- const head = msgs.slice(0, 2).map((m) => `- [${m.role}] ${truncate(m.content, 120)}`);
17632
- const tail = msgs.slice(-2).map((m) => `- [${m.role}] ${truncate(m.content, 120)}`);
17633
- const tagCounts = new Map;
17634
- let userCount = 0;
17635
- let assistantCount = 0;
17636
- let toolCount = 0;
17637
- for (const m of msgs) {
17638
- if (m.role === "user")
17639
- userCount++;
17640
- else if (m.role === "assistant")
17641
- assistantCount++;
17642
- else if (m.role === "tool")
17643
- toolCount++;
17644
- if (m.tag)
17645
- tagCounts.set(m.tag, (tagCounts.get(m.tag) ?? 0) + 1);
17646
- }
17647
- const tagsLine = [...tagCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([t, c]) => `${t}×${c}`).join(", ");
17648
- return [
17649
- `### 历史摘要(共 ${msgs.length} 条 · user=${userCount} assistant=${assistantCount} tool=${toolCount})`,
17650
- tagsLine ? `- 关键事件:${tagsLine}` : "",
17651
- "",
17652
- "**开头:**",
17653
- ...head,
17654
- "",
17655
- "**结尾:**",
17656
- ...tail
17657
- ].filter(Boolean).join(`
17658
- `);
17659
- }
17660
- function truncate(s, max) {
17661
- if (!s)
17662
- return "";
17663
- return s.length <= max ? s : s.slice(0, max - 1) + "…";
17664
- }
17665
- async function condense(input, opts = {}) {
17666
- const cfg = { ...DEFAULT_CONDENSE, ...opts.config ?? {} };
17667
- const msgs = [...input];
17668
- const before = {
17669
- count: msgs.length,
17670
- tokens: tokenizeMessages(msgs, cfg.charsPerToken)
17671
- };
17672
- if (cfg.budget <= 0 || before.tokens / cfg.budget < cfg.threshold) {
17673
- return {
17674
- messages: msgs,
17675
- before,
17676
- after: { ...before },
17677
- compressed: false,
17678
- reason: `usage ${(before.tokens / Math.max(cfg.budget, 1)).toFixed(2)} < threshold ${cfg.threshold}`
17679
- };
17680
- }
17681
- const systems = cfg.keepSystem ? msgs.filter((m) => m.role === "system") : [];
17682
- const nonSystem = cfg.keepSystem ? msgs.filter((m) => m.role !== "system") : msgs;
17683
- const recents = nonSystem.slice(-cfg.keepRecent);
17684
- const middles = nonSystem.slice(0, Math.max(0, nonSystem.length - cfg.keepRecent));
17685
- if (middles.length === 0) {
17686
- return {
17687
- messages: msgs,
17688
- before,
17689
- after: { ...before },
17690
- compressed: false,
17691
- reason: "nothing in middle to compress"
17692
- };
17693
- }
17694
- let summary = "";
17695
- try {
17696
- summary = await (opts.summarize ?? fallbackSummarize)(middles) ?? "";
17697
- } catch {
17698
- summary = fallbackSummarize(middles);
17699
- }
17700
- if (!summary)
17701
- summary = fallbackSummarize(middles);
17702
- const summaryMsg = {
17703
- role: "assistant",
17704
- content: summary,
17705
- tag: "condensed-summary"
17706
- };
17707
- const out = [...systems, summaryMsg, ...recents];
17708
- const after = {
17709
- count: out.length,
17710
- tokens: tokenizeMessages(out, cfg.charsPerToken)
17711
- };
17712
- return {
17713
- messages: out,
17714
- before,
17715
- after,
17716
- compressed: true,
17717
- summary,
17718
- reason: `compressed ${middles.length} → 1 summary (target ${cfg.target})`
17719
- };
17720
- }
17721
-
17722
- // plugins/kh-reminder.ts
17723
- var PLUGIN_NAME11 = "kh-reminder";
17724
- logLifecycle(PLUGIN_NAME11, "import", {});
17725
- var TRIGGER_WORDS_ZH = [
17726
- "怎么",
17727
- "怎样",
17728
- "如何",
17729
- "为什么",
17730
- "之前",
17731
- "历史",
17732
- "以前",
17733
- "部署",
17734
- "上线",
17735
- "发布",
17736
- "配置",
17737
- "地址",
17738
- "端口",
17739
- "凭证",
17740
- "接入",
17741
- "集成",
17742
- "谁负责",
17743
- "谁写的",
17744
- "报错",
17745
- "报什么错",
17746
- "跑不起来",
17747
- "起不来",
17748
- "挂了",
17749
- "崩了",
17750
- "规范",
17751
- "约定",
17752
- "风格"
17753
- ];
17754
- var TRIGGER_WORDS_EN = [
17755
- "how to",
17756
- "why",
17757
- "previously",
17758
- "deploy",
17759
- "release",
17760
- "config",
17761
- "URL",
17762
- "credential",
17763
- "token",
17764
- "API key",
17765
- "api[_-]?key",
17766
- "integrate",
17767
- "who owns",
17768
- "error",
17769
- "failed",
17770
- "convention"
17771
- ];
17772
- function buildTriggerRegex() {
17773
- const escapedZh = TRIGGER_WORDS_ZH.map(escapeRegex).join("|");
17774
- const enParts = [];
17775
- for (const w of TRIGGER_WORDS_EN) {
17776
- if (w === "api[_-]?key") {
17777
- enParts.push(w);
17778
- } else {
17779
- enParts.push(escapeRegex(w));
17780
- }
17781
- }
17782
- const escapedEn = enParts.join("|");
17783
- const pattern = `(${escapedZh})|\\b(${escapedEn})\\b`;
17784
- return new RegExp(pattern, "i");
17785
- }
17786
- function escapeRegex(s) {
17787
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17788
- }
17789
- var TRIGGER_REGEX = buildTriggerRegex();
17790
- var COOLDOWN_MS = 5 * 60 * 1000;
17791
- var lastTriggerAt = new Map;
17792
- var DEFAULT_THRESHOLD = 0.5;
17793
- var DEFAULT_RECENT_ROUNDS = 5;
17794
- function computeUsageRatio(messages) {
17795
- const cpt = DEFAULT_CONDENSE.charsPerToken;
17796
- const budget = DEFAULT_CONDENSE.budget;
17797
- if (budget <= 0)
17798
- return 0;
17799
- let total = 0;
17800
- for (const m of messages) {
17801
- total += m.tokens ?? estimateTokens(m.content, cpt);
17802
- }
17803
- return total / budget;
17804
- }
17805
- function findTriggerWord(messages) {
17806
- for (let i = messages.length - 1;i >= 0; i--) {
17807
- const m = messages[i];
17808
- if (!m || m.role !== "user")
17809
- continue;
17810
- const text = m.content ?? "";
17811
- const match = TRIGGER_REGEX.exec(text);
17812
- if (match) {
17813
- return match[0];
17814
- }
17815
- return null;
17816
- }
17817
- return null;
17818
- }
17819
- function hasRecentSmartSearch(messages, recentRounds) {
17820
- const slice = messages.slice(-recentRounds);
17821
- for (const m of slice) {
17822
- const c = m.content ?? "";
17823
- if (/smart_search/i.test(c))
17824
- return true;
17825
- if (m.tag && /smart_search/i.test(m.tag))
17826
- return true;
17827
- }
17828
- return false;
17829
- }
17830
- function evaluate(input) {
17831
- const messages = Array.isArray(input.messages) ? input.messages : [];
17832
- const sessionId = input.sessionId ?? "unknown";
17833
- const threshold = input.threshold ?? DEFAULT_THRESHOLD;
17834
- const recentRounds = input.recentRounds ?? DEFAULT_RECENT_ROUNDS;
17835
- const now = (input.nowFn ?? Date.now)();
17836
- if (messages.length === 0) {
17837
- return { triggered: false, sessionId };
17838
- }
17839
- let reason = null;
17840
- const ratio = computeUsageRatio(messages);
17841
- if (ratio >= threshold) {
17842
- reason = { kind: "token-usage", ratio, threshold };
17843
- }
17844
- if (!reason) {
17845
- const word = findTriggerWord(messages);
17846
- if (word) {
17847
- reason = { kind: "trigger-word", word };
17848
- }
17849
- }
17850
- if (!reason) {
17851
- if (messages.length >= 6 && !hasRecentSmartSearch(messages, recentRounds)) {
17852
- reason = { kind: "no-search-in-recent-rounds", rounds: recentRounds };
17853
- }
17854
- }
17855
- if (!reason) {
17856
- return { triggered: false, sessionId };
17857
- }
17858
- const last = lastTriggerAt.get(sessionId);
17859
- if (last !== undefined && now - last < COOLDOWN_MS) {
17860
- return {
17861
- triggered: false,
17862
- cooldown_skipped: true,
17863
- reason,
17864
- sessionId
17865
- };
17866
- }
17867
- lastTriggerAt.set(sessionId, now);
17868
- return { triggered: true, reason, sessionId };
17869
- }
17870
- function handleObserve(raw, log7) {
17871
- const ctx = raw ?? {};
17872
- if (!Array.isArray(ctx.messages))
17873
- return null;
17874
- try {
17875
- const result = evaluate(ctx);
17876
- if (result.triggered && result.reason) {
17877
- const reasonStr = formatReason(result.reason);
17878
- log7?.info(`[${PLUGIN_NAME11}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
17879
- sessionId: result.sessionId,
17880
- reason: result.reason,
17881
- suggestion: "调用 smart_search 查项目历史;完成后用 save_chat_insight 沉淀"
17882
- });
17883
- }
17884
- return result;
17885
- } catch (err) {
17886
- log7?.warn(`[${PLUGIN_NAME11}] evaluate 异常(已隔离)`, {
17887
- error: err instanceof Error ? err.message : String(err)
17888
- });
17889
- return null;
17890
- }
17891
- }
17892
- function formatReason(r) {
17893
- switch (r.kind) {
17894
- case "token-usage":
17895
- return `token-usage ${(r.ratio * 100).toFixed(1)}% ≥ ${(r.threshold * 100).toFixed(0)}%`;
17896
- case "trigger-word":
17897
- return `trigger-word: "${r.word}"`;
17898
- case "no-search-in-recent-rounds":
17899
- return `no-search-in-recent-rounds (last ${r.rounds})`;
17900
- }
17901
- }
17902
- var log7 = makePluginLogger(PLUGIN_NAME11);
17903
- var khReminderServer = async (ctx) => {
17904
- logLifecycle(PLUGIN_NAME11, "activate", {
17905
- directory: ctx.directory,
17906
- threshold: DEFAULT_THRESHOLD,
17907
- cooldown_ms: COOLDOWN_MS,
17908
- trigger_words_total: TRIGGER_WORDS_ZH.length + TRIGGER_WORDS_EN.length
17909
- });
17910
- return {
17911
- "experimental.chat.messages.transform": async (_input, output) => {
17912
- await safeAsync(PLUGIN_NAME11, "experimental.chat.messages.transform", async () => {
17913
- const list = output.messages;
17914
- if (!Array.isArray(list) || list.length === 0)
17915
- return;
17916
- let sessionId = "unknown";
17917
- const flat = list.map((m) => {
17918
- const info = m.info;
17919
- if (info && typeof info.sessionID === "string" && sessionId === "unknown") {
17920
- sessionId = info.sessionID;
17921
- }
17922
- const role = info?.role ?? "user";
17923
- const parts = m.parts ?? [];
17924
- const content = parts.filter((p) => p.type === "text").map((p) => p.text ?? "").join(`
17925
- `);
17926
- return { role, content };
17927
- });
17928
- const result = handleObserve({ messages: flat, sessionId }, log7);
17929
- if (!result)
17930
- return;
17931
- safeWriteLog(PLUGIN_NAME11, {
17932
- hook: "experimental.chat.messages.transform",
17933
- mode: "observe-only",
17934
- sessionId: result.sessionId,
17935
- triggered: result.triggered,
17936
- cooldown_skipped: result.cooldown_skipped ?? false,
17937
- reason: result.reason ?? null,
17938
- msgs: flat.length
17939
- });
17940
- });
17941
- }
17942
- };
17943
- };
17944
- var handler11 = khReminderServer;
17945
-
17946
- // lib/memories.ts
17947
- import { promises as fs15 } from "node:fs";
17948
- import * as path19 from "node:path";
17949
- import * as os5 from "node:os";
17950
- function resolveConfig(c) {
17951
- return {
17952
- projectRoot: c.projectRoot,
17953
- homeDir: c.homeDir ?? os5.homedir(),
17954
- projectName: c.projectName ?? path19.basename(c.projectRoot),
17955
- kh: c.kh,
17956
- now: c.now ?? Date.now,
17957
- log: c.log ?? (() => {}),
17958
- maxPerScope: c.maxPerScope ?? 1000
17959
- };
17960
- }
17961
- function fileFor(scope, cfg) {
17962
- if (scope === "project") {
17963
- return path19.join(cfg.projectRoot, ".codeforge", "memories.json");
17964
- }
17965
- return path19.join(cfg.homeDir, ".codeforge", "memories.json");
17966
- }
17967
- async function readBank(p) {
17968
- try {
17969
- const raw = await fs15.readFile(p, "utf8");
17970
- const arr = JSON.parse(raw);
17971
- if (!Array.isArray(arr))
17972
- return [];
17973
- return arr.filter((x) => isMemory(x));
17974
- } catch {
17975
- return [];
17976
- }
17977
- }
17978
- async function writeBank(p, items) {
17979
- await fs15.mkdir(path19.dirname(p), { recursive: true });
17980
- const tmp = `${p}.tmp`;
17981
- await fs15.writeFile(tmp, JSON.stringify(items, null, 2), "utf8");
17982
- await fs15.rename(tmp, p);
17983
- }
17984
- function isMemory(x) {
17985
- if (!x || typeof x !== "object")
17986
- return false;
17987
- const m = x;
17988
- return typeof m.id === "string" && (m.scope === "project" || m.scope === "user") && typeof m.content === "string" && typeof m.created_at === "number" && typeof m.updated_at === "number";
17989
- }
17990
- var _seq = 0;
17991
- function localId(now) {
17992
- _seq = (_seq + 1) % 1e5;
17993
- return `mem-${now}-${_seq.toString(36)}`;
17994
- }
17995
- async function addMemory(params, cfgRaw) {
17996
- const cfg = resolveConfig(cfgRaw);
17997
- if (!params.content || params.content.trim().length === 0) {
17998
- return { ok: false, id: "", written_to: "local", error: "content 不能为空" };
17999
- }
18000
- const now = cfg.now();
18001
- const base = {
18002
- scope: params.scope,
18003
- content: params.content.trim(),
18004
- tags: params.tags?.filter((t) => typeof t === "string") ?? [],
18005
- project: params.scope === "project" ? cfg.projectName : undefined,
18006
- created_at: now,
18007
- updated_at: now,
18008
- source: params.source ?? "manual"
18009
- };
18010
- if (cfg.kh) {
18011
- try {
18012
- const r = await cfg.kh.add(base);
18013
- cfg.log("info", `[memories] add → KH ${r.id}`);
18014
- return { ok: true, id: r.id, written_to: "kh" };
18015
- } catch (err) {
18016
- cfg.log("warn", `[memories] KH 写入失败,降级本地`, {
18017
- error: err instanceof Error ? err.message : String(err)
18018
- });
18019
- }
18020
- }
18021
- const file = fileFor(params.scope, cfg);
18022
- const id = localId(now);
18023
- const m = { id, ...base };
18024
- const items = await readBank(file);
18025
- items.push(m);
18026
- if (items.length > cfg.maxPerScope) {
18027
- items.splice(0, items.length - cfg.maxPerScope);
18028
- }
18029
- await writeBank(file, items);
18030
- return { ok: true, id, written_to: "local" };
16953
+ await writeBank(file, items);
16954
+ return { ok: true, id, written_to: "local" };
18031
16955
  }
18032
16956
  async function listMemories(opts, cfgRaw) {
18033
16957
  const cfg = resolveConfig(cfgRaw);
@@ -18142,9 +17066,9 @@ function bagOfWordsScore(query, doc) {
18142
17066
  }
18143
17067
 
18144
17068
  // plugins/memories-context.ts
18145
- var PLUGIN_NAME12 = "memories-context";
18146
- var INJECTION_MODE2 = "observe-only";
18147
- var DEFAULT_CONFIG5 = {
17069
+ var PLUGIN_NAME9 = "memories-context";
17070
+ var INJECTION_MODE = "observe-only";
17071
+ var DEFAULT_CONFIG3 = {
18148
17072
  minTextLength: 8,
18149
17073
  limit: 5,
18150
17074
  perItemLimit: 240,
@@ -18155,7 +17079,7 @@ var DEFAULT_CONFIG5 = {
18155
17079
  skipKeywords: ["你好", "hello", "hi", "thanks", "谢谢"]
18156
17080
  };
18157
17081
 
18158
- class QueryCache2 {
17082
+ class QueryCache {
18159
17083
  ttlMs;
18160
17084
  now;
18161
17085
  map = new Map;
@@ -18180,7 +17104,7 @@ class QueryCache2 {
18180
17104
  }
18181
17105
  }
18182
17106
  var FILLER_RE = /^(请|帮我|麻烦|我想|你能|你可以|看一?下|查一?下|how (do|can) (i|you)|can you|could you|please|help me)\s*/i;
18183
- function extractQuery2(text) {
17107
+ function extractQuery(text) {
18184
17108
  if (!text)
18185
17109
  return "";
18186
17110
  const norm = text.trim().replace(/\s+/g, " ");
@@ -18193,7 +17117,7 @@ function extractQuery2(text) {
18193
17117
  }
18194
17118
  return head.slice(0, 100);
18195
17119
  }
18196
- function shouldRecall(text, cfg = DEFAULT_CONFIG5) {
17120
+ function shouldRecall(text, cfg = DEFAULT_CONFIG3) {
18197
17121
  if (!text)
18198
17122
  return false;
18199
17123
  const t = text.trim();
@@ -18227,28 +17151,28 @@ function parseDirective(text) {
18227
17151
  return { kind: "list" };
18228
17152
  return { kind: "none" };
18229
17153
  }
18230
- async function handleMessage3(raw, opts) {
18231
- const cfg = opts.cfg ?? DEFAULT_CONFIG5;
18232
- const cache2 = opts.cache ?? new QueryCache2(cfg.cacheTtlMs);
17154
+ async function handleMessage2(raw, opts) {
17155
+ const cfg = opts.cfg ?? DEFAULT_CONFIG3;
17156
+ const cache2 = opts.cache ?? new QueryCache(cfg.cacheTtlMs);
18233
17157
  const ctx = raw ?? {};
18234
- const log8 = ctx.log;
17158
+ const log7 = ctx.log;
18235
17159
  const text = (ctx.content ?? "").trim();
18236
17160
  if (!text)
18237
- return { kind: "noop", reason: "empty", mode: INJECTION_MODE2 };
17161
+ return { kind: "noop", reason: "empty", mode: INJECTION_MODE };
18238
17162
  const dir = parseDirective(text);
18239
17163
  if (dir.kind !== "none") {
18240
- return await handleDirective(dir, ctx, opts.memCfg, log8);
17164
+ return await handleDirective(dir, ctx, opts.memCfg, log7);
18241
17165
  }
18242
17166
  if (!shouldRecall(text, cfg)) {
18243
- log8?.debug?.(`[${PLUGIN_NAME12}] skip (filter)`, { textLen: text.length });
18244
- return { kind: "noop", reason: "filtered", mode: INJECTION_MODE2 };
17167
+ log7?.debug?.(`[${PLUGIN_NAME9}] skip (filter)`, { textLen: text.length });
17168
+ return { kind: "noop", reason: "filtered", mode: INJECTION_MODE };
18245
17169
  }
18246
- const query = extractQuery2(text);
17170
+ const query = extractQuery(text);
18247
17171
  if (!query)
18248
- return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE2 };
17172
+ return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE };
18249
17173
  if (cache2.shouldSkip(query)) {
18250
- log8?.debug?.(`[${PLUGIN_NAME12}] cache hit`, { query });
18251
- return { kind: "noop", reason: "cache", mode: INJECTION_MODE2 };
17174
+ log7?.debug?.(`[${PLUGIN_NAME9}] cache hit`, { query });
17175
+ return { kind: "noop", reason: "cache", mode: INJECTION_MODE };
18252
17176
  }
18253
17177
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
18254
17178
  let timer = null;
@@ -18272,43 +17196,43 @@ async function handleMessage3(raw, opts) {
18272
17196
  }, opts.memCfg);
18273
17197
  const result = await racer(injectPromise, cfg.timeoutMs);
18274
17198
  if (result === "__timeout__") {
18275
- log8?.warn(`[${PLUGIN_NAME12}] timeout`, { query, ms: cfg.timeoutMs });
17199
+ log7?.warn(`[${PLUGIN_NAME9}] timeout`, { query, ms: cfg.timeoutMs });
18276
17200
  cache2.record(query, 0);
18277
- return { kind: "noop", reason: "timeout", mode: INJECTION_MODE2 };
17201
+ return { kind: "noop", reason: "timeout", mode: INJECTION_MODE };
18278
17202
  }
18279
17203
  if (result.recalled === 0 || result.used === 0) {
18280
17204
  cache2.record(query, 0);
18281
- return { kind: "noop", reason: "no_match", mode: INJECTION_MODE2 };
17205
+ return { kind: "noop", reason: "no_match", mode: INJECTION_MODE };
18282
17206
  }
18283
17207
  if (typeof ctx.injectContext === "function") {
18284
17208
  try {
18285
17209
  await ctx.injectContext(result.text);
18286
17210
  } catch (err) {
18287
- log8?.warn(`[${PLUGIN_NAME12}] injectContext threw`, {
17211
+ log7?.warn(`[${PLUGIN_NAME9}] injectContext threw`, {
18288
17212
  error: err instanceof Error ? err.message : String(err)
18289
17213
  });
18290
17214
  }
18291
17215
  }
18292
17216
  cache2.record(query, result.recalled);
18293
- log8?.info(`[${PLUGIN_NAME12}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
18294
- return { kind: "injected", payload: result, mode: INJECTION_MODE2 };
17217
+ log7?.info(`[${PLUGIN_NAME9}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE });
17218
+ return { kind: "injected", payload: result, mode: INJECTION_MODE };
18295
17219
  }
18296
- async function handleDirective(dir, ctx, memCfg, log8) {
17220
+ async function handleDirective(dir, ctx, memCfg, log7) {
18297
17221
  if (dir.kind === "remember") {
18298
17222
  const r = await addMemory({ scope: dir.scope, content: dir.content, source: "directive" }, memCfg);
18299
17223
  if (r.ok) {
18300
17224
  const target = r.written_to === "kh" ? "KH" : "本地";
18301
17225
  await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
18302
- log8?.info(`[${PLUGIN_NAME12}] /remember ok`, {
17226
+ log7?.info(`[${PLUGIN_NAME9}] /remember ok`, {
18303
17227
  scope: dir.scope,
18304
17228
  id: r.id,
18305
- mode: INJECTION_MODE2
17229
+ mode: INJECTION_MODE
18306
17230
  });
18307
17231
  } else {
18308
17232
  await safeReply(ctx, `❌ 记忆失败:${r.error ?? "unknown"}`);
18309
- log8?.warn(`[${PLUGIN_NAME12}] /remember failed`, { error: r.error });
17233
+ log7?.warn(`[${PLUGIN_NAME9}] /remember failed`, { error: r.error });
18310
17234
  }
18311
- return { kind: "remembered", result: r, mode: INJECTION_MODE2 };
17235
+ return { kind: "remembered", result: r, mode: INJECTION_MODE };
18312
17236
  }
18313
17237
  if (dir.kind === "forget") {
18314
17238
  const r = await removeMemory({ scope: "project", id: dir.id }, memCfg);
@@ -18318,17 +17242,17 @@ async function handleDirective(dir, ctx, memCfg, log8) {
18318
17242
  const r2 = await removeMemory({ scope: "user", id: dir.id }, memCfg);
18319
17243
  if (r2.removed) {
18320
17244
  await safeReply(ctx, `\uD83D\uDDD1 已遗忘记忆 ${dir.id}(user scope)`);
18321
- return { kind: "forgotten", result: r2, mode: INJECTION_MODE2 };
17245
+ return { kind: "forgotten", result: r2, mode: INJECTION_MODE };
18322
17246
  }
18323
17247
  await safeReply(ctx, `⚠ 找不到记忆 ${dir.id}`);
18324
17248
  }
18325
- return { kind: "forgotten", result: r, mode: INJECTION_MODE2 };
17249
+ return { kind: "forgotten", result: r, mode: INJECTION_MODE };
18326
17250
  }
18327
17251
  const proj = await listMemories({ scope: "project" }, memCfg);
18328
17252
  const user = await listMemories({ scope: "user" }, memCfg);
18329
17253
  const total = proj.length + user.length;
18330
17254
  await safeReply(ctx, `\uD83D\uDCDA 共 ${total} 条记忆(project=${proj.length}, user=${user.length})`);
18331
- return { kind: "listed", total, mode: INJECTION_MODE2 };
17255
+ return { kind: "listed", total, mode: INJECTION_MODE };
18332
17256
  }
18333
17257
  async function safeReply(ctx, text) {
18334
17258
  if (typeof ctx.reply !== "function")
@@ -18337,58 +17261,58 @@ async function safeReply(ctx, text) {
18337
17261
  await ctx.reply(text);
18338
17262
  } catch {}
18339
17263
  }
18340
- logLifecycle(PLUGIN_NAME12, "import");
18341
- var sharedCache2 = new QueryCache2(DEFAULT_CONFIG5.cacheTtlMs);
17264
+ logLifecycle(PLUGIN_NAME9, "import");
17265
+ var sharedCache = new QueryCache(DEFAULT_CONFIG3.cacheTtlMs);
18342
17266
  function buildMemCfg(directory) {
18343
17267
  return { projectRoot: directory };
18344
17268
  }
18345
17269
  var memoriesContextServer = async (ctx) => {
18346
- const log8 = makePluginLogger(PLUGIN_NAME12);
17270
+ const log7 = makePluginLogger(PLUGIN_NAME9);
18347
17271
  const memCfg = buildMemCfg(ctx.directory);
18348
- logLifecycle(PLUGIN_NAME12, "activate", {
17272
+ logLifecycle(PLUGIN_NAME9, "activate", {
18349
17273
  directory: ctx.directory,
18350
17274
  projectRoot: memCfg.projectRoot,
18351
- mode: INJECTION_MODE2
17275
+ mode: INJECTION_MODE
18352
17276
  });
18353
17277
  return {
18354
17278
  "chat.message": async (input, output) => {
18355
- await safeAsync(PLUGIN_NAME12, "chat.message", async () => {
17279
+ await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
18356
17280
  const text = extractUserText(output);
18357
17281
  if (!text)
18358
17282
  return;
18359
- await handleMessage3({
17283
+ await handleMessage2({
18360
17284
  content: text,
18361
17285
  sessionId: input.sessionID,
18362
17286
  injectContext: async (markdown) => {
18363
- log8.info(`memories context candidate (${markdown.length} chars)`, {
17287
+ log7.info(`memories context candidate (${markdown.length} chars)`, {
18364
17288
  sessionID: input.sessionID,
18365
- mode: INJECTION_MODE2,
17289
+ mode: INJECTION_MODE,
18366
17290
  preview: markdown.slice(0, 200)
18367
17291
  });
18368
17292
  },
18369
17293
  reply: async (msg) => {
18370
- log8.info(`directive reply (observe-only): ${msg}`, {
17294
+ log7.info(`directive reply (observe-only): ${msg}`, {
18371
17295
  sessionID: input.sessionID,
18372
- mode: INJECTION_MODE2
17296
+ mode: INJECTION_MODE
18373
17297
  });
18374
17298
  },
18375
- log: log8
18376
- }, { cache: sharedCache2, memCfg });
17299
+ log: log7
17300
+ }, { cache: sharedCache, memCfg });
18377
17301
  });
18378
17302
  }
18379
17303
  };
18380
17304
  };
18381
- var handler12 = memoriesContextServer;
17305
+ var handler9 = memoriesContextServer;
18382
17306
 
18383
17307
  // plugins/model-fallback.ts
18384
- var PLUGIN_NAME13 = "model-fallback";
17308
+ var PLUGIN_NAME10 = "model-fallback";
18385
17309
  var state = {
18386
17310
  config: null,
18387
17311
  configPath: null,
18388
17312
  warnings: [],
18389
17313
  error: null
18390
17314
  };
18391
- logLifecycle(PLUGIN_NAME13, "import");
17315
+ logLifecycle(PLUGIN_NAME10, "import");
18392
17316
  function loadOnce(root) {
18393
17317
  if (state.config !== null || state.error !== null)
18394
17318
  return;
@@ -18398,11 +17322,11 @@ function loadOnce(root) {
18398
17322
  state.configPath = r.path ?? null;
18399
17323
  state.warnings = r.warnings;
18400
17324
  if (r.warnings.length > 0) {
18401
- safeWriteLog(PLUGIN_NAME13, { phase: "load.warnings", warnings: r.warnings });
17325
+ safeWriteLog(PLUGIN_NAME10, { phase: "load.warnings", warnings: r.warnings });
18402
17326
  }
18403
17327
  } else {
18404
17328
  state.error = r.error ?? "unknown_load_error";
18405
- safeWriteLog(PLUGIN_NAME13, { phase: "load.failed", error: state.error, path: r.path });
17329
+ safeWriteLog(PLUGIN_NAME10, { phase: "load.failed", error: state.error, path: r.path });
18406
17330
  }
18407
17331
  }
18408
17332
  var MODEL_ERR_PATTERNS = [
@@ -18447,9 +17371,9 @@ fallback 链已用尽:${meta.chain.join(" → ")}`;
18447
17371
  完整链:${meta.chain.join(" → ")}`;
18448
17372
  }
18449
17373
  var modelFallbackServer = async (ctx) => {
18450
- const log8 = makePluginLogger(PLUGIN_NAME13);
17374
+ const log7 = makePluginLogger(PLUGIN_NAME10);
18451
17375
  loadOnce(ctx.directory ?? process.cwd());
18452
- logLifecycle(PLUGIN_NAME13, "activate", {
17376
+ logLifecycle(PLUGIN_NAME10, "activate", {
18453
17377
  directory: ctx.directory,
18454
17378
  config_path: state.configPath,
18455
17379
  config_loaded: state.config !== null,
@@ -18459,7 +17383,7 @@ var modelFallbackServer = async (ctx) => {
18459
17383
  });
18460
17384
  return {
18461
17385
  "chat.params": async (input, output) => {
18462
- await safeAsync(PLUGIN_NAME13, "chat.params", async () => {
17386
+ await safeAsync(PLUGIN_NAME10, "chat.params", async () => {
18463
17387
  if (!state.config)
18464
17388
  return;
18465
17389
  const agent = input.agent;
@@ -18480,7 +17404,7 @@ var modelFallbackServer = async (ctx) => {
18480
17404
  next: meta.next_fallback,
18481
17405
  source: meta.source
18482
17406
  };
18483
- safeWriteLog(PLUGIN_NAME13, {
17407
+ safeWriteLog(PLUGIN_NAME10, {
18484
17408
  hook: "chat.params",
18485
17409
  agent,
18486
17410
  model: currentModel,
@@ -18490,7 +17414,7 @@ var modelFallbackServer = async (ctx) => {
18490
17414
  });
18491
17415
  },
18492
17416
  event: async ({ event }) => {
18493
- await safeAsync(PLUGIN_NAME13, "event", async () => {
17417
+ await safeAsync(PLUGIN_NAME10, "event", async () => {
18494
17418
  if (!state.config)
18495
17419
  return;
18496
17420
  const e = event;
@@ -18506,8 +17430,8 @@ var modelFallbackServer = async (ctx) => {
18506
17430
  const model = props.model ?? "unknown/unknown";
18507
17431
  const meta = buildFallbackMeta(state.config, agent, model);
18508
17432
  const suggestion = meta ? buildSuggestion(meta, String(message)) : `⚠️ ${agent}/${model} 失败:${message}`;
18509
- log8.warn(`[${PLUGIN_NAME13}] ${suggestion}`);
18510
- safeWriteLog(PLUGIN_NAME13, {
17433
+ log7.warn(`[${PLUGIN_NAME10}] ${suggestion}`);
17434
+ safeWriteLog(PLUGIN_NAME10, {
18511
17435
  hook: "event.error",
18512
17436
  eventType: e.type,
18513
17437
  agent,
@@ -18519,7 +17443,7 @@ var modelFallbackServer = async (ctx) => {
18519
17443
  }
18520
17444
  };
18521
17445
  };
18522
- var handler13 = modelFallbackServer;
17446
+ var handler10 = modelFallbackServer;
18523
17447
 
18524
17448
  // plugins/subtask-heartbeat.ts
18525
17449
  import { promises as fsPromises } from "node:fs";
@@ -18533,8 +17457,8 @@ var sweepExpiredSessionParents2 = sweepExpiredSessionParents;
18533
17457
  var _bulkInjectSessionParentMap2 = _bulkInjectSessionParentMap;
18534
17458
  var _capSessionParentMap2 = _capSessionParentMap;
18535
17459
  var _setPersistRootForTests2 = _setPersistRootForTests;
18536
- var PLUGIN_NAME14 = "subtask-heartbeat";
18537
- logLifecycle(PLUGIN_NAME14, "import", {});
17460
+ var PLUGIN_NAME11 = "subtask-heartbeat";
17461
+ logLifecycle(PLUGIN_NAME11, "import", {});
18538
17462
  var HEARTBEAT_INTERVAL_MS2 = 30000;
18539
17463
  var HEARTBEAT_DEBOUNCE_MS = 25000;
18540
17464
  var TOAST_DURATION_MS2 = 5000;
@@ -18544,11 +17468,11 @@ var PENDING_TASK_MAX_PARENTS = 64;
18544
17468
  var PENDING_TASK_MAX_PER_PARENT = 16;
18545
17469
  var DESCRIPTION_MAX_LEN = 60;
18546
17470
  var PARENT_PARSE_FAIL_MAX_LOG = 10;
18547
- var inflight3 = new Map;
17471
+ var inflight2 = new Map;
18548
17472
  var pendingTask = new Map;
18549
17473
  var _parentParseFailLogged = 0;
18550
17474
  function _snapshotInflight() {
18551
- return [...inflight3.values()].map((r) => ({ ...r }));
17475
+ return [...inflight2.values()].map((r) => ({ ...r }));
18552
17476
  }
18553
17477
  function detectUnparsedParentID(event) {
18554
17478
  if (!event || typeof event !== "object")
@@ -18684,11 +17608,11 @@ function registerInflight(payload, now = Date.now()) {
18684
17608
  lastTool: null,
18685
17609
  pendingCalls: new Map
18686
17610
  };
18687
- inflight3.set(payload.childID, r);
17611
+ inflight2.set(payload.childID, r);
18688
17612
  return r;
18689
17613
  }
18690
17614
  function recordToolBeat(sessionID, tool2, now = Date.now()) {
18691
- const r = inflight3.get(sessionID);
17615
+ const r = inflight2.get(sessionID);
18692
17616
  if (!r)
18693
17617
  return null;
18694
17618
  r.lastBeatAt = now;
@@ -18696,15 +17620,15 @@ function recordToolBeat(sessionID, tool2, now = Date.now()) {
18696
17620
  return r;
18697
17621
  }
18698
17622
  function clearInflight2(sessionID) {
18699
- const r = inflight3.get(sessionID);
17623
+ const r = inflight2.get(sessionID);
18700
17624
  if (!r)
18701
17625
  return null;
18702
- inflight3.delete(sessionID);
17626
+ inflight2.delete(sessionID);
18703
17627
  return r;
18704
17628
  }
18705
17629
  function pickHeartbeats(now = Date.now()) {
18706
17630
  const out = [];
18707
- for (const r of inflight3.values()) {
17631
+ for (const r of inflight2.values()) {
18708
17632
  if (now - r.lastBeatAt >= HEARTBEAT_DEBOUNCE_MS)
18709
17633
  out.push(r);
18710
17634
  }
@@ -18823,13 +17747,13 @@ function buildAfterLogLine(toolName, ok, durationMs, now = Date.now()) {
18823
17747
  duration_ms: durationMs
18824
17748
  });
18825
17749
  }
18826
- async function appendSubagentLog(filePath, line, log8) {
17750
+ async function appendSubagentLog(filePath, line, log7) {
18827
17751
  try {
18828
17752
  await fsPromises.mkdir(path20.dirname(filePath), { recursive: true });
18829
17753
  await fsPromises.appendFile(filePath, line + `
18830
17754
  `, "utf8");
18831
17755
  } catch (err) {
18832
- log8?.debug?.("appendSubagentLog 失败(已隔离)", {
17756
+ log7?.debug?.("appendSubagentLog 失败(已隔离)", {
18833
17757
  error: err instanceof Error ? err.message : String(err),
18834
17758
  file: filePath
18835
17759
  });
@@ -18854,9 +17778,9 @@ function normalizeVariant2(raw) {
18854
17778
  return "default";
18855
17779
  return raw;
18856
17780
  }
18857
- async function showToast2(client, payload, log8) {
17781
+ async function showToast2(client, payload, log7) {
18858
17782
  if (typeof client?.tui?.showToast !== "function") {
18859
- log8?.debug?.("tui.showToast 不可用,noop");
17783
+ log7?.debug?.("tui.showToast 不可用,noop");
18860
17784
  return false;
18861
17785
  }
18862
17786
  try {
@@ -18870,15 +17794,15 @@ async function showToast2(client, payload, log8) {
18870
17794
  });
18871
17795
  return true;
18872
17796
  } catch (err) {
18873
- log8?.warn("tui.showToast 抛错(已隔离)", {
17797
+ log7?.warn("tui.showToast 抛错(已隔离)", {
18874
17798
  error: err instanceof Error ? err.message : String(err)
18875
17799
  });
18876
17800
  return false;
18877
17801
  }
18878
17802
  }
18879
- var log8 = makePluginLogger(PLUGIN_NAME14);
17803
+ var log7 = makePluginLogger(PLUGIN_NAME11);
18880
17804
  var subtaskHeartbeatServer = async (ctx) => {
18881
- logLifecycle(PLUGIN_NAME14, "activate", {
17805
+ logLifecycle(PLUGIN_NAME11, "activate", {
18882
17806
  directory: ctx.directory,
18883
17807
  intervalMs: HEARTBEAT_INTERVAL_MS2
18884
17808
  });
@@ -18895,7 +17819,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18895
17819
  }));
18896
17820
  _bulkInjectSessionParentMap2(entries);
18897
17821
  const cappedOut = _capSessionParentMap2();
18898
- safeWriteLog(PLUGIN_NAME14, {
17822
+ safeWriteLog(PLUGIN_NAME11, {
18899
17823
  hook: "activate",
18900
17824
  type: "parent-map.restore",
18901
17825
  restored: restored.size,
@@ -18903,27 +17827,27 @@ var subtaskHeartbeatServer = async (ctx) => {
18903
17827
  });
18904
17828
  }
18905
17829
  } catch (err) {
18906
- log8.warn("loadParentMap 失败(已隔离),降级为空表", {
17830
+ log7.warn("loadParentMap 失败(已隔离),降级为空表", {
18907
17831
  error: err instanceof Error ? err.message : String(err)
18908
17832
  });
18909
17833
  }
18910
17834
  const interval = setInterval(() => {
18911
- safeAsync(PLUGIN_NAME14, "interval", async () => {
17835
+ safeAsync(PLUGIN_NAME11, "interval", async () => {
18912
17836
  const swept = sweepExpiredPendingTasks();
18913
17837
  if (swept > 0) {
18914
- safeWriteLog(PLUGIN_NAME14, { hook: "interval", pending_task_swept: swept });
17838
+ safeWriteLog(PLUGIN_NAME11, { hook: "interval", pending_task_swept: swept });
18915
17839
  }
18916
17840
  const sweptParents = sweepExpiredSessionParents2();
18917
17841
  if (sweptParents > 0) {
18918
- safeWriteLog(PLUGIN_NAME14, { hook: "interval", session_parent_swept: sweptParents });
17842
+ safeWriteLog(PLUGIN_NAME11, { hook: "interval", session_parent_swept: sweptParents });
18919
17843
  }
18920
17844
  const beats = pickHeartbeats();
18921
17845
  if (beats.length === 0)
18922
17846
  return;
18923
17847
  for (const r of beats) {
18924
17848
  const t = buildHeartbeatToast(r);
18925
- const sent = await showToast2(client, t, log8);
18926
- safeWriteLog(PLUGIN_NAME14, {
17849
+ const sent = await showToast2(client, t, log7);
17850
+ safeWriteLog(PLUGIN_NAME11, {
18927
17851
  hook: "interval",
18928
17852
  child: r.childID,
18929
17853
  parent: r.parentID,
@@ -18940,15 +17864,15 @@ var subtaskHeartbeatServer = async (ctx) => {
18940
17864
  }
18941
17865
  return {
18942
17866
  event: async ({ event }) => {
18943
- await safeAsync(PLUGIN_NAME14, "event", async () => {
17867
+ await safeAsync(PLUGIN_NAME11, "event", async () => {
18944
17868
  try {
18945
17869
  if (detectUnparsedParentID(event) && _parentParseFailLogged < PARENT_PARSE_FAIL_MAX_LOG) {
18946
17870
  _parentParseFailLogged++;
18947
- log8.warn("session.created 含 parentID 关键字但 extractCreatedChild 解析失败,可能 SDK schema 变更", {
17871
+ log7.warn("session.created 含 parentID 关键字但 extractCreatedChild 解析失败,可能 SDK schema 变更", {
18948
17872
  sample_count: _parentParseFailLogged,
18949
17873
  hint: "如频繁出现请检查 plugins/subtask-heartbeat.ts::extractCreatedChild 是否适配新 SDK"
18950
17874
  });
18951
- safeWriteLog(PLUGIN_NAME14, {
17875
+ safeWriteLog(PLUGIN_NAME11, {
18952
17876
  hook: "event",
18953
17877
  type: "session.created.unparsed-parent-id",
18954
17878
  sample_count: _parentParseFailLogged
@@ -18960,7 +17884,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18960
17884
  try {
18961
17885
  recordSessionParent2(created.childID, created.parentID);
18962
17886
  } catch (err) {
18963
- log8.warn("recordSessionParent 抛错(已隔离)", {
17887
+ log7.warn("recordSessionParent 抛错(已隔离)", {
18964
17888
  error: err instanceof Error ? err.message : String(err)
18965
17889
  });
18966
17890
  }
@@ -18971,7 +17895,7 @@ var subtaskHeartbeatServer = async (ctx) => {
18971
17895
  agent: pending?.agent ?? created.agent,
18972
17896
  description: pending?.description ?? null
18973
17897
  });
18974
- safeWriteLog(PLUGIN_NAME14, {
17898
+ safeWriteLog(PLUGIN_NAME11, {
18975
17899
  hook: "event",
18976
17900
  type: "session.created",
18977
17901
  child: created.childID,
@@ -18981,8 +17905,8 @@ var subtaskHeartbeatServer = async (ctx) => {
18981
17905
  description_len: record.description?.length ?? 0
18982
17906
  });
18983
17907
  const startToast = buildStartToast(record);
18984
- const sent = await showToast2(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log8);
18985
- safeWriteLog(PLUGIN_NAME14, {
17908
+ const sent = await showToast2(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log7);
17909
+ safeWriteLog(PLUGIN_NAME11, {
18986
17910
  hook: "event",
18987
17911
  type: "session.created.toast",
18988
17912
  child: created.childID,
@@ -19001,8 +17925,8 @@ var subtaskHeartbeatServer = async (ctx) => {
19001
17925
  const r = clearInflight2(ended.sessionID);
19002
17926
  if (r) {
19003
17927
  const t = buildEndToast(r, ended.type);
19004
- const sent = await showToast2(client, t, log8);
19005
- safeWriteLog(PLUGIN_NAME14, {
17928
+ const sent = await showToast2(client, t, log7);
17929
+ safeWriteLog(PLUGIN_NAME11, {
19006
17930
  hook: "event",
19007
17931
  type: ended.type,
19008
17932
  child: r.childID,
@@ -19020,14 +17944,14 @@ var subtaskHeartbeatServer = async (ctx) => {
19020
17944
  },
19021
17945
  "tool.execute.before": async (input, output) => {
19022
17946
  const isTaskTool = input?.tool === "task";
19023
- if (inflight3.size === 0 && !isTaskTool)
17947
+ if (inflight2.size === 0 && !isTaskTool)
19024
17948
  return;
19025
- await safeAsync(PLUGIN_NAME14, "tool.execute.before", async () => {
17949
+ await safeAsync(PLUGIN_NAME11, "tool.execute.before", async () => {
19026
17950
  if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
19027
17951
  return;
19028
17952
  if (isTaskTool) {
19029
17953
  const args = output?.args ?? null;
19030
- safeWriteLog(PLUGIN_NAME14, {
17954
+ safeWriteLog(PLUGIN_NAME11, {
19031
17955
  hook: "tool.execute.before.task",
19032
17956
  sessionID: input.sessionID,
19033
17957
  args_keys: args && typeof args === "object" ? Object.keys(args) : null,
@@ -19039,7 +17963,7 @@ var subtaskHeartbeatServer = async (ctx) => {
19039
17963
  agent: extracted.subagentType,
19040
17964
  description: extracted.description
19041
17965
  });
19042
- safeWriteLog(PLUGIN_NAME14, {
17966
+ safeWriteLog(PLUGIN_NAME11, {
19043
17967
  hook: "tool.execute.before.task.enqueued",
19044
17968
  parent: input.sessionID,
19045
17969
  agent: extracted.subagentType,
@@ -19047,7 +17971,7 @@ var subtaskHeartbeatServer = async (ctx) => {
19047
17971
  });
19048
17972
  }
19049
17973
  }
19050
- const rec = inflight3.get(input.sessionID);
17974
+ const rec = inflight2.get(input.sessionID);
19051
17975
  if (rec) {
19052
17976
  recordToolBeat(input.sessionID, input.tool);
19053
17977
  if (!rec.pendingCalls)
@@ -19059,18 +17983,18 @@ var subtaskHeartbeatServer = async (ctx) => {
19059
17983
  const args = output?.args ?? null;
19060
17984
  const line = buildBeforeLogLine(input.tool, args);
19061
17985
  const file = subagentLogPath(cwd, rec.parentID, rec.childID);
19062
- appendSubagentLog(file, line, log8);
17986
+ appendSubagentLog(file, line, log7);
19063
17987
  }
19064
17988
  }
19065
17989
  });
19066
17990
  },
19067
17991
  "tool.execute.after": async (input, _output) => {
19068
- if (inflight3.size === 0)
17992
+ if (inflight2.size === 0)
19069
17993
  return;
19070
- await safeAsync(PLUGIN_NAME14, "tool.execute.after", async () => {
17994
+ await safeAsync(PLUGIN_NAME11, "tool.execute.after", async () => {
19071
17995
  if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
19072
17996
  return;
19073
- const rec = inflight3.get(input.sessionID);
17997
+ const rec = inflight2.get(input.sessionID);
19074
17998
  if (!rec)
19075
17999
  return;
19076
18000
  let durationMs = 0;
@@ -19084,17 +18008,17 @@ var subtaskHeartbeatServer = async (ctx) => {
19084
18008
  if (isLogPersistenceEnabled(cwd)) {
19085
18009
  const line = buildAfterLogLine(input.tool, true, durationMs);
19086
18010
  const file = subagentLogPath(cwd, rec.parentID, rec.childID);
19087
- appendSubagentLog(file, line, log8);
18011
+ appendSubagentLog(file, line, log7);
19088
18012
  }
19089
18013
  });
19090
18014
  }
19091
18015
  };
19092
18016
  };
19093
- var handler14 = subtaskHeartbeatServer;
18017
+ var handler11 = subtaskHeartbeatServer;
19094
18018
 
19095
18019
  // plugins/parallel-status.ts
19096
- var PLUGIN_NAME15 = "parallel-status";
19097
- logLifecycle(PLUGIN_NAME15, "import");
18020
+ var PLUGIN_NAME12 = "parallel-status";
18021
+ logLifecycle(PLUGIN_NAME12, "import");
19098
18022
  var ID_MAX_LEN = 16;
19099
18023
  var ID_KEEP_LEN = 13;
19100
18024
  function shortId(s) {
@@ -19126,8 +18050,8 @@ function formatInflightMarkdown(snapshot, now = Date.now()) {
19126
18050
  `);
19127
18051
  }
19128
18052
  var parallelStatusServer = async (ctx) => {
19129
- const log9 = makePluginLogger(PLUGIN_NAME15);
19130
- logLifecycle(PLUGIN_NAME15, "activate", { directory: ctx.directory });
18053
+ const log8 = makePluginLogger(PLUGIN_NAME12);
18054
+ logLifecycle(PLUGIN_NAME12, "activate", { directory: ctx.directory });
19131
18055
  return {
19132
18056
  "command.execute.before": async (input, output) => {
19133
18057
  try {
@@ -19146,23 +18070,23 @@ var parallelStatusServer = async (ctx) => {
19146
18070
  synthetic: false
19147
18071
  });
19148
18072
  }
19149
- log9.info(`[${PLUGIN_NAME15}] 已回写 ${snapshot.length} 条 inflight`);
18073
+ log8.info(`[${PLUGIN_NAME12}] 已回写 ${snapshot.length} 条 inflight`);
19150
18074
  } catch (err) {
19151
- log9.error(`[${PLUGIN_NAME15}] command.execute.before 异常(已隔离)`, {
18075
+ log8.error(`[${PLUGIN_NAME12}] command.execute.before 异常(已隔离)`, {
19152
18076
  error: err instanceof Error ? err.message : String(err)
19153
18077
  });
19154
18078
  }
19155
18079
  }
19156
18080
  };
19157
18081
  };
19158
- var handler15 = parallelStatusServer;
18082
+ var handler12 = parallelStatusServer;
19159
18083
 
19160
18084
  // plugins/parallel-tool-nudge.ts
19161
18085
  import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
19162
18086
  import { join as join17 } from "node:path";
19163
18087
  import { homedir as homedir6 } from "node:os";
19164
- var PLUGIN_NAME16 = "parallel-tool-nudge";
19165
- logLifecycle(PLUGIN_NAME16, "import", {});
18088
+ var PLUGIN_NAME13 = "parallel-tool-nudge";
18089
+ logLifecycle(PLUGIN_NAME13, "import", {});
19166
18090
  var PARALLEL_SAFE_TOOLS = [
19167
18091
  "read",
19168
18092
  "smart_search",
@@ -19224,7 +18148,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
19224
18148
  const result = new Map;
19225
18149
  const safeSet = new Set(PARALLEL_SAFE_TOOLS);
19226
18150
  const unionTools = new Set;
19227
- const log9 = makePluginLogger(PLUGIN_NAME16);
18151
+ const log8 = makePluginLogger(PLUGIN_NAME13);
19228
18152
  for (const dir of candidateDirs) {
19229
18153
  if (!dirExists(dir))
19230
18154
  continue;
@@ -19232,7 +18156,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
19232
18156
  try {
19233
18157
  entries = dirReader(dir);
19234
18158
  } catch (err) {
19235
- log9.warn(`agents 目录读取失败(已跳过)`, {
18159
+ log8.warn(`agents 目录读取失败(已跳过)`, {
19236
18160
  dir,
19237
18161
  error: err instanceof Error ? err.message : String(err)
19238
18162
  });
@@ -19246,7 +18170,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
19246
18170
  try {
19247
18171
  content = reader(path21);
19248
18172
  } catch (err) {
19249
- log9.warn(`agent.md 读取失败(已跳过)`, {
18173
+ log8.warn(`agent.md 读取失败(已跳过)`, {
19250
18174
  path: path21,
19251
18175
  error: err instanceof Error ? err.message : String(err)
19252
18176
  });
@@ -19254,7 +18178,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
19254
18178
  }
19255
18179
  const parsed = parseAgentFrontmatter(content);
19256
18180
  if (!parsed) {
19257
- log9.warn(`agent frontmatter 解析失败(已跳过)`, { path: path21 });
18181
+ log8.warn(`agent frontmatter 解析失败(已跳过)`, { path: path21 });
19258
18182
  continue;
19259
18183
  }
19260
18184
  if (result.has(parsed.name))
@@ -19332,7 +18256,7 @@ This directive is re-injected every turn — it is not optional advice.
19332
18256
  return body.slice(0, NUDGE_MAX_LEN2 - 4) + `
19333
18257
  …`;
19334
18258
  }
19335
- var log9 = makePluginLogger(PLUGIN_NAME16);
18259
+ var log8 = makePluginLogger(PLUGIN_NAME13);
19336
18260
  var parallelToolNudgeServer = async (ctx) => {
19337
18261
  try {
19338
18262
  const loaded = loadAgentToolsMap(ctx.directory ?? process.cwd());
@@ -19340,19 +18264,19 @@ var parallelToolNudgeServer = async (ctx) => {
19340
18264
  for (const [k, v] of loaded.entries())
19341
18265
  agentToolsMap.set(k, v);
19342
18266
  } catch (err) {
19343
- log9.warn(`loadAgentToolsMap 失败(plugin 将 no-op)`, {
18267
+ log8.warn(`loadAgentToolsMap 失败(plugin 将 no-op)`, {
19344
18268
  error: err instanceof Error ? err.message : String(err)
19345
18269
  });
19346
18270
  }
19347
18271
  const realAgentCount = Math.max(0, agentToolsMap.size - (agentToolsMap.has(DEFAULT_AGENT_KEY) ? 1 : 0));
19348
18272
  if (realAgentCount === 0) {
19349
18273
  agentToolsMap.clear();
19350
- log9.warn(`0 real agents loaded; plugin will be no-op for this session`, {
18274
+ log8.warn(`0 real agents loaded; plugin will be no-op for this session`, {
19351
18275
  directory: ctx.directory
19352
18276
  });
19353
18277
  }
19354
18278
  const defaultTools = agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
19355
- logLifecycle(PLUGIN_NAME16, "activate", {
18279
+ logLifecycle(PLUGIN_NAME13, "activate", {
19356
18280
  directory: ctx.directory,
19357
18281
  real_agents_loaded: realAgentCount,
19358
18282
  default_tools_union: defaultTools,
@@ -19363,7 +18287,7 @@ var parallelToolNudgeServer = async (ctx) => {
19363
18287
  });
19364
18288
  return {
19365
18289
  "chat.params": async (input, _output) => {
19366
- await safeAsync(PLUGIN_NAME16, "chat.params", async () => {
18290
+ await safeAsync(PLUGIN_NAME13, "chat.params", async () => {
19367
18291
  if (agentToolsMap.size === 0)
19368
18292
  return;
19369
18293
  const sid = input?.sessionID;
@@ -19375,7 +18299,7 @@ var parallelToolNudgeServer = async (ctx) => {
19375
18299
  });
19376
18300
  },
19377
18301
  "experimental.chat.system.transform": async (input, output) => {
19378
- await safeAsync(PLUGIN_NAME16, "experimental.chat.system.transform", async () => {
18302
+ await safeAsync(PLUGIN_NAME13, "experimental.chat.system.transform", async () => {
19379
18303
  if (agentToolsMap.size === 0)
19380
18304
  return;
19381
18305
  if (!output || !Array.isArray(output.system))
@@ -19385,7 +18309,7 @@ var parallelToolNudgeServer = async (ctx) => {
19385
18309
  const tools = agent !== "unknown" ? agentToolsMap.get(agent) ?? agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [] : agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
19386
18310
  const nudge = renderNudge(agent, tools);
19387
18311
  output.system.push(nudge);
19388
- safeWriteLog(PLUGIN_NAME16, {
18312
+ safeWriteLog(PLUGIN_NAME13, {
19389
18313
  hook: "experimental.chat.system.transform",
19390
18314
  sessionID: sid,
19391
18315
  agent,
@@ -19397,11 +18321,11 @@ var parallelToolNudgeServer = async (ctx) => {
19397
18321
  }
19398
18322
  };
19399
18323
  };
19400
- var handler16 = parallelToolNudgeServer;
18324
+ var handler13 = parallelToolNudgeServer;
19401
18325
 
19402
18326
  // plugins/pwsh-utf8.ts
19403
- var PLUGIN_NAME17 = "pwsh-utf8";
19404
- logLifecycle(PLUGIN_NAME17, "import", {});
18327
+ var PLUGIN_NAME14 = "pwsh-utf8";
18328
+ logLifecycle(PLUGIN_NAME14, "import", {});
19405
18329
  var PRELUDE = "chcp 65001 *> $null; " + "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); " + "$OutputEncoding = [System.Text.UTF8Encoding]::new(); ";
19406
18330
  function prependUtf8Prelude(command) {
19407
18331
  if (typeof command !== "string")
@@ -19414,15 +18338,15 @@ function prependUtf8Prelude(command) {
19414
18338
  return command;
19415
18339
  return PRELUDE + command;
19416
18340
  }
19417
- var handler17 = async (_ctx3) => {
18341
+ var handler14 = async (_ctx3) => {
19418
18342
  const enabled = process.platform === "win32" && process.env.CODEFORGE_DISABLE_PWSH_UTF8 !== "1";
19419
18343
  const reason = enabled ? "win32" : process.platform !== "win32" ? "non-win32" : "disabled-by-env";
19420
- logLifecycle(PLUGIN_NAME17, "activate", { enabled, platform: process.platform, reason });
18344
+ logLifecycle(PLUGIN_NAME14, "activate", { enabled, platform: process.platform, reason });
19421
18345
  if (!enabled)
19422
18346
  return {};
19423
18347
  return {
19424
18348
  "tool.execute.before": async (input, output) => {
19425
- await safeAsync(PLUGIN_NAME17, "tool.execute.before", async () => {
18349
+ await safeAsync(PLUGIN_NAME14, "tool.execute.before", async () => {
19426
18350
  if (input.tool !== "bash")
19427
18351
  return;
19428
18352
  const args = output.args ?? {};
@@ -19431,7 +18355,7 @@ var handler17 = async (_ctx3) => {
19431
18355
  if (next !== undefined && next !== original) {
19432
18356
  args["command"] = next;
19433
18357
  output.args = args;
19434
- safeWriteLog(PLUGIN_NAME17, {
18358
+ safeWriteLog(PLUGIN_NAME14, {
19435
18359
  hook: "tool.execute.before",
19436
18360
  tool: input.tool,
19437
18361
  callID: input.callID,
@@ -19587,7 +18511,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
19587
18511
  for (let i = events.length - 1;i >= 0; i--) {
19588
18512
  const e = events[i];
19589
18513
  if (e.type === "message" && e.role === "user") {
19590
- lastUser = clip5(e.content, o.excerptLimit);
18514
+ lastUser = clip4(e.content, o.excerptLimit);
19591
18515
  break;
19592
18516
  }
19593
18517
  }
@@ -19605,7 +18529,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
19605
18529
  const toolMap = new Map;
19606
18530
  for (const t of window) {
19607
18531
  const cur = toolMap.get(t.tool);
19608
- const argsExcerpt = clip5(safeJson(t.args), o.excerptLimit);
18532
+ const argsExcerpt = clip4(safeJson(t.args), o.excerptLimit);
19609
18533
  if (cur) {
19610
18534
  cur.count++;
19611
18535
  cur.last_ok = t.ok;
@@ -19621,7 +18545,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
19621
18545
  });
19622
18546
  }
19623
18547
  }
19624
- const inflight4 = [...toolMap.values()].sort((a, b) => b.last_ts - a.last_ts);
18548
+ const inflight3 = [...toolMap.values()].sort((a, b) => b.last_ts - a.last_ts);
19625
18549
  const proposed = window.some((t) => PENDING_CHANGES_TOOLS.has(t.tool));
19626
18550
  const applied = window.some((t) => APPLY_TOOLS.has(t.tool) && t.ok);
19627
18551
  const pending_changes_likely = proposed && !applied;
@@ -19645,7 +18569,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
19645
18569
  idleMs,
19646
18570
  lastUser,
19647
18571
  lastAgent,
19648
- inflight: inflight4,
18572
+ inflight: inflight3,
19649
18573
  pending_changes_likely,
19650
18574
  open_subtasks_likely,
19651
18575
  reason
@@ -19656,7 +18580,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
19656
18580
  idle_ms: idleMs,
19657
18581
  last_user_intent: lastUser,
19658
18582
  last_agent: lastAgent,
19659
- inflight_tools: inflight4,
18583
+ inflight_tools: inflight3,
19660
18584
  pending_changes_likely,
19661
18585
  open_subtasks_likely,
19662
18586
  summary
@@ -19694,7 +18618,7 @@ function formatIdle(ms) {
19694
18618
  return `${Math.round(ms / 3600000)}h`;
19695
18619
  return `${Math.round(ms / 86400000)}d`;
19696
18620
  }
19697
- function clip5(s, max) {
18621
+ function clip4(s, max) {
19698
18622
  if (!s)
19699
18623
  return "";
19700
18624
  if (s.length <= max)
@@ -19847,8 +18771,8 @@ async function markBlocksConsumed(absRoot, entries) {
19847
18771
  }
19848
18772
 
19849
18773
  // plugins/session-recovery.ts
19850
- var PLUGIN_NAME18 = "session-recovery";
19851
- logLifecycle(PLUGIN_NAME18, "import", {});
18774
+ var PLUGIN_NAME15 = "session-recovery";
18775
+ logLifecycle(PLUGIN_NAME15, "import", {});
19852
18776
  async function processSessionStart(currentSessionId, opts = {}) {
19853
18777
  if (opts.disabled) {
19854
18778
  return { ok: true, injected: false, reason: "disabled" };
@@ -19858,7 +18782,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
19858
18782
  excludeIds.add(currentSessionId);
19859
18783
  const r = await scanLastSession({ ...opts, excludeIds: [...excludeIds] });
19860
18784
  if (!r.ok) {
19861
- opts.log?.warn?.(`[${PLUGIN_NAME18}] 扫描失败:${r.error}`);
18785
+ opts.log?.warn?.(`[${PLUGIN_NAME15}] 扫描失败:${r.error}`);
19862
18786
  return { ok: false, injected: false, reason: "scan_error", error: r.error };
19863
18787
  }
19864
18788
  const plan = r.plan;
@@ -19867,7 +18791,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
19867
18791
  try {
19868
18792
  pendingBlocks = await scanBlockPending(opts.root);
19869
18793
  } catch (err) {
19870
- opts.log?.warn?.(`[${PLUGIN_NAME18}] scanBlockPending 异常:${err instanceof Error ? err.message : String(err)}`);
18794
+ opts.log?.warn?.(`[${PLUGIN_NAME15}] scanBlockPending 异常:${err instanceof Error ? err.message : String(err)}`);
19871
18795
  }
19872
18796
  }
19873
18797
  const hasPendingBlocks = pendingBlocks.length > 0;
@@ -19885,7 +18809,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
19885
18809
  await opts.injectRecovery(injection);
19886
18810
  } catch (err) {
19887
18811
  const msg = err instanceof Error ? err.message : String(err);
19888
- opts.log?.warn?.(`[${PLUGIN_NAME18}] injectRecovery 异常:${msg}`);
18812
+ opts.log?.warn?.(`[${PLUGIN_NAME15}] injectRecovery 异常:${msg}`);
19889
18813
  return { ok: false, injected: false, plan, reason: "inject_error", error: msg, pendingBlocks };
19890
18814
  }
19891
18815
  }
@@ -19893,7 +18817,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
19893
18817
  try {
19894
18818
  await markBlocksConsumed(opts.root, pendingBlocks);
19895
18819
  } catch (err) {
19896
- opts.log?.warn?.(`[${PLUGIN_NAME18}] markBlocksConsumed 异常:${err instanceof Error ? err.message : String(err)}`);
18820
+ opts.log?.warn?.(`[${PLUGIN_NAME15}] markBlocksConsumed 异常:${err instanceof Error ? err.message : String(err)}`);
19897
18821
  }
19898
18822
  }
19899
18823
  return { ok: true, injected: true, plan, reason: "ok", pendingBlocks };
@@ -19940,13 +18864,13 @@ function renderPrompt(plan) {
19940
18864
  return lines.join(`
19941
18865
  `);
19942
18866
  }
19943
- var log10 = makePluginLogger(PLUGIN_NAME18);
18867
+ var log9 = makePluginLogger(PLUGIN_NAME15);
19944
18868
  var _lastInjection = null;
19945
18869
  var sessionRecoveryServer = async (ctx) => {
19946
- logLifecycle(PLUGIN_NAME18, "activate", { directory: ctx.directory });
18870
+ logLifecycle(PLUGIN_NAME15, "activate", { directory: ctx.directory });
19947
18871
  return {
19948
18872
  event: async ({ event }) => {
19949
- await safeAsync(PLUGIN_NAME18, "event", async () => {
18873
+ await safeAsync(PLUGIN_NAME15, "event", async () => {
19950
18874
  const e = event;
19951
18875
  if (!e || typeof e.type !== "string")
19952
18876
  return;
@@ -19956,12 +18880,12 @@ var sessionRecoveryServer = async (ctx) => {
19956
18880
  const root = typeof e.properties?.["root"] === "string" ? e.properties["root"] : ctx.directory ?? process.cwd();
19957
18881
  const r = await processSessionStart(sid, {
19958
18882
  root,
19959
- log: log10,
18883
+ log: log9,
19960
18884
  injectRecovery: (inj) => {
19961
18885
  _lastInjection = inj;
19962
18886
  }
19963
18887
  });
19964
- safeWriteLog(PLUGIN_NAME18, {
18888
+ safeWriteLog(PLUGIN_NAME15, {
19965
18889
  hook: "event",
19966
18890
  type: "session.start",
19967
18891
  ok: r.ok,
@@ -19971,13 +18895,13 @@ var sessionRecoveryServer = async (ctx) => {
19971
18895
  pending_blocks_count: r.pendingBlocks?.length ?? 0
19972
18896
  });
19973
18897
  if (r.injected && r.plan) {
19974
- log10.info(`[${PLUGIN_NAME18}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason}, pending_blocks=${r.pendingBlocks?.length ?? 0})`);
18898
+ log9.info(`[${PLUGIN_NAME15}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason}, pending_blocks=${r.pendingBlocks?.length ?? 0})`);
19975
18899
  }
19976
18900
  });
19977
18901
  }
19978
18902
  };
19979
18903
  };
19980
- var handler18 = sessionRecoveryServer;
18904
+ var handler15 = sessionRecoveryServer;
19981
18905
 
19982
18906
  // plugins/subtasks.ts
19983
18907
  import { promises as fs18 } from "node:fs";
@@ -19995,7 +18919,7 @@ function resolveMergeFns(deps) {
19995
18919
  worktreeHasChangesFn: deps.worktreeHasChanges ?? worktreeHasChanges
19996
18920
  };
19997
18921
  }
19998
- async function mergeOneAttempt(r, opts, mergeFns, log11) {
18922
+ async function mergeOneAttempt(r, opts, mergeFns, log10) {
19999
18923
  const t0 = Date.now();
20000
18924
  const root = opts.mergeRoot;
20001
18925
  const {
@@ -20077,7 +19001,7 @@ async function mergeOneAttempt(r, opts, mergeFns, log11) {
20077
19001
  await mergeAbortFn({ root });
20078
19002
  } catch (abortErr) {
20079
19003
  abortFailed = true;
20080
- log11("error", `[parallel-merge] mergeAbort 失败(仓库可能锁死)`, {
19004
+ log10("error", `[parallel-merge] mergeAbort 失败(仓库可能锁死)`, {
20081
19005
  id: r.id,
20082
19006
  error: describe6(abortErr)
20083
19007
  });
@@ -20093,13 +19017,13 @@ async function mergeOneAttempt(r, opts, mergeFns, log11) {
20093
19017
  }
20094
19018
  attempt.ok = true;
20095
19019
  attempt.durationMs = Date.now() - t0;
20096
- await safeRemoveWorktree(removeWorktreeFn, root, wt, log11, r.id);
19020
+ await safeRemoveWorktree(removeWorktreeFn, root, wt, log10, r.id);
20097
19021
  return { attempt };
20098
19022
  } else {
20099
19023
  attempt.ok = true;
20100
19024
  attempt.skippedReason = "no_changes";
20101
19025
  attempt.durationMs = Date.now() - t0;
20102
- await safeRemoveWorktree(removeWorktreeFn, root, wt, log11, r.id);
19026
+ await safeRemoveWorktree(removeWorktreeFn, root, wt, log10, r.id);
20103
19027
  return { attempt };
20104
19028
  }
20105
19029
  } catch (err) {
@@ -20113,7 +19037,7 @@ async function mergeOneAttempt(r, opts, mergeFns, log11) {
20113
19037
  }
20114
19038
  }
20115
19039
  async function mergeWorktrees(results, opts, deps) {
20116
- const log11 = deps.log ?? (() => {});
19040
+ const log10 = deps.log ?? (() => {});
20117
19041
  const root = opts.mergeRoot;
20118
19042
  const mergeFns = resolveMergeFns(deps);
20119
19043
  const report = {
@@ -20155,10 +19079,10 @@ async function mergeWorktrees(results, opts, deps) {
20155
19079
  };
20156
19080
  report.attempts.push(attempt);
20157
19081
  report.skipped++;
20158
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
19082
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
20159
19083
  }
20160
19084
  for (const r of mergeable) {
20161
- const { attempt, pendingItem } = await mergeOneAttempt(r, { mergeRoot: root }, mergeFns, log11);
19085
+ const { attempt, pendingItem } = await mergeOneAttempt(r, { mergeRoot: root }, mergeFns, log10);
20162
19086
  report.attempts.push(attempt);
20163
19087
  if (attempt.ok && !attempt.skippedReason)
20164
19088
  report.merged++;
@@ -20168,12 +19092,12 @@ async function mergeWorktrees(results, opts, deps) {
20168
19092
  report.conflicted++;
20169
19093
  if (pendingItem)
20170
19094
  report.pendingWorktrees.push(pendingItem);
20171
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
19095
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
20172
19096
  }
20173
19097
  return report;
20174
19098
  }
20175
19099
  function createMergeQueue(opts, deps = {}) {
20176
- const log11 = deps.log ?? (() => {});
19100
+ const log10 = deps.log ?? (() => {});
20177
19101
  const mergeFns = {
20178
19102
  commitWorktreeIfDirtyFn: deps.commitWorktreeIfDirtyFn ?? commitWorktreeIfDirty,
20179
19103
  tryMergeFn: deps.tryMergeFn ?? tryMerge,
@@ -20214,10 +19138,10 @@ function createMergeQueue(opts, deps = {}) {
20214
19138
  conflicts: []
20215
19139
  });
20216
19140
  }
20217
- await fireMergeAttempt(opts.onMergeAttempt, attempt2, idx++, log11);
19141
+ await fireMergeAttempt(opts.onMergeAttempt, attempt2, idx++, log10);
20218
19142
  return;
20219
19143
  }
20220
- const { attempt, abortFailed, pendingItem } = await mergeOneAttempt(result, { mergeRoot: opts.mergeRoot, summaryCharLimit: opts.summaryCharLimit }, mergeFns, log11);
19144
+ const { attempt, abortFailed, pendingItem } = await mergeOneAttempt(result, { mergeRoot: opts.mergeRoot, summaryCharLimit: opts.summaryCharLimit }, mergeFns, log10);
20221
19145
  report.attempts.push(attempt);
20222
19146
  if (attempt.ok && !attempt.skippedReason)
20223
19147
  report.merged++;
@@ -20229,11 +19153,11 @@ function createMergeQueue(opts, deps = {}) {
20229
19153
  report.pendingWorktrees.push(pendingItem);
20230
19154
  if (abortFailed) {
20231
19155
  queueAborted = true;
20232
- log11("error", `[parallel-merge] queue aborted (mergeAbort failed)`, {
19156
+ log10("error", `[parallel-merge] queue aborted (mergeAbort failed)`, {
20233
19157
  id: result.id
20234
19158
  });
20235
19159
  }
20236
- await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log11);
19160
+ await fireMergeAttempt(opts.onMergeAttempt, attempt, idx++, log10);
20237
19161
  });
20238
19162
  },
20239
19163
  async flushAndReport() {
@@ -20242,23 +19166,23 @@ function createMergeQueue(opts, deps = {}) {
20242
19166
  }
20243
19167
  };
20244
19168
  }
20245
- async function safeRemoveWorktree(fn, root, wt, log11, subtaskId) {
19169
+ async function safeRemoveWorktree(fn, root, wt, log10, subtaskId) {
20246
19170
  try {
20247
19171
  await fn({ root, worktree_path: wt, force: true });
20248
19172
  } catch (err) {
20249
- log11("warn", `[parallel] removeWorktree 失败 ${subtaskId}`, {
19173
+ log10("warn", `[parallel] removeWorktree 失败 ${subtaskId}`, {
20250
19174
  error: describe6(err),
20251
19175
  worktree: wt
20252
19176
  });
20253
19177
  }
20254
19178
  }
20255
- async function fireMergeAttempt(cb, attempt, idx, log11) {
19179
+ async function fireMergeAttempt(cb, attempt, idx, log10) {
20256
19180
  if (!cb)
20257
19181
  return;
20258
19182
  try {
20259
19183
  await cb(attempt, idx);
20260
19184
  } catch (err) {
20261
- log11("warn", `[parallel] onMergeAttempt 抛错(已隔离)`, {
19185
+ log10("warn", `[parallel] onMergeAttempt 抛错(已隔离)`, {
20262
19186
  id: attempt.subtaskId,
20263
19187
  error: describe6(err)
20264
19188
  });
@@ -20284,7 +19208,7 @@ async function schedule(opts) {
20284
19208
  }
20285
19209
  const now = opts.deps.now ?? Date.now;
20286
19210
  const start = now();
20287
- const log11 = opts.deps.log ?? (() => {});
19211
+ const log10 = opts.deps.log ?? (() => {});
20288
19212
  const concurrency = Math.max(1, opts.maxConcurrency ?? 4);
20289
19213
  const totalTimeout = opts.totalTimeout_ms ?? 30 * 60000;
20290
19214
  const limit = Math.max(1, opts.summaryCharLimit ?? 500);
@@ -20317,7 +19241,7 @@ async function schedule(opts) {
20317
19241
  mergeAbortFn: opts.deps.mergeAbort,
20318
19242
  removeWorktreeFn: opts.deps.removeWorktree,
20319
19243
  worktreeHasChangesFn: opts.deps.worktreeHasChanges,
20320
- log: log11
19244
+ log: log10
20321
19245
  });
20322
19246
  }
20323
19247
  const fireFinish = async (i, res) => {
@@ -20325,7 +19249,7 @@ async function schedule(opts) {
20325
19249
  try {
20326
19250
  queue.enqueue(res);
20327
19251
  } catch (err) {
20328
- log11("warn", `[parallel] queue.enqueue 抛错(已隔离)`, {
19252
+ log10("warn", `[parallel] queue.enqueue 抛错(已隔离)`, {
20329
19253
  id: res.id,
20330
19254
  error: describe7(err)
20331
19255
  });
@@ -20336,7 +19260,7 @@ async function schedule(opts) {
20336
19260
  try {
20337
19261
  await opts.onSubtaskFinish(res, i);
20338
19262
  } catch (err) {
20339
- log11("warn", `[parallel] onSubtaskFinish 抛错(已隔离)`, {
19263
+ log10("warn", `[parallel] onSubtaskFinish 抛错(已隔离)`, {
20340
19264
  id: res.id,
20341
19265
  error: describe7(err)
20342
19266
  });
@@ -20366,7 +19290,7 @@ async function schedule(opts) {
20366
19290
  try {
20367
19291
  await opts.onSubtaskStart(spec, i);
20368
19292
  } catch (err) {
20369
- log11("warn", `[parallel] onSubtaskStart 抛错(已隔离)`, {
19293
+ log10("warn", `[parallel] onSubtaskStart 抛错(已隔离)`, {
20370
19294
  id: spec.id,
20371
19295
  error: describe7(err)
20372
19296
  });
@@ -20417,7 +19341,7 @@ async function schedule(opts) {
20417
19341
  try {
20418
19342
  await alloc.cleanup();
20419
19343
  } catch (err) {
20420
- log11("warn", `[parallel] worktree 清理失败 ${spec.id}`, {
19344
+ log10("warn", `[parallel] worktree 清理失败 ${spec.id}`, {
20421
19345
  error: describe7(err)
20422
19346
  });
20423
19347
  }
@@ -20597,20 +19521,20 @@ function buildSystemPrompt(maxSubtasks) {
20597
19521
  `);
20598
19522
  }
20599
19523
  async function decomposeTask(description30, opts) {
20600
- const log11 = opts.log ?? (() => {});
19524
+ const log10 = opts.log ?? (() => {});
20601
19525
  if (opts.mockResponse) {
20602
- return validateAndFinalize(opts.mockResponse, undefined, log11, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
19526
+ return validateAndFinalize(opts.mockResponse, undefined, log10, opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS);
20603
19527
  }
20604
19528
  const maxSubtasks = opts.maxSubtasks ?? DEFAULT_MAX_SUBTASKS;
20605
19529
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
20606
19530
  let childSessionId;
20607
19531
  try {
20608
19532
  const created = await opts.client.session.create({
20609
- body: { title: `decompose:${clip6(description30, 60)}` },
19533
+ body: { title: `decompose:${clip5(description30, 60)}` },
20610
19534
  query: opts.directory ? { directory: opts.directory } : undefined
20611
19535
  });
20612
19536
  if (created.error || !created.data?.id) {
20613
- log11("warn", "[decompose] session.create 失败", { error: describe8(created.error) });
19537
+ log10("warn", "[decompose] session.create 失败", { error: describe8(created.error) });
20614
19538
  return {
20615
19539
  ok: false,
20616
19540
  subtasks: [],
@@ -20632,22 +19556,22 @@ async function decomposeTask(description30, opts) {
20632
19556
  sleep2(timeoutMs).then(() => ({ kind: "timeout" }))
20633
19557
  ]);
20634
19558
  if (raced.kind === "timeout") {
20635
- log11("warn", "[decompose] LLM 调用超时", { timeoutMs });
19559
+ log10("warn", "[decompose] LLM 调用超时", { timeoutMs });
20636
19560
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
20637
19561
  }
20638
19562
  const r = raced.value;
20639
19563
  if (r.error || !r.data) {
20640
- log11("warn", "[decompose] session.prompt 返回错误", { error: describe8(r.error) });
19564
+ log10("warn", "[decompose] session.prompt 返回错误", { error: describe8(r.error) });
20641
19565
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
20642
19566
  }
20643
19567
  const rawText = pickLastText2(r.data.parts ?? []);
20644
19568
  if (!rawText) {
20645
- log11("warn", "[decompose] LLM 输出为空");
19569
+ log10("warn", "[decompose] LLM 输出为空");
20646
19570
  return { ok: false, subtasks: [], reason: "parse_failed", raw: "" };
20647
19571
  }
20648
19572
  const parsed = extractJson(rawText);
20649
19573
  if (!parsed) {
20650
- log11("warn", "[decompose] JSON 解析失败", { raw: clip6(rawText, 200) });
19574
+ log10("warn", "[decompose] JSON 解析失败", { raw: clip5(rawText, 200) });
20651
19575
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
20652
19576
  }
20653
19577
  if (parsed && typeof parsed === "object" && parsed.single_task === true) {
@@ -20655,7 +19579,7 @@ async function decomposeTask(description30, opts) {
20655
19579
  }
20656
19580
  const subtasksRaw = parsed.subtasks;
20657
19581
  if (!Array.isArray(subtasksRaw)) {
20658
- log11("warn", "[decompose] JSON 缺 subtasks 数组");
19582
+ log10("warn", "[decompose] JSON 缺 subtasks 数组");
20659
19583
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
20660
19584
  }
20661
19585
  const normalized = [];
@@ -20671,12 +19595,12 @@ async function decomposeTask(description30, opts) {
20671
19595
  normalized.push({ description: desc, hintFiles });
20672
19596
  }
20673
19597
  if (normalized.length > maxSubtasks) {
20674
- log11("warn", "[decompose] LLM 返回子任务超过上限", { count: normalized.length, maxSubtasks });
19598
+ log10("warn", "[decompose] LLM 返回子任务超过上限", { count: normalized.length, maxSubtasks });
20675
19599
  return { ok: false, subtasks: [], reason: "parse_failed", raw: rawText };
20676
19600
  }
20677
- return validateAndFinalize(normalized, rawText, log11, maxSubtasks);
19601
+ return validateAndFinalize(normalized, rawText, log10, maxSubtasks);
20678
19602
  } catch (err) {
20679
- log11("warn", "[decompose] 抛错", { error: describe8(err) });
19603
+ log10("warn", "[decompose] 抛错", { error: describe8(err) });
20680
19604
  return { ok: false, subtasks: [], reason: "llm_unavailable" };
20681
19605
  } finally {
20682
19606
  if (childSessionId) {
@@ -20686,12 +19610,12 @@ async function decomposeTask(description30, opts) {
20686
19610
  query: opts.directory ? { directory: opts.directory } : undefined
20687
19611
  });
20688
19612
  } catch (err) {
20689
- log11("warn", "[decompose] session.delete 失败", { error: describe8(err) });
19613
+ log10("warn", "[decompose] session.delete 失败", { error: describe8(err) });
20690
19614
  }
20691
19615
  }
20692
19616
  }
20693
19617
  }
20694
- function validateAndFinalize(subtasks, raw, log11, maxSubtasks) {
19618
+ function validateAndFinalize(subtasks, raw, log10, maxSubtasks) {
20695
19619
  if (subtasks.length < 2) {
20696
19620
  return { ok: false, subtasks: [], reason: "single_task", raw };
20697
19621
  }
@@ -20701,7 +19625,7 @@ function validateAndFinalize(subtasks, raw, log11, maxSubtasks) {
20701
19625
  }
20702
19626
  }
20703
19627
  if (subtasks.length > maxSubtasks) {
20704
- log11("warn", "[decompose] LLM 返回子任务超过上限", { count: subtasks.length, maxSubtasks });
19628
+ log10("warn", "[decompose] LLM 返回子任务超过上限", { count: subtasks.length, maxSubtasks });
20705
19629
  return { ok: false, subtasks: [], reason: "parse_failed", raw };
20706
19630
  }
20707
19631
  for (let i = 0;i < subtasks.length; i++) {
@@ -20715,7 +19639,7 @@ function validateAndFinalize(subtasks, raw, log11, maxSubtasks) {
20715
19639
  continue;
20716
19640
  for (const f of b) {
20717
19641
  if (setA.has(f.trim())) {
20718
- log11("info", "[decompose] hintFiles 有交集,降级 single_task", {
19642
+ log10("info", "[decompose] hintFiles 有交集,降级 single_task", {
20719
19643
  a: subtasks[i].description,
20720
19644
  b: subtasks[j].description,
20721
19645
  file: f
@@ -20780,7 +19704,7 @@ function tryParse(s) {
20780
19704
  return null;
20781
19705
  }
20782
19706
  }
20783
- function clip6(s, n) {
19707
+ function clip5(s, n) {
20784
19708
  if (!s)
20785
19709
  return "";
20786
19710
  return s.length <= n ? s : s.slice(0, n - 1) + "…";
@@ -20804,7 +19728,7 @@ function sleep2(ms) {
20804
19728
 
20805
19729
  // plugins/subtasks.ts
20806
19730
  init_runtime_paths();
20807
- var PLUGIN_NAME19 = "subtasks";
19731
+ var PLUGIN_NAME16 = "subtasks";
20808
19732
  function getLogFile(root = process.cwd()) {
20809
19733
  return path23.join(runtimeDir(root), "logs", "subtasks.log");
20810
19734
  }
@@ -20870,7 +19794,7 @@ var mockRunner = async (spec) => {
20870
19794
  };
20871
19795
  async function handleParallelCommand(raw) {
20872
19796
  const ctx = raw ?? {};
20873
- const log11 = ctx.log;
19797
+ const log10 = ctx.log;
20874
19798
  const args = ctx.args ?? {};
20875
19799
  const parentId = (args.parentId ?? `task-${Date.now().toString(36)}`).replace(/[^a-zA-Z0-9._-]/g, "-");
20876
19800
  const rawDescription = typeof args.description === "string" ? args.description.trim() : "";
@@ -20884,7 +19808,7 @@ async function handleParallelCommand(raw) {
20884
19808
  specs = splitDescriptions(args.description, { parentId });
20885
19809
  }
20886
19810
  if (specs.length === 0) {
20887
- log11?.warn(`[${PLUGIN_NAME19}] /parallel 缺有效子任务`);
19811
+ log10?.warn(`[${PLUGIN_NAME16}] /parallel 缺有效子任务`);
20888
19812
  await safeReply2(ctx, "⚠ /parallel 需要至少 1 个子任务(用 ; 或换行分隔)");
20889
19813
  return { ok: false, reason: "no_subtasks" };
20890
19814
  }
@@ -20910,7 +19834,7 @@ async function handleParallelCommand(raw) {
20910
19834
  if (!gitOk) {
20911
19835
  autoMerge = false;
20912
19836
  downgradedAutoMerge = true;
20913
- log11?.warn("[subtasks] mergeRoot 非 git 仓库,降级 autoMerge=false");
19837
+ log10?.warn("[subtasks] mergeRoot 非 git 仓库,降级 autoMerge=false");
20914
19838
  const canNoticeEarly = Boolean(ctx.client && ctx.parentSessionID);
20915
19839
  if (canNoticeEarly) {
20916
19840
  await sendParentNotice(ctx.client, ctx.parentSessionID, "ℹ 当前目录不是 git 仓库,worktree 隔离已自动关闭,以单目录模式运行", { directory: ctx.directory });
@@ -20919,17 +19843,17 @@ async function handleParallelCommand(raw) {
20919
19843
  }
20920
19844
  const canNotice = Boolean(ctx.client && ctx.parentSessionID);
20921
19845
  if (specs.length === 1 && rawDescription.length >= 30 && ctx.client) {
20922
- log11?.info(`[${PLUGIN_NAME19}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
19846
+ log10?.info(`[${PLUGIN_NAME16}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
20923
19847
  const dec = await decomposeTask(rawDescription, {
20924
19848
  client: ctx.client,
20925
19849
  directory: ctx.directory,
20926
19850
  log: (lvl, msg, data) => {
20927
19851
  if (lvl === "error")
20928
- log11?.error(msg, data);
19852
+ log10?.error(msg, data);
20929
19853
  else if (lvl === "warn")
20930
- log11?.warn(msg, data);
19854
+ log10?.warn(msg, data);
20931
19855
  else
20932
- log11?.info(msg, data);
19856
+ log10?.info(msg, data);
20933
19857
  },
20934
19858
  mockResponse: ctx.decomposeMockResponse
20935
19859
  });
@@ -20940,7 +19864,7 @@ async function handleParallelCommand(raw) {
20940
19864
  timeout_ms: undefined,
20941
19865
  args: d.hintFiles && d.hintFiles.length > 0 ? { hintFiles: d.hintFiles } : undefined
20942
19866
  }));
20943
- log11?.info(`[${PLUGIN_NAME19}] AI 拆分成功 → ${specs.length} 个子任务`);
19867
+ log10?.info(`[${PLUGIN_NAME16}] AI 拆分成功 → ${specs.length} 个子任务`);
20944
19868
  if (canNotice) {
20945
19869
  const lines = [
20946
19870
  `\uD83E\uDD16 AI 已自动拆为 ${specs.length} 个并行子任务:`,
@@ -20952,7 +19876,7 @@ async function handleParallelCommand(raw) {
20952
19876
  });
20953
19877
  }
20954
19878
  } else if (dec.reason === "llm_unavailable" || dec.reason === "parse_failed") {
20955
- log11?.warn(`[${PLUGIN_NAME19}] AI 拆分失败(${dec.reason}),按单任务执行`);
19879
+ log10?.warn(`[${PLUGIN_NAME16}] AI 拆分失败(${dec.reason}),按单任务执行`);
20956
19880
  }
20957
19881
  }
20958
19882
  if (canNotice) {
@@ -20967,11 +19891,11 @@ async function handleParallelCommand(raw) {
20967
19891
  directory: ctx.directory,
20968
19892
  log: (lvl, msg, data) => {
20969
19893
  if (lvl === "error")
20970
- log11?.error(msg, data);
19894
+ log10?.error(msg, data);
20971
19895
  else if (lvl === "warn")
20972
- log11?.warn(msg, data);
19896
+ log10?.warn(msg, data);
20973
19897
  else
20974
- log11?.info(msg, data);
19898
+ log10?.info(msg, data);
20975
19899
  }
20976
19900
  });
20977
19901
  }
@@ -21015,11 +19939,11 @@ async function handleParallelCommand(raw) {
21015
19939
  allocateWorktree: downgradedAutoMerge ? undefined : ctx.allocateWorktree,
21016
19940
  log: (lvl, msg, data) => {
21017
19941
  if (lvl === "error")
21018
- log11?.error(msg, data);
19942
+ log10?.error(msg, data);
21019
19943
  else if (lvl === "warn")
21020
- log11?.warn(msg, data);
19944
+ log10?.warn(msg, data);
21021
19945
  else
21022
- log11?.info(msg, data);
19946
+ log10?.info(msg, data);
21023
19947
  },
21024
19948
  tryMerge: ctx.tryMerge,
21025
19949
  mergeCommit: ctx.mergeCommit,
@@ -21031,7 +19955,7 @@ async function handleParallelCommand(raw) {
21031
19955
  });
21032
19956
  } catch (err) {
21033
19957
  const msg = err instanceof Error ? err.message : String(err);
21034
- log11?.error(`[${PLUGIN_NAME19}] schedule 抛错`, { error: msg });
19958
+ log10?.error(`[${PLUGIN_NAME16}] schedule 抛错`, { error: msg });
21035
19959
  await safeReply2(ctx, `❌ 并发调度失败:${msg}`);
21036
19960
  return { ok: false, reason: msg };
21037
19961
  }
@@ -21049,7 +19973,7 @@ async function handleParallelCommand(raw) {
21049
19973
  }
21050
19974
  await safeReply2(ctx, summaryLines.join(`
21051
19975
  `));
21052
- log11?.info(`[${PLUGIN_NAME19}] schedule 完成`, {
19976
+ log10?.info(`[${PLUGIN_NAME16}] schedule 完成`, {
21053
19977
  parentId,
21054
19978
  success: result.digest.success,
21055
19979
  failed: result.digest.failed,
@@ -21062,7 +19986,7 @@ async function handleParallelCommand(raw) {
21062
19986
  try {
21063
19987
  await ctx.onCompleted(result);
21064
19988
  } catch (err) {
21065
- log11?.warn(`[${PLUGIN_NAME19}] onCompleted hook 抛错(已隔离)`, {
19989
+ log10?.warn(`[${PLUGIN_NAME16}] onCompleted hook 抛错(已隔离)`, {
21066
19990
  error: err instanceof Error ? err.message : String(err)
21067
19991
  });
21068
19992
  }
@@ -21104,7 +20028,7 @@ async function writeLog(level, msg, data) {
21104
20028
  const line = JSON.stringify({
21105
20029
  ts: new Date().toISOString(),
21106
20030
  level,
21107
- plugin: PLUGIN_NAME19,
20031
+ plugin: PLUGIN_NAME16,
21108
20032
  msg,
21109
20033
  data
21110
20034
  }) + `
@@ -21115,11 +20039,11 @@ async function writeLog(level, msg, data) {
21115
20039
  await fs18.appendFile(logFile, line, "utf8");
21116
20040
  } catch {}
21117
20041
  }
21118
- logLifecycle(PLUGIN_NAME19, "import");
20042
+ logLifecycle(PLUGIN_NAME16, "import");
21119
20043
  var subtasksServer = async (ctx) => {
21120
- const log11 = makePluginLogger(PLUGIN_NAME19);
20044
+ const log10 = makePluginLogger(PLUGIN_NAME16);
21121
20045
  const client = ctx?.client ?? undefined;
21122
- logLifecycle(PLUGIN_NAME19, "activate", {
20046
+ logLifecycle(PLUGIN_NAME16, "activate", {
21123
20047
  directory: ctx.directory,
21124
20048
  hasClient: Boolean(client)
21125
20049
  });
@@ -21144,7 +20068,7 @@ var subtasksServer = async (ctx) => {
21144
20068
  const gitOk = await isGitRepo({ root: repoRoot }).catch(() => false);
21145
20069
  if (!gitOk) {
21146
20070
  autoMerge = false;
21147
- log11?.warn("[subtasks] worktree_isolation=true 但非 git 仓库,自动降级 autoMerge=false");
20071
+ log10?.warn("[subtasks] worktree_isolation=true 但非 git 仓库,自动降级 autoMerge=false");
21148
20072
  const canNotice = Boolean(client);
21149
20073
  if (canNotice) {
21150
20074
  await sendParentNotice(client, input.sessionID, "ℹ 当前目录不是 git 仓库,worktree 隔离已自动关闭,以单目录模式运行", { directory: ctx.directory });
@@ -21173,7 +20097,7 @@ var subtasksServer = async (ctx) => {
21173
20097
  replyLines.push(s);
21174
20098
  return Promise.resolve();
21175
20099
  },
21176
- log: log11,
20100
+ log: log10,
21177
20101
  client,
21178
20102
  parentSessionID: input.sessionID,
21179
20103
  directory: ctx.directory,
@@ -21186,11 +20110,11 @@ var subtasksServer = async (ctx) => {
21186
20110
  perTaskTimeoutMs: 5 * 60000,
21187
20111
  log: (lvl, msg, data) => {
21188
20112
  if (lvl === "error")
21189
- log11.error(msg, data);
20113
+ log10.error(msg, data);
21190
20114
  else if (lvl === "warn")
21191
- log11.warn(msg, data);
20115
+ log10.warn(msg, data);
21192
20116
  else
21193
- log11.info(msg, data);
20117
+ log10.info(msg, data);
21194
20118
  }
21195
20119
  }) : undefined
21196
20120
  };
@@ -21216,20 +20140,20 @@ var subtasksServer = async (ctx) => {
21216
20140
  });
21217
20141
  }
21218
20142
  } catch (err) {
21219
- log11.error(`[${PLUGIN_NAME19}] command.execute.before 异常(已隔离)`, {
20143
+ log10.error(`[${PLUGIN_NAME16}] command.execute.before 异常(已隔离)`, {
21220
20144
  error: err instanceof Error ? err.message : String(err)
21221
20145
  });
21222
20146
  }
21223
20147
  }
21224
20148
  };
21225
20149
  };
21226
- var handler19 = subtasksServer;
20150
+ var handler16 = subtasksServer;
21227
20151
 
21228
20152
  // plugins/terminal-monitor.ts
21229
- import * as crypto5 from "node:crypto";
21230
- var PLUGIN_NAME20 = "terminal-monitor";
21231
- logLifecycle(PLUGIN_NAME20, "import", {});
21232
- var DEFAULT_CONFIG6 = {
20153
+ import * as crypto4 from "node:crypto";
20154
+ var PLUGIN_NAME17 = "terminal-monitor";
20155
+ logLifecycle(PLUGIN_NAME17, "import", {});
20156
+ var DEFAULT_CONFIG4 = {
21233
20157
  minScore: 0.6,
21234
20158
  cooldownMs: 30000,
21235
20159
  lruLimit: 256,
@@ -21344,7 +20268,7 @@ var DEFAULT_RULES = [
21344
20268
  score: 0.3
21345
20269
  }
21346
20270
  ];
21347
- function normalizeLines(raw, maxLines = DEFAULT_CONFIG6.maxLines) {
20271
+ function normalizeLines(raw, maxLines = DEFAULT_CONFIG4.maxLines) {
21348
20272
  if (!raw)
21349
20273
  return [];
21350
20274
  const stripped = raw.replace(/\u001b\[[0-9;?]*[A-Za-z]/g, "");
@@ -21355,7 +20279,7 @@ function normalizeLines(raw, maxLines = DEFAULT_CONFIG6.maxLines) {
21355
20279
  return [...lines.slice(0, half), `... [省略 ${lines.length - maxLines} 行] ...`, ...lines.slice(-half)];
21356
20280
  }
21357
20281
  function parseTerminalOutput(ev, cfg = {}, rules = DEFAULT_RULES) {
21358
- const c = { ...DEFAULT_CONFIG6, ...cfg };
20282
+ const c = { ...DEFAULT_CONFIG4, ...cfg };
21359
20283
  const stdoutLines = normalizeLines(ev.stdout, c.maxLines);
21360
20284
  const stderrLines = normalizeLines(ev.stderr, c.maxLines);
21361
20285
  const text = [...stdoutLines, ...stderrLines].join(`
@@ -21371,7 +20295,7 @@ function parseTerminalOutput(ev, cfg = {}, rules = DEFAULT_RULES) {
21371
20295
  `).length;
21372
20296
  const lineFromStart = text.split(`
21373
20297
  `)[lineIdx - 1] ?? m[0];
21374
- const excerpt = clip7(lineFromStart.trim(), c.maxExcerpt);
20298
+ const excerpt = clip6(lineFromStart.trim(), c.maxExcerpt);
21375
20299
  if (!out.has(rule.kind)) {
21376
20300
  out.set(rule.kind, {
21377
20301
  severity: rule.severity,
@@ -21393,16 +20317,16 @@ function parseTerminalOutput(ev, cfg = {}, rules = DEFAULT_RULES) {
21393
20317
  }
21394
20318
  return [...out.values()].sort((a, b) => b.score - a.score);
21395
20319
  }
21396
- function clip7(s, max) {
20320
+ function clip6(s, max) {
21397
20321
  if (s.length <= max)
21398
20322
  return s;
21399
20323
  return s.slice(0, max - 1) + "…";
21400
20324
  }
21401
20325
 
21402
- class FingerprintLRU2 {
20326
+ class FingerprintLRU {
21403
20327
  limit;
21404
20328
  map = new Map;
21405
- constructor(limit = DEFAULT_CONFIG6.lruLimit) {
20329
+ constructor(limit = DEFAULT_CONFIG4.lruLimit) {
21406
20330
  this.limit = limit;
21407
20331
  }
21408
20332
  add(fp, ts = Date.now()) {
@@ -21428,10 +20352,10 @@ class FingerprintLRU2 {
21428
20352
  }
21429
20353
  function fingerprintFinding(cmd, f) {
21430
20354
  const raw = `${cmd ?? ""}|${f.kind}|${f.excerpt.slice(0, 60)}`;
21431
- return crypto5.createHash("sha1").update(raw).digest("hex").slice(0, 16);
20355
+ return crypto4.createHash("sha1").update(raw).digest("hex").slice(0, 16);
21432
20356
  }
21433
20357
  function shouldNotify(findings, ev, lru, cfg = {}, now = Date.now()) {
21434
- const c = { ...DEFAULT_CONFIG6, ...cfg };
20358
+ const c = { ...DEFAULT_CONFIG4, ...cfg };
21435
20359
  const out = [];
21436
20360
  let suppressed = 0;
21437
20361
  for (const f of findings) {
@@ -21452,7 +20376,7 @@ function shouldNotify(findings, ev, lru, cfg = {}, now = Date.now()) {
21452
20376
  function buildSummary(ev, findings) {
21453
20377
  const parts = [];
21454
20378
  if (ev.cmd)
21455
- parts.push(`\`${clip7(ev.cmd, 60)}\``);
20379
+ parts.push(`\`${clip6(ev.cmd, 60)}\``);
21456
20380
  if (ev.type === "terminal.exit" && typeof ev.exit_code === "number") {
21457
20381
  parts.push(`exit=${ev.exit_code}`);
21458
20382
  }
@@ -21464,7 +20388,7 @@ function buildSummary(ev, findings) {
21464
20388
  return parts.join(" · ");
21465
20389
  }
21466
20390
  const top = findings[0];
21467
- parts.push(`${top.severity}/${top.kind}: ${clip7(top.excerpt, 80)}`);
20391
+ parts.push(`${top.severity}/${top.kind}: ${clip6(top.excerpt, 80)}`);
21468
20392
  if (findings.length > 1)
21469
20393
  parts.push(`(+${findings.length - 1} 条)`);
21470
20394
  return parts.join(" · ");
@@ -21498,7 +20422,7 @@ async function processTerminalEvent(ev, opts = {}) {
21498
20422
  return { ok: false, notified: false, suppressed: 0, error: msg };
21499
20423
  }
21500
20424
  }
21501
- var sharedLRU = new FingerprintLRU2;
20425
+ var sharedLRU = new FingerprintLRU;
21502
20426
  function describeError(err) {
21503
20427
  if (err instanceof Error)
21504
20428
  return err.message;
@@ -21510,17 +20434,17 @@ function describeError(err) {
21510
20434
  return String(err);
21511
20435
  }
21512
20436
  }
21513
- var log11 = makePluginLogger(PLUGIN_NAME20);
21514
- var lru = new FingerprintLRU2;
20437
+ var log10 = makePluginLogger(PLUGIN_NAME17);
20438
+ var lru = new FingerprintLRU;
21515
20439
  var _lastNotification = null;
21516
20440
  var terminalMonitorServer = async (ctx) => {
21517
- logLifecycle(PLUGIN_NAME20, "activate", {
20441
+ logLifecycle(PLUGIN_NAME17, "activate", {
21518
20442
  directory: ctx.directory,
21519
20443
  triggerEventTypes: ["terminal.output", "terminal.exit"]
21520
20444
  });
21521
20445
  return {
21522
20446
  event: async ({ event }) => {
21523
- await safeAsync(PLUGIN_NAME20, "event", async () => {
20447
+ await safeAsync(PLUGIN_NAME17, "event", async () => {
21524
20448
  const e = event;
21525
20449
  if (!e || typeof e.type !== "string")
21526
20450
  return;
@@ -21529,12 +20453,12 @@ var terminalMonitorServer = async (ctx) => {
21529
20453
  const ev = { type: e.type, ...e.properties ?? {} };
21530
20454
  const r = await processTerminalEvent(ev, {
21531
20455
  lru,
21532
- log: log11,
20456
+ log: log10,
21533
20457
  notifyAgent: (msg) => {
21534
20458
  _lastNotification = msg;
21535
20459
  }
21536
20460
  });
21537
- safeWriteLog(PLUGIN_NAME20, {
20461
+ safeWriteLog(PLUGIN_NAME17, {
21538
20462
  hook: "event",
21539
20463
  type: e.type,
21540
20464
  notified: r.notified,
@@ -21542,18 +20466,133 @@ var terminalMonitorServer = async (ctx) => {
21542
20466
  findings: r.notification?.findings.length ?? 0
21543
20467
  });
21544
20468
  if (r.notified && r.notification) {
21545
- log11.info(`[${PLUGIN_NAME20}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
20469
+ log10.info(`[${PLUGIN_NAME17}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
21546
20470
  }
21547
20471
  });
21548
20472
  }
21549
20473
  };
21550
20474
  };
21551
- var handler20 = terminalMonitorServer;
20475
+ var handler17 = terminalMonitorServer;
20476
+
20477
+ // lib/condenser.ts
20478
+ var DEFAULT_CONDENSE = {
20479
+ budget: 128000,
20480
+ threshold: 0.7,
20481
+ target: 0.4,
20482
+ keepRecent: 6,
20483
+ keepSystem: true,
20484
+ charsPerToken: 4
20485
+ };
20486
+ function estimateTokens(text, charsPerToken = 4) {
20487
+ if (!text)
20488
+ return 0;
20489
+ return Math.max(1, Math.ceil(text.length / charsPerToken));
20490
+ }
20491
+ function tokenizeMessages(msgs, charsPerToken = 4) {
20492
+ let total = 0;
20493
+ for (const m of msgs) {
20494
+ total += m.tokens ?? estimateTokens(m.content, charsPerToken);
20495
+ }
20496
+ return total;
20497
+ }
20498
+ function fallbackSummarize(msgs) {
20499
+ if (msgs.length === 0)
20500
+ return "";
20501
+ const head = msgs.slice(0, 2).map((m) => `- [${m.role}] ${truncate(m.content, 120)}`);
20502
+ const tail = msgs.slice(-2).map((m) => `- [${m.role}] ${truncate(m.content, 120)}`);
20503
+ const tagCounts = new Map;
20504
+ let userCount = 0;
20505
+ let assistantCount = 0;
20506
+ let toolCount = 0;
20507
+ for (const m of msgs) {
20508
+ if (m.role === "user")
20509
+ userCount++;
20510
+ else if (m.role === "assistant")
20511
+ assistantCount++;
20512
+ else if (m.role === "tool")
20513
+ toolCount++;
20514
+ if (m.tag)
20515
+ tagCounts.set(m.tag, (tagCounts.get(m.tag) ?? 0) + 1);
20516
+ }
20517
+ const tagsLine = [...tagCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([t, c]) => `${t}×${c}`).join(", ");
20518
+ return [
20519
+ `### 历史摘要(共 ${msgs.length} 条 · user=${userCount} assistant=${assistantCount} tool=${toolCount})`,
20520
+ tagsLine ? `- 关键事件:${tagsLine}` : "",
20521
+ "",
20522
+ "**开头:**",
20523
+ ...head,
20524
+ "",
20525
+ "**结尾:**",
20526
+ ...tail
20527
+ ].filter(Boolean).join(`
20528
+ `);
20529
+ }
20530
+ function truncate(s, max) {
20531
+ if (!s)
20532
+ return "";
20533
+ return s.length <= max ? s : s.slice(0, max - 1) + "…";
20534
+ }
20535
+ async function condense(input, opts = {}) {
20536
+ const cfg = { ...DEFAULT_CONDENSE, ...opts.config ?? {} };
20537
+ const msgs = [...input];
20538
+ const before = {
20539
+ count: msgs.length,
20540
+ tokens: tokenizeMessages(msgs, cfg.charsPerToken)
20541
+ };
20542
+ if (cfg.budget <= 0 || before.tokens / cfg.budget < cfg.threshold) {
20543
+ return {
20544
+ messages: msgs,
20545
+ before,
20546
+ after: { ...before },
20547
+ compressed: false,
20548
+ reason: `usage ${(before.tokens / Math.max(cfg.budget, 1)).toFixed(2)} < threshold ${cfg.threshold}`
20549
+ };
20550
+ }
20551
+ const systems = cfg.keepSystem ? msgs.filter((m) => m.role === "system") : [];
20552
+ const nonSystem = cfg.keepSystem ? msgs.filter((m) => m.role !== "system") : msgs;
20553
+ const recents = nonSystem.slice(-cfg.keepRecent);
20554
+ const middles = nonSystem.slice(0, Math.max(0, nonSystem.length - cfg.keepRecent));
20555
+ if (middles.length === 0) {
20556
+ return {
20557
+ messages: msgs,
20558
+ before,
20559
+ after: { ...before },
20560
+ compressed: false,
20561
+ reason: "nothing in middle to compress"
20562
+ };
20563
+ }
20564
+ let summary = "";
20565
+ try {
20566
+ summary = await (opts.summarize ?? fallbackSummarize)(middles) ?? "";
20567
+ } catch {
20568
+ summary = fallbackSummarize(middles);
20569
+ }
20570
+ if (!summary)
20571
+ summary = fallbackSummarize(middles);
20572
+ const summaryMsg = {
20573
+ role: "assistant",
20574
+ content: summary,
20575
+ tag: "condensed-summary"
20576
+ };
20577
+ const out = [...systems, summaryMsg, ...recents];
20578
+ const after = {
20579
+ count: out.length,
20580
+ tokens: tokenizeMessages(out, cfg.charsPerToken)
20581
+ };
20582
+ return {
20583
+ messages: out,
20584
+ before,
20585
+ after,
20586
+ compressed: true,
20587
+ summary,
20588
+ reason: `compressed ${middles.length} → 1 summary (target ${cfg.target})`
20589
+ };
20590
+ }
21552
20591
 
21553
20592
  // plugins/token-manager.ts
21554
- var PLUGIN_NAME21 = "token-manager";
21555
- logLifecycle(PLUGIN_NAME21, "import", {});
21556
- async function handleMessageBefore(raw, log12, defaults) {
20593
+ var PLUGIN_NAME18 = "token-manager";
20594
+ logLifecycle(PLUGIN_NAME18, "import", {});
20595
+ async function handleMessageBefore(raw, log11, defaults) {
21557
20596
  const ctx = raw ?? {};
21558
20597
  if (!Array.isArray(ctx.messages) || ctx.messages.length === 0)
21559
20598
  return null;
@@ -21572,21 +20611,21 @@ async function handleMessageBefore(raw, log12, defaults) {
21572
20611
  };
21573
20612
  if (r.compressed) {
21574
20613
  ctx.messages = r.messages;
21575
- log12?.info(`[${PLUGIN_NAME21}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
20614
+ log11?.info(`[${PLUGIN_NAME18}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
21576
20615
  }
21577
20616
  return r;
21578
20617
  } catch (err) {
21579
- log12?.warn(`[${PLUGIN_NAME21}] 压缩异常(已隔离)`, {
20618
+ log11?.warn(`[${PLUGIN_NAME18}] 压缩异常(已隔离)`, {
21580
20619
  error: err instanceof Error ? err.message : String(err)
21581
20620
  });
21582
20621
  return null;
21583
20622
  }
21584
20623
  }
21585
- var log12 = makePluginLogger(PLUGIN_NAME21);
20624
+ var log11 = makePluginLogger(PLUGIN_NAME18);
21586
20625
  var tokenManagerServer = async (ctx) => {
21587
20626
  const rt = loadRuntimeSync();
21588
20627
  const threshold = rt.runtime.context.condenser_threshold_ratio;
21589
- logLifecycle(PLUGIN_NAME21, "activate", {
20628
+ logLifecycle(PLUGIN_NAME18, "activate", {
21590
20629
  directory: ctx.directory,
21591
20630
  threshold,
21592
20631
  target: DEFAULT_CONDENSE.target,
@@ -21595,7 +20634,7 @@ var tokenManagerServer = async (ctx) => {
21595
20634
  });
21596
20635
  return {
21597
20636
  "experimental.chat.messages.transform": async (_input, output) => {
21598
- await safeAsync(PLUGIN_NAME21, "experimental.chat.messages.transform", async () => {
20637
+ await safeAsync(PLUGIN_NAME18, "experimental.chat.messages.transform", async () => {
21599
20638
  const list = output.messages;
21600
20639
  if (!Array.isArray(list) || list.length === 0)
21601
20640
  return;
@@ -21605,10 +20644,10 @@ var tokenManagerServer = async (ctx) => {
21605
20644
  `);
21606
20645
  return { role, content };
21607
20646
  });
21608
- const r = await handleMessageBefore({ messages: flat }, log12, { threshold });
20647
+ const r = await handleMessageBefore({ messages: flat }, log11, { threshold });
21609
20648
  if (!r)
21610
20649
  return;
21611
- safeWriteLog(PLUGIN_NAME21, {
20650
+ safeWriteLog(PLUGIN_NAME18, {
21612
20651
  hook: "experimental.chat.messages.transform",
21613
20652
  mode: "observe-only",
21614
20653
  before_msgs: r.before.count,
@@ -21619,13 +20658,13 @@ var tokenManagerServer = async (ctx) => {
21619
20658
  reason: r.reason
21620
20659
  });
21621
20660
  if (r.compressed) {
21622
- log12.warn(`[${PLUGIN_NAME21}] 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)`);
20661
+ log11.warn(`[${PLUGIN_NAME18}] 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)`);
21623
20662
  }
21624
20663
  });
21625
20664
  }
21626
20665
  };
21627
20666
  };
21628
- var handler21 = tokenManagerServer;
20667
+ var handler18 = tokenManagerServer;
21629
20668
 
21630
20669
  // plugins/tool-policy.ts
21631
20670
  import { promises as fs19 } from "node:fs";
@@ -21909,8 +20948,8 @@ function checkFileAccess(acl, file, op) {
21909
20948
  }
21910
20949
 
21911
20950
  // plugins/tool-policy.ts
21912
- var PLUGIN_NAME22 = "tool-policy";
21913
- logLifecycle(PLUGIN_NAME22, "import", {});
20951
+ var PLUGIN_NAME19 = "tool-policy";
20952
+ logLifecycle(PLUGIN_NAME19, "import", {});
21914
20953
  var EMPTY_ACL = { whitelistMode: false };
21915
20954
  function getAgentAcl(cfg, agent) {
21916
20955
  if (!agent || !cfg.per_agent)
@@ -21980,18 +21019,18 @@ async function loadPolicy(root = process.cwd()) {
21980
21019
  function classifyToolKind(toolName) {
21981
21020
  return classifyTool(toolName);
21982
21021
  }
21983
- async function resolveCurrentAgent2(client, sessionID, log13) {
21022
+ async function resolveCurrentAgent2(client, sessionID, log12) {
21984
21023
  try {
21985
21024
  const sessionApi = client?.session;
21986
21025
  if (!sessionApi || typeof sessionApi.get !== "function") {
21987
- log13.warn(`client.session.get unavailable`, { sessionID });
21026
+ log12.warn(`client.session.get unavailable`, { sessionID });
21988
21027
  return;
21989
21028
  }
21990
21029
  const res = await sessionApi.get({ path: { id: sessionID } });
21991
21030
  const data = res?.data ?? res;
21992
21031
  const rawAgent = data?.agent;
21993
21032
  if (typeof rawAgent !== "string" || rawAgent === "") {
21994
- log13.warn(`client.session.get returned no string agent (保守放行)`, {
21033
+ log12.warn(`client.session.get returned no string agent (保守放行)`, {
21995
21034
  sessionID,
21996
21035
  dataKeys: data && typeof data === "object" ? Object.keys(data) : null
21997
21036
  });
@@ -21999,31 +21038,31 @@ async function resolveCurrentAgent2(client, sessionID, log13) {
21999
21038
  }
22000
21039
  return rawAgent;
22001
21040
  } catch (err) {
22002
- log13.warn(`client.session.get failed (保守放行)`, {
21041
+ log12.warn(`client.session.get failed (保守放行)`, {
22003
21042
  sessionID,
22004
21043
  error: err instanceof Error ? err.message : String(err)
22005
21044
  });
22006
21045
  return;
22007
21046
  }
22008
21047
  }
22009
- var log13 = makePluginLogger(PLUGIN_NAME22);
21048
+ var log12 = makePluginLogger(PLUGIN_NAME19);
22010
21049
  var toolPolicyServer = async (ctx) => {
22011
21050
  const directory = ctx.directory ?? process.cwd();
22012
21051
  const cfg = await loadPolicy(directory);
22013
- logLifecycle(PLUGIN_NAME22, "activate", {
21052
+ logLifecycle(PLUGIN_NAME19, "activate", {
22014
21053
  directory,
22015
21054
  acl_loaded: !!cfg.acl
22016
21055
  });
22017
21056
  return {
22018
21057
  "tool.execute.before": async (input, output) => {
22019
21058
  let denied;
22020
- await safeAsync(PLUGIN_NAME22, "tool.execute.before", async () => {
21059
+ await safeAsync(PLUGIN_NAME19, "tool.execute.before", async () => {
22021
21060
  const toolName = input.tool;
22022
21061
  const argsObj = output.args ?? {};
22023
21062
  const needsAgentDetection = toolName === "pending_changes" && (argsObj.action === "apply" || argsObj.action === "apply_all");
22024
21063
  let currentAgent = undefined;
22025
21064
  if (needsAgentDetection) {
22026
- currentAgent = await resolveCurrentAgent2(ctx.client, input.sessionID, log13);
21065
+ currentAgent = await resolveCurrentAgent2(ctx.client, input.sessionID, log12);
22027
21066
  }
22028
21067
  const files = [];
22029
21068
  const filePath = argsObj["path"] ?? argsObj["filePath"] ?? argsObj["file"];
@@ -22037,7 +21076,7 @@ var toolPolicyServer = async (ctx) => {
22037
21076
  args: argsObj,
22038
21077
  files: files.length ? files : undefined
22039
21078
  }, cfg, currentAgent);
22040
- safeWriteLog(PLUGIN_NAME22, {
21079
+ safeWriteLog(PLUGIN_NAME19, {
22041
21080
  hook: "tool.execute.before",
22042
21081
  tool: toolName,
22043
21082
  callID: input.callID,
@@ -22048,7 +21087,7 @@ var toolPolicyServer = async (ctx) => {
22048
21087
  currentAgent
22049
21088
  });
22050
21089
  if (decision.action === "deny") {
22051
- log13.warn(`[${PLUGIN_NAME22}] DENY ${toolName}`, {
21090
+ log12.warn(`[${PLUGIN_NAME19}] DENY ${toolName}`, {
22052
21091
  action: argsObj.action,
22053
21092
  currentAgent,
22054
21093
  reasons: decision.reasons,
@@ -22063,7 +21102,7 @@ var toolPolicyServer = async (ctx) => {
22063
21102
  }
22064
21103
  };
22065
21104
  };
22066
- var handler22 = toolPolicyServer;
21105
+ var handler19 = toolPolicyServer;
22067
21106
 
22068
21107
  // plugins/update-checker.ts
22069
21108
  import { existsSync as existsSync6 } from "node:fs";
@@ -22071,7 +21110,7 @@ import { homedir as homedir8 } from "node:os";
22071
21110
  import { join as join23 } from "node:path";
22072
21111
 
22073
21112
  // lib/update-checker-impl.ts
22074
- import { createHash as createHash5 } from "node:crypto";
21113
+ import { createHash as createHash4 } from "node:crypto";
22075
21114
  import {
22076
21115
  copyFileSync,
22077
21116
  existsSync as existsSync5,
@@ -22093,7 +21132,7 @@ import * as zlib from "node:zlib";
22093
21132
  // lib/version-injected.ts
22094
21133
  function getInjectedVersion() {
22095
21134
  try {
22096
- const v = "0.5.20";
21135
+ const v = "0.5.21";
22097
21136
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
22098
21137
  return v;
22099
21138
  }
@@ -22372,7 +21411,7 @@ function verifyIntegrity(buf, expected) {
22372
21411
  if (algo !== "sha512" && algo !== "sha256" && algo !== "sha384") {
22373
21412
  throw new Error(`integrity_algo_unsupported: ${algo}`);
22374
21413
  }
22375
- const actualB64 = createHash5(algo).update(buf).digest("base64");
21414
+ const actualB64 = createHash4(algo).update(buf).digest("base64");
22376
21415
  if (actualB64 !== expectedB64) {
22377
21416
  throw new Error(`integrity_mismatch: expected ${algo}=${expectedB64.slice(0, 16)}... got ${actualB64.slice(0, 16)}...`);
22378
21417
  }
@@ -22596,20 +21635,20 @@ function compareOpencodeVersion(opts) {
22596
21635
  }
22597
21636
 
22598
21637
  // plugins/update-checker.ts
22599
- var PLUGIN_NAME23 = "update-checker";
21638
+ var PLUGIN_NAME20 = "update-checker";
22600
21639
  var PLUGIN_VERSION = "2.0.0";
22601
- logLifecycle(PLUGIN_NAME23, "import", { version: PLUGIN_VERSION });
21640
+ logLifecycle(PLUGIN_NAME20, "import", { version: PLUGIN_VERSION });
22602
21641
  var updateCheckerServer = async (ctx) => {
22603
21642
  const yieldResult = shouldYieldToLocalPlugin({ directory: ctx.directory });
22604
21643
  if (yieldResult.yield) {
22605
- safeWriteLog(PLUGIN_NAME23, {
21644
+ safeWriteLog(PLUGIN_NAME20, {
22606
21645
  level: "info",
22607
21646
  msg: "dev_mode_yield_skip",
22608
21647
  reason: yieldResult.reason,
22609
21648
  markerPath: yieldResult.markerPath,
22610
21649
  detail: formatYieldLog(yieldResult)
22611
21650
  });
22612
- logLifecycle(PLUGIN_NAME23, "activate", {
21651
+ logLifecycle(PLUGIN_NAME20, "activate", {
22613
21652
  yield_to_local: true,
22614
21653
  yield_reason: yieldResult.reason,
22615
21654
  skipped: "auto_install + background_check"
@@ -22618,7 +21657,7 @@ var updateCheckerServer = async (ctx) => {
22618
21657
  }
22619
21658
  const rt = loadRuntimeSync();
22620
21659
  const u = rt.runtime.update;
22621
- logLifecycle(PLUGIN_NAME23, "activate", {
21660
+ logLifecycle(PLUGIN_NAME20, "activate", {
22622
21661
  version: PLUGIN_VERSION,
22623
21662
  auto_check_enabled: u.auto_check_enabled,
22624
21663
  interval_hours: u.interval_hours,
@@ -22630,14 +21669,14 @@ var updateCheckerServer = async (ctx) => {
22630
21669
  repo_fallback: u.repo,
22631
21670
  config_source: "codeforge.json"
22632
21671
  });
22633
- await safeAsync(PLUGIN_NAME23, "opencode_version_check", async () => {
21672
+ await safeAsync(PLUGIN_NAME20, "opencode_version_check", async () => {
22634
21673
  const compat = loadCompatibility();
22635
21674
  const opencodeVer = detectOpencodeVersion();
22636
21675
  const verdict = compareOpencodeVersion({
22637
21676
  currentOpencodeVer: opencodeVer,
22638
21677
  compat
22639
21678
  });
22640
- safeWriteLog(PLUGIN_NAME23, {
21679
+ safeWriteLog(PLUGIN_NAME20, {
22641
21680
  level: "info",
22642
21681
  msg: "opencode_version_check",
22643
21682
  opencodeVer,
@@ -22654,14 +21693,14 @@ var updateCheckerServer = async (ctx) => {
22654
21693
  }
22655
21694
  });
22656
21695
  if (!u.auto_check_enabled) {
22657
- safeWriteLog(PLUGIN_NAME23, {
21696
+ safeWriteLog(PLUGIN_NAME20, {
22658
21697
  level: "info",
22659
21698
  msg: "auto-check disabled (codeforge.json update.auto_check_enabled=false)"
22660
21699
  });
22661
21700
  return {};
22662
21701
  }
22663
21702
  setImmediate(() => {
22664
- safeAsync(PLUGIN_NAME23, "checkAndMaybeUpdate", async () => {
21703
+ safeAsync(PLUGIN_NAME20, "checkAndMaybeUpdate", async () => {
22665
21704
  const local = readLocalVersion();
22666
21705
  let npmResult = null;
22667
21706
  try {
@@ -22672,7 +21711,7 @@ var updateCheckerServer = async (ctx) => {
22672
21711
  timeoutMs: 5000
22673
21712
  });
22674
21713
  } catch (e) {
22675
- safeWriteLog(PLUGIN_NAME23, {
21714
+ safeWriteLog(PLUGIN_NAME20, {
22676
21715
  level: "warn",
22677
21716
  msg: "npm_fetch_failed",
22678
21717
  error: e.message
@@ -22681,7 +21720,7 @@ var updateCheckerServer = async (ctx) => {
22681
21720
  return;
22682
21721
  }
22683
21722
  if (!npmResult) {
22684
- safeWriteLog(PLUGIN_NAME23, {
21723
+ safeWriteLog(PLUGIN_NAME20, {
22685
21724
  level: "info",
22686
21725
  msg: "npm_no_release",
22687
21726
  package: u.package,
@@ -22690,7 +21729,7 @@ var updateCheckerServer = async (ctx) => {
22690
21729
  return;
22691
21730
  }
22692
21731
  const hasUpdate = cmpVersion(local, npmResult.version) < 0;
22693
- safeWriteLog(PLUGIN_NAME23, {
21732
+ safeWriteLog(PLUGIN_NAME20, {
22694
21733
  level: "info",
22695
21734
  msg: "npm_check_result",
22696
21735
  local,
@@ -22705,10 +21744,10 @@ var updateCheckerServer = async (ctx) => {
22705
21744
  更新命令:npx ${u.package} install --global`);
22706
21745
  return;
22707
21746
  }
22708
- await safeAsync(PLUGIN_NAME23, "auto_install_bundle", async () => {
21747
+ await safeAsync(PLUGIN_NAME20, "auto_install_bundle", async () => {
22709
21748
  const target = getOpencodeBundlePath();
22710
21749
  if (!target) {
22711
- safeWriteLog(PLUGIN_NAME23, {
21750
+ safeWriteLog(PLUGIN_NAME20, {
22712
21751
  level: "warn",
22713
21752
  msg: "auto_install_skip",
22714
21753
  reason: "无法定位 opencode bundle 路径"
@@ -22727,7 +21766,7 @@ var updateCheckerServer = async (ctx) => {
22727
21766
  oldVersion: local,
22728
21767
  keepBackups: u.backup_keep
22729
21768
  });
22730
- safeWriteLog(PLUGIN_NAME23, {
21769
+ safeWriteLog(PLUGIN_NAME20, {
22731
21770
  level: "info",
22732
21771
  msg: "auto_install_success",
22733
21772
  local,
@@ -22767,7 +21806,7 @@ function getOpencodeBundlePath() {
22767
21806
  return candidates[0] ?? null;
22768
21807
  }
22769
21808
  async function fallbackToGitHubReleases(ctx, u) {
22770
- await safeAsync(PLUGIN_NAME23, "github_fallback", async () => {
21809
+ await safeAsync(PLUGIN_NAME20, "github_fallback", async () => {
22771
21810
  const result = await checkUpdateOnce({
22772
21811
  repo: u.repo,
22773
21812
  intervalMs: u.interval_hours * 3600 * 1000
@@ -22777,7 +21816,7 @@ async function fallbackToGitHubReleases(ctx, u) {
22777
21816
  }
22778
21817
  async function reportLegacyResult(ctx, result, repo) {
22779
21818
  if (result.error && !result.fromCache) {
22780
- safeWriteLog(PLUGIN_NAME23, {
21819
+ safeWriteLog(PLUGIN_NAME20, {
22781
21820
  level: "warn",
22782
21821
  msg: `update check failed: ${result.error}`,
22783
21822
  ...result
@@ -22785,7 +21824,7 @@ async function reportLegacyResult(ctx, result, repo) {
22785
21824
  return;
22786
21825
  }
22787
21826
  if (!result.hasUpdate) {
22788
- safeWriteLog(PLUGIN_NAME23, {
21827
+ safeWriteLog(PLUGIN_NAME20, {
22789
21828
  level: "info",
22790
21829
  msg: `up-to-date (local=${result.local}, remote=${result.remote}${result.fromCache ? ", from_cache" : ""})`,
22791
21830
  ...result
@@ -22795,7 +21834,7 @@ async function reportLegacyResult(ctx, result, repo) {
22795
21834
  const updateCmd = `bunx --bun github:${repo} install`;
22796
21835
  const toast = `[CodeForge] 有新版本:${result.local} → ${result.remote}
22797
21836
  更新命令:${updateCmd}`;
22798
- safeWriteLog(PLUGIN_NAME23, {
21837
+ safeWriteLog(PLUGIN_NAME20, {
22799
21838
  level: "info",
22800
21839
  msg: "new_version_available_github_fallback",
22801
21840
  local: result.local,
@@ -22806,17 +21845,17 @@ async function reportLegacyResult(ctx, result, repo) {
22806
21845
  await postToast(ctx, toast);
22807
21846
  }
22808
21847
  async function postToast(ctx, message) {
22809
- await safeAsync(PLUGIN_NAME23, "client.app.log", async () => {
21848
+ await safeAsync(PLUGIN_NAME20, "client.app.log", async () => {
22810
21849
  await ctx.client.app.log({
22811
21850
  body: {
22812
- service: PLUGIN_NAME23,
21851
+ service: PLUGIN_NAME20,
22813
21852
  level: "info",
22814
21853
  message
22815
21854
  }
22816
21855
  });
22817
21856
  });
22818
21857
  }
22819
- var handler23 = updateCheckerServer;
21858
+ var handler20 = updateCheckerServer;
22820
21859
 
22821
21860
  // plugins/workflow-engine.ts
22822
21861
  import * as path27 from "node:path";
@@ -23271,9 +22310,9 @@ async function runStepAutoFeedback(step, adapter) {
23271
22310
  }
23272
22311
 
23273
22312
  // plugins/workflow-engine.ts
23274
- var PLUGIN_NAME24 = "workflow-engine";
23275
- logLifecycle(PLUGIN_NAME24, "import", {});
23276
- var fallbackLog2 = makePluginLogger(PLUGIN_NAME24);
22313
+ var PLUGIN_NAME21 = "workflow-engine";
22314
+ logLifecycle(PLUGIN_NAME21, "import", {});
22315
+ var fallbackLog2 = makePluginLogger(PLUGIN_NAME21);
23277
22316
  var _registry = null;
23278
22317
  async function loadRegistry(workflowsDir) {
23279
22318
  const { loaded, failed } = await loadWorkflowsFromDir(workflowsDir);
@@ -23290,35 +22329,35 @@ async function ensureRegistry(workflowsDir = "workflows") {
23290
22329
  }
23291
22330
  async function handleCommandInvoked(raw, workflowsDir = "workflows") {
23292
22331
  const ctx = raw ?? {};
23293
- const log14 = ctx.log ?? fallbackLog2;
22332
+ const log13 = ctx.log ?? fallbackLog2;
23294
22333
  const command = typeof ctx.command === "string" ? ctx.command : null;
23295
22334
  if (!command) {
23296
- log14.warn(`[${PLUGIN_NAME24}] command.invoked 缺 command 字段`, ctx);
22335
+ log13.warn(`[${PLUGIN_NAME21}] command.invoked 缺 command 字段`, ctx);
23297
22336
  return null;
23298
22337
  }
23299
22338
  const reg = await ensureRegistry(workflowsDir);
23300
22339
  if (reg.errors.length) {
23301
- log14.warn(`[${PLUGIN_NAME24}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
22340
+ log13.warn(`[${PLUGIN_NAME21}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
23302
22341
  }
23303
22342
  const wf = reg.workflows.find((w) => matchesTrigger(w, command));
23304
22343
  if (!wf) {
23305
- log14.info(`[${PLUGIN_NAME24}] no workflow matches "${command}"`);
22344
+ log13.info(`[${PLUGIN_NAME21}] no workflow matches "${command}"`);
23306
22345
  return null;
23307
22346
  }
23308
- log14.info(`[${PLUGIN_NAME24}] dispatch "${command}" → workflow "${wf.name}"`);
22347
+ log13.info(`[${PLUGIN_NAME21}] dispatch "${command}" → workflow "${wf.name}"`);
23309
22348
  try {
23310
22349
  const result = await run(wf, {
23311
22350
  mode: ctx.adapter ? "real" : "dry_run",
23312
22351
  autonomy: ctx.autonomy ?? "semi",
23313
22352
  adapter: ctx.adapter
23314
22353
  });
23315
- log14.info(`[${PLUGIN_NAME24}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
22354
+ log13.info(`[${PLUGIN_NAME21}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
23316
22355
  steps: result.plan.steps.length,
23317
22356
  results: result.results.length
23318
22357
  });
23319
22358
  return result;
23320
22359
  } catch (err) {
23321
- log14.error(`[${PLUGIN_NAME24}] workflow "${wf.name}" 执行失败`, {
22360
+ log13.error(`[${PLUGIN_NAME21}] workflow "${wf.name}" 执行失败`, {
23322
22361
  error: err instanceof Error ? err.message : String(err)
23323
22362
  });
23324
22363
  throw err;
@@ -23327,15 +22366,15 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
23327
22366
  var workflowEngineServer = async (ctx) => {
23328
22367
  const directory = ctx.directory ?? process.cwd();
23329
22368
  const workflowsDir = path27.join(directory, "workflows");
23330
- ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME24}] preload workflows failed`, {
22369
+ ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME21}] preload workflows failed`, {
23331
22370
  error: err instanceof Error ? err.message : String(err)
23332
22371
  }));
23333
- logLifecycle(PLUGIN_NAME24, "activate", { directory, workflowsDir });
22372
+ logLifecycle(PLUGIN_NAME21, "activate", { directory, workflowsDir });
23334
22373
  return {
23335
22374
  "command.execute.before": async (input, output) => {
23336
- await safeAsync(PLUGIN_NAME24, "command.execute.before", async () => {
22375
+ await safeAsync(PLUGIN_NAME21, "command.execute.before", async () => {
23337
22376
  const cmd = input.command.startsWith("/") ? input.command : `/${input.command}`;
23338
- safeWriteLog(PLUGIN_NAME24, {
22377
+ safeWriteLog(PLUGIN_NAME21, {
23339
22378
  hook: "command.execute.before",
23340
22379
  command: cmd,
23341
22380
  sessionID: input.sessionID
@@ -23350,14 +22389,14 @@ var workflowEngineServer = async (ctx) => {
23350
22389
  });
23351
22390
  },
23352
22391
  "chat.message": async (input, output) => {
23353
- await safeAsync(PLUGIN_NAME24, "chat.message", async () => {
22392
+ await safeAsync(PLUGIN_NAME21, "chat.message", async () => {
23354
22393
  const text = extractUserText(output).trim();
23355
22394
  if (!text.startsWith("/"))
23356
22395
  return;
23357
22396
  const cmd = text.split(/\s+/)[0];
23358
22397
  if (!cmd)
23359
22398
  return;
23360
- safeWriteLog(PLUGIN_NAME24, {
22399
+ safeWriteLog(PLUGIN_NAME21, {
23361
22400
  hook: "chat.message",
23362
22401
  command: cmd,
23363
22402
  sessionID: input.sessionID
@@ -23367,12 +22406,12 @@ var workflowEngineServer = async (ctx) => {
23367
22406
  }
23368
22407
  };
23369
22408
  };
23370
- var handler24 = workflowEngineServer;
22409
+ var handler21 = workflowEngineServer;
23371
22410
 
23372
22411
  // plugins/session-worktree-guard.ts
23373
22412
  import path28 from "node:path";
23374
- var PLUGIN_NAME25 = "session-worktree-guard";
23375
- logLifecycle(PLUGIN_NAME25, "import", {});
22413
+ var PLUGIN_NAME22 = "session-worktree-guard";
22414
+ logLifecycle(PLUGIN_NAME22, "import", {});
23376
22415
  var WRITE_INTENT_RE = />(?![=&])(?!\s*\/dev\/(?:null|stdout|stderr|fd\/\d+)\b)|\btee\b|\brm\b|\bmv\b|\bcp\b|\bmkdir\b|\btouch\b|\bchmod\b|\bchown\b|\bln\b/;
23377
22416
  var READ_ONLY_COMMANDS = /^\s*(?:ls|cat|head|tail|grep|rg|find|fd|wc|stat|file|which|whereis|echo|pwd|cd|pushd|popd|env|printenv|type|less|more|sort|uniq|awk|tr|cut|jq|date|whoami|id|uname|node|npx|tsc|diff|python3?|git(?:\s+-C\s+\S+)?\s+(?:log|show|diff|status|branch|tag|remote|config\s+--get|rev-parse|rev-list|ls-files|ls-tree|cat-file|describe|reflog|blame|shortlog|name-rev|symbolic-ref|merge-base|worktree\s+list|stash\s+list|stash\s+show))\b/;
23378
22417
  var SIDE_EFFECT_TOKEN_RE = />(?![=&])(?!\s*\/dev\/(?:null|stdout|stderr|fd\/\d+)\b)|\|\s*tee\b|\btee\b/;
@@ -23395,11 +22434,11 @@ var INTERPRETER_WRITE_RES = [
23395
22434
  function stripCommitMessageArgs(command) {
23396
22435
  return command.replace(/(?:^|\s)(?:-m|--message|-F|--file)(?:=|\s+)("[^"]*"|'[^']*')/g, " ");
23397
22436
  }
23398
- function escapeRegex2(s) {
22437
+ function escapeRegex(s) {
23399
22438
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
23400
22439
  }
23401
22440
  function buildGitVcsWriteRegex(mainRoot) {
23402
- const esc = escapeRegex2(mainRoot);
22441
+ const esc = escapeRegex(mainRoot);
23403
22442
  return new RegExp(`git\\b[^\\n]*(?:-C\\s+|--work-tree[=\\s])${esc}`);
23404
22443
  }
23405
22444
  var WRITE_TOOLS = new Set(["write", "edit", "ast_edit"]);
@@ -23514,7 +22553,7 @@ function commandContainsMainRoot(command, mainRoot) {
23514
22553
  const prefix = mainRoot.endsWith("/") ? mainRoot : mainRoot + "/";
23515
22554
  if (command.includes(prefix))
23516
22555
  return true;
23517
- const re = new RegExp(`${escapeRegex2(mainRoot)}(?=[\\s'"\`)]|$)`);
22556
+ const re = new RegExp(`${escapeRegex(mainRoot)}(?=[\\s'"\`)]|$)`);
23518
22557
  return re.test(command);
23519
22558
  }
23520
22559
  function commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePath) {
@@ -23530,7 +22569,7 @@ function commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePat
23530
22569
  }
23531
22570
  const wtRoot = worktreesRoot(mainRoot);
23532
22571
  const wtRootPrefix = wtRoot + path28.sep;
23533
- const escapedWtRootPrefix = escapeRegex2(wtRootPrefix);
22572
+ const escapedWtRootPrefix = escapeRegex(wtRootPrefix);
23534
22573
  const wtPathPattern = escapedWtRootPrefix + `[^\\s'"\\x60)]*`;
23535
22574
  const allWorktreePathsReForEscape = new RegExp(wtPathPattern, "g");
23536
22575
  const allWorktreePathsReForReplace = new RegExp(wtPathPattern, "g");
@@ -23589,7 +22628,7 @@ function collectWritePaths(toolName, argsObj, worktreeRoot) {
23589
22628
  out.push(rel);
23590
22629
  return out;
23591
22630
  }
23592
- var log14 = makePluginLogger(PLUGIN_NAME25);
22631
+ var log13 = makePluginLogger(PLUGIN_NAME22);
23593
22632
  function resolveMainRoot2(rawDir) {
23594
22633
  const worktreeMarker = "/.git/codeforge-worktrees/";
23595
22634
  const idx = rawDir.indexOf(worktreeMarker);
@@ -23601,14 +22640,14 @@ function resolveMainRoot2(rawDir) {
23601
22640
  var sessionWorktreeGuardPlugin = async (ctx) => {
23602
22641
  const disableEnv = process.env["CODEFORGE_DISABLE_WORKTREE_GUARD"];
23603
22642
  if (disableEnv === "1" || disableEnv === "true" || disableEnv === "yes") {
23604
- log14.warn("[guard] CODEFORGE_DISABLE_WORKTREE_GUARD 已启用,session-worktree-guard 全部 hook 跳过;" + "本次 opencode 会话所有写操作将直接落到主工作区(失去隔离保护)", { env: disableEnv });
23605
- safeWriteLog(PLUGIN_NAME25, {
22643
+ log13.warn("[guard] CODEFORGE_DISABLE_WORKTREE_GUARD 已启用,session-worktree-guard 全部 hook 跳过;" + "本次 opencode 会话所有写操作将直接落到主工作区(失去隔离保护)", { env: disableEnv });
22644
+ safeWriteLog(PLUGIN_NAME22, {
23606
22645
  hook: "activate",
23607
22646
  action: "skip",
23608
22647
  source: "disable-env",
23609
22648
  env_value: disableEnv
23610
22649
  });
23611
- logLifecycle(PLUGIN_NAME25, "activate", { disabled_by_env: true });
22650
+ logLifecycle(PLUGIN_NAME22, "activate", { disabled_by_env: true });
23612
22651
  return {};
23613
22652
  }
23614
22653
  const mainRoot = resolveMainRoot2(ctx.directory ?? process.cwd());
@@ -23616,13 +22655,13 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23616
22655
  try {
23617
22656
  policyCfg = await loadPolicy(mainRoot);
23618
22657
  } catch (err) {
23619
- log14.warn("loadPolicy failed (class E skipped)", {
22658
+ log13.warn("loadPolicy failed (class E skipped)", {
23620
22659
  mainRoot,
23621
22660
  error: err instanceof Error ? err.message : String(err)
23622
22661
  });
23623
22662
  }
23624
22663
  const perAgentEnabled = !!policyCfg.per_agent && Object.keys(policyCfg.per_agent).length > 0;
23625
- logLifecycle(PLUGIN_NAME25, "activate", {
22664
+ logLifecycle(PLUGIN_NAME22, "activate", {
23626
22665
  mainRoot,
23627
22666
  CODEFORGE_SESSION_ID: process.env["CODEFORGE_SESSION_ID"] ?? "(not set)"
23628
22667
  });
@@ -23633,12 +22672,12 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23633
22672
  const isWrite = isWriteOperation(input.tool, output.args ?? {}, mainRoot);
23634
22673
  if (isWrite && !_sessionIdMissingWarned) {
23635
22674
  _sessionIdMissingWarned = true;
23636
- log14.warn("[guard] sessionID 缺失,无法绑定 worktree;本会话所有写操作将落到主工作区(仅本进程内 warn 一次)。" + "排查:grep no-session-id ~/.cache/codeforge/plugins.log", {
22675
+ log13.warn("[guard] sessionID 缺失,无法绑定 worktree;本会话所有写操作将落到主工作区(仅本进程内 warn 一次)。" + "排查:grep no-session-id ~/.cache/codeforge/plugins.log", {
23637
22676
  tool: input.tool,
23638
22677
  opencode_version_hint: "需 opencode >= 0.x 才会在 tool.execute.before 注入 input.sessionID"
23639
22678
  });
23640
22679
  }
23641
- safeWriteLog(PLUGIN_NAME25, {
22680
+ safeWriteLog(PLUGIN_NAME22, {
23642
22681
  hook: "tool.execute.before",
23643
22682
  tool: input.tool,
23644
22683
  action: "skip",
@@ -23648,14 +22687,14 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23648
22687
  return;
23649
22688
  }
23650
22689
  let denied;
23651
- await safeAsync(PLUGIN_NAME25, "tool.execute.before", async () => {
22690
+ await safeAsync(PLUGIN_NAME22, "tool.execute.before", async () => {
23652
22691
  const toolName = input.tool;
23653
22692
  const argsObj = output.args ?? {};
23654
22693
  let entry = null;
23655
22694
  try {
23656
22695
  entry = await getSessionWorktree(sessionId, mainRoot);
23657
22696
  } catch (err) {
23658
- log14.warn(`getSessionWorktree failed (跳过本次检查)`, {
22697
+ log13.warn(`getSessionWorktree failed (跳过本次检查)`, {
23659
22698
  sessionId,
23660
22699
  mainRoot,
23661
22700
  error: err instanceof Error ? err.message : String(err)
@@ -23672,8 +22711,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23672
22711
  const parentEntry = await getSessionWorktree(parentId, mainRoot);
23673
22712
  if (parentEntry && parentEntry.status === "active") {
23674
22713
  entry = parentEntry;
23675
- log14.debug?.(`[child-inherit] session ${sessionId} 继承父 ${parentId} 的 worktree`, { parentSessionId: parentId, worktreePath: parentEntry.worktreePath });
23676
- safeWriteLog(PLUGIN_NAME25, {
22714
+ log13.debug?.(`[child-inherit] session ${sessionId} 继承父 ${parentId} 的 worktree`, { parentSessionId: parentId, worktreePath: parentEntry.worktreePath });
22715
+ safeWriteLog(PLUGIN_NAME22, {
23677
22716
  hook: "tool.execute.before",
23678
22717
  tool: toolName,
23679
22718
  sessionID: input.sessionID,
@@ -23685,14 +22724,14 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23685
22724
  }
23686
22725
  }
23687
22726
  } catch (lookupErr) {
23688
- log14.debug?.("[child-inherit] lookupParentSessionId 抛错(已隔离,退回 lazy-bind)", { error: lookupErr instanceof Error ? lookupErr.message : String(lookupErr) });
22727
+ log13.debug?.("[child-inherit] lookupParentSessionId 抛错(已隔离,退回 lazy-bind)", { error: lookupErr instanceof Error ? lookupErr.message : String(lookupErr) });
23689
22728
  }
23690
22729
  }
23691
22730
  if (!entry) {
23692
22731
  try {
23693
22732
  entry = await bindSessionWorktree({ sessionId, mainRoot });
23694
- log14.info(`[lazy-bind] auto-created worktree for session ${sessionId}`, { branch: entry.branch, path: entry.worktreePath });
23695
- safeWriteLog(PLUGIN_NAME25, {
22733
+ log13.info(`[lazy-bind] auto-created worktree for session ${sessionId}`, { branch: entry.branch, path: entry.worktreePath });
22734
+ safeWriteLog(PLUGIN_NAME22, {
23696
22735
  hook: "tool.execute.before",
23697
22736
  tool: toolName,
23698
22737
  sessionID: input.sessionID,
@@ -23711,13 +22750,13 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23711
22750
  alreadyNotified
23712
22751
  });
23713
22752
  _bindFailNotified.add(sessionId);
23714
- log14.warn(`[lazy-bind] DENY (bind failed)`, {
22753
+ log13.warn(`[lazy-bind] DENY (bind failed)`, {
23715
22754
  sessionId,
23716
22755
  tool: toolName,
23717
22756
  error: errMsg,
23718
22757
  throttled: alreadyNotified
23719
22758
  });
23720
- safeWriteLog(PLUGIN_NAME25, {
22759
+ safeWriteLog(PLUGIN_NAME22, {
23721
22760
  hook: "tool.execute.before",
23722
22761
  tool: toolName,
23723
22762
  sessionID: input.sessionID,
@@ -23740,7 +22779,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23740
22779
  sessionId: entry.sessionId,
23741
22780
  mainRoot
23742
22781
  }).catch((err) => {
23743
- log14.warn("touchEntryUpdatedAt 失败 (已忽略)", {
22782
+ log13.warn("touchEntryUpdatedAt 失败 (已忽略)", {
23744
22783
  sessionId: entry?.sessionId,
23745
22784
  error: err instanceof Error ? err.message : String(err)
23746
22785
  });
@@ -23753,13 +22792,13 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23753
22792
  const caller = input.agent;
23754
22793
  if (caller !== undefined && caller !== "codeforge") {
23755
22794
  const reason = `[session-worktree-guard] DENIED: session_merge action=merge 仅 codeforge orchestrator 或用户可调;当前 caller=${caller}`;
23756
- log14.warn(reason, {
22795
+ log13.warn(reason, {
23757
22796
  sessionId,
23758
22797
  tool: toolName,
23759
22798
  action,
23760
22799
  caller
23761
22800
  });
23762
- safeWriteLog(PLUGIN_NAME25, {
22801
+ safeWriteLog(PLUGIN_NAME22, {
23763
22802
  hook: "tool.execute.before",
23764
22803
  tool: toolName,
23765
22804
  sessionID: input.sessionID,
@@ -23774,15 +22813,15 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23774
22813
  }
23775
22814
  }
23776
22815
  if (perAgentEnabled && isWriteOperation(toolName, argsObj, mainRoot)) {
23777
- const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
22816
+ const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log13);
23778
22817
  if (caller === null) {
23779
22818
  const reason = `[session-worktree-guard] DENIED: per-agent ACL 启用但 agent 解析失败 ` + `(L1 input.agent 缺, L2a chat-agent-cache 无, L2 IPC 反查失败) — fail-closed 拒绝写 ${toolName} ` + `(ADR:discover-write-permission-acl)`;
23780
- log14.warn(reason, {
22819
+ log13.warn(reason, {
23781
22820
  sessionId,
23782
22821
  tool: toolName,
23783
22822
  source: "per-agent-acl-fail-closed"
23784
22823
  });
23785
- safeWriteLog(PLUGIN_NAME25, {
22824
+ safeWriteLog(PLUGIN_NAME22, {
23786
22825
  hook: "tool.execute.before",
23787
22826
  tool: toolName,
23788
22827
  sessionID: input.sessionID,
@@ -23800,14 +22839,14 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23800
22839
  const dec = checkFileAccess(agentAcl, relPath, "write");
23801
22840
  if (dec.action === "deny") {
23802
22841
  const reason = `[session-worktree-guard] DENIED: agent='${caller}' 写路径不在 ACL 白名单 — ` + `${relPath} (${dec.reason})`;
23803
- log14.warn(reason, {
22842
+ log13.warn(reason, {
23804
22843
  sessionId,
23805
22844
  tool: toolName,
23806
22845
  caller,
23807
22846
  path: relPath,
23808
22847
  acl_decision: dec
23809
22848
  });
23810
- safeWriteLog(PLUGIN_NAME25, {
22849
+ safeWriteLog(PLUGIN_NAME22, {
23811
22850
  hook: "tool.execute.before",
23812
22851
  tool: toolName,
23813
22852
  sessionID: input.sessionID,
@@ -23845,13 +22884,13 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23845
22884
  const reasonBase = `[session-worktree-guard] DENIED: 当前 session 要求先调用 plan_read(plan_id="${entry.requiredPlanId}") 再执行写操作`;
23846
22885
  const reason = inherited ? `${reasonBase}
23847
22886
  [gate-deny] child session=${sessionId} 继承父 entry 但父 session planReadOk=false,父 session=${entry.sessionId} 需先调 plan_read(plan_id="${entry.requiredPlanId}")` : reasonBase;
23848
- log14.warn(reason, {
22887
+ log13.warn(reason, {
23849
22888
  tool: toolName,
23850
22889
  sessionId,
23851
22890
  requiredPlanId: entry.requiredPlanId,
23852
22891
  inheritedFromParent: inherited ? entry.sessionId : undefined
23853
22892
  });
23854
- safeWriteLog(PLUGIN_NAME25, {
22893
+ safeWriteLog(PLUGIN_NAME22, {
23855
22894
  hook: "tool.execute.before",
23856
22895
  tool: toolName,
23857
22896
  sessionID: input.sessionID,
@@ -23866,10 +22905,10 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23866
22905
  if (toolName === "bash") {
23867
22906
  const command = argsObj["command"];
23868
22907
  if (typeof command === "string" && commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePath) && detectBashWriteIntent(command, mainRoot)) {
23869
- const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
22908
+ const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log13);
23870
22909
  if (caller !== null && CLASS_B_CALLER_WHITELIST.has(caller)) {
23871
- log14.debug?.(`[class-b-whitelist] allow caller=${caller}`, { sessionId, tool: toolName, command: command.slice(0, 200) });
23872
- safeWriteLog(PLUGIN_NAME25, {
22910
+ log13.debug?.(`[class-b-whitelist] allow caller=${caller}`, { sessionId, tool: toolName, command: command.slice(0, 200) });
22911
+ safeWriteLog(PLUGIN_NAME22, {
23873
22912
  hook: "tool.execute.before",
23874
22913
  tool: toolName,
23875
22914
  sessionID: input.sessionID,
@@ -23882,8 +22921,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23882
22921
  const callerTag = caller === null ? "unresolved" : caller;
23883
22922
  const snippet = command.length > 60 ? command.slice(0, 60) + "…" : command;
23884
22923
  const reason = `[session-worktree-guard] DENIED: bash.command 含主仓绝对路径写操作 (${snippet}) [caller=${callerTag}],请在当前 session worktree (${worktreePath}) 内操作`;
23885
- log14.warn(reason, { sessionId, caller: callerTag, command: command.slice(0, 200) });
23886
- safeWriteLog(PLUGIN_NAME25, {
22924
+ log13.warn(reason, { sessionId, caller: callerTag, command: command.slice(0, 200) });
22925
+ safeWriteLog(PLUGIN_NAME22, {
23887
22926
  hook: "tool.execute.before",
23888
22927
  tool: toolName,
23889
22928
  sessionID: input.sessionID,
@@ -23902,8 +22941,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23902
22941
  if (typeof filePath === "string") {
23903
22942
  const newPath = rewritePath(filePath, mainRoot, worktreePath);
23904
22943
  if (newPath !== null) {
23905
- log14.info(`rewrote ${toolName}.filePath: ${filePath} → ${newPath}`);
23906
- safeWriteLog(PLUGIN_NAME25, {
22944
+ log13.info(`rewrote ${toolName}.filePath: ${filePath} → ${newPath}`);
22945
+ safeWriteLog(PLUGIN_NAME22, {
23907
22946
  hook: "tool.execute.before",
23908
22947
  tool: toolName,
23909
22948
  field: "filePath",
@@ -23920,8 +22959,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23920
22959
  if (typeof target === "string") {
23921
22960
  const newTarget = rewritePath(target, mainRoot, worktreePath);
23922
22961
  if (newTarget !== null) {
23923
- log14.info(`rewrote ast_edit.target: ${target} → ${newTarget}`);
23924
- safeWriteLog(PLUGIN_NAME25, {
22962
+ log13.info(`rewrote ast_edit.target: ${target} → ${newTarget}`);
22963
+ safeWriteLog(PLUGIN_NAME22, {
23925
22964
  hook: "tool.execute.before",
23926
22965
  tool: toolName,
23927
22966
  field: "target",
@@ -23936,8 +22975,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23936
22975
  if (typeof root === "string") {
23937
22976
  const newRoot = rewritePath(root, mainRoot, worktreePath);
23938
22977
  if (newRoot !== null) {
23939
- log14.info(`rewrote ast_edit.root: ${root} → ${newRoot}`);
23940
- safeWriteLog(PLUGIN_NAME25, {
22978
+ log13.info(`rewrote ast_edit.root: ${root} → ${newRoot}`);
22979
+ safeWriteLog(PLUGIN_NAME22, {
23941
22980
  hook: "tool.execute.before",
23942
22981
  tool: toolName,
23943
22982
  field: "root",
@@ -23954,8 +22993,8 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23954
22993
  if (typeof workdir === "string") {
23955
22994
  const newWorkdir = rewritePath(workdir, mainRoot, worktreePath);
23956
22995
  if (newWorkdir !== null) {
23957
- log14.info(`rewrote bash.workdir: ${workdir} → ${newWorkdir}`);
23958
- safeWriteLog(PLUGIN_NAME25, {
22996
+ log13.info(`rewrote bash.workdir: ${workdir} → ${newWorkdir}`);
22997
+ safeWriteLog(PLUGIN_NAME22, {
23959
22998
  hook: "tool.execute.before",
23960
22999
  tool: toolName,
23961
23000
  field: "workdir",
@@ -23973,7 +23012,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23973
23012
  }
23974
23013
  };
23975
23014
  };
23976
- var handler25 = sessionWorktreeGuardPlugin;
23015
+ var handler22 = sessionWorktreeGuardPlugin;
23977
23016
 
23978
23017
  // lib/opencode-session-probe.ts
23979
23018
  import * as path29 from "node:path";
@@ -24073,8 +23112,8 @@ function createSessionProbe(opts = {}) {
24073
23112
  }
24074
23113
 
24075
23114
  // plugins/worktree-lifecycle.ts
24076
- var PLUGIN_NAME26 = "worktree-lifecycle";
24077
- logLifecycle(PLUGIN_NAME26, "import", {});
23115
+ var PLUGIN_NAME23 = "worktree-lifecycle";
23116
+ logLifecycle(PLUGIN_NAME23, "import", {});
24078
23117
  var IDLE_TOAST_THROTTLE_MS = 60 * 60000;
24079
23118
  var IDLE_TOAST_DURATION_MS = 8000;
24080
23119
  var IDLE_TOAST_REMINDER_INTERVAL_MS = 30 * 60000;
@@ -24083,10 +23122,10 @@ var lastIdleToastAt = new Map;
24083
23122
  var pruneRunning = false;
24084
23123
  var _pruneTimer;
24085
23124
  var _probe = null;
24086
- var log15 = makePluginLogger(PLUGIN_NAME26);
23125
+ var log14 = makePluginLogger(PLUGIN_NAME23);
24087
23126
  var worktreeLifecyclePlugin = async (ctx) => {
24088
23127
  const mainRoot = ctx.directory;
24089
- logLifecycle(PLUGIN_NAME26, "activate", {
23128
+ logLifecycle(PLUGIN_NAME23, "activate", {
24090
23129
  mainRoot: mainRoot ?? "(not set)",
24091
23130
  idle_threshold_ms: IDLE_TOAST_THROTTLE_MS
24092
23131
  });
@@ -24101,13 +23140,13 @@ var worktreeLifecyclePlugin = async (ctx) => {
24101
23140
  }
24102
23141
  _probe = createSessionProbe();
24103
23142
  setImmediate(() => {
24104
- safeAsync(PLUGIN_NAME26, "activate.pruneOrphan", async () => {
23143
+ safeAsync(PLUGIN_NAME23, "activate.pruneOrphan", async () => {
24105
23144
  const result = await pruneOrphanWorktrees(mainRoot, {
24106
23145
  isSessionAlive: _probe.isSessionAlive
24107
23146
  });
24108
23147
  if (result.cleaned.length > 0 || result.failed.length > 0) {
24109
- log15.info(`[pruneOrphan] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
24110
- safeWriteLog(PLUGIN_NAME26, {
23148
+ log14.info(`[pruneOrphan] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
23149
+ safeWriteLog(PLUGIN_NAME23, {
24111
23150
  hook: "activate.pruneOrphan",
24112
23151
  cleaned: result.cleaned,
24113
23152
  failed: result.failed,
@@ -24120,7 +23159,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
24120
23159
  clearInterval(_pruneTimer);
24121
23160
  _pruneTimer = setInterval(() => {
24122
23161
  if (pruneRunning) {
24123
- safeWriteLog(PLUGIN_NAME26, {
23162
+ safeWriteLog(PLUGIN_NAME23, {
24124
23163
  hook: "interval.pruneOrphan",
24125
23164
  action: "skip",
24126
23165
  reason: "previous prune still running"
@@ -24128,14 +23167,14 @@ var worktreeLifecyclePlugin = async (ctx) => {
24128
23167
  return;
24129
23168
  }
24130
23169
  pruneRunning = true;
24131
- safeAsync(PLUGIN_NAME26, "interval.pruneOrphan", async () => {
23170
+ safeAsync(PLUGIN_NAME23, "interval.pruneOrphan", async () => {
24132
23171
  try {
24133
23172
  const result = await pruneOrphanWorktrees(mainRoot, {
24134
23173
  isSessionAlive: _probe.isSessionAlive
24135
23174
  });
24136
23175
  if (result.cleaned.length > 0 || result.failed.length > 0) {
24137
- log15.info(`[pruneOrphan interval] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
24138
- safeWriteLog(PLUGIN_NAME26, {
23176
+ log14.info(`[pruneOrphan interval] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
23177
+ safeWriteLog(PLUGIN_NAME23, {
24139
23178
  hook: "interval.pruneOrphan",
24140
23179
  cleaned: result.cleaned,
24141
23180
  failed: result.failed,
@@ -24150,14 +23189,14 @@ var worktreeLifecyclePlugin = async (ctx) => {
24150
23189
  _pruneTimer.unref();
24151
23190
  return {
24152
23191
  event: async ({ event }) => {
24153
- await safeAsync(PLUGIN_NAME26, "event", async () => {
23192
+ await safeAsync(PLUGIN_NAME23, "event", async () => {
24154
23193
  const ended = extractEndedSessionID(event);
24155
23194
  if (!ended)
24156
23195
  return;
24157
23196
  if (ended.type === "session.deleted") {
24158
23197
  const entry = await getSessionWorktree(ended.sessionID, mainRoot);
24159
23198
  if (!entry || entry.status !== "active") {
24160
- safeWriteLog(PLUGIN_NAME26, {
23199
+ safeWriteLog(PLUGIN_NAME23, {
24161
23200
  hook: "event",
24162
23201
  type: ended.type,
24163
23202
  sessionID: ended.sessionID,
@@ -24172,7 +23211,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
24172
23211
  const fastDirty = await isWorktreeDirty(entry.worktreePath);
24173
23212
  if (!fastDirty) {
24174
23213
  await discardSession({ sessionId: ended.sessionID, mainRoot });
24175
- safeWriteLog(PLUGIN_NAME26, {
23214
+ safeWriteLog(PLUGIN_NAME23, {
24176
23215
  hook: "event",
24177
23216
  type: ended.type,
24178
23217
  sessionID: ended.sessionID,
@@ -24185,7 +23224,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
24185
23224
  }
24186
23225
  }
24187
23226
  } catch (err) {
24188
- log15.warn(`[lifecycle] empty-worktree fast-path 检测失败 (回退到常规路径)`, {
23227
+ log14.warn(`[lifecycle] empty-worktree fast-path 检测失败 (回退到常规路径)`, {
24189
23228
  sessionId: ended.sessionID,
24190
23229
  error: err instanceof Error ? err.message : String(err)
24191
23230
  });
@@ -24200,14 +23239,14 @@ var worktreeLifecyclePlugin = async (ctx) => {
24200
23239
  });
24201
23240
  }
24202
23241
  } catch (err) {
24203
- log15.warn(`[lifecycle] checkpointCommit failed (继续 discard)`, {
23242
+ log14.warn(`[lifecycle] checkpointCommit failed (继续 discard)`, {
24204
23243
  sessionId: ended.sessionID,
24205
23244
  error: err instanceof Error ? err.message : String(err)
24206
23245
  });
24207
23246
  }
24208
23247
  try {
24209
23248
  await discardSession({ sessionId: ended.sessionID, mainRoot });
24210
- safeWriteLog(PLUGIN_NAME26, {
23249
+ safeWriteLog(PLUGIN_NAME23, {
24211
23250
  hook: "event",
24212
23251
  type: ended.type,
24213
23252
  sessionID: ended.sessionID,
@@ -24217,7 +23256,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
24217
23256
  worktreePath: entry.worktreePath
24218
23257
  });
24219
23258
  } catch (err) {
24220
- log15.warn(`[lifecycle] discardSession failed`, {
23259
+ log14.warn(`[lifecycle] discardSession failed`, {
24221
23260
  sessionId: ended.sessionID,
24222
23261
  error: err instanceof Error ? err.message : String(err)
24223
23262
  });
@@ -24240,8 +23279,8 @@ var worktreeLifecyclePlugin = async (ctx) => {
24240
23279
  lastIdleToastAt.set(ended.sessionID, now);
24241
23280
  const idleMin = Math.round(idleMs / 60000);
24242
23281
  const msg = `\uD83D\uDCA4 Session ${ended.sessionID.slice(0, 8)} worktree 已空闲 ${idleMin}min,` + `用 /merge 收尾或 /discard-session 放弃`;
24243
- const sent = await showToast2(client, { message: msg, variant: "default", duration: IDLE_TOAST_DURATION_MS, title: "CodeForge" }, log15);
24244
- safeWriteLog(PLUGIN_NAME26, {
23282
+ const sent = await showToast2(client, { message: msg, variant: "default", duration: IDLE_TOAST_DURATION_MS, title: "CodeForge" }, log14);
23283
+ safeWriteLog(PLUGIN_NAME23, {
24245
23284
  hook: "event",
24246
23285
  type: ended.type,
24247
23286
  sessionID: ended.sessionID,
@@ -24254,39 +23293,36 @@ var worktreeLifecyclePlugin = async (ctx) => {
24254
23293
  }
24255
23294
  };
24256
23295
  };
24257
- var handler26 = worktreeLifecyclePlugin;
23296
+ var handler23 = worktreeLifecyclePlugin;
24258
23297
 
24259
23298
  // src/index.ts
24260
23299
  var PLUGIN_ID = "codeforge";
24261
- var log16 = makePluginLogger(PLUGIN_ID);
23300
+ var log15 = makePluginLogger(PLUGIN_ID);
24262
23301
  logLifecycle(PLUGIN_ID, "import", { entry: "src/index.ts" });
24263
23302
  var HANDLERS = [
24264
23303
  { name: "agent-router", init: handler },
24265
23304
  { name: "arena-orchestrator", init: handler2 },
24266
23305
  { name: "auto-commit", init: handler3 },
24267
- { name: "auto-learning", init: handler4 },
24268
- { name: "channels", init: handler5 },
24269
- { name: "chat-agent-cache", init: handler6 },
24270
- { name: "codeforge-tools", init: handler8 },
24271
- { name: "discover-spec-suggest", init: handler9 },
24272
- { name: "kh-auto-context", init: handler10 },
24273
- { name: "kh-reminder", init: handler11 },
24274
- { name: "memories-context", init: handler12 },
24275
- { name: "model-fallback", init: handler13 },
24276
- { name: "parallel-tool-nudge", init: handler16 },
24277
- { name: "pwsh-utf8", init: handler17 },
24278
- { name: "session-recovery", init: handler18 },
24279
- { name: "subtask-heartbeat", init: handler14 },
24280
- { name: "subtasks", init: handler19 },
24281
- { name: "parallel-status", init: handler15 },
24282
- { name: "terminal-monitor", init: handler20 },
24283
- { name: "token-manager", init: handler21 },
24284
- { name: "tool-heartbeat", init: handler7 },
24285
- { name: "tool-policy", init: handler22 },
24286
- { name: "update-checker", init: handler23 },
24287
- { name: "workflow-engine", init: handler24 },
24288
- { name: "session-worktree-guard", init: handler25 },
24289
- { name: "worktree-lifecycle", init: handler26 }
23306
+ { name: "channels", init: handler4 },
23307
+ { name: "chat-agent-cache", init: handler5 },
23308
+ { name: "codeforge-tools", init: handler7 },
23309
+ { name: "discover-spec-suggest", init: handler8 },
23310
+ { name: "memories-context", init: handler9 },
23311
+ { name: "model-fallback", init: handler10 },
23312
+ { name: "parallel-tool-nudge", init: handler13 },
23313
+ { name: "pwsh-utf8", init: handler14 },
23314
+ { name: "session-recovery", init: handler15 },
23315
+ { name: "subtask-heartbeat", init: handler11 },
23316
+ { name: "subtasks", init: handler16 },
23317
+ { name: "parallel-status", init: handler12 },
23318
+ { name: "terminal-monitor", init: handler17 },
23319
+ { name: "token-manager", init: handler18 },
23320
+ { name: "tool-heartbeat", init: handler6 },
23321
+ { name: "tool-policy", init: handler19 },
23322
+ { name: "update-checker", init: handler20 },
23323
+ { name: "workflow-engine", init: handler21 },
23324
+ { name: "session-worktree-guard", init: handler22 },
23325
+ { name: "worktree-lifecycle", init: handler23 }
24290
23326
  ];
24291
23327
  function makeSerialHook(hookName, fns) {
24292
23328
  return async (input, output) => {
@@ -24296,7 +23332,7 @@ function makeSerialHook(hookName, fns) {
24296
23332
  } catch (err) {
24297
23333
  if (isDeniedError(err))
24298
23334
  throw err;
24299
- log16.warn(`[${PLUGIN_ID}] ${hookName} handler 异常(已隔离)`, {
23335
+ log15.warn(`[${PLUGIN_ID}] ${hookName} handler 异常(已隔离)`, {
24300
23336
  error: err instanceof Error ? err.message : String(err)
24301
23337
  });
24302
23338
  }
@@ -24309,7 +23345,7 @@ function createCodeforgeServer(opts) {
24309
23345
  const yieldResult = shouldYieldToLocalPlugin({ directory: input.directory });
24310
23346
  if (yieldResult.yield) {
24311
23347
  const msg = formatYieldLog(yieldResult);
24312
- log16.info(msg, { reason: yieldResult.reason, markerPath: yieldResult.markerPath });
23348
+ log15.info(msg, { reason: yieldResult.reason, markerPath: yieldResult.markerPath });
24313
23349
  logLifecycle(PLUGIN_ID, "activate", {
24314
23350
  yield_to_local: true,
24315
23351
  yield_reason: yieldResult.reason,
@@ -24327,7 +23363,7 @@ function createCodeforgeServer(opts) {
24327
23363
  if (r.status === "fulfilled" && r.value && typeof r.value === "object") {
24328
23364
  hooksList.push(r.value);
24329
23365
  } else if (r.status === "rejected") {
24330
- log16.warn(`[${PLUGIN_ID}] handler ${HANDLERS[i].name} init failed (隔离,其他 handler 继续)`, { error: r.reason instanceof Error ? r.reason.message : String(r.reason) });
23366
+ log15.warn(`[${PLUGIN_ID}] handler ${HANDLERS[i].name} init failed (隔离,其他 handler 继续)`, { error: r.reason instanceof Error ? r.reason.message : String(r.reason) });
24331
23367
  }
24332
23368
  });
24333
23369
  logLifecycle(PLUGIN_ID, "activate", {
@@ -24382,7 +23418,7 @@ function createCodeforgeServer(opts) {
24382
23418
  } catch (err) {
24383
23419
  if (isDeniedError(err))
24384
23420
  throw err;
24385
- log16.warn(`[${PLUGIN_ID}] event handler 异常(已隔离)`, {
23421
+ log15.warn(`[${PLUGIN_ID}] event handler 异常(已隔离)`, {
24386
23422
  error: err instanceof Error ? err.message : String(err)
24387
23423
  });
24388
23424
  }