@andyqiu/codeforge 0.5.8 → 0.5.9
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 +552 -299
- 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
|
|
|
@@ -14856,6 +14998,15 @@ ${r.text.slice(0, 800)}`
|
|
|
14856
14998
|
err: describe5(err)
|
|
14857
14999
|
});
|
|
14858
15000
|
}
|
|
15001
|
+
const mainRoot = this.opts.mainRoot ?? this.opts.directory;
|
|
15002
|
+
if (mainRoot) {
|
|
15003
|
+
const cid = childId;
|
|
15004
|
+
discardSession({ sessionId: cid, mainRoot }).catch((err) => {
|
|
15005
|
+
this.opts.log?.("warn", `[spawner] auto-discard 失败 ${cid}`, {
|
|
15006
|
+
err: describe5(err)
|
|
15007
|
+
});
|
|
15008
|
+
});
|
|
15009
|
+
}
|
|
14859
15010
|
}
|
|
14860
15011
|
}
|
|
14861
15012
|
}
|
|
@@ -14955,7 +15106,6 @@ import { promises as fs14 } from "node:fs";
|
|
|
14955
15106
|
import * as path17 from "node:path";
|
|
14956
15107
|
var DEFAULT_RUNTIME = {
|
|
14957
15108
|
autonomy: {
|
|
14958
|
-
default_mode: "semi",
|
|
14959
15109
|
downgrade_on_risky: true
|
|
14960
15110
|
},
|
|
14961
15111
|
runtime: {
|
|
@@ -15039,13 +15189,6 @@ function parseRuntime(raw, abs) {
|
|
|
15039
15189
|
const cfg = cloneDefaults();
|
|
15040
15190
|
if (root.autonomy && typeof root.autonomy === "object" && !Array.isArray(root.autonomy)) {
|
|
15041
15191
|
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
15192
|
if (a.downgrade_on_risky !== undefined) {
|
|
15050
15193
|
if (typeof a.downgrade_on_risky === "boolean") {
|
|
15051
15194
|
cfg.autonomy.downgrade_on_risky = a.downgrade_on_risky;
|
|
@@ -15165,8 +15308,8 @@ function parseRuntime(raw, abs) {
|
|
|
15165
15308
|
}
|
|
15166
15309
|
|
|
15167
15310
|
// plugins/tool-heartbeat.ts
|
|
15168
|
-
var
|
|
15169
|
-
logLifecycle(
|
|
15311
|
+
var PLUGIN_NAME7 = "tool-heartbeat";
|
|
15312
|
+
logLifecycle(PLUGIN_NAME7, "import", {});
|
|
15170
15313
|
var HEARTBEAT_INTERVAL_MS = 15000;
|
|
15171
15314
|
var ALERT_30S_MS = 30000;
|
|
15172
15315
|
var ALERT_60S_MS = 60000;
|
|
@@ -15248,15 +15391,15 @@ async function showToast(client, payload, log5) {
|
|
|
15248
15391
|
return false;
|
|
15249
15392
|
}
|
|
15250
15393
|
}
|
|
15251
|
-
var log5 = makePluginLogger(
|
|
15394
|
+
var log5 = makePluginLogger(PLUGIN_NAME7);
|
|
15252
15395
|
var toolHeartbeatServer = async (ctx) => {
|
|
15253
|
-
logLifecycle(
|
|
15396
|
+
logLifecycle(PLUGIN_NAME7, "activate", {
|
|
15254
15397
|
directory: ctx.directory,
|
|
15255
15398
|
intervalMs: HEARTBEAT_INTERVAL_MS
|
|
15256
15399
|
});
|
|
15257
15400
|
const client = ctx.client;
|
|
15258
15401
|
const interval = setInterval(() => {
|
|
15259
|
-
safeAsync(
|
|
15402
|
+
safeAsync(PLUGIN_NAME7, "interval", async () => {
|
|
15260
15403
|
if (inflight.size === 0)
|
|
15261
15404
|
return;
|
|
15262
15405
|
const records = [...inflight.values()];
|
|
@@ -15266,7 +15409,7 @@ var toolHeartbeatServer = async (ctx) => {
|
|
|
15266
15409
|
continue;
|
|
15267
15410
|
const sent = await showToast(client, { message: alert.message, variant: alert.variant }, log5);
|
|
15268
15411
|
r.alertsSent.add(alert.threshold);
|
|
15269
|
-
safeWriteLog(
|
|
15412
|
+
safeWriteLog(PLUGIN_NAME7, {
|
|
15270
15413
|
hook: "interval",
|
|
15271
15414
|
tool: r.toolName,
|
|
15272
15415
|
callID: r.callID,
|
|
@@ -15284,12 +15427,12 @@ var toolHeartbeatServer = async (ctx) => {
|
|
|
15284
15427
|
event: async () => {}
|
|
15285
15428
|
};
|
|
15286
15429
|
};
|
|
15287
|
-
var
|
|
15430
|
+
var handler7 = toolHeartbeatServer;
|
|
15288
15431
|
|
|
15289
15432
|
// plugins/codeforge-tools.ts
|
|
15290
15433
|
var z30 = tool.schema;
|
|
15291
|
-
var
|
|
15292
|
-
logLifecycle(
|
|
15434
|
+
var PLUGIN_NAME8 = "codeforge-tools";
|
|
15435
|
+
logLifecycle(PLUGIN_NAME8, "import");
|
|
15293
15436
|
function wrap(output, metadata) {
|
|
15294
15437
|
const text = typeof output === "string" ? output : JSON.stringify(output, null, 2);
|
|
15295
15438
|
return metadata && Object.keys(metadata).length > 0 ? { output: text, metadata } : { output: text };
|
|
@@ -15299,7 +15442,7 @@ async function runSafe(toolName, fn) {
|
|
|
15299
15442
|
return await fn();
|
|
15300
15443
|
} catch (err) {
|
|
15301
15444
|
const msg = err instanceof Error ? err.message : String(err);
|
|
15302
|
-
safeWriteLog(
|
|
15445
|
+
safeWriteLog(PLUGIN_NAME8, {
|
|
15303
15446
|
level: "error",
|
|
15304
15447
|
tool: toolName,
|
|
15305
15448
|
error: msg
|
|
@@ -15696,7 +15839,7 @@ var codeforgeToolsServer = async (ctx) => {
|
|
|
15696
15839
|
const rt = loadRuntimeSync({ root: ctx.directory });
|
|
15697
15840
|
const browserEnabled = rt.runtime.tools.browser.enabled;
|
|
15698
15841
|
const activeTools = browserEnabled ? [...CORE_TOOL_NAMES, ...BROWSER_TOOL_NAMES] : [...CORE_TOOL_NAMES];
|
|
15699
|
-
logLifecycle(
|
|
15842
|
+
logLifecycle(PLUGIN_NAME8, "activate", {
|
|
15700
15843
|
directory: ctx.directory,
|
|
15701
15844
|
tools: activeTools,
|
|
15702
15845
|
count: activeTools.length,
|
|
@@ -15711,7 +15854,8 @@ var codeforgeToolsServer = async (ctx) => {
|
|
|
15711
15854
|
const spawner = new ProductionSpawner({
|
|
15712
15855
|
client: ctx.client,
|
|
15713
15856
|
directory: ctx.directory ?? process.cwd(),
|
|
15714
|
-
|
|
15857
|
+
mainRoot: ctx.directory ?? process.cwd(),
|
|
15858
|
+
log: (level, msg, data) => safeWriteLog(PLUGIN_NAME8, { level, msg, data })
|
|
15715
15859
|
});
|
|
15716
15860
|
__setContext({
|
|
15717
15861
|
mainRoot: ctx.directory ?? process.cwd(),
|
|
@@ -16003,7 +16147,7 @@ var codeforgeToolsServer = async (ctx) => {
|
|
|
16003
16147
|
}
|
|
16004
16148
|
};
|
|
16005
16149
|
};
|
|
16006
|
-
var
|
|
16150
|
+
var handler8 = codeforgeToolsServer;
|
|
16007
16151
|
|
|
16008
16152
|
// plugins/discover-spec-suggest.ts
|
|
16009
16153
|
import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "node:fs";
|
|
@@ -16185,26 +16329,26 @@ function isValidSlug(slug) {
|
|
|
16185
16329
|
}
|
|
16186
16330
|
|
|
16187
16331
|
// plugins/discover-spec-suggest.ts
|
|
16188
|
-
var
|
|
16189
|
-
logLifecycle(
|
|
16332
|
+
var PLUGIN_NAME9 = "discover-spec-suggest";
|
|
16333
|
+
logLifecycle(PLUGIN_NAME9, "import", {});
|
|
16190
16334
|
var TARGET_AGENT = "codeforge";
|
|
16191
|
-
var
|
|
16192
|
-
var
|
|
16335
|
+
var SESSION_CAP2 = 200;
|
|
16336
|
+
var SESSION_TTL_MS2 = 24 * 60 * 60 * 1000;
|
|
16193
16337
|
var MATCH_THRESHOLD = 0.15;
|
|
16194
16338
|
var MAX_CANDIDATES = 3;
|
|
16195
16339
|
var NUDGE_MAX_LEN = 1500;
|
|
16196
16340
|
var SPECS_REL_DIR = join14(".codeforge", "specs");
|
|
16197
16341
|
var sessionMap = new Map;
|
|
16198
|
-
function
|
|
16199
|
-
while (sessionMap.size >
|
|
16342
|
+
function pruneIfOversize2() {
|
|
16343
|
+
while (sessionMap.size > SESSION_CAP2) {
|
|
16200
16344
|
const oldestKey = sessionMap.keys().next().value;
|
|
16201
16345
|
if (oldestKey === undefined)
|
|
16202
16346
|
break;
|
|
16203
16347
|
sessionMap.delete(oldestKey);
|
|
16204
16348
|
}
|
|
16205
16349
|
}
|
|
16206
|
-
function
|
|
16207
|
-
return now - entry.ts >
|
|
16350
|
+
function isExpired2(entry, now = Date.now()) {
|
|
16351
|
+
return now - entry.ts > SESSION_TTL_MS2;
|
|
16208
16352
|
}
|
|
16209
16353
|
var specIndex = [];
|
|
16210
16354
|
function defaultReader(p) {
|
|
@@ -16299,7 +16443,7 @@ function loadSpecs(rootDir, opts = {}) {
|
|
|
16299
16443
|
const dirReader = opts.dirReader ?? defaultDirReader;
|
|
16300
16444
|
const dirExists = opts.dirExists ?? defaultDirExists;
|
|
16301
16445
|
const statReader = opts.statReader ?? defaultStatReader;
|
|
16302
|
-
const log6 = makePluginLogger(
|
|
16446
|
+
const log6 = makePluginLogger(PLUGIN_NAME9);
|
|
16303
16447
|
const specsRoot = join14(rootDir, SPECS_REL_DIR);
|
|
16304
16448
|
const records = [];
|
|
16305
16449
|
if (!dirExists(specsRoot)) {
|
|
@@ -16427,7 +16571,7 @@ function renderCandidatesNudge(matched) {
|
|
|
16427
16571
|
return body.slice(0, NUDGE_MAX_LEN - 4) + `
|
|
16428
16572
|
…`;
|
|
16429
16573
|
}
|
|
16430
|
-
var log6 = makePluginLogger(
|
|
16574
|
+
var log6 = makePluginLogger(PLUGIN_NAME9);
|
|
16431
16575
|
var discoverSpecSuggestServer = async (ctx) => {
|
|
16432
16576
|
try {
|
|
16433
16577
|
const loaded = loadSpecs(ctx.directory ?? process.cwd());
|
|
@@ -16438,19 +16582,19 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16438
16582
|
error: err instanceof Error ? err.message : String(err)
|
|
16439
16583
|
});
|
|
16440
16584
|
}
|
|
16441
|
-
logLifecycle(
|
|
16585
|
+
logLifecycle(PLUGIN_NAME9, "activate", {
|
|
16442
16586
|
directory: ctx.directory,
|
|
16443
16587
|
specs_loaded: specIndex.length,
|
|
16444
16588
|
target_agent: TARGET_AGENT,
|
|
16445
16589
|
match_threshold: MATCH_THRESHOLD,
|
|
16446
16590
|
max_candidates: MAX_CANDIDATES,
|
|
16447
|
-
session_cap:
|
|
16448
|
-
session_ttl_ms:
|
|
16591
|
+
session_cap: SESSION_CAP2,
|
|
16592
|
+
session_ttl_ms: SESSION_TTL_MS2,
|
|
16449
16593
|
no_op: specIndex.length === 0
|
|
16450
16594
|
});
|
|
16451
16595
|
return {
|
|
16452
16596
|
"chat.message": async (input, output) => {
|
|
16453
|
-
await safeAsync(
|
|
16597
|
+
await safeAsync(PLUGIN_NAME9, "chat.message", async () => {
|
|
16454
16598
|
if (specIndex.length === 0)
|
|
16455
16599
|
return;
|
|
16456
16600
|
const text = extractUserText(output);
|
|
@@ -16461,11 +16605,11 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16461
16605
|
if (!sid)
|
|
16462
16606
|
return;
|
|
16463
16607
|
sessionMap.set(sid, { text, agent, ts: Date.now() });
|
|
16464
|
-
|
|
16608
|
+
pruneIfOversize2();
|
|
16465
16609
|
});
|
|
16466
16610
|
},
|
|
16467
16611
|
"experimental.chat.system.transform": async (input, output) => {
|
|
16468
|
-
await safeAsync(
|
|
16612
|
+
await safeAsync(PLUGIN_NAME9, "experimental.chat.system.transform", async () => {
|
|
16469
16613
|
if (specIndex.length === 0)
|
|
16470
16614
|
return;
|
|
16471
16615
|
if (!output || !Array.isArray(output.system))
|
|
@@ -16476,7 +16620,7 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16476
16620
|
const entry = sessionMap.get(sid);
|
|
16477
16621
|
if (!entry)
|
|
16478
16622
|
return;
|
|
16479
|
-
if (
|
|
16623
|
+
if (isExpired2(entry)) {
|
|
16480
16624
|
sessionMap.delete(sid);
|
|
16481
16625
|
return;
|
|
16482
16626
|
}
|
|
@@ -16491,7 +16635,7 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16491
16635
|
if (!nudge)
|
|
16492
16636
|
return;
|
|
16493
16637
|
output.system.push(nudge);
|
|
16494
|
-
safeWriteLog(
|
|
16638
|
+
safeWriteLog(PLUGIN_NAME9, {
|
|
16495
16639
|
hook: "experimental.chat.system.transform",
|
|
16496
16640
|
sessionID: sid,
|
|
16497
16641
|
agent: entry.agent,
|
|
@@ -16503,7 +16647,7 @@ var discoverSpecSuggestServer = async (ctx) => {
|
|
|
16503
16647
|
}
|
|
16504
16648
|
};
|
|
16505
16649
|
};
|
|
16506
|
-
var
|
|
16650
|
+
var handler9 = discoverSpecSuggestServer;
|
|
16507
16651
|
|
|
16508
16652
|
// plugins/kh-auto-context.ts
|
|
16509
16653
|
init_kh_client();
|
|
@@ -16643,7 +16787,7 @@ class SessionScopedCache {
|
|
|
16643
16787
|
var sharedKhCache = new SessionScopedCache;
|
|
16644
16788
|
|
|
16645
16789
|
// plugins/kh-auto-context.ts
|
|
16646
|
-
var
|
|
16790
|
+
var PLUGIN_NAME10 = "kh-auto-context";
|
|
16647
16791
|
var INJECTION_MODE = "observe-only";
|
|
16648
16792
|
function resolveInjectionMode(client) {
|
|
16649
16793
|
return client.hasTransport() ? "system-injected" : "observe-only";
|
|
@@ -16742,7 +16886,7 @@ var CODEFORGE_CONSTRAINTS = [
|
|
|
16742
16886
|
];
|
|
16743
16887
|
async function seedConstraints(client, log7) {
|
|
16744
16888
|
if (!client.hasTransport()) {
|
|
16745
|
-
log7?.debug?.(`[${
|
|
16889
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] seedConstraints: transport 不可用,跳过`, {});
|
|
16746
16890
|
return { ok: false, reason: "transport_unavailable" };
|
|
16747
16891
|
}
|
|
16748
16892
|
try {
|
|
@@ -16756,7 +16900,7 @@ async function seedConstraints(client, log7) {
|
|
|
16756
16900
|
});
|
|
16757
16901
|
if (result && typeof result === "object" && "ok" in result && result.ok === false) {
|
|
16758
16902
|
const r = result;
|
|
16759
|
-
log7?.warn(`[${
|
|
16903
|
+
log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 降级`, {
|
|
16760
16904
|
reason: r.reason,
|
|
16761
16905
|
message: r.message
|
|
16762
16906
|
});
|
|
@@ -16766,11 +16910,11 @@ async function seedConstraints(client, log7) {
|
|
|
16766
16910
|
message: r.message
|
|
16767
16911
|
};
|
|
16768
16912
|
}
|
|
16769
|
-
log7?.info(`[${
|
|
16913
|
+
log7?.info(`[${PLUGIN_NAME10}] seedConstraints: 已写入 ${CODEFORGE_CONSTRAINTS.length} 条 constraints`, { count: CODEFORGE_CONSTRAINTS.length });
|
|
16770
16914
|
return { ok: true, itemsWritten: CODEFORGE_CONSTRAINTS.length };
|
|
16771
16915
|
} catch (err) {
|
|
16772
16916
|
const message = err instanceof Error ? err.message : String(err);
|
|
16773
|
-
log7?.warn(`[${
|
|
16917
|
+
log7?.warn(`[${PLUGIN_NAME10}] seedConstraints 失败(已静默)`, { error: message });
|
|
16774
16918
|
return { ok: false, reason: "exception", message };
|
|
16775
16919
|
}
|
|
16776
16920
|
}
|
|
@@ -16785,7 +16929,7 @@ function createSystemInjectedHook(client, sessionId, log7) {
|
|
|
16785
16929
|
});
|
|
16786
16930
|
if (result && typeof result === "object" && "ok" in result && result.ok === false) {
|
|
16787
16931
|
const r = result;
|
|
16788
|
-
log7?.warn(`[${
|
|
16932
|
+
log7?.warn(`[${PLUGIN_NAME10}] system-injected 降级到 observe-only:${r.reason ?? "unknown"}`, {
|
|
16789
16933
|
sessionId,
|
|
16790
16934
|
section,
|
|
16791
16935
|
preview: markdown.slice(0, 200),
|
|
@@ -16794,12 +16938,12 @@ function createSystemInjectedHook(client, sessionId, log7) {
|
|
|
16794
16938
|
});
|
|
16795
16939
|
return;
|
|
16796
16940
|
}
|
|
16797
|
-
log7?.info(`[${
|
|
16941
|
+
log7?.info(`[${PLUGIN_NAME10}] system-injected: 写入 KH working_memory 成功 (${markdown.length} chars)`, {
|
|
16798
16942
|
sessionId,
|
|
16799
16943
|
section
|
|
16800
16944
|
});
|
|
16801
16945
|
} catch (err) {
|
|
16802
|
-
log7?.warn(`[${
|
|
16946
|
+
log7?.warn(`[${PLUGIN_NAME10}] system-injected 抛异常,降级到 observe-only`, {
|
|
16803
16947
|
sessionId,
|
|
16804
16948
|
section,
|
|
16805
16949
|
preview: markdown.slice(0, 200),
|
|
@@ -16842,7 +16986,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16842
16986
|
});
|
|
16843
16987
|
result = await racer(searchPromise, cfg.timeoutMs);
|
|
16844
16988
|
} catch (err) {
|
|
16845
|
-
log7?.warn(`[${
|
|
16989
|
+
log7?.warn(`[${PLUGIN_NAME10}] client.search threw (sync or async), return null`, {
|
|
16846
16990
|
query,
|
|
16847
16991
|
elapsedMs: Date.now() - startedAt,
|
|
16848
16992
|
sessionId: ctx.sessionId,
|
|
@@ -16852,7 +16996,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16852
16996
|
return null;
|
|
16853
16997
|
}
|
|
16854
16998
|
if (result === "__timeout__") {
|
|
16855
|
-
log7?.warn(`[${
|
|
16999
|
+
log7?.warn(`[${PLUGIN_NAME10}] timeout`, {
|
|
16856
17000
|
query,
|
|
16857
17001
|
ms: cfg.timeoutMs,
|
|
16858
17002
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -16862,7 +17006,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16862
17006
|
return null;
|
|
16863
17007
|
}
|
|
16864
17008
|
if (!result.ok) {
|
|
16865
|
-
log7?.warn(`[${
|
|
17009
|
+
log7?.warn(`[${PLUGIN_NAME10}] kh degraded`, {
|
|
16866
17010
|
reason: result.reason,
|
|
16867
17011
|
query,
|
|
16868
17012
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -16874,7 +17018,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16874
17018
|
const filtered = result.insights.filter((i) => (i.confidence ?? 0) >= cfg.minConfidence);
|
|
16875
17019
|
if (filtered.length === 0) {
|
|
16876
17020
|
opts.cache.record(query, []);
|
|
16877
|
-
log7?.debug?.(`[${
|
|
17021
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] no candidate above threshold`, {
|
|
16878
17022
|
query,
|
|
16879
17023
|
rawCount: result.insights.length,
|
|
16880
17024
|
elapsedMs: Date.now() - startedAt,
|
|
@@ -16887,7 +17031,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16887
17031
|
try {
|
|
16888
17032
|
await ctx.injectContext(payload.markdown);
|
|
16889
17033
|
} catch (err) {
|
|
16890
|
-
log7?.warn(`[${
|
|
17034
|
+
log7?.warn(`[${PLUGIN_NAME10}] injectContext threw`, {
|
|
16891
17035
|
error: err instanceof Error ? err.message : String(err),
|
|
16892
17036
|
query,
|
|
16893
17037
|
sessionId: ctx.sessionId
|
|
@@ -16895,7 +17039,7 @@ async function runKhSearchAndInject(args) {
|
|
|
16895
17039
|
}
|
|
16896
17040
|
}
|
|
16897
17041
|
opts.cache.record(query, filtered);
|
|
16898
|
-
log7?.info(`[${
|
|
17042
|
+
log7?.info(`[${PLUGIN_NAME10}] inject complete (${mode})`, {
|
|
16899
17043
|
query,
|
|
16900
17044
|
mode,
|
|
16901
17045
|
candidateCount: filtered.length,
|
|
@@ -16911,18 +17055,18 @@ async function handleMessage2(raw, opts) {
|
|
|
16911
17055
|
const log7 = ctx.log;
|
|
16912
17056
|
const text = (ctx.content ?? "").trim();
|
|
16913
17057
|
if (!shouldInject(text, cfg)) {
|
|
16914
|
-
log7?.debug?.(`[${
|
|
17058
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] skip (filter)`, { textLen: text.length });
|
|
16915
17059
|
return;
|
|
16916
17060
|
}
|
|
16917
17061
|
const query = extractQuery(text);
|
|
16918
17062
|
if (!query)
|
|
16919
17063
|
return;
|
|
16920
17064
|
if (opts.cache.shouldSkip(query)) {
|
|
16921
|
-
log7?.debug?.(`[${
|
|
17065
|
+
log7?.debug?.(`[${PLUGIN_NAME10}] cache hit, skip`, { query });
|
|
16922
17066
|
return;
|
|
16923
17067
|
}
|
|
16924
17068
|
if (inflight2.size >= INFLIGHT_CAP) {
|
|
16925
|
-
log7?.warn(`[${
|
|
17069
|
+
log7?.warn(`[${PLUGIN_NAME10}] inflight cap reached, skip`, {
|
|
16926
17070
|
query,
|
|
16927
17071
|
inflightSize: inflight2.size,
|
|
16928
17072
|
cap: INFLIGHT_CAP,
|
|
@@ -16933,7 +17077,7 @@ async function handleMessage2(raw, opts) {
|
|
|
16933
17077
|
const key = inflightKey(ctx.sessionId, query);
|
|
16934
17078
|
inflight2.add(key);
|
|
16935
17079
|
runKhSearchAndInject({ query, ctx, opts, mode }).catch((err) => {
|
|
16936
|
-
log7?.warn(`[${
|
|
17080
|
+
log7?.warn(`[${PLUGIN_NAME10}] runKhSearchAndInject 顶层兜底捕获`, {
|
|
16937
17081
|
error: err instanceof Error ? err.message : String(err),
|
|
16938
17082
|
query,
|
|
16939
17083
|
sessionId: ctx.sessionId
|
|
@@ -16942,13 +17086,13 @@ async function handleMessage2(raw, opts) {
|
|
|
16942
17086
|
inflight2.delete(key);
|
|
16943
17087
|
});
|
|
16944
17088
|
}
|
|
16945
|
-
logLifecycle(
|
|
17089
|
+
logLifecycle(PLUGIN_NAME10, "import");
|
|
16946
17090
|
var sharedClient2 = new KhClient;
|
|
16947
17091
|
var sharedCache = new QueryCache(DEFAULT_CONFIG4.cacheTtlMs);
|
|
16948
17092
|
var khAutoContextServer = async (ctx) => {
|
|
16949
|
-
const log7 = makePluginLogger(
|
|
17093
|
+
const log7 = makePluginLogger(PLUGIN_NAME10);
|
|
16950
17094
|
const runtimeMode = resolveInjectionMode(sharedClient2);
|
|
16951
|
-
logLifecycle(
|
|
17095
|
+
logLifecycle(PLUGIN_NAME10, "activate", {
|
|
16952
17096
|
directory: ctx.directory,
|
|
16953
17097
|
minConfidence: DEFAULT_CONFIG4.minConfidence,
|
|
16954
17098
|
timeoutMs: DEFAULT_CONFIG4.timeoutMs,
|
|
@@ -16962,7 +17106,7 @@ var khAutoContextServer = async (ctx) => {
|
|
|
16962
17106
|
});
|
|
16963
17107
|
return {
|
|
16964
17108
|
"chat.message": async (input, output) => {
|
|
16965
|
-
await safeAsync(
|
|
17109
|
+
await safeAsync(PLUGIN_NAME10, "chat.message", async () => {
|
|
16966
17110
|
const text = extractUserText(output);
|
|
16967
17111
|
if (!text)
|
|
16968
17112
|
return;
|
|
@@ -16982,7 +17126,7 @@ var khAutoContextServer = async (ctx) => {
|
|
|
16982
17126
|
});
|
|
16983
17127
|
},
|
|
16984
17128
|
event: async ({ event }) => {
|
|
16985
|
-
await safeAsync(
|
|
17129
|
+
await safeAsync(PLUGIN_NAME10, "event", async () => {
|
|
16986
17130
|
const e = event;
|
|
16987
17131
|
if (e.type !== "session.idle")
|
|
16988
17132
|
return;
|
|
@@ -16991,14 +17135,14 @@ var khAutoContextServer = async (ctx) => {
|
|
|
16991
17135
|
if (typeof sid !== "string" || !sid)
|
|
16992
17136
|
return;
|
|
16993
17137
|
sharedKhCache.onSessionEnd(sid);
|
|
16994
|
-
log7.debug?.(`[${
|
|
17138
|
+
log7.debug?.(`[${PLUGIN_NAME10}] session.idle: cleared shared cache`, {
|
|
16995
17139
|
sessionID: sid
|
|
16996
17140
|
});
|
|
16997
17141
|
});
|
|
16998
17142
|
}
|
|
16999
17143
|
};
|
|
17000
17144
|
};
|
|
17001
|
-
var
|
|
17145
|
+
var handler10 = khAutoContextServer;
|
|
17002
17146
|
|
|
17003
17147
|
// lib/condenser.ts
|
|
17004
17148
|
var DEFAULT_CONDENSE = {
|
|
@@ -17116,8 +17260,8 @@ async function condense(input, opts = {}) {
|
|
|
17116
17260
|
}
|
|
17117
17261
|
|
|
17118
17262
|
// plugins/kh-reminder.ts
|
|
17119
|
-
var
|
|
17120
|
-
logLifecycle(
|
|
17263
|
+
var PLUGIN_NAME11 = "kh-reminder";
|
|
17264
|
+
logLifecycle(PLUGIN_NAME11, "import", {});
|
|
17121
17265
|
var TRIGGER_WORDS_ZH = [
|
|
17122
17266
|
"怎么",
|
|
17123
17267
|
"怎样",
|
|
@@ -17271,7 +17415,7 @@ function handleObserve(raw, log7) {
|
|
|
17271
17415
|
const result = evaluate(ctx);
|
|
17272
17416
|
if (result.triggered && result.reason) {
|
|
17273
17417
|
const reasonStr = formatReason(result.reason);
|
|
17274
|
-
log7?.info(`[${
|
|
17418
|
+
log7?.info(`[${PLUGIN_NAME11}] ⚡ KH 提醒(observe-only) · session=${result.sessionId} · ${reasonStr}`, {
|
|
17275
17419
|
sessionId: result.sessionId,
|
|
17276
17420
|
reason: result.reason,
|
|
17277
17421
|
suggestion: "调用 smart_search 查项目历史;完成后用 save_chat_insight 沉淀"
|
|
@@ -17279,7 +17423,7 @@ function handleObserve(raw, log7) {
|
|
|
17279
17423
|
}
|
|
17280
17424
|
return result;
|
|
17281
17425
|
} catch (err) {
|
|
17282
|
-
log7?.warn(`[${
|
|
17426
|
+
log7?.warn(`[${PLUGIN_NAME11}] evaluate 异常(已隔离)`, {
|
|
17283
17427
|
error: err instanceof Error ? err.message : String(err)
|
|
17284
17428
|
});
|
|
17285
17429
|
return null;
|
|
@@ -17295,9 +17439,9 @@ function formatReason(r) {
|
|
|
17295
17439
|
return `no-search-in-recent-rounds (last ${r.rounds})`;
|
|
17296
17440
|
}
|
|
17297
17441
|
}
|
|
17298
|
-
var log7 = makePluginLogger(
|
|
17442
|
+
var log7 = makePluginLogger(PLUGIN_NAME11);
|
|
17299
17443
|
var khReminderServer = async (ctx) => {
|
|
17300
|
-
logLifecycle(
|
|
17444
|
+
logLifecycle(PLUGIN_NAME11, "activate", {
|
|
17301
17445
|
directory: ctx.directory,
|
|
17302
17446
|
threshold: DEFAULT_THRESHOLD,
|
|
17303
17447
|
cooldown_ms: COOLDOWN_MS,
|
|
@@ -17305,7 +17449,7 @@ var khReminderServer = async (ctx) => {
|
|
|
17305
17449
|
});
|
|
17306
17450
|
return {
|
|
17307
17451
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
17308
|
-
await safeAsync(
|
|
17452
|
+
await safeAsync(PLUGIN_NAME11, "experimental.chat.messages.transform", async () => {
|
|
17309
17453
|
const list = output.messages;
|
|
17310
17454
|
if (!Array.isArray(list) || list.length === 0)
|
|
17311
17455
|
return;
|
|
@@ -17324,7 +17468,7 @@ var khReminderServer = async (ctx) => {
|
|
|
17324
17468
|
const result = handleObserve({ messages: flat, sessionId }, log7);
|
|
17325
17469
|
if (!result)
|
|
17326
17470
|
return;
|
|
17327
|
-
safeWriteLog(
|
|
17471
|
+
safeWriteLog(PLUGIN_NAME11, {
|
|
17328
17472
|
hook: "experimental.chat.messages.transform",
|
|
17329
17473
|
mode: "observe-only",
|
|
17330
17474
|
sessionId: result.sessionId,
|
|
@@ -17337,7 +17481,7 @@ var khReminderServer = async (ctx) => {
|
|
|
17337
17481
|
}
|
|
17338
17482
|
};
|
|
17339
17483
|
};
|
|
17340
|
-
var
|
|
17484
|
+
var handler11 = khReminderServer;
|
|
17341
17485
|
|
|
17342
17486
|
// lib/memories.ts
|
|
17343
17487
|
import { promises as fs15 } from "node:fs";
|
|
@@ -17538,7 +17682,7 @@ function bagOfWordsScore(query, doc) {
|
|
|
17538
17682
|
}
|
|
17539
17683
|
|
|
17540
17684
|
// plugins/memories-context.ts
|
|
17541
|
-
var
|
|
17685
|
+
var PLUGIN_NAME12 = "memories-context";
|
|
17542
17686
|
var INJECTION_MODE2 = "observe-only";
|
|
17543
17687
|
var DEFAULT_CONFIG5 = {
|
|
17544
17688
|
minTextLength: 8,
|
|
@@ -17636,14 +17780,14 @@ async function handleMessage3(raw, opts) {
|
|
|
17636
17780
|
return await handleDirective(dir, ctx, opts.memCfg, log8);
|
|
17637
17781
|
}
|
|
17638
17782
|
if (!shouldRecall(text, cfg)) {
|
|
17639
|
-
log8?.debug?.(`[${
|
|
17783
|
+
log8?.debug?.(`[${PLUGIN_NAME12}] skip (filter)`, { textLen: text.length });
|
|
17640
17784
|
return { kind: "noop", reason: "filtered", mode: INJECTION_MODE2 };
|
|
17641
17785
|
}
|
|
17642
17786
|
const query = extractQuery2(text);
|
|
17643
17787
|
if (!query)
|
|
17644
17788
|
return { kind: "noop", reason: "empty_query", mode: INJECTION_MODE2 };
|
|
17645
17789
|
if (cache2.shouldSkip(query)) {
|
|
17646
|
-
log8?.debug?.(`[${
|
|
17790
|
+
log8?.debug?.(`[${PLUGIN_NAME12}] cache hit`, { query });
|
|
17647
17791
|
return { kind: "noop", reason: "cache", mode: INJECTION_MODE2 };
|
|
17648
17792
|
}
|
|
17649
17793
|
const racer = opts.scheduler?.raceTimeout ?? (async (p, ms) => {
|
|
@@ -17668,7 +17812,7 @@ async function handleMessage3(raw, opts) {
|
|
|
17668
17812
|
}, opts.memCfg);
|
|
17669
17813
|
const result = await racer(injectPromise, cfg.timeoutMs);
|
|
17670
17814
|
if (result === "__timeout__") {
|
|
17671
|
-
log8?.warn(`[${
|
|
17815
|
+
log8?.warn(`[${PLUGIN_NAME12}] timeout`, { query, ms: cfg.timeoutMs });
|
|
17672
17816
|
cache2.record(query, 0);
|
|
17673
17817
|
return { kind: "noop", reason: "timeout", mode: INJECTION_MODE2 };
|
|
17674
17818
|
}
|
|
@@ -17680,13 +17824,13 @@ async function handleMessage3(raw, opts) {
|
|
|
17680
17824
|
try {
|
|
17681
17825
|
await ctx.injectContext(result.text);
|
|
17682
17826
|
} catch (err) {
|
|
17683
|
-
log8?.warn(`[${
|
|
17827
|
+
log8?.warn(`[${PLUGIN_NAME12}] injectContext threw`, {
|
|
17684
17828
|
error: err instanceof Error ? err.message : String(err)
|
|
17685
17829
|
});
|
|
17686
17830
|
}
|
|
17687
17831
|
}
|
|
17688
17832
|
cache2.record(query, result.recalled);
|
|
17689
|
-
log8?.info(`[${
|
|
17833
|
+
log8?.info(`[${PLUGIN_NAME12}] observe-only: ${result.used}/${result.recalled} memories ready`, { query, mode: INJECTION_MODE2 });
|
|
17690
17834
|
return { kind: "injected", payload: result, mode: INJECTION_MODE2 };
|
|
17691
17835
|
}
|
|
17692
17836
|
async function handleDirective(dir, ctx, memCfg, log8) {
|
|
@@ -17695,14 +17839,14 @@ async function handleDirective(dir, ctx, memCfg, log8) {
|
|
|
17695
17839
|
if (r.ok) {
|
|
17696
17840
|
const target = r.written_to === "kh" ? "KH" : "本地";
|
|
17697
17841
|
await safeReply(ctx, `\uD83E\uDDE0 已记住(${dir.scope} / ${target},id=${r.id})`);
|
|
17698
|
-
log8?.info(`[${
|
|
17842
|
+
log8?.info(`[${PLUGIN_NAME12}] /remember ok`, {
|
|
17699
17843
|
scope: dir.scope,
|
|
17700
17844
|
id: r.id,
|
|
17701
17845
|
mode: INJECTION_MODE2
|
|
17702
17846
|
});
|
|
17703
17847
|
} else {
|
|
17704
17848
|
await safeReply(ctx, `❌ 记忆失败:${r.error ?? "unknown"}`);
|
|
17705
|
-
log8?.warn(`[${
|
|
17849
|
+
log8?.warn(`[${PLUGIN_NAME12}] /remember failed`, { error: r.error });
|
|
17706
17850
|
}
|
|
17707
17851
|
return { kind: "remembered", result: r, mode: INJECTION_MODE2 };
|
|
17708
17852
|
}
|
|
@@ -17733,22 +17877,22 @@ async function safeReply(ctx, text) {
|
|
|
17733
17877
|
await ctx.reply(text);
|
|
17734
17878
|
} catch {}
|
|
17735
17879
|
}
|
|
17736
|
-
logLifecycle(
|
|
17880
|
+
logLifecycle(PLUGIN_NAME12, "import");
|
|
17737
17881
|
var sharedCache2 = new QueryCache2(DEFAULT_CONFIG5.cacheTtlMs);
|
|
17738
17882
|
function buildMemCfg(directory) {
|
|
17739
17883
|
return { projectRoot: directory };
|
|
17740
17884
|
}
|
|
17741
17885
|
var memoriesContextServer = async (ctx) => {
|
|
17742
|
-
const log8 = makePluginLogger(
|
|
17886
|
+
const log8 = makePluginLogger(PLUGIN_NAME12);
|
|
17743
17887
|
const memCfg = buildMemCfg(ctx.directory);
|
|
17744
|
-
logLifecycle(
|
|
17888
|
+
logLifecycle(PLUGIN_NAME12, "activate", {
|
|
17745
17889
|
directory: ctx.directory,
|
|
17746
17890
|
projectRoot: memCfg.projectRoot,
|
|
17747
17891
|
mode: INJECTION_MODE2
|
|
17748
17892
|
});
|
|
17749
17893
|
return {
|
|
17750
17894
|
"chat.message": async (input, output) => {
|
|
17751
|
-
await safeAsync(
|
|
17895
|
+
await safeAsync(PLUGIN_NAME12, "chat.message", async () => {
|
|
17752
17896
|
const text = extractUserText(output);
|
|
17753
17897
|
if (!text)
|
|
17754
17898
|
return;
|
|
@@ -17774,17 +17918,17 @@ var memoriesContextServer = async (ctx) => {
|
|
|
17774
17918
|
}
|
|
17775
17919
|
};
|
|
17776
17920
|
};
|
|
17777
|
-
var
|
|
17921
|
+
var handler12 = memoriesContextServer;
|
|
17778
17922
|
|
|
17779
17923
|
// plugins/model-fallback.ts
|
|
17780
|
-
var
|
|
17924
|
+
var PLUGIN_NAME13 = "model-fallback";
|
|
17781
17925
|
var state = {
|
|
17782
17926
|
config: null,
|
|
17783
17927
|
configPath: null,
|
|
17784
17928
|
warnings: [],
|
|
17785
17929
|
error: null
|
|
17786
17930
|
};
|
|
17787
|
-
logLifecycle(
|
|
17931
|
+
logLifecycle(PLUGIN_NAME13, "import");
|
|
17788
17932
|
function loadOnce(root) {
|
|
17789
17933
|
if (state.config !== null || state.error !== null)
|
|
17790
17934
|
return;
|
|
@@ -17794,11 +17938,11 @@ function loadOnce(root) {
|
|
|
17794
17938
|
state.configPath = r.path ?? null;
|
|
17795
17939
|
state.warnings = r.warnings;
|
|
17796
17940
|
if (r.warnings.length > 0) {
|
|
17797
|
-
safeWriteLog(
|
|
17941
|
+
safeWriteLog(PLUGIN_NAME13, { phase: "load.warnings", warnings: r.warnings });
|
|
17798
17942
|
}
|
|
17799
17943
|
} else {
|
|
17800
17944
|
state.error = r.error ?? "unknown_load_error";
|
|
17801
|
-
safeWriteLog(
|
|
17945
|
+
safeWriteLog(PLUGIN_NAME13, { phase: "load.failed", error: state.error, path: r.path });
|
|
17802
17946
|
}
|
|
17803
17947
|
}
|
|
17804
17948
|
var MODEL_ERR_PATTERNS = [
|
|
@@ -17843,9 +17987,9 @@ fallback 链已用尽:${meta.chain.join(" → ")}`;
|
|
|
17843
17987
|
完整链:${meta.chain.join(" → ")}`;
|
|
17844
17988
|
}
|
|
17845
17989
|
var modelFallbackServer = async (ctx) => {
|
|
17846
|
-
const log8 = makePluginLogger(
|
|
17990
|
+
const log8 = makePluginLogger(PLUGIN_NAME13);
|
|
17847
17991
|
loadOnce(ctx.directory ?? process.cwd());
|
|
17848
|
-
logLifecycle(
|
|
17992
|
+
logLifecycle(PLUGIN_NAME13, "activate", {
|
|
17849
17993
|
directory: ctx.directory,
|
|
17850
17994
|
config_path: state.configPath,
|
|
17851
17995
|
config_loaded: state.config !== null,
|
|
@@ -17855,7 +17999,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17855
17999
|
});
|
|
17856
18000
|
return {
|
|
17857
18001
|
"chat.params": async (input, output) => {
|
|
17858
|
-
await safeAsync(
|
|
18002
|
+
await safeAsync(PLUGIN_NAME13, "chat.params", async () => {
|
|
17859
18003
|
if (!state.config)
|
|
17860
18004
|
return;
|
|
17861
18005
|
const agent = input.agent;
|
|
@@ -17876,7 +18020,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17876
18020
|
next: meta.next_fallback,
|
|
17877
18021
|
source: meta.source
|
|
17878
18022
|
};
|
|
17879
|
-
safeWriteLog(
|
|
18023
|
+
safeWriteLog(PLUGIN_NAME13, {
|
|
17880
18024
|
hook: "chat.params",
|
|
17881
18025
|
agent,
|
|
17882
18026
|
model: currentModel,
|
|
@@ -17886,7 +18030,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17886
18030
|
});
|
|
17887
18031
|
},
|
|
17888
18032
|
event: async ({ event }) => {
|
|
17889
|
-
await safeAsync(
|
|
18033
|
+
await safeAsync(PLUGIN_NAME13, "event", async () => {
|
|
17890
18034
|
if (!state.config)
|
|
17891
18035
|
return;
|
|
17892
18036
|
const e = event;
|
|
@@ -17902,8 +18046,8 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17902
18046
|
const model = props.model ?? "unknown/unknown";
|
|
17903
18047
|
const meta = buildFallbackMeta(state.config, agent, model);
|
|
17904
18048
|
const suggestion = meta ? buildSuggestion(meta, String(message)) : `⚠️ ${agent}/${model} 失败:${message}`;
|
|
17905
|
-
log8.warn(`[${
|
|
17906
|
-
safeWriteLog(
|
|
18049
|
+
log8.warn(`[${PLUGIN_NAME13}] ${suggestion}`);
|
|
18050
|
+
safeWriteLog(PLUGIN_NAME13, {
|
|
17907
18051
|
hook: "event.error",
|
|
17908
18052
|
eventType: e.type,
|
|
17909
18053
|
agent,
|
|
@@ -17915,7 +18059,7 @@ var modelFallbackServer = async (ctx) => {
|
|
|
17915
18059
|
}
|
|
17916
18060
|
};
|
|
17917
18061
|
};
|
|
17918
|
-
var
|
|
18062
|
+
var handler13 = modelFallbackServer;
|
|
17919
18063
|
|
|
17920
18064
|
// plugins/subtask-heartbeat.ts
|
|
17921
18065
|
import { promises as fsPromises } from "node:fs";
|
|
@@ -17929,8 +18073,8 @@ var sweepExpiredSessionParents2 = sweepExpiredSessionParents;
|
|
|
17929
18073
|
var _bulkInjectSessionParentMap2 = _bulkInjectSessionParentMap;
|
|
17930
18074
|
var _capSessionParentMap2 = _capSessionParentMap;
|
|
17931
18075
|
var _setPersistRootForTests2 = _setPersistRootForTests;
|
|
17932
|
-
var
|
|
17933
|
-
logLifecycle(
|
|
18076
|
+
var PLUGIN_NAME14 = "subtask-heartbeat";
|
|
18077
|
+
logLifecycle(PLUGIN_NAME14, "import", {});
|
|
17934
18078
|
var HEARTBEAT_INTERVAL_MS2 = 30000;
|
|
17935
18079
|
var HEARTBEAT_DEBOUNCE_MS = 25000;
|
|
17936
18080
|
var TOAST_DURATION_MS2 = 5000;
|
|
@@ -18272,9 +18416,9 @@ async function showToast2(client, payload, log8) {
|
|
|
18272
18416
|
return false;
|
|
18273
18417
|
}
|
|
18274
18418
|
}
|
|
18275
|
-
var log8 = makePluginLogger(
|
|
18419
|
+
var log8 = makePluginLogger(PLUGIN_NAME14);
|
|
18276
18420
|
var subtaskHeartbeatServer = async (ctx) => {
|
|
18277
|
-
logLifecycle(
|
|
18421
|
+
logLifecycle(PLUGIN_NAME14, "activate", {
|
|
18278
18422
|
directory: ctx.directory,
|
|
18279
18423
|
intervalMs: HEARTBEAT_INTERVAL_MS2
|
|
18280
18424
|
});
|
|
@@ -18291,7 +18435,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18291
18435
|
}));
|
|
18292
18436
|
_bulkInjectSessionParentMap2(entries);
|
|
18293
18437
|
const cappedOut = _capSessionParentMap2();
|
|
18294
|
-
safeWriteLog(
|
|
18438
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18295
18439
|
hook: "activate",
|
|
18296
18440
|
type: "parent-map.restore",
|
|
18297
18441
|
restored: restored.size,
|
|
@@ -18304,14 +18448,14 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18304
18448
|
});
|
|
18305
18449
|
}
|
|
18306
18450
|
const interval = setInterval(() => {
|
|
18307
|
-
safeAsync(
|
|
18451
|
+
safeAsync(PLUGIN_NAME14, "interval", async () => {
|
|
18308
18452
|
const swept = sweepExpiredPendingTasks();
|
|
18309
18453
|
if (swept > 0) {
|
|
18310
|
-
safeWriteLog(
|
|
18454
|
+
safeWriteLog(PLUGIN_NAME14, { hook: "interval", pending_task_swept: swept });
|
|
18311
18455
|
}
|
|
18312
18456
|
const sweptParents = sweepExpiredSessionParents2();
|
|
18313
18457
|
if (sweptParents > 0) {
|
|
18314
|
-
safeWriteLog(
|
|
18458
|
+
safeWriteLog(PLUGIN_NAME14, { hook: "interval", session_parent_swept: sweptParents });
|
|
18315
18459
|
}
|
|
18316
18460
|
const beats = pickHeartbeats();
|
|
18317
18461
|
if (beats.length === 0)
|
|
@@ -18319,7 +18463,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18319
18463
|
for (const r of beats) {
|
|
18320
18464
|
const t = buildHeartbeatToast(r);
|
|
18321
18465
|
const sent = await showToast2(client, t, log8);
|
|
18322
|
-
safeWriteLog(
|
|
18466
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18323
18467
|
hook: "interval",
|
|
18324
18468
|
child: r.childID,
|
|
18325
18469
|
parent: r.parentID,
|
|
@@ -18336,7 +18480,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18336
18480
|
}
|
|
18337
18481
|
return {
|
|
18338
18482
|
event: async ({ event }) => {
|
|
18339
|
-
await safeAsync(
|
|
18483
|
+
await safeAsync(PLUGIN_NAME14, "event", async () => {
|
|
18340
18484
|
try {
|
|
18341
18485
|
if (detectUnparsedParentID(event) && _parentParseFailLogged < PARENT_PARSE_FAIL_MAX_LOG) {
|
|
18342
18486
|
_parentParseFailLogged++;
|
|
@@ -18344,7 +18488,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18344
18488
|
sample_count: _parentParseFailLogged,
|
|
18345
18489
|
hint: "如频繁出现请检查 plugins/subtask-heartbeat.ts::extractCreatedChild 是否适配新 SDK"
|
|
18346
18490
|
});
|
|
18347
|
-
safeWriteLog(
|
|
18491
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18348
18492
|
hook: "event",
|
|
18349
18493
|
type: "session.created.unparsed-parent-id",
|
|
18350
18494
|
sample_count: _parentParseFailLogged
|
|
@@ -18367,7 +18511,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18367
18511
|
agent: pending?.agent ?? created.agent,
|
|
18368
18512
|
description: pending?.description ?? null
|
|
18369
18513
|
});
|
|
18370
|
-
safeWriteLog(
|
|
18514
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18371
18515
|
hook: "event",
|
|
18372
18516
|
type: "session.created",
|
|
18373
18517
|
child: created.childID,
|
|
@@ -18378,7 +18522,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18378
18522
|
});
|
|
18379
18523
|
const startToast = buildStartToast(record);
|
|
18380
18524
|
const sent = await showToast2(client, { ...startToast, duration: START_TOAST_DURATION_MS }, log8);
|
|
18381
|
-
safeWriteLog(
|
|
18525
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18382
18526
|
hook: "event",
|
|
18383
18527
|
type: "session.created.toast",
|
|
18384
18528
|
child: created.childID,
|
|
@@ -18398,7 +18542,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18398
18542
|
if (r) {
|
|
18399
18543
|
const t = buildEndToast(r, ended.type);
|
|
18400
18544
|
const sent = await showToast2(client, t, log8);
|
|
18401
|
-
safeWriteLog(
|
|
18545
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18402
18546
|
hook: "event",
|
|
18403
18547
|
type: ended.type,
|
|
18404
18548
|
child: r.childID,
|
|
@@ -18418,12 +18562,12 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18418
18562
|
const isTaskTool = input?.tool === "task";
|
|
18419
18563
|
if (inflight3.size === 0 && !isTaskTool)
|
|
18420
18564
|
return;
|
|
18421
|
-
await safeAsync(
|
|
18565
|
+
await safeAsync(PLUGIN_NAME14, "tool.execute.before", async () => {
|
|
18422
18566
|
if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
|
|
18423
18567
|
return;
|
|
18424
18568
|
if (isTaskTool) {
|
|
18425
18569
|
const args = output?.args ?? null;
|
|
18426
|
-
safeWriteLog(
|
|
18570
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18427
18571
|
hook: "tool.execute.before.task",
|
|
18428
18572
|
sessionID: input.sessionID,
|
|
18429
18573
|
args_keys: args && typeof args === "object" ? Object.keys(args) : null,
|
|
@@ -18435,7 +18579,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18435
18579
|
agent: extracted.subagentType,
|
|
18436
18580
|
description: extracted.description
|
|
18437
18581
|
});
|
|
18438
|
-
safeWriteLog(
|
|
18582
|
+
safeWriteLog(PLUGIN_NAME14, {
|
|
18439
18583
|
hook: "tool.execute.before.task.enqueued",
|
|
18440
18584
|
parent: input.sessionID,
|
|
18441
18585
|
agent: extracted.subagentType,
|
|
@@ -18463,7 +18607,7 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18463
18607
|
"tool.execute.after": async (input, _output) => {
|
|
18464
18608
|
if (inflight3.size === 0)
|
|
18465
18609
|
return;
|
|
18466
|
-
await safeAsync(
|
|
18610
|
+
await safeAsync(PLUGIN_NAME14, "tool.execute.after", async () => {
|
|
18467
18611
|
if (!input || typeof input.sessionID !== "string" || typeof input.tool !== "string")
|
|
18468
18612
|
return;
|
|
18469
18613
|
const rec = inflight3.get(input.sessionID);
|
|
@@ -18486,11 +18630,11 @@ var subtaskHeartbeatServer = async (ctx) => {
|
|
|
18486
18630
|
}
|
|
18487
18631
|
};
|
|
18488
18632
|
};
|
|
18489
|
-
var
|
|
18633
|
+
var handler14 = subtaskHeartbeatServer;
|
|
18490
18634
|
|
|
18491
18635
|
// plugins/parallel-status.ts
|
|
18492
|
-
var
|
|
18493
|
-
logLifecycle(
|
|
18636
|
+
var PLUGIN_NAME15 = "parallel-status";
|
|
18637
|
+
logLifecycle(PLUGIN_NAME15, "import");
|
|
18494
18638
|
var ID_MAX_LEN = 16;
|
|
18495
18639
|
var ID_KEEP_LEN = 13;
|
|
18496
18640
|
function shortId(s) {
|
|
@@ -18522,8 +18666,8 @@ function formatInflightMarkdown(snapshot, now = Date.now()) {
|
|
|
18522
18666
|
`);
|
|
18523
18667
|
}
|
|
18524
18668
|
var parallelStatusServer = async (ctx) => {
|
|
18525
|
-
const log9 = makePluginLogger(
|
|
18526
|
-
logLifecycle(
|
|
18669
|
+
const log9 = makePluginLogger(PLUGIN_NAME15);
|
|
18670
|
+
logLifecycle(PLUGIN_NAME15, "activate", { directory: ctx.directory });
|
|
18527
18671
|
return {
|
|
18528
18672
|
"command.execute.before": async (input, output) => {
|
|
18529
18673
|
try {
|
|
@@ -18542,23 +18686,23 @@ var parallelStatusServer = async (ctx) => {
|
|
|
18542
18686
|
synthetic: false
|
|
18543
18687
|
});
|
|
18544
18688
|
}
|
|
18545
|
-
log9.info(`[${
|
|
18689
|
+
log9.info(`[${PLUGIN_NAME15}] 已回写 ${snapshot.length} 条 inflight`);
|
|
18546
18690
|
} catch (err) {
|
|
18547
|
-
log9.error(`[${
|
|
18691
|
+
log9.error(`[${PLUGIN_NAME15}] command.execute.before 异常(已隔离)`, {
|
|
18548
18692
|
error: err instanceof Error ? err.message : String(err)
|
|
18549
18693
|
});
|
|
18550
18694
|
}
|
|
18551
18695
|
}
|
|
18552
18696
|
};
|
|
18553
18697
|
};
|
|
18554
|
-
var
|
|
18698
|
+
var handler15 = parallelStatusServer;
|
|
18555
18699
|
|
|
18556
18700
|
// plugins/parallel-tool-nudge.ts
|
|
18557
18701
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
|
|
18558
18702
|
import { join as join16 } from "node:path";
|
|
18559
18703
|
import { homedir as homedir6 } from "node:os";
|
|
18560
|
-
var
|
|
18561
|
-
logLifecycle(
|
|
18704
|
+
var PLUGIN_NAME16 = "parallel-tool-nudge";
|
|
18705
|
+
logLifecycle(PLUGIN_NAME16, "import", {});
|
|
18562
18706
|
var PARALLEL_SAFE_TOOLS = [
|
|
18563
18707
|
"read",
|
|
18564
18708
|
"smart_search",
|
|
@@ -18620,7 +18764,7 @@ function loadAgentToolsMap(rootDir, opts = {}) {
|
|
|
18620
18764
|
const result = new Map;
|
|
18621
18765
|
const safeSet = new Set(PARALLEL_SAFE_TOOLS);
|
|
18622
18766
|
const unionTools = new Set;
|
|
18623
|
-
const log9 = makePluginLogger(
|
|
18767
|
+
const log9 = makePluginLogger(PLUGIN_NAME16);
|
|
18624
18768
|
for (const dir of candidateDirs) {
|
|
18625
18769
|
if (!dirExists(dir))
|
|
18626
18770
|
continue;
|
|
@@ -18670,33 +18814,33 @@ function loadAgentToolsMap(rootDir, opts = {}) {
|
|
|
18670
18814
|
result.set(DEFAULT_AGENT_KEY, Array.from(unionTools));
|
|
18671
18815
|
return result;
|
|
18672
18816
|
}
|
|
18673
|
-
var
|
|
18674
|
-
var
|
|
18675
|
-
var
|
|
18676
|
-
function
|
|
18677
|
-
while (
|
|
18678
|
-
const oldestKey =
|
|
18817
|
+
var sessionAgentMap2 = new Map;
|
|
18818
|
+
var SESSION_CAP3 = 200;
|
|
18819
|
+
var SESSION_TTL_MS3 = 24 * 60 * 60 * 1000;
|
|
18820
|
+
function pruneIfOversize3() {
|
|
18821
|
+
while (sessionAgentMap2.size > SESSION_CAP3) {
|
|
18822
|
+
const oldestKey = sessionAgentMap2.keys().next().value;
|
|
18679
18823
|
if (oldestKey === undefined)
|
|
18680
18824
|
break;
|
|
18681
|
-
|
|
18825
|
+
sessionAgentMap2.delete(oldestKey);
|
|
18682
18826
|
}
|
|
18683
18827
|
}
|
|
18684
|
-
function
|
|
18685
|
-
return now - entry.ts >
|
|
18828
|
+
function isExpired3(entry, now) {
|
|
18829
|
+
return now - entry.ts > SESSION_TTL_MS3;
|
|
18686
18830
|
}
|
|
18687
18831
|
function resolveAgent(sessionID, nowFn = Date.now) {
|
|
18688
18832
|
if (!sessionID)
|
|
18689
18833
|
return "unknown";
|
|
18690
|
-
const entry =
|
|
18834
|
+
const entry = sessionAgentMap2.get(sessionID);
|
|
18691
18835
|
if (!entry)
|
|
18692
18836
|
return "unknown";
|
|
18693
18837
|
const now = nowFn();
|
|
18694
|
-
if (
|
|
18695
|
-
|
|
18838
|
+
if (isExpired3(entry, now)) {
|
|
18839
|
+
sessionAgentMap2.delete(sessionID);
|
|
18696
18840
|
return "unknown";
|
|
18697
18841
|
}
|
|
18698
|
-
|
|
18699
|
-
|
|
18842
|
+
sessionAgentMap2.delete(sessionID);
|
|
18843
|
+
sessionAgentMap2.set(sessionID, entry);
|
|
18700
18844
|
return entry.agent;
|
|
18701
18845
|
}
|
|
18702
18846
|
function renderNudge(agent, tools) {
|
|
@@ -18728,7 +18872,7 @@ This directive is re-injected every turn — it is not optional advice.
|
|
|
18728
18872
|
return body.slice(0, NUDGE_MAX_LEN2 - 4) + `
|
|
18729
18873
|
…`;
|
|
18730
18874
|
}
|
|
18731
|
-
var log9 = makePluginLogger(
|
|
18875
|
+
var log9 = makePluginLogger(PLUGIN_NAME16);
|
|
18732
18876
|
var parallelToolNudgeServer = async (ctx) => {
|
|
18733
18877
|
try {
|
|
18734
18878
|
const loaded = loadAgentToolsMap(ctx.directory ?? process.cwd());
|
|
@@ -18748,30 +18892,30 @@ var parallelToolNudgeServer = async (ctx) => {
|
|
|
18748
18892
|
});
|
|
18749
18893
|
}
|
|
18750
18894
|
const defaultTools = agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
|
|
18751
|
-
logLifecycle(
|
|
18895
|
+
logLifecycle(PLUGIN_NAME16, "activate", {
|
|
18752
18896
|
directory: ctx.directory,
|
|
18753
18897
|
real_agents_loaded: realAgentCount,
|
|
18754
18898
|
default_tools_union: defaultTools,
|
|
18755
18899
|
parallel_safe_whitelist: PARALLEL_SAFE_TOOLS,
|
|
18756
|
-
session_cap:
|
|
18757
|
-
session_ttl_ms:
|
|
18900
|
+
session_cap: SESSION_CAP3,
|
|
18901
|
+
session_ttl_ms: SESSION_TTL_MS3,
|
|
18758
18902
|
no_op: agentToolsMap.size === 0
|
|
18759
18903
|
});
|
|
18760
18904
|
return {
|
|
18761
18905
|
"chat.params": async (input, _output) => {
|
|
18762
|
-
await safeAsync(
|
|
18906
|
+
await safeAsync(PLUGIN_NAME16, "chat.params", async () => {
|
|
18763
18907
|
if (agentToolsMap.size === 0)
|
|
18764
18908
|
return;
|
|
18765
18909
|
const sid = input?.sessionID;
|
|
18766
18910
|
const agent = input?.agent;
|
|
18767
18911
|
if (!sid || !agent)
|
|
18768
18912
|
return;
|
|
18769
|
-
|
|
18770
|
-
|
|
18913
|
+
sessionAgentMap2.set(sid, { agent, ts: Date.now() });
|
|
18914
|
+
pruneIfOversize3();
|
|
18771
18915
|
});
|
|
18772
18916
|
},
|
|
18773
18917
|
"experimental.chat.system.transform": async (input, output) => {
|
|
18774
|
-
await safeAsync(
|
|
18918
|
+
await safeAsync(PLUGIN_NAME16, "experimental.chat.system.transform", async () => {
|
|
18775
18919
|
if (agentToolsMap.size === 0)
|
|
18776
18920
|
return;
|
|
18777
18921
|
if (!output || !Array.isArray(output.system))
|
|
@@ -18781,7 +18925,7 @@ var parallelToolNudgeServer = async (ctx) => {
|
|
|
18781
18925
|
const tools = agent !== "unknown" ? agentToolsMap.get(agent) ?? agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [] : agentToolsMap.get(DEFAULT_AGENT_KEY) ?? [];
|
|
18782
18926
|
const nudge = renderNudge(agent, tools);
|
|
18783
18927
|
output.system.push(nudge);
|
|
18784
|
-
safeWriteLog(
|
|
18928
|
+
safeWriteLog(PLUGIN_NAME16, {
|
|
18785
18929
|
hook: "experimental.chat.system.transform",
|
|
18786
18930
|
sessionID: sid,
|
|
18787
18931
|
agent,
|
|
@@ -18793,11 +18937,11 @@ var parallelToolNudgeServer = async (ctx) => {
|
|
|
18793
18937
|
}
|
|
18794
18938
|
};
|
|
18795
18939
|
};
|
|
18796
|
-
var
|
|
18940
|
+
var handler16 = parallelToolNudgeServer;
|
|
18797
18941
|
|
|
18798
18942
|
// plugins/pwsh-utf8.ts
|
|
18799
|
-
var
|
|
18800
|
-
logLifecycle(
|
|
18943
|
+
var PLUGIN_NAME17 = "pwsh-utf8";
|
|
18944
|
+
logLifecycle(PLUGIN_NAME17, "import", {});
|
|
18801
18945
|
var PRELUDE = "chcp 65001 *> $null; " + "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); " + "$OutputEncoding = [System.Text.UTF8Encoding]::new(); ";
|
|
18802
18946
|
function prependUtf8Prelude(command) {
|
|
18803
18947
|
if (typeof command !== "string")
|
|
@@ -18810,15 +18954,15 @@ function prependUtf8Prelude(command) {
|
|
|
18810
18954
|
return command;
|
|
18811
18955
|
return PRELUDE + command;
|
|
18812
18956
|
}
|
|
18813
|
-
var
|
|
18957
|
+
var handler17 = async (_ctx3) => {
|
|
18814
18958
|
const enabled = process.platform === "win32" && process.env.CODEFORGE_DISABLE_PWSH_UTF8 !== "1";
|
|
18815
18959
|
const reason = enabled ? "win32" : process.platform !== "win32" ? "non-win32" : "disabled-by-env";
|
|
18816
|
-
logLifecycle(
|
|
18960
|
+
logLifecycle(PLUGIN_NAME17, "activate", { enabled, platform: process.platform, reason });
|
|
18817
18961
|
if (!enabled)
|
|
18818
18962
|
return {};
|
|
18819
18963
|
return {
|
|
18820
18964
|
"tool.execute.before": async (input, output) => {
|
|
18821
|
-
await safeAsync(
|
|
18965
|
+
await safeAsync(PLUGIN_NAME17, "tool.execute.before", async () => {
|
|
18822
18966
|
if (input.tool !== "bash")
|
|
18823
18967
|
return;
|
|
18824
18968
|
const args = output.args ?? {};
|
|
@@ -18827,7 +18971,7 @@ var handler16 = async (_ctx3) => {
|
|
|
18827
18971
|
if (next !== undefined && next !== original) {
|
|
18828
18972
|
args["command"] = next;
|
|
18829
18973
|
output.args = args;
|
|
18830
|
-
safeWriteLog(
|
|
18974
|
+
safeWriteLog(PLUGIN_NAME17, {
|
|
18831
18975
|
hook: "tool.execute.before",
|
|
18832
18976
|
tool: input.tool,
|
|
18833
18977
|
callID: input.callID,
|
|
@@ -19243,8 +19387,8 @@ async function markBlocksConsumed(absRoot, entries) {
|
|
|
19243
19387
|
}
|
|
19244
19388
|
|
|
19245
19389
|
// plugins/session-recovery.ts
|
|
19246
|
-
var
|
|
19247
|
-
logLifecycle(
|
|
19390
|
+
var PLUGIN_NAME18 = "session-recovery";
|
|
19391
|
+
logLifecycle(PLUGIN_NAME18, "import", {});
|
|
19248
19392
|
async function processSessionStart(currentSessionId, opts = {}) {
|
|
19249
19393
|
if (opts.disabled) {
|
|
19250
19394
|
return { ok: true, injected: false, reason: "disabled" };
|
|
@@ -19254,7 +19398,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19254
19398
|
excludeIds.add(currentSessionId);
|
|
19255
19399
|
const r = await scanLastSession({ ...opts, excludeIds: [...excludeIds] });
|
|
19256
19400
|
if (!r.ok) {
|
|
19257
|
-
opts.log?.warn?.(`[${
|
|
19401
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] 扫描失败:${r.error}`);
|
|
19258
19402
|
return { ok: false, injected: false, reason: "scan_error", error: r.error };
|
|
19259
19403
|
}
|
|
19260
19404
|
const plan = r.plan;
|
|
@@ -19263,7 +19407,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19263
19407
|
try {
|
|
19264
19408
|
pendingBlocks = await scanBlockPending(opts.root);
|
|
19265
19409
|
} catch (err) {
|
|
19266
|
-
opts.log?.warn?.(`[${
|
|
19410
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] scanBlockPending 异常:${err instanceof Error ? err.message : String(err)}`);
|
|
19267
19411
|
}
|
|
19268
19412
|
}
|
|
19269
19413
|
const hasPendingBlocks = pendingBlocks.length > 0;
|
|
@@ -19281,7 +19425,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19281
19425
|
await opts.injectRecovery(injection);
|
|
19282
19426
|
} catch (err) {
|
|
19283
19427
|
const msg = err instanceof Error ? err.message : String(err);
|
|
19284
|
-
opts.log?.warn?.(`[${
|
|
19428
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] injectRecovery 异常:${msg}`);
|
|
19285
19429
|
return { ok: false, injected: false, plan, reason: "inject_error", error: msg, pendingBlocks };
|
|
19286
19430
|
}
|
|
19287
19431
|
}
|
|
@@ -19289,7 +19433,7 @@ async function processSessionStart(currentSessionId, opts = {}) {
|
|
|
19289
19433
|
try {
|
|
19290
19434
|
await markBlocksConsumed(opts.root, pendingBlocks);
|
|
19291
19435
|
} catch (err) {
|
|
19292
|
-
opts.log?.warn?.(`[${
|
|
19436
|
+
opts.log?.warn?.(`[${PLUGIN_NAME18}] markBlocksConsumed 异常:${err instanceof Error ? err.message : String(err)}`);
|
|
19293
19437
|
}
|
|
19294
19438
|
}
|
|
19295
19439
|
return { ok: true, injected: true, plan, reason: "ok", pendingBlocks };
|
|
@@ -19336,13 +19480,13 @@ function renderPrompt(plan) {
|
|
|
19336
19480
|
return lines.join(`
|
|
19337
19481
|
`);
|
|
19338
19482
|
}
|
|
19339
|
-
var log10 = makePluginLogger(
|
|
19483
|
+
var log10 = makePluginLogger(PLUGIN_NAME18);
|
|
19340
19484
|
var _lastInjection = null;
|
|
19341
19485
|
var sessionRecoveryServer = async (ctx) => {
|
|
19342
|
-
logLifecycle(
|
|
19486
|
+
logLifecycle(PLUGIN_NAME18, "activate", { directory: ctx.directory });
|
|
19343
19487
|
return {
|
|
19344
19488
|
event: async ({ event }) => {
|
|
19345
|
-
await safeAsync(
|
|
19489
|
+
await safeAsync(PLUGIN_NAME18, "event", async () => {
|
|
19346
19490
|
const e = event;
|
|
19347
19491
|
if (!e || typeof e.type !== "string")
|
|
19348
19492
|
return;
|
|
@@ -19357,7 +19501,7 @@ var sessionRecoveryServer = async (ctx) => {
|
|
|
19357
19501
|
_lastInjection = inj;
|
|
19358
19502
|
}
|
|
19359
19503
|
});
|
|
19360
|
-
safeWriteLog(
|
|
19504
|
+
safeWriteLog(PLUGIN_NAME18, {
|
|
19361
19505
|
hook: "event",
|
|
19362
19506
|
type: "session.start",
|
|
19363
19507
|
ok: r.ok,
|
|
@@ -19367,13 +19511,13 @@ var sessionRecoveryServer = async (ctx) => {
|
|
|
19367
19511
|
pending_blocks_count: r.pendingBlocks?.length ?? 0
|
|
19368
19512
|
});
|
|
19369
19513
|
if (r.injected && r.plan) {
|
|
19370
|
-
log10.info(`[${
|
|
19514
|
+
log10.info(`[${PLUGIN_NAME18}] 注入恢复提示(last=${r.plan.last_session_id?.slice(0, 8) ?? "?"}, reason=${r.plan.reason}, pending_blocks=${r.pendingBlocks?.length ?? 0})`);
|
|
19371
19515
|
}
|
|
19372
19516
|
});
|
|
19373
19517
|
}
|
|
19374
19518
|
};
|
|
19375
19519
|
};
|
|
19376
|
-
var
|
|
19520
|
+
var handler18 = sessionRecoveryServer;
|
|
19377
19521
|
|
|
19378
19522
|
// plugins/subtasks.ts
|
|
19379
19523
|
import { promises as fs18 } from "node:fs";
|
|
@@ -20200,7 +20344,7 @@ function sleep2(ms) {
|
|
|
20200
20344
|
|
|
20201
20345
|
// plugins/subtasks.ts
|
|
20202
20346
|
init_runtime_paths();
|
|
20203
|
-
var
|
|
20347
|
+
var PLUGIN_NAME19 = "subtasks";
|
|
20204
20348
|
function getLogFile(root = process.cwd()) {
|
|
20205
20349
|
return path22.join(runtimeDir(root), "logs", "subtasks.log");
|
|
20206
20350
|
}
|
|
@@ -20280,7 +20424,7 @@ async function handleParallelCommand(raw) {
|
|
|
20280
20424
|
specs = splitDescriptions(args.description, { parentId });
|
|
20281
20425
|
}
|
|
20282
20426
|
if (specs.length === 0) {
|
|
20283
|
-
log11?.warn(`[${
|
|
20427
|
+
log11?.warn(`[${PLUGIN_NAME19}] /parallel 缺有效子任务`);
|
|
20284
20428
|
await safeReply2(ctx, "⚠ /parallel 需要至少 1 个子任务(用 ; 或换行分隔)");
|
|
20285
20429
|
return { ok: false, reason: "no_subtasks" };
|
|
20286
20430
|
}
|
|
@@ -20315,7 +20459,7 @@ async function handleParallelCommand(raw) {
|
|
|
20315
20459
|
}
|
|
20316
20460
|
const canNotice = Boolean(ctx.client && ctx.parentSessionID);
|
|
20317
20461
|
if (specs.length === 1 && rawDescription.length >= 30 && ctx.client) {
|
|
20318
|
-
log11?.info(`[${
|
|
20462
|
+
log11?.info(`[${PLUGIN_NAME19}] 触发 AI 拆分(描述长度=${rawDescription.length})`);
|
|
20319
20463
|
const dec = await decomposeTask(rawDescription, {
|
|
20320
20464
|
client: ctx.client,
|
|
20321
20465
|
directory: ctx.directory,
|
|
@@ -20336,7 +20480,7 @@ async function handleParallelCommand(raw) {
|
|
|
20336
20480
|
timeout_ms: undefined,
|
|
20337
20481
|
args: d.hintFiles && d.hintFiles.length > 0 ? { hintFiles: d.hintFiles } : undefined
|
|
20338
20482
|
}));
|
|
20339
|
-
log11?.info(`[${
|
|
20483
|
+
log11?.info(`[${PLUGIN_NAME19}] AI 拆分成功 → ${specs.length} 个子任务`);
|
|
20340
20484
|
if (canNotice) {
|
|
20341
20485
|
const lines = [
|
|
20342
20486
|
`\uD83E\uDD16 AI 已自动拆为 ${specs.length} 个并行子任务:`,
|
|
@@ -20348,7 +20492,7 @@ async function handleParallelCommand(raw) {
|
|
|
20348
20492
|
});
|
|
20349
20493
|
}
|
|
20350
20494
|
} else if (dec.reason === "llm_unavailable" || dec.reason === "parse_failed") {
|
|
20351
|
-
log11?.warn(`[${
|
|
20495
|
+
log11?.warn(`[${PLUGIN_NAME19}] AI 拆分失败(${dec.reason}),按单任务执行`);
|
|
20352
20496
|
}
|
|
20353
20497
|
}
|
|
20354
20498
|
if (canNotice) {
|
|
@@ -20427,7 +20571,7 @@ async function handleParallelCommand(raw) {
|
|
|
20427
20571
|
});
|
|
20428
20572
|
} catch (err) {
|
|
20429
20573
|
const msg = err instanceof Error ? err.message : String(err);
|
|
20430
|
-
log11?.error(`[${
|
|
20574
|
+
log11?.error(`[${PLUGIN_NAME19}] schedule 抛错`, { error: msg });
|
|
20431
20575
|
await safeReply2(ctx, `❌ 并发调度失败:${msg}`);
|
|
20432
20576
|
return { ok: false, reason: msg };
|
|
20433
20577
|
}
|
|
@@ -20445,7 +20589,7 @@ async function handleParallelCommand(raw) {
|
|
|
20445
20589
|
}
|
|
20446
20590
|
await safeReply2(ctx, summaryLines.join(`
|
|
20447
20591
|
`));
|
|
20448
|
-
log11?.info(`[${
|
|
20592
|
+
log11?.info(`[${PLUGIN_NAME19}] schedule 完成`, {
|
|
20449
20593
|
parentId,
|
|
20450
20594
|
success: result.digest.success,
|
|
20451
20595
|
failed: result.digest.failed,
|
|
@@ -20458,7 +20602,7 @@ async function handleParallelCommand(raw) {
|
|
|
20458
20602
|
try {
|
|
20459
20603
|
await ctx.onCompleted(result);
|
|
20460
20604
|
} catch (err) {
|
|
20461
|
-
log11?.warn(`[${
|
|
20605
|
+
log11?.warn(`[${PLUGIN_NAME19}] onCompleted hook 抛错(已隔离)`, {
|
|
20462
20606
|
error: err instanceof Error ? err.message : String(err)
|
|
20463
20607
|
});
|
|
20464
20608
|
}
|
|
@@ -20500,7 +20644,7 @@ async function writeLog(level, msg, data) {
|
|
|
20500
20644
|
const line = JSON.stringify({
|
|
20501
20645
|
ts: new Date().toISOString(),
|
|
20502
20646
|
level,
|
|
20503
|
-
plugin:
|
|
20647
|
+
plugin: PLUGIN_NAME19,
|
|
20504
20648
|
msg,
|
|
20505
20649
|
data
|
|
20506
20650
|
}) + `
|
|
@@ -20511,11 +20655,11 @@ async function writeLog(level, msg, data) {
|
|
|
20511
20655
|
await fs18.appendFile(logFile, line, "utf8");
|
|
20512
20656
|
} catch {}
|
|
20513
20657
|
}
|
|
20514
|
-
logLifecycle(
|
|
20658
|
+
logLifecycle(PLUGIN_NAME19, "import");
|
|
20515
20659
|
var subtasksServer = async (ctx) => {
|
|
20516
|
-
const log11 = makePluginLogger(
|
|
20660
|
+
const log11 = makePluginLogger(PLUGIN_NAME19);
|
|
20517
20661
|
const client = ctx?.client ?? undefined;
|
|
20518
|
-
logLifecycle(
|
|
20662
|
+
logLifecycle(PLUGIN_NAME19, "activate", {
|
|
20519
20663
|
directory: ctx.directory,
|
|
20520
20664
|
hasClient: Boolean(client)
|
|
20521
20665
|
});
|
|
@@ -20612,19 +20756,19 @@ var subtasksServer = async (ctx) => {
|
|
|
20612
20756
|
});
|
|
20613
20757
|
}
|
|
20614
20758
|
} catch (err) {
|
|
20615
|
-
log11.error(`[${
|
|
20759
|
+
log11.error(`[${PLUGIN_NAME19}] command.execute.before 异常(已隔离)`, {
|
|
20616
20760
|
error: err instanceof Error ? err.message : String(err)
|
|
20617
20761
|
});
|
|
20618
20762
|
}
|
|
20619
20763
|
}
|
|
20620
20764
|
};
|
|
20621
20765
|
};
|
|
20622
|
-
var
|
|
20766
|
+
var handler19 = subtasksServer;
|
|
20623
20767
|
|
|
20624
20768
|
// plugins/terminal-monitor.ts
|
|
20625
20769
|
import * as crypto5 from "node:crypto";
|
|
20626
|
-
var
|
|
20627
|
-
logLifecycle(
|
|
20770
|
+
var PLUGIN_NAME20 = "terminal-monitor";
|
|
20771
|
+
logLifecycle(PLUGIN_NAME20, "import", {});
|
|
20628
20772
|
var DEFAULT_CONFIG6 = {
|
|
20629
20773
|
minScore: 0.6,
|
|
20630
20774
|
cooldownMs: 30000,
|
|
@@ -20906,17 +21050,17 @@ function describeError(err) {
|
|
|
20906
21050
|
return String(err);
|
|
20907
21051
|
}
|
|
20908
21052
|
}
|
|
20909
|
-
var log11 = makePluginLogger(
|
|
21053
|
+
var log11 = makePluginLogger(PLUGIN_NAME20);
|
|
20910
21054
|
var lru = new FingerprintLRU2;
|
|
20911
21055
|
var _lastNotification = null;
|
|
20912
21056
|
var terminalMonitorServer = async (ctx) => {
|
|
20913
|
-
logLifecycle(
|
|
21057
|
+
logLifecycle(PLUGIN_NAME20, "activate", {
|
|
20914
21058
|
directory: ctx.directory,
|
|
20915
21059
|
triggerEventTypes: ["terminal.output", "terminal.exit"]
|
|
20916
21060
|
});
|
|
20917
21061
|
return {
|
|
20918
21062
|
event: async ({ event }) => {
|
|
20919
|
-
await safeAsync(
|
|
21063
|
+
await safeAsync(PLUGIN_NAME20, "event", async () => {
|
|
20920
21064
|
const e = event;
|
|
20921
21065
|
if (!e || typeof e.type !== "string")
|
|
20922
21066
|
return;
|
|
@@ -20930,7 +21074,7 @@ var terminalMonitorServer = async (ctx) => {
|
|
|
20930
21074
|
_lastNotification = msg;
|
|
20931
21075
|
}
|
|
20932
21076
|
});
|
|
20933
|
-
safeWriteLog(
|
|
21077
|
+
safeWriteLog(PLUGIN_NAME20, {
|
|
20934
21078
|
hook: "event",
|
|
20935
21079
|
type: e.type,
|
|
20936
21080
|
notified: r.notified,
|
|
@@ -20938,17 +21082,17 @@ var terminalMonitorServer = async (ctx) => {
|
|
|
20938
21082
|
findings: r.notification?.findings.length ?? 0
|
|
20939
21083
|
});
|
|
20940
21084
|
if (r.notified && r.notification) {
|
|
20941
|
-
log11.info(`[${
|
|
21085
|
+
log11.info(`[${PLUGIN_NAME20}] 反馈 ${r.notification.findings.length} 条 → ${r.notification.summary}`);
|
|
20942
21086
|
}
|
|
20943
21087
|
});
|
|
20944
21088
|
}
|
|
20945
21089
|
};
|
|
20946
21090
|
};
|
|
20947
|
-
var
|
|
21091
|
+
var handler20 = terminalMonitorServer;
|
|
20948
21092
|
|
|
20949
21093
|
// plugins/token-manager.ts
|
|
20950
|
-
var
|
|
20951
|
-
logLifecycle(
|
|
21094
|
+
var PLUGIN_NAME21 = "token-manager";
|
|
21095
|
+
logLifecycle(PLUGIN_NAME21, "import", {});
|
|
20952
21096
|
async function handleMessageBefore(raw, log12, defaults) {
|
|
20953
21097
|
const ctx = raw ?? {};
|
|
20954
21098
|
if (!Array.isArray(ctx.messages) || ctx.messages.length === 0)
|
|
@@ -20968,21 +21112,21 @@ async function handleMessageBefore(raw, log12, defaults) {
|
|
|
20968
21112
|
};
|
|
20969
21113
|
if (r.compressed) {
|
|
20970
21114
|
ctx.messages = r.messages;
|
|
20971
|
-
log12?.info(`[${
|
|
21115
|
+
log12?.info(`[${PLUGIN_NAME21}] 压缩 ${r.before.count}→${r.after.count} 条 / ${r.before.tokens}→${r.after.tokens} tokens`);
|
|
20972
21116
|
}
|
|
20973
21117
|
return r;
|
|
20974
21118
|
} catch (err) {
|
|
20975
|
-
log12?.warn(`[${
|
|
21119
|
+
log12?.warn(`[${PLUGIN_NAME21}] 压缩异常(已隔离)`, {
|
|
20976
21120
|
error: err instanceof Error ? err.message : String(err)
|
|
20977
21121
|
});
|
|
20978
21122
|
return null;
|
|
20979
21123
|
}
|
|
20980
21124
|
}
|
|
20981
|
-
var log12 = makePluginLogger(
|
|
21125
|
+
var log12 = makePluginLogger(PLUGIN_NAME21);
|
|
20982
21126
|
var tokenManagerServer = async (ctx) => {
|
|
20983
21127
|
const rt = loadRuntimeSync();
|
|
20984
21128
|
const threshold = rt.runtime.context.condenser_threshold_ratio;
|
|
20985
|
-
logLifecycle(
|
|
21129
|
+
logLifecycle(PLUGIN_NAME21, "activate", {
|
|
20986
21130
|
directory: ctx.directory,
|
|
20987
21131
|
threshold,
|
|
20988
21132
|
target: DEFAULT_CONDENSE.target,
|
|
@@ -20991,7 +21135,7 @@ var tokenManagerServer = async (ctx) => {
|
|
|
20991
21135
|
});
|
|
20992
21136
|
return {
|
|
20993
21137
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
20994
|
-
await safeAsync(
|
|
21138
|
+
await safeAsync(PLUGIN_NAME21, "experimental.chat.messages.transform", async () => {
|
|
20995
21139
|
const list = output.messages;
|
|
20996
21140
|
if (!Array.isArray(list) || list.length === 0)
|
|
20997
21141
|
return;
|
|
@@ -21004,7 +21148,7 @@ var tokenManagerServer = async (ctx) => {
|
|
|
21004
21148
|
const r = await handleMessageBefore({ messages: flat }, log12, { threshold });
|
|
21005
21149
|
if (!r)
|
|
21006
21150
|
return;
|
|
21007
|
-
safeWriteLog(
|
|
21151
|
+
safeWriteLog(PLUGIN_NAME21, {
|
|
21008
21152
|
hook: "experimental.chat.messages.transform",
|
|
21009
21153
|
mode: "observe-only",
|
|
21010
21154
|
before_msgs: r.before.count,
|
|
@@ -21015,13 +21159,13 @@ var tokenManagerServer = async (ctx) => {
|
|
|
21015
21159
|
reason: r.reason
|
|
21016
21160
|
});
|
|
21017
21161
|
if (r.compressed) {
|
|
21018
|
-
log12.warn(`[${
|
|
21162
|
+
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
21163
|
}
|
|
21020
21164
|
});
|
|
21021
21165
|
}
|
|
21022
21166
|
};
|
|
21023
21167
|
};
|
|
21024
|
-
var
|
|
21168
|
+
var handler21 = tokenManagerServer;
|
|
21025
21169
|
|
|
21026
21170
|
// plugins/tool-policy.ts
|
|
21027
21171
|
import { promises as fs19 } from "node:fs";
|
|
@@ -21284,9 +21428,14 @@ function checkFileAccess(acl, file, op) {
|
|
|
21284
21428
|
}
|
|
21285
21429
|
|
|
21286
21430
|
// plugins/tool-policy.ts
|
|
21287
|
-
var
|
|
21288
|
-
logLifecycle(
|
|
21431
|
+
var PLUGIN_NAME22 = "tool-policy";
|
|
21432
|
+
logLifecycle(PLUGIN_NAME22, "import", {});
|
|
21289
21433
|
var EMPTY_ACL = { whitelistMode: false };
|
|
21434
|
+
function getAgentAcl(cfg, agent) {
|
|
21435
|
+
if (!agent || !cfg.per_agent)
|
|
21436
|
+
return;
|
|
21437
|
+
return cfg.per_agent[agent];
|
|
21438
|
+
}
|
|
21290
21439
|
var SUBAGENT_APPLY_DENY_LIST = new Set([
|
|
21291
21440
|
"coder",
|
|
21292
21441
|
"planner",
|
|
@@ -21350,7 +21499,7 @@ async function loadPolicy(root = process.cwd()) {
|
|
|
21350
21499
|
function classifyToolKind(toolName) {
|
|
21351
21500
|
return classifyTool(toolName);
|
|
21352
21501
|
}
|
|
21353
|
-
async function
|
|
21502
|
+
async function resolveCurrentAgent2(client, sessionID, log13) {
|
|
21354
21503
|
try {
|
|
21355
21504
|
const sessionApi = client?.session;
|
|
21356
21505
|
if (!sessionApi || typeof sessionApi.get !== "function") {
|
|
@@ -21376,24 +21525,24 @@ async function resolveCurrentAgent(client, sessionID, log13) {
|
|
|
21376
21525
|
return;
|
|
21377
21526
|
}
|
|
21378
21527
|
}
|
|
21379
|
-
var log13 = makePluginLogger(
|
|
21528
|
+
var log13 = makePluginLogger(PLUGIN_NAME22);
|
|
21380
21529
|
var toolPolicyServer = async (ctx) => {
|
|
21381
21530
|
const directory = ctx.directory ?? process.cwd();
|
|
21382
21531
|
const cfg = await loadPolicy(directory);
|
|
21383
|
-
logLifecycle(
|
|
21532
|
+
logLifecycle(PLUGIN_NAME22, "activate", {
|
|
21384
21533
|
directory,
|
|
21385
21534
|
acl_loaded: !!cfg.acl
|
|
21386
21535
|
});
|
|
21387
21536
|
return {
|
|
21388
21537
|
"tool.execute.before": async (input, output) => {
|
|
21389
21538
|
let denied;
|
|
21390
|
-
await safeAsync(
|
|
21539
|
+
await safeAsync(PLUGIN_NAME22, "tool.execute.before", async () => {
|
|
21391
21540
|
const toolName = input.tool;
|
|
21392
21541
|
const argsObj = output.args ?? {};
|
|
21393
21542
|
const needsAgentDetection = toolName === "pending_changes" && (argsObj.action === "apply" || argsObj.action === "apply_all");
|
|
21394
21543
|
let currentAgent = undefined;
|
|
21395
21544
|
if (needsAgentDetection) {
|
|
21396
|
-
currentAgent = await
|
|
21545
|
+
currentAgent = await resolveCurrentAgent2(ctx.client, input.sessionID, log13);
|
|
21397
21546
|
}
|
|
21398
21547
|
const files = [];
|
|
21399
21548
|
const filePath = argsObj["path"] ?? argsObj["filePath"] ?? argsObj["file"];
|
|
@@ -21407,7 +21556,7 @@ var toolPolicyServer = async (ctx) => {
|
|
|
21407
21556
|
args: argsObj,
|
|
21408
21557
|
files: files.length ? files : undefined
|
|
21409
21558
|
}, cfg, currentAgent);
|
|
21410
|
-
safeWriteLog(
|
|
21559
|
+
safeWriteLog(PLUGIN_NAME22, {
|
|
21411
21560
|
hook: "tool.execute.before",
|
|
21412
21561
|
tool: toolName,
|
|
21413
21562
|
callID: input.callID,
|
|
@@ -21418,7 +21567,7 @@ var toolPolicyServer = async (ctx) => {
|
|
|
21418
21567
|
currentAgent
|
|
21419
21568
|
});
|
|
21420
21569
|
if (decision.action === "deny") {
|
|
21421
|
-
log13.warn(`[${
|
|
21570
|
+
log13.warn(`[${PLUGIN_NAME22}] DENY ${toolName}`, {
|
|
21422
21571
|
action: argsObj.action,
|
|
21423
21572
|
currentAgent,
|
|
21424
21573
|
reasons: decision.reasons,
|
|
@@ -21433,7 +21582,7 @@ var toolPolicyServer = async (ctx) => {
|
|
|
21433
21582
|
}
|
|
21434
21583
|
};
|
|
21435
21584
|
};
|
|
21436
|
-
var
|
|
21585
|
+
var handler22 = toolPolicyServer;
|
|
21437
21586
|
|
|
21438
21587
|
// plugins/update-checker.ts
|
|
21439
21588
|
import { existsSync as existsSync5 } from "node:fs";
|
|
@@ -21463,7 +21612,7 @@ import * as zlib from "node:zlib";
|
|
|
21463
21612
|
// lib/version-injected.ts
|
|
21464
21613
|
function getInjectedVersion() {
|
|
21465
21614
|
try {
|
|
21466
|
-
const v = "0.5.
|
|
21615
|
+
const v = "0.5.9";
|
|
21467
21616
|
if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
|
|
21468
21617
|
return v;
|
|
21469
21618
|
}
|
|
@@ -21966,20 +22115,20 @@ function compareOpencodeVersion(opts) {
|
|
|
21966
22115
|
}
|
|
21967
22116
|
|
|
21968
22117
|
// plugins/update-checker.ts
|
|
21969
|
-
var
|
|
22118
|
+
var PLUGIN_NAME23 = "update-checker";
|
|
21970
22119
|
var PLUGIN_VERSION = "2.0.0";
|
|
21971
|
-
logLifecycle(
|
|
22120
|
+
logLifecycle(PLUGIN_NAME23, "import", { version: PLUGIN_VERSION });
|
|
21972
22121
|
var updateCheckerServer = async (ctx) => {
|
|
21973
22122
|
const yieldResult = shouldYieldToLocalPlugin({ directory: ctx.directory });
|
|
21974
22123
|
if (yieldResult.yield) {
|
|
21975
|
-
safeWriteLog(
|
|
22124
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
21976
22125
|
level: "info",
|
|
21977
22126
|
msg: "dev_mode_yield_skip",
|
|
21978
22127
|
reason: yieldResult.reason,
|
|
21979
22128
|
markerPath: yieldResult.markerPath,
|
|
21980
22129
|
detail: formatYieldLog(yieldResult)
|
|
21981
22130
|
});
|
|
21982
|
-
logLifecycle(
|
|
22131
|
+
logLifecycle(PLUGIN_NAME23, "activate", {
|
|
21983
22132
|
yield_to_local: true,
|
|
21984
22133
|
yield_reason: yieldResult.reason,
|
|
21985
22134
|
skipped: "auto_install + background_check"
|
|
@@ -21988,7 +22137,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
21988
22137
|
}
|
|
21989
22138
|
const rt = loadRuntimeSync();
|
|
21990
22139
|
const u = rt.runtime.update;
|
|
21991
|
-
logLifecycle(
|
|
22140
|
+
logLifecycle(PLUGIN_NAME23, "activate", {
|
|
21992
22141
|
version: PLUGIN_VERSION,
|
|
21993
22142
|
auto_check_enabled: u.auto_check_enabled,
|
|
21994
22143
|
interval_hours: u.interval_hours,
|
|
@@ -22000,14 +22149,14 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22000
22149
|
repo_fallback: u.repo,
|
|
22001
22150
|
config_source: "codeforge.json"
|
|
22002
22151
|
});
|
|
22003
|
-
await safeAsync(
|
|
22152
|
+
await safeAsync(PLUGIN_NAME23, "opencode_version_check", async () => {
|
|
22004
22153
|
const compat = loadCompatibility();
|
|
22005
22154
|
const opencodeVer = detectOpencodeVersion();
|
|
22006
22155
|
const verdict = compareOpencodeVersion({
|
|
22007
22156
|
currentOpencodeVer: opencodeVer,
|
|
22008
22157
|
compat
|
|
22009
22158
|
});
|
|
22010
|
-
safeWriteLog(
|
|
22159
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22011
22160
|
level: "info",
|
|
22012
22161
|
msg: "opencode_version_check",
|
|
22013
22162
|
opencodeVer,
|
|
@@ -22024,14 +22173,14 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22024
22173
|
}
|
|
22025
22174
|
});
|
|
22026
22175
|
if (!u.auto_check_enabled) {
|
|
22027
|
-
safeWriteLog(
|
|
22176
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22028
22177
|
level: "info",
|
|
22029
22178
|
msg: "auto-check disabled (codeforge.json update.auto_check_enabled=false)"
|
|
22030
22179
|
});
|
|
22031
22180
|
return {};
|
|
22032
22181
|
}
|
|
22033
22182
|
setImmediate(() => {
|
|
22034
|
-
safeAsync(
|
|
22183
|
+
safeAsync(PLUGIN_NAME23, "checkAndMaybeUpdate", async () => {
|
|
22035
22184
|
const local = readLocalVersion();
|
|
22036
22185
|
let npmResult = null;
|
|
22037
22186
|
try {
|
|
@@ -22042,7 +22191,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22042
22191
|
timeoutMs: 5000
|
|
22043
22192
|
});
|
|
22044
22193
|
} catch (e) {
|
|
22045
|
-
safeWriteLog(
|
|
22194
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22046
22195
|
level: "warn",
|
|
22047
22196
|
msg: "npm_fetch_failed",
|
|
22048
22197
|
error: e.message
|
|
@@ -22051,7 +22200,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22051
22200
|
return;
|
|
22052
22201
|
}
|
|
22053
22202
|
if (!npmResult) {
|
|
22054
|
-
safeWriteLog(
|
|
22203
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22055
22204
|
level: "info",
|
|
22056
22205
|
msg: "npm_no_release",
|
|
22057
22206
|
package: u.package,
|
|
@@ -22060,7 +22209,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22060
22209
|
return;
|
|
22061
22210
|
}
|
|
22062
22211
|
const hasUpdate = cmpVersion(local, npmResult.version) < 0;
|
|
22063
|
-
safeWriteLog(
|
|
22212
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22064
22213
|
level: "info",
|
|
22065
22214
|
msg: "npm_check_result",
|
|
22066
22215
|
local,
|
|
@@ -22075,10 +22224,10 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22075
22224
|
更新命令:npx ${u.package} install --global`);
|
|
22076
22225
|
return;
|
|
22077
22226
|
}
|
|
22078
|
-
await safeAsync(
|
|
22227
|
+
await safeAsync(PLUGIN_NAME23, "auto_install_bundle", async () => {
|
|
22079
22228
|
const target = getOpencodeBundlePath();
|
|
22080
22229
|
if (!target) {
|
|
22081
|
-
safeWriteLog(
|
|
22230
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22082
22231
|
level: "warn",
|
|
22083
22232
|
msg: "auto_install_skip",
|
|
22084
22233
|
reason: "无法定位 opencode bundle 路径"
|
|
@@ -22097,7 +22246,7 @@ var updateCheckerServer = async (ctx) => {
|
|
|
22097
22246
|
oldVersion: local,
|
|
22098
22247
|
keepBackups: u.backup_keep
|
|
22099
22248
|
});
|
|
22100
|
-
safeWriteLog(
|
|
22249
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22101
22250
|
level: "info",
|
|
22102
22251
|
msg: "auto_install_success",
|
|
22103
22252
|
local,
|
|
@@ -22137,7 +22286,7 @@ function getOpencodeBundlePath() {
|
|
|
22137
22286
|
return candidates[0] ?? null;
|
|
22138
22287
|
}
|
|
22139
22288
|
async function fallbackToGitHubReleases(ctx, u) {
|
|
22140
|
-
await safeAsync(
|
|
22289
|
+
await safeAsync(PLUGIN_NAME23, "github_fallback", async () => {
|
|
22141
22290
|
const result = await checkUpdateOnce({
|
|
22142
22291
|
repo: u.repo,
|
|
22143
22292
|
intervalMs: u.interval_hours * 3600 * 1000
|
|
@@ -22147,7 +22296,7 @@ async function fallbackToGitHubReleases(ctx, u) {
|
|
|
22147
22296
|
}
|
|
22148
22297
|
async function reportLegacyResult(ctx, result, repo) {
|
|
22149
22298
|
if (result.error && !result.fromCache) {
|
|
22150
|
-
safeWriteLog(
|
|
22299
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22151
22300
|
level: "warn",
|
|
22152
22301
|
msg: `update check failed: ${result.error}`,
|
|
22153
22302
|
...result
|
|
@@ -22155,7 +22304,7 @@ async function reportLegacyResult(ctx, result, repo) {
|
|
|
22155
22304
|
return;
|
|
22156
22305
|
}
|
|
22157
22306
|
if (!result.hasUpdate) {
|
|
22158
|
-
safeWriteLog(
|
|
22307
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22159
22308
|
level: "info",
|
|
22160
22309
|
msg: `up-to-date (local=${result.local}, remote=${result.remote}${result.fromCache ? ", from_cache" : ""})`,
|
|
22161
22310
|
...result
|
|
@@ -22165,7 +22314,7 @@ async function reportLegacyResult(ctx, result, repo) {
|
|
|
22165
22314
|
const updateCmd = `bunx --bun github:${repo} install`;
|
|
22166
22315
|
const toast = `[CodeForge] 有新版本:${result.local} → ${result.remote}
|
|
22167
22316
|
更新命令:${updateCmd}`;
|
|
22168
|
-
safeWriteLog(
|
|
22317
|
+
safeWriteLog(PLUGIN_NAME23, {
|
|
22169
22318
|
level: "info",
|
|
22170
22319
|
msg: "new_version_available_github_fallback",
|
|
22171
22320
|
local: result.local,
|
|
@@ -22176,17 +22325,17 @@ async function reportLegacyResult(ctx, result, repo) {
|
|
|
22176
22325
|
await postToast(ctx, toast);
|
|
22177
22326
|
}
|
|
22178
22327
|
async function postToast(ctx, message) {
|
|
22179
|
-
await safeAsync(
|
|
22328
|
+
await safeAsync(PLUGIN_NAME23, "client.app.log", async () => {
|
|
22180
22329
|
await ctx.client.app.log({
|
|
22181
22330
|
body: {
|
|
22182
|
-
service:
|
|
22331
|
+
service: PLUGIN_NAME23,
|
|
22183
22332
|
level: "info",
|
|
22184
22333
|
message
|
|
22185
22334
|
}
|
|
22186
22335
|
});
|
|
22187
22336
|
});
|
|
22188
22337
|
}
|
|
22189
|
-
var
|
|
22338
|
+
var handler23 = updateCheckerServer;
|
|
22190
22339
|
|
|
22191
22340
|
// plugins/workflow-engine.ts
|
|
22192
22341
|
import * as path26 from "node:path";
|
|
@@ -22641,9 +22790,9 @@ async function runStepAutoFeedback(step, adapter) {
|
|
|
22641
22790
|
}
|
|
22642
22791
|
|
|
22643
22792
|
// plugins/workflow-engine.ts
|
|
22644
|
-
var
|
|
22645
|
-
logLifecycle(
|
|
22646
|
-
var fallbackLog2 = makePluginLogger(
|
|
22793
|
+
var PLUGIN_NAME24 = "workflow-engine";
|
|
22794
|
+
logLifecycle(PLUGIN_NAME24, "import", {});
|
|
22795
|
+
var fallbackLog2 = makePluginLogger(PLUGIN_NAME24);
|
|
22647
22796
|
var _registry = null;
|
|
22648
22797
|
async function loadRegistry(workflowsDir) {
|
|
22649
22798
|
const { loaded, failed } = await loadWorkflowsFromDir(workflowsDir);
|
|
@@ -22663,32 +22812,32 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
|
|
|
22663
22812
|
const log14 = ctx.log ?? fallbackLog2;
|
|
22664
22813
|
const command = typeof ctx.command === "string" ? ctx.command : null;
|
|
22665
22814
|
if (!command) {
|
|
22666
|
-
log14.warn(`[${
|
|
22815
|
+
log14.warn(`[${PLUGIN_NAME24}] command.invoked 缺 command 字段`, ctx);
|
|
22667
22816
|
return null;
|
|
22668
22817
|
}
|
|
22669
22818
|
const reg = await ensureRegistry(workflowsDir);
|
|
22670
22819
|
if (reg.errors.length) {
|
|
22671
|
-
log14.warn(`[${
|
|
22820
|
+
log14.warn(`[${PLUGIN_NAME24}] 有 ${reg.errors.length} 个 workflow 加载失败`, reg.errors);
|
|
22672
22821
|
}
|
|
22673
22822
|
const wf = reg.workflows.find((w) => matchesTrigger(w, command));
|
|
22674
22823
|
if (!wf) {
|
|
22675
|
-
log14.info(`[${
|
|
22824
|
+
log14.info(`[${PLUGIN_NAME24}] no workflow matches "${command}"`);
|
|
22676
22825
|
return null;
|
|
22677
22826
|
}
|
|
22678
|
-
log14.info(`[${
|
|
22827
|
+
log14.info(`[${PLUGIN_NAME24}] dispatch "${command}" → workflow "${wf.name}"`);
|
|
22679
22828
|
try {
|
|
22680
22829
|
const result = await run(wf, {
|
|
22681
22830
|
mode: ctx.adapter ? "real" : "dry_run",
|
|
22682
22831
|
autonomy: ctx.autonomy ?? "semi",
|
|
22683
22832
|
adapter: ctx.adapter
|
|
22684
22833
|
});
|
|
22685
|
-
log14.info(`[${
|
|
22834
|
+
log14.info(`[${PLUGIN_NAME24}] workflow "${wf.name}" 完成 (${result.plan.mode})`, {
|
|
22686
22835
|
steps: result.plan.steps.length,
|
|
22687
22836
|
results: result.results.length
|
|
22688
22837
|
});
|
|
22689
22838
|
return result;
|
|
22690
22839
|
} catch (err) {
|
|
22691
|
-
log14.error(`[${
|
|
22840
|
+
log14.error(`[${PLUGIN_NAME24}] workflow "${wf.name}" 执行失败`, {
|
|
22692
22841
|
error: err instanceof Error ? err.message : String(err)
|
|
22693
22842
|
});
|
|
22694
22843
|
throw err;
|
|
@@ -22697,15 +22846,15 @@ async function handleCommandInvoked(raw, workflowsDir = "workflows") {
|
|
|
22697
22846
|
var workflowEngineServer = async (ctx) => {
|
|
22698
22847
|
const directory = ctx.directory ?? process.cwd();
|
|
22699
22848
|
const workflowsDir = path26.join(directory, "workflows");
|
|
22700
|
-
ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${
|
|
22849
|
+
ensureRegistry(workflowsDir).catch((err) => fallbackLog2.warn(`[${PLUGIN_NAME24}] preload workflows failed`, {
|
|
22701
22850
|
error: err instanceof Error ? err.message : String(err)
|
|
22702
22851
|
}));
|
|
22703
|
-
logLifecycle(
|
|
22852
|
+
logLifecycle(PLUGIN_NAME24, "activate", { directory, workflowsDir });
|
|
22704
22853
|
return {
|
|
22705
22854
|
"command.execute.before": async (input, output) => {
|
|
22706
|
-
await safeAsync(
|
|
22855
|
+
await safeAsync(PLUGIN_NAME24, "command.execute.before", async () => {
|
|
22707
22856
|
const cmd = input.command.startsWith("/") ? input.command : `/${input.command}`;
|
|
22708
|
-
safeWriteLog(
|
|
22857
|
+
safeWriteLog(PLUGIN_NAME24, {
|
|
22709
22858
|
hook: "command.execute.before",
|
|
22710
22859
|
command: cmd,
|
|
22711
22860
|
sessionID: input.sessionID
|
|
@@ -22720,14 +22869,14 @@ var workflowEngineServer = async (ctx) => {
|
|
|
22720
22869
|
});
|
|
22721
22870
|
},
|
|
22722
22871
|
"chat.message": async (input, output) => {
|
|
22723
|
-
await safeAsync(
|
|
22872
|
+
await safeAsync(PLUGIN_NAME24, "chat.message", async () => {
|
|
22724
22873
|
const text = extractUserText(output).trim();
|
|
22725
22874
|
if (!text.startsWith("/"))
|
|
22726
22875
|
return;
|
|
22727
22876
|
const cmd = text.split(/\s+/)[0];
|
|
22728
22877
|
if (!cmd)
|
|
22729
22878
|
return;
|
|
22730
|
-
safeWriteLog(
|
|
22879
|
+
safeWriteLog(PLUGIN_NAME24, {
|
|
22731
22880
|
hook: "chat.message",
|
|
22732
22881
|
command: cmd,
|
|
22733
22882
|
sessionID: input.sessionID
|
|
@@ -22737,12 +22886,12 @@ var workflowEngineServer = async (ctx) => {
|
|
|
22737
22886
|
}
|
|
22738
22887
|
};
|
|
22739
22888
|
};
|
|
22740
|
-
var
|
|
22889
|
+
var handler24 = workflowEngineServer;
|
|
22741
22890
|
|
|
22742
22891
|
// plugins/session-worktree-guard.ts
|
|
22743
22892
|
import path27 from "node:path";
|
|
22744
|
-
var
|
|
22745
|
-
logLifecycle(
|
|
22893
|
+
var PLUGIN_NAME25 = "session-worktree-guard";
|
|
22894
|
+
logLifecycle(PLUGIN_NAME25, "import", {});
|
|
22746
22895
|
var WRITE_INTENT_RE = />(?!=)|\btee\b|\brm\b|\bmv\b|\bcp\b|\bmkdir\b|\btouch\b|\bchmod\b|\bchown\b|\bln\b/;
|
|
22747
22896
|
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
22897
|
var SIDE_EFFECT_TOKEN_RE = />(?!=)|\|\s*tee\b|\btee\b/;
|
|
@@ -22821,7 +22970,17 @@ function isWriteOperation(toolName, argsObj, mainRoot) {
|
|
|
22821
22970
|
return true;
|
|
22822
22971
|
return false;
|
|
22823
22972
|
}
|
|
22824
|
-
|
|
22973
|
+
function collectWritePaths(toolName, argsObj, worktreeRoot) {
|
|
22974
|
+
const out = [];
|
|
22975
|
+
const candidate = toolName === "write" || toolName === "edit" ? argsObj["filePath"] : toolName === "ast_edit" ? argsObj["target"] : undefined;
|
|
22976
|
+
if (typeof candidate !== "string" || candidate.length === 0)
|
|
22977
|
+
return out;
|
|
22978
|
+
const abs = path27.isAbsolute(candidate) ? candidate : path27.resolve(worktreeRoot, candidate);
|
|
22979
|
+
const rel = path27.relative(worktreeRoot, abs).split(path27.sep).join("/");
|
|
22980
|
+
out.push(rel);
|
|
22981
|
+
return out;
|
|
22982
|
+
}
|
|
22983
|
+
var log14 = makePluginLogger(PLUGIN_NAME25);
|
|
22825
22984
|
function resolveMainRoot2(rawDir) {
|
|
22826
22985
|
const worktreeMarker = "/.git/codeforge-worktrees/";
|
|
22827
22986
|
const idx = rawDir.indexOf(worktreeMarker);
|
|
@@ -22832,7 +22991,17 @@ function resolveMainRoot2(rawDir) {
|
|
|
22832
22991
|
}
|
|
22833
22992
|
var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
22834
22993
|
const mainRoot = resolveMainRoot2(ctx.directory ?? process.cwd());
|
|
22835
|
-
|
|
22994
|
+
let policyCfg = {};
|
|
22995
|
+
try {
|
|
22996
|
+
policyCfg = await loadPolicy(mainRoot);
|
|
22997
|
+
} catch (err) {
|
|
22998
|
+
log14.warn("loadPolicy failed (class E skipped)", {
|
|
22999
|
+
mainRoot,
|
|
23000
|
+
error: err instanceof Error ? err.message : String(err)
|
|
23001
|
+
});
|
|
23002
|
+
}
|
|
23003
|
+
const perAgentEnabled = !!policyCfg.per_agent && Object.keys(policyCfg.per_agent).length > 0;
|
|
23004
|
+
logLifecycle(PLUGIN_NAME25, "activate", {
|
|
22836
23005
|
mainRoot,
|
|
22837
23006
|
CODEFORGE_SESSION_ID: process.env["CODEFORGE_SESSION_ID"] ?? "(not set)"
|
|
22838
23007
|
});
|
|
@@ -22842,7 +23011,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22842
23011
|
if (!sessionId)
|
|
22843
23012
|
return;
|
|
22844
23013
|
let denied;
|
|
22845
|
-
await safeAsync(
|
|
23014
|
+
await safeAsync(PLUGIN_NAME25, "tool.execute.before", async () => {
|
|
22846
23015
|
const toolName = input.tool;
|
|
22847
23016
|
const argsObj = output.args ?? {};
|
|
22848
23017
|
let entry = null;
|
|
@@ -22867,7 +23036,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22867
23036
|
if (parentEntry && parentEntry.status === "active") {
|
|
22868
23037
|
entry = parentEntry;
|
|
22869
23038
|
log14.debug?.(`[child-inherit] session ${sessionId} 继承父 ${parentId} 的 worktree`, { parentSessionId: parentId, worktreePath: parentEntry.worktreePath });
|
|
22870
|
-
safeWriteLog(
|
|
23039
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22871
23040
|
hook: "tool.execute.before",
|
|
22872
23041
|
tool: toolName,
|
|
22873
23042
|
sessionID: input.sessionID,
|
|
@@ -22886,7 +23055,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22886
23055
|
try {
|
|
22887
23056
|
entry = await bindSessionWorktree({ sessionId, mainRoot });
|
|
22888
23057
|
log14.info(`[lazy-bind] auto-created worktree for session ${sessionId}`, { branch: entry.branch, path: entry.worktreePath });
|
|
22889
|
-
safeWriteLog(
|
|
23058
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22890
23059
|
hook: "tool.execute.before",
|
|
22891
23060
|
tool: toolName,
|
|
22892
23061
|
sessionID: input.sessionID,
|
|
@@ -22896,7 +23065,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22896
23065
|
});
|
|
22897
23066
|
} catch (err) {
|
|
22898
23067
|
log14.warn(`[lazy-bind] failed (落到主仓写)`, { sessionId, error: err instanceof Error ? err.message : String(err) });
|
|
22899
|
-
safeWriteLog(
|
|
23068
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22900
23069
|
hook: "tool.execute.before",
|
|
22901
23070
|
tool: toolName,
|
|
22902
23071
|
sessionID: input.sessionID,
|
|
@@ -22919,7 +23088,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22919
23088
|
action,
|
|
22920
23089
|
caller
|
|
22921
23090
|
});
|
|
22922
|
-
safeWriteLog(
|
|
23091
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22923
23092
|
hook: "tool.execute.before",
|
|
22924
23093
|
tool: toolName,
|
|
22925
23094
|
sessionID: input.sessionID,
|
|
@@ -22933,6 +23102,56 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22933
23102
|
}
|
|
22934
23103
|
}
|
|
22935
23104
|
}
|
|
23105
|
+
if (perAgentEnabled && isWriteOperation(toolName, argsObj, mainRoot)) {
|
|
23106
|
+
const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
|
|
23107
|
+
if (caller === null) {
|
|
23108
|
+
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)`;
|
|
23109
|
+
log14.warn(reason, {
|
|
23110
|
+
sessionId,
|
|
23111
|
+
tool: toolName,
|
|
23112
|
+
source: "per-agent-acl-fail-closed"
|
|
23113
|
+
});
|
|
23114
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23115
|
+
hook: "tool.execute.before",
|
|
23116
|
+
tool: toolName,
|
|
23117
|
+
sessionID: input.sessionID,
|
|
23118
|
+
action: "deny",
|
|
23119
|
+
source: "per-agent-acl-fail-closed",
|
|
23120
|
+
caller: null
|
|
23121
|
+
});
|
|
23122
|
+
denied = new DeniedError(reason);
|
|
23123
|
+
return;
|
|
23124
|
+
}
|
|
23125
|
+
const agentAcl = getAgentAcl(policyCfg, caller);
|
|
23126
|
+
if (agentAcl) {
|
|
23127
|
+
const writePaths = collectWritePaths(toolName, argsObj, worktreePath);
|
|
23128
|
+
for (const relPath of writePaths) {
|
|
23129
|
+
const dec = checkFileAccess(agentAcl, relPath, "write");
|
|
23130
|
+
if (dec.action === "deny") {
|
|
23131
|
+
const reason = `[session-worktree-guard] DENIED: agent='${caller}' 写路径不在 ACL 白名单 — ` + `${relPath} (${dec.reason})`;
|
|
23132
|
+
log14.warn(reason, {
|
|
23133
|
+
sessionId,
|
|
23134
|
+
tool: toolName,
|
|
23135
|
+
caller,
|
|
23136
|
+
path: relPath,
|
|
23137
|
+
acl_decision: dec
|
|
23138
|
+
});
|
|
23139
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23140
|
+
hook: "tool.execute.before",
|
|
23141
|
+
tool: toolName,
|
|
23142
|
+
sessionID: input.sessionID,
|
|
23143
|
+
action: "deny",
|
|
23144
|
+
source: "per-agent-acl",
|
|
23145
|
+
caller,
|
|
23146
|
+
path: relPath,
|
|
23147
|
+
acl_decision: dec
|
|
23148
|
+
});
|
|
23149
|
+
denied = new DeniedError(reason);
|
|
23150
|
+
return;
|
|
23151
|
+
}
|
|
23152
|
+
}
|
|
23153
|
+
}
|
|
23154
|
+
}
|
|
22936
23155
|
if (toolName !== "plan_read" && entry.requiredPlanId && entry.planReadOk !== true) {
|
|
22937
23156
|
let isWriteOp = WRITE_TOOLS.has(toolName);
|
|
22938
23157
|
if (!isWriteOp && toolName === "bash") {
|
|
@@ -22961,7 +23180,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22961
23180
|
requiredPlanId: entry.requiredPlanId,
|
|
22962
23181
|
inheritedFromParent: inherited ? entry.sessionId : undefined
|
|
22963
23182
|
});
|
|
22964
|
-
safeWriteLog(
|
|
23183
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22965
23184
|
hook: "tool.execute.before",
|
|
22966
23185
|
tool: toolName,
|
|
22967
23186
|
sessionID: input.sessionID,
|
|
@@ -22979,7 +23198,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22979
23198
|
const snippet = command.length > 60 ? command.slice(0, 60) + "…" : command;
|
|
22980
23199
|
const reason = `[session-worktree-guard] DENIED: bash.command 含主仓绝对路径写操作 (${snippet}),请在当前 session worktree (${worktreePath}) 内操作`;
|
|
22981
23200
|
log14.warn(reason, { sessionId, command: command.slice(0, 200) });
|
|
22982
|
-
safeWriteLog(
|
|
23201
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
22983
23202
|
hook: "tool.execute.before",
|
|
22984
23203
|
tool: toolName,
|
|
22985
23204
|
sessionID: input.sessionID,
|
|
@@ -22997,7 +23216,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
22997
23216
|
const newPath = rewritePath(filePath, mainRoot, worktreePath);
|
|
22998
23217
|
if (newPath !== null) {
|
|
22999
23218
|
log14.info(`rewrote ${toolName}.filePath: ${filePath} → ${newPath}`);
|
|
23000
|
-
safeWriteLog(
|
|
23219
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23001
23220
|
hook: "tool.execute.before",
|
|
23002
23221
|
tool: toolName,
|
|
23003
23222
|
field: "filePath",
|
|
@@ -23015,7 +23234,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23015
23234
|
const newTarget = rewritePath(target, mainRoot, worktreePath);
|
|
23016
23235
|
if (newTarget !== null) {
|
|
23017
23236
|
log14.info(`rewrote ast_edit.target: ${target} → ${newTarget}`);
|
|
23018
|
-
safeWriteLog(
|
|
23237
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23019
23238
|
hook: "tool.execute.before",
|
|
23020
23239
|
tool: toolName,
|
|
23021
23240
|
field: "target",
|
|
@@ -23031,7 +23250,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23031
23250
|
const newRoot = rewritePath(root, mainRoot, worktreePath);
|
|
23032
23251
|
if (newRoot !== null) {
|
|
23033
23252
|
log14.info(`rewrote ast_edit.root: ${root} → ${newRoot}`);
|
|
23034
|
-
safeWriteLog(
|
|
23253
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23035
23254
|
hook: "tool.execute.before",
|
|
23036
23255
|
tool: toolName,
|
|
23037
23256
|
field: "root",
|
|
@@ -23049,7 +23268,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23049
23268
|
const newWorkdir = rewritePath(workdir, mainRoot, worktreePath);
|
|
23050
23269
|
if (newWorkdir !== null) {
|
|
23051
23270
|
log14.info(`rewrote bash.workdir: ${workdir} → ${newWorkdir}`);
|
|
23052
|
-
safeWriteLog(
|
|
23271
|
+
safeWriteLog(PLUGIN_NAME25, {
|
|
23053
23272
|
hook: "tool.execute.before",
|
|
23054
23273
|
tool: toolName,
|
|
23055
23274
|
field: "workdir",
|
|
@@ -23067,19 +23286,22 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
|
|
|
23067
23286
|
}
|
|
23068
23287
|
};
|
|
23069
23288
|
};
|
|
23070
|
-
var
|
|
23289
|
+
var handler25 = sessionWorktreeGuardPlugin;
|
|
23071
23290
|
|
|
23072
23291
|
// plugins/worktree-lifecycle.ts
|
|
23073
|
-
var
|
|
23074
|
-
logLifecycle(
|
|
23292
|
+
var PLUGIN_NAME26 = "worktree-lifecycle";
|
|
23293
|
+
logLifecycle(PLUGIN_NAME26, "import", {});
|
|
23075
23294
|
var IDLE_TOAST_THROTTLE_MS = 60 * 60000;
|
|
23076
23295
|
var IDLE_TOAST_DURATION_MS = 8000;
|
|
23077
23296
|
var IDLE_TOAST_REMINDER_INTERVAL_MS = 30 * 60000;
|
|
23297
|
+
var PRUNE_INTERVAL_MS = 30 * 60000;
|
|
23078
23298
|
var lastIdleToastAt = new Map;
|
|
23079
|
-
var
|
|
23299
|
+
var pruneRunning = false;
|
|
23300
|
+
var _pruneTimer;
|
|
23301
|
+
var log15 = makePluginLogger(PLUGIN_NAME26);
|
|
23080
23302
|
var worktreeLifecyclePlugin = async (ctx) => {
|
|
23081
23303
|
const mainRoot = ctx.directory;
|
|
23082
|
-
logLifecycle(
|
|
23304
|
+
logLifecycle(PLUGIN_NAME26, "activate", {
|
|
23083
23305
|
mainRoot: mainRoot ?? "(not set)",
|
|
23084
23306
|
idle_threshold_ms: IDLE_TOAST_THROTTLE_MS
|
|
23085
23307
|
});
|
|
@@ -23088,11 +23310,11 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23088
23310
|
}
|
|
23089
23311
|
const client = ctx.client;
|
|
23090
23312
|
setImmediate(() => {
|
|
23091
|
-
safeAsync(
|
|
23313
|
+
safeAsync(PLUGIN_NAME26, "activate.pruneOrphan", async () => {
|
|
23092
23314
|
const result = await pruneOrphanWorktrees(mainRoot);
|
|
23093
23315
|
if (result.cleaned.length > 0 || result.failed.length > 0) {
|
|
23094
23316
|
log15.info(`[pruneOrphan] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
|
|
23095
|
-
safeWriteLog(
|
|
23317
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23096
23318
|
hook: "activate.pruneOrphan",
|
|
23097
23319
|
cleaned: result.cleaned,
|
|
23098
23320
|
failed: result.failed,
|
|
@@ -23101,16 +23323,46 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23101
23323
|
}
|
|
23102
23324
|
});
|
|
23103
23325
|
});
|
|
23326
|
+
if (_pruneTimer)
|
|
23327
|
+
clearInterval(_pruneTimer);
|
|
23328
|
+
_pruneTimer = setInterval(() => {
|
|
23329
|
+
if (pruneRunning) {
|
|
23330
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23331
|
+
hook: "interval.pruneOrphan",
|
|
23332
|
+
action: "skip",
|
|
23333
|
+
reason: "previous prune still running"
|
|
23334
|
+
});
|
|
23335
|
+
return;
|
|
23336
|
+
}
|
|
23337
|
+
pruneRunning = true;
|
|
23338
|
+
safeAsync(PLUGIN_NAME26, "interval.pruneOrphan", async () => {
|
|
23339
|
+
try {
|
|
23340
|
+
const result = await pruneOrphanWorktrees(mainRoot);
|
|
23341
|
+
if (result.cleaned.length > 0 || result.failed.length > 0) {
|
|
23342
|
+
log15.info(`[pruneOrphan interval] cleaned=${result.cleaned.length} failed=${result.failed.length} skipped=${result.skipped}`);
|
|
23343
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23344
|
+
hook: "interval.pruneOrphan",
|
|
23345
|
+
cleaned: result.cleaned,
|
|
23346
|
+
failed: result.failed,
|
|
23347
|
+
skipped: result.skipped
|
|
23348
|
+
});
|
|
23349
|
+
}
|
|
23350
|
+
} finally {
|
|
23351
|
+
pruneRunning = false;
|
|
23352
|
+
}
|
|
23353
|
+
});
|
|
23354
|
+
}, PRUNE_INTERVAL_MS);
|
|
23355
|
+
_pruneTimer.unref();
|
|
23104
23356
|
return {
|
|
23105
23357
|
event: async ({ event }) => {
|
|
23106
|
-
await safeAsync(
|
|
23358
|
+
await safeAsync(PLUGIN_NAME26, "event", async () => {
|
|
23107
23359
|
const ended = extractEndedSessionID(event);
|
|
23108
23360
|
if (!ended)
|
|
23109
23361
|
return;
|
|
23110
23362
|
if (ended.type === "session.deleted") {
|
|
23111
23363
|
const entry = await getSessionWorktree(ended.sessionID, mainRoot);
|
|
23112
23364
|
if (!entry || entry.status !== "active") {
|
|
23113
|
-
safeWriteLog(
|
|
23365
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23114
23366
|
hook: "event",
|
|
23115
23367
|
type: ended.type,
|
|
23116
23368
|
sessionID: ended.sessionID,
|
|
@@ -23125,7 +23377,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23125
23377
|
const fastDirty = await isWorktreeDirty(entry.worktreePath);
|
|
23126
23378
|
if (!fastDirty) {
|
|
23127
23379
|
await discardSession({ sessionId: ended.sessionID, mainRoot });
|
|
23128
|
-
safeWriteLog(
|
|
23380
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23129
23381
|
hook: "event",
|
|
23130
23382
|
type: ended.type,
|
|
23131
23383
|
sessionID: ended.sessionID,
|
|
@@ -23160,7 +23412,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23160
23412
|
}
|
|
23161
23413
|
try {
|
|
23162
23414
|
await discardSession({ sessionId: ended.sessionID, mainRoot });
|
|
23163
|
-
safeWriteLog(
|
|
23415
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23164
23416
|
hook: "event",
|
|
23165
23417
|
type: ended.type,
|
|
23166
23418
|
sessionID: ended.sessionID,
|
|
@@ -23194,7 +23446,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23194
23446
|
const idleMin = Math.round(idleMs / 60000);
|
|
23195
23447
|
const msg = `\uD83D\uDCA4 Session ${ended.sessionID.slice(0, 8)} worktree 已空闲 ${idleMin}min,` + `用 /merge 收尾或 /discard-session 放弃`;
|
|
23196
23448
|
const sent = await showToast2(client, { message: msg, variant: "default", duration: IDLE_TOAST_DURATION_MS, title: "CodeForge" }, log15);
|
|
23197
|
-
safeWriteLog(
|
|
23449
|
+
safeWriteLog(PLUGIN_NAME26, {
|
|
23198
23450
|
hook: "event",
|
|
23199
23451
|
type: ended.type,
|
|
23200
23452
|
sessionID: ended.sessionID,
|
|
@@ -23207,7 +23459,7 @@ var worktreeLifecyclePlugin = async (ctx) => {
|
|
|
23207
23459
|
}
|
|
23208
23460
|
};
|
|
23209
23461
|
};
|
|
23210
|
-
var
|
|
23462
|
+
var handler26 = worktreeLifecyclePlugin;
|
|
23211
23463
|
|
|
23212
23464
|
// src/index.ts
|
|
23213
23465
|
var PLUGIN_ID = "codeforge";
|
|
@@ -23219,26 +23471,27 @@ var HANDLERS = [
|
|
|
23219
23471
|
{ name: "auto-commit", init: handler3 },
|
|
23220
23472
|
{ name: "auto-learning", init: handler4 },
|
|
23221
23473
|
{ 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-
|
|
23474
|
+
{ name: "chat-agent-cache", init: handler6 },
|
|
23475
|
+
{ name: "codeforge-tools", init: handler8 },
|
|
23476
|
+
{ name: "discover-spec-suggest", init: handler9 },
|
|
23477
|
+
{ name: "kh-auto-context", init: handler10 },
|
|
23478
|
+
{ name: "kh-reminder", init: handler11 },
|
|
23479
|
+
{ name: "memories-context", init: handler12 },
|
|
23480
|
+
{ name: "model-fallback", init: handler13 },
|
|
23481
|
+
{ name: "parallel-tool-nudge", init: handler16 },
|
|
23482
|
+
{ name: "pwsh-utf8", init: handler17 },
|
|
23483
|
+
{ name: "session-recovery", init: handler18 },
|
|
23484
|
+
{ name: "subtask-heartbeat", init: handler14 },
|
|
23485
|
+
{ name: "subtasks", init: handler19 },
|
|
23486
|
+
{ name: "parallel-status", init: handler15 },
|
|
23487
|
+
{ name: "terminal-monitor", init: handler20 },
|
|
23488
|
+
{ name: "token-manager", init: handler21 },
|
|
23489
|
+
{ name: "tool-heartbeat", init: handler7 },
|
|
23490
|
+
{ name: "tool-policy", init: handler22 },
|
|
23491
|
+
{ name: "update-checker", init: handler23 },
|
|
23492
|
+
{ name: "workflow-engine", init: handler24 },
|
|
23493
|
+
{ name: "session-worktree-guard", init: handler25 },
|
|
23494
|
+
{ name: "worktree-lifecycle", init: handler26 }
|
|
23242
23495
|
];
|
|
23243
23496
|
function makeSerialHook(hookName, fns) {
|
|
23244
23497
|
return async (input, output) => {
|