@andyqiu/codeforge 0.5.8 → 0.5.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/agents/discover.md +14 -1
- package/commands/pause.md +4 -4
- package/dist/index.js +629 -327
- package/package.json +1 -1
- package/commands/autonomy.md +0 -91
package/dist/index.js
CHANGED
|
@@ -10119,6 +10119,148 @@ var channelsServer = async (ctx) => {
|
|
|
10119
10119
|
};
|
|
10120
10120
|
var handler5 = channelsServer;
|
|
10121
10121
|
|
|
10122
|
+
// lib/agent-resolver.ts
|
|
10123
|
+
var chatAgentCacheReader = null;
|
|
10124
|
+
function setChatAgentCacheReader(reader) {
|
|
10125
|
+
chatAgentCacheReader = reader;
|
|
10126
|
+
}
|
|
10127
|
+
async function resolveCurrentAgent(client, sessionID, log4) {
|
|
10128
|
+
try {
|
|
10129
|
+
const sessionApi = client?.session;
|
|
10130
|
+
if (!sessionApi || typeof sessionApi.get !== "function") {
|
|
10131
|
+
log4.warn(`client.session.get unavailable`, { sessionID });
|
|
10132
|
+
return;
|
|
10133
|
+
}
|
|
10134
|
+
const res = await sessionApi.get({ path: { id: sessionID } });
|
|
10135
|
+
const data = res?.data ?? res;
|
|
10136
|
+
const rawAgent = data?.agent;
|
|
10137
|
+
if (typeof rawAgent !== "string" || rawAgent === "") {
|
|
10138
|
+
log4.warn(`client.session.get returned no string agent (返回 undefined)`, {
|
|
10139
|
+
sessionID,
|
|
10140
|
+
dataKeys: data && typeof data === "object" ? Object.keys(data) : null
|
|
10141
|
+
});
|
|
10142
|
+
return;
|
|
10143
|
+
}
|
|
10144
|
+
return rawAgent;
|
|
10145
|
+
} catch (err) {
|
|
10146
|
+
log4.warn(`client.session.get failed (返回 undefined)`, {
|
|
10147
|
+
sessionID,
|
|
10148
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10149
|
+
});
|
|
10150
|
+
return;
|
|
10151
|
+
}
|
|
10152
|
+
}
|
|
10153
|
+
async function resolveAgentForGuard(input, client, log4, opts = {}) {
|
|
10154
|
+
if (typeof input.agent === "string" && input.agent.length > 0) {
|
|
10155
|
+
return input.agent;
|
|
10156
|
+
}
|
|
10157
|
+
if (!opts.skipCache && chatAgentCacheReader) {
|
|
10158
|
+
try {
|
|
10159
|
+
const cached = chatAgentCacheReader(input.sessionID);
|
|
10160
|
+
if (typeof cached === "string" && cached.length > 0) {
|
|
10161
|
+
return cached;
|
|
10162
|
+
}
|
|
10163
|
+
} catch (err) {
|
|
10164
|
+
log4.warn(`chat-agent-cache reader failed (降级到 L2 IPC)`, {
|
|
10165
|
+
sessionID: input.sessionID,
|
|
10166
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10167
|
+
});
|
|
10168
|
+
}
|
|
10169
|
+
}
|
|
10170
|
+
if (!opts.skipIpcLookup) {
|
|
10171
|
+
const viaSession = await resolveCurrentAgent(client, input.sessionID, log4);
|
|
10172
|
+
if (typeof viaSession === "string" && viaSession.length > 0) {
|
|
10173
|
+
return viaSession;
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10176
|
+
return null;
|
|
10177
|
+
}
|
|
10178
|
+
|
|
10179
|
+
// plugins/chat-agent-cache.ts
|
|
10180
|
+
var PLUGIN_NAME6 = "chat-agent-cache";
|
|
10181
|
+
logLifecycle(PLUGIN_NAME6, "import", {});
|
|
10182
|
+
var SESSION_CAP = 500;
|
|
10183
|
+
var SESSION_TTL_MS = 24 * 60 * 60 * 1000;
|
|
10184
|
+
var sessionAgentMap = new Map;
|
|
10185
|
+
function pruneIfOversize() {
|
|
10186
|
+
while (sessionAgentMap.size > SESSION_CAP) {
|
|
10187
|
+
const oldestKey = sessionAgentMap.keys().next().value;
|
|
10188
|
+
if (oldestKey === undefined)
|
|
10189
|
+
break;
|
|
10190
|
+
sessionAgentMap.delete(oldestKey);
|
|
10191
|
+
}
|
|
10192
|
+
}
|
|
10193
|
+
function isExpired(entry, now = Date.now()) {
|
|
10194
|
+
return now - entry.ts > SESSION_TTL_MS;
|
|
10195
|
+
}
|
|
10196
|
+
function rememberSessionAgent(sessionID, agent) {
|
|
10197
|
+
if (typeof sessionID !== "string" || sessionID.length === 0)
|
|
10198
|
+
return;
|
|
10199
|
+
if (typeof agent !== "string" || agent.length === 0)
|
|
10200
|
+
return;
|
|
10201
|
+
sessionAgentMap.set(sessionID, { agent, ts: Date.now() });
|
|
10202
|
+
pruneIfOversize();
|
|
10203
|
+
}
|
|
10204
|
+
function readSessionAgent(sessionID) {
|
|
10205
|
+
if (typeof sessionID !== "string" || sessionID.length === 0)
|
|
10206
|
+
return null;
|
|
10207
|
+
const entry = sessionAgentMap.get(sessionID);
|
|
10208
|
+
if (!entry)
|
|
10209
|
+
return null;
|
|
10210
|
+
if (isExpired(entry)) {
|
|
10211
|
+
sessionAgentMap.delete(sessionID);
|
|
10212
|
+
return null;
|
|
10213
|
+
}
|
|
10214
|
+
return entry.agent;
|
|
10215
|
+
}
|
|
10216
|
+
var chatAgentCachePlugin = async (ctx) => {
|
|
10217
|
+
setChatAgentCacheReader(readSessionAgent);
|
|
10218
|
+
logLifecycle(PLUGIN_NAME6, "activate", {
|
|
10219
|
+
directory: ctx.directory,
|
|
10220
|
+
session_cap: SESSION_CAP,
|
|
10221
|
+
session_ttl_ms: SESSION_TTL_MS
|
|
10222
|
+
});
|
|
10223
|
+
return {
|
|
10224
|
+
"chat.params": async (input, _output) => {
|
|
10225
|
+
await safeAsync(PLUGIN_NAME6, "chat.params", async () => {
|
|
10226
|
+
const sid = input?.sessionID;
|
|
10227
|
+
const agent = input?.agent;
|
|
10228
|
+
if (typeof sid !== "string" || sid.length === 0)
|
|
10229
|
+
return;
|
|
10230
|
+
if (typeof agent !== "string" || agent.length === 0)
|
|
10231
|
+
return;
|
|
10232
|
+
rememberSessionAgent(sid, agent);
|
|
10233
|
+
safeWriteLog(PLUGIN_NAME6, {
|
|
10234
|
+
hook: "chat.params",
|
|
10235
|
+
sessionID: sid,
|
|
10236
|
+
agent,
|
|
10237
|
+
action: "cache",
|
|
10238
|
+
map_size: sessionAgentMap.size
|
|
10239
|
+
});
|
|
10240
|
+
});
|
|
10241
|
+
},
|
|
10242
|
+
"chat.message": async (input, _output) => {
|
|
10243
|
+
await safeAsync(PLUGIN_NAME6, "chat.message", async () => {
|
|
10244
|
+
const sid = input?.sessionID;
|
|
10245
|
+
const agent = input?.agent;
|
|
10246
|
+
if (typeof sid !== "string" || sid.length === 0)
|
|
10247
|
+
return;
|
|
10248
|
+
if (typeof agent !== "string" || agent.length === 0)
|
|
10249
|
+
return;
|
|
10250
|
+
rememberSessionAgent(sid, agent);
|
|
10251
|
+
safeWriteLog(PLUGIN_NAME6, {
|
|
10252
|
+
hook: "chat.message",
|
|
10253
|
+
sessionID: sid,
|
|
10254
|
+
agent,
|
|
10255
|
+
action: "cache",
|
|
10256
|
+
map_size: sessionAgentMap.size
|
|
10257
|
+
});
|
|
10258
|
+
});
|
|
10259
|
+
}
|
|
10260
|
+
};
|
|
10261
|
+
};
|
|
10262
|
+
var handler6 = chatAgentCachePlugin;
|
|
10263
|
+
|
|
10122
10264
|
// plugins/codeforge-tools.ts
|
|
10123
10265
|
import { tool } from "@opencode-ai/plugin";
|
|
10124
10266
|
|
|
@@ -13421,8 +13563,8 @@ async function pruneOrphanWorktrees(mainRoot) {
|
|
|
13421
13563
|
var DEFAULT_MERGE_LOOP_CONFIG = {
|
|
13422
13564
|
maxReviewLoops: 3,
|
|
13423
13565
|
autoCoder: true,
|
|
13424
|
-
reviewTimeoutMs:
|
|
13425
|
-
coderTimeoutMs:
|
|
13566
|
+
reviewTimeoutMs: 180000,
|
|
13567
|
+
coderTimeoutMs: 600000,
|
|
13426
13568
|
abortDirtyStrategy: "checkpoint"
|
|
13427
13569
|
};
|
|
13428
13570
|
async function runMergeLoop(opts) {
|
|
@@ -13472,7 +13614,11 @@ async function runMergeLoop(opts) {
|
|
|
13472
13614
|
maxRounds: config.maxReviewLoops,
|
|
13473
13615
|
...lastReviewSummary ? { prevSummary: lastReviewSummary } : {},
|
|
13474
13616
|
...opts.signal ? { signal: opts.signal } : {}
|
|
13475
|
-
}), config.reviewTimeoutMs, `reviewer 第 ${loops} 轮`, opts.signal
|
|
13617
|
+
}), config.reviewTimeoutMs, `reviewer 第 ${loops} 轮`, opts.signal, {
|
|
13618
|
+
onHeartbeat: (elapsedMs) => {
|
|
13619
|
+
progress("dispatch_review", `reviewer 第 ${loops}/${config.maxReviewLoops} 轮仍在运行,已等待 ${Math.round(elapsedMs / 1000)}s`);
|
|
13620
|
+
}
|
|
13621
|
+
});
|
|
13476
13622
|
} catch (err) {
|
|
13477
13623
|
const e = err;
|
|
13478
13624
|
if (isAbortError2(e)) {
|
|
@@ -13541,7 +13687,11 @@ async function runMergeLoop(opts) {
|
|
|
13541
13687
|
...opts.planId ? { planId: opts.planId } : {},
|
|
13542
13688
|
reviewerSummary: reviewResult.summary,
|
|
13543
13689
|
...opts.signal ? { signal: opts.signal } : {}
|
|
13544
|
-
}), config.coderTimeoutMs, `coder round ${loops}`, opts.signal
|
|
13690
|
+
}), config.coderTimeoutMs, `coder round ${loops}`, opts.signal, {
|
|
13691
|
+
onHeartbeat: (elapsedMs) => {
|
|
13692
|
+
progress("dispatch_coder", `coder round ${loops} 仍在运行,已等待 ${Math.round(elapsedMs / 1000)}s`);
|
|
13693
|
+
}
|
|
13694
|
+
});
|
|
13545
13695
|
progress("wait_coder", `coder 完成: ${coderResult.ok ? "ok" : "fail"} - ${coderResult.summary}`);
|
|
13546
13696
|
if (!coderResult.ok) {
|
|
13547
13697
|
return {
|
|
@@ -13624,34 +13774,50 @@ async function handleAbortDirty(opts, config, entry) {
|
|
|
13624
13774
|
function isAbortError2(err) {
|
|
13625
13775
|
return err instanceof Error && err.name === "AbortError";
|
|
13626
13776
|
}
|
|
13627
|
-
function withTimeout2(p, ms, label, signal) {
|
|
13777
|
+
function withTimeout2(p, ms, label, signal, hbOpts) {
|
|
13628
13778
|
return new Promise((resolve11, reject) => {
|
|
13629
|
-
const
|
|
13779
|
+
const startedAt = Date.now();
|
|
13780
|
+
let hbTimer = null;
|
|
13781
|
+
let timer;
|
|
13782
|
+
const cleanup = () => {
|
|
13783
|
+
clearTimeout(timer);
|
|
13784
|
+
if (hbTimer)
|
|
13785
|
+
clearInterval(hbTimer);
|
|
13786
|
+
if (signal)
|
|
13787
|
+
signal.removeEventListener("abort", onAbort);
|
|
13788
|
+
};
|
|
13789
|
+
timer = setTimeout(() => {
|
|
13790
|
+
cleanup();
|
|
13630
13791
|
reject(new Error(`${label} 超时 (${ms}ms)`));
|
|
13631
13792
|
}, ms);
|
|
13793
|
+
const hbInterval = hbOpts?.heartbeatIntervalMs ?? 30000;
|
|
13794
|
+
const hbCb = hbOpts?.onHeartbeat;
|
|
13795
|
+
if (hbCb) {
|
|
13796
|
+
hbTimer = setInterval(() => {
|
|
13797
|
+
try {
|
|
13798
|
+
hbCb(Date.now() - startedAt);
|
|
13799
|
+
} catch {}
|
|
13800
|
+
}, hbInterval);
|
|
13801
|
+
}
|
|
13632
13802
|
const onAbort = () => {
|
|
13633
|
-
|
|
13803
|
+
cleanup();
|
|
13634
13804
|
const err = new Error(`${label} aborted by signal`);
|
|
13635
13805
|
err.name = "AbortError";
|
|
13636
13806
|
reject(err);
|
|
13637
13807
|
};
|
|
13638
13808
|
if (signal) {
|
|
13639
13809
|
if (signal.aborted) {
|
|
13640
|
-
|
|
13810
|
+
cleanup();
|
|
13641
13811
|
onAbort();
|
|
13642
13812
|
return;
|
|
13643
13813
|
}
|
|
13644
13814
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
13645
13815
|
}
|
|
13646
13816
|
p.then((v) => {
|
|
13647
|
-
|
|
13648
|
-
if (signal)
|
|
13649
|
-
signal.removeEventListener("abort", onAbort);
|
|
13817
|
+
cleanup();
|
|
13650
13818
|
resolve11(v);
|
|
13651
13819
|
}, (e) => {
|
|
13652
|
-
|
|
13653
|
-
if (signal)
|
|
13654
|
-
signal.removeEventListener("abort", onAbort);
|
|
13820
|
+
cleanup();
|
|
13655
13821
|
reject(e);
|
|
13656
13822
|
});
|
|
13657
13823
|
});
|
|
@@ -14759,7 +14925,7 @@ class ProductionSpawner {
|
|
|
14759
14925
|
prompt,
|
|
14760
14926
|
title: `[merge-review] sess=${args.sessionId.slice(0, 8)} r=${args.round}/${args.maxRounds}`,
|
|
14761
14927
|
...args.signal ? { signal: args.signal } : {},
|
|
14762
|
-
timeoutMs: this.opts.reviewerTimeoutMs ??
|
|
14928
|
+
timeoutMs: this.opts.reviewerTimeoutMs ?? 180000
|
|
14763
14929
|
}, args.sessionId);
|
|
14764
14930
|
} catch (err) {
|
|
14765
14931
|
throw err;
|
|
@@ -14791,7 +14957,7 @@ ${r.text.slice(0, 800)}`
|
|
|
14791
14957
|
prompt,
|
|
14792
14958
|
title: `[merge-fix] sess=${args.sessionId.slice(0, 8)}`,
|
|
14793
14959
|
...args.signal ? { signal: args.signal } : {},
|
|
14794
|
-
timeoutMs: this.opts.coderTimeoutMs ??
|
|
14960
|
+
timeoutMs: this.opts.coderTimeoutMs ?? 600000
|
|
14795
14961
|
}, args.sessionId);
|
|
14796
14962
|
} catch (err) {
|
|
14797
14963
|
throw err;
|
|
@@ -14856,6 +15022,15 @@ ${r.text.slice(0, 800)}`
|
|
|
14856
15022
|
err: describe5(err)
|
|
14857
15023
|
});
|
|
14858
15024
|
}
|
|
15025
|
+
const mainRoot = this.opts.mainRoot ?? this.opts.directory;
|
|
15026
|
+
if (mainRoot) {
|
|
15027
|
+
const cid = childId;
|
|
15028
|
+
discardSession({ sessionId: cid, mainRoot }).catch((err) => {
|
|
15029
|
+
this.opts.log?.("warn", `[spawner] auto-discard 失败 ${cid}`, {
|
|
15030
|
+
err: describe5(err)
|
|
15031
|
+
});
|
|
15032
|
+
});
|
|
15033
|
+
}
|
|
14859
15034
|
}
|
|
14860
15035
|
}
|
|
14861
15036
|
}
|
|
@@ -14955,7 +15130,6 @@ import { promises as fs14 } from "node:fs";
|
|
|
14955
15130
|
import * as path17 from "node:path";
|
|
14956
15131
|
var DEFAULT_RUNTIME = {
|
|
14957
15132
|
autonomy: {
|
|
14958
|
-
default_mode: "semi",
|
|
14959
15133
|
downgrade_on_risky: true
|
|
14960
15134
|
},
|
|
14961
15135
|
runtime: {
|
|
@@ -15039,13 +15213,6 @@ function parseRuntime(raw, abs) {
|
|
|
15039
15213
|
const cfg = cloneDefaults();
|
|
15040
15214
|
if (root.autonomy && typeof root.autonomy === "object" && !Array.isArray(root.autonomy)) {
|
|
15041
15215
|
const a = root.autonomy;
|
|
15042
|
-
if (a.default_mode !== undefined) {
|
|
15043
|
-
if (a.default_mode === "step" || a.default_mode === "semi" || a.default_mode === "full") {
|
|
15044
|
-
cfg.autonomy.default_mode = a.default_mode;
|
|
15045
|
-
} else {
|
|
15046
|
-
warnings.push(`autonomy.default_mode invalid: ${String(a.default_mode)} (keep ${cfg.autonomy.default_mode})`);
|
|
15047
|
-
}
|
|
15048
|
-
}
|
|
15049
15216
|
if (a.downgrade_on_risky !== undefined) {
|
|
15050
15217
|
if (typeof a.downgrade_on_risky === "boolean") {
|
|
15051
15218
|
cfg.autonomy.downgrade_on_risky = a.downgrade_on_risky;
|
|
@@ -15165,8 +15332,8 @@ function parseRuntime(raw, abs) {
|
|
|
15165
15332
|
}
|
|
15166
15333
|
|
|
15167
15334
|
// plugins/tool-heartbeat.ts
|
|
15168
|
-
var
|
|
15169
|
-
logLifecycle(
|
|
15335
|
+
var PLUGIN_NAME7 = "tool-heartbeat";
|
|
15336
|
+
logLifecycle(PLUGIN_NAME7, "import", {});
|
|
15170
15337
|
var HEARTBEAT_INTERVAL_MS = 15000;
|
|
15171
15338
|
var ALERT_30S_MS = 30000;
|
|
15172
15339
|
var ALERT_60S_MS = 60000;
|
|
@@ -15248,15 +15415,15 @@ async function showToast(client, payload, log5) {
|
|
|
15248
15415
|
return false;
|
|
15249
15416
|
}
|
|
15250
15417
|
}
|
|
15251
|
-
var log5 = makePluginLogger(
|
|
15418
|
+
var log5 = makePluginLogger(PLUGIN_NAME7);
|
|
15252
15419
|
var toolHeartbeatServer = async (ctx) => {
|
|
15253
|
-
logLifecycle(
|
|
15420
|
+
logLifecycle(PLUGIN_NAME7, "activate", {
|
|
15254
15421
|
directory: ctx.directory,
|
|
15255
15422
|
intervalMs: HEARTBEAT_INTERVAL_MS
|
|
15256
15423
|
});
|
|
15257
15424
|
const client = ctx.client;
|
|
15258
15425
|
const interval = setInterval(() => {
|
|
15259
|
-
safeAsync(
|
|
15426
|
+
safeAsync(PLUGIN_NAME7, "interval", async () => {
|
|
15260
15427
|
if (inflight.size === 0)
|
|
15261
15428
|
return;
|
|
15262
15429
|
const records = [...inflight.values()];
|
|
@@ -15266,7 +15433,7 @@ var toolHeartbeatServer = async (ctx) => {
|
|
|
15266
15433
|
continue;
|
|
15267
15434
|
const sent = await showToast(client, { message: alert.message, variant: alert.variant }, log5);
|
|
15268
15435
|
r.alertsSent.add(alert.threshold);
|
|
15269
|
-
safeWriteLog(
|
|
15436
|
+
safeWriteLog(PLUGIN_NAME7, {
|
|
15270
15437
|
hook: "interval",
|
|
15271
15438
|
tool: r.toolName,
|
|
15272
15439
|
callID: r.callID,
|
|
@@ -15284,12 +15451,12 @@ var toolHeartbeatServer = async (ctx) => {
|
|
|
15284
15451
|
event: async () => {}
|
|
15285
15452
|
};
|
|
15286
15453
|
};
|
|
15287
|
-
var
|
|
15454
|
+
var handler7 = toolHeartbeatServer;
|
|
15288
15455
|
|
|
15289
15456
|
// plugins/codeforge-tools.ts
|
|
15290
15457
|
var z30 = tool.schema;
|
|
15291
|
-
var
|
|
15292
|
-
logLifecycle(
|
|
15458
|
+
var PLUGIN_NAME8 = "codeforge-tools";
|
|
15459
|
+
logLifecycle(PLUGIN_NAME8, "import");
|
|
15293
15460
|
function wrap(output, metadata) {
|
|
15294
15461
|
const text = typeof output === "string" ? output : JSON.stringify(output, null, 2);
|
|
15295
15462
|
return metadata && Object.keys(metadata).length > 0 ? { output: text, metadata } : { output: text };
|
|
@@ -15299,7 +15466,7 @@ async function runSafe(toolName, fn) {
|
|
|
15299
15466
|
return await fn();
|
|
15300
15467
|
} catch (err) {
|
|
15301
15468
|
const msg = err instanceof Error ? err.message : String(err);
|
|
15302
|
-
safeWriteLog(
|
|
15469
|
+
safeWriteLog(PLUGIN_NAME8, {
|
|
15303
15470
|
level: "error",
|
|
15304
15471
|
tool: toolName,
|
|
15305
15472
|
error: msg
|
|
@@ -15696,7 +15863,7 @@ var codeforgeToolsServer = async (ctx) => {
|
|
|
15696
15863
|
const rt = loadRuntimeSync({ root: ctx.directory });
|
|
15697
15864
|
const browserEnabled = rt.runtime.tools.browser.enabled;
|
|
15698
15865
|
const activeTools = browserEnabled ? [...CORE_TOOL_NAMES, ...BROWSER_TOOL_NAMES] : [...CORE_TOOL_NAMES];
|
|
15699
|
-
logLifecycle(
|
|
15866
|
+
logLifecycle(PLUGIN_NAME8, "activate", {
|
|
15700
15867
|
directory: ctx.directory,
|
|
15701
15868
|
tools: activeTools,
|
|
15702
15869
|
count: activeTools.length,
|
|
@@ -15711,7 +15878,8 @@ var codeforgeToolsServer = async (ctx) => {
|
|
|
15711
15878
|
const spawner = new ProductionSpawner({
|
|
15712
15879
|
client: ctx.client,
|
|
15713
15880
|
directory: ctx.directory ?? process.cwd(),
|
|
15714
|
-
|
|
15881
|
+
mainRoot: ctx.directory ?? process.cwd(),
|
|
15882
|
+
log: (level, msg, data) => safeWriteLog(PLUGIN_NAME8, { level, msg, data })
|
|
15715
15883
|
});
|
|
15716
15884
|
__setContext({
|
|
15717
15885
|
mainRoot: ctx.directory ?? process.cwd(),
|
|
@@ -16003,7 +16171,7 @@ var codeforgeToolsServer = async (ctx) => {
|
|
|
16003
16171
|
}
|
|
16004
16172
|
};
|
|
16005
16173
|
};
|
|
16006
|
-
var
|
|
16174
|
+
var handler8 = codeforgeToolsServer;
|
|
16007
16175
|
|
|
16008
16176
|
// plugins/discover-spec-suggest.ts
|
|
16009
16177
|
import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "node:fs";
|
|
@@ -16185,26 +16353,26 @@ function isValidSlug(slug) {
|
|
|
16185
16353
|
}
|
|
16186
16354
|
|
|
16187
16355
|
// plugins/discover-spec-suggest.ts
|
|
16188
|
-
var
|
|
16189
|
-
logLifecycle(
|
|
16356
|
+
var PLUGIN_NAME9 = "discover-spec-suggest";
|
|
16357
|
+
logLifecycle(PLUGIN_NAME9, "import", {});
|
|
16190
16358
|
var TARGET_AGENT = "codeforge";
|
|
16191
|
-
var
|
|
16192
|
-
var
|
|
16359
|
+
var SESSION_CAP2 = 200;
|
|
16360
|
+
var SESSION_TTL_MS2 = 24 * 60 * 60 * 1000;
|
|
16193
16361
|
var MATCH_THRESHOLD = 0.15;
|
|
16194
16362
|
var MAX_CANDIDATES = 3;
|
|
16195
16363
|
var NUDGE_MAX_LEN = 1500;
|
|
16196
16364
|
var SPECS_REL_DIR = join14(".codeforge", "specs");
|
|
16197
16365
|
var sessionMap = new Map;
|
|
16198
|
-
function
|
|
16199
|
-
while (sessionMap.size >
|
|
16366
|
+
function pruneIfOversize2() {
|
|
16367
|
+
while (sessionMap.size > SESSION_CAP2) {
|
|
16200
16368
|
const oldestKey = sessionMap.keys().next().value;
|
|
16201
16369
|
if (oldestKey === undefined)
|
|
16202
16370
|
break;
|
|
16203
16371
|
sessionMap.delete(oldestKey);
|
|
16204
16372
|
}
|
|
16205
16373
|
}
|
|
16206
|
-
function
|
|
16207
|
-
return now - entry.ts >
|
|
16374
|
+
function isExpired2(entry, now = Date.now()) {
|
|
16375
|
+
return now - entry.ts > SESSION_TTL_MS2;
|
|
16208
16376
|
}
|
|
16209
16377
|
var specIndex = [];
|
|
16210
16378
|
function defaultReader(p) {
|
|
@@ -16299,7 +16467,7 @@ function loadSpecs(rootDir, opts = {}) {
|
|
|
16299
16467
|
const dirReader = opts.dirReader ?? defaultDirReader;
|
|
16300
16468
|
const dirExists = opts.dirExists ?? defaultDirExists;
|
|
16301
16469
|
const statReader = opts.statReader ?? defaultStatReader;
|
|
16302
|
-
const log6 = makePluginLogger(
|
|
16470
|
+
const log6 = makePluginLogger(PLUGIN_NAME9);
|
|
16303
16471
|
const specsRoot = join14(rootDir, SPECS_REL_DIR);
|
|
16304
16472
|
const records = [];
|
|
16305
16473
|
if (!dirExists(specsRoot)) {
|
|
@@ -16427,7 +16595,7 @@ function renderCandidatesNudge(matched) {
|
|
|
16427
16595
|
return body.slice(0, NUDGE_MAX_LEN - 4) + `
|
|
16428
16596
|
…`;
|
|
16429
16597
|
}
|
|
16430
|
-
var log6 = makePluginLogger(
|
|
16598
|
+
var log6 = makePluginLogger(PLUGIN_NAME9);
|
|
16431
16599
|
var discoverSpecSuggestServer = async (ctx) => {
|
|
16432
16600
|
try {
|
|
16433
16601
|
const loaded = loadSpecs(ctx.directory ?? process.cwd());
|
|
@@ -16438,19 +16606,19 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16438
16606
|
error: err instanceof Error ? err.message : String(err)
|
|
16439
16607
|
});
|
|
16440
16608
|
}
|
|
16441
|
-
logLifecycle(
|
|
16609
|
+
logLifecycle(PLUGIN_NAME9, "activate", {
|
|
16442
16610
|
directory: ctx.directory,
|
|
16443
16611
|
specs_loaded: specIndex.length,
|
|
16444
16612
|
target_agent: TARGET_AGENT,
|
|
16445
16613
|
match_threshold: MATCH_THRESHOLD,
|
|
16446
16614
|
max_candidates: MAX_CANDIDATES,
|
|
16447
|
-
session_cap:
|
|
16448
|
-
session_ttl_ms:
|
|
16615
|
+
session_cap: SESSION_CAP2,
|
|
16616
|
+
session_ttl_ms: SESSION_TTL_MS2,
|
|
16449
16617
|
no_op: specIndex.length === 0
|
|
16450
16618
|
});
|
|
16451
16619
|
return {
|
|
16452
16620
|
"chat.message": async (input, output) => {
|
|
16453
|
-
await safeAsync(
|
|
16621
|
+
await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
|
|
16454
16622
|
if (specIndex.length === 0)
|
|
16455
16623
|
return;
|
|
16456
16624
|
const text = extractUserText(output);
|
|
@@ -16461,11 +16629,11 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16461
16629
|
if (!sid)
|
|
16462
16630
|
return;
|
|
16463
16631
|
sessionMap.set(sid, { text, agent, ts: Date.now() });
|
|
16464
|
-
|
|
16632
|
+
pruneIfOversize2();
|
|
16465
16633
|
});
|
|
16466
16634
|
},
|
|
16467
16635
|
"experimental.chat.system.transform": async (input, output) => {
|
|
16468
|
-
await safeAsync(
|
|
16636
|
+
await safeAsync(PLUGIN_NAME9, "experimental.chat.system.transform", async () => {
|
|
16469
16637
|
if (specIndex.length === 0)
|
|
16470
16638
|
return;
|
|
16471
16639
|
if (!output || !Array.isArray(output.system))
|
|
@@ -16476,7 +16644,7 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16476
16644
|
const entry = sessionMap.get(sid);
|
|
16477
16645
|
if (!entry)
|
|
16478
16646
|
return;
|
|
16479
|
-
if (
|
|
16647
|
+
if (isExpired2(entry)) {
|
|
16480
16648
|
sessionMap.delete(sid);
|
|
16481
16649
|
return;
|
|
16482
16650
|
}
|
|
@@ -16491,7 +16659,7 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16491
16659
|
if (!nudge)
|
|
16492
16660
|
return;
|
|
16493
16661
|
output.system.push(nudge);
|
|
16494
|
-
safeWriteLog(
|
|
16662
|
+
safeWriteLog(PLUGIN_NAME9, {
|
|
16495
16663
|
hook: "experimental.chat.system.transform",
|
|
16496
16664
|
sessionID: sid,
|
|
16497
16665
|
agent: entry.agent,
|
|
@@ -16503,7 +16671,7 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16503
16671
|
}
|
|
16504
16672
|
};
|
|
16505
16673
|
};
|
|
16506
|
-
var
|
|
16674
|
+
var handler9 = discoverSpecSuggestServer;
|
|
16507
16675
|
|
|
16508
16676
|
// plugins/kh-auto-context.ts
|
|
16509
16677
|
init_kh_client();
|
|
@@ -16643,7 +16811,7 @@ class SessionScopedCache {
|
|
|
16643
16811
|
var sharedKhCache = new SessionScopedCache;
|
|
16644
16812
|
|
|
16645
16813
|
// plugins/kh-auto-context.ts
|
|
16646
|
-
var
|
|
16814
|
+
var PLUGIN_NAME10 = "kh-auto-context";
|
|
16647
16815
|
var INJECTION_MODE = "observe-only";
|
|
16648
16816
|
function resolveInjectionMode(client) {
|
|
16649
16817
|
return client.hasTransport() ? "system-injected" : "observe-only";
|
|
@@ -16742,7 +16910,7 @@ var CODEFORGE_CONSTRAINTS = [
|
|
|
16742
16910
|
];
|
|
16743
16911
|
async function seedConstraints(client, log7) {
|
|
16744
16912
|
if (!client.hasTransport()) {
|
|
16745
|
-
log7?.debug?.(`[${
|
|
16913
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] seedConstraints: transport 不可用,跳过`, {});
|
|
16746
16914
|
return { ok: false, reason: "transport_unavailable" };
|
|
16747
16915
|
}
|
|
16748
16916
|
try {
|
|
@@ -16756,7 +16924,7 @@ async function seedConstraints(client, log7) {
|
|
|
16756
16924
|
});
|
|
16757
16925
|
if (result && typeof result === "object" && "ok" in result && result.ok === false) {
|
|
16758
16926
|
const r = result;
|
|
16759
|
-
log7?.warn(`[${
|
|
16927
|
+
log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 降级`, {
|
|
16760
16928
|
reason: r.reason,
|
|
16761
16929
|
message: r.message
|
|
16762
16930
|
});
|
|
@@ -16766,11 +16934,11 @@ async function seedConstraints(client, log7) {
|
|
|
16766
16934
|
message: r.message
|
|
16767
16935
|
};
|
|
16768
16936
|
}
|
|
16769
|
-
log7?.info(`[${
|
|
16937
|
+
log7?.info(`[${PLUGIN_NAME10}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
|
|
16770
16938
|
return { ok: true, itemsWritten: CODEFORGE_CONSTRAINTS.length };
|
|
16771
16939
|
} catch (err) {
|
|
16772
16940
|
const message = err instanceof Error ? err.message : String(err);
|
|
16773
|
-
log7?.warn(`[${
|
|
16941
|
+
log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 失败(已静默)`, { error: message });
|
|
16774
16942
|
return { ok: false, reason: "exception", message };
|
|
16775
16943
|
}
|
|
16776
16944
|
}
|
|
@@ -16785,7 +16953,7 @@ function createSystemInjectedHook(client, sessionId, log7) {
|
|
|
16785
16953
|
});
|
|
16786
16954
|
if (result && typeof result === "object" && "ok" in result && result.ok === false) {
|
|
16787
16955
|
const r = result;
|
|
16788
|
-
log7?.warn(`[${
|
|
16956
|
+
log7?.warn(`[${PLUGIN_NAME10}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
|
|
16789
16957
|
sessionId,
|
|
16790
16958
|
section,
|
|
16791
16959
|
preview: markdown.slice(0, 200),
|
|
@@ -16794,12 +16962,12 @@ function createSystemInjectedHook(client, sessionId, log7) {
|
|
|
16794
16962
|
});
|
|
16795
16963
|
return;
|
|
16796
16964
|
}
|
|
16797
|
-
log7?.info(`[${
|
|
16965
|
+
log7?.info(`[${PLUGIN_NAME10}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
|
|
16798
16966
|
sessionId,
|
|
16799
16967
|
section
|
|
16800
16968
|
});
|
|
16801
16969
|
} catch (err) {
|
|
16802
|
-
log7?.warn(`[${
|
|
16970
|
+
log7?.warn(`[${PLUGIN_NAME10}] system-injected 抛异常,降级到 observe-only`, {
|
|
16803
16971
|
sessionId,
|
|
16804
16972
|
section,
|
|
16805
16973
|
preview: markdown.slice(0, 200),
|
|
@@ -16842,7 +17010,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16842
17010
|
});
|
|
16843
17011
|
result = await racer(searchPromise, cfg.timeoutMs);
|
|
16844
17012
|
} catch (err) {
|
|
16845
|
-
log7?.warn(`[${
|
|
17013
|
+
log7?.warn(`[${PLUGIN_NAME10}] client.search threw (sync or async), return null`, {
|
|
16846
17014
|
query,
|
|
16847
17015
|
elapsedMs: Date.now() - startedAt,
|
|
16848
17016
|
sessionId: ctx.sessionId,
|
|
@@ -16852,7 +17020,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16852
17020
|
return null;
|
|
16853
17021
|
}
|
|
16854
17022
|
if (result === "__timeout__") {
|
|
16855
|
-
log7?.warn(`[${
|
|
17023
|
+
log7?.warn(`[${PLUGIN_NAME10}] timeout`, {
|
|
16856
17024
|
query,
|
|
16857
17025
|
ms: cfg.timeoutMs,
|
|
16858
17026
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -16862,7 +17030,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16862
17030
|
return null;
|
|
16863
17031
|
}
|
|
16864
17032
|
if (!result.ok) {
|
|
16865
|
-
log7?.warn(`[${
|
|
17033
|
+
log7?.warn(`[${PLUGIN_NAME10}] kh degraded`, {
|
|
16866
17034
|
reason: result.reason,
|
|
16867
17035
|
query,
|
|
16868
17036
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -16874,7 +17042,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16874
17042
|
const filtered = result.insights.filter((i) => (i.confidence ?? 0) >= cfg.minConfidence);
|
|
16875
17043
|
if (filtered.length === 0) {
|
|
16876
17044
|
opts.cache.record(query, []);
|
|
16877
|
-
log7?.debug?.(`[${
|
|
17045
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] no candidate above threshold`, {
|
|
16878
17046
|
query,
|
|
16879
17047
|
rawCount: result.insights.length,
|
|
16880
17048
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -16887,7 +17055,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16887
17055
|
try {
|
|
16888
17056
|
await ctx.injectContext(payload.markdown);
|
|
16889
17057
|
} catch (err) {
|
|
16890
|
-
log7?.warn(`[${
|
|
17058
|
+
log7?.warn(`[${PLUGIN_NAME10}] injectContext threw`, {
|
|
16891
17059
|
error: err instanceof Error ? err.message : String(err),
|
|
16892
17060
|
query,
|
|
16893
17061
|
sessionId: ctx.sessionId
|
|
@@ -16895,7 +17063,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16895
17063
|
}
|
|
16896
17064
|
}
|
|
16897
17065
|
opts.cache.record(query, filtered);
|
|
16898
|
-
log7?.info(`[${
|
|
17066
|
+
log7?.info(`[${PLUGIN_NAME10}] inject complete (${mode})`, {
|
|
16899
17067
|
query,
|
|
16900
17068
|
mode,
|
|
16901
17069
|
candidateCount: filtered.length,
|
|
@@ -16911,18 +17079,18 @@ async function handleMessage2(raw, opts) {
|
|
|
16911
17079
|
const log7 = ctx.log;
|
|
16912
17080
|
const text = (ctx.content ?? "").trim();
|
|
16913
17081
|
if (!shouldInject(text, cfg)) {
|
|
16914
|
-
log7?.debug?.(`[${
|
|
17082
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] skip (filter)`, { textLen: text.length });
|
|
16915
17083
|
return;
|
|
16916
17084
|
}
|
|
16917
17085
|
const query = extractQuery(text);
|
|
16918
17086
|
if (!query)
|
|
16919
17087
|
return;
|
|
16920
17088
|
if (opts.cache.shouldSkip(query)) {
|
|
16921
|
-
log7?.debug?.(`[${
|
|
17089
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] cache hit, skip`, { query });
|
|
16922
17090
|
return;
|
|
16923
17091
|
}
|
|
16924
17092
|
if (inflight2.size >= INFLIGHT_CAP) {
|
|
16925
|
-
log7?.warn(`[${
|
|
17093
|
+
log7?.warn(`[${PLUGIN_NAME10}] inflight cap reached, skip`, {
|
|
16926
17094
|
query,
|
|
16927
17095
|
inflightSize: inflight2.size,
|
|
16928
17096
|
cap: INFLIGHT_CAP,
|
|
@@ -16933,7 +17101,7 @@ async function handleMessage2(raw, opts) {
|
|
|
16933
17101
|
const key = inflightKey(ctx.sessionId, query);
|
|
16934
17102
|
inflight2.add(key);
|
|
16935
17103
|
runKhSearchAndInject({ query, ctx, opts, mode }).catch((err) => {
|
|
16936
|
-
log7?.warn(`[${
|
|
17104
|
+
log7?.warn(`[${PLUGIN_NAME10}] runKhSearchAndInject 顶层兜底捕获`, {
|
|
16937
17105
|
error: err instanceof Error ? err.message : String(err),
|
|
16938
17106
|
query,
|
|
16939
17107
|
sessionId: ctx.sessionId
|
|
@@ -16942,13 +17110,13 @@ async function handleMessage2(raw, opts) {
|
|
|
16942
17110
|
inflight2.delete(key);
|
|
16943
17111
|
});
|
|
16944
17112
|
}
|
|
16945
|
-
logLifecycle(
|
|
17113
|
+
logLifecycle(PLUGIN_NAME10, "import");
|
|
16946
17114
|
var sharedClient2 = new KhClient;
|
|
16947
17115
|
var sharedCache = new QueryCache(DEFAULT_CONFIG4.cacheTtlMs);
|
|
16948
17116
|
var khAutoContextServer = async (ctx) => {
|
|
16949
|
-
const log7 = makePluginLogger(
|
|
17117
|
+
const log7 = makePluginLogger(PLUGIN_NAME10);
|
|
16950
17118
|
const runtimeMode = resolveInjectionMode(sharedClient2);
|
|
16951
|
-
logLifecycle(
|
|
17119
|
+
logLifecycle(PLUGIN_NAME10, "activate", {
|
|
16952
17120
|
directory: ctx.directory,
|
|
16953
17121
|
minConfidence: DEFAULT_CONFIG4.minConfidence,
|
|
16954
17122
|
timeoutMs: DEFAULT_CONFIG4.timeoutMs,
|
|
@@ -16962,7 +17130,7 @@ var khAutoContextServer = async (ctx) => {
|
|
|
16962
17130
|
});
|
|
16963
17131
|
return {
|
|
16964
17132
|
"chat.message": async (input, output) => {
|
|
16965
|
-
await safeAsync(
|
|
17133
|
+
await safeAsync(PLUGIN_NAME10, "chat.message", async () => {
|
|
16966
17134
|
const text = extractUserText(output);
|
|
16967
17135
|
if (!text)
|
|
16968
17136
|
return;
|
|
@@ -16982,7 +17150,7 @@ var khAutoContextServer = async (ctx) => {
|
|
|
16982
17150
|
});
|
|
16983
17151
|
},
|
|
16984
17152
|
event: async ({ event }) => {
|
|
16985
|
-
await safeAsync(
|
|
17153
|
+
await safeAsync(PLUGIN_NAME10, "event", async () => {
|
|
16986
17154
|
const e = event;
|
|
16987
17155
|
if (e.type !== "session.idle")
|
|
16988
17156
|
return;
|
|
@@ -16991,14 +17159,14 @@ var khAutoContextServer = async (ctx) => {
|
|
|
16991
17159
|
if (typeof sid !== "string" || !sid)
|
|
16992
17160
|
return;
|
|
16993
17161
|
sharedKhCache.onSessionEnd(sid);
|
|
16994
|
-
log7.debug?.(`[${
|
|
17162
|
+
log7.debug?.(`[${PLUGIN_NAME10}] session.idle: cleared shared cache`, {
|
|
16995
17163
|
sessionID: sid
|
|
16996
17164
|
});
|
|
16997
17165
|
});
|
|
16998
17166
|
}
|
|
16999
17167
|
};
|
|
17000
17168
|
};
|
|
17001
|
-
var
|
|
17169
|
+
var handler10 = khAutoContextServer;
|
|
17002
17170
|
|
|
17003
17171
|
// lib/condenser.ts
|
|
17004
17172
|
var DEFAULT_CONDENSE = {
|
|
@@ -17116,8 +17284,8 @@ async function condense(input, opts = {}) {
|
|
|
17116
17284
|
}
|
|
17117
17285
|
|
|
17118
17286
|
// plugins/kh-reminder.ts
|
|
17119
|
-
var
|
|
17120
|
-
logLifecycle(
|
|
17287
|
+
var PLUGIN_NAME11 = "kh-reminder";
|
|
17288
|
+
logLifecycle(PLUGIN_NAME11, "import", {});
|
|
17121
17289
|
var TRIGGER_WORDS_ZH = [
|
|
17122
17290
|
"怎么",
|
|
17123
17291
|
"怎样",
|
|
@@ -17271,7 +17439,7 @@ function handleObserve(raw, log7) {
|
|
|
17271
17439
|
const result = evaluate(ctx);
|
|
17272
17440
|
if (result.triggered && result.reason) {
|
|
17273
17441
|
const reasonStr = formatReason(result.reason);
|
|
17274
|
-
log7?.info(`[${
|
|
17442
|
+
log7?.info(`[${PLUGIN_NAME11}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
|
|
17275
17443
|
sessionId: result.sessionId,
|
|
17276
17444
|
reason: result.reason,
|
|
17277
17445
|
suggestion: "调用 smart_search 查项目历史;完成后用 save_chat_insight 沉淀"
|
|
@@ -17279,7 +17447,7 @@ function handleObserve(raw, log7) {
|
|
|
17279
17447
|
}
|
|
17280
17448
|
return result;
|
|
17281
17449
|
} catch (err) {
|
|
17282
|
-
log7?.warn(`[${
|
|
17450
|
+
log7?.warn(`[${PLUGIN_NAME11}] evaluate 异常(已隔离)`, {
|
|
17283
17451
|
error: err instanceof Error ? err.message : String(err)
|
|
17284
17452
|
});
|
|
17285
17453
|
return null;
|
|
@@ -17295,9 +17463,9 @@ function formatReason(r) {
|
|
|
17295
17463
|
return `no-search-in-recent-rounds (last ${r.rounds})`;
|
|
17296
17464
|
}
|
|
17297
17465
|
}
|
|
17298
|
-
var log7 = makePluginLogger(
|
|
17466
|
+
var log7 = makePluginLogger(PLUGIN_NAME11);
|
|
17299
17467
|
var khReminderServer = async (ctx) => {
|
|
17300
|
-
logLifecycle(
|
|
17468
|
+
logLifecycle(PLUGIN_NAME11, "activate", {
|
|
17301
17469
|
directory: ctx.directory,
|
|
17302
17470
|
threshold: DEFAULT_THRESHOLD,
|
|
17303
17471
|
cooldown_ms: COOLDOWN_MS,
|
|
@@ -17305,7 +17473,7 @@ var khReminderServer = async (ctx) => {
|
|
|
17305
17473
|
});
|
|
17306
17474
|
return {
|
|
17307
17475
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
17308
|
-
await safeAsync(
|
|
17476
|
+
await safeAsync(PLUGIN_NAME11, "experimental.chat.messages.transform", async () => {
|
|
17309
17477
|
const list = output.messages;
|
|
17310
17478
|
if (!Array.isArray(list) || list.length === 0)
|
|
17311
17479
|
return;
|
|
@@ -17324,7 +17492,7 @@ var khReminderServer = async (ctx) => {
|
|
|
17324
17492
|
const result = handleObserve({ messages: flat, sessionId }, log7);
|
|
17325
17493
|
if (!result)
|
|
17326
17494
|
return;
|
|
17327
|
-
safeWriteLog(
|
|
17495
|
+
safeWriteLog(PLUGIN_NAME11, {
|
|
17328
17496
|
hook: "experimental.chat.messages.transform",
|
|
17329
17497
|
mode: "observe-only",
|
|
17330
17498
|
sessionId: result.sessionId,
|
|
@@ -17337,7 +17505,7 @@ var khReminderServer = async (ctx) => {
|
|
|
17337
17505
|
}
|
|
17338
17506
|
};
|
|
17339
17507
|
};
|
|
17340
|
-
var
|
|
17508
|
+
var handler11 = khReminderServer;
|
|
17341
17509
|
|
|
17342
17510
|
// lib/memories.ts
|
|
17343
17511
|
import { promises as fs15 } from "node:fs";
|
|
@@ -17538,7 +17706,7 @@ function bagOfWordsScore(query, doc) {
|
|
|
17538
17706
|
}
|
|
17539
17707
|
|
|
17540
17708
|
// plugins/memories-context.ts
|
|
17541
|
-
var
|
|
17709
|
+
var PLUGIN_NAME12 = "memories-context";
|
|
17542
17710
|
var INJECTION_MODE2 = "observe-only";
|
|
17543
17711
|
var DEFAULT_CONFIG5 = {
|
|
17544
17712
|
minTextLength: 8,
|
|
@@ -17636,14 +17804,14 @@ async function handleMessage3(raw, opts) {
|
|
|
17636
17804
|
return await handleDirective(dir, ctx, opts.memCfg, log8);
|
|
17637
17805
|
}
|
|
17638
17806
|
if (!shouldRecall(text, cfg)) {
|
|
17639
|
-
log8?.debug?.(`[${
|
|
17807
|
+
log8?.debug?.(`[${PLUGIN_NAME12}] skip (filter)`, { textLen: text.length });
|
|
17640
17808
|
return { kind: "noop", reason: "filtered", mode: INJECTION_MODE2 };
|
|
17641
17809
|
}
|
|
17642
17810
|
const query = extractQuery2(text);
|
|
17643
17811
|
if (!query)
|
|
17644
17812
|
return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE2 };
|
|
17645
17813
|
if (cache2.shouldSkip(query)) {
|
|
17646
|
-
log8?.debug?.(`[${
|
|
17814
|
+
log8?.debug?.(`[${PLUGIN_NAME12}] cache hit`, { query });
|
|
17647
17815
|
return { kind: "noop", reason: "cache", mode: INJECTION_MODE2 };
|
|
17648
17816
|
}
|
|
17649
17817
|
const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
|
|
@@ -17668,7 +17836,7 @@ async function handleMessage3(raw, opts) {
|
|
|
17668
17836
|
}, opts.memCfg);
|
|
17669
17837
|
const result = await racer(injectPromise, cfg.timeoutMs);
|
|
17670
17838
|
if (result === "__timeout__") {
|
|
17671
|
-
log8?.warn(`[${
|
|
17839
|
+
log8?.warn(`[${PLUGIN_NAME12}] timeout`, { query, ms: cfg.timeoutMs });
|
|
17672
17840
|
cache2.record(query, 0);
|
|
17673
17841
|
return { kind: "noop", reason: "timeout", mode: INJECTION_MODE2 };
|
|
17674
17842
|
}
|
|
@@ -17680,13 +17848,13 @@ async function handleMessage3(raw, opts) {
|
|
|
17680
17848
|
try {
|
|
17681
17849
|
await ctx.injectContext(result.text);
|
|
17682
17850
|
} catch (err) {
|
|
17683
|
-
log8?.warn(`[${
|
|
17851
|
+
log8?.warn(`[${PLUGIN_NAME12}] injectContext threw`, {
|
|
17684
17852
|
error: err instanceof Error ? err.message : String(err)
|
|
17685
17853
|
});
|
|
17686
17854
|
}
|
|
17687
17855
|
}
|
|
17688
17856
|
cache2.record(query, result.recalled);
|
|
17689
|
-
log8?.info(`[${
|
|
17857
|
+
log8?.info(`[${PLUGIN_NAME12}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
|
|
17690
17858
|
return { kind: "injected", payload: result, mode: INJECTION_MODE2 };
|
|
17691
17859
|
}
|
|
17692
17860
|
async function handleDirective(dir, ctx, memCfg, log8) {
|
|
@@ -17695,14 +17863,14 @@ async function handleDirective(dir, ctx, memCfg, log8) {
|
|
|
17695
17863
|
if (r.ok) {
|
|
17696
17864
|
const target = r.written_to === "kh" ? "KH" : "本地";
|
|
17697
17865
|
await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
|
|
17698
|
-
log8?.info(`[${
|
|
17866
|
+
log8?.info(`[${PLUGIN_NAME12}] /remember ok`, {
|
|
17699
17867
|
scope: dir.scope,
|
|
17700
17868
|
id: r.id,
|
|
17701
17869
|
mode: INJECTION_MODE2
|
|
17702
17870
|
});
|
|
17703
17871
|
} else {
|
|
17704
17872
|
await safeReply(ctx, `❌ 记忆失败:${r.error ?? "unknown"}`);
|
|
17705
|
-
log8?.warn(`[${
|
|
17873
|
+
log8?.warn(`[${PLUGIN_NAME12}] /remember failed`, { error: r.error });
|
|
17706
17874
|
}
|
|
17707
17875
|
return { kind: "remembered", result: r, mode: INJECTION_MODE2 };
|
|
17708
17876
|
}
|
|
@@ -17733,22 +17901,22 @@ async function safeReply(ctx, text) {
|
|
|
17733
17901
|
await ctx.reply(text);
|
|
17734
17902
|
} catch {}
|
|
17735
17903
|
}
|
|
17736
|
-
logLifecycle(
|
|
17904
|
+
logLifecycle(PLUGIN_NAME12, "import");
|
|
17737
17905
|
var sharedCache2 = new QueryCache2(DEFAULT_CONFIG5.cacheTtlMs);
|
|
17738
17906
|
function buildMemCfg(directory) {
|
|
17739
17907
|
return { projectRoot: directory };
|
|
17740
17908
|
}
|
|
17741
17909
|
var memoriesContextServer = async (ctx) => {
|
|
17742
|
-
const log8 = makePluginLogger(
|
|
17910
|
+
const log8 = makePluginLogger(PLUGIN_NAME12);
|
|
17743
17911
|
const memCfg = buildMemCfg(ctx.directory);
|
|
17744
|
-
logLifecycle(
|
|
17912
|
+
logLifecycle(PLUGIN_NAME12, "activate", {
|
|
17745
17913
|
directory: ctx.directory,
|
|
17746
17914
|
projectRoot: memCfg.projectRoot,
|
|
17747
17915
|
mode: INJECTION_MODE2
|
|
17748
17916
|
});
|
|
17749
17917
|
return {
|
|
17750
17918
|
"chat.message": async (input, output) => {
|
|
17751
|
-
await safeAsync(
|
|
17919
|
+
await safeAsync(PLUGIN_NAME12, "chat.message", async () => {
|
|
17752
17920
|
const text = extractUserText(output);
|
|
17753
17921
|
if (!text)
|
|
17754
17922
|
return;
|
|
@@ -17774,17 +17942,17 @@ var memoriesContextServer = async (ctx) => {
|
|
|
17774
17942
|
}
|
|
17775
17943
|
};
|
|
17776
17944
|
};
|
|
17777
|
-
var
|
|
17945
|
+
var handler12 = memoriesContextServer;
|
|
17778
17946
|
|
|
17779
17947
|
// plugins/model-fallback.ts
|
|
17780
|
-
var
|
|
17948
|
+
var PLUGIN_NAME13 = "model-fallback";
|
|
17781
17949
|
var state = {
|
|
17782
17950
|
config: null,
|
|
17783
17951
|
configPath: null,
|
|
17784
17952
|
warnings: [],
|
|
17785
17953
|
error: null
|
|
17786
17954
|
};
|
|
17787
|
-
logLifecycle(
|
|
17955
|
+
logLifecycle(PLUGIN_NAME13, "import");
|
|
17788
17956
|
function loadOnce(root) {
|
|
17789
17957
|
if (state.config !== null || state.error !== null)
|
|
17790
17958
|
return;
|
|
@@ -17794,11 +17962,11 @@ function loadOnce(root) {
|
|
|
17794
17962
|
state.configPath = r.path ?? null;
|
|
17795
17963
|
state.warnings = r.warnings;
|
|
17796
17964
|
if (r.warnings.length > 0) {
|
|
17797
|
-
safeWriteLog(
|
|
17965
|
+
safeWriteLog(PLUGIN_NAME13, { phase: "load.warnings", warnings: r.warnings });
|
|
17798
17966
|
}
|
|
17799
17967
|
} else {
|
|
17800
17968
|
state.error = r.error ?? "unknown_load_error";
|
|
17801
|
-
safeWriteLog(
|
|
17969
|
+
safeWriteLog(PLUGIN_NAME13, { phase: "load.failed", error: state.error, path: r.path });
|
|
17802
17970
|
}
|
|
17803
17971
|
}
|
|
17804
17972
|
var MODEL_ERR_PATTERNS = [
|
|
@@ -17843,9 +18011,9 @@ fallback 链已用尽:${meta.chain.join(" → ")}`;
|
|
|
17843
18011
|
完整链:${meta.chain.join(" → ")}`;
|
|
17844
18012
|
}
|
|
17845
18013
|
var modelFallbackServer = async (ctx) => {
|
|
17846
|
-
const log8 = makePluginLogger(
|
|
18014
|
+
const log8 = makePluginLogger(PLUGIN_NAME13);
|
|
17847
18015
|
loadOnce(ctx.directory ?? process.cwd());
|
|
17848
|
-
logLifecycle(
|
|
18016
|
+
logLifecycle(PLUGIN_NAME13, "activate", {
|
|
17849
18017
|
directory: ctx.directory,
|
|
17850
18018
|
config_path: state.configPath,
|
|
17851
18019
|
config_loaded: state.config !== null,
|
|
@@ -17855,7 +18023,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17855
18023
|
});
|
|
17856
18024
|
return {
|
|
17857
18025
|
"chat.params": async (input, output) => {
|
|
17858
|
-
await safeAsync(
|
|
18026
|
+
await safeAsync(PLUGIN_NAME13, "chat.params", async () => {
|
|
17859
18027
|
if (!state.config)
|
|
17860
18028
|
return;
|
|
17861
18029
|
const agent = input.agent;
|
|
@@ -17876,7 +18044,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17876
18044
|
next: meta.next_fallback,
|
|
17877
18045
|
source: meta.source
|
|
17878
18046
|
};
|
|
17879
|
-
safeWriteLog(
|
|
18047
|
+
safeWriteLog(PLUGIN_NAME13, {
|
|
17880
18048
|
hook: "chat.params",
|
|
17881
18049
|
agent,
|
|
17882
18050
|
model: currentModel,
|
|
@@ -17886,7 +18054,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17886
18054
|
});
|
|
17887
18055
|
},
|
|
17888
18056
|
event: async ({ event }) => {
|
|
17889
|
-
await safeAsync(
|
|
18057
|
+
await safeAsync(PLUGIN_NAME13, "event", async () => {
|
|
17890
18058
|
if (!state.config)
|
|
17891
18059
|
return;
|
|
17892
18060
|
const e = event;
|
|
@@ -17902,8 +18070,8 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17902
18070
|
const model = props.model ?? "unknown/unknown";
|
|
17903
18071
|
const meta = buildFallbackMeta(state.config, agent, model);
|
|
17904
18072
|
const suggestion = meta ? buildSuggestion(meta, String(message)) : `⚠️ ${agent}/${model} 失败:${message}`;
|
|
17905
|
-
log8.warn(`[${
|
|
17906
|
-
safeWriteLog(
|
|
18073
|
+
log8.warn(`[${PLUGIN_NAME13}] ${suggestion}`);
|
|
18074
|
+
safeWriteLog(PLUGIN_NAME13, {
|
|
17907
18075
|
hook: "event.error",
|
|
17908
18076
|
eventType: e.type,
|
|
17909
18077
|
agent,
|
|
@@ -17915,7 +18083,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17915
18083
|
}
|
|
17916
18084
|
};
|
|
17917
18085
|
};
|
|
17918
|
-
var
|
|
18086
|
+
var handler13 = modelFallbackServer;
|
|
17919
18087
|
|
|
17920
18088
|
// plugins/subtask-heartbeat.ts
|
|
17921
18089
|
import { promises as fsPromises } from "node:fs";
|
|
@@ -17929,8 +18097,8 @@ var sweepExpiredSessionParents2 = sweepExpiredSessionParents;
|
|
|
17929
18097
|
var _bulkInjectSessionParentMap2 = _bulkInjectSessionParentMap;
|
|
17930
18098
|
var _capSessionParentMap2 = _capSessionParentMap;
|
|
17931
18099
|
var _setPersistRootForTests2 = _setPersistRootForTests;
|
|
17932
|
-
var
|
|
17933
|
-
logLifecycle(
|
|
18100
|
+
var PLUGIN_NAME14 = "subtask-heartbeat";
|
|
18101
|
+
logLifecycle(PLUGIN_NAME14, "import", {});
|
|
17934
18102
|
var HEARTBEAT_INTERVAL_MS2 = 30000;
|
|
17935
18103
|
var HEARTBEAT_DEBOUNCE_MS = 25000;
|
|
17936
18104
|
var TOAST_DURATION_MS2 = 5000;
|
|
@@ -18272,9 +18440,9 @@ async function showToast2(client, payload, log8) {
|
|
|
18272
18440
|
return false;
|
|
18273
18441
|
}
|
|
18274
18442
|
}
|
|
18275
|
-
var log8 = makePluginLogger(
|
|
18443
|
+
var log8 = makePluginLogger(PLUGIN_NAME14);
|
|
18276
18444
|
var subtaskHeartbeatServer = async (ctx) => {
|
|
18277
|
-
logLifecycle(
|
|
18445
|
+
logLifecycle(PLUGIN_NAME14, "activate", {
|
|
18278
18446
|
directory: ctx.directory,
|
|
18279
18447
|
intervalMs: HEARTBEAT_INTERVAL_MS2
|
|
18280
18448
|
});
|
|
@@ -18291,7 +18459,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18291
18459
|
}));
|
|
18292
18460
|
_bulkInjectSessionParentMap2(entries);
|
|
18293
18461
|
const cappedOut = _capSessionParentMap2();
|
|
18294
|
-
safeWriteLog(
|
|
18462
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18295
18463
|
hook: "activate",
|
|
18296
18464
|
type: "parent-map.restore",
|
|
18297
18465
|
restored: restored.size,
|
|
@@ -18304,14 +18472,14 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18304
18472
|
});
|
|
18305
18473
|
}
|
|
18306
18474
|
const interval = setInterval(() => {
|
|
18307
|
-
safeAsync(
|
|
18475
|
+
safeAsync(PLUGIN_NAME14, "interval", async () => {
|
|
18308
18476
|
const swept = sweepExpiredPendingTasks();
|
|
18309
18477
|
if (swept > 0) {
|
|
18310
|
-
safeWriteLog(
|
|
18478
|
+
safeWriteLog(PLUGIN_NAME14, { hook: "interval", pending_task_swept: swept });
|
|
18311
18479
|
}
|
|
18312
18480
|
const sweptParents = sweepExpiredSessionParents2();
|
|
18313
18481
|
if (sweptParents > 0) {
|
|
18314
|
-
safeWriteLog(
|
|
18482
|
+
safeWriteLog(PLUGIN_NAME14, { hook: "interval", session_parent_swept: sweptParents });
|
|
18315
18483
|
}
|
|
18316
18484
|
const beats = pickHeartbeats();
|
|
18317
18485
|
if (beats.length === 0)
|
|
@@ -18319,7 +18487,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18319
18487
|
for (const r of beats) {
|
|
18320
18488
|
const t = buildHeartbeatToast(r);
|
|
18321
18489
|
const sent = await showToast2(client, t, log8);
|
|
18322
|
-
safeWriteLog(
|
|
18490
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18323
18491
|
hook: "interval",
|
|
18324
18492
|
child: r.childID,
|
|
18325
18493
|
parent: r.parentID,
|
|
@@ -18336,7 +18504,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18336
18504
|
}
|
|
18337
18505
|
return {
|
|
18338
18506
|
event: async ({ event }) => {
|
|
18339
|
-
await safeAsync(
|
|
18507
|
+
await safeAsync(PLUGIN_NAME14, "event", async () => {
|
|
18340
18508
|
try {
|
|
18341
18509
|
if (detectUnparsedParentID(event) && _parentParseFailLogged < PARENT_PARSE_FAIL_MAX_LOG) {
|
|
18342
18510
|
_parentParseFailLogged++;
|
|
@@ -18344,7 +18512,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18344
18512
|
sample_count: _parentParseFailLogged,
|
|
18345
18513
|
hint: "如频繁出现请检查 plugins/subtask-heartbeat.ts::extractCreatedChild 是否适配新 SDK"
|
|
18346
18514
|
});
|
|
18347
|
-
safeWriteLog(
|
|
18515
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18348
18516
|
hook: "event",
|
|
18349
18517
|
type: "session.created.unparsed-parent-id",
|
|
18350
18518
|
sample_count: _parentParseFailLogged
|
|
@@ -18367,7 +18535,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18367
18535
|
agent: pending?.agent ?? created.agent,
|
|
18368
18536
|
description: pending?.description ?? null
|
|
18369
18537
|
});
|
|
18370
|
-
safeWriteLog(
|
|
18538
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18371
18539
|
hook: "event",
|
|
18372
18540
|
type: "session.created",
|
|
18373
18541
|
child: created.childID,
|
|
@@ -18378,7 +18546,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18378
18546
|
});
|
|
18379
18547
|
const startToast = buildStartToast(record);
|
|
18380
18548
|
const sent = await showToast2(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log8);
|
|
18381
|
-
safeWriteLog(
|
|
18549
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18382
18550
|
hook: "event",
|
|
18383
18551
|
type: "session.created.toast",
|
|
18384
18552
|
child: created.childID,
|
|
@@ -18398,7 +18566,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18398
18566
|
if (r) {
|
|
18399
18567
|
const t = buildEndToast(r, ended.type);
|
|
18400
18568
|
const sent = await showToast2(client, t, log8);
|
|
18401
|
-
safeWriteLog(
|
|
18569
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18402
18570
|
hook: "event",
|
|
18403
18571
|
type: ended.type,
|
|
18404
18572
|
child: r.childID,
|
|
@@ -18418,12 +18586,12 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18418
18586
|
const isTaskTool = input?.tool === "task";
|
|
18419
18587
|
if (inflight3.size === 0 && !isTaskTool)
|
|
18420
18588
|
return;
|
|
18421
|
-
await safeAsync(
|
|
18589
|
+
await safeAsync(PLUGIN_NAME14, "tool.execute.before", async () => {
|
|
18422
18590
|
if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
|
|
18423
18591
|
return;
|
|
18424
18592
|
if (isTaskTool) {
|
|
18425
18593
|
const args = output?.args ?? null;
|
|
18426
|
-
safeWriteLog(
|
|
18594
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18427
18595
|
hook: "tool.execute.before.task",
|
|
18428
18596
|
sessionID: input.sessionID,
|
|
18429
18597
|
args_keys: args && typeof args === "object" ? Object.keys(args) : null,
|
|
@@ -18435,7 +18603,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18435
18603
|
agent: extracted.subagentType,
|
|
18436
18604
|
description: extracted.description
|
|
18437
18605
|
});
|
|
18438
|
-
safeWriteLog(
|
|
18606
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18439
18607
|
hook: "tool.execute.before.task.enqueued",
|
|
18440
18608
|
parent: input.sessionID,
|
|
18441
18609
|
agent: extracted.subagentType,
|
|
@@ -18463,7 +18631,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18463
18631
|
"tool.execute.after": async (input, _output) => {
|
|
18464
18632
|
if (inflight3.size === 0)
|
|
18465
18633
|
return;
|
|
18466
|
-
await safeAsync(
|
|
18634
|
+
await safeAsync(PLUGIN_NAME14, "tool.execute.after", async () => {
|
|
18467
18635
|
if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
|
|
18468
18636
|
return;
|
|
18469
18637
|
const rec = inflight3.get(input.sessionID);
|
|
@@ -18486,11 +18654,11 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18486
18654
|
}
|
|
18487
18655
|
};
|
|
18488
18656
|
};
|
|
18489
|
-
var
|
|
18657
|
+
var handler14 = subtaskHeartbeatServer;
|
|
18490
18658
|
|
|
18491
18659
|
// plugins/parallel-status.ts
|
|
18492
|
-
var
|
|
18493
|
-
logLifecycle(
|
|
18660
|
+
var PLUGIN_NAME15 = "parallel-status";
|
|
18661
|
+
logLifecycle(PLUGIN_NAME15, "import");
|
|
18494
18662
|
var ID_MAX_LEN = 16;
|
|
18495
18663
|
var ID_KEEP_LEN = 13;
|
|
18496
18664
|
function shortId(s) {
|
|
@@ -18522,8 +18690,8 @@ function formatInflightMarkdown(snapshot, now = Date.now()) {
|
|
|
18522
18690
|
`);
|
|
18523
18691
|
}
|
|
18524
18692
|
var parallelStatusServer = async (ctx) => {
|
|
18525
|
-
const log9 = makePluginLogger(
|
|
18526
|
-
logLifecycle(
|
|
18693
|
+
const log9 = makePluginLogger(PLUGIN_NAME15);
|
|
18694
|
+
logLifecycle(PLUGIN_NAME15, "activate", { directory: ctx.directory });
|
|
18527
18695
|
return {
|
|
18528
18696
|
"command.execute.before": async (input, output) => {
|
|
18529
18697
|
try {
|
|
@@ -18542,23 +18710,23 @@ var parallelStatusServer = async (ctx) => {
|
|
|
18542
18710
|
synthetic: false
|
|
18543
18711
|
});
|
|
18544
18712
|
}
|
|
18545
|
-
log9.info(`[${
|
|
18713
|
+
log9.info(`[${PLUGIN_NAME15}] 已回写 ${snapshot.length} 条 inflight`);
|
|
18546
18714
|
} catch (err) {
|
|
18547
|
-
log9.error(`[${
|
|
18715
|
+
log9.error(`[${PLUGIN_NAME15}] command.execute.before 异常(已隔离)`, {
|
|
18548
18716
|
error: err instanceof Error ? err.message : String(err)
|
|
18549
18717
|
});
|
|
18550
18718
|
}
|
|
18551
18719
|
}
|
|
18552
18720
|
};
|
|
18553
18721
|
};
|
|
18554
|
-
var
|
|
18722
|
+
var handler15 = parallelStatusServer;
|
|
18555
18723
|
|
|
18556
18724
|
// plugins/parallel-tool-nudge.ts
|
|
18557
18725
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
|
|
18558
18726
|
import { join as join16 } from "node:path";
|
|
18559
18727
|
import { homedir as homedir6 } from "node:os";
|
|
18560
|
-
var
|
|
18561
|
-
logLifecycle(
|
|
18728
|
+
var PLUGIN_NAME16 = "parallel-tool-nudge";
|
|
18729
|
+
logLifecycle(PLUGIN_NAME16, "import", {});
|
|
18562
18730
|
var PARALLEL_SAFE_TOOLS = [
|
|
18563
18731
|
"read",
|
|
18564
18732
|
"smart_search",
|
|
@@ -18620,7 +18788,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
|
|
|
18620
18788
|
const result = new Map;
|
|
18621
18789
|
const safeSet = new Set(PARALLEL_SAFE_TOOLS);
|
|
18622
18790
|
const unionTools = new Set;
|
|
18623
|
-
const log9 = makePluginLogger(
|
|
18791
|
+
const log9 = makePluginLogger(PLUGIN_NAME16);
|
|
18624
18792
|
for (const dir of candidateDirs) {
|
|
18625
18793
|
if (!dirExists(dir))
|
|
18626
18794
|
continue;
|
|
@@ -18670,33 +18838,33 @@ function loadAgentToolsMap(rootDir, opts = {}) {
|
|
|
18670
18838
|
result.set(DEFAULT_AGENT_KEY, Array.from(unionTools));
|
|
18671
18839
|
return result;
|
|
18672
18840
|
}
|
|
18673
|
-
var
|
|
18674
|
-
var
|
|
18675
|
-
var
|
|
18676
|
-
function
|
|
18677
|
-
while (
|
|
18678
|
-
const oldestKey =
|
|
18841
|
+
var sessionAgentMap2 = new Map;
|
|
18842
|
+
var SESSION_CAP3 = 200;
|
|
18843
|
+
var SESSION_TTL_MS3 = 24 * 60 * 60 * 1000;
|
|
18844
|
+
function pruneIfOversize3() {
|
|
18845
|
+
while (sessionAgentMap2.size > SESSION_CAP3) {
|
|
18846
|
+
const oldestKey = sessionAgentMap2.keys().next().value;
|
|
18679
18847
|
if (oldestKey === undefined)
|
|
18680
18848
|
break;
|
|
18681
|
-
|
|
18849
|
+
sessionAgentMap2.delete(oldestKey);
|
|
18682
18850
|
}
|
|
18683
18851
|
}
|
|
18684
|
-
function
|
|
18685
|
-
return now - entry.ts >
|
|
18852
|
+
function isExpired3(entry, now) {
|
|
18853
|
+
return now - entry.ts > SESSION_TTL_MS3;
|
|
18686
18854
|
}
|
|
18687
18855
|
function resolveAgent(sessionID, nowFn = Date.now) {
|
|
18688
18856
|
if (!sessionID)
|
|
18689
18857
|
return "unknown";
|
|
18690
|
-
const entry =
|
|
18858
|
+
const entry = sessionAgentMap2.get(sessionID);
|
|
18691
18859
|
if (!entry)
|
|
18692
18860
|
return "unknown";
|
|
18693
18861
|
const now = nowFn();
|
|
18694
|
-
if (
|
|
18695
|
-
|
|
18862
|
+
if (isExpired3(entry, now)) {
|
|
18863
|
+
sessionAgentMap2.delete(sessionID);
|
|
18696
18864
|
return "unknown";
|
|
18697
18865
|
}
|
|
18698
|
-
|
|
18699
|
-
|
|
18866
|
+
sessionAgentMap2.delete(sessionID);
|
|
18867
|
+
sessionAgentMap2.set(sessionID, entry);
|
|
18700
18868
|
return entry.agent;
|
|
18701
18869
|
}
|
|
18702
18870
|
function renderNudge(agent, tools) {
|
|
@@ -18728,7 +18896,7 @@ This directive is re-injected every turn — it is not optional advice.
|
|
|
18728
18896
|
return body.slice(0, NUDGE_MAX_LEN2 - 4) + `
|
|
18729
18897
|
…`;
|
|
18730
18898
|
}
|
|
18731
|
-
var log9 = makePluginLogger(
|
|
18899
|
+
var log9 = makePluginLogger(PLUGIN_NAME16);
|
|
18732
18900
|
var parallelToolNudgeServer = async (ctx) => {
|
|
18733
18901
|
try {
|
|
18734
18902
|
const loaded = loadAgentToolsMap(ctx.directory ?? process.cwd());
|
|
@@ -18748,30 +18916,30 @@ var parallelToolNudgeServer = async (ctx) => {
|
|
|
18748
18916
|
});
|
|
18749
18917
|
}
|
|
18750
18918
|
const defaultTools = agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
|
|
18751
|
-
logLifecycle(
|
|
18919
|
+
logLifecycle(PLUGIN_NAME16, "activate", {
|
|
18752
18920
|
directory: ctx.directory,
|
|
18753
18921
|
real_agents_loaded: realAgentCount,
|
|
18754
18922
|
default_tools_union: defaultTools,
|
|
18755
18923
|
parallel_safe_whitelist: PARALLEL_SAFE_TOOLS,
|
|
18756
|
-
session_cap:
|
|
18757
|
-
session_ttl_ms:
|
|
18924
|
+
session_cap: SESSION_CAP3,
|
|
18925
|
+
session_ttl_ms: SESSION_TTL_MS3,
|
|
18758
18926
|
no_op: agentToolsMap.size === 0
|
|
18759
18927
|
});
|
|
18760
18928
|
return {
|
|
18761
18929
|
"chat.params": async (input, _output) => {
|
|
18762
|
-
await safeAsync(
|
|
18930
|
+
await safeAsync(PLUGIN_NAME16, "chat.params", async () => {
|
|
18763
18931
|
if (agentToolsMap.size === 0)
|
|
18764
18932
|
return;
|
|
18765
18933
|
const sid = input?.sessionID;
|
|
18766
18934
|
const agent = input?.agent;
|
|
18767
18935
|
if (!sid || !agent)
|
|
18768
18936
|
return;
|
|
18769
|
-
|
|
18770
|
-
|
|
18937
|
+
sessionAgentMap2.set(sid, { agent, ts: Date.now() });
|
|
18938
|
+
pruneIfOversize3();
|
|
18771
18939
|
});
|
|
18772
18940
|
},
|
|
18773
18941
|
"experimental.chat.system.transform": async (input, output) => {
|
|
18774
|
-
await safeAsync(
|
|
18942
|
+
await safeAsync(PLUGIN_NAME16, "experimental.chat.system.transform", async () => {
|
|
18775
18943
|
if (agentToolsMap.size === 0)
|
|
18776
18944
|
return;
|
|
18777
18945
|
if (!output || !Array.isArray(output.system))
|
|
@@ -18781,7 +18949,7 @@ var parallelToolNudgeServer = async (ctx) => {
|
|
|
18781
18949
|
const tools = agent !== "unknown" ? agentToolsMap.get(agent) ?? agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [] : agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
|
|
18782
18950
|
const nudge = renderNudge(agent, tools);
|
|
18783
18951
|
output.system.push(nudge);
|
|
18784
|
-
safeWriteLog(
|
|
18952
|
+
safeWriteLog(PLUGIN_NAME16, {
|
|
18785
18953
|
hook: "experimental.chat.system.transform",
|
|
18786
18954
|
sessionID: sid,
|
|
18787
18955
|
agent,
|
|
@@ -18793,11 +18961,11 @@ var parallelToolNudgeServer = async (ctx) => {
|
|
|
18793
18961
|
}
|
|
18794
18962
|
};
|
|
18795
18963
|
};
|
|
18796
|
-
var
|
|
18964
|
+
var handler16 = parallelToolNudgeServer;
|
|
18797
18965
|
|
|
18798
18966
|
// plugins/pwsh-utf8.ts
|
|
18799
|
-
var
|
|
18800
|
-
logLifecycle(
|
|
18967
|
+
var PLUGIN_NAME17 = "pwsh-utf8";
|
|
18968
|
+
logLifecycle(PLUGIN_NAME17, "import", {});
|
|
18801
18969
|
var PRELUDE = "chcp 65001 *> $null; " + "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); " + "$OutputEncoding = [System.Text.UTF8Encoding]::new(); ";
|
|
18802
18970
|
function prependUtf8Prelude(command) {
|
|
18803
18971
|
if (typeof command !== "string")
|
|
@@ -18810,15 +18978,15 @@ function prependUtf8Prelude(command) {
|
|
|
18810
18978
|
return command;
|
|
18811
18979
|
return PRELUDE + command;
|
|
18812
18980
|
}
|
|
18813
|
-
var
|
|
18981
|
+
var handler17 = async (_ctx3) => {
|
|
18814
18982
|
const enabled = process.platform === "win32" && process.env.CODEFORGE_DISABLE_PWSH_UTF8 !== "1";
|
|
18815
18983
|
const reason = enabled ? "win32" : process.platform !== "win32" ? "non-win32" : "disabled-by-env";
|
|
18816
|
-
logLifecycle(
|
|
18984
|
+
logLifecycle(PLUGIN_NAME17, "activate", { enabled, platform: process.platform, reason });
|
|
18817
18985
|
if (!enabled)
|
|
18818
18986
|
return {};
|
|
18819
18987
|
return {
|
|
18820
18988
|
"tool.execute.before": async (input, output) => {
|
|
18821
|
-
await safeAsync(
|
|
18989
|
+
await safeAsync(PLUGIN_NAME17, "tool.execute.before", async () => {
|
|
18822
18990
|
if (input.tool !== "bash")
|
|
18823
18991
|
return;
|
|
18824
18992
|
const args = output.args ?? {};
|
|
@@ -18827,7 +18995,7 @@ var handler16 = async (_ctx3) => {
|
|
|
18827
18995
|
if (next !== undefined && next !== original) {
|
|
18828
18996
|
args["command"] = next;
|
|
18829
18997
|
output.args = args;
|
|
18830
|
-
safeWriteLog(
|
|
18998
|
+
safeWriteLog(PLUGIN_NAME17, {
|
|
18831
18999
|
hook: "tool.execute.before",
|
|
18832
19000
|
tool: input.tool,
|
|
18833
19001
|
callID: input.callID,
|
|
@@ -19243,8 +19411,8 @@ async function markBlocksConsumed(absRoot, entries) {
|
|
|
19243
19411
|
}
|
|
19244
19412
|
|
|
19245
19413
|
// plugins/session-recovery.ts
|
|
19246
|
-
var
|
|
19247
|
-
logLifecycle(
|
|
19414
|
+
var PLUGIN_NAME18 = "session-recovery";
|
|
19415
|
+
logLifecycle(PLUGIN_NAME18, "import", {});
|
|
19248
19416
|
async function processSessionStart(currentSessionId, opts = {}) {
|
|
19249
19417
|
if (opts.disabled) {
|
|
19250
19418
|
return { ok: true, injected: false, reason: "disabled" };
|
|
@@ -19254,7 +19422,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19254
19422
|
excludeIds.add(currentSessionId);
|
|
19255
19423
|
const r = await scanLastSession({ ...opts, excludeIds: [...excludeIds] });
|
|
19256
19424
|
if (!r.ok) {
|
|
19257
|
-
opts.log?.warn?.(`[${
|
|
19425
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] 扫描失败:${r.error}`);
|
|
19258
19426
|
return { ok: false, injected: false, reason: "scan_error", error: r.error };
|
|
19259
19427
|
}
|
|
19260
19428
|
const plan = r.plan;
|
|
@@ -19263,7 +19431,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19263
19431
|
try {
|
|
19264
19432
|
pendingBlocks = await scanBlockPending(opts.root);
|
|
19265
19433
|
} catch (err) {
|
|
19266
|
-
opts.log?.warn?.(`[${
|
|
19434
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] scanBlockPending 异常:${err instanceof Error ? err.message : String(err)}`);
|
|
19267
19435
|
}
|
|
19268
19436
|
}
|
|
19269
19437
|
const hasPendingBlocks = pendingBlocks.length > 0;
|
|
@@ -19281,7 +19449,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19281
19449
|
await opts.injectRecovery(injection);
|
|
19282
19450
|
} catch (err) {
|
|
19283
19451
|
const msg = err instanceof Error ? err.message : String(err);
|
|
19284
|
-
opts.log?.warn?.(`[${
|
|
19452
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] injectRecovery 异常:${msg}`);
|
|
19285
19453
|
return { ok: false, injected: false, plan, reason: "inject_error", error: msg, pendingBlocks };
|
|
19286
19454
|
}
|
|
19287
19455
|
}
|
|
@@ -19289,7 +19457,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19289
19457
|
try {
|
|
19290
19458
|
await markBlocksConsumed(opts.root, pendingBlocks);
|
|
19291
19459
|
} catch (err) {
|
|
19292
|
-
opts.log?.warn?.(`[${
|
|
19460
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] markBlocksConsumed 异常:${err instanceof Error ? err.message : String(err)}`);
|
|
19293
19461
|
}
|
|
19294
19462
|
}
|
|
19295
19463
|
return { ok: true, injected: true, plan, reason: "ok", pendingBlocks };
|
|
@@ -19336,13 +19504,13 @@ function renderPrompt(plan) {
|
|
|
19336
19504
|
return lines.join(`
|
|
19337
19505
|
`);
|
|
19338
19506
|
}
|
|
19339
|
-
var log10 = makePluginLogger(
|
|
19507
|
+
var log10 = makePluginLogger(PLUGIN_NAME18);
|
|
19340
19508
|
var _lastInjection = null;
|
|
19341
19509
|
var sessionRecoveryServer = async (ctx) => {
|
|
19342
|
-
logLifecycle(
|
|
19510
|
+
logLifecycle(PLUGIN_NAME18, "activate", { directory: ctx.directory });
|
|
19343
19511
|
return {
|
|
19344
19512
|
event: async ({ event }) => {
|
|
19345
|
-
await safeAsync(
|
|
19513
|
+
await safeAsync(PLUGIN_NAME18, "event", async () => {
|
|
19346
19514
|
const e = event;
|
|
19347
19515
|
if (!e || typeof e.type !== "string")
|
|
19348
19516
|
return;
|
|
@@ -19357,7 +19525,7 @@ var sessionRecoveryServer = async (ctx) => {
|
|
|
19357
19525
|
_lastInjection = inj;
|
|
19358
19526
|
}
|
|
19359
19527
|
});
|
|
19360
|
-
safeWriteLog(
|
|
19528
|
+
safeWriteLog(PLUGIN_NAME18, {
|
|
19361
19529
|
hook: "event",
|
|
19362
19530
|
type: "session.start",
|
|
19363
19531
|
ok: r.ok,
|
|
@@ -19367,13 +19535,13 @@ var sessionRecoveryServer = async (ctx) => {
|
|
|
19367
19535
|
pending_blocks_count: r.pendingBlocks?.length ?? 0
|
|
19368
19536
|
});
|
|
19369
19537
|
if (r.injected && r.plan) {
|
|
19370
|
-
log10.info(`[${
|
|
19538
|
+
log10.info(`[${PLUGIN_NAME18}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason}, pending_blocks=${r.pendingBlocks?.length ?? 0})`);
|
|
19371
19539
|
}
|
|
19372
19540
|
});
|
|
19373
19541
|
}
|
|
19374
19542
|
};
|
|
19375
19543
|
};
|
|
19376
|
-
var
|
|
19544
|
+
var handler18 = sessionRecoveryServer;
|
|
19377
19545
|
|
|
19378
19546
|
// plugins/subtasks.ts
|
|
19379
19547
|
import { promises as fs18 } from "node:fs";
|
|
@@ -20200,7 +20368,7 @@ function sleep2(ms) {
|
|
|
20200
20368
|
|
|
20201
20369
|
// plugins/subtasks.ts
|
|
20202
20370
|
init_runtime_paths();
|
|
20203
|
-
var
|
|
20371
|
+
var PLUGIN_NAME19 = "subtasks";
|
|
20204
20372
|
function getLogFile(root = process.cwd()) {
|
|
20205
20373
|
return path22.join(runtimeDir(root), "logs", "subtasks.log");
|
|
20206
20374
|
}
|
|
@@ -20280,7 +20448,7 @@ async function handleParallelCommand(raw) {
|
|
|
20280
20448
|
specs = splitDescriptions(args.description, { parentId });
|
|
20281
20449
|
}
|
|
20282
20450
|
if (specs.length === 0) {
|
|
20283
|
-
log11?.warn(`[${
|
|
20451
|
+
log11?.warn(`[${PLUGIN_NAME19}] /parallel 缺有效子任务`);
|
|
20284
20452
|
await safeReply2(ctx, "⚠ /parallel 需要至少 1 个子任务(用 ; 或换行分隔)");
|
|
20285
20453
|
return { ok: false, reason: "no_subtasks" };
|
|
20286
20454
|
}
|
|
@@ -20315,7 +20483,7 @@ async function handleParallelCommand(raw) {
|
|
|
20315
20483
|
}
|
|
20316
20484
|
const canNotice = Boolean(ctx.client && ctx.parentSessionID);
|
|
20317
20485
|
if (specs.length === 1 && rawDescription.length >= 30 && ctx.client) {
|
|
20318
|
-
log11?.info(`[${
|
|
20486
|
+
log11?.info(`[${PLUGIN_NAME19}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
|
|
20319
20487
|
const dec = await decomposeTask(rawDescription, {
|
|
20320
20488
|
client: ctx.client,
|
|
20321
20489
|
directory: ctx.directory,
|
|
@@ -20336,7 +20504,7 @@ async function handleParallelCommand(raw) {
|
|
|
20336
20504
|
timeout_ms: undefined,
|
|
20337
20505
|
args: d.hintFiles && d.hintFiles.length > 0 ? { hintFiles: d.hintFiles } : undefined
|
|
20338
20506
|
}));
|
|
20339
|
-
log11?.info(`[${
|
|
20507
|
+
log11?.info(`[${PLUGIN_NAME19}] AI 拆分成功 → ${specs.length} 个子任务`);
|
|
20340
20508
|
if (canNotice) {
|
|
20341
20509
|
const lines = [
|
|
20342
20510
|
`\uD83E\uDD16 AI 已自动拆为 ${specs.length} 个并行子任务:`,
|
|
@@ -20348,7 +20516,7 @@ async function handleParallelCommand(raw) {
|
|
|
20348
20516
|
});
|
|
20349
20517
|
}
|
|
20350
20518
|
} else if (dec.reason === "llm_unavailable" || dec.reason === "parse_failed") {
|
|
20351
|
-
log11?.warn(`[${
|
|
20519
|
+
log11?.warn(`[${PLUGIN_NAME19}] AI 拆分失败(${dec.reason}),按单任务执行`);
|
|
20352
20520
|
}
|
|
20353
20521
|
}
|
|
20354
20522
|
if (canNotice) {
|
|
@@ -20427,7 +20595,7 @@ async function handleParallelCommand(raw) {
|
|
|
20427
20595
|
});
|
|
20428
20596
|
} catch (err) {
|
|
20429
20597
|
const msg = err instanceof Error ? err.message : String(err);
|
|
20430
|
-
log11?.error(`[${
|
|
20598
|
+
log11?.error(`[${PLUGIN_NAME19}] schedule 抛错`, { error: msg });
|
|
20431
20599
|
await safeReply2(ctx, `❌ 并发调度失败:${msg}`);
|
|
20432
20600
|
return { ok: false, reason: msg };
|
|
20433
20601
|
}
|
|
@@ -20445,7 +20613,7 @@ async function handleParallelCommand(raw) {
|
|
|
20445
20613
|
}
|
|
20446
20614
|
await safeReply2(ctx, summaryLines.join(`
|
|
20447
20615
|
`));
|
|
20448
|
-
log11?.info(`[${
|
|
20616
|
+
log11?.info(`[${PLUGIN_NAME19}] schedule 完成`, {
|
|
20449
20617
|
parentId,
|
|
20450
20618
|
success: result.digest.success,
|
|
20451
20619
|
failed: result.digest.failed,
|
|
@@ -20458,7 +20626,7 @@ async function handleParallelCommand(raw) {
|
|
|
20458
20626
|
try {
|
|
20459
20627
|
await ctx.onCompleted(result);
|
|
20460
20628
|
} catch (err) {
|
|
20461
|
-
log11?.warn(`[${
|
|
20629
|
+
log11?.warn(`[${PLUGIN_NAME19}] onCompleted hook 抛错(已隔离)`, {
|
|
20462
20630
|
error: err instanceof Error ? err.message : String(err)
|
|
20463
20631
|
});
|
|
20464
20632
|
}
|
|
@@ -20500,7 +20668,7 @@ async function writeLog(level, msg, data) {
|
|
|
20500
20668
|
const line = JSON.stringify({
|
|
20501
20669
|
ts: new Date().toISOString(),
|
|
20502
20670
|
level,
|
|
20503
|
-
plugin:
|
|
20671
|
+
plugin: PLUGIN_NAME19,
|
|
20504
20672
|
msg,
|
|
20505
20673
|
data
|
|
20506
20674
|
}) + `
|
|
@@ -20511,11 +20679,11 @@ async function writeLog(level, msg, data) {
|
|
|
20511
20679
|
await fs18.appendFile(logFile, line, "utf8");
|
|
20512
20680
|
} catch {}
|
|
20513
20681
|
}
|
|
20514
|
-
logLifecycle(
|
|
20682
|
+
logLifecycle(PLUGIN_NAME19, "import");
|
|
20515
20683
|
var subtasksServer = async (ctx) => {
|
|
20516
|
-
const log11 = makePluginLogger(
|
|
20684
|
+
const log11 = makePluginLogger(PLUGIN_NAME19);
|
|
20517
20685
|
const client = ctx?.client ?? undefined;
|
|
20518
|
-
logLifecycle(
|
|
20686
|
+
logLifecycle(PLUGIN_NAME19, "activate", {
|
|
20519
20687
|
directory: ctx.directory,
|
|
20520
20688
|
hasClient: Boolean(client)
|
|
20521
20689
|
});
|
|
@@ -20612,19 +20780,19 @@ var subtasksServer = async (ctx) => {
|
|
|
20612
20780
|
});
|
|
20613
20781
|
}
|
|
20614
20782
|
} catch (err) {
|
|
20615
|
-
log11.error(`[${
|
|
20783
|
+
log11.error(`[${PLUGIN_NAME19}] command.execute.before 异常(已隔离)`, {
|
|
20616
20784
|
error: err instanceof Error ? err.message : String(err)
|
|
20617
20785
|
});
|
|
20618
20786
|
}
|
|
20619
20787
|
}
|
|
20620
20788
|
};
|
|
20621
20789
|
};
|
|
20622
|
-
var
|
|
20790
|
+
var handler19 = subtasksServer;
|
|
20623
20791
|
|
|
20624
20792
|
// plugins/terminal-monitor.ts
|
|
20625
20793
|
import * as crypto5 from "node:crypto";
|
|
20626
|
-
var
|
|
20627
|
-
logLifecycle(
|
|
20794
|
+
var PLUGIN_NAME20 = "terminal-monitor";
|
|
20795
|
+
logLifecycle(PLUGIN_NAME20, "import", {});
|
|
20628
20796
|
var DEFAULT_CONFIG6 = {
|
|
20629
20797
|
minScore: 0.6,
|
|
20630
20798
|
cooldownMs: 30000,
|
|
@@ -20906,17 +21074,17 @@ function describeError(err) {
|
|
|
20906
21074
|
return String(err);
|
|
20907
21075
|
}
|
|
20908
21076
|
}
|
|
20909
|
-
var log11 = makePluginLogger(
|
|
21077
|
+
var log11 = makePluginLogger(PLUGIN_NAME20);
|
|
20910
21078
|
var lru = new FingerprintLRU2;
|
|
20911
21079
|
var _lastNotification = null;
|
|
20912
21080
|
var terminalMonitorServer = async (ctx) => {
|
|
20913
|
-
logLifecycle(
|
|
21081
|
+
logLifecycle(PLUGIN_NAME20, "activate", {
|
|
20914
21082
|
directory: ctx.directory,
|
|
20915
21083
|
triggerEventTypes: ["terminal.output", "terminal.exit"]
|
|
20916
21084
|
});
|
|
20917
21085
|
return {
|
|
20918
21086
|
event: async ({ event }) => {
|
|
20919
|
-
await safeAsync(
|
|
21087
|
+
await safeAsync(PLUGIN_NAME20, "event", async () => {
|
|
20920
21088
|
const e = event;
|
|
20921
21089
|
if (!e || typeof e.type !== "string")
|
|
20922
21090
|
return;
|
|
@@ -20930,7 +21098,7 @@ var terminalMonitorServer = async (ctx) => {
|
|
|
20930
21098
|
_lastNotification = msg;
|
|
20931
21099
|
}
|
|
20932
21100
|
});
|
|
20933
|
-
safeWriteLog(
|
|
21101
|
+
safeWriteLog(PLUGIN_NAME20, {
|
|
20934
21102
|
hook: "event",
|
|
20935
21103
|
type: e.type,
|
|
20936
21104
|
notified: r.notified,
|
|
@@ -20938,17 +21106,17 @@ var terminalMonitorServer = async (ctx) => {
|
|
|
20938
21106
|
findings: r.notification?.findings.length ?? 0
|
|
20939
21107
|
});
|
|
20940
21108
|
if (r.notified && r.notification) {
|
|
20941
|
-
log11.info(`[${
|
|
21109
|
+
log11.info(`[${PLUGIN_NAME20}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
|
|
20942
21110
|
}
|
|
20943
21111
|
});
|
|
20944
21112
|
}
|
|
20945
21113
|
};
|
|
20946
21114
|
};
|
|
20947
|
-
var
|
|
21115
|
+
var handler20 = terminalMonitorServer;
|
|
20948
21116
|
|
|
20949
21117
|
// plugins/token-manager.ts
|
|
20950
|
-
var
|
|
20951
|
-
logLifecycle(
|
|
21118
|
+
var PLUGIN_NAME21 = "token-manager";
|
|
21119
|
+
logLifecycle(PLUGIN_NAME21, "import", {});
|
|
20952
21120
|
async function handleMessageBefore(raw, log12, defaults) {
|
|
20953
21121
|
const ctx = raw ?? {};
|
|
20954
21122
|
if (!Array.isArray(ctx.messages) || ctx.messages.length === 0)
|
|
@@ -20968,21 +21136,21 @@ async function handleMessageBefore(raw, log12, defaults) {
|
|
|
20968
21136
|
};
|
|
20969
21137
|
if (r.compressed) {
|
|
20970
21138
|
ctx.messages = r.messages;
|
|
20971
|
-
log12?.info(`[${
|
|
21139
|
+
log12?.info(`[${PLUGIN_NAME21}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
|
|
20972
21140
|
}
|
|
20973
21141
|
return r;
|
|
20974
21142
|
} catch (err) {
|
|
20975
|
-
log12?.warn(`[${
|
|
21143
|
+
log12?.warn(`[${PLUGIN_NAME21}] 压缩异常(已隔离)`, {
|
|
20976
21144
|
error: err instanceof Error ? err.message : String(err)
|
|
20977
21145
|
});
|
|
20978
21146
|
return null;
|
|
20979
21147
|
}
|
|
20980
21148
|
}
|
|
20981
|
-
var log12 = makePluginLogger(
|
|
21149
|
+
var log12 = makePluginLogger(PLUGIN_NAME21);
|
|
20982
21150
|
var tokenManagerServer = async (ctx) => {
|
|
20983
21151
|
const rt = loadRuntimeSync();
|
|
20984
21152
|
const threshold = rt.runtime.context.condenser_threshold_ratio;
|
|
20985
|
-
logLifecycle(
|
|
21153
|
+
logLifecycle(PLUGIN_NAME21, "activate", {
|
|
20986
21154
|
directory: ctx.directory,
|
|
20987
21155
|
threshold,
|
|
20988
21156
|
target: DEFAULT_CONDENSE.target,
|
|
@@ -20991,7 +21159,7 @@ var tokenManagerServer = async (ctx) => {
|
|
|
20991
21159
|
});
|
|
20992
21160
|
return {
|
|
20993
21161
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
20994
|
-
await safeAsync(
|
|
21162
|
+
await safeAsync(PLUGIN_NAME21, "experimental.chat.messages.transform", async () => {
|
|
20995
21163
|
const list = output.messages;
|
|
20996
21164
|
if (!Array.isArray(list) || list.length === 0)
|
|
20997
21165
|
return;
|
|
@@ -21004,7 +21172,7 @@ var tokenManagerServer = async (ctx) => {
|
|
|
21004
21172
|
const r = await handleMessageBefore({ messages: flat }, log12, { threshold });
|
|
21005
21173
|
if (!r)
|
|
21006
21174
|
return;
|
|
21007
|
-
safeWriteLog(
|
|
21175
|
+
safeWriteLog(PLUGIN_NAME21, {
|
|
21008
21176
|
hook: "experimental.chat.messages.transform",
|
|
21009
21177
|
mode: "observe-only",
|
|
21010
21178
|
before_msgs: r.before.count,
|
|
@@ -21015,13 +21183,13 @@ var tokenManagerServer = async (ctx) => {
|
|
|
21015
21183
|
reason: r.reason
|
|
21016
21184
|
});
|
|
21017
21185
|
if (r.compressed) {
|
|
21018
|
-
log12.warn(`[${
|
|
21186
|
+
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)`);
|
|
21019
21187
|
}
|
|
21020
21188
|
});
|
|
21021
21189
|
}
|
|
21022
21190
|
};
|
|
21023
21191
|
};
|
|
21024
|
-
var
|
|
21192
|
+
var handler21 = tokenManagerServer;
|
|
21025
21193
|
|
|
21026
21194
|
// plugins/tool-policy.ts
|
|
21027
21195
|
import { promises as fs19 } from "node:fs";
|
|
@@ -21284,9 +21452,14 @@ function checkFileAccess(acl, file, op) {
|
|
|
21284
21452
|
}
|
|
21285
21453
|
|
|
21286
21454
|
// plugins/tool-policy.ts
|
|
21287
|
-
var
|
|
21288
|
-
logLifecycle(
|
|
21455
|
+
var PLUGIN_NAME22 = "tool-policy";
|
|
21456
|
+
logLifecycle(PLUGIN_NAME22, "import", {});
|
|
21289
21457
|
var EMPTY_ACL = { whitelistMode: false };
|
|
21458
|
+
function getAgentAcl(cfg, agent) {
|
|
21459
|
+
if (!agent || !cfg.per_agent)
|
|
21460
|
+
return;
|
|
21461
|
+
return cfg.per_agent[agent];
|
|
21462
|
+
}
|
|
21290
21463
|
var SUBAGENT_APPLY_DENY_LIST = new Set([
|
|
21291
21464
|
"coder",
|
|
21292
21465
|
"planner",
|
|
@@ -21350,7 +21523,7 @@ async function loadPolicy(root = process.cwd()) {
|
|
|
21350
21523
|
function classifyToolKind(toolName) {
|
|
21351
21524
|
return classifyTool(toolName);
|
|
21352
21525
|
}
|
|
21353
|
-
async function
|
|
21526
|
+
async function resolveCurrentAgent2(client, sessionID, log13) {
|
|
21354
21527
|
try {
|
|
21355
21528
|
const sessionApi = client?.session;
|
|
21356
21529
|
if (!sessionApi || typeof sessionApi.get !== "function") {
|
|
@@ -21376,24 +21549,24 @@ async function resolveCurrentAgent(client, sessionID, log13) {
|
|
|
21376
21549
|
return;
|
|
21377
21550
|
}
|
|
21378
21551
|
}
|
|
21379
|
-
var log13 = makePluginLogger(
|
|
21552
|
+
var log13 = makePluginLogger(PLUGIN_NAME22);
|
|
21380
21553
|
var toolPolicyServer = async (ctx) => {
|
|
21381
21554
|
const directory = ctx.directory ?? process.cwd();
|
|
21382
21555
|
const cfg = await loadPolicy(directory);
|
|
21383
|
-
logLifecycle(
|
|
21556
|
+
logLifecycle(PLUGIN_NAME22, "activate", {
|
|
21384
21557
|
directory,
|
|
21385
21558
|
acl_loaded: !!cfg.acl
|
|
21386
21559
|
});
|
|
21387
21560
|
return {
|
|
21388
21561
|
"tool.execute.before": async (input, output) => {
|
|
21389
21562
|
let denied;
|
|
21390
|
-
await safeAsync(
|
|
21563
|
+
await safeAsync(PLUGIN_NAME22, "tool.execute.before", async () => {
|
|
21391
21564
|
const toolName = input.tool;
|
|
21392
21565
|
const argsObj = output.args ?? {};
|
|
21393
21566
|
const needsAgentDetection = toolName === "pending_changes" && (argsObj.action === "apply" || argsObj.action === "apply_all");
|
|
21394
21567
|
let currentAgent = undefined;
|
|
21395
21568
|
if (needsAgentDetection) {
|
|
21396
|
-
currentAgent = await
|
|
21569
|
+
currentAgent = await resolveCurrentAgent2(ctx.client, input.sessionID, log13);
|
|
21397
21570
|
}
|
|
21398
21571
|
const files = [];
|
|
21399
21572
|
const filePath = argsObj["path"] ?? argsObj["filePath"] ?? argsObj["file"];
|
|
@@ -21407,7 +21580,7 @@ var toolPolicyServer = async (ctx) => {
|
|
|
21407
21580
|
args: argsObj,
|
|
21408
21581
|
files: files.length ? files : undefined
|
|
21409
21582
|
}, cfg, currentAgent);
|
|
21410
|
-
safeWriteLog(
|
|
21583
|
+
safeWriteLog(PLUGIN_NAME22, {
|
|
21411
21584
|
hook: "tool.execute.before",
|
|
21412
21585
|
tool: toolName,
|
|
21413
21586
|
callID: input.callID,
|
|
@@ -21418,7 +21591,7 @@ var toolPolicyServer = async (ctx) => {
|
|
|
21418
21591
|
currentAgent
|
|
21419
21592
|
});
|
|
21420
21593
|
if (decision.action === "deny") {
|
|
21421
|
-
log13.warn(`[${
|
|
21594
|
+
log13.warn(`[${PLUGIN_NAME22}] DENY ${toolName}`, {
|
|
21422
21595
|
action: argsObj.action,
|
|
21423
21596
|
currentAgent,
|
|
21424
21597
|
reasons: decision.reasons,
|
|
@@ -21433,7 +21606,7 @@ var toolPolicyServer = async (ctx) => {
|
|
|
21433
21606
|
}
|
|
21434
21607
|
};
|
|
21435
21608
|
};
|
|
21436
|
-
var
|
|
21609
|
+
var handler22 = toolPolicyServer;
|
|
21437
21610
|
|
|
21438
21611
|
// plugins/update-checker.ts
|
|
21439
21612
|
import { existsSync as existsSync5 } from "node:fs";
|
|
@@ -21463,7 +21636,7 @@ import * as zlib from "node:zlib";
|
|
|
21463
21636
|
// lib/version-injected.ts
|
|
21464
21637
|
function getInjectedVersion() {
|
|
21465
21638
|
try {
|
|
21466
|
-
const v = "0.5.
|
|
21639
|
+
const v = "0.5.10";
|
|
21467
21640
|
if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
|
|
21468
21641
|
return v;
|
|
21469
21642
|
}
|
|
@@ -21966,20 +22139,20 @@ function compareOpencodeVersion(opts) {
|
|
|
21966
22139
|
}
|
|
21967
22140
|
|
|
21968
22141
|
// plugins/update-checker.ts
|
|
21969
|
-
var
|
|
22142
|
+
var PLUGIN_NAME23 = "update-checker";
|
|
21970
22143
|
var PLUGIN_VERSION = "2.0.0";
|
|
21971
|
-
logLifecycle(
|
|
22144
|
+
logLifecycle(PLUGIN_NAME23, "import", { version: PLUGIN_VERSION });
|
|
21972
22145
|
var updateCheckerServer = async (ctx) => {
|
|
21973
22146
|
const yieldResult = shouldYieldToLocalPlugin({ directory: ctx.directory });
|
|
21974
22147
|
if (yieldResult.yield) {
|
|
21975
|
-
safeWriteLog(
|
|
22148
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
21976
22149
|
level: "info",
|
|
21977
22150
|
msg: "dev_mode_yield_skip",
|
|
21978
22151
|
reason: yieldResult.reason,
|
|
21979
22152
|
markerPath: yieldResult.markerPath,
|
|
21980
22153
|
detail: formatYieldLog(yieldResult)
|
|
21981
22154
|
});
|
|
21982
|
-
logLifecycle(
|
|
22155
|
+
logLifecycle(PLUGIN_NAME23, "activate", {
|
|
21983
22156
|
yield_to_local: true,
|
|
21984
22157
|
yield_reason: yieldResult.reason,
|
|
21985
22158
|
skipped: "auto_install + background_check"
|
|
@@ -21988,7 +22161,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
21988
22161
|
}
|
|
21989
22162
|
const rt = loadRuntimeSync();
|
|
21990
22163
|
const u = rt.runtime.update;
|
|
21991
|
-
logLifecycle(
|
|
22164
|
+
logLifecycle(PLUGIN_NAME23, "activate", {
|
|
21992
22165
|
version: PLUGIN_VERSION,
|
|
21993
22166
|
auto_check_enabled: u.auto_check_enabled,
|
|
21994
22167
|
interval_hours: u.interval_hours,
|
|
@@ -22000,14 +22173,14 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22000
22173
|
repo_fallback: u.repo,
|
|
22001
22174
|
config_source: "codeforge.json"
|
|
22002
22175
|
});
|
|
22003
|
-
await safeAsync(
|
|
22176
|
+
await safeAsync(PLUGIN_NAME23, "opencode_version_check", async () => {
|
|
22004
22177
|
const compat = loadCompatibility();
|
|
22005
22178
|
const opencodeVer = detectOpencodeVersion();
|
|
22006
22179
|
const verdict = compareOpencodeVersion({
|
|
22007
22180
|
currentOpencodeVer: opencodeVer,
|
|
22008
22181
|
compat
|
|
22009
22182
|
});
|
|
22010
|
-
safeWriteLog(
|
|
22183
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22011
22184
|
level: "info",
|
|
22012
22185
|
msg: "opencode_version_check",
|
|
22013
22186
|
opencodeVer,
|
|
@@ -22024,14 +22197,14 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22024
22197
|
}
|
|
22025
22198
|
});
|
|
22026
22199
|
if (!u.auto_check_enabled) {
|
|
22027
|
-
safeWriteLog(
|
|
22200
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22028
22201
|
level: "info",
|
|
22029
22202
|
msg: "auto-check disabled (codeforge.json update.auto_check_enabled=false)"
|
|
22030
22203
|
});
|
|
22031
22204
|
return {};
|
|
22032
22205
|
}
|
|
22033
22206
|
setImmediate(() => {
|
|
22034
|
-
safeAsync(
|
|
22207
|
+
safeAsync(PLUGIN_NAME23, "checkAndMaybeUpdate", async () => {
|
|
22035
22208
|
const local = readLocalVersion();
|
|
22036
22209
|
let npmResult = null;
|
|
22037
22210
|
try {
|
|
@@ -22042,7 +22215,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22042
22215
|
timeoutMs: 5000
|
|
22043
22216
|
});
|
|
22044
22217
|
} catch (e) {
|
|
22045
|
-
safeWriteLog(
|
|
22218
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22046
22219
|
level: "warn",
|
|
22047
22220
|
msg: "npm_fetch_failed",
|
|
22048
22221
|
error: e.message
|
|
@@ -22051,7 +22224,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22051
22224
|
return;
|
|
22052
22225
|
}
|
|
22053
22226
|
if (!npmResult) {
|
|
22054
|
-
safeWriteLog(
|
|
22227
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22055
22228
|
level: "info",
|
|
22056
22229
|
msg: "npm_no_release",
|
|
22057
22230
|
package: u.package,
|
|
@@ -22060,7 +22233,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22060
22233
|
return;
|
|
22061
22234
|
}
|
|
22062
22235
|
const hasUpdate = cmpVersion(local, npmResult.version) < 0;
|
|
22063
|
-
safeWriteLog(
|
|
22236
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22064
22237
|
level: "info",
|
|
22065
22238
|
msg: "npm_check_result",
|
|
22066
22239
|
local,
|
|
@@ -22075,10 +22248,10 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22075
22248
|
更新命令:npx ${u.package} install --global`);
|
|
22076
22249
|
return;
|
|
22077
22250
|
}
|
|
22078
|
-
await safeAsync(
|
|
22251
|
+
await safeAsync(PLUGIN_NAME23, "auto_install_bundle", async () => {
|
|
22079
22252
|
const target = getOpencodeBundlePath();
|
|
22080
22253
|
if (!target) {
|
|
22081
|
-
safeWriteLog(
|
|
22254
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22082
22255
|
level: "warn",
|
|
22083
22256
|
msg: "auto_install_skip",
|
|
22084
22257
|
reason: "无法定位 opencode bundle 路径"
|
|
@@ -22097,7 +22270,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22097
22270
|
oldVersion: local,
|
|
22098
22271
|
keepBackups: u.backup_keep
|
|
22099
22272
|
});
|
|
22100
|
-
safeWriteLog(
|
|
22273
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22101
22274
|
level: "info",
|
|
22102
22275
|
msg: "auto_install_success",
|
|
22103
22276
|
local,
|
|
@@ -22137,7 +22310,7 @@ function getOpencodeBundlePath() {
|
|
|
22137
22310
|
return candidates[0] ?? null;
|
|
22138
22311
|
}
|
|
22139
22312
|
async function fallbackToGitHubReleases(ctx, u) {
|
|
22140
|
-
await safeAsync(
|
|
22313
|
+
await safeAsync(PLUGIN_NAME23, "github_fallback", async () => {
|
|
22141
22314
|
const result = await checkUpdateOnce({
|
|
22142
22315
|
repo: u.repo,
|
|
22143
22316
|
intervalMs: u.interval_hours * 3600 * 1000
|
|
@@ -22147,7 +22320,7 @@ async function fallbackToGitHubReleases(ctx, u) {
|
|
|
22147
22320
|
}
|
|
22148
22321
|
async function reportLegacyResult(ctx, result, repo) {
|
|
22149
22322
|
if (result.error && !result.fromCache) {
|
|
22150
|
-
safeWriteLog(
|
|
22323
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22151
22324
|
level: "warn",
|
|
22152
22325
|
msg: `update check failed: ${result.error}`,
|
|
22153
22326
|
...result
|
|
@@ -22155,7 +22328,7 @@ async function reportLegacyResult(ctx, result, repo) {
|
|
|
22155
22328
|
return;
|
|
22156
22329
|
}
|
|
22157
22330
|
if (!result.hasUpdate) {
|
|
22158
|
-
safeWriteLog(
|
|
22331
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22159
22332
|
level: "info",
|
|
22160
22333
|
msg: `up-to-date (local=${result.local}, remote=${result.remote}${result.fromCache ? ", from_cache" : ""})`,
|
|
22161
22334
|
...result
|
|
@@ -22165,7 +22338,7 @@ async function reportLegacyResult(ctx, result, repo) {
|
|
|
22165
22338
|
const updateCmd = `bunx --bun github:${repo} install`;
|
|
22166
22339
|
const toast = `[CodeForge] 有新版本:${result.local} → ${result.remote}
|
|
22167
22340
|
更新命令:${updateCmd}`;
|
|
22168
|
-
safeWriteLog(
|
|
22341
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22169
22342
|
level: "info",
|
|
22170
22343
|
msg: "new_version_available_github_fallback",
|
|
22171
22344
|
local: result.local,
|
|
@@ -22176,17 +22349,17 @@ async function reportLegacyResult(ctx, result, repo) {
|
|
|
22176
22349
|
await postToast(ctx, toast);
|
|
22177
22350
|
}
|
|
22178
22351
|
async function postToast(ctx, message) {
|
|
22179
|
-
await safeAsync(
|
|
22352
|
+
await safeAsync(PLUGIN_NAME23, "client.app.log", async () => {
|
|
22180
22353
|
await ctx.client.app.log({
|
|
22181
22354
|
body: {
|
|
22182
|
-
service:
|
|
22355
|
+
service: PLUGIN_NAME23,
|
|
22183
22356
|
level: "info",
|
|
22184
22357
|
message
|
|
22185
22358
|
}
|
|
22186
22359
|
});
|
|
22187
22360
|
});
|
|
22188
22361
|
}
|
|
22189
|
-
var
|
|
22362
|
+
var handler23 = updateCheckerServer;
|
|
22190
22363
|
|
|
22191
22364
|
// plugins/workflow-engine.ts
|
|
22192
22365
|
import * as path26 from "node:path";
|
|
@@ -22641,9 +22814,9 @@ async function runStepAutoFeedback(step, adapter) {
|
|
|
22641
22814
|
}
|
|
22642
22815
|
|
|
22643
22816
|
// plugins/workflow-engine.ts
|
|
22644
|
-
var
|
|
22645
|
-
logLifecycle(
|
|
22646
|
-
var fallbackLog2 = makePluginLogger(
|
|
22817
|
+
var PLUGIN_NAME24 = "workflow-engine";
|
|
22818
|
+
logLifecycle(PLUGIN_NAME24, "import", {});
|
|
22819
|
+
var fallbackLog2 = makePluginLogger(PLUGIN_NAME24);
|
|
22647
22820
|
var _registry = null;
|
|
22648
22821
|
async function loadRegistry(workflowsDir) {
|
|
22649
22822
|
const { loaded, failed } = await loadWorkflowsFromDir(workflowsDir);
|
|
@@ -22663,32 +22836,32 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
|
|
|
22663
22836
|
const log14 = ctx.log ?? fallbackLog2;
|
|
22664
22837
|
const command = typeof ctx.command === "string" ? ctx.command : null;
|
|
22665
22838
|
if (!command) {
|
|
22666
|
-
log14.warn(`[${
|
|
22839
|
+
log14.warn(`[${PLUGIN_NAME24}] command.invoked 缺 command 字段`, ctx);
|
|
22667
22840
|
return null;
|
|
22668
22841
|
}
|
|
22669
22842
|
const reg = await ensureRegistry(workflowsDir);
|
|
22670
22843
|
if (reg.errors.length) {
|
|
22671
|
-
log14.warn(`[${
|
|
22844
|
+
log14.warn(`[${PLUGIN_NAME24}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
|
|
22672
22845
|
}
|
|
22673
22846
|
const wf = reg.workflows.find((w) => matchesTrigger(w, command));
|
|
22674
22847
|
if (!wf) {
|
|
22675
|
-
log14.info(`[${
|
|
22848
|
+
log14.info(`[${PLUGIN_NAME24}] no workflow matches "${command}"`);
|
|
22676
22849
|
return null;
|
|
22677
22850
|
}
|
|
22678
|
-
log14.info(`[${
|
|
22851
|
+
log14.info(`[${PLUGIN_NAME24}] dispatch "${command}" → workflow "${wf.name}"`);
|
|
22679
22852
|
try {
|
|
22680
22853
|
const result = await run(wf, {
|
|
22681
22854
|
mode: ctx.adapter ? "real" : "dry_run",
|
|
22682
22855
|
autonomy: ctx.autonomy ?? "semi",
|
|
22683
22856
|
adapter: ctx.adapter
|
|
22684
22857
|
});
|
|
22685
|
-
log14.info(`[${
|
|
22858
|
+
log14.info(`[${PLUGIN_NAME24}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
|
|
22686
22859
|
steps: result.plan.steps.length,
|
|
22687
22860
|
results: result.results.length
|
|
22688
22861
|
});
|
|
22689
22862
|
return result;
|
|
22690
22863
|
} catch (err) {
|
|
22691
|
-
log14.error(`[${
|
|
22864
|
+
log14.error(`[${PLUGIN_NAME24}] workflow "${wf.name}" 执行失败`, {
|
|
22692
22865
|
error: err instanceof Error ? err.message : String(err)
|
|
22693
22866
|
});
|
|
22694
22867
|
throw err;
|
|
@@ -22697,15 +22870,15 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
|
|
|
22697
22870
|
var workflowEngineServer = async (ctx) => {
|
|
22698
22871
|
const directory = ctx.directory ?? process.cwd();
|
|
22699
22872
|
const workflowsDir = path26.join(directory, "workflows");
|
|
22700
|
-
ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${
|
|
22873
|
+
ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME24}] preload workflows failed`, {
|
|
22701
22874
|
error: err instanceof Error ? err.message : String(err)
|
|
22702
22875
|
}));
|
|
22703
|
-
logLifecycle(
|
|
22876
|
+
logLifecycle(PLUGIN_NAME24, "activate", { directory, workflowsDir });
|
|
22704
22877
|
return {
|
|
22705
22878
|
"command.execute.before": async (input, output) => {
|
|
22706
|
-
await safeAsync(
|
|
22879
|
+
await safeAsync(PLUGIN_NAME24, "command.execute.before", async () => {
|
|
22707
22880
|
const cmd = input.command.startsWith("/") ? input.command : `/${input.command}`;
|
|
22708
|
-
safeWriteLog(
|
|
22881
|
+
safeWriteLog(PLUGIN_NAME24, {
|
|
22709
22882
|
hook: "command.execute.before",
|
|
22710
22883
|
command: cmd,
|
|
22711
22884
|
sessionID: input.sessionID
|
|
@@ -22720,14 +22893,14 @@ var workflowEngineServer = async (ctx) => {
|
|
|
22720
22893
|
});
|
|
22721
22894
|
},
|
|
22722
22895
|
"chat.message": async (input, output) => {
|
|
22723
|
-
await safeAsync(
|
|
22896
|
+
await safeAsync(PLUGIN_NAME24, "chat.message", async () => {
|
|
22724
22897
|
const text = extractUserText(output).trim();
|
|
22725
22898
|
if (!text.startsWith("/"))
|
|
22726
22899
|
return;
|
|
22727
22900
|
const cmd = text.split(/\s+/)[0];
|
|
22728
22901
|
if (!cmd)
|
|
22729
22902
|
return;
|
|
22730
|
-
safeWriteLog(
|
|
22903
|
+
safeWriteLog(PLUGIN_NAME24, {
|
|
22731
22904
|
hook: "chat.message",
|
|
22732
22905
|
command: cmd,
|
|
22733
22906
|
sessionID: input.sessionID
|
|
@@ -22737,12 +22910,12 @@ var workflowEngineServer = async (ctx) => {
|
|
|
22737
22910
|
}
|
|
22738
22911
|
};
|
|
22739
22912
|
};
|
|
22740
|
-
var
|
|
22913
|
+
var handler24 = workflowEngineServer;
|
|
22741
22914
|
|
|
22742
22915
|
// plugins/session-worktree-guard.ts
|
|
22743
22916
|
import path27 from "node:path";
|
|
22744
|
-
var
|
|
22745
|
-
logLifecycle(
|
|
22917
|
+
var PLUGIN_NAME25 = "session-worktree-guard";
|
|
22918
|
+
logLifecycle(PLUGIN_NAME25, "import", {});
|
|
22746
22919
|
var WRITE_INTENT_RE = />(?!=)|\btee\b|\brm\b|\bmv\b|\bcp\b|\bmkdir\b|\btouch\b|\bchmod\b|\bchown\b|\bln\b/;
|
|
22747
22920
|
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|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/;
|
|
22748
22921
|
var SIDE_EFFECT_TOKEN_RE = />(?!=)|\|\s*tee\b|\btee\b/;
|
|
@@ -22766,10 +22939,19 @@ function buildGitVcsWriteRegex(mainRoot) {
|
|
|
22766
22939
|
return new RegExp(`git\\b[^\\n]*(?:-C\\s+|--work-tree[=\\s])${esc}`);
|
|
22767
22940
|
}
|
|
22768
22941
|
var WRITE_TOOLS = new Set(["write", "edit", "ast_edit"]);
|
|
22942
|
+
var CLASS_B_CALLER_WHITELIST = new Set([
|
|
22943
|
+
"codeforge",
|
|
22944
|
+
"reviewer",
|
|
22945
|
+
"general"
|
|
22946
|
+
]);
|
|
22769
22947
|
function rewritePath(value, mainRoot, worktreeRoot) {
|
|
22770
22948
|
if (!value)
|
|
22771
22949
|
return null;
|
|
22772
22950
|
const resolved = path27.isAbsolute(value) ? value : path27.resolve(mainRoot, value);
|
|
22951
|
+
const wtPrefix2 = worktreeRoot.endsWith("/") ? worktreeRoot : worktreeRoot + "/";
|
|
22952
|
+
if (resolved === worktreeRoot || resolved.startsWith(wtPrefix2)) {
|
|
22953
|
+
return null;
|
|
22954
|
+
}
|
|
22773
22955
|
if (resolved === mainRoot)
|
|
22774
22956
|
return worktreeRoot;
|
|
22775
22957
|
const prefix = mainRoot.endsWith("/") ? mainRoot : mainRoot + "/";
|
|
@@ -22821,7 +23003,17 @@ function isWriteOperation(toolName, argsObj, mainRoot) {
|
|
|
22821
23003
|
return true;
|
|
22822
23004
|
return false;
|
|
22823
23005
|
}
|
|
22824
|
-
|
|
23006
|
+
function collectWritePaths(toolName, argsObj, worktreeRoot) {
|
|
23007
|
+
const out = [];
|
|
23008
|
+
const candidate = toolName === "write" || toolName === "edit" ? argsObj["filePath"] : toolName === "ast_edit" ? argsObj["target"] : undefined;
|
|
23009
|
+
if (typeof candidate !== "string" || candidate.length === 0)
|
|
23010
|
+
return out;
|
|
23011
|
+
const abs = path27.isAbsolute(candidate) ? candidate : path27.resolve(worktreeRoot, candidate);
|
|
23012
|
+
const rel = path27.relative(worktreeRoot, abs).split(path27.sep).join("/");
|
|
23013
|
+
out.push(rel);
|
|
23014
|
+
return out;
|
|
23015
|
+
}
|
|
23016
|
+
var log14 = makePluginLogger(PLUGIN_NAME25);
|
|
22825
23017
|
function resolveMainRoot2(rawDir) {
|
|
22826
23018
|
const worktreeMarker = "/.git/codeforge-worktrees/";
|
|
22827
23019
|
const idx = rawDir.indexOf(worktreeMarker);
|
|
@@ -22832,7 +23024,17 @@ function resolveMainRoot2(rawDir) {
|
|
|
22832
23024
|
}
|
|
22833
23025
|
var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
22834
23026
|
const mainRoot = resolveMainRoot2(ctx.directory ?? process.cwd());
|
|
22835
|
-
|
|
23027
|
+
let policyCfg = {};
|
|
23028
|
+
try {
|
|
23029
|
+
policyCfg = await loadPolicy(mainRoot);
|
|
23030
|
+
} catch (err) {
|
|
23031
|
+
log14.warn("loadPolicy failed (class E skipped)", {
|
|
23032
|
+
mainRoot,
|
|
23033
|
+
error: err instanceof Error ? err.message : String(err)
|
|
23034
|
+
});
|
|
23035
|
+
}
|
|
23036
|
+
const perAgentEnabled = !!policyCfg.per_agent && Object.keys(policyCfg.per_agent).length > 0;
|
|
23037
|
+
logLifecycle(PLUGIN_NAME25, "activate", {
|
|
22836
23038
|
mainRoot,
|
|
22837
23039
|
CODEFORGE_SESSION_ID: process.env["CODEFORGE_SESSION_ID"] ?? "(not set)"
|
|
22838
23040
|
});
|
|
@@ -22842,7 +23044,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22842
23044
|
if (!sessionId)
|
|
22843
23045
|
return;
|
|
22844
23046
|
let denied;
|
|
22845
|
-
await safeAsync(
|
|
23047
|
+
await safeAsync(PLUGIN_NAME25, "tool.execute.before", async () => {
|
|
22846
23048
|
const toolName = input.tool;
|
|
22847
23049
|
const argsObj = output.args ?? {};
|
|
22848
23050
|
let entry = null;
|
|
@@ -22867,7 +23069,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22867
23069
|
if (parentEntry && parentEntry.status === "active") {
|
|
22868
23070
|
entry = parentEntry;
|
|
22869
23071
|
log14.debug?.(`[child-inherit] session ${sessionId} 继承父 ${parentId} 的 worktree`, { parentSessionId: parentId, worktreePath: parentEntry.worktreePath });
|
|
22870
|
-
safeWriteLog(
|
|
23072
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22871
23073
|
hook: "tool.execute.before",
|
|
22872
23074
|
tool: toolName,
|
|
22873
23075
|
sessionID: input.sessionID,
|
|
@@ -22886,7 +23088,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22886
23088
|
try {
|
|
22887
23089
|
entry = await bindSessionWorktree({ sessionId, mainRoot });
|
|
22888
23090
|
log14.info(`[lazy-bind] auto-created worktree for session ${sessionId}`, { branch: entry.branch, path: entry.worktreePath });
|
|
22889
|
-
safeWriteLog(
|
|
23091
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22890
23092
|
hook: "tool.execute.before",
|
|
22891
23093
|
tool: toolName,
|
|
22892
23094
|
sessionID: input.sessionID,
|
|
@@ -22896,7 +23098,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22896
23098
|
});
|
|
22897
23099
|
} catch (err) {
|
|
22898
23100
|
log14.warn(`[lazy-bind] failed (落到主仓写)`, { sessionId, error: err instanceof Error ? err.message : String(err) });
|
|
22899
|
-
safeWriteLog(
|
|
23101
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22900
23102
|
hook: "tool.execute.before",
|
|
22901
23103
|
tool: toolName,
|
|
22902
23104
|
sessionID: input.sessionID,
|
|
@@ -22919,7 +23121,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22919
23121
|
action,
|
|
22920
23122
|
caller
|
|
22921
23123
|
});
|
|
22922
|
-
safeWriteLog(
|
|
23124
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22923
23125
|
hook: "tool.execute.before",
|
|
22924
23126
|
tool: toolName,
|
|
22925
23127
|
sessionID: input.sessionID,
|
|
@@ -22933,6 +23135,56 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22933
23135
|
}
|
|
22934
23136
|
}
|
|
22935
23137
|
}
|
|
23138
|
+
if (perAgentEnabled && isWriteOperation(toolName, argsObj, mainRoot)) {
|
|
23139
|
+
const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
|
|
23140
|
+
if (caller === null) {
|
|
23141
|
+
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)`;
|
|
23142
|
+
log14.warn(reason, {
|
|
23143
|
+
sessionId,
|
|
23144
|
+
tool: toolName,
|
|
23145
|
+
source: "per-agent-acl-fail-closed"
|
|
23146
|
+
});
|
|
23147
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23148
|
+
hook: "tool.execute.before",
|
|
23149
|
+
tool: toolName,
|
|
23150
|
+
sessionID: input.sessionID,
|
|
23151
|
+
action: "deny",
|
|
23152
|
+
source: "per-agent-acl-fail-closed",
|
|
23153
|
+
caller: null
|
|
23154
|
+
});
|
|
23155
|
+
denied = new DeniedError(reason);
|
|
23156
|
+
return;
|
|
23157
|
+
}
|
|
23158
|
+
const agentAcl = getAgentAcl(policyCfg, caller);
|
|
23159
|
+
if (agentAcl) {
|
|
23160
|
+
const writePaths = collectWritePaths(toolName, argsObj, worktreePath);
|
|
23161
|
+
for (const relPath of writePaths) {
|
|
23162
|
+
const dec = checkFileAccess(agentAcl, relPath, "write");
|
|
23163
|
+
if (dec.action === "deny") {
|
|
23164
|
+
const reason = `[session-worktree-guard] DENIED: agent='${caller}' 写路径不在 ACL 白名单 — ` + `${relPath} (${dec.reason})`;
|
|
23165
|
+
log14.warn(reason, {
|
|
23166
|
+
sessionId,
|
|
23167
|
+
tool: toolName,
|
|
23168
|
+
caller,
|
|
23169
|
+
path: relPath,
|
|
23170
|
+
acl_decision: dec
|
|
23171
|
+
});
|
|
23172
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23173
|
+
hook: "tool.execute.before",
|
|
23174
|
+
tool: toolName,
|
|
23175
|
+
sessionID: input.sessionID,
|
|
23176
|
+
action: "deny",
|
|
23177
|
+
source: "per-agent-acl",
|
|
23178
|
+
caller,
|
|
23179
|
+
path: relPath,
|
|
23180
|
+
acl_decision: dec
|
|
23181
|
+
});
|
|
23182
|
+
denied = new DeniedError(reason);
|
|
23183
|
+
return;
|
|
23184
|
+
}
|
|
23185
|
+
}
|
|
23186
|
+
}
|
|
23187
|
+
}
|
|
22936
23188
|
if (toolName !== "plan_read" && entry.requiredPlanId && entry.planReadOk !== true) {
|
|
22937
23189
|
let isWriteOp = WRITE_TOOLS.has(toolName);
|
|
22938
23190
|
if (!isWriteOp && toolName === "bash") {
|
|
@@ -22961,7 +23213,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22961
23213
|
requiredPlanId: entry.requiredPlanId,
|
|
22962
23214
|
inheritedFromParent: inherited ? entry.sessionId : undefined
|
|
22963
23215
|
});
|
|
22964
|
-
safeWriteLog(
|
|
23216
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22965
23217
|
hook: "tool.execute.before",
|
|
22966
23218
|
tool: toolName,
|
|
22967
23219
|
sessionID: input.sessionID,
|
|
@@ -22976,19 +23228,35 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22976
23228
|
if (toolName === "bash") {
|
|
22977
23229
|
const command = argsObj["command"];
|
|
22978
23230
|
if (typeof command === "string" && commandContainsMainRoot(command, mainRoot) && detectBashWriteIntent(command, mainRoot)) {
|
|
22979
|
-
const
|
|
22980
|
-
|
|
22981
|
-
|
|
22982
|
-
|
|
22983
|
-
|
|
22984
|
-
|
|
22985
|
-
|
|
22986
|
-
|
|
22987
|
-
|
|
22988
|
-
|
|
22989
|
-
|
|
22990
|
-
|
|
22991
|
-
|
|
23231
|
+
const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
|
|
23232
|
+
if (caller !== null && CLASS_B_CALLER_WHITELIST.has(caller)) {
|
|
23233
|
+
log14.debug?.(`[class-b-whitelist] allow caller=${caller}`, { sessionId, tool: toolName, command: command.slice(0, 200) });
|
|
23234
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23235
|
+
hook: "tool.execute.before",
|
|
23236
|
+
tool: toolName,
|
|
23237
|
+
sessionID: input.sessionID,
|
|
23238
|
+
action: "allow-whitelist",
|
|
23239
|
+
source: "class-b-caller-whitelist",
|
|
23240
|
+
caller,
|
|
23241
|
+
command: command.slice(0, 200)
|
|
23242
|
+
});
|
|
23243
|
+
} else {
|
|
23244
|
+
const callerTag = caller === null ? "unresolved" : caller;
|
|
23245
|
+
const snippet = command.length > 60 ? command.slice(0, 60) + "…" : command;
|
|
23246
|
+
const reason = `[session-worktree-guard] DENIED: bash.command 含主仓绝对路径写操作 (${snippet}) [caller=${callerTag}],请在当前 session worktree (${worktreePath}) 内操作`;
|
|
23247
|
+
log14.warn(reason, { sessionId, caller: callerTag, command: command.slice(0, 200) });
|
|
23248
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23249
|
+
hook: "tool.execute.before",
|
|
23250
|
+
tool: toolName,
|
|
23251
|
+
sessionID: input.sessionID,
|
|
23252
|
+
action: "deny",
|
|
23253
|
+
source: "bash-write-intent",
|
|
23254
|
+
caller: callerTag,
|
|
23255
|
+
command: command.slice(0, 200)
|
|
23256
|
+
});
|
|
23257
|
+
denied = new DeniedError(reason);
|
|
23258
|
+
return;
|
|
23259
|
+
}
|
|
22992
23260
|
}
|
|
22993
23261
|
}
|
|
22994
23262
|
if (toolName === "write" || toolName === "edit") {
|
|
@@ -22997,7 +23265,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22997
23265
|
const newPath = rewritePath(filePath, mainRoot, worktreePath);
|
|
22998
23266
|
if (newPath !== null) {
|
|
22999
23267
|
log14.info(`rewrote ${toolName}.filePath: ${filePath} → ${newPath}`);
|
|
23000
|
-
safeWriteLog(
|
|
23268
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23001
23269
|
hook: "tool.execute.before",
|
|
23002
23270
|
tool: toolName,
|
|
23003
23271
|
field: "filePath",
|
|
@@ -23015,7 +23283,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23015
23283
|
const newTarget = rewritePath(target, mainRoot, worktreePath);
|
|
23016
23284
|
if (newTarget !== null) {
|
|
23017
23285
|
log14.info(`rewrote ast_edit.target: ${target} → ${newTarget}`);
|
|
23018
|
-
safeWriteLog(
|
|
23286
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23019
23287
|
hook: "tool.execute.before",
|
|
23020
23288
|
tool: toolName,
|
|
23021
23289
|
field: "target",
|
|
@@ -23031,7 +23299,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23031
23299
|
const newRoot = rewritePath(root, mainRoot, worktreePath);
|
|
23032
23300
|
if (newRoot !== null) {
|
|
23033
23301
|
log14.info(`rewrote ast_edit.root: ${root} → ${newRoot}`);
|
|
23034
|
-
safeWriteLog(
|
|
23302
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23035
23303
|
hook: "tool.execute.before",
|
|
23036
23304
|
tool: toolName,
|
|
23037
23305
|
field: "root",
|
|
@@ -23049,7 +23317,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23049
23317
|
const newWorkdir = rewritePath(workdir, mainRoot, worktreePath);
|
|
23050
23318
|
if (newWorkdir !== null) {
|
|
23051
23319
|
log14.info(`rewrote bash.workdir: ${workdir} → ${newWorkdir}`);
|
|
23052
|
-
safeWriteLog(
|
|
23320
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23053
23321
|
hook: "tool.execute.before",
|
|
23054
23322
|
tool: toolName,
|
|
23055
23323
|
field: "workdir",
|
|
@@ -23067,19 +23335,22 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23067
23335
|
}
|
|
23068
23336
|
};
|
|
23069
23337
|
};
|
|
23070
|
-
var
|
|
23338
|
+
var handler25 = sessionWorktreeGuardPlugin;
|
|
23071
23339
|
|
|
23072
23340
|
// plugins/worktree-lifecycle.ts
|
|
23073
|
-
var
|
|
23074
|
-
logLifecycle(
|
|
23341
|
+
var PLUGIN_NAME26 = "worktree-lifecycle";
|
|
23342
|
+
logLifecycle(PLUGIN_NAME26, "import", {});
|
|
23075
23343
|
var IDLE_TOAST_THROTTLE_MS = 60 * 60000;
|
|
23076
23344
|
var IDLE_TOAST_DURATION_MS = 8000;
|
|
23077
23345
|
var IDLE_TOAST_REMINDER_INTERVAL_MS = 30 * 60000;
|
|
23346
|
+
var PRUNE_INTERVAL_MS = 30 * 60000;
|
|
23078
23347
|
var lastIdleToastAt = new Map;
|
|
23079
|
-
var
|
|
23348
|
+
var pruneRunning = false;
|
|
23349
|
+
var _pruneTimer;
|
|
23350
|
+
var log15 = makePluginLogger(PLUGIN_NAME26);
|
|
23080
23351
|
var worktreeLifecyclePlugin = async (ctx) => {
|
|
23081
23352
|
const mainRoot = ctx.directory;
|
|
23082
|
-
logLifecycle(
|
|
23353
|
+
logLifecycle(PLUGIN_NAME26, "activate", {
|
|
23083
23354
|
mainRoot: mainRoot ?? "(not set)",
|
|
23084
23355
|
idle_threshold_ms: IDLE_TOAST_THROTTLE_MS
|
|
23085
23356
|
});
|
|
@@ -23088,11 +23359,11 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23088
23359
|
}
|
|
23089
23360
|
const client = ctx.client;
|
|
23090
23361
|
setImmediate(() => {
|
|
23091
|
-
safeAsync(
|
|
23362
|
+
safeAsync(PLUGIN_NAME26, "activate.pruneOrphan", async () => {
|
|
23092
23363
|
const result = await pruneOrphanWorktrees(mainRoot);
|
|
23093
23364
|
if (result.cleaned.length > 0 || result.failed.length > 0) {
|
|
23094
23365
|
log15.info(`[pruneOrphan] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
|
|
23095
|
-
safeWriteLog(
|
|
23366
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23096
23367
|
hook: "activate.pruneOrphan",
|
|
23097
23368
|
cleaned: result.cleaned,
|
|
23098
23369
|
failed: result.failed,
|
|
@@ -23101,16 +23372,46 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23101
23372
|
}
|
|
23102
23373
|
});
|
|
23103
23374
|
});
|
|
23375
|
+
if (_pruneTimer)
|
|
23376
|
+
clearInterval(_pruneTimer);
|
|
23377
|
+
_pruneTimer = setInterval(() => {
|
|
23378
|
+
if (pruneRunning) {
|
|
23379
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23380
|
+
hook: "interval.pruneOrphan",
|
|
23381
|
+
action: "skip",
|
|
23382
|
+
reason: "previous prune still running"
|
|
23383
|
+
});
|
|
23384
|
+
return;
|
|
23385
|
+
}
|
|
23386
|
+
pruneRunning = true;
|
|
23387
|
+
safeAsync(PLUGIN_NAME26, "interval.pruneOrphan", async () => {
|
|
23388
|
+
try {
|
|
23389
|
+
const result = await pruneOrphanWorktrees(mainRoot);
|
|
23390
|
+
if (result.cleaned.length > 0 || result.failed.length > 0) {
|
|
23391
|
+
log15.info(`[pruneOrphan interval] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
|
|
23392
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23393
|
+
hook: "interval.pruneOrphan",
|
|
23394
|
+
cleaned: result.cleaned,
|
|
23395
|
+
failed: result.failed,
|
|
23396
|
+
skipped: result.skipped
|
|
23397
|
+
});
|
|
23398
|
+
}
|
|
23399
|
+
} finally {
|
|
23400
|
+
pruneRunning = false;
|
|
23401
|
+
}
|
|
23402
|
+
});
|
|
23403
|
+
}, PRUNE_INTERVAL_MS);
|
|
23404
|
+
_pruneTimer.unref();
|
|
23104
23405
|
return {
|
|
23105
23406
|
event: async ({ event }) => {
|
|
23106
|
-
await safeAsync(
|
|
23407
|
+
await safeAsync(PLUGIN_NAME26, "event", async () => {
|
|
23107
23408
|
const ended = extractEndedSessionID(event);
|
|
23108
23409
|
if (!ended)
|
|
23109
23410
|
return;
|
|
23110
23411
|
if (ended.type === "session.deleted") {
|
|
23111
23412
|
const entry = await getSessionWorktree(ended.sessionID, mainRoot);
|
|
23112
23413
|
if (!entry || entry.status !== "active") {
|
|
23113
|
-
safeWriteLog(
|
|
23414
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23114
23415
|
hook: "event",
|
|
23115
23416
|
type: ended.type,
|
|
23116
23417
|
sessionID: ended.sessionID,
|
|
@@ -23125,7 +23426,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23125
23426
|
const fastDirty = await isWorktreeDirty(entry.worktreePath);
|
|
23126
23427
|
if (!fastDirty) {
|
|
23127
23428
|
await discardSession({ sessionId: ended.sessionID, mainRoot });
|
|
23128
|
-
safeWriteLog(
|
|
23429
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23129
23430
|
hook: "event",
|
|
23130
23431
|
type: ended.type,
|
|
23131
23432
|
sessionID: ended.sessionID,
|
|
@@ -23160,7 +23461,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23160
23461
|
}
|
|
23161
23462
|
try {
|
|
23162
23463
|
await discardSession({ sessionId: ended.sessionID, mainRoot });
|
|
23163
|
-
safeWriteLog(
|
|
23464
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23164
23465
|
hook: "event",
|
|
23165
23466
|
type: ended.type,
|
|
23166
23467
|
sessionID: ended.sessionID,
|
|
@@ -23194,7 +23495,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23194
23495
|
const idleMin = Math.round(idleMs / 60000);
|
|
23195
23496
|
const msg = `\uD83D\uDCA4 Session ${ended.sessionID.slice(0, 8)} worktree 已空闲 ${idleMin}min,` + `用 /merge 收尾或 /discard-session 放弃`;
|
|
23196
23497
|
const sent = await showToast2(client, { message: msg, variant: "default", duration: IDLE_TOAST_DURATION_MS, title: "CodeForge" }, log15);
|
|
23197
|
-
safeWriteLog(
|
|
23498
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23198
23499
|
hook: "event",
|
|
23199
23500
|
type: ended.type,
|
|
23200
23501
|
sessionID: ended.sessionID,
|
|
@@ -23207,7 +23508,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23207
23508
|
}
|
|
23208
23509
|
};
|
|
23209
23510
|
};
|
|
23210
|
-
var
|
|
23511
|
+
var handler26 = worktreeLifecyclePlugin;
|
|
23211
23512
|
|
|
23212
23513
|
// src/index.ts
|
|
23213
23514
|
var PLUGIN_ID = "codeforge";
|
|
@@ -23219,26 +23520,27 @@ var HANDLERS = [
|
|
|
23219
23520
|
{ name: "auto-commit", init: handler3 },
|
|
23220
23521
|
{ name: "auto-learning", init: handler4 },
|
|
23221
23522
|
{ name: "channels", init: handler5 },
|
|
23222
|
-
{ name: "
|
|
23223
|
-
{ name: "
|
|
23224
|
-
{ name: "
|
|
23225
|
-
{ name: "kh-
|
|
23226
|
-
{ name: "
|
|
23227
|
-
{ name: "
|
|
23228
|
-
{ name: "
|
|
23229
|
-
{ name: "
|
|
23230
|
-
{ name: "
|
|
23231
|
-
{ name: "
|
|
23232
|
-
{ name: "
|
|
23233
|
-
{ name: "
|
|
23234
|
-
{ name: "
|
|
23235
|
-
{ name: "
|
|
23236
|
-
{ name: "
|
|
23237
|
-
{ name: "tool-
|
|
23238
|
-
{ name: "
|
|
23239
|
-
{ name: "
|
|
23240
|
-
{ name: "
|
|
23241
|
-
{ name: "worktree-
|
|
23523
|
+
{ name: "chat-agent-cache", init: handler6 },
|
|
23524
|
+
{ name: "codeforge-tools", init: handler8 },
|
|
23525
|
+
{ name: "discover-spec-suggest", init: handler9 },
|
|
23526
|
+
{ name: "kh-auto-context", init: handler10 },
|
|
23527
|
+
{ name: "kh-reminder", init: handler11 },
|
|
23528
|
+
{ name: "memories-context", init: handler12 },
|
|
23529
|
+
{ name: "model-fallback", init: handler13 },
|
|
23530
|
+
{ name: "parallel-tool-nudge", init: handler16 },
|
|
23531
|
+
{ name: "pwsh-utf8", init: handler17 },
|
|
23532
|
+
{ name: "session-recovery", init: handler18 },
|
|
23533
|
+
{ name: "subtask-heartbeat", init: handler14 },
|
|
23534
|
+
{ name: "subtasks", init: handler19 },
|
|
23535
|
+
{ name: "parallel-status", init: handler15 },
|
|
23536
|
+
{ name: "terminal-monitor", init: handler20 },
|
|
23537
|
+
{ name: "token-manager", init: handler21 },
|
|
23538
|
+
{ name: "tool-heartbeat", init: handler7 },
|
|
23539
|
+
{ name: "tool-policy", init: handler22 },
|
|
23540
|
+
{ name: "update-checker", init: handler23 },
|
|
23541
|
+
{ name: "workflow-engine", init: handler24 },
|
|
23542
|
+
{ name: "session-worktree-guard", init: handler25 },
|
|
23543
|
+
{ name: "worktree-lifecycle", init: handler26 }
|
|
23242
23544
|
];
|
|
23243
23545
|
function makeSerialHook(hookName, fns) {
|
|
23244
23546
|
return async (input, output) => {
|