@shgroup/dsh-serenity-hooks 1.27.2 → 1.27.4
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/dsh.plugin.json +1 -1
- package/experiments/{autotrajectory → autopilot-trajectory}/SKILL.md +24 -23
- package/experiments/{autotrajectory/scripts/autotrajectory-exp.ts → autopilot-trajectory/scripts/autopilot-trajectory.ts} +46 -46
- package/lib/autopilot-trajectory.d.ts +226 -0
- package/lib/ccc.d.ts +15 -10
- package/lib/client.js +273 -195
- package/lib/index.js +310 -146
- package/lib/{skiff-debug-vLsdk7wV.js → skiff-debug-BRlc17GU.js} +27 -0
- package/lib/tools/{autotrajectory-exp.d.ts → autopilot-trajectory.d.ts} +9 -8
- package/lib/{weixin-api-b8HLmcPE.js → weixin-api-DAugVJ-f.js} +126 -3
- package/lib/weixin-api.d.ts +91 -4
- package/lib/weixin-bridge.d.ts +13 -10
- package/lib/{weixin-route-CvAWARN3.js → weixin-route-EVu56Ytp.js} +51 -6
- package/lib/weixin-route.d.ts +28 -2
- package/package.json +3 -3
- package/lib/autotrajectory.d.ts +0 -193
package/lib/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { a as findSerenityRoot, c as matchBlacklist, d as readHandymanConfig, i as findGitRoot, l as pathInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as resolveInside, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-CfDrlfA7.js";
|
|
3
3
|
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, l as systemPromptSource, r as isSkiffSessionId, s as roleToolWhitelist } from "./skiff-role-Dw7UKCUm.js";
|
|
4
|
-
import { C as splitModel, E as skiffRoleFor, S as requireWhitelistedModel, T as writeProgress, _ as buildRoundPrompt, a as startSkiffDebugServer, b as newStopToken, c as askSkiff, d as skiffMsmGate, f as skiffSessionInfo, g as HANDYMAN_GUIDE, h as unregisterSkiffSession, l as createSkiffAgent, m as skiffTrajectoryEnabled, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffSessionSnapshot, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as getSkiffAgent, v as handymanProgressPaths, w as writeFailedStatus, x as readProgress, y as listActiveHandymen } from "./skiff-debug-
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
4
|
+
import { C as splitModel, E as skiffRoleFor, S as requireWhitelistedModel, T as writeProgress, _ as buildRoundPrompt, a as startSkiffDebugServer, b as newStopToken, c as askSkiff, d as skiffMsmGate, f as skiffSessionInfo, g as HANDYMAN_GUIDE, h as unregisterSkiffSession, l as createSkiffAgent, m as skiffTrajectoryEnabled, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffSessionSnapshot, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as getSkiffAgent, v as handymanProgressPaths, w as writeFailedStatus, x as readProgress, y as listActiveHandymen } from "./skiff-debug-BRlc17GU.js";
|
|
5
|
+
import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, h as runLocalStore, i as matchWeixinRoute, l as weixinSessionIdFor, m as readGitTrack, n as extractWeixinText, o as readWeixinSettings, p as localstorePath, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-EVu56Ytp.js";
|
|
6
|
+
import { a as sendTextMessage, i as getUpdates, n as downloadMedia, o as sendTyping, r as getConfig, s as sniffImageExt, t as TypingStatus } from "./weixin-api-DAugVJ-f.js";
|
|
7
7
|
import z from "@deepseek-ai/schemastery";
|
|
8
8
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
9
9
|
import { appendFileSync, chmodSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, utimesSync, writeFileSync } from "node:fs";
|
|
@@ -6136,36 +6136,46 @@ const skiffAdminTool = defineTool({
|
|
|
6136
6136
|
}
|
|
6137
6137
|
});
|
|
6138
6138
|
//#endregion
|
|
6139
|
-
//#region src/
|
|
6140
|
-
var
|
|
6139
|
+
//#region src/autopilot-trajectory.ts
|
|
6140
|
+
var autopilot_trajectory_exports = /* @__PURE__ */ __exportAll({
|
|
6141
|
+
AUDIT_HISTORY_MAX: () => 50,
|
|
6141
6142
|
AUTO_DIR_SUFFIX: () => AUTO_DIR_SUFFIX,
|
|
6143
|
+
BIAS_OUTPUT_MAX: () => BIAS_OUTPUT_MAX,
|
|
6144
|
+
BIAS_RUN_TIMEOUT_MS: () => BIAS_RUN_TIMEOUT_MS,
|
|
6142
6145
|
DEFAULT_AVOID_HOURS: () => DEFAULT_AVOID_HOURS,
|
|
6143
6146
|
DEFAULT_BIAS_PROVIDER: () => DEFAULT_BIAS_PROVIDER,
|
|
6147
|
+
DEFAULT_MAX_DAILY_WAKES: () => 24,
|
|
6148
|
+
LEGACY_BIAS_PROVIDER: () => LEGACY_BIAS_PROVIDER,
|
|
6144
6149
|
MOTIVATION_MARKER: () => MOTIVATION_MARKER,
|
|
6145
6150
|
TICK_MS: () => TICK_MS,
|
|
6146
6151
|
beijingHour: () => beijingHour,
|
|
6147
6152
|
buildWakeMessage: () => buildWakeMessage,
|
|
6153
|
+
collectAutopilotCccs: () => collectAutopilotCccs,
|
|
6154
|
+
dailyWakeCount: () => dailyWakeCount,
|
|
6148
6155
|
diagLive: () => diagLive,
|
|
6149
6156
|
fetchBiasContent: () => fetchBiasContent,
|
|
6150
|
-
|
|
6157
|
+
getAutopilotStatus: () => getAutopilotStatus,
|
|
6151
6158
|
inAllowedWakeWindow: () => inAllowedWakeWindow,
|
|
6152
|
-
|
|
6159
|
+
isAutopilotSession: () => isAutopilotSession,
|
|
6153
6160
|
listLiveSessions: () => listLiveSessions,
|
|
6154
|
-
|
|
6155
|
-
|
|
6161
|
+
performAutopilotWake: () => performAutopilotWake,
|
|
6162
|
+
readAutopilotSettings: () => readAutopilotSettings,
|
|
6156
6163
|
readSelfGeneratedMotivation: () => readSelfGeneratedMotivation,
|
|
6157
|
-
|
|
6158
|
-
|
|
6164
|
+
recordWake: () => recordWake,
|
|
6165
|
+
registerAutopilot: () => registerAutopilot,
|
|
6159
6166
|
resolveTargetMd: () => resolveTargetMd,
|
|
6160
|
-
shouldWake: () => shouldWake
|
|
6167
|
+
shouldWake: () => shouldWake,
|
|
6168
|
+
wakeHistoryFor: () => wakeHistoryFor
|
|
6161
6169
|
});
|
|
6162
6170
|
const PLUGIN_SOURCE$3 = {
|
|
6163
6171
|
kind: "plugin",
|
|
6164
6172
|
plugin: "dsh-serenity-hooks"
|
|
6165
6173
|
};
|
|
6166
|
-
/** 偏见内容提供者脚本缺省名(CCC
|
|
6167
|
-
const DEFAULT_BIAS_PROVIDER = "
|
|
6168
|
-
/**
|
|
6174
|
+
/** 偏见内容提供者脚本缺省名(CCC 根目录下;正式版 autopilot-bias.ts) */
|
|
6175
|
+
const DEFAULT_BIAS_PROVIDER = "autopilot-bias.ts";
|
|
6176
|
+
/** 旧默认偏见脚本名(autotrajectory 时代;未显式配置时回退——pangu 等已配置 CCC 零迁移) */
|
|
6177
|
+
const LEGACY_BIAS_PROVIDER = "autotrajectory-bias.ts";
|
|
6178
|
+
/** 自主轨迹会话目录后缀标志(--auto 保留——简短且历史会话 S060--auto 已存在) */
|
|
6169
6179
|
const AUTO_DIR_SUFFIX = "--auto";
|
|
6170
6180
|
/** 调度 tick 周期(10min——验证形态"一个就够") */
|
|
6171
6181
|
const TICK_MS = 6e5;
|
|
@@ -6176,8 +6186,14 @@ const DEFAULT_AVOID_HOURS = {
|
|
|
6176
6186
|
start: 8,
|
|
6177
6187
|
end: 18
|
|
6178
6188
|
};
|
|
6179
|
-
|
|
6180
|
-
|
|
6189
|
+
/** 偏见脚本输出上限(沙箱:截断防失控——正式版 v1.27.4) */
|
|
6190
|
+
const BIAS_OUTPUT_MAX = 8192;
|
|
6191
|
+
/** 偏见脚本执行超时(正式版沙箱;原 600s 过长阻塞 tick) */
|
|
6192
|
+
const BIAS_RUN_TIMEOUT_MS = 6e4;
|
|
6193
|
+
/** 读配置:新键 autopilotTrajectory 优先,旧键 autotrajectory 回退(正式化兼容) */
|
|
6194
|
+
function readAutopilotSettings(root) {
|
|
6195
|
+
const cfg = loadSerenityConfig(root);
|
|
6196
|
+
return cfg.autopilotTrajectory ?? cfg.autotrajectory ?? null;
|
|
6181
6197
|
}
|
|
6182
6198
|
/**
|
|
6183
6199
|
* 北京时间(UTC+8)当前小时——不依赖服务器时区(服务器可能 UTC/本地任意)。
|
|
@@ -6185,6 +6201,11 @@ function readAutoTrajectorySettings(root) {
|
|
|
6185
6201
|
function beijingHour(nowMs) {
|
|
6186
6202
|
return Math.floor((nowMs + 288e5) % 864e5 / 36e5);
|
|
6187
6203
|
}
|
|
6204
|
+
/** 当日已用唤起次数(按北京时间日;轮次预算 maxDailyWakes 判定) */
|
|
6205
|
+
function dailyWakeCount(history, nowMs) {
|
|
6206
|
+
const day = Math.floor((nowMs + 288e5) / 864e5);
|
|
6207
|
+
return history.filter((r) => Math.floor((r.time + 288e5) / 864e5) === day).length;
|
|
6208
|
+
}
|
|
6188
6209
|
/**
|
|
6189
6210
|
* 唤起窗口判定:北京时间 [avoidStart, avoidEnd) 内不唤起(缺省 8~18)。
|
|
6190
6211
|
* 单段避开(start<=end):窗口 = [0,start) ∪ [end,24);跨零点避开(start>end):窗口 = [end,start)。
|
|
@@ -6197,7 +6218,7 @@ function inAllowedWakeWindow(nowMs, avoid) {
|
|
|
6197
6218
|
return h >= end && h < start;
|
|
6198
6219
|
}
|
|
6199
6220
|
/** 标志位判定:SESSION.md 所在目录名以 --auto 结尾 → 自主轨迹形态 */
|
|
6200
|
-
function
|
|
6221
|
+
function isAutopilotSession(mdPath) {
|
|
6201
6222
|
return basename(dirname(mdPath)).endsWith(AUTO_DIR_SUFFIX);
|
|
6202
6223
|
}
|
|
6203
6224
|
/**
|
|
@@ -6213,12 +6234,14 @@ function resolveTargetMd(root, cfg) {
|
|
|
6213
6234
|
return existsSync(md) ? md : null;
|
|
6214
6235
|
}
|
|
6215
6236
|
/**
|
|
6216
|
-
* 唤起条件(纯逻辑,可测):enabled + 未在运行 + 目录标志 + mtime 超间隔 +
|
|
6237
|
+
* 唤起条件(纯逻辑,可测):enabled + 未在运行 + 目录标志 + mtime 超间隔 + 窗口允许 + 预算未超。
|
|
6217
6238
|
*/
|
|
6218
|
-
function shouldWake(settings, mdPath, nowMs, running) {
|
|
6239
|
+
function shouldWake(settings, mdPath, nowMs, running, history = []) {
|
|
6219
6240
|
if (!settings?.enabled || running) return false;
|
|
6220
|
-
if (!mdPath || !
|
|
6241
|
+
if (!mdPath || !isAutopilotSession(mdPath)) return false;
|
|
6221
6242
|
if (!inAllowedWakeWindow(nowMs, settings.avoidWakeHours)) return false;
|
|
6243
|
+
const maxDaily = Math.max(1, settings.maxDailyWakes ?? 24);
|
|
6244
|
+
if (dailyWakeCount(history, nowMs) >= maxDaily) return false;
|
|
6222
6245
|
try {
|
|
6223
6246
|
const mtime = statSync(mdPath).mtimeMs;
|
|
6224
6247
|
const hours = Math.max(1, settings.intervalHours ?? 12);
|
|
@@ -6241,9 +6264,10 @@ function readSelfGeneratedMotivation(mdPath) {
|
|
|
6241
6264
|
}
|
|
6242
6265
|
}
|
|
6243
6266
|
/**
|
|
6244
|
-
* 偏见内容:直接运行 CCC 根目录下偏见提供者脚本(biasProvider,缺省
|
|
6267
|
+
* 偏见内容:直接运行 CCC 根目录下偏见提供者脚本(biasProvider,缺省 autopilot-bias.ts;
|
|
6268
|
+
* 未显式配置且新默认缺失 → 回退旧默认 autotrajectory-bias.ts——pangu 兼容)→ stdout。
|
|
6245
6269
|
* 脚本缺失 → 返回 { text: null, error: 提示实现 }(唤起侧报错要求实现,不静默跳过)。
|
|
6246
|
-
* 路径逃逸校验(resolveInside);bun 优先,node
|
|
6270
|
+
* 路径逃逸校验(resolveInside);bun 优先,node 兜底;**60s 超时 + 8KB 输出截断(沙箱)**。
|
|
6247
6271
|
*/
|
|
6248
6272
|
async function fetchBiasContent(root, providerRel) {
|
|
6249
6273
|
let scriptAbs;
|
|
@@ -6255,15 +6279,19 @@ async function fetchBiasContent(root, providerRel) {
|
|
|
6255
6279
|
error: `biasProvider 路径逃逸(须在 CCC 根内): ${providerRel}`
|
|
6256
6280
|
};
|
|
6257
6281
|
}
|
|
6282
|
+
if (!existsSync(scriptAbs) && providerRel === "autopilot-bias.ts") {
|
|
6283
|
+
const legacy = resolveInside(root, LEGACY_BIAS_PROVIDER);
|
|
6284
|
+
if (existsSync(legacy)) scriptAbs = legacy;
|
|
6285
|
+
}
|
|
6258
6286
|
if (!existsSync(scriptAbs)) return {
|
|
6259
6287
|
text: null,
|
|
6260
|
-
error: `请在 CCC 根目录实现偏见内容提供者脚本: ${providerRel}
|
|
6288
|
+
error: `请在 CCC 根目录实现偏见内容提供者脚本: ${providerRel}(或旧默认 ${LEGACY_BIAS_PROVIDER};stdout 输出偏见内容一行;acc_msm exec autopilot-trajectory init 可生成模板)`
|
|
6261
6289
|
};
|
|
6262
6290
|
const runs = [["bun", [scriptAbs]], [process.execPath, [scriptAbs]]];
|
|
6263
6291
|
for (const [cmd, args] of runs) try {
|
|
6264
6292
|
const r = spawnSync(cmd, args, {
|
|
6265
6293
|
encoding: "utf-8",
|
|
6266
|
-
timeout:
|
|
6294
|
+
timeout: BIAS_RUN_TIMEOUT_MS,
|
|
6267
6295
|
stdio: [
|
|
6268
6296
|
"ignore",
|
|
6269
6297
|
"pipe",
|
|
@@ -6271,7 +6299,7 @@ async function fetchBiasContent(root, providerRel) {
|
|
|
6271
6299
|
]
|
|
6272
6300
|
});
|
|
6273
6301
|
if (r.status === 0) return {
|
|
6274
|
-
text: (r.stdout ?? "").trim() || null,
|
|
6302
|
+
text: (r.stdout ?? "").trim().slice(0, 8192) || null,
|
|
6275
6303
|
error: null
|
|
6276
6304
|
};
|
|
6277
6305
|
if (r.error?.code === "ENOENT") continue;
|
|
@@ -6294,24 +6322,43 @@ function buildWakeMessage(opts) {
|
|
|
6294
6322
|
lines.push(`[轨迹焦点] ${opts.topPrompt}`);
|
|
6295
6323
|
lines.push("");
|
|
6296
6324
|
}
|
|
6297
|
-
lines.push(`[
|
|
6325
|
+
lines.push(`[Autopilot Trajectory 唤起] — 距上次轨迹活动已满 ${opts.intervalHours} 小时,自动继续。`, "", `身份锚定:继续 ${opts.sessionName} 的 trajectory(SESSION.md: ${opts.mdPath})。`, "先验偏见:");
|
|
6298
6326
|
if (opts.motivation) lines.push(` · 自生动机:${opts.motivation}`);
|
|
6299
6327
|
if (opts.biasContent) lines.push(` · 偏见内容:${opts.biasContent}`);
|
|
6300
6328
|
if (!opts.motivation && !opts.biasContent) lines.push(" · (无——本轮纯自主探索)");
|
|
6301
6329
|
lines.push("", "任务:执行一轮自主认知(探索/反事实检验),把产出写入 SESSION.md", "「自主探索日志」段,并预写「下一轮动机」段。完成后自然结束。");
|
|
6302
6330
|
return lines.join("\n");
|
|
6303
6331
|
}
|
|
6304
|
-
|
|
6332
|
+
/** 审计历史(root → 最近唤醒记录;进程生命周期;上限 AUDIT_HISTORY_MAX/CCC) */
|
|
6333
|
+
const wakeHistory = /* @__PURE__ */ new Map();
|
|
6334
|
+
/** 追加审计记录(ring 截断) */
|
|
6335
|
+
function recordWake(root, rec) {
|
|
6336
|
+
const list = wakeHistory.get(root) ?? [];
|
|
6337
|
+
list.push(rec);
|
|
6338
|
+
if (list.length > 50) list.splice(0, list.length - 50);
|
|
6339
|
+
wakeHistory.set(root, list);
|
|
6340
|
+
}
|
|
6341
|
+
/** 读审计历史(root 的最近记录;无 → []) */
|
|
6342
|
+
function wakeHistoryFor(root) {
|
|
6343
|
+
return [...wakeHistory.get(root) ?? []];
|
|
6344
|
+
}
|
|
6345
|
+
/**
|
|
6346
|
+
* 执行一次唤起(时钟 tick 与面板「立即唤起」共用):
|
|
6347
|
+
* - force=false(时钟):完整校验——enabled / 目标命中 / --auto 标志 / 窗口 / 间隔 / 预算 / 偏见脚本
|
|
6348
|
+
* - force=true(手动调试,用户在场):跳过窗口/间隔/预算,仍校验 enabled / 目标命中 / --auto / 偏见脚本
|
|
6349
|
+
* 成功 → 注入前台会话(agent.steer,用户可见);返回结果供调用方(tick 打日志 / 面板显示 / 审计)。
|
|
6350
|
+
*/
|
|
6351
|
+
async function performAutopilotWake(ctx, root, settings, opts = {}) {
|
|
6305
6352
|
if (!settings?.enabled) return {
|
|
6306
6353
|
ok: false,
|
|
6307
|
-
detail: "
|
|
6354
|
+
detail: "Autopilot Trajectory 未启用(enabled=false)"
|
|
6308
6355
|
};
|
|
6309
6356
|
const mdPath = resolveTargetMd(root, settings);
|
|
6310
6357
|
if (!mdPath) return {
|
|
6311
6358
|
ok: false,
|
|
6312
6359
|
detail: "目标会话未命中(session 未配置或 AGENT_SESSIONS 无匹配)"
|
|
6313
6360
|
};
|
|
6314
|
-
if (!
|
|
6361
|
+
if (!isAutopilotSession(mdPath)) return {
|
|
6315
6362
|
ok: false,
|
|
6316
6363
|
detail: "目标会话目录无 --auto 标志(AGENT_SESSIONS/<date>--<desc>--auto/)"
|
|
6317
6364
|
};
|
|
@@ -6340,7 +6387,7 @@ async function performAutoTrajectoryWake(ctx, root, settings, opts = {}) {
|
|
|
6340
6387
|
ok: false,
|
|
6341
6388
|
detail: `目标会话 agent 不可得——${diagnoseTargetUnavailable(ctx, mdPath) ?? "目标会话未打开或命名未生效"}`
|
|
6342
6389
|
};
|
|
6343
|
-
const provider = settings.biasProvider?.trim() || "
|
|
6390
|
+
const provider = settings.biasProvider?.trim() || "autopilot-bias.ts";
|
|
6344
6391
|
const biasRes = await fetchBiasContent(root, provider);
|
|
6345
6392
|
if (biasRes.error) return {
|
|
6346
6393
|
ok: false,
|
|
@@ -6368,54 +6415,62 @@ async function performAutoTrajectoryWake(ctx, root, settings, opts = {}) {
|
|
|
6368
6415
|
};
|
|
6369
6416
|
}
|
|
6370
6417
|
/**
|
|
6371
|
-
* 装配(index.ts apply 调用)。时钟唤起(v1.26.14
|
|
6418
|
+
* 装配(index.ts apply 调用)。时钟唤起(v1.26.14 修复 + v1.27.4 多 CCC 独立):
|
|
6372
6419
|
*
|
|
6373
|
-
*
|
|
6374
|
-
*
|
|
6375
|
-
* 手动唤起走 HTTP 端点(点击时重新解析 root)→ 正常。
|
|
6420
|
+
* v1.26.14 根因:旧实现启动时一次性解析 root——web 进程启动时 live 会话往往为空 → 定时器不启动。
|
|
6421
|
+
* 修复:① 每次 tick 动态解析 root+settings ② 监听 session/created 启动定时器 ③ 优先实验 CCC。
|
|
6376
6422
|
*
|
|
6377
|
-
*
|
|
6378
|
-
*
|
|
6379
|
-
*
|
|
6380
|
-
*
|
|
6381
|
-
*
|
|
6382
|
-
* 进程 cwd/任一 live(多 CCC 同实例时时钟唤起绑定实验 CCC,而非当前维护会话 CCC)。
|
|
6423
|
+
* **v1.27.4 多 CCC 独立(用户"4个CCC能各自有autotrajectory吗")**:
|
|
6424
|
+
* - 单定时器保留(TICK_MS 10min),每次 tick **遍历所有 live+enabled CCC**(collectAutopilotCccs)
|
|
6425
|
+
* 各自评估 shouldWake + 各自唤起——每 CCC 的 interval/session/bias/topPrompt/窗口独立
|
|
6426
|
+
* - `running` 守卫 **per-CCC**(不同 CCC 唤起互不阻塞)
|
|
6427
|
+
* - **全局串行化**(wakeChain:同 tick 多 CCC 到点 → 依次唤起,防模型并发挤兑)
|
|
6383
6428
|
*
|
|
6384
|
-
*
|
|
6429
|
+
* 零资源占用语义保留:无 enabled CCC → tick 内直接 return(定时器存在但每 10min 一次空检查,
|
|
6385
6430
|
* unref 不阻塞进程退出)。
|
|
6386
6431
|
*/
|
|
6387
|
-
function
|
|
6432
|
+
function registerAutopilot(ctx) {
|
|
6388
6433
|
let timer = null;
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
const resolveRoot = () => resolveAutoTrajectoryCcc(ctx) ?? resolveAutoTrajectoryRoot(ctx);
|
|
6434
|
+
const runningByRoot = /* @__PURE__ */ new Map();
|
|
6435
|
+
let wakeChain = Promise.resolve();
|
|
6392
6436
|
const tick = () => {
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
const
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6437
|
+
const roots = collectAutopilotCccs(ctx);
|
|
6438
|
+
if (roots.length === 0) return;
|
|
6439
|
+
for (const root of roots) {
|
|
6440
|
+
if (runningByRoot.get(root)) continue;
|
|
6441
|
+
const settings = readAutopilotSettings(root);
|
|
6442
|
+
if (!settings?.enabled) continue;
|
|
6443
|
+
if (!shouldWake(settings, resolveTargetMd(root, settings), Date.now(), false, wakeHistoryFor(root))) continue;
|
|
6444
|
+
runningByRoot.set(root, true);
|
|
6445
|
+
wakeChain = wakeChain.then(async () => {
|
|
6446
|
+
try {
|
|
6447
|
+
const res = await performAutopilotWake(ctx, root, settings, { force: false });
|
|
6448
|
+
recordWake(root, {
|
|
6449
|
+
time: Date.now(),
|
|
6450
|
+
ok: res.ok,
|
|
6451
|
+
detail: res.detail
|
|
6452
|
+
});
|
|
6453
|
+
if (res.ok) console.log(`[serenity-hooks] ✓ Autopilot Trajectory 唤起(${res.detail})`);
|
|
6454
|
+
else console.warn(`[serenity-hooks] ✗ Autopilot Trajectory 唤起跳过:${res.detail}`);
|
|
6455
|
+
} catch (err) {
|
|
6456
|
+
recordWake(root, {
|
|
6457
|
+
time: Date.now(),
|
|
6458
|
+
ok: false,
|
|
6459
|
+
detail: `唤起异常: ${String(err?.message ?? err)}`
|
|
6460
|
+
});
|
|
6461
|
+
console.warn(`[serenity-hooks] ✗ Autopilot Trajectory 唤起失败: ${String(err?.message ?? err)}`);
|
|
6462
|
+
} finally {
|
|
6463
|
+
runningByRoot.set(root, false);
|
|
6464
|
+
}
|
|
6465
|
+
});
|
|
6466
|
+
}
|
|
6410
6467
|
};
|
|
6411
6468
|
const startTimer = () => {
|
|
6412
6469
|
if (timer) return;
|
|
6413
|
-
|
|
6414
|
-
const settings = root ? readAutoTrajectorySettings(root) : null;
|
|
6415
|
-
if (!settings?.enabled) return;
|
|
6470
|
+
if (collectAutopilotCccs(ctx).length === 0) return;
|
|
6416
6471
|
timer = setInterval(tick, TICK_MS);
|
|
6417
6472
|
timer.unref();
|
|
6418
|
-
console.log(`[serenity-hooks] ✓
|
|
6473
|
+
console.log(`[serenity-hooks] ✓ Autopilot Trajectory 定时器启动(${collectAutopilotCccs(ctx).length} 个 CCC 启用)`);
|
|
6419
6474
|
tick();
|
|
6420
6475
|
};
|
|
6421
6476
|
startTimer();
|
|
@@ -6461,7 +6516,7 @@ function readSessionTitle(session) {
|
|
|
6461
6516
|
return null;
|
|
6462
6517
|
}
|
|
6463
6518
|
/**
|
|
6464
|
-
* 诊断:目标会话 agent 为何不可得(供
|
|
6519
|
+
* 诊断:目标会话 agent 为何不可得(供 performAutopilotWake 失败信息——
|
|
6465
6520
|
* 区分"live 会话里无匹配" vs "匹配但 agent 未加载")。返回诊断文本(无则 null)。
|
|
6466
6521
|
*/
|
|
6467
6522
|
function diagnoseTargetUnavailable(ctx, mdPath) {
|
|
@@ -6488,8 +6543,8 @@ function diagnoseTargetUnavailable(ctx, mdPath) {
|
|
|
6488
6543
|
if (sameCccTitles.length > 0) return `目标 CCC 内 live 会话标题: [${sameCccTitles.join(", ")}]——均不匹配 ${sid}(目标会话未在 WebUI 打开,或命名未生效)`;
|
|
6489
6544
|
return `目标 CCC 内无 live 会话(先在 WebUI 打开 ${sid} 会话后重试)`;
|
|
6490
6545
|
}
|
|
6491
|
-
/**
|
|
6492
|
-
function
|
|
6546
|
+
/** Autopilot 绑定的回退 CCC 根:进程 cwd 上溯 .serenity 优先,回退任一 live 会话 root */
|
|
6547
|
+
function resolveAutopilotRoot(ctx) {
|
|
6493
6548
|
const fromCwd = findSerenityRoot(process.cwd());
|
|
6494
6549
|
if (fromCwd) return fromCwd;
|
|
6495
6550
|
try {
|
|
@@ -6501,14 +6556,15 @@ function resolveAutoTrajectoryRoot(ctx) {
|
|
|
6501
6556
|
} catch {}
|
|
6502
6557
|
return null;
|
|
6503
6558
|
}
|
|
6504
|
-
function
|
|
6505
|
-
const cfg =
|
|
6559
|
+
function getAutopilotStatus(root) {
|
|
6560
|
+
const cfg = readAutopilotSettings(root);
|
|
6506
6561
|
const now = Date.now();
|
|
6507
6562
|
const base = {
|
|
6508
6563
|
configured: cfg !== null,
|
|
6509
6564
|
enabled: cfg?.enabled ?? false,
|
|
6510
6565
|
intervalHours: Math.max(1, cfg?.intervalHours ?? 12),
|
|
6511
|
-
|
|
6566
|
+
maxDailyWakes: Math.max(1, cfg?.maxDailyWakes ?? 24),
|
|
6567
|
+
biasProvider: cfg?.biasProvider?.trim() || "autopilot-bias.ts",
|
|
6512
6568
|
topPrompt: cfg?.topPrompt?.trim() || null,
|
|
6513
6569
|
session: cfg?.session ?? null,
|
|
6514
6570
|
avoidWakeHours: {
|
|
@@ -6516,7 +6572,8 @@ function getAutoTrajectoryStatus(root) {
|
|
|
6516
6572
|
end: cfg?.avoidWakeHours?.end ?? DEFAULT_AVOID_HOURS.end
|
|
6517
6573
|
},
|
|
6518
6574
|
beijingHour: beijingHour(now),
|
|
6519
|
-
windowAllowed: inAllowedWakeWindow(now, cfg?.avoidWakeHours)
|
|
6575
|
+
windowAllowed: inAllowedWakeWindow(now, cfg?.avoidWakeHours),
|
|
6576
|
+
recentWakes: wakeHistoryFor(root).slice(-10).reverse()
|
|
6520
6577
|
};
|
|
6521
6578
|
if (!cfg) return {
|
|
6522
6579
|
...base,
|
|
@@ -6535,9 +6592,9 @@ function getAutoTrajectoryStatus(root) {
|
|
|
6535
6592
|
...base,
|
|
6536
6593
|
target: {
|
|
6537
6594
|
dirName: basename(dirname(mdPath)),
|
|
6538
|
-
autoFlag:
|
|
6595
|
+
autoFlag: isAutopilotSession(mdPath),
|
|
6539
6596
|
idleHours: Math.max(0, idleHours),
|
|
6540
|
-
wakeable: shouldWake(cfg, mdPath, now, false)
|
|
6597
|
+
wakeable: shouldWake(cfg, mdPath, now, false, wakeHistoryFor(root))
|
|
6541
6598
|
}
|
|
6542
6599
|
};
|
|
6543
6600
|
}
|
|
@@ -6559,41 +6616,40 @@ function listLiveSessions(ctx) {
|
|
|
6559
6616
|
return out;
|
|
6560
6617
|
}
|
|
6561
6618
|
/**
|
|
6562
|
-
*
|
|
6563
|
-
*
|
|
6564
|
-
*
|
|
6565
|
-
* 用户实验 CCC(pangu)检测不到)。优先级:已启用 → 已配置(未启用)→ null。
|
|
6619
|
+
* 收集所有「配置了 Autopilot Trajectory 的 live CCC」——**v1.27.4 多 CCC 独立**:
|
|
6620
|
+
* 返回 enabled 的 CCC 列表(时钟 tick 遍历目标);无 enabled → []。
|
|
6621
|
+
* (原 resolveAutoTrajectoryCcc 单目标语义被 collect 取代——面板/诊断用 enableOnly=false 取全量)
|
|
6566
6622
|
*/
|
|
6567
|
-
function
|
|
6623
|
+
function collectAutopilotCccs(ctx, opts = {}) {
|
|
6568
6624
|
const enabled = [];
|
|
6569
6625
|
const configured = [];
|
|
6570
6626
|
for (const s of listLiveSessions(ctx)) {
|
|
6571
6627
|
if (!s.cccRoot) continue;
|
|
6572
|
-
const cfg =
|
|
6628
|
+
const cfg = readAutopilotSettings(s.cccRoot);
|
|
6573
6629
|
if (!cfg) continue;
|
|
6574
6630
|
if (cfg.enabled) {
|
|
6575
6631
|
if (!enabled.includes(s.cccRoot)) enabled.push(s.cccRoot);
|
|
6576
6632
|
} else if (!configured.includes(s.cccRoot)) configured.push(s.cccRoot);
|
|
6577
6633
|
}
|
|
6578
|
-
return enabled
|
|
6634
|
+
return opts.includeDisabled ? [...enabled, ...configured] : enabled;
|
|
6579
6635
|
}
|
|
6580
6636
|
function diagLive(ctx) {
|
|
6581
6637
|
const liveSessions = listLiveSessions(ctx);
|
|
6582
6638
|
const processCwd = process.cwd();
|
|
6583
6639
|
const processCcc = findSerenityRoot(processCwd);
|
|
6584
|
-
const panelResolved =
|
|
6585
|
-
const
|
|
6640
|
+
const panelResolved = collectAutopilotCccs(ctx)[0] ?? resolveAutopilotRoot(ctx);
|
|
6641
|
+
const autopilotCccs = [];
|
|
6586
6642
|
const seen = /* @__PURE__ */ new Set();
|
|
6587
6643
|
for (const s of liveSessions) {
|
|
6588
6644
|
if (!s.cccRoot || seen.has(s.cccRoot)) continue;
|
|
6589
|
-
const cfg =
|
|
6645
|
+
const cfg = readAutopilotSettings(s.cccRoot);
|
|
6590
6646
|
if (!cfg) continue;
|
|
6591
6647
|
seen.add(s.cccRoot);
|
|
6592
|
-
const status =
|
|
6648
|
+
const status = getAutopilotStatus(s.cccRoot);
|
|
6593
6649
|
const mdPath = resolveTargetMd(s.cccRoot, cfg);
|
|
6594
6650
|
const agentResolved = mdPath ? resolveTargetAgent(ctx, mdPath) !== null : false;
|
|
6595
6651
|
const agentDiagnosis = mdPath && !agentResolved ? diagnoseTargetUnavailable(ctx, mdPath) : null;
|
|
6596
|
-
|
|
6652
|
+
autopilotCccs.push({
|
|
6597
6653
|
root: s.cccRoot,
|
|
6598
6654
|
enabled: status.enabled,
|
|
6599
6655
|
session: status.session,
|
|
@@ -6606,17 +6662,18 @@ function diagLive(ctx) {
|
|
|
6606
6662
|
processCwd,
|
|
6607
6663
|
processCcc,
|
|
6608
6664
|
liveSessions,
|
|
6609
|
-
|
|
6665
|
+
autopilotCccs,
|
|
6610
6666
|
panelResolved
|
|
6611
6667
|
};
|
|
6612
6668
|
}
|
|
6613
6669
|
//#endregion
|
|
6614
|
-
//#region src/tools/
|
|
6670
|
+
//#region src/tools/autopilot-trajectory.ts
|
|
6615
6671
|
/**
|
|
6616
|
-
*
|
|
6672
|
+
* autopilot-trajectory.ts — Autopilot Trajectory 一站式管理工具(v1.26.12 实验
|
|
6673
|
+
* autotrajectory-exp → v1.27.4 正式化改名)
|
|
6617
6674
|
*
|
|
6618
|
-
* 定位:dsp **只提供工具与知识**,不向 CCC
|
|
6619
|
-
*
|
|
6675
|
+
* 定位:dsp **只提供工具与知识**,不向 CCC 自动安装任何东西(机制是 CCC 的自选动作)——
|
|
6676
|
+
* agent 调本工具(doc/全报告)即懂机制,init/random/check 辅助,
|
|
6620
6677
|
* 实际执行(写配置/写偏见脚本/标记会话)由 CCC 自己决定、自己用现有工具完成。
|
|
6621
6678
|
*
|
|
6622
6679
|
* 实现:薄封装——exec 包内静态脚本(npm files 含 experiments/),脚本是单一真相源。
|
|
@@ -6625,24 +6682,24 @@ function diagLive(ctx) {
|
|
|
6625
6682
|
* 直接读 live 会话/标题/agent(脚本 diag 看不到运行时),输出实例级诊断报告。
|
|
6626
6683
|
*/
|
|
6627
6684
|
/**
|
|
6628
|
-
*
|
|
6685
|
+
* 定位包内脚本(npm files 分发 experiments/autopilot-trajectory/)。
|
|
6629
6686
|
* 布局差异:tsdown bundle 后 import.meta.url 指向 lib/index.js(lib → 包根 1 层);
|
|
6630
6687
|
* vitest 源码直跑时指向 src/tools/x.ts(src/tools → 包根 2 层)——逐级上溯查找,
|
|
6631
|
-
* 两种布局都稳(找到 experiments/
|
|
6688
|
+
* 两种布局都稳(找到 experiments/autopilot-trajectory/scripts/autopilot-trajectory.ts 即止)。
|
|
6632
6689
|
*/
|
|
6633
6690
|
function findExpScript(startDir) {
|
|
6634
6691
|
let cur = startDir;
|
|
6635
6692
|
while (true) {
|
|
6636
|
-
const cand = join(cur, "experiments", "
|
|
6693
|
+
const cand = join(cur, "experiments", "autopilot-trajectory", "scripts", "autopilot-trajectory.ts");
|
|
6637
6694
|
if (existsSync(cand)) return cand;
|
|
6638
6695
|
const parent = dirname(cur);
|
|
6639
6696
|
if (parent === cur) return null;
|
|
6640
6697
|
cur = parent;
|
|
6641
6698
|
}
|
|
6642
6699
|
}
|
|
6643
|
-
/**
|
|
6700
|
+
/** 包内脚本(上溯查找;找不到 → execute 报错提示包完整性) */
|
|
6644
6701
|
const EXP_SCRIPT = findExpScript(dirname(fileURLToPath(import.meta.url)));
|
|
6645
|
-
const
|
|
6702
|
+
const AUTOPILOT_ACTIONS = [
|
|
6646
6703
|
"all",
|
|
6647
6704
|
"init",
|
|
6648
6705
|
"random",
|
|
@@ -6665,31 +6722,31 @@ function renderText(value) {
|
|
|
6665
6722
|
/** 渲染 diag-live 报告为可读文本(EAP:结构清晰、命中点显式) */
|
|
6666
6723
|
function renderDiagLive(r) {
|
|
6667
6724
|
const lines = [
|
|
6668
|
-
"═══
|
|
6725
|
+
"═══ Autopilot Trajectory 进程内诊断(diag-live,v1.26.14)═══",
|
|
6669
6726
|
`进程 cwd: ${r.processCwd}(CCC: ${r.processCcc ?? "无 .serenity"})`,
|
|
6670
|
-
`面板解析(无参 GET 目标): ${r.panelResolved ?? "无配置
|
|
6727
|
+
`面板解析(无参 GET 目标): ${r.panelResolved ?? "无配置 Autopilot 的 live CCC"}`,
|
|
6671
6728
|
"",
|
|
6672
6729
|
`── live 会话(${r.liveSessions.length})──`
|
|
6673
6730
|
];
|
|
6674
6731
|
if (r.liveSessions.length === 0) lines.push(" (无 live 会话——WebUI 未打开任何会话;定时器绑定依赖 live 会话 cwd)");
|
|
6675
6732
|
for (const s of r.liveSessions) lines.push(` · ${s.id}${s.title ? ` [${s.title}]` : " [无标题]"}${s.cwd ? ` cwd=${s.cwd}` : " cwd=无"}${s.cccRoot ? ` → CCC ${s.cccRoot}` : ""}`);
|
|
6676
|
-
lines.push("", `── 配置了
|
|
6677
|
-
if (r.
|
|
6678
|
-
for (const c of r.
|
|
6733
|
+
lines.push("", `── 配置了 Autopilot Trajectory 的 CCC(${r.autopilotCccs.length})──`);
|
|
6734
|
+
if (r.autopilotCccs.length === 0) lines.push(" (无——live 会话中无任何 CCC 配置 autopilotTrajectory)");
|
|
6735
|
+
for (const c of r.autopilotCccs) {
|
|
6679
6736
|
lines.push(` · ${c.root}(enabled=${c.enabled})`);
|
|
6680
6737
|
lines.push(` session=${c.session ?? "未配置"} | 目标=${c.target ? `${c.target.dirName}(--auto ${c.target.autoFlag ? "✓" : "✗"},空闲 ${c.target.idleHours.toFixed(1)}h)` : "未命中"}`);
|
|
6681
6738
|
lines.push(` agent 定位: ${c.agentResolved ? "✓ 可注入" : `✗ 不可得——${c.agentDiagnosis ?? "未知"}`}`);
|
|
6682
6739
|
}
|
|
6683
6740
|
return lines.join("\n");
|
|
6684
6741
|
}
|
|
6685
|
-
/** 创建
|
|
6686
|
-
function
|
|
6742
|
+
/** 创建 autopilot-trajectory 工具(闭包捕获 ctx → diag-live 进程内诊断;v1.26.14 + v1.27.4 改名) */
|
|
6743
|
+
function createAutopilotTool(ctx) {
|
|
6687
6744
|
return defineTool({
|
|
6688
|
-
name: "
|
|
6689
|
-
description: "
|
|
6745
|
+
name: "autopilot-trajectory",
|
|
6746
|
+
description: "Autopilot Trajectory(自动巡航轨迹,正式版 v1.27.4;前身 autotrajectory 实验)一站式管理。无参/action=all:全报告(背景摘要 + 就绪检查 + 状态 + 下一步)——CCC agent 看一次即完整理解并知道怎么开始;init:初始化辅助(写配置 + 生成偏见提供者脚本模板);random:运行偏见提供者脚本输出当前偏见内容;diag:唤起条件链诊断(--ccc <path> 指定 CCC,无参递归扫描 /home/yh 两层;逐条件输出 + 阻断点 + 修复建议);diag-live:**进程内诊断**(live 会话清单/标题/agent 定位/面板解析目标;排查\"面板检测不到实验 CCC\");doc:定义全文;check/status/guide:单项。**topPrompt(轨迹焦点)**:CCC 定义时自己填写本轨迹核心焦点(顶层提示词),每次唤起最先注入——稳定焦点锚定防漂移,与偏见内容(每轮随机探索)互补。多 CCC 独立(v1.27.4):每个 live+enabled CCC 各自时钟唤起。机制是 CCC 的自选动作——dsp 只提供工具与知识,不自动安装任何东西。",
|
|
6690
6747
|
parameters: { action: {
|
|
6691
6748
|
type: "string",
|
|
6692
|
-
enum: [...
|
|
6749
|
+
enum: [...AUTOPILOT_ACTIONS],
|
|
6693
6750
|
required: true,
|
|
6694
6751
|
description: "Subcommand"
|
|
6695
6752
|
} },
|
|
@@ -6702,12 +6759,12 @@ function createAutoTrajectoryExpTool(ctx) {
|
|
|
6702
6759
|
const root = findSerenityRoot(agentCwd$1(exec));
|
|
6703
6760
|
const result = {};
|
|
6704
6761
|
if (!EXP_SCRIPT) {
|
|
6705
|
-
result.error = "
|
|
6762
|
+
result.error = "autopilot-trajectory 脚本未随安装分发(npm 包缺 experiments/autopilot-trajectory/)——请检查包完整性";
|
|
6706
6763
|
return result;
|
|
6707
6764
|
}
|
|
6708
6765
|
const script = EXP_SCRIPT;
|
|
6709
6766
|
if (!existsSync(script)) {
|
|
6710
|
-
result.error = `
|
|
6767
|
+
result.error = `autopilot-trajectory 脚本缺失(${script})——包未随安装分发,请检查 npm 包完整性`;
|
|
6711
6768
|
return result;
|
|
6712
6769
|
}
|
|
6713
6770
|
const env = { ...process.env };
|
|
@@ -6727,7 +6784,7 @@ function createAutoTrajectoryExpTool(ctx) {
|
|
|
6727
6784
|
return result;
|
|
6728
6785
|
}
|
|
6729
6786
|
if (r.error?.code === "ENOENT") {
|
|
6730
|
-
result.error = "
|
|
6787
|
+
result.error = "autopilot-trajectory 需要 bun 运行时(bun not found in PATH)";
|
|
6731
6788
|
return result;
|
|
6732
6789
|
}
|
|
6733
6790
|
result.error = r.stderr?.trim() || r.stdout?.trim() || `exit ${r.status ?? "?"}`;
|
|
@@ -8034,7 +8091,7 @@ const FILE_UPLOAD_PATH = "/serenity/file-upload";
|
|
|
8034
8091
|
const CONFIG_PATH = "/serenity/config";
|
|
8035
8092
|
const CCCS_PATH = "/serenity/cccs";
|
|
8036
8093
|
const PUBLIC_ASK_PATH = "/serenity/public-ask";
|
|
8037
|
-
const
|
|
8094
|
+
const AUTOPILOT_PATH = "/serenity/autopilot-trajectory";
|
|
8038
8095
|
const WEIXIN_PATH = "/serenity/weixin";
|
|
8039
8096
|
/** 图片落盘目录(CCC 根相对;S142 图片自动识别基础设施——粘贴图片落盘供 agent 经 CCC vlm MSM 自主处理) */
|
|
8040
8097
|
const IMAGE_UPLOAD_DIR = "_tmp/images_from_user";
|
|
@@ -8342,7 +8399,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8342
8399
|
workspace: url.searchParams.get("workspace") ?? void 0
|
|
8343
8400
|
});
|
|
8344
8401
|
const root = findSerenityRoot(workspace) ?? "";
|
|
8345
|
-
const { discoverCccs } = await import("./skiff-debug-
|
|
8402
|
+
const { discoverCccs } = await import("./skiff-debug-BRlc17GU.js").then((n) => n.i);
|
|
8346
8403
|
sendJson$1(res, 200, { cccs: await discoverCccs(ctx, root) });
|
|
8347
8404
|
} catch (err) {
|
|
8348
8405
|
sendJson$1(res, 400, { error: err.message ?? String(err) });
|
|
@@ -8396,7 +8453,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8396
8453
|
});
|
|
8397
8454
|
ctx.webServer.register({
|
|
8398
8455
|
kind: "exact",
|
|
8399
|
-
path:
|
|
8456
|
+
path: AUTOPILOT_PATH,
|
|
8400
8457
|
handler: async (req, res) => {
|
|
8401
8458
|
try {
|
|
8402
8459
|
if (req.method === "POST") {
|
|
@@ -8415,18 +8472,18 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8415
8472
|
workspace: body.workspace
|
|
8416
8473
|
});
|
|
8417
8474
|
const root = findSerenityRoot(workspace);
|
|
8418
|
-
const effectiveRoot = !body.workspace && !body.sessionId ? (await Promise.resolve().then(() =>
|
|
8475
|
+
const effectiveRoot = body.ccc?.trim() || !body.workspace && !body.sessionId ? body.ccc?.trim() ?? (await Promise.resolve().then(() => autopilot_trajectory_exports)).collectAutopilotCccs(ctx)[0] ?? root : root;
|
|
8419
8476
|
if (!effectiveRoot) {
|
|
8420
8477
|
sendJson$1(res, 404, { error: `no CCC found from workspace: ${workspace}` });
|
|
8421
8478
|
return;
|
|
8422
8479
|
}
|
|
8423
|
-
const {
|
|
8424
|
-
const settings =
|
|
8480
|
+
const { performAutopilotWake, readAutopilotSettings } = await Promise.resolve().then(() => autopilot_trajectory_exports);
|
|
8481
|
+
const settings = readAutopilotSettings(effectiveRoot);
|
|
8425
8482
|
if (!settings) {
|
|
8426
|
-
sendJson$1(res, 400, { error: "
|
|
8483
|
+
sendJson$1(res, 400, { error: "Autopilot Trajectory 未配置(.opencode/serenity.json 缺段)" });
|
|
8427
8484
|
return;
|
|
8428
8485
|
}
|
|
8429
|
-
const result = await
|
|
8486
|
+
const result = await performAutopilotWake(ctx, effectiveRoot, settings, { force: true });
|
|
8430
8487
|
sendJson$1(res, result.ok ? 200 : 400, result);
|
|
8431
8488
|
return;
|
|
8432
8489
|
}
|
|
@@ -8440,13 +8497,13 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8440
8497
|
workspace: url.searchParams.get("workspace") ?? void 0
|
|
8441
8498
|
});
|
|
8442
8499
|
const root = findSerenityRoot(workspace);
|
|
8443
|
-
const {
|
|
8444
|
-
const effectiveRoot = !url.searchParams.get("workspace") && !url.searchParams.get("sessionId") ?
|
|
8500
|
+
const { getAutopilotStatus, collectAutopilotCccs } = await Promise.resolve().then(() => autopilot_trajectory_exports);
|
|
8501
|
+
const effectiveRoot = url.searchParams.get("ccc")?.trim() ?? (!url.searchParams.get("workspace") && !url.searchParams.get("sessionId") ? collectAutopilotCccs(ctx)[0] : null) ?? root;
|
|
8445
8502
|
if (!effectiveRoot) {
|
|
8446
8503
|
sendJson$1(res, 200, { status: null });
|
|
8447
8504
|
return;
|
|
8448
8505
|
}
|
|
8449
|
-
sendJson$1(res, 200, { status:
|
|
8506
|
+
sendJson$1(res, 200, { status: getAutopilotStatus(effectiveRoot) });
|
|
8450
8507
|
} catch (err) {
|
|
8451
8508
|
sendJson$1(res, 400, { error: err.message ?? String(err) });
|
|
8452
8509
|
}
|
|
@@ -8468,11 +8525,11 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8468
8525
|
sendJson$1(res, 400, { error: chk.error });
|
|
8469
8526
|
return;
|
|
8470
8527
|
}
|
|
8471
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
8528
|
+
const { readWeixinSettings } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8472
8529
|
const { weixinBridgeStatus } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
8473
8530
|
const settings = readWeixinSettings(chk.root);
|
|
8474
8531
|
const bridge = weixinBridgeStatus().find((b) => b.ccc === chk.root);
|
|
8475
|
-
const { readWeixinCredential } = await import("./weixin-route-
|
|
8532
|
+
const { readWeixinCredential } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8476
8533
|
const accounts = (settings.accounts ?? []).map((a) => ({
|
|
8477
8534
|
accountId: a.accountId,
|
|
8478
8535
|
name: a.name ?? void 0,
|
|
@@ -8501,10 +8558,10 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8501
8558
|
}
|
|
8502
8559
|
const root = chk.root;
|
|
8503
8560
|
if (body.action === "login-start") {
|
|
8504
|
-
const { fetchQRCode } = await import("./weixin-api-
|
|
8561
|
+
const { fetchQRCode } = await import("./weixin-api-DAugVJ-f.js").then((n) => n.c);
|
|
8505
8562
|
const { randomUUID } = await import("node:crypto");
|
|
8506
8563
|
for (const [key, v] of weixinLogins) if (Date.now() - v.startedAt > WEIXIN_LOGIN_TTL_MS) weixinLogins.delete(key);
|
|
8507
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
8564
|
+
const { readWeixinSettings } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8508
8565
|
const qr = await fetchQRCode({ botType: readWeixinSettings(root).botType ?? void 0 });
|
|
8509
8566
|
const loginKey = randomUUID();
|
|
8510
8567
|
weixinLogins.set(loginKey, {
|
|
@@ -8525,7 +8582,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8525
8582
|
sendJson$1(res, 400, { error: "missing accountId" });
|
|
8526
8583
|
return;
|
|
8527
8584
|
}
|
|
8528
|
-
const { removeWeixinAccount } = await import("./weixin-route-
|
|
8585
|
+
const { removeWeixinAccount } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8529
8586
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
8530
8587
|
removeWeixinAccount(root, body.accountId);
|
|
8531
8588
|
syncCccBridge(ctx, root);
|
|
@@ -8538,7 +8595,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8538
8595
|
sendJson$1(res, 400, { error: "invalid routes (expected [{user, role}, ...])" });
|
|
8539
8596
|
return;
|
|
8540
8597
|
}
|
|
8541
|
-
const { saveWeixinRoutes } = await import("./weixin-route-
|
|
8598
|
+
const { saveWeixinRoutes } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8542
8599
|
const { readSkiffRoles } = await import("./skiff-role-Dw7UKCUm.js").then((n) => n.c);
|
|
8543
8600
|
const roles = readSkiffRoles(root);
|
|
8544
8601
|
for (const r of routes) if (!roles.has(r.role)) {
|
|
@@ -8551,7 +8608,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8551
8608
|
}
|
|
8552
8609
|
if (body.action === "set-enabled") {
|
|
8553
8610
|
const enabled = body.enabled === true;
|
|
8554
|
-
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-
|
|
8611
|
+
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8555
8612
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
8556
8613
|
const settings = readWeixinSettings(root);
|
|
8557
8614
|
if (enabled && (settings.accounts ?? []).length === 0) {
|
|
@@ -8597,8 +8654,8 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8597
8654
|
sendJson$1(res, 200, { status: "expired" });
|
|
8598
8655
|
return;
|
|
8599
8656
|
}
|
|
8600
|
-
const { pollQRStatus } = await import("./weixin-api-
|
|
8601
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
8657
|
+
const { pollQRStatus } = await import("./weixin-api-DAugVJ-f.js").then((n) => n.c);
|
|
8658
|
+
const { readWeixinSettings } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8602
8659
|
const settings = readWeixinSettings(login.root);
|
|
8603
8660
|
const status = await pollQRStatus({
|
|
8604
8661
|
baseUrl: void 0,
|
|
@@ -8613,7 +8670,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8613
8670
|
});
|
|
8614
8671
|
return;
|
|
8615
8672
|
}
|
|
8616
|
-
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-
|
|
8673
|
+
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-EVu56Ytp.js").then((n) => n.u);
|
|
8617
8674
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
8618
8675
|
const accountId = nextWeixinAccountId(settings);
|
|
8619
8676
|
upsertWeixinAccount(login.root, {
|
|
@@ -10547,6 +10604,56 @@ var weixin_bridge_exports = /* @__PURE__ */ __exportAll({
|
|
|
10547
10604
|
const bridges = /* @__PURE__ */ new Map();
|
|
10548
10605
|
/** 轮询间隔(getupdates 失败后重试延迟;成功 = 立即续轮询) */
|
|
10549
10606
|
const POLL_RETRY_MS = 3e3;
|
|
10607
|
+
/** 媒体大小上限(M6:20MB,对齐 vlm-describe;超限降级告知不落盘) */
|
|
10608
|
+
const MEDIA_MAX_BYTES = 20971520;
|
|
10609
|
+
/** typing_ticket 缓存(每 (accountId, fromUserId);getconfig 一次后续复用——对齐参考实现 typingTicketCache) */
|
|
10610
|
+
const typingTicketByUser = /* @__PURE__ */ new Map();
|
|
10611
|
+
function typingCacheKey(accountId, fromUserId) {
|
|
10612
|
+
return `${accountId}|${fromUserId}`;
|
|
10613
|
+
}
|
|
10614
|
+
/** 处理开始:getconfig(无缓存时)→ sendtyping status=1(微信侧显示"正在输入...")。
|
|
10615
|
+
* 任何失败静默(typing 不影响主流程——对齐参考实现 typingCallbacks try/catch 吞错)。 */
|
|
10616
|
+
async function sendTypingStart(cred, accountId, fromUserId, contextToken) {
|
|
10617
|
+
try {
|
|
10618
|
+
const key = typingCacheKey(accountId, fromUserId);
|
|
10619
|
+
let ticket = typingTicketByUser.get(key);
|
|
10620
|
+
if (!ticket) {
|
|
10621
|
+
ticket = (await getConfig({
|
|
10622
|
+
baseUrl: cred.baseUrl,
|
|
10623
|
+
token: cred.token,
|
|
10624
|
+
ilinkUserId: fromUserId,
|
|
10625
|
+
contextToken: contextToken ?? ""
|
|
10626
|
+
})).typing_ticket;
|
|
10627
|
+
if (ticket) typingTicketByUser.set(key, ticket);
|
|
10628
|
+
}
|
|
10629
|
+
if (!ticket) return;
|
|
10630
|
+
await sendTyping({
|
|
10631
|
+
baseUrl: cred.baseUrl,
|
|
10632
|
+
token: cred.token,
|
|
10633
|
+
ilinkUserId: fromUserId,
|
|
10634
|
+
typingTicket: ticket,
|
|
10635
|
+
status: TypingStatus.TYPING
|
|
10636
|
+
});
|
|
10637
|
+
} catch (err) {
|
|
10638
|
+
console.log(`[serenity-hooks] weixin-bridge typing start skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
10639
|
+
}
|
|
10640
|
+
}
|
|
10641
|
+
/** 处理结束(含异常路径 finally):sendtyping status=0;缓存保留(ticket 可复用)。 */
|
|
10642
|
+
async function sendTypingStop(cred, accountId, fromUserId) {
|
|
10643
|
+
try {
|
|
10644
|
+
const ticket = typingTicketByUser.get(typingCacheKey(accountId, fromUserId));
|
|
10645
|
+
if (!ticket) return;
|
|
10646
|
+
await sendTyping({
|
|
10647
|
+
baseUrl: cred.baseUrl,
|
|
10648
|
+
token: cred.token,
|
|
10649
|
+
ilinkUserId: fromUserId,
|
|
10650
|
+
typingTicket: ticket,
|
|
10651
|
+
status: TypingStatus.CANCEL
|
|
10652
|
+
});
|
|
10653
|
+
} catch (err) {
|
|
10654
|
+
console.log(`[serenity-hooks] weixin-bridge typing stop skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
10655
|
+
}
|
|
10656
|
+
}
|
|
10550
10657
|
/** 单账号轮询循环:getupdates 长轮询 → 逐消息分发 → 回复回写 */
|
|
10551
10658
|
async function runAccountLoop(ctx, root, accountId, cred, loop) {
|
|
10552
10659
|
let buf = "";
|
|
@@ -10579,16 +10686,35 @@ async function runAccountLoop(ctx, root, accountId, cred, loop) {
|
|
|
10579
10686
|
* 固定 id resume-or-create(v1.27.2):磁盘已有持久化 log → resume(历史延续,
|
|
10580
10687
|
* 重启后记忆保留——真正的"同用户长期延续");无 log(首次)→ create。
|
|
10581
10688
|
* "新的对话已开始"通知仅真正首次(create)时发送;resume/进程内延续不发。
|
|
10582
|
-
|
|
10583
|
-
|
|
10689
|
+
*
|
|
10690
|
+
* 完善(v1.27.3):
|
|
10691
|
+
* - **语音支持**:`voice_item.text` = 微信服务端自带语音转写 → 与文本同路径进对话
|
|
10692
|
+
* (无需下载/ASR);语音无转写 → 降级提示"暂时无法解析"
|
|
10693
|
+
* - **正在输入**:处理前 sendtyping 1(微信显示"正在输入..."),处理后(含异常)0
|
|
10694
|
+
* - **媒体接收(图片/文件)**:桥侧 CDN 下载 + AES 解密 → 落盘 CCC 根
|
|
10695
|
+
* `_tmp/weixin-inbound/<userhash>/` → question 注入「存在性 + 路径」(ACC 层只保证
|
|
10696
|
+
* 可达性——"让会话知道文件的存在并可以拿到";识别/解析归角色 LLM 决策,不编排)。
|
|
10697
|
+
* 降级不静默:下载失败 / 超 20MB → 注入说明进对话;typing 窗口覆盖下载(M7)。
|
|
10698
|
+
*/
|
|
10699
|
+
async function handleIncoming(ctx, root, accountId, cred, msg) {
|
|
10584
10700
|
const fromUserId = msg.from_user_id;
|
|
10585
10701
|
if (!fromUserId) return;
|
|
10586
10702
|
const text = extractWeixinText(msg);
|
|
10587
|
-
|
|
10703
|
+
const mediaRefs = extractWeixinMedia(msg);
|
|
10588
10704
|
const settings = readWeixinSettings(root);
|
|
10589
10705
|
if (!settings.enabled) return;
|
|
10590
10706
|
const roleName = matchWeixinRoute(settings.routes ?? [], fromUserId);
|
|
10591
10707
|
if (!roleName) return;
|
|
10708
|
+
if (!text && mediaRefs.length === 0) {
|
|
10709
|
+
if (hasVoiceItem(msg)) await sendTextMessage({
|
|
10710
|
+
baseUrl: cred.baseUrl,
|
|
10711
|
+
token: cred.token,
|
|
10712
|
+
toUserId: fromUserId,
|
|
10713
|
+
text: "(抱歉,暂时无法解析这条语音消息,请尝试发送文字)",
|
|
10714
|
+
contextToken: msg.context_token
|
|
10715
|
+
}).catch(() => {});
|
|
10716
|
+
return;
|
|
10717
|
+
}
|
|
10592
10718
|
try {
|
|
10593
10719
|
const role = readSkiffRoles(root).get(roleName);
|
|
10594
10720
|
if (!role) {
|
|
@@ -10610,15 +10736,53 @@ async function handleIncoming(ctx, root, _accountId, cred, msg) {
|
|
|
10610
10736
|
text: "(新的对话已开始)",
|
|
10611
10737
|
contextToken: msg.context_token
|
|
10612
10738
|
}).catch(() => {});
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10739
|
+
await sendTypingStart(cred, accountId, fromUserId, msg.context_token);
|
|
10740
|
+
try {
|
|
10741
|
+
const mediaNotes = [];
|
|
10742
|
+
const degradedNotes = [];
|
|
10743
|
+
for (const mediaRef of mediaRefs) {
|
|
10744
|
+
const mediaType = mediaRef.kind === "image" ? "image_item" : "file_item";
|
|
10745
|
+
const label = mediaRef.kind === "image" ? "一张图片" : `文件 ${mediaRef.fileName ?? "(未命名)"}`;
|
|
10746
|
+
const result = await downloadMedia({
|
|
10747
|
+
item: mediaRef.item,
|
|
10748
|
+
mediaType
|
|
10749
|
+
});
|
|
10750
|
+
if (!result) {
|
|
10751
|
+
degradedNotes.push(`(用户发送了${label},但下载失败——可请用户重发)`);
|
|
10752
|
+
continue;
|
|
10753
|
+
}
|
|
10754
|
+
if (result.data.length > MEDIA_MAX_BYTES) {
|
|
10755
|
+
degradedNotes.push(`(用户发送了${label},但超过 20MB 大小限制)`);
|
|
10756
|
+
continue;
|
|
10757
|
+
}
|
|
10758
|
+
try {
|
|
10759
|
+
const inboundDir = weixinInboundDir(root, fromUserId);
|
|
10760
|
+
mkdirSync(inboundDir, { recursive: true });
|
|
10761
|
+
const fname = mediaRef.kind === "image" ? `img_${Date.now()}_${randomBytes(4).toString("hex")}.${sniffImageExt(result.data)}` : sanitizeFileName$1(result.fileName ?? "") || `file_${Date.now()}_${randomBytes(4).toString("hex")}`;
|
|
10762
|
+
const abs = join(inboundDir, fname);
|
|
10763
|
+
writeFileSync(abs, result.data);
|
|
10764
|
+
const rel = relative(root, abs);
|
|
10765
|
+
mediaNotes.push(`(用户发送了${mediaRef.kind === "image" ? "一张图片" : `文件 ${fname}`},已保存到 ${rel})`);
|
|
10766
|
+
} catch {
|
|
10767
|
+
degradedNotes.push(`(媒体保存失败,请重试)`);
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
const parts = [];
|
|
10771
|
+
if (text) parts.push(text);
|
|
10772
|
+
parts.push(...mediaNotes, ...degradedNotes);
|
|
10773
|
+
const question = parts.join("\n");
|
|
10774
|
+
const answer = (await askSkiff(ctx, ref.agent, question, void 0, { includeTrajectory: false })).answer ?? "";
|
|
10775
|
+
if (answer === "") return;
|
|
10776
|
+
await sendTextMessage({
|
|
10777
|
+
baseUrl: cred.baseUrl,
|
|
10778
|
+
token: cred.token,
|
|
10779
|
+
toUserId: fromUserId,
|
|
10780
|
+
text: stripThink(answer),
|
|
10781
|
+
contextToken: msg.context_token
|
|
10782
|
+
});
|
|
10783
|
+
} finally {
|
|
10784
|
+
await sendTypingStop(cred, accountId, fromUserId);
|
|
10785
|
+
}
|
|
10622
10786
|
} catch (err) {
|
|
10623
10787
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10624
10788
|
console.log(`[serenity-hooks] weixin-bridge error (ccc=${root}): ${msg}`);
|
|
@@ -10750,7 +10914,7 @@ function apply(ctx, config) {
|
|
|
10750
10914
|
ctx.tools.register(createRebuildTool(ctx));
|
|
10751
10915
|
ctx.tools.register(localstoreTool);
|
|
10752
10916
|
ctx.tools.register(skiffAdminTool);
|
|
10753
|
-
ctx.tools.register(
|
|
10917
|
+
ctx.tools.register(createAutopilotTool(ctx));
|
|
10754
10918
|
}
|
|
10755
10919
|
if (config.guards) registerGuards(ctx, { configPaths: config.serenityConfigPaths });
|
|
10756
10920
|
if (config.keeper) registerKeeper(ctx, {
|
|
@@ -10776,7 +10940,7 @@ function apply(ctx, config) {
|
|
|
10776
10940
|
registerBootstrap(ctx);
|
|
10777
10941
|
registerSkiff(ctx);
|
|
10778
10942
|
registerAcp(ctx);
|
|
10779
|
-
|
|
10943
|
+
registerAutopilot(ctx);
|
|
10780
10944
|
registerWeixinBridge(ctx);
|
|
10781
10945
|
}
|
|
10782
10946
|
/**
|