@andyqiu/codeforge 0.3.9 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10769,6 +10769,14 @@ function applyAnchor(content, edit, eol, beforeHash) {
10769
10769
  if (!edit.anchor) {
10770
10770
  return { ok: false, reason: "invalid_input", message: "anchor 不能为空" };
10771
10771
  }
10772
+ if (edit.anchor.includes(`
10773
+ `)) {
10774
+ return {
10775
+ ok: false,
10776
+ reason: "invalid_input",
10777
+ message: "anchor 不能含换行符(仅支持单行匹配);多行改动请改用 pending_changes.stage 整文件暂存"
10778
+ };
10779
+ }
10772
10780
  const lines = splitLines(content);
10773
10781
  const hits = findAnchorLines(lines, edit.anchor, edit.regex === true);
10774
10782
  if (hits.length === 0) {
@@ -10971,6 +10979,7 @@ function finish(before, after, beforeHash, affected) {
10971
10979
  // tools/ast-edit.ts
10972
10980
  var description16 = [
10973
10981
  "AST 风格的精确文件编辑:anchor + 哈希校验,避免 LLM 「整文件重写」误改无关代码。",
10982
+ "**anchor 仅支持单行**:含 `\\n` 的多行 anchor 会被直接拒绝(reason=invalid_input);多行改动请改用 `pending_changes.stage` 整文件暂存。",
10974
10983
  "**何时调用**:",
10975
10984
  "- 需要在指定 anchor(行特征)后插入代码(hook 注入、import 添加)",
10976
10985
  "- 重命名一个标识符(rename_symbol,全文件词边界匹配)",
@@ -10978,6 +10987,7 @@ var description16 = [
10978
10987
  "**何时不需要**:",
10979
10988
  "- 整文件重写更合理(直接 pending-changes.stage 整文件)",
10980
10989
  "- 只是 1-2 个字符的 typo(用 edit 工具更直接)",
10990
+ "- 多行 anchor / 跨行匹配(请用 pending_changes.stage 整文件)",
10981
10991
  "**安全约束**:",
10982
10992
  "- 必须传 before_hash(如果文件已存在),不一致会被拒绝",
10983
10993
  "- 默认 auto_stage=true:结果直接进 pending-changes 等待人审,不立刻落盘"
@@ -10995,7 +11005,7 @@ var AnchorAction = Common.extend({
10995
11005
  "insert_after_anchor",
10996
11006
  "insert_before_anchor"
10997
11007
  ]),
10998
- anchor: z17.string().min(1).describe("anchor 文本子串或正则源"),
11008
+ anchor: z17.string().min(1).describe("anchor 文本子串或正则源(必须单行;含 \\n 会被拒绝,多行改动请用 pending_changes.stage)"),
10999
11009
  regex: z17.boolean().optional().describe("anchor 是否按 RegExp 解释,默认 false"),
11000
11010
  occurrence: z17.number().int().min(1).optional().describe("第几次匹配(1-based),默认 1;多次命中时必须显式指定"),
11001
11011
  payload: z17.string().describe("要写入的内容;引擎会按文件原 EOL 处理")
@@ -13517,76 +13527,146 @@ var codeforgeToolsServer = async (ctx) => {
13517
13527
  };
13518
13528
  var handler8 = codeforgeToolsServer;
13519
13529
 
13520
- // plugins/hello-world.ts
13521
- init_opencode_plugin_helpers();
13522
- var PLUGIN_NAME9 = "hello-world";
13523
- var PLUGIN_VERSION = "1.0.0";
13524
- try {
13525
- logLifecycle(PLUGIN_NAME9, "import", { version: PLUGIN_VERSION });
13526
- safeWriteLog(PLUGIN_NAME9, { phase: "import", version: PLUGIN_VERSION });
13527
- } catch (importErr) {
13528
- console.error(`[${PLUGIN_NAME9}] import lifecycle log failed:`, importErr);
13529
- }
13530
- var helloWorldServer = async (ctx) => {
13531
- logLifecycle(PLUGIN_NAME9, "activate", {
13532
- version: PLUGIN_VERSION,
13533
- directory: ctx.directory,
13534
- worktree: ctx.worktree,
13535
- projectId: ctx.project?.id
13536
- });
13537
- await safeAsync(PLUGIN_NAME9, "client.app.log", async () => {
13538
- await ctx.client.app.log({
13539
- body: {
13540
- service: PLUGIN_NAME9,
13541
- level: "info",
13542
- message: `${PLUGIN_NAME9} v${PLUGIN_VERSION} activated in ${ctx.directory}`
13543
- }
13530
+ // plugins/kh-auto-context.ts
13531
+ init_kh_client();
13532
+
13533
+ // lib/kh-shared-context.ts
13534
+ var DEFAULT_MAX_PER_SESSION = 20;
13535
+ var DEFAULT_TTL_MS = 5 * 60 * 1000;
13536
+
13537
+ class SessionScopedCache {
13538
+ cache = new Map;
13539
+ inflight = new Map;
13540
+ generation = new Map;
13541
+ ttlMs;
13542
+ maxPerSession;
13543
+ now;
13544
+ constructor(opts = {}) {
13545
+ this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
13546
+ this.maxPerSession = opts.maxPerSession ?? DEFAULT_MAX_PER_SESSION;
13547
+ this.now = opts.now ?? (() => Date.now());
13548
+ }
13549
+ async searchOrGet(args) {
13550
+ const { client, sessionId, query, limit, timeoutMs } = args;
13551
+ const queryHash = this.hashQuery(sessionId, query);
13552
+ const inflightKey = `${sessionId}::${queryHash}`;
13553
+ const sessionMap = this.cache.get(sessionId);
13554
+ if (sessionMap) {
13555
+ const entry = sessionMap.get(queryHash);
13556
+ if (entry && this.now() - entry.cachedAt < this.ttlMs) {
13557
+ sessionMap.delete(queryHash);
13558
+ sessionMap.set(queryHash, entry);
13559
+ return entry.result;
13560
+ }
13561
+ if (entry)
13562
+ sessionMap.delete(queryHash);
13563
+ }
13564
+ const pending = this.inflight.get(inflightKey);
13565
+ if (pending)
13566
+ return pending;
13567
+ const startGen = this.generation.get(sessionId) ?? 0;
13568
+ const promise = this.doSearchAndMaybeCache(client, sessionId, queryHash, query, limit, timeoutMs, startGen).finally(() => {
13569
+ this.inflight.delete(inflightKey);
13544
13570
  });
13545
- });
13546
- return {
13547
- event: async ({ event }) => {
13548
- await safeAsync(PLUGIN_NAME9, "event", async () => {
13549
- const e = event;
13550
- const propKeys = e.properties && typeof e.properties === "object" ? Object.keys(e.properties).slice(0, 10) : [];
13551
- safeWriteLog(PLUGIN_NAME9, {
13552
- hook: "event",
13553
- type: e.type ?? "unknown",
13554
- propKeys
13555
- });
13556
- });
13557
- },
13558
- "chat.message": async (input, output) => {
13559
- await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
13560
- const text = extractUserText(output);
13561
- safeWriteLog(PLUGIN_NAME9, {
13562
- hook: "chat.message",
13563
- sessionID: input.sessionID,
13564
- agent: input.agent,
13565
- messageID: input.messageID,
13566
- textPreview: text.slice(0, 80),
13567
- textLen: text.length,
13568
- partsCount: output.parts?.length ?? 0
13569
- });
13570
- });
13571
- },
13572
- "tool.execute.before": async (input, _output) => {
13573
- await safeAsync(PLUGIN_NAME9, "tool.execute.before", async () => {
13574
- safeWriteLog(PLUGIN_NAME9, {
13575
- hook: "tool.execute.before",
13576
- tool: input.tool,
13577
- sessionID: input.sessionID,
13578
- callID: input.callID
13579
- });
13571
+ this.inflight.set(inflightKey, promise);
13572
+ return promise;
13573
+ }
13574
+ onSessionEnd(sessionId) {
13575
+ this.generation.set(sessionId, (this.generation.get(sessionId) ?? 0) + 1);
13576
+ this.cache.delete(sessionId);
13577
+ const prefix = `${sessionId}::`;
13578
+ for (const key of this.inflight.keys()) {
13579
+ if (key.startsWith(prefix)) {
13580
+ this.inflight.delete(key);
13581
+ }
13582
+ }
13583
+ }
13584
+ _snapshot() {
13585
+ const perSession = {};
13586
+ let total = 0;
13587
+ for (const [sid, map] of this.cache.entries()) {
13588
+ perSession[sid] = map.size;
13589
+ total += map.size;
13590
+ }
13591
+ const generations = {};
13592
+ for (const [sid, g] of this.generation.entries()) {
13593
+ generations[sid] = g;
13594
+ }
13595
+ return {
13596
+ cacheSize: total,
13597
+ inflightSize: this.inflight.size,
13598
+ sessions: Array.from(this.cache.keys()),
13599
+ perSession,
13600
+ generations
13601
+ };
13602
+ }
13603
+ _reset() {
13604
+ this.cache.clear();
13605
+ this.inflight.clear();
13606
+ this.generation.clear();
13607
+ }
13608
+ hashQuery(sessionId, query) {
13609
+ return `${sessionId}::${query}`;
13610
+ }
13611
+ async doSearchAndMaybeCache(client, sessionId, queryHash, query, limit, timeoutMs, startGen) {
13612
+ const result = await this.doSearch(client, query, limit, timeoutMs);
13613
+ const currentGen = this.generation.get(sessionId) ?? 0;
13614
+ if (currentGen === startGen && result.ok) {
13615
+ this.writeCache(sessionId, queryHash, query, result);
13616
+ }
13617
+ return result;
13618
+ }
13619
+ async doSearch(client, query, limit, timeoutMs) {
13620
+ const callPromise = (async () => {
13621
+ try {
13622
+ return await client.search({ query, limit });
13623
+ } catch (err) {
13624
+ return {
13625
+ ok: false,
13626
+ reason: "kh_returned_error",
13627
+ message: err instanceof Error ? err.message : String(err)
13628
+ };
13629
+ }
13630
+ })();
13631
+ if (timeoutMs === undefined || timeoutMs <= 0) {
13632
+ return callPromise;
13633
+ }
13634
+ let timer = null;
13635
+ try {
13636
+ const racer = new Promise((res) => {
13637
+ timer = setTimeout(() => res({
13638
+ ok: false,
13639
+ reason: "kh_returned_error",
13640
+ message: `kh search timeout after ${timeoutMs}ms`
13641
+ }), timeoutMs);
13580
13642
  });
13643
+ return await Promise.race([callPromise, racer]);
13644
+ } finally {
13645
+ if (timer)
13646
+ clearTimeout(timer);
13581
13647
  }
13582
- };
13583
- };
13584
- var handler9 = helloWorldServer;
13648
+ }
13649
+ writeCache(sessionId, queryHash, query, result) {
13650
+ let sessionMap = this.cache.get(sessionId);
13651
+ if (!sessionMap) {
13652
+ sessionMap = new Map;
13653
+ this.cache.set(sessionId, sessionMap);
13654
+ }
13655
+ sessionMap.delete(queryHash);
13656
+ sessionMap.set(queryHash, { query, result, cachedAt: this.now() });
13657
+ while (sessionMap.size > this.maxPerSession) {
13658
+ const oldest = sessionMap.keys().next().value;
13659
+ if (oldest === undefined)
13660
+ break;
13661
+ sessionMap.delete(oldest);
13662
+ }
13663
+ }
13664
+ }
13665
+ var sharedKhCache = new SessionScopedCache;
13585
13666
 
13586
13667
  // plugins/kh-auto-context.ts
13587
- init_kh_client();
13588
13668
  init_opencode_plugin_helpers();
13589
- var PLUGIN_NAME10 = "kh-auto-context";
13669
+ var PLUGIN_NAME9 = "kh-auto-context";
13590
13670
  var INJECTION_MODE = "observe-only";
13591
13671
  function resolveInjectionMode(client) {
13592
13672
  return client.hasTransport() ? "system-injected" : "observe-only";
@@ -13685,7 +13765,7 @@ var CODEFORGE_CONSTRAINTS = [
13685
13765
  ];
13686
13766
  async function seedConstraints(client, log6) {
13687
13767
  if (!client.hasTransport()) {
13688
- log6?.debug?.(`[${PLUGIN_NAME10}] seedConstraints: transport 不可用,跳过`, {});
13768
+ log6?.debug?.(`[${PLUGIN_NAME9}] seedConstraints: transport 不可用,跳过`, {});
13689
13769
  return { ok: false, reason: "transport_unavailable" };
13690
13770
  }
13691
13771
  try {
@@ -13699,7 +13779,7 @@ async function seedConstraints(client, log6) {
13699
13779
  });
13700
13780
  if (result && typeof result === "object" && "ok" in result && result.ok === false) {
13701
13781
  const r = result;
13702
- log6?.warn(`[${PLUGIN_NAME10}] seedConstraints 降级`, {
13782
+ log6?.warn(`[${PLUGIN_NAME9}] seedConstraints 降级`, {
13703
13783
  reason: r.reason,
13704
13784
  message: r.message
13705
13785
  });
@@ -13709,11 +13789,11 @@ async function seedConstraints(client, log6) {
13709
13789
  message: r.message
13710
13790
  };
13711
13791
  }
13712
- log6?.info(`[${PLUGIN_NAME10}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
13792
+ log6?.info(`[${PLUGIN_NAME9}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
13713
13793
  return { ok: true, itemsWritten: CODEFORGE_CONSTRAINTS.length };
13714
13794
  } catch (err) {
13715
13795
  const message = err instanceof Error ? err.message : String(err);
13716
- log6?.warn(`[${PLUGIN_NAME10}] seedConstraints 失败(已静默)`, { error: message });
13796
+ log6?.warn(`[${PLUGIN_NAME9}] seedConstraints 失败(已静默)`, { error: message });
13717
13797
  return { ok: false, reason: "exception", message };
13718
13798
  }
13719
13799
  }
@@ -13728,7 +13808,7 @@ function createSystemInjectedHook(client, sessionId, log6) {
13728
13808
  });
13729
13809
  if (result && typeof result === "object" && "ok" in result && result.ok === false) {
13730
13810
  const r = result;
13731
- log6?.warn(`[${PLUGIN_NAME10}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
13811
+ log6?.warn(`[${PLUGIN_NAME9}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
13732
13812
  sessionId,
13733
13813
  section,
13734
13814
  preview: markdown.slice(0, 200),
@@ -13737,12 +13817,12 @@ function createSystemInjectedHook(client, sessionId, log6) {
13737
13817
  });
13738
13818
  return;
13739
13819
  }
13740
- log6?.info(`[${PLUGIN_NAME10}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
13820
+ log6?.info(`[${PLUGIN_NAME9}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
13741
13821
  sessionId,
13742
13822
  section
13743
13823
  });
13744
13824
  } catch (err) {
13745
- log6?.warn(`[${PLUGIN_NAME10}] system-injected 抛异常,降级到 observe-only`, {
13825
+ log6?.warn(`[${PLUGIN_NAME9}] system-injected 抛异常,降级到 observe-only`, {
13746
13826
  sessionId,
13747
13827
  section,
13748
13828
  preview: markdown.slice(0, 200),
@@ -13751,23 +13831,16 @@ function createSystemInjectedHook(client, sessionId, log6) {
13751
13831
  }
13752
13832
  };
13753
13833
  }
13754
- async function handleMessage2(raw, opts) {
13834
+ var inflight2 = new Set;
13835
+ var INFLIGHT_CAP = 5;
13836
+ function inflightKey(sessionId, query) {
13837
+ return `${sessionId ?? "global"}:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
13838
+ }
13839
+ async function runKhSearchAndInject(args) {
13840
+ const { query, ctx, opts, mode } = args;
13755
13841
  const cfg = opts.config ?? DEFAULT_CONFIG5;
13756
- const mode = opts.mode ?? INJECTION_MODE;
13757
- const ctx = raw ?? {};
13758
13842
  const log6 = ctx.log;
13759
- const text = (ctx.content ?? "").trim();
13760
- if (!shouldInject(text, cfg)) {
13761
- log6?.debug?.(`[${PLUGIN_NAME10}] skip (filter)`, { textLen: text.length });
13762
- return null;
13763
- }
13764
- const query = extractQuery(text);
13765
- if (!query)
13766
- return null;
13767
- if (opts.cache.shouldSkip(query)) {
13768
- log6?.debug?.(`[${PLUGIN_NAME10}] cache hit, skip`, { query });
13769
- return null;
13770
- }
13843
+ const startedAt = Date.now();
13771
13844
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
13772
13845
  let timer = null;
13773
13846
  try {
@@ -13782,20 +13855,54 @@ async function handleMessage2(raw, opts) {
13782
13855
  clearTimeout(timer);
13783
13856
  }
13784
13857
  });
13785
- const result = await racer(opts.client.search({ query, limit: cfg.limit }), cfg.timeoutMs);
13858
+ let result;
13859
+ try {
13860
+ const searchPromise = sharedKhCache.searchOrGet({
13861
+ client: opts.client,
13862
+ sessionId: ctx.sessionId ?? "global",
13863
+ query,
13864
+ limit: cfg.limit
13865
+ });
13866
+ result = await racer(searchPromise, cfg.timeoutMs);
13867
+ } catch (err) {
13868
+ log6?.warn(`[${PLUGIN_NAME9}] client.search threw (sync or async), return null`, {
13869
+ query,
13870
+ elapsedMs: Date.now() - startedAt,
13871
+ sessionId: ctx.sessionId,
13872
+ error: err instanceof Error ? err.message : String(err)
13873
+ });
13874
+ opts.cache.record(query, []);
13875
+ return null;
13876
+ }
13786
13877
  if (result === "__timeout__") {
13787
- log6?.warn(`[${PLUGIN_NAME10}] timeout`, { query, ms: cfg.timeoutMs });
13878
+ log6?.warn(`[${PLUGIN_NAME9}] timeout`, {
13879
+ query,
13880
+ ms: cfg.timeoutMs,
13881
+ elapsedMs: Date.now() - startedAt,
13882
+ sessionId: ctx.sessionId
13883
+ });
13788
13884
  opts.cache.record(query, []);
13789
13885
  return null;
13790
13886
  }
13791
13887
  if (!result.ok) {
13792
- log6?.debug?.(`[${PLUGIN_NAME10}] kh degraded`, { reason: result.reason });
13888
+ log6?.warn(`[${PLUGIN_NAME9}] kh degraded`, {
13889
+ reason: result.reason,
13890
+ query,
13891
+ elapsedMs: Date.now() - startedAt,
13892
+ sessionId: ctx.sessionId
13893
+ });
13793
13894
  opts.cache.record(query, []);
13794
13895
  return null;
13795
13896
  }
13796
13897
  const filtered = result.insights.filter((i) => (i.confidence ?? 0) >= cfg.minConfidence);
13797
13898
  if (filtered.length === 0) {
13798
13899
  opts.cache.record(query, []);
13900
+ log6?.debug?.(`[${PLUGIN_NAME9}] no candidate above threshold`, {
13901
+ query,
13902
+ rawCount: result.insights.length,
13903
+ elapsedMs: Date.now() - startedAt,
13904
+ sessionId: ctx.sessionId
13905
+ });
13799
13906
  return null;
13800
13907
  }
13801
13908
  const payload = formatInjection(query, filtered, mode);
@@ -13803,22 +13910,68 @@ async function handleMessage2(raw, opts) {
13803
13910
  try {
13804
13911
  await ctx.injectContext(payload.markdown);
13805
13912
  } catch (err) {
13806
- log6?.warn(`[${PLUGIN_NAME10}] injectContext threw`, {
13807
- error: err instanceof Error ? err.message : String(err)
13913
+ log6?.warn(`[${PLUGIN_NAME9}] injectContext threw`, {
13914
+ error: err instanceof Error ? err.message : String(err),
13915
+ query,
13916
+ sessionId: ctx.sessionId
13808
13917
  });
13809
13918
  }
13810
13919
  }
13811
13920
  opts.cache.record(query, filtered);
13812
- log6?.info(`[${PLUGIN_NAME10}] ${mode}: ${filtered.length} candidate insights ready`, { query, mode });
13921
+ log6?.info(`[${PLUGIN_NAME9}] inject complete (${mode})`, {
13922
+ query,
13923
+ mode,
13924
+ candidateCount: filtered.length,
13925
+ elapsedMs: Date.now() - startedAt,
13926
+ sessionId: ctx.sessionId
13927
+ });
13813
13928
  return payload;
13814
13929
  }
13815
- logLifecycle(PLUGIN_NAME10, "import");
13930
+ async function handleMessage2(raw, opts) {
13931
+ const cfg = opts.config ?? DEFAULT_CONFIG5;
13932
+ const mode = opts.mode ?? INJECTION_MODE;
13933
+ const ctx = raw ?? {};
13934
+ const log6 = ctx.log;
13935
+ const text = (ctx.content ?? "").trim();
13936
+ if (!shouldInject(text, cfg)) {
13937
+ log6?.debug?.(`[${PLUGIN_NAME9}] skip (filter)`, { textLen: text.length });
13938
+ return;
13939
+ }
13940
+ const query = extractQuery(text);
13941
+ if (!query)
13942
+ return;
13943
+ if (opts.cache.shouldSkip(query)) {
13944
+ log6?.debug?.(`[${PLUGIN_NAME9}] cache hit, skip`, { query });
13945
+ return;
13946
+ }
13947
+ if (inflight2.size >= INFLIGHT_CAP) {
13948
+ log6?.warn(`[${PLUGIN_NAME9}] inflight cap reached, skip`, {
13949
+ query,
13950
+ inflightSize: inflight2.size,
13951
+ cap: INFLIGHT_CAP,
13952
+ sessionId: ctx.sessionId
13953
+ });
13954
+ return;
13955
+ }
13956
+ const key = inflightKey(ctx.sessionId, query);
13957
+ inflight2.add(key);
13958
+ runKhSearchAndInject({ query, ctx, opts, mode }).catch((err) => {
13959
+ log6?.warn(`[${PLUGIN_NAME9}] runKhSearchAndInject 顶层兜底捕获`, {
13960
+ error: err instanceof Error ? err.message : String(err),
13961
+ query,
13962
+ sessionId: ctx.sessionId
13963
+ });
13964
+ }).finally(() => {
13965
+ inflight2.delete(key);
13966
+ });
13967
+ }
13968
+ logLifecycle(PLUGIN_NAME9, "import");
13816
13969
  var sharedClient2 = new KhClient;
13817
13970
  var sharedCache = new QueryCache(DEFAULT_CONFIG5.cacheTtlMs);
13818
13971
  var khAutoContextServer = async (ctx) => {
13819
- const log6 = makePluginLogger(PLUGIN_NAME10);
13972
+ const log6 = makePluginLogger(PLUGIN_NAME9);
13820
13973
  const runtimeMode = resolveInjectionMode(sharedClient2);
13821
- logLifecycle(PLUGIN_NAME10, "activate", {
13974
+ logLifecycle(PLUGIN_NAME9, "activate", {
13822
13975
  directory: ctx.directory,
13823
13976
  minConfidence: DEFAULT_CONFIG5.minConfidence,
13824
13977
  timeoutMs: DEFAULT_CONFIG5.timeoutMs,
@@ -13832,7 +13985,7 @@ var khAutoContextServer = async (ctx) => {
13832
13985
  });
13833
13986
  return {
13834
13987
  "chat.message": async (input, output) => {
13835
- await safeAsync(PLUGIN_NAME10, "chat.message", async () => {
13988
+ await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
13836
13989
  const text = extractUserText(output);
13837
13990
  if (!text)
13838
13991
  return;
@@ -13850,10 +14003,25 @@ var khAutoContextServer = async (ctx) => {
13850
14003
  log: log6
13851
14004
  }, { client: sharedClient2, cache: sharedCache, mode: runtimeMode });
13852
14005
  });
14006
+ },
14007
+ event: async ({ event }) => {
14008
+ await safeAsync(PLUGIN_NAME9, "event", async () => {
14009
+ const e = event;
14010
+ if (e.type !== "session.idle")
14011
+ return;
14012
+ const props = e.properties;
14013
+ const sid = props?.sessionID;
14014
+ if (typeof sid !== "string" || !sid)
14015
+ return;
14016
+ sharedKhCache.onSessionEnd(sid);
14017
+ log6.debug?.(`[${PLUGIN_NAME9}] session.idle: cleared shared cache`, {
14018
+ sessionID: sid
14019
+ });
14020
+ });
13853
14021
  }
13854
14022
  };
13855
14023
  };
13856
- var handler10 = khAutoContextServer;
14024
+ var handler9 = khAutoContextServer;
13857
14025
 
13858
14026
  // plugins/kh-reminder.ts
13859
14027
  init_opencode_plugin_helpers();
@@ -13974,8 +14142,8 @@ async function condense(input, opts = {}) {
13974
14142
  }
13975
14143
 
13976
14144
  // plugins/kh-reminder.ts
13977
- var PLUGIN_NAME11 = "kh-reminder";
13978
- logLifecycle(PLUGIN_NAME11, "import", {});
14145
+ var PLUGIN_NAME10 = "kh-reminder";
14146
+ logLifecycle(PLUGIN_NAME10, "import", {});
13979
14147
  var TRIGGER_WORDS_ZH = [
13980
14148
  "怎么",
13981
14149
  "怎样",
@@ -14129,7 +14297,7 @@ function handleObserve(raw, log6) {
14129
14297
  const result = evaluate(ctx);
14130
14298
  if (result.triggered && result.reason) {
14131
14299
  const reasonStr = formatReason(result.reason);
14132
- log6?.info(`[${PLUGIN_NAME11}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
14300
+ log6?.info(`[${PLUGIN_NAME10}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
14133
14301
  sessionId: result.sessionId,
14134
14302
  reason: result.reason,
14135
14303
  suggestion: "调用 smart_search 查项目历史;完成后用 save_chat_insight 沉淀"
@@ -14137,7 +14305,7 @@ function handleObserve(raw, log6) {
14137
14305
  }
14138
14306
  return result;
14139
14307
  } catch (err) {
14140
- log6?.warn(`[${PLUGIN_NAME11}] evaluate 异常(已隔离)`, {
14308
+ log6?.warn(`[${PLUGIN_NAME10}] evaluate 异常(已隔离)`, {
14141
14309
  error: err instanceof Error ? err.message : String(err)
14142
14310
  });
14143
14311
  return null;
@@ -14153,9 +14321,9 @@ function formatReason(r) {
14153
14321
  return `no-search-in-recent-rounds (last ${r.rounds})`;
14154
14322
  }
14155
14323
  }
14156
- var log6 = makePluginLogger(PLUGIN_NAME11);
14324
+ var log6 = makePluginLogger(PLUGIN_NAME10);
14157
14325
  var khReminderServer = async (ctx) => {
14158
- logLifecycle(PLUGIN_NAME11, "activate", {
14326
+ logLifecycle(PLUGIN_NAME10, "activate", {
14159
14327
  directory: ctx.directory,
14160
14328
  threshold: DEFAULT_THRESHOLD,
14161
14329
  cooldown_ms: COOLDOWN_MS,
@@ -14163,7 +14331,7 @@ var khReminderServer = async (ctx) => {
14163
14331
  });
14164
14332
  return {
14165
14333
  "experimental.chat.messages.transform": async (_input, output) => {
14166
- await safeAsync(PLUGIN_NAME11, "experimental.chat.messages.transform", async () => {
14334
+ await safeAsync(PLUGIN_NAME10, "experimental.chat.messages.transform", async () => {
14167
14335
  const list = output.messages;
14168
14336
  if (!Array.isArray(list) || list.length === 0)
14169
14337
  return;
@@ -14182,7 +14350,7 @@ var khReminderServer = async (ctx) => {
14182
14350
  const result = handleObserve({ messages: flat, sessionId }, log6);
14183
14351
  if (!result)
14184
14352
  return;
14185
- safeWriteLog(PLUGIN_NAME11, {
14353
+ safeWriteLog(PLUGIN_NAME10, {
14186
14354
  hook: "experimental.chat.messages.transform",
14187
14355
  mode: "observe-only",
14188
14356
  sessionId: result.sessionId,
@@ -14195,7 +14363,7 @@ var khReminderServer = async (ctx) => {
14195
14363
  }
14196
14364
  };
14197
14365
  };
14198
- var handler11 = khReminderServer;
14366
+ var handler10 = khReminderServer;
14199
14367
 
14200
14368
  // lib/memories.ts
14201
14369
  import { promises as fs8 } from "node:fs";
@@ -14397,7 +14565,7 @@ function bagOfWordsScore(query, doc) {
14397
14565
 
14398
14566
  // plugins/memories-context.ts
14399
14567
  init_opencode_plugin_helpers();
14400
- var PLUGIN_NAME12 = "memories-context";
14568
+ var PLUGIN_NAME11 = "memories-context";
14401
14569
  var INJECTION_MODE2 = "observe-only";
14402
14570
  var DEFAULT_CONFIG6 = {
14403
14571
  minTextLength: 8,
@@ -14495,14 +14663,14 @@ async function handleMessage3(raw, opts) {
14495
14663
  return await handleDirective(dir, ctx, opts.memCfg, log7);
14496
14664
  }
14497
14665
  if (!shouldRecall(text, cfg)) {
14498
- log7?.debug?.(`[${PLUGIN_NAME12}] skip (filter)`, { textLen: text.length });
14666
+ log7?.debug?.(`[${PLUGIN_NAME11}] skip (filter)`, { textLen: text.length });
14499
14667
  return { kind: "noop", reason: "filtered", mode: INJECTION_MODE2 };
14500
14668
  }
14501
14669
  const query = extractQuery2(text);
14502
14670
  if (!query)
14503
14671
  return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE2 };
14504
14672
  if (cache2.shouldSkip(query)) {
14505
- log7?.debug?.(`[${PLUGIN_NAME12}] cache hit`, { query });
14673
+ log7?.debug?.(`[${PLUGIN_NAME11}] cache hit`, { query });
14506
14674
  return { kind: "noop", reason: "cache", mode: INJECTION_MODE2 };
14507
14675
  }
14508
14676
  const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
@@ -14527,7 +14695,7 @@ async function handleMessage3(raw, opts) {
14527
14695
  }, opts.memCfg);
14528
14696
  const result = await racer(injectPromise, cfg.timeoutMs);
14529
14697
  if (result === "__timeout__") {
14530
- log7?.warn(`[${PLUGIN_NAME12}] timeout`, { query, ms: cfg.timeoutMs });
14698
+ log7?.warn(`[${PLUGIN_NAME11}] timeout`, { query, ms: cfg.timeoutMs });
14531
14699
  cache2.record(query, 0);
14532
14700
  return { kind: "noop", reason: "timeout", mode: INJECTION_MODE2 };
14533
14701
  }
@@ -14539,13 +14707,13 @@ async function handleMessage3(raw, opts) {
14539
14707
  try {
14540
14708
  await ctx.injectContext(result.text);
14541
14709
  } catch (err) {
14542
- log7?.warn(`[${PLUGIN_NAME12}] injectContext threw`, {
14710
+ log7?.warn(`[${PLUGIN_NAME11}] injectContext threw`, {
14543
14711
  error: err instanceof Error ? err.message : String(err)
14544
14712
  });
14545
14713
  }
14546
14714
  }
14547
14715
  cache2.record(query, result.recalled);
14548
- log7?.info(`[${PLUGIN_NAME12}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
14716
+ log7?.info(`[${PLUGIN_NAME11}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
14549
14717
  return { kind: "injected", payload: result, mode: INJECTION_MODE2 };
14550
14718
  }
14551
14719
  async function handleDirective(dir, ctx, memCfg, log7) {
@@ -14554,14 +14722,14 @@ async function handleDirective(dir, ctx, memCfg, log7) {
14554
14722
  if (r.ok) {
14555
14723
  const target = r.written_to === "kh" ? "KH" : "本地";
14556
14724
  await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
14557
- log7?.info(`[${PLUGIN_NAME12}] /remember ok`, {
14725
+ log7?.info(`[${PLUGIN_NAME11}] /remember ok`, {
14558
14726
  scope: dir.scope,
14559
14727
  id: r.id,
14560
14728
  mode: INJECTION_MODE2
14561
14729
  });
14562
14730
  } else {
14563
14731
  await safeReply(ctx, `❌ 记忆失败:${r.error ?? "unknown"}`);
14564
- log7?.warn(`[${PLUGIN_NAME12}] /remember failed`, { error: r.error });
14732
+ log7?.warn(`[${PLUGIN_NAME11}] /remember failed`, { error: r.error });
14565
14733
  }
14566
14734
  return { kind: "remembered", result: r, mode: INJECTION_MODE2 };
14567
14735
  }
@@ -14592,22 +14760,22 @@ async function safeReply(ctx, text) {
14592
14760
  await ctx.reply(text);
14593
14761
  } catch {}
14594
14762
  }
14595
- logLifecycle(PLUGIN_NAME12, "import");
14763
+ logLifecycle(PLUGIN_NAME11, "import");
14596
14764
  var sharedCache2 = new QueryCache2(DEFAULT_CONFIG6.cacheTtlMs);
14597
14765
  function buildMemCfg(directory) {
14598
14766
  return { projectRoot: directory };
14599
14767
  }
14600
14768
  var memoriesContextServer = async (ctx) => {
14601
- const log7 = makePluginLogger(PLUGIN_NAME12);
14769
+ const log7 = makePluginLogger(PLUGIN_NAME11);
14602
14770
  const memCfg = buildMemCfg(ctx.directory);
14603
- logLifecycle(PLUGIN_NAME12, "activate", {
14771
+ logLifecycle(PLUGIN_NAME11, "activate", {
14604
14772
  directory: ctx.directory,
14605
14773
  projectRoot: memCfg.projectRoot,
14606
14774
  mode: INJECTION_MODE2
14607
14775
  });
14608
14776
  return {
14609
14777
  "chat.message": async (input, output) => {
14610
- await safeAsync(PLUGIN_NAME12, "chat.message", async () => {
14778
+ await safeAsync(PLUGIN_NAME11, "chat.message", async () => {
14611
14779
  const text = extractUserText(output);
14612
14780
  if (!text)
14613
14781
  return;
@@ -14633,18 +14801,18 @@ var memoriesContextServer = async (ctx) => {
14633
14801
  }
14634
14802
  };
14635
14803
  };
14636
- var handler12 = memoriesContextServer;
14804
+ var handler11 = memoriesContextServer;
14637
14805
 
14638
14806
  // plugins/model-fallback.ts
14639
14807
  init_opencode_plugin_helpers();
14640
- var PLUGIN_NAME13 = "model-fallback";
14808
+ var PLUGIN_NAME12 = "model-fallback";
14641
14809
  var state2 = {
14642
14810
  config: null,
14643
14811
  configPath: null,
14644
14812
  warnings: [],
14645
14813
  error: null
14646
14814
  };
14647
- logLifecycle(PLUGIN_NAME13, "import");
14815
+ logLifecycle(PLUGIN_NAME12, "import");
14648
14816
  function loadOnce(root) {
14649
14817
  if (state2.config !== null || state2.error !== null)
14650
14818
  return;
@@ -14654,11 +14822,11 @@ function loadOnce(root) {
14654
14822
  state2.configPath = r.path ?? null;
14655
14823
  state2.warnings = r.warnings;
14656
14824
  if (r.warnings.length > 0) {
14657
- safeWriteLog(PLUGIN_NAME13, { phase: "load.warnings", warnings: r.warnings });
14825
+ safeWriteLog(PLUGIN_NAME12, { phase: "load.warnings", warnings: r.warnings });
14658
14826
  }
14659
14827
  } else {
14660
14828
  state2.error = r.error ?? "unknown_load_error";
14661
- safeWriteLog(PLUGIN_NAME13, { phase: "load.failed", error: state2.error, path: r.path });
14829
+ safeWriteLog(PLUGIN_NAME12, { phase: "load.failed", error: state2.error, path: r.path });
14662
14830
  }
14663
14831
  }
14664
14832
  var MODEL_ERR_PATTERNS = [
@@ -14703,9 +14871,9 @@ fallback 链已用尽:${meta.chain.join(" → ")}`;
14703
14871
  完整链:${meta.chain.join(" → ")}`;
14704
14872
  }
14705
14873
  var modelFallbackServer = async (ctx) => {
14706
- const log7 = makePluginLogger(PLUGIN_NAME13);
14874
+ const log7 = makePluginLogger(PLUGIN_NAME12);
14707
14875
  loadOnce(ctx.directory ?? process.cwd());
14708
- logLifecycle(PLUGIN_NAME13, "activate", {
14876
+ logLifecycle(PLUGIN_NAME12, "activate", {
14709
14877
  directory: ctx.directory,
14710
14878
  config_path: state2.configPath,
14711
14879
  config_loaded: state2.config !== null,
@@ -14715,7 +14883,7 @@ var modelFallbackServer = async (ctx) => {
14715
14883
  });
14716
14884
  return {
14717
14885
  "chat.params": async (input, output) => {
14718
- await safeAsync(PLUGIN_NAME13, "chat.params", async () => {
14886
+ await safeAsync(PLUGIN_NAME12, "chat.params", async () => {
14719
14887
  if (!state2.config)
14720
14888
  return;
14721
14889
  const agent = input.agent;
@@ -14736,7 +14904,7 @@ var modelFallbackServer = async (ctx) => {
14736
14904
  next: meta.next_fallback,
14737
14905
  source: meta.source
14738
14906
  };
14739
- safeWriteLog(PLUGIN_NAME13, {
14907
+ safeWriteLog(PLUGIN_NAME12, {
14740
14908
  hook: "chat.params",
14741
14909
  agent,
14742
14910
  model: currentModel,
@@ -14746,7 +14914,7 @@ var modelFallbackServer = async (ctx) => {
14746
14914
  });
14747
14915
  },
14748
14916
  event: async ({ event }) => {
14749
- await safeAsync(PLUGIN_NAME13, "event", async () => {
14917
+ await safeAsync(PLUGIN_NAME12, "event", async () => {
14750
14918
  if (!state2.config)
14751
14919
  return;
14752
14920
  const e = event;
@@ -14762,8 +14930,8 @@ var modelFallbackServer = async (ctx) => {
14762
14930
  const model = props.model ?? "unknown/unknown";
14763
14931
  const meta = buildFallbackMeta(state2.config, agent, model);
14764
14932
  const suggestion = meta ? buildSuggestion(meta, String(message)) : `⚠️ ${agent}/${model} 失败:${message}`;
14765
- log7.warn(`[${PLUGIN_NAME13}] ${suggestion}`);
14766
- safeWriteLog(PLUGIN_NAME13, {
14933
+ log7.warn(`[${PLUGIN_NAME12}] ${suggestion}`);
14934
+ safeWriteLog(PLUGIN_NAME12, {
14767
14935
  hook: "event.error",
14768
14936
  eventType: e.type,
14769
14937
  agent,
@@ -14775,19 +14943,24 @@ var modelFallbackServer = async (ctx) => {
14775
14943
  }
14776
14944
  };
14777
14945
  };
14778
- var handler13 = modelFallbackServer;
14946
+ var handler12 = modelFallbackServer;
14779
14947
 
14780
14948
  // plugins/subtask-heartbeat.ts
14781
14949
  init_opencode_plugin_helpers();
14782
- var PLUGIN_NAME14 = "subtask-heartbeat";
14783
- logLifecycle(PLUGIN_NAME14, "import", {});
14950
+ var PLUGIN_NAME13 = "subtask-heartbeat";
14951
+ logLifecycle(PLUGIN_NAME13, "import", {});
14784
14952
  var HEARTBEAT_INTERVAL_MS2 = 30000;
14785
14953
  var HEARTBEAT_DEBOUNCE_MS = 25000;
14786
14954
  var TOAST_DURATION_MS3 = 5000;
14787
14955
  var START_TOAST_DURATION_MS = 2000;
14788
- var inflight2 = new Map;
14956
+ var PENDING_TASK_TTL_MS = 60000;
14957
+ var PENDING_TASK_MAX_PARENTS = 64;
14958
+ var PENDING_TASK_MAX_PER_PARENT = 16;
14959
+ var DESCRIPTION_MAX_LEN = 60;
14960
+ var inflight3 = new Map;
14961
+ var pendingTask = new Map;
14789
14962
  function _snapshotInflight() {
14790
- return [...inflight2.values()].map((r) => ({ ...r }));
14963
+ return [...inflight3.values()].map((r) => ({ ...r }));
14791
14964
  }
14792
14965
  function getInflightSnapshot() {
14793
14966
  return _snapshotInflight();
@@ -14829,20 +15002,97 @@ function extractEndedSessionID(event) {
14829
15002
  }
14830
15003
  return null;
14831
15004
  }
15005
+ function extractSubtaskPart(event) {
15006
+ if (!event || typeof event !== "object")
15007
+ return null;
15008
+ const e = event;
15009
+ if (e.type !== "message.part.updated")
15010
+ return null;
15011
+ const part = e.properties?.part;
15012
+ if (!part || typeof part !== "object")
15013
+ return null;
15014
+ const p = part;
15015
+ if (p.type !== "subtask")
15016
+ return null;
15017
+ if (typeof p.sessionID !== "string" || p.sessionID === "")
15018
+ return null;
15019
+ if (typeof p.agent !== "string" || p.agent === "")
15020
+ return null;
15021
+ if (typeof p.description !== "string" || p.description === "")
15022
+ return null;
15023
+ return { parentID: p.sessionID, agent: p.agent, description: p.description };
15024
+ }
15025
+ function enqueuePendingTask(parentID, entry, now = Date.now()) {
15026
+ const ts = entry.ts ?? now;
15027
+ let bucket = pendingTask.get(parentID);
15028
+ if (!bucket) {
15029
+ if (pendingTask.size >= PENDING_TASK_MAX_PARENTS) {
15030
+ let oldestKey = null;
15031
+ let oldestTs = Number.POSITIVE_INFINITY;
15032
+ for (const [k, v] of pendingTask.entries()) {
15033
+ const headTs = v[0]?.ts ?? Number.POSITIVE_INFINITY;
15034
+ if (headTs < oldestTs) {
15035
+ oldestTs = headTs;
15036
+ oldestKey = k;
15037
+ }
15038
+ }
15039
+ if (oldestKey !== null)
15040
+ pendingTask.delete(oldestKey);
15041
+ }
15042
+ bucket = [];
15043
+ pendingTask.set(parentID, bucket);
15044
+ }
15045
+ bucket.push({ agent: entry.agent, description: entry.description, ts });
15046
+ while (bucket.length > PENDING_TASK_MAX_PER_PARENT) {
15047
+ bucket.shift();
15048
+ }
15049
+ }
15050
+ function dequeuePendingTask(parentID, now = Date.now()) {
15051
+ const bucket = pendingTask.get(parentID);
15052
+ if (!bucket || bucket.length === 0) {
15053
+ if (bucket)
15054
+ pendingTask.delete(parentID);
15055
+ return null;
15056
+ }
15057
+ while (bucket.length > 0 && now - bucket[0].ts > PENDING_TASK_TTL_MS) {
15058
+ bucket.shift();
15059
+ }
15060
+ if (bucket.length === 0) {
15061
+ pendingTask.delete(parentID);
15062
+ return null;
15063
+ }
15064
+ const entry = bucket.shift();
15065
+ if (bucket.length === 0)
15066
+ pendingTask.delete(parentID);
15067
+ return entry;
15068
+ }
15069
+ function sweepExpiredPendingTasks(now = Date.now()) {
15070
+ let removed = 0;
15071
+ for (const [parentID, bucket] of [...pendingTask.entries()]) {
15072
+ while (bucket.length > 0 && now - bucket[0].ts > PENDING_TASK_TTL_MS) {
15073
+ bucket.shift();
15074
+ removed++;
15075
+ }
15076
+ if (bucket.length === 0)
15077
+ pendingTask.delete(parentID);
15078
+ }
15079
+ return removed;
15080
+ }
14832
15081
  function registerInflight(payload, now = Date.now()) {
14833
15082
  const r = {
14834
15083
  childID: payload.childID,
14835
15084
  parentID: payload.parentID,
14836
15085
  agent: payload.agent,
15086
+ description: payload.description ?? null,
14837
15087
  startedAt: now,
14838
15088
  lastBeatAt: now,
14839
15089
  lastTool: null
14840
15090
  };
14841
- inflight2.set(payload.childID, r);
15091
+ inflight3.set(payload.childID, r);
14842
15092
  return r;
14843
15093
  }
14844
15094
  function recordToolBeat(sessionID, tool2, now = Date.now()) {
14845
- const r = inflight2.get(sessionID);
15095
+ const r = inflight3.get(sessionID);
14846
15096
  if (!r)
14847
15097
  return null;
14848
15098
  r.lastBeatAt = now;
@@ -14850,15 +15100,15 @@ function recordToolBeat(sessionID, tool2, now = Date.now()) {
14850
15100
  return r;
14851
15101
  }
14852
15102
  function clearInflight2(sessionID) {
14853
- const r = inflight2.get(sessionID);
15103
+ const r = inflight3.get(sessionID);
14854
15104
  if (!r)
14855
15105
  return null;
14856
- inflight2.delete(sessionID);
15106
+ inflight3.delete(sessionID);
14857
15107
  return r;
14858
15108
  }
14859
15109
  function pickHeartbeats(now = Date.now()) {
14860
15110
  const out = [];
14861
- for (const r of inflight2.values()) {
15111
+ for (const r of inflight3.values()) {
14862
15112
  if (now - r.lastBeatAt >= HEARTBEAT_DEBOUNCE_MS)
14863
15113
  out.push(r);
14864
15114
  }
@@ -14870,10 +15120,32 @@ function fmtElapsed(ms) {
14870
15120
  const s = total % 60;
14871
15121
  return m > 0 ? `${m}m${s.toString().padStart(2, "0")}s` : `${s}s`;
14872
15122
  }
15123
+ function titleCase(s) {
15124
+ if (!s)
15125
+ return s;
15126
+ return s.charAt(0).toUpperCase() + s.slice(1);
15127
+ }
15128
+ function sanitizeDescription(s) {
15129
+ const collapsed = s.replace(/[\r\n\t]+/g, " ").replace(/\s+/g, " ").trim();
15130
+ if (collapsed.length <= DESCRIPTION_MAX_LEN)
15131
+ return collapsed;
15132
+ return collapsed.slice(0, DESCRIPTION_MAX_LEN) + "…";
15133
+ }
14873
15134
  function buildStartToast(r) {
14874
- const who = r.agent ?? "subagent";
15135
+ if (r.agent && r.description) {
15136
+ return {
15137
+ message: `\uD83D\uDE80 ${titleCase(r.agent)} 启动 — ${sanitizeDescription(r.description)}`,
15138
+ variant: "info"
15139
+ };
15140
+ }
15141
+ if (r.agent) {
15142
+ return {
15143
+ message: `\uD83D\uDE80 ${titleCase(r.agent)} 启动`,
15144
+ variant: "info"
15145
+ };
15146
+ }
14875
15147
  return {
14876
- message: `\uD83D\uDE80 子 session 启动: ${who}`,
15148
+ message: `\uD83D\uDE80 子 session 启动: subagent`,
14877
15149
  variant: "info"
14878
15150
  };
14879
15151
  }
@@ -14886,15 +15158,32 @@ function buildHeartbeatToast(r, now = Date.now()) {
14886
15158
  };
14887
15159
  }
14888
15160
  function buildEndToast(r, type, now = Date.now()) {
14889
- const who = r.agent ?? "subagent";
14890
15161
  const elapsed = fmtElapsed(now - r.startedAt);
14891
- if (type === "session.error") {
14892
- return { message: `❌ ${who} 失败 (${elapsed})`, variant: "error" };
15162
+ const variant = type === "session.error" || type === "session.deleted" ? "error" : "success";
15163
+ const emoji = type === "session.error" ? "❌" : type === "session.deleted" ? "\uD83D\uDDD1️" : "";
15164
+ const verb = type === "session.error" ? "失败" : type === "session.deleted" ? "被取消" : "完成";
15165
+ if (r.agent && r.description) {
15166
+ if (type === "session.idle" || type !== "session.error" && type !== "session.deleted") {
15167
+ return {
15168
+ message: `${emoji} ${titleCase(r.agent)} Task — ${sanitizeDescription(r.description)} (${elapsed})`,
15169
+ variant
15170
+ };
15171
+ }
15172
+ return {
15173
+ message: `${emoji} ${titleCase(r.agent)} ${verb} — ${sanitizeDescription(r.description)} (${elapsed})`,
15174
+ variant
15175
+ };
14893
15176
  }
14894
- if (type === "session.deleted") {
14895
- return { message: `\uD83D\uDDD1️ ${who} 被取消 (${elapsed})`, variant: "error" };
15177
+ if (r.agent) {
15178
+ return {
15179
+ message: `${emoji} ${titleCase(r.agent)} ${verb} (${elapsed})`,
15180
+ variant
15181
+ };
14896
15182
  }
14897
- return { message: `✅ ${who} 完成 (${elapsed})`, variant: "success" };
15183
+ return {
15184
+ message: `${emoji} subagent ${verb} (${elapsed})`,
15185
+ variant
15186
+ };
14898
15187
  }
14899
15188
  function normalizeVariant3(raw) {
14900
15189
  if (raw === "info" || raw === "warning")
@@ -14923,22 +15212,26 @@ async function showToast3(client, payload, log7) {
14923
15212
  return false;
14924
15213
  }
14925
15214
  }
14926
- var log7 = makePluginLogger(PLUGIN_NAME14);
15215
+ var log7 = makePluginLogger(PLUGIN_NAME13);
14927
15216
  var subtaskHeartbeatServer = async (ctx) => {
14928
- logLifecycle(PLUGIN_NAME14, "activate", {
15217
+ logLifecycle(PLUGIN_NAME13, "activate", {
14929
15218
  directory: ctx.directory,
14930
15219
  intervalMs: HEARTBEAT_INTERVAL_MS2
14931
15220
  });
14932
15221
  const client = ctx.client;
14933
15222
  const interval = setInterval(() => {
14934
- safeAsync(PLUGIN_NAME14, "interval", async () => {
15223
+ safeAsync(PLUGIN_NAME13, "interval", async () => {
15224
+ const swept = sweepExpiredPendingTasks();
15225
+ if (swept > 0) {
15226
+ safeWriteLog(PLUGIN_NAME13, { hook: "interval", pending_task_swept: swept });
15227
+ }
14935
15228
  const beats = pickHeartbeats();
14936
15229
  if (beats.length === 0)
14937
15230
  return;
14938
15231
  for (const r of beats) {
14939
15232
  const t = buildHeartbeatToast(r);
14940
15233
  const sent = await showToast3(client, t, log7);
14941
- safeWriteLog(PLUGIN_NAME14, {
15234
+ safeWriteLog(PLUGIN_NAME13, {
14942
15235
  hook: "interval",
14943
15236
  child: r.childID,
14944
15237
  parent: r.parentID,
@@ -14955,23 +15248,47 @@ var subtaskHeartbeatServer = async (ctx) => {
14955
15248
  }
14956
15249
  return {
14957
15250
  event: async ({ event }) => {
14958
- await safeAsync(PLUGIN_NAME14, "event", async () => {
15251
+ await safeAsync(PLUGIN_NAME13, "event", async () => {
15252
+ const subtask = extractSubtaskPart(event);
15253
+ if (subtask) {
15254
+ enqueuePendingTask(subtask.parentID, {
15255
+ agent: subtask.agent,
15256
+ description: subtask.description
15257
+ });
15258
+ safeWriteLog(PLUGIN_NAME13, {
15259
+ hook: "event",
15260
+ type: "message.part.updated.subtask",
15261
+ parent: subtask.parentID,
15262
+ agent: subtask.agent,
15263
+ description_len: subtask.description.length
15264
+ });
15265
+ return;
15266
+ }
14959
15267
  const created = extractCreatedChild(event);
14960
15268
  if (created) {
14961
- const record = registerInflight(created);
14962
- safeWriteLog(PLUGIN_NAME14, {
15269
+ const pending = dequeuePendingTask(created.parentID);
15270
+ const record = registerInflight({
15271
+ childID: created.childID,
15272
+ parentID: created.parentID,
15273
+ agent: pending?.agent ?? created.agent,
15274
+ description: pending?.description ?? null
15275
+ });
15276
+ safeWriteLog(PLUGIN_NAME13, {
14963
15277
  hook: "event",
14964
15278
  type: "session.created",
14965
15279
  child: created.childID,
14966
- parent: created.parentID
15280
+ parent: created.parentID,
15281
+ pending_task_matched: pending !== null,
15282
+ agent: record.agent
14967
15283
  });
14968
15284
  const startToast = buildStartToast(record);
14969
15285
  const sent = await showToast3(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log7);
14970
- safeWriteLog(PLUGIN_NAME14, {
15286
+ safeWriteLog(PLUGIN_NAME13, {
14971
15287
  hook: "event",
14972
15288
  type: "session.created.toast",
14973
15289
  child: created.childID,
14974
- toast_sent: sent
15290
+ toast_sent: sent,
15291
+ start_toast_message: startToast.message
14975
15292
  });
14976
15293
  return;
14977
15294
  }
@@ -14981,7 +15298,7 @@ var subtaskHeartbeatServer = async (ctx) => {
14981
15298
  if (r) {
14982
15299
  const t = buildEndToast(r, ended.type);
14983
15300
  const sent = await showToast3(client, t, log7);
14984
- safeWriteLog(PLUGIN_NAME14, {
15301
+ safeWriteLog(PLUGIN_NAME13, {
14985
15302
  hook: "event",
14986
15303
  type: ended.type,
14987
15304
  child: r.childID,
@@ -14994,7 +15311,9 @@ var subtaskHeartbeatServer = async (ctx) => {
14994
15311
  });
14995
15312
  },
14996
15313
  "tool.execute.before": async (input) => {
14997
- await safeAsync(PLUGIN_NAME14, "tool.execute.before", async () => {
15314
+ if (inflight3.size === 0)
15315
+ return;
15316
+ await safeAsync(PLUGIN_NAME13, "tool.execute.before", async () => {
14998
15317
  if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
14999
15318
  return;
15000
15319
  recordToolBeat(input.sessionID, input.tool);
@@ -15002,12 +15321,12 @@ var subtaskHeartbeatServer = async (ctx) => {
15002
15321
  }
15003
15322
  };
15004
15323
  };
15005
- var handler14 = subtaskHeartbeatServer;
15324
+ var handler13 = subtaskHeartbeatServer;
15006
15325
 
15007
15326
  // plugins/parallel-status.ts
15008
15327
  init_opencode_plugin_helpers();
15009
- var PLUGIN_NAME15 = "parallel-status";
15010
- logLifecycle(PLUGIN_NAME15, "import");
15328
+ var PLUGIN_NAME14 = "parallel-status";
15329
+ logLifecycle(PLUGIN_NAME14, "import");
15011
15330
  var ID_MAX_LEN = 16;
15012
15331
  var ID_KEEP_LEN = 13;
15013
15332
  function shortId(s) {
@@ -15039,8 +15358,8 @@ function formatInflightMarkdown(snapshot, now = Date.now()) {
15039
15358
  `);
15040
15359
  }
15041
15360
  var parallelStatusServer = async (ctx) => {
15042
- const log8 = makePluginLogger(PLUGIN_NAME15);
15043
- logLifecycle(PLUGIN_NAME15, "activate", { directory: ctx.directory });
15361
+ const log8 = makePluginLogger(PLUGIN_NAME14);
15362
+ logLifecycle(PLUGIN_NAME14, "activate", { directory: ctx.directory });
15044
15363
  return {
15045
15364
  "command.execute.before": async (input, output) => {
15046
15365
  try {
@@ -15059,21 +15378,21 @@ var parallelStatusServer = async (ctx) => {
15059
15378
  synthetic: false
15060
15379
  });
15061
15380
  }
15062
- log8.info(`[${PLUGIN_NAME15}] 已回写 ${snapshot.length} 条 inflight`);
15381
+ log8.info(`[${PLUGIN_NAME14}] 已回写 ${snapshot.length} 条 inflight`);
15063
15382
  } catch (err) {
15064
- log8.error(`[${PLUGIN_NAME15}] command.execute.before 异常(已隔离)`, {
15383
+ log8.error(`[${PLUGIN_NAME14}] command.execute.before 异常(已隔离)`, {
15065
15384
  error: err instanceof Error ? err.message : String(err)
15066
15385
  });
15067
15386
  }
15068
15387
  }
15069
15388
  };
15070
15389
  };
15071
- var handler15 = parallelStatusServer;
15390
+ var handler14 = parallelStatusServer;
15072
15391
 
15073
15392
  // plugins/pwsh-utf8.ts
15074
15393
  init_opencode_plugin_helpers();
15075
- var PLUGIN_NAME16 = "pwsh-utf8";
15076
- logLifecycle(PLUGIN_NAME16, "import", {});
15394
+ var PLUGIN_NAME15 = "pwsh-utf8";
15395
+ logLifecycle(PLUGIN_NAME15, "import", {});
15077
15396
  var PRELUDE = "chcp 65001 *> $null; " + "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); " + "$OutputEncoding = [System.Text.UTF8Encoding]::new(); ";
15078
15397
  function prependUtf8Prelude(command) {
15079
15398
  if (typeof command !== "string")
@@ -15086,14 +15405,15 @@ function prependUtf8Prelude(command) {
15086
15405
  return command;
15087
15406
  return PRELUDE + command;
15088
15407
  }
15089
- var handler16 = async (_ctx) => {
15408
+ var handler15 = async (_ctx) => {
15090
15409
  const enabled = process.platform === "win32" && process.env.CODEFORGE_DISABLE_PWSH_UTF8 !== "1";
15091
- logLifecycle(PLUGIN_NAME16, "activate", { enabled, platform: process.platform });
15410
+ const reason = enabled ? "win32" : process.platform !== "win32" ? "non-win32" : "disabled-by-env";
15411
+ logLifecycle(PLUGIN_NAME15, "activate", { enabled, platform: process.platform, reason });
15092
15412
  if (!enabled)
15093
15413
  return {};
15094
15414
  return {
15095
15415
  "tool.execute.before": async (input, output) => {
15096
- await safeAsync(PLUGIN_NAME16, "tool.execute.before", async () => {
15416
+ await safeAsync(PLUGIN_NAME15, "tool.execute.before", async () => {
15097
15417
  if (input.tool !== "bash")
15098
15418
  return;
15099
15419
  const args = output.args ?? {};
@@ -15102,7 +15422,7 @@ var handler16 = async (_ctx) => {
15102
15422
  if (next !== undefined && next !== original) {
15103
15423
  args["command"] = next;
15104
15424
  output.args = args;
15105
- safeWriteLog(PLUGIN_NAME16, {
15425
+ safeWriteLog(PLUGIN_NAME15, {
15106
15426
  hook: "tool.execute.before",
15107
15427
  tool: input.tool,
15108
15428
  callID: input.callID,
@@ -15295,7 +15615,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
15295
15615
  });
15296
15616
  }
15297
15617
  }
15298
- const inflight3 = [...toolMap.values()].sort((a, b) => b.last_ts - a.last_ts);
15618
+ const inflight4 = [...toolMap.values()].sort((a, b) => b.last_ts - a.last_ts);
15299
15619
  const proposed = window.some((t) => PENDING_CHANGES_TOOLS.has(t.tool));
15300
15620
  const applied = window.some((t) => APPLY_TOOLS.has(t.tool) && t.ok);
15301
15621
  const pending_changes_likely = proposed && !applied;
@@ -15319,7 +15639,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
15319
15639
  idleMs,
15320
15640
  lastUser,
15321
15641
  lastAgent,
15322
- inflight: inflight3,
15642
+ inflight: inflight4,
15323
15643
  pending_changes_likely,
15324
15644
  open_subtasks_likely,
15325
15645
  reason
@@ -15330,7 +15650,7 @@ function buildRecoveryPlan(events, meta = { id: null }, opts = {}) {
15330
15650
  idle_ms: idleMs,
15331
15651
  last_user_intent: lastUser,
15332
15652
  last_agent: lastAgent,
15333
- inflight_tools: inflight3,
15653
+ inflight_tools: inflight4,
15334
15654
  pending_changes_likely,
15335
15655
  open_subtasks_likely,
15336
15656
  summary
@@ -15435,8 +15755,8 @@ function isRecoveryWorthShowing(plan) {
15435
15755
  }
15436
15756
 
15437
15757
  // plugins/session-recovery.ts
15438
- var PLUGIN_NAME17 = "session-recovery";
15439
- logLifecycle(PLUGIN_NAME17, "import", {});
15758
+ var PLUGIN_NAME16 = "session-recovery";
15759
+ logLifecycle(PLUGIN_NAME16, "import", {});
15440
15760
  async function processSessionStart(currentSessionId, opts = {}) {
15441
15761
  if (opts.disabled) {
15442
15762
  return { ok: true, injected: false, reason: "disabled" };
@@ -15446,7 +15766,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
15446
15766
  excludeIds.add(currentSessionId);
15447
15767
  const r = await scanLastSession({ ...opts, excludeIds: [...excludeIds] });
15448
15768
  if (!r.ok) {
15449
- opts.log?.warn?.(`[${PLUGIN_NAME17}] 扫描失败:${r.error}`);
15769
+ opts.log?.warn?.(`[${PLUGIN_NAME16}] 扫描失败:${r.error}`);
15450
15770
  return { ok: false, injected: false, reason: "scan_error", error: r.error };
15451
15771
  }
15452
15772
  const plan = r.plan;
@@ -15460,7 +15780,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
15460
15780
  await opts.injectRecovery(injection);
15461
15781
  } catch (err) {
15462
15782
  const msg = err instanceof Error ? err.message : String(err);
15463
- opts.log?.warn?.(`[${PLUGIN_NAME17}] injectRecovery 异常:${msg}`);
15783
+ opts.log?.warn?.(`[${PLUGIN_NAME16}] injectRecovery 异常:${msg}`);
15464
15784
  return { ok: false, injected: false, plan, reason: "inject_error", error: msg };
15465
15785
  }
15466
15786
  }
@@ -15489,13 +15809,13 @@ function renderPrompt(plan) {
15489
15809
  return lines.join(`
15490
15810
  `);
15491
15811
  }
15492
- var log8 = makePluginLogger(PLUGIN_NAME17);
15812
+ var log8 = makePluginLogger(PLUGIN_NAME16);
15493
15813
  var _lastInjection = null;
15494
15814
  var sessionRecoveryServer = async (ctx) => {
15495
- logLifecycle(PLUGIN_NAME17, "activate", { directory: ctx.directory });
15815
+ logLifecycle(PLUGIN_NAME16, "activate", { directory: ctx.directory });
15496
15816
  return {
15497
15817
  event: async ({ event }) => {
15498
- await safeAsync(PLUGIN_NAME17, "event", async () => {
15818
+ await safeAsync(PLUGIN_NAME16, "event", async () => {
15499
15819
  const e = event;
15500
15820
  if (!e || typeof e.type !== "string")
15501
15821
  return;
@@ -15510,7 +15830,7 @@ var sessionRecoveryServer = async (ctx) => {
15510
15830
  _lastInjection = inj;
15511
15831
  }
15512
15832
  });
15513
- safeWriteLog(PLUGIN_NAME17, {
15833
+ safeWriteLog(PLUGIN_NAME16, {
15514
15834
  hook: "event",
15515
15835
  type: "session.start",
15516
15836
  ok: r.ok,
@@ -15519,13 +15839,13 @@ var sessionRecoveryServer = async (ctx) => {
15519
15839
  last_session_id: r.plan?.last_session_id
15520
15840
  });
15521
15841
  if (r.injected && r.plan) {
15522
- log8.info(`[${PLUGIN_NAME17}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason})`);
15842
+ log8.info(`[${PLUGIN_NAME16}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason})`);
15523
15843
  }
15524
15844
  });
15525
15845
  }
15526
15846
  };
15527
15847
  };
15528
- var handler17 = sessionRecoveryServer;
15848
+ var handler16 = sessionRecoveryServer;
15529
15849
 
15530
15850
  // plugins/subtasks.ts
15531
15851
  import { promises as fs10 } from "node:fs";
@@ -16050,7 +16370,7 @@ async function sendParentNotice(client, sessionID, text, opts = {}) {
16050
16370
  // plugins/subtasks.ts
16051
16371
  init_opencode_plugin_helpers();
16052
16372
  init_runtime_paths();
16053
- var PLUGIN_NAME18 = "subtasks";
16373
+ var PLUGIN_NAME17 = "subtasks";
16054
16374
  function getLogFile(root = process.cwd()) {
16055
16375
  return path13.join(runtimeDir(root), "logs", "subtasks.log");
16056
16376
  }
@@ -16129,7 +16449,7 @@ async function handleParallelCommand(raw) {
16129
16449
  specs = splitDescriptions(args.description, { parentId });
16130
16450
  }
16131
16451
  if (specs.length === 0) {
16132
- log9?.warn(`[${PLUGIN_NAME18}] /parallel 缺有效子任务`);
16452
+ log9?.warn(`[${PLUGIN_NAME17}] /parallel 缺有效子任务`);
16133
16453
  await safeReply2(ctx, "⚠ /parallel 需要至少 1 个子任务(用 ; 或换行分隔)");
16134
16454
  return { ok: false, reason: "no_subtasks" };
16135
16455
  }
@@ -16187,7 +16507,7 @@ async function handleParallelCommand(raw) {
16187
16507
  });
16188
16508
  } catch (err) {
16189
16509
  const msg = err instanceof Error ? err.message : String(err);
16190
- log9?.error(`[${PLUGIN_NAME18}] schedule 抛错`, { error: msg });
16510
+ log9?.error(`[${PLUGIN_NAME17}] schedule 抛错`, { error: msg });
16191
16511
  await safeReply2(ctx, `❌ 并发调度失败:${msg}`);
16192
16512
  return { ok: false, reason: msg };
16193
16513
  }
@@ -16195,7 +16515,7 @@ async function handleParallelCommand(raw) {
16195
16515
  const summaryLines = [head, "", "```", result.digest.text, "```"];
16196
16516
  await safeReply2(ctx, summaryLines.join(`
16197
16517
  `));
16198
- log9?.info(`[${PLUGIN_NAME18}] schedule 完成`, {
16518
+ log9?.info(`[${PLUGIN_NAME17}] schedule 完成`, {
16199
16519
  parentId,
16200
16520
  success: result.digest.success,
16201
16521
  failed: result.digest.failed,
@@ -16205,7 +16525,7 @@ async function handleParallelCommand(raw) {
16205
16525
  try {
16206
16526
  await ctx.onCompleted(result);
16207
16527
  } catch (err) {
16208
- log9?.warn(`[${PLUGIN_NAME18}] onCompleted hook 抛错(已隔离)`, {
16528
+ log9?.warn(`[${PLUGIN_NAME17}] onCompleted hook 抛错(已隔离)`, {
16209
16529
  error: err instanceof Error ? err.message : String(err)
16210
16530
  });
16211
16531
  }
@@ -16246,7 +16566,7 @@ async function writeLog(level, msg, data) {
16246
16566
  const line = JSON.stringify({
16247
16567
  ts: new Date().toISOString(),
16248
16568
  level,
16249
- plugin: PLUGIN_NAME18,
16569
+ plugin: PLUGIN_NAME17,
16250
16570
  msg,
16251
16571
  data
16252
16572
  }) + `
@@ -16257,11 +16577,11 @@ async function writeLog(level, msg, data) {
16257
16577
  await fs10.appendFile(logFile, line, "utf8");
16258
16578
  } catch {}
16259
16579
  }
16260
- logLifecycle(PLUGIN_NAME18, "import");
16580
+ logLifecycle(PLUGIN_NAME17, "import");
16261
16581
  var subtasksServer = async (ctx) => {
16262
- const log9 = makePluginLogger(PLUGIN_NAME18);
16582
+ const log9 = makePluginLogger(PLUGIN_NAME17);
16263
16583
  const client = ctx?.client ?? undefined;
16264
- logLifecycle(PLUGIN_NAME18, "activate", {
16584
+ logLifecycle(PLUGIN_NAME17, "activate", {
16265
16585
  directory: ctx.directory,
16266
16586
  hasClient: Boolean(client)
16267
16587
  });
@@ -16321,20 +16641,20 @@ var subtasksServer = async (ctx) => {
16321
16641
  });
16322
16642
  }
16323
16643
  } catch (err) {
16324
- log9.error(`[${PLUGIN_NAME18}] command.execute.before 异常(已隔离)`, {
16644
+ log9.error(`[${PLUGIN_NAME17}] command.execute.before 异常(已隔离)`, {
16325
16645
  error: err instanceof Error ? err.message : String(err)
16326
16646
  });
16327
16647
  }
16328
16648
  }
16329
16649
  };
16330
16650
  };
16331
- var handler18 = subtasksServer;
16651
+ var handler17 = subtasksServer;
16332
16652
 
16333
16653
  // plugins/terminal-monitor.ts
16334
16654
  init_opencode_plugin_helpers();
16335
16655
  import * as crypto5 from "node:crypto";
16336
- var PLUGIN_NAME19 = "terminal-monitor";
16337
- logLifecycle(PLUGIN_NAME19, "import", {});
16656
+ var PLUGIN_NAME18 = "terminal-monitor";
16657
+ logLifecycle(PLUGIN_NAME18, "import", {});
16338
16658
  var DEFAULT_CONFIG7 = {
16339
16659
  minScore: 0.6,
16340
16660
  cooldownMs: 30000,
@@ -16616,17 +16936,17 @@ function describeError(err) {
16616
16936
  return String(err);
16617
16937
  }
16618
16938
  }
16619
- var log9 = makePluginLogger(PLUGIN_NAME19);
16939
+ var log9 = makePluginLogger(PLUGIN_NAME18);
16620
16940
  var lru = new FingerprintLRU2;
16621
16941
  var _lastNotification = null;
16622
16942
  var terminalMonitorServer = async (ctx) => {
16623
- logLifecycle(PLUGIN_NAME19, "activate", {
16943
+ logLifecycle(PLUGIN_NAME18, "activate", {
16624
16944
  directory: ctx.directory,
16625
16945
  triggerEventTypes: ["terminal.output", "terminal.exit"]
16626
16946
  });
16627
16947
  return {
16628
16948
  event: async ({ event }) => {
16629
- await safeAsync(PLUGIN_NAME19, "event", async () => {
16949
+ await safeAsync(PLUGIN_NAME18, "event", async () => {
16630
16950
  const e = event;
16631
16951
  if (!e || typeof e.type !== "string")
16632
16952
  return;
@@ -16640,7 +16960,7 @@ var terminalMonitorServer = async (ctx) => {
16640
16960
  _lastNotification = msg;
16641
16961
  }
16642
16962
  });
16643
- safeWriteLog(PLUGIN_NAME19, {
16963
+ safeWriteLog(PLUGIN_NAME18, {
16644
16964
  hook: "event",
16645
16965
  type: e.type,
16646
16966
  notified: r.notified,
@@ -16648,18 +16968,18 @@ var terminalMonitorServer = async (ctx) => {
16648
16968
  findings: r.notification?.findings.length ?? 0
16649
16969
  });
16650
16970
  if (r.notified && r.notification) {
16651
- log9.info(`[${PLUGIN_NAME19}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
16971
+ log9.info(`[${PLUGIN_NAME18}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
16652
16972
  }
16653
16973
  });
16654
16974
  }
16655
16975
  };
16656
16976
  };
16657
- var handler19 = terminalMonitorServer;
16977
+ var handler18 = terminalMonitorServer;
16658
16978
 
16659
16979
  // plugins/token-manager.ts
16660
16980
  init_opencode_plugin_helpers();
16661
- var PLUGIN_NAME20 = "token-manager";
16662
- logLifecycle(PLUGIN_NAME20, "import", {});
16981
+ var PLUGIN_NAME19 = "token-manager";
16982
+ logLifecycle(PLUGIN_NAME19, "import", {});
16663
16983
  async function handleMessageBefore(raw, log10, defaults) {
16664
16984
  const ctx = raw ?? {};
16665
16985
  if (!Array.isArray(ctx.messages) || ctx.messages.length === 0)
@@ -16679,21 +16999,21 @@ async function handleMessageBefore(raw, log10, defaults) {
16679
16999
  };
16680
17000
  if (r.compressed) {
16681
17001
  ctx.messages = r.messages;
16682
- log10?.info(`[${PLUGIN_NAME20}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
17002
+ log10?.info(`[${PLUGIN_NAME19}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
16683
17003
  }
16684
17004
  return r;
16685
17005
  } catch (err) {
16686
- log10?.warn(`[${PLUGIN_NAME20}] 压缩异常(已隔离)`, {
17006
+ log10?.warn(`[${PLUGIN_NAME19}] 压缩异常(已隔离)`, {
16687
17007
  error: err instanceof Error ? err.message : String(err)
16688
17008
  });
16689
17009
  return null;
16690
17010
  }
16691
17011
  }
16692
- var log10 = makePluginLogger(PLUGIN_NAME20);
17012
+ var log10 = makePluginLogger(PLUGIN_NAME19);
16693
17013
  var tokenManagerServer = async (ctx) => {
16694
17014
  const rt = loadRuntimeSync();
16695
17015
  const threshold = rt.runtime.context.condenser_threshold_ratio;
16696
- logLifecycle(PLUGIN_NAME20, "activate", {
17016
+ logLifecycle(PLUGIN_NAME19, "activate", {
16697
17017
  directory: ctx.directory,
16698
17018
  threshold,
16699
17019
  target: DEFAULT_CONDENSE.target,
@@ -16702,7 +17022,7 @@ var tokenManagerServer = async (ctx) => {
16702
17022
  });
16703
17023
  return {
16704
17024
  "experimental.chat.messages.transform": async (_input, output) => {
16705
- await safeAsync(PLUGIN_NAME20, "experimental.chat.messages.transform", async () => {
17025
+ await safeAsync(PLUGIN_NAME19, "experimental.chat.messages.transform", async () => {
16706
17026
  const list = output.messages;
16707
17027
  if (!Array.isArray(list) || list.length === 0)
16708
17028
  return;
@@ -16715,7 +17035,7 @@ var tokenManagerServer = async (ctx) => {
16715
17035
  const r = await handleMessageBefore({ messages: flat }, log10, { threshold });
16716
17036
  if (!r)
16717
17037
  return;
16718
- safeWriteLog(PLUGIN_NAME20, {
17038
+ safeWriteLog(PLUGIN_NAME19, {
16719
17039
  hook: "experimental.chat.messages.transform",
16720
17040
  mode: "observe-only",
16721
17041
  before_msgs: r.before.count,
@@ -16726,13 +17046,13 @@ var tokenManagerServer = async (ctx) => {
16726
17046
  reason: r.reason
16727
17047
  });
16728
17048
  if (r.compressed) {
16729
- log10.warn(`[${PLUGIN_NAME20}] advise condense: ${r.before.count}→${r.after.count} msgs / ${r.before.tokens}→${r.after.tokens} tokens (observe-only, no write-back to avoid opencode message schema mismatch)`);
17049
+ log10.warn(`[${PLUGIN_NAME19}] 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)`);
16730
17050
  }
16731
17051
  });
16732
17052
  }
16733
17053
  };
16734
17054
  };
16735
- var handler20 = tokenManagerServer;
17055
+ var handler19 = tokenManagerServer;
16736
17056
 
16737
17057
  // plugins/tool-policy.ts
16738
17058
  init_opencode_plugin_helpers();
@@ -16975,10 +17295,42 @@ function checkFileAccess(acl, file, op) {
16975
17295
  }
16976
17296
 
16977
17297
  // plugins/tool-policy.ts
16978
- var PLUGIN_NAME21 = "tool-policy";
16979
- logLifecycle(PLUGIN_NAME21, "import", {});
17298
+ var PLUGIN_NAME20 = "tool-policy";
17299
+ logLifecycle(PLUGIN_NAME20, "import", {});
16980
17300
  var EMPTY_ACL = { whitelistMode: false };
16981
- function decideToolCall(ctx, cfg = {}) {
17301
+ var SUBAGENT_APPLY_DENY_LIST = new Set([
17302
+ "coder",
17303
+ "planner",
17304
+ "reviewer"
17305
+ ]);
17306
+ function isSubagentApplyBlocked(tool2, args, currentAgent) {
17307
+ if (tool2 !== "pending_changes")
17308
+ return false;
17309
+ const action = args?.action;
17310
+ if (action !== "apply" && action !== "apply_all")
17311
+ return false;
17312
+ if (!currentAgent)
17313
+ return false;
17314
+ return SUBAGENT_APPLY_DENY_LIST.has(currentAgent);
17315
+ }
17316
+ function decideToolCall(ctx, cfg = {}, currentAgent) {
17317
+ if (isSubagentApplyBlocked(ctx.tool, ctx.args, currentAgent)) {
17318
+ const action2 = String(ctx.args?.action);
17319
+ return {
17320
+ action: "deny",
17321
+ reasons: [
17322
+ `[ADR-0061] subagent '${currentAgent}' 禁止调 pending_changes.${action2}` + ` — apply 必须由 codeforge orchestrator 或用户拍板`,
17323
+ `替代路径:stage 完成后回报 codeforge,由其决定 apply 后通过 task_id 复用启动你跑测试`
17324
+ ],
17325
+ autonomy: {
17326
+ effective_mode: ctx.mode ?? cfg.defaultMode ?? DEFAULT_RUNTIME.autonomy.default_mode,
17327
+ action: "confirm",
17328
+ downgraded: false,
17329
+ detected_risks: ["subagent_apply_blocked"],
17330
+ reason: `subagent '${currentAgent}' 自 apply 越权 (ADR-0061)`
17331
+ }
17332
+ };
17333
+ }
16982
17334
  const fallbackMode = cfg.defaultMode ?? DEFAULT_RUNTIME.autonomy.default_mode;
16983
17335
  const mode = ctx.mode ?? fallbackMode;
16984
17336
  const a = evaluate2(mode, {
@@ -17028,13 +17380,39 @@ function classifyToolKind(toolName) {
17028
17380
  return "webfetch";
17029
17381
  return "other";
17030
17382
  }
17031
- var log11 = makePluginLogger(PLUGIN_NAME21);
17383
+ async function resolveCurrentAgent(client, sessionID, log11) {
17384
+ try {
17385
+ const sessionApi = client?.session;
17386
+ if (!sessionApi || typeof sessionApi.get !== "function") {
17387
+ log11.warn(`client.session.get unavailable`, { sessionID });
17388
+ return;
17389
+ }
17390
+ const res = await sessionApi.get({ path: { id: sessionID } });
17391
+ const data = res?.data ?? res;
17392
+ const rawAgent = data?.agent;
17393
+ if (typeof rawAgent !== "string" || rawAgent === "") {
17394
+ log11.warn(`client.session.get returned no string agent (保守放行)`, {
17395
+ sessionID,
17396
+ dataKeys: data && typeof data === "object" ? Object.keys(data) : null
17397
+ });
17398
+ return;
17399
+ }
17400
+ return rawAgent;
17401
+ } catch (err) {
17402
+ log11.warn(`client.session.get failed (保守放行)`, {
17403
+ sessionID,
17404
+ error: err instanceof Error ? err.message : String(err)
17405
+ });
17406
+ return;
17407
+ }
17408
+ }
17409
+ var log11 = makePluginLogger(PLUGIN_NAME20);
17032
17410
  var toolPolicyServer = async (ctx) => {
17033
17411
  const directory = ctx.directory ?? process.cwd();
17034
17412
  const cfg = await loadPolicy(directory);
17035
17413
  const rt = loadRuntimeSync();
17036
17414
  cfg.defaultMode = rt.runtime.autonomy.default_mode;
17037
- logLifecycle(PLUGIN_NAME21, "activate", {
17415
+ logLifecycle(PLUGIN_NAME20, "activate", {
17038
17416
  directory,
17039
17417
  acl_loaded: !!cfg.acl,
17040
17418
  default_mode: cfg.defaultMode,
@@ -17042,9 +17420,15 @@ var toolPolicyServer = async (ctx) => {
17042
17420
  });
17043
17421
  return {
17044
17422
  "tool.execute.before": async (input, output) => {
17045
- await safeAsync(PLUGIN_NAME21, "tool.execute.before", async () => {
17423
+ let denied;
17424
+ await safeAsync(PLUGIN_NAME20, "tool.execute.before", async () => {
17046
17425
  const toolName = input.tool;
17047
17426
  const argsObj = output.args ?? {};
17427
+ const needsAgentDetection = toolName === "pending_changes" && (argsObj.action === "apply" || argsObj.action === "apply_all");
17428
+ let currentAgent = undefined;
17429
+ if (needsAgentDetection) {
17430
+ currentAgent = await resolveCurrentAgent(ctx.client, input.sessionID, log11);
17431
+ }
17048
17432
  const files = [];
17049
17433
  const filePath = argsObj["path"] ?? argsObj["filePath"] ?? argsObj["file"];
17050
17434
  if (typeof filePath === "string") {
@@ -17057,25 +17441,33 @@ var toolPolicyServer = async (ctx) => {
17057
17441
  args: argsObj,
17058
17442
  mode: cfg.defaultMode,
17059
17443
  files: files.length ? files : undefined
17060
- }, cfg);
17061
- safeWriteLog(PLUGIN_NAME21, {
17444
+ }, cfg, currentAgent);
17445
+ safeWriteLog(PLUGIN_NAME20, {
17062
17446
  hook: "tool.execute.before",
17063
17447
  tool: toolName,
17064
17448
  callID: input.callID,
17065
17449
  sessionID: input.sessionID,
17066
17450
  action: decision.action,
17067
- reasons: decision.reasons
17451
+ reasons: decision.reasons,
17452
+ currentAgent
17068
17453
  });
17069
17454
  if (decision.action === "deny") {
17070
- log11.warn(`[${PLUGIN_NAME21}] DENY ${toolName}: ${decision.reasons.join("; ")}`);
17455
+ log11.warn(`[${PLUGIN_NAME20}] DENY ${toolName}`, {
17456
+ action: argsObj.action,
17457
+ currentAgent,
17458
+ reasons: decision.reasons
17459
+ });
17460
+ denied = new Error(`[tool-policy] DENIED: ${decision.reasons.join(" / ")}`);
17071
17461
  } else if (decision.action === "confirm") {
17072
- log11.info(`[${PLUGIN_NAME21}] CONFIRM ${toolName}: ${decision.reasons.join("; ")}`);
17462
+ log11.info(`[${PLUGIN_NAME20}] CONFIRM ${toolName}: ${decision.reasons.join("; ")}`);
17073
17463
  }
17074
17464
  });
17465
+ if (denied)
17466
+ throw denied;
17075
17467
  }
17076
17468
  };
17077
17469
  };
17078
- var handler21 = toolPolicyServer;
17470
+ var handler20 = toolPolicyServer;
17079
17471
 
17080
17472
  // plugins/update-checker.ts
17081
17473
  init_opencode_plugin_helpers();
@@ -17106,7 +17498,7 @@ import * as zlib from "node:zlib";
17106
17498
  // lib/version-injected.ts
17107
17499
  function getInjectedVersion() {
17108
17500
  try {
17109
- const v = "0.3.9";
17501
+ const v = "0.3.10";
17110
17502
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
17111
17503
  return v;
17112
17504
  }
@@ -17609,20 +18001,20 @@ function compareOpencodeVersion(opts) {
17609
18001
  }
17610
18002
 
17611
18003
  // plugins/update-checker.ts
17612
- var PLUGIN_NAME22 = "update-checker";
17613
- var PLUGIN_VERSION2 = "2.0.0";
17614
- logLifecycle(PLUGIN_NAME22, "import", { version: PLUGIN_VERSION2 });
18004
+ var PLUGIN_NAME21 = "update-checker";
18005
+ var PLUGIN_VERSION = "2.0.0";
18006
+ logLifecycle(PLUGIN_NAME21, "import", { version: PLUGIN_VERSION });
17615
18007
  var updateCheckerServer = async (ctx) => {
17616
18008
  const yieldResult = shouldYieldToLocalPlugin({ directory: ctx.directory });
17617
18009
  if (yieldResult.yield) {
17618
- safeWriteLog(PLUGIN_NAME22, {
18010
+ safeWriteLog(PLUGIN_NAME21, {
17619
18011
  level: "info",
17620
18012
  msg: "dev_mode_yield_skip",
17621
18013
  reason: yieldResult.reason,
17622
18014
  markerPath: yieldResult.markerPath,
17623
18015
  detail: formatYieldLog(yieldResult)
17624
18016
  });
17625
- logLifecycle(PLUGIN_NAME22, "activate", {
18017
+ logLifecycle(PLUGIN_NAME21, "activate", {
17626
18018
  yield_to_local: true,
17627
18019
  yield_reason: yieldResult.reason,
17628
18020
  skipped: "auto_install + background_check"
@@ -17631,8 +18023,8 @@ var updateCheckerServer = async (ctx) => {
17631
18023
  }
17632
18024
  const rt = loadRuntimeSync();
17633
18025
  const u = rt.runtime.update;
17634
- logLifecycle(PLUGIN_NAME22, "activate", {
17635
- version: PLUGIN_VERSION2,
18026
+ logLifecycle(PLUGIN_NAME21, "activate", {
18027
+ version: PLUGIN_VERSION,
17636
18028
  auto_check_enabled: u.auto_check_enabled,
17637
18029
  interval_hours: u.interval_hours,
17638
18030
  package: u.package,
@@ -17643,14 +18035,14 @@ var updateCheckerServer = async (ctx) => {
17643
18035
  repo_fallback: u.repo,
17644
18036
  config_source: "codeforge.json"
17645
18037
  });
17646
- await safeAsync(PLUGIN_NAME22, "opencode_version_check", async () => {
18038
+ await safeAsync(PLUGIN_NAME21, "opencode_version_check", async () => {
17647
18039
  const compat = loadCompatibility();
17648
18040
  const opencodeVer = detectOpencodeVersion();
17649
18041
  const verdict = compareOpencodeVersion({
17650
18042
  currentOpencodeVer: opencodeVer,
17651
18043
  compat
17652
18044
  });
17653
- safeWriteLog(PLUGIN_NAME22, {
18045
+ safeWriteLog(PLUGIN_NAME21, {
17654
18046
  level: "info",
17655
18047
  msg: "opencode_version_check",
17656
18048
  opencodeVer,
@@ -17667,14 +18059,14 @@ var updateCheckerServer = async (ctx) => {
17667
18059
  }
17668
18060
  });
17669
18061
  if (!u.auto_check_enabled) {
17670
- safeWriteLog(PLUGIN_NAME22, {
18062
+ safeWriteLog(PLUGIN_NAME21, {
17671
18063
  level: "info",
17672
18064
  msg: "auto-check disabled (codeforge.json update.auto_check_enabled=false)"
17673
18065
  });
17674
18066
  return {};
17675
18067
  }
17676
18068
  setImmediate(() => {
17677
- safeAsync(PLUGIN_NAME22, "checkAndMaybeUpdate", async () => {
18069
+ safeAsync(PLUGIN_NAME21, "checkAndMaybeUpdate", async () => {
17678
18070
  const local = readLocalVersion();
17679
18071
  let npmResult = null;
17680
18072
  try {
@@ -17685,7 +18077,7 @@ var updateCheckerServer = async (ctx) => {
17685
18077
  timeoutMs: 5000
17686
18078
  });
17687
18079
  } catch (e) {
17688
- safeWriteLog(PLUGIN_NAME22, {
18080
+ safeWriteLog(PLUGIN_NAME21, {
17689
18081
  level: "warn",
17690
18082
  msg: "npm_fetch_failed",
17691
18083
  error: e.message
@@ -17694,7 +18086,7 @@ var updateCheckerServer = async (ctx) => {
17694
18086
  return;
17695
18087
  }
17696
18088
  if (!npmResult) {
17697
- safeWriteLog(PLUGIN_NAME22, {
18089
+ safeWriteLog(PLUGIN_NAME21, {
17698
18090
  level: "info",
17699
18091
  msg: "npm_no_release",
17700
18092
  package: u.package,
@@ -17703,7 +18095,7 @@ var updateCheckerServer = async (ctx) => {
17703
18095
  return;
17704
18096
  }
17705
18097
  const hasUpdate = cmpVersion(local, npmResult.version) < 0;
17706
- safeWriteLog(PLUGIN_NAME22, {
18098
+ safeWriteLog(PLUGIN_NAME21, {
17707
18099
  level: "info",
17708
18100
  msg: "npm_check_result",
17709
18101
  local,
@@ -17718,10 +18110,10 @@ var updateCheckerServer = async (ctx) => {
17718
18110
  更新命令:npx ${u.package} install --global`);
17719
18111
  return;
17720
18112
  }
17721
- await safeAsync(PLUGIN_NAME22, "auto_install_bundle", async () => {
18113
+ await safeAsync(PLUGIN_NAME21, "auto_install_bundle", async () => {
17722
18114
  const target = getOpencodeBundlePath();
17723
18115
  if (!target) {
17724
- safeWriteLog(PLUGIN_NAME22, {
18116
+ safeWriteLog(PLUGIN_NAME21, {
17725
18117
  level: "warn",
17726
18118
  msg: "auto_install_skip",
17727
18119
  reason: "无法定位 opencode bundle 路径"
@@ -17740,7 +18132,7 @@ var updateCheckerServer = async (ctx) => {
17740
18132
  oldVersion: local,
17741
18133
  keepBackups: u.backup_keep
17742
18134
  });
17743
- safeWriteLog(PLUGIN_NAME22, {
18135
+ safeWriteLog(PLUGIN_NAME21, {
17744
18136
  level: "info",
17745
18137
  msg: "auto_install_success",
17746
18138
  local,
@@ -17780,7 +18172,7 @@ function getOpencodeBundlePath() {
17780
18172
  return candidates[0] ?? null;
17781
18173
  }
17782
18174
  async function fallbackToGitHubReleases(ctx, u) {
17783
- await safeAsync(PLUGIN_NAME22, "github_fallback", async () => {
18175
+ await safeAsync(PLUGIN_NAME21, "github_fallback", async () => {
17784
18176
  const result = await checkUpdateOnce({
17785
18177
  repo: u.repo,
17786
18178
  intervalMs: u.interval_hours * 3600 * 1000
@@ -17790,7 +18182,7 @@ async function fallbackToGitHubReleases(ctx, u) {
17790
18182
  }
17791
18183
  async function reportLegacyResult(ctx, result, repo) {
17792
18184
  if (result.error && !result.fromCache) {
17793
- safeWriteLog(PLUGIN_NAME22, {
18185
+ safeWriteLog(PLUGIN_NAME21, {
17794
18186
  level: "warn",
17795
18187
  msg: `update check failed: ${result.error}`,
17796
18188
  ...result
@@ -17798,7 +18190,7 @@ async function reportLegacyResult(ctx, result, repo) {
17798
18190
  return;
17799
18191
  }
17800
18192
  if (!result.hasUpdate) {
17801
- safeWriteLog(PLUGIN_NAME22, {
18193
+ safeWriteLog(PLUGIN_NAME21, {
17802
18194
  level: "info",
17803
18195
  msg: `up-to-date (local=${result.local}, remote=${result.remote}${result.fromCache ? ", from_cache" : ""})`,
17804
18196
  ...result
@@ -17808,7 +18200,7 @@ async function reportLegacyResult(ctx, result, repo) {
17808
18200
  const updateCmd = `bunx --bun github:${repo} install`;
17809
18201
  const toast = `[CodeForge] 有新版本:${result.local} → ${result.remote}
17810
18202
  更新命令:${updateCmd}`;
17811
- safeWriteLog(PLUGIN_NAME22, {
18203
+ safeWriteLog(PLUGIN_NAME21, {
17812
18204
  level: "info",
17813
18205
  msg: "new_version_available_github_fallback",
17814
18206
  local: result.local,
@@ -17819,17 +18211,17 @@ async function reportLegacyResult(ctx, result, repo) {
17819
18211
  await postToast(ctx, toast);
17820
18212
  }
17821
18213
  async function postToast(ctx, message) {
17822
- await safeAsync(PLUGIN_NAME22, "client.app.log", async () => {
18214
+ await safeAsync(PLUGIN_NAME21, "client.app.log", async () => {
17823
18215
  await ctx.client.app.log({
17824
18216
  body: {
17825
- service: PLUGIN_NAME22,
18217
+ service: PLUGIN_NAME21,
17826
18218
  level: "info",
17827
18219
  message
17828
18220
  }
17829
18221
  });
17830
18222
  });
17831
18223
  }
17832
- var handler22 = updateCheckerServer;
18224
+ var handler21 = updateCheckerServer;
17833
18225
 
17834
18226
  // plugins/workflow-engine.ts
17835
18227
  init_opencode_plugin_helpers();
@@ -18333,9 +18725,9 @@ async function runStepAutoFeedback(step, adapter) {
18333
18725
  }
18334
18726
 
18335
18727
  // plugins/workflow-engine.ts
18336
- var PLUGIN_NAME23 = "workflow-engine";
18337
- logLifecycle(PLUGIN_NAME23, "import", {});
18338
- var fallbackLog2 = makePluginLogger(PLUGIN_NAME23);
18728
+ var PLUGIN_NAME22 = "workflow-engine";
18729
+ logLifecycle(PLUGIN_NAME22, "import", {});
18730
+ var fallbackLog2 = makePluginLogger(PLUGIN_NAME22);
18339
18731
  var _registry = null;
18340
18732
  async function loadRegistry(workflowsDir) {
18341
18733
  const { loaded, failed } = await loadWorkflowsFromDir(workflowsDir);
@@ -18355,32 +18747,32 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
18355
18747
  const log12 = ctx.log ?? fallbackLog2;
18356
18748
  const command = typeof ctx.command === "string" ? ctx.command : null;
18357
18749
  if (!command) {
18358
- log12.warn(`[${PLUGIN_NAME23}] command.invoked 缺 command 字段`, ctx);
18750
+ log12.warn(`[${PLUGIN_NAME22}] command.invoked 缺 command 字段`, ctx);
18359
18751
  return null;
18360
18752
  }
18361
18753
  const reg = await ensureRegistry(workflowsDir);
18362
18754
  if (reg.errors.length) {
18363
- log12.warn(`[${PLUGIN_NAME23}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
18755
+ log12.warn(`[${PLUGIN_NAME22}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
18364
18756
  }
18365
18757
  const wf = reg.workflows.find((w) => matchesTrigger(w, command));
18366
18758
  if (!wf) {
18367
- log12.info(`[${PLUGIN_NAME23}] no workflow matches "${command}"`);
18759
+ log12.info(`[${PLUGIN_NAME22}] no workflow matches "${command}"`);
18368
18760
  return null;
18369
18761
  }
18370
- log12.info(`[${PLUGIN_NAME23}] dispatch "${command}" → workflow "${wf.name}"`);
18762
+ log12.info(`[${PLUGIN_NAME22}] dispatch "${command}" → workflow "${wf.name}"`);
18371
18763
  try {
18372
18764
  const result = await run(wf, {
18373
18765
  mode: ctx.adapter ? "real" : "dry_run",
18374
18766
  autonomy: ctx.autonomy ?? "semi",
18375
18767
  adapter: ctx.adapter
18376
18768
  });
18377
- log12.info(`[${PLUGIN_NAME23}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
18769
+ log12.info(`[${PLUGIN_NAME22}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
18378
18770
  steps: result.plan.steps.length,
18379
18771
  results: result.results.length
18380
18772
  });
18381
18773
  return result;
18382
18774
  } catch (err) {
18383
- log12.error(`[${PLUGIN_NAME23}] workflow "${wf.name}" 执行失败`, {
18775
+ log12.error(`[${PLUGIN_NAME22}] workflow "${wf.name}" 执行失败`, {
18384
18776
  error: err instanceof Error ? err.message : String(err)
18385
18777
  });
18386
18778
  throw err;
@@ -18389,15 +18781,15 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
18389
18781
  var workflowEngineServer = async (ctx) => {
18390
18782
  const directory = ctx.directory ?? process.cwd();
18391
18783
  const workflowsDir = path17.join(directory, "workflows");
18392
- ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME23}] preload workflows failed`, {
18784
+ ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME22}] preload workflows failed`, {
18393
18785
  error: err instanceof Error ? err.message : String(err)
18394
18786
  }));
18395
- logLifecycle(PLUGIN_NAME23, "activate", { directory, workflowsDir });
18787
+ logLifecycle(PLUGIN_NAME22, "activate", { directory, workflowsDir });
18396
18788
  return {
18397
18789
  "command.execute.before": async (input, output) => {
18398
- await safeAsync(PLUGIN_NAME23, "command.execute.before", async () => {
18790
+ await safeAsync(PLUGIN_NAME22, "command.execute.before", async () => {
18399
18791
  const cmd = input.command.startsWith("/") ? input.command : `/${input.command}`;
18400
- safeWriteLog(PLUGIN_NAME23, {
18792
+ safeWriteLog(PLUGIN_NAME22, {
18401
18793
  hook: "command.execute.before",
18402
18794
  command: cmd,
18403
18795
  sessionID: input.sessionID
@@ -18412,14 +18804,14 @@ var workflowEngineServer = async (ctx) => {
18412
18804
  });
18413
18805
  },
18414
18806
  "chat.message": async (input, output) => {
18415
- await safeAsync(PLUGIN_NAME23, "chat.message", async () => {
18807
+ await safeAsync(PLUGIN_NAME22, "chat.message", async () => {
18416
18808
  const text = extractUserText(output).trim();
18417
18809
  if (!text.startsWith("/"))
18418
18810
  return;
18419
18811
  const cmd = text.split(/\s+/)[0];
18420
18812
  if (!cmd)
18421
18813
  return;
18422
- safeWriteLog(PLUGIN_NAME23, {
18814
+ safeWriteLog(PLUGIN_NAME22, {
18423
18815
  hook: "chat.message",
18424
18816
  command: cmd,
18425
18817
  sessionID: input.sessionID
@@ -18429,7 +18821,7 @@ var workflowEngineServer = async (ctx) => {
18429
18821
  }
18430
18822
  };
18431
18823
  };
18432
- var handler23 = workflowEngineServer;
18824
+ var handler22 = workflowEngineServer;
18433
18825
 
18434
18826
  // src/index.ts
18435
18827
  var PLUGIN_ID = "codeforge";
@@ -18443,22 +18835,21 @@ var HANDLERS = [
18443
18835
  { name: "auto-learning", init: handler5 },
18444
18836
  { name: "channels", init: handler6 },
18445
18837
  { name: "codeforge-tools", init: handler8 },
18446
- { name: "hello-world", init: handler9 },
18447
- { name: "kh-auto-context", init: handler10 },
18448
- { name: "kh-reminder", init: handler11 },
18449
- { name: "memories-context", init: handler12 },
18450
- { name: "model-fallback", init: handler13 },
18451
- { name: "pwsh-utf8", init: handler16 },
18452
- { name: "session-recovery", init: handler17 },
18453
- { name: "subtask-heartbeat", init: handler14 },
18454
- { name: "subtasks", init: handler18 },
18455
- { name: "parallel-status", init: handler15 },
18456
- { name: "terminal-monitor", init: handler19 },
18457
- { name: "token-manager", init: handler20 },
18838
+ { name: "kh-auto-context", init: handler9 },
18839
+ { name: "kh-reminder", init: handler10 },
18840
+ { name: "memories-context", init: handler11 },
18841
+ { name: "model-fallback", init: handler12 },
18842
+ { name: "pwsh-utf8", init: handler15 },
18843
+ { name: "session-recovery", init: handler16 },
18844
+ { name: "subtask-heartbeat", init: handler13 },
18845
+ { name: "subtasks", init: handler17 },
18846
+ { name: "parallel-status", init: handler14 },
18847
+ { name: "terminal-monitor", init: handler18 },
18848
+ { name: "token-manager", init: handler19 },
18458
18849
  { name: "tool-heartbeat", init: handler7 },
18459
- { name: "tool-policy", init: handler21 },
18460
- { name: "update-checker", init: handler22 },
18461
- { name: "workflow-engine", init: handler23 }
18850
+ { name: "tool-policy", init: handler20 },
18851
+ { name: "update-checker", init: handler21 },
18852
+ { name: "workflow-engine", init: handler22 }
18462
18853
  ];
18463
18854
  function makeSerialHook(hookName, fns) {
18464
18855
  return async (input, output) => {