@shgroup/dsh-serenity-hooks 1.30.4 → 1.30.8
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/lib/access-CdL6BAYj.js +49 -0
- package/lib/acp-core.d.ts +1 -1
- package/lib/agent-idle.d.ts +27 -0
- package/lib/{ccc-CX48YNUL.js → ccc-DAsSHsub.js} +22 -1
- package/lib/ccc.d.ts +13 -0
- package/lib/client.js +19 -3
- package/lib/gateway-proxy.d.ts +21 -0
- package/lib/gateway.d.ts +1 -1
- package/lib/host/access.d.ts +53 -0
- package/lib/host/contract.d.ts +99 -0
- package/lib/host/effect.d.ts +24 -0
- package/lib/index.js +515 -111
- package/lib/kit-ops.d.ts +1 -1
- package/lib/localstore-ops.d.ts +2 -0
- package/lib/seams/lifecycle.d.ts +29 -0
- package/lib/seams/system-prompt.d.ts +6 -1
- package/lib/{session-bound-p9oR9dj9.js → session-bound-D2ANqVn-.js} +82 -28
- package/lib/session-bound.d.ts +28 -47
- package/lib/{settings-section-BfVxgCZy.js → settings-section-BPk0e1du.js} +8 -1
- package/lib/settings-section.d.ts +2 -0
- package/lib/{skiff-debug-gYigSgl_.js → skiff-debug-8pF60JBO.js} +51 -25
- package/lib/{skiff-role-LW4tjF9L.js → skiff-role-DlrbHPLD.js} +4 -2
- package/lib/tools/handyman.d.ts +1 -1
- package/lib/tools/kit.d.ts +6 -1
- package/lib/{weixin-route-jA0qENOf.js → weixin-route-BJ4g4ZNq.js} +9 -3
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { a as findSerenityRoot, c as matchBlacklist, d as readCccName$2, f as readHandymanConfig, i as findGitRoot, l as pathInside, m as resolveInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-
|
|
3
|
-
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, l as systemPromptSource, r as isSkiffSessionId, s as roleToolWhitelist } from "./skiff-role-
|
|
4
|
-
import { C as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { n as
|
|
2
|
+
import { a as findSerenityRoot, c as matchBlacklist, d as readCccName$2, f as readHandymanConfig, i as findGitRoot, l as pathInside, m as resolveInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-DAsSHsub.js";
|
|
3
|
+
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, l as systemPromptSource, r as isSkiffSessionId, s as roleToolWhitelist } from "./skiff-role-DlrbHPLD.js";
|
|
4
|
+
import { C as newStopToken, D as writeFailedStatus, E as splitModel, O as writeProgress, S as listActiveHandymen, T as requireWhitelistedModel, _ as workspaceTrajectoryLine, a as startSkiffDebugServer, b as buildRoundPrompt, c as askSkiff, d as getSkiffAgent, f as skiffMsmGate, g as unregisterSkiffSession, h as skiffTrajectoryEnabled, k as skiffRoleFor, l as createSkiffAgent, m as skiffSessionSnapshot, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffSessionInfo, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as ensureSkiffSession, v as waitAgentIdle, w as readProgress, x as handymanProgressPaths, y as HANDYMAN_GUIDE } from "./skiff-debug-8pF60JBO.js";
|
|
5
|
+
import { i as hostSessions, n as hostInjected, o as hostWebServer, r as hostService, t as hostAgents } from "./access-CdL6BAYj.js";
|
|
6
|
+
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-BJ4g4ZNq.js";
|
|
7
|
+
import { C as summarize, S as showSession, _ as readActiveSessionMd, a as archiveSessions, b as sessionsRoot, c as createSession, d as findSession, f as getActiveSessionInfo, g as qaCheck, h as parseSessionContextFromEvents, i as SESSION_ACTIONS, l as extractSessionMdPathFromText, m as listSessions, n as readLastBound, o as clearActiveSessionInfo, p as healthCheck, r as DEFAULT_SESSION_SCOPE, s as closeSession, t as appendBound, u as findLatestActiveSessionMd, v as resolveSessionByTitle, w as useSession, x as setActiveSessionInfo, y as sessionEvents } from "./session-bound-D2ANqVn-.js";
|
|
8
|
+
import { n as registerSettingsSection, t as readSimpleSettings } from "./settings-section-BPk0e1du.js";
|
|
8
9
|
import { a as markdownToPlainText, c as sniffImageExt, i as getUpdates, n as downloadMedia, o as sendTextMessage, r as getConfig, s as sendTyping, t as TypingStatus } from "./weixin-api-OUCuw0Ws.js";
|
|
9
10
|
import z from "@deepseek-ai/schemastery";
|
|
10
11
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
@@ -836,6 +837,306 @@ function setSafeMode(root, on) {
|
|
|
836
837
|
return { on: isSafeModeOn(root) };
|
|
837
838
|
}
|
|
838
839
|
//#endregion
|
|
840
|
+
//#region src/host/contract.ts
|
|
841
|
+
/** dsp 依赖的宿主服务与成员(对照 AI_LAB/dsh-harness-public @ 0.1.2-rc.1 逐一核对) */
|
|
842
|
+
const HOST_SERVICES = [
|
|
843
|
+
{
|
|
844
|
+
id: "tools",
|
|
845
|
+
name: "tools",
|
|
846
|
+
access: "injected",
|
|
847
|
+
members: [{
|
|
848
|
+
name: "register",
|
|
849
|
+
kind: "function"
|
|
850
|
+
}, {
|
|
851
|
+
name: "guard",
|
|
852
|
+
kind: "function"
|
|
853
|
+
}],
|
|
854
|
+
impact: "10 个 ACC 工具无法注册 / 机械守卫失效",
|
|
855
|
+
required: true
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
id: "sessions",
|
|
859
|
+
name: "sessions",
|
|
860
|
+
access: "injected",
|
|
861
|
+
members: [
|
|
862
|
+
{
|
|
863
|
+
name: "list",
|
|
864
|
+
kind: "function"
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
name: "get",
|
|
868
|
+
kind: "function"
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
name: "create",
|
|
872
|
+
kind: "function"
|
|
873
|
+
}
|
|
874
|
+
],
|
|
875
|
+
impact: "会话定位/rebuild/CCC 解析失效",
|
|
876
|
+
required: true
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
id: "agents",
|
|
880
|
+
name: "agents",
|
|
881
|
+
access: "injected",
|
|
882
|
+
members: [{
|
|
883
|
+
name: "create",
|
|
884
|
+
kind: "function"
|
|
885
|
+
}, {
|
|
886
|
+
name: "get",
|
|
887
|
+
kind: "function"
|
|
888
|
+
}],
|
|
889
|
+
impact: "skiff/handyman 无法创建或复用 agent",
|
|
890
|
+
required: true
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
id: "webServer",
|
|
894
|
+
name: "webServer",
|
|
895
|
+
access: "injected",
|
|
896
|
+
members: [{
|
|
897
|
+
name: "register",
|
|
898
|
+
kind: "function"
|
|
899
|
+
}, {
|
|
900
|
+
name: "port",
|
|
901
|
+
kind: "value"
|
|
902
|
+
}],
|
|
903
|
+
impact: "状态接口/网关端口发现失效",
|
|
904
|
+
required: true
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
id: "settings",
|
|
908
|
+
name: "settings",
|
|
909
|
+
access: "injected",
|
|
910
|
+
members: [{
|
|
911
|
+
name: "installSection",
|
|
912
|
+
kind: "function"
|
|
913
|
+
}],
|
|
914
|
+
impact: "设置面板不安装 → 所有开关静默 no-op",
|
|
915
|
+
required: true
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
id: "systemPrompt",
|
|
919
|
+
name: "systemPrompt",
|
|
920
|
+
access: "injected",
|
|
921
|
+
members: [],
|
|
922
|
+
impact: "Induction(入口 skill 段)注入失效",
|
|
923
|
+
required: true
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
id: "sessionProjections",
|
|
927
|
+
name: "sessionProjections",
|
|
928
|
+
access: "injected",
|
|
929
|
+
members: [{
|
|
930
|
+
name: "snapshot",
|
|
931
|
+
kind: "function"
|
|
932
|
+
}],
|
|
933
|
+
impact: "上下文压力读数失效 → rebuild 提醒不再触发",
|
|
934
|
+
required: false
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
id: "skills",
|
|
938
|
+
name: "skills",
|
|
939
|
+
access: "injected",
|
|
940
|
+
members: [],
|
|
941
|
+
impact: "opencode skill 兼容层(.opencode/skills 注册)失效",
|
|
942
|
+
required: false
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
id: "shellEnv",
|
|
946
|
+
name: "shellEnv",
|
|
947
|
+
access: "injected",
|
|
948
|
+
members: [],
|
|
949
|
+
impact: "DSH_SERENITY_* 环境事实注入失效",
|
|
950
|
+
required: false
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
id: "agentLoop",
|
|
954
|
+
name: "agentLoop",
|
|
955
|
+
access: "injected",
|
|
956
|
+
members: [],
|
|
957
|
+
impact: "handyman worker agent 无法装配 preset",
|
|
958
|
+
required: false
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
id: "sessionTitle",
|
|
962
|
+
name: "sessionTitle",
|
|
963
|
+
access: "lazy",
|
|
964
|
+
members: [{
|
|
965
|
+
name: "rename",
|
|
966
|
+
kind: "function"
|
|
967
|
+
}],
|
|
968
|
+
impact: "会话命名 / rebuild 后重命名跳过(仅告警)",
|
|
969
|
+
required: false
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
id: "tokenMeter",
|
|
973
|
+
name: "tokenMeter",
|
|
974
|
+
access: "lazy",
|
|
975
|
+
members: [{
|
|
976
|
+
name: "estimateMessage",
|
|
977
|
+
kind: "function"
|
|
978
|
+
}],
|
|
979
|
+
impact: "rebuild shadow-price 定价失效(计量不准)",
|
|
980
|
+
required: false
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
id: "workspaceRegistry",
|
|
984
|
+
name: "workspaceRegistry",
|
|
985
|
+
access: "lazy",
|
|
986
|
+
members: [{
|
|
987
|
+
name: "list",
|
|
988
|
+
kind: "function"
|
|
989
|
+
}],
|
|
990
|
+
impact: "工作区白名单下拉为空(外部访问配置受限)",
|
|
991
|
+
required: false
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
id: "sessionPersistence",
|
|
995
|
+
name: "sessionPersistence",
|
|
996
|
+
access: "lazy",
|
|
997
|
+
members: [{
|
|
998
|
+
name: "list",
|
|
999
|
+
kind: "function"
|
|
1000
|
+
}],
|
|
1001
|
+
impact: "CCC 自动发现回落失效(诊断面受限)",
|
|
1002
|
+
required: false
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
id: "connection",
|
|
1006
|
+
name: "connection",
|
|
1007
|
+
access: "lazy",
|
|
1008
|
+
members: [{
|
|
1009
|
+
name: "authenticatedUrl",
|
|
1010
|
+
kind: "function"
|
|
1011
|
+
}, {
|
|
1012
|
+
name: "authorizeIndex",
|
|
1013
|
+
kind: "function"
|
|
1014
|
+
}],
|
|
1015
|
+
impact: "双端口网关无法换取 dsh cookie → 外部反代 401",
|
|
1016
|
+
required: false
|
|
1017
|
+
}
|
|
1018
|
+
];
|
|
1019
|
+
/** dsp 被验证过的宿主版本范围(与 package.json peerDependencies 同源,单一真相源) */
|
|
1020
|
+
const REQUIRED_HOST_RANGE = "^0.1.2-rc.1";
|
|
1021
|
+
/** 最小 semver 比较(无依赖):返回 -1 / 0 / 1;解析失败返回 null */
|
|
1022
|
+
function compareSemver(a, b) {
|
|
1023
|
+
const parse = (v) => {
|
|
1024
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(?:-(.*))?$/.exec(v.trim());
|
|
1025
|
+
if (!m) return null;
|
|
1026
|
+
return [
|
|
1027
|
+
Number(m[1]),
|
|
1028
|
+
Number(m[2]),
|
|
1029
|
+
Number(m[3]),
|
|
1030
|
+
m[4] ?? ""
|
|
1031
|
+
];
|
|
1032
|
+
};
|
|
1033
|
+
const pa = parse(a);
|
|
1034
|
+
const pb = parse(b);
|
|
1035
|
+
if (!pa || !pb) return null;
|
|
1036
|
+
for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i] < pb[i] ? -1 : 1;
|
|
1037
|
+
const ra = pa[3];
|
|
1038
|
+
const rb = pb[3];
|
|
1039
|
+
if (ra === rb) return 0;
|
|
1040
|
+
if (ra === "") return 1;
|
|
1041
|
+
if (rb === "") return -1;
|
|
1042
|
+
return ra < rb ? -1 : 1;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* 宿主版本是否落在 dsp 被验证的范围(`^0.1.2-rc.1`)。
|
|
1046
|
+
* 无版本信息 → 视为未知(ok=false,detail 说明),不猜测。
|
|
1047
|
+
*/
|
|
1048
|
+
function checkHostVersion(version) {
|
|
1049
|
+
if (version === null) return {
|
|
1050
|
+
ok: false,
|
|
1051
|
+
detail: `host version unknown (required ${REQUIRED_HOST_RANGE})`
|
|
1052
|
+
};
|
|
1053
|
+
const floor = compareSemver(version, "0.1.2-rc.1");
|
|
1054
|
+
const ceiling = compareSemver(version, "0.2.0");
|
|
1055
|
+
if (floor === null || ceiling === null) return {
|
|
1056
|
+
ok: false,
|
|
1057
|
+
detail: `host version "${version}" not parseable (required ${REQUIRED_HOST_RANGE})`
|
|
1058
|
+
};
|
|
1059
|
+
if (floor < 0) return {
|
|
1060
|
+
ok: false,
|
|
1061
|
+
detail: `host ${version} is below the verified floor (required ${REQUIRED_HOST_RANGE})`
|
|
1062
|
+
};
|
|
1063
|
+
if (ceiling >= 0) return {
|
|
1064
|
+
ok: false,
|
|
1065
|
+
detail: `host ${version} is outside the verified range (required ${REQUIRED_HOST_RANGE})`
|
|
1066
|
+
};
|
|
1067
|
+
return {
|
|
1068
|
+
ok: true,
|
|
1069
|
+
detail: `host ${version} within ${REQUIRED_HOST_RANGE}`
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
/** 结构化读取宿主服务(lazy → ctx.get;injected → 直接属性,属性缺失再试 ctx.get) */
|
|
1073
|
+
function readService(ctx, name, access) {
|
|
1074
|
+
return access === "lazy" ? hostService(ctx, name) : hostInjected(ctx, name);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* 探针:核对运行中的宿主是否仍满足 dsp 依赖的服务与成员。
|
|
1078
|
+
*
|
|
1079
|
+
* @param ctx 宿主插件上下文(真实 cordis Context;测试可传结构化替身)
|
|
1080
|
+
* @param hostVersion 运行中宿主版本(`readDshVersion()` 结果;null = 未知)
|
|
1081
|
+
* @returns 报告(`ok:false` 时 `issues` 逐条给出缺失项与后果)
|
|
1082
|
+
*/
|
|
1083
|
+
function probeHostContract(ctx, hostVersion = null) {
|
|
1084
|
+
const issues = [];
|
|
1085
|
+
let checked = 0;
|
|
1086
|
+
for (const svc of HOST_SERVICES) {
|
|
1087
|
+
checked += 1;
|
|
1088
|
+
let value;
|
|
1089
|
+
try {
|
|
1090
|
+
value = readService(ctx, svc.name, svc.access);
|
|
1091
|
+
} catch {
|
|
1092
|
+
value = void 0;
|
|
1093
|
+
}
|
|
1094
|
+
if (value === void 0 || value === null) {
|
|
1095
|
+
issues.push({
|
|
1096
|
+
id: svc.id,
|
|
1097
|
+
kind: "service",
|
|
1098
|
+
detail: `service "${svc.name}" not available (${svc.access})`,
|
|
1099
|
+
impact: svc.impact,
|
|
1100
|
+
required: svc.required
|
|
1101
|
+
});
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
for (const member of svc.members) {
|
|
1105
|
+
checked += 1;
|
|
1106
|
+
const actual = value[member.name];
|
|
1107
|
+
if (!(member.kind === "function" ? typeof actual === "function" : actual !== void 0)) issues.push({
|
|
1108
|
+
id: `${svc.id}.${member.name}`,
|
|
1109
|
+
kind: "member",
|
|
1110
|
+
detail: `service "${svc.name}" is missing ${member.kind} "${member.name}"`,
|
|
1111
|
+
impact: svc.impact,
|
|
1112
|
+
required: svc.required
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
const version = checkHostVersion(hostVersion);
|
|
1117
|
+
if (!version.ok) issues.push({
|
|
1118
|
+
id: "host.version",
|
|
1119
|
+
kind: "version",
|
|
1120
|
+
detail: version.detail,
|
|
1121
|
+
impact: "契约未经该宿主版本验证(漂移风险)",
|
|
1122
|
+
required: false
|
|
1123
|
+
});
|
|
1124
|
+
return {
|
|
1125
|
+
ok: issues.every((i) => !i.required),
|
|
1126
|
+
checked,
|
|
1127
|
+
hostVersion,
|
|
1128
|
+
versionOk: version.ok,
|
|
1129
|
+
issues
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
/** 单行摘要(启动告警用) */
|
|
1133
|
+
function summarizeHostContract(report) {
|
|
1134
|
+
if (report.ok && report.issues.length === 0) return `host contract ok (${report.checked} checks, host ${report.hostVersion ?? "unknown"})`;
|
|
1135
|
+
const required = report.issues.filter((i) => i.required);
|
|
1136
|
+
const optional = report.issues.filter((i) => !i.required);
|
|
1137
|
+
return `${required.length > 0 ? `host contract BROKEN (${required.length} required)` : `host contract degraded (${optional.length} optional)`}: ${report.issues.map((i) => i.detail).join("; ")}`;
|
|
1138
|
+
}
|
|
1139
|
+
//#endregion
|
|
839
1140
|
//#region src/kit-ops.ts
|
|
840
1141
|
/**
|
|
841
1142
|
* kit-ops.ts — dashboard 纯操作层(acc_kit → dashboard,v1.30;零 DSH 依赖)
|
|
@@ -933,7 +1234,7 @@ function checkRegistryHealth(root) {
|
|
|
933
1234
|
issues
|
|
934
1235
|
};
|
|
935
1236
|
}
|
|
936
|
-
async function runKit(root, args) {
|
|
1237
|
+
async function runKit(root, args, hostCtx) {
|
|
937
1238
|
switch (args.action) {
|
|
938
1239
|
case "health": {
|
|
939
1240
|
const cccName = readCccName$1(root);
|
|
@@ -973,6 +1274,11 @@ async function runKit(root, args) {
|
|
|
973
1274
|
if (root) report.config = loadSerenityConfig(root);
|
|
974
1275
|
report.accVersion = ACC_VERSION;
|
|
975
1276
|
report.dshVersion = readDshVersion();
|
|
1277
|
+
if (hostCtx !== void 0) {
|
|
1278
|
+
const contract = probeHostContract(hostCtx, report.dshVersion);
|
|
1279
|
+
report.hostContract = contract;
|
|
1280
|
+
if (!contract.ok) report.status = "degraded";
|
|
1281
|
+
}
|
|
976
1282
|
return report;
|
|
977
1283
|
}
|
|
978
1284
|
case "time": {
|
|
@@ -1006,29 +1312,35 @@ function renderText$11(value) {
|
|
|
1006
1312
|
text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
|
|
1007
1313
|
}];
|
|
1008
1314
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1315
|
+
/**
|
|
1316
|
+
* dashboard 工具(v1.30.7:闭包捕获 ctx → health 可附宿主契约探针 F-05)。
|
|
1317
|
+
* ctx 仅用于**只读探针**(`ctx.get` / 属性读取),无副作用。
|
|
1318
|
+
*/
|
|
1319
|
+
function createKitTool(ctx) {
|
|
1320
|
+
return defineTool({
|
|
1321
|
+
name: "dashboard",
|
|
1322
|
+
description: "Dashboard — always-on container instruments (works any time, no side effects): health (CCC three-principle check P1/P2/config + MSM registry integrity + host-contract probe, healthy/degraded) / time (now_iso/now_local/epoch_ms) / wait (wait N seconds, default 1). Routine self-check before entering a CCC.",
|
|
1323
|
+
parameters: {
|
|
1324
|
+
action: {
|
|
1325
|
+
type: "string",
|
|
1326
|
+
enum: [...KIT_ACTIONS],
|
|
1327
|
+
required: true,
|
|
1328
|
+
description: "Subcommand"
|
|
1329
|
+
},
|
|
1330
|
+
seconds: {
|
|
1331
|
+
type: "integer",
|
|
1332
|
+
description: "Seconds to wait (positive integer, default 1)"
|
|
1333
|
+
}
|
|
1018
1334
|
},
|
|
1019
|
-
|
|
1020
|
-
type: "
|
|
1021
|
-
|
|
1335
|
+
output: {
|
|
1336
|
+
schema: { type: "json" },
|
|
1337
|
+
render: (args, value) => renderText$11(value)
|
|
1338
|
+
},
|
|
1339
|
+
async execute(args, exec) {
|
|
1340
|
+
return await runKit(findSerenityRoot(agentCwd$8(exec)), args, ctx);
|
|
1022
1341
|
}
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
schema: { type: "json" },
|
|
1026
|
-
render: (args, value) => renderText$11(value)
|
|
1027
|
-
},
|
|
1028
|
-
async execute(args, exec) {
|
|
1029
|
-
return await runKit(findSerenityRoot(agentCwd$8(exec)), args);
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1032
1344
|
//#endregion
|
|
1033
1345
|
//#region src/git-ops.ts
|
|
1034
1346
|
/**
|
|
@@ -2332,7 +2644,7 @@ function handymanPresetInheritance(parentCtx) {
|
|
|
2332
2644
|
*
|
|
2333
2645
|
* 机制:ctx.agents.create()(带 setup 钩子)创建专用 agent(进程内),
|
|
2334
2646
|
* 每轮 followup → agent/status idle → 读 session.events 响应 → 写进度 → stop token 检查 →
|
|
2335
|
-
* 未完成继续下一轮;followup
|
|
2647
|
+
* 未完成继续下一轮;followup/等待空闲抛错(非正常停止)→ dispose 并重新 create agent
|
|
2336
2648
|
* (重启计数,≤100),同一轮重试。工厂模式:apply 时闭包捕获插件 ctx(工具 execute 无 ctx 参数)。
|
|
2337
2649
|
*
|
|
2338
2650
|
* preset 继承 + 工具收窄:setup 钩子里对子 agent 执行 agentPresets.composeFrom(对齐
|
|
@@ -2347,21 +2659,6 @@ function renderText$4(value) {
|
|
|
2347
2659
|
text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
|
|
2348
2660
|
}];
|
|
2349
2661
|
}
|
|
2350
|
-
/** 等待 agent 空闲(agent/status → idle);无超时(handyman 可永续,agent 工作多久等多久) */
|
|
2351
|
-
function waitIdle(ctx, agent) {
|
|
2352
|
-
return new Promise((resolve) => {
|
|
2353
|
-
let settled = false;
|
|
2354
|
-
const finish = () => {
|
|
2355
|
-
if (settled) return;
|
|
2356
|
-
settled = true;
|
|
2357
|
-
dispose();
|
|
2358
|
-
resolve();
|
|
2359
|
-
};
|
|
2360
|
-
const dispose = ctx.on("agent/status", (payload) => {
|
|
2361
|
-
if (payload.agent === agent && payload.status === "idle") finish();
|
|
2362
|
-
});
|
|
2363
|
-
});
|
|
2364
|
-
}
|
|
2365
2662
|
/** 读取会话最后一个 assistant/message 文本 */
|
|
2366
2663
|
function lastAssistantText$1(agent) {
|
|
2367
2664
|
const s = agent.session;
|
|
@@ -2461,7 +2758,7 @@ async function runHandymanJob(ctx, opts) {
|
|
|
2461
2758
|
plugin: "dsh-serenity-hooks"
|
|
2462
2759
|
}
|
|
2463
2760
|
}));
|
|
2464
|
-
await
|
|
2761
|
+
await waitAgentIdle(ctx, workerAgent);
|
|
2465
2762
|
lastResponse = lastAssistantText$1(workerAgent);
|
|
2466
2763
|
} catch {
|
|
2467
2764
|
restarts++;
|
|
@@ -2727,7 +3024,7 @@ function renameDshSessionOnUse(deps, session, titles, active, summary) {
|
|
|
2727
3024
|
*/
|
|
2728
3025
|
function renameDshSessionForActive(ctx, exec, info, summary) {
|
|
2729
3026
|
try {
|
|
2730
|
-
const titles = ctx
|
|
3027
|
+
const titles = hostService(ctx, "sessionTitle");
|
|
2731
3028
|
const dshSession = exec.agent?.session;
|
|
2732
3029
|
if (!dshSession) {
|
|
2733
3030
|
console.warn(`[serenity-hooks] dsh 会话重命名未执行: 缺少 agent session(info: ${info.sessionId})`);
|
|
@@ -3328,6 +3625,40 @@ const containerAdminTool = defineTool({
|
|
|
3328
3625
|
}
|
|
3329
3626
|
});
|
|
3330
3627
|
//#endregion
|
|
3628
|
+
//#region src/host/effect.ts
|
|
3629
|
+
/** 已告警的标签(进程级去重:重复装配/多测试替身不刷屏,但绝不静默) */
|
|
3630
|
+
const warnedLabels = /* @__PURE__ */ new Set();
|
|
3631
|
+
/**
|
|
3632
|
+
* 登记一个卸载时执行的清理。
|
|
3633
|
+
* @param ctx 宿主插件上下文
|
|
3634
|
+
* @param label 日志标签(定位是哪个资源未装配)
|
|
3635
|
+
* @param cleanup 卸载时执行;抛错被吞掉(不阻断其它清理)
|
|
3636
|
+
* @returns 是否成功装配(false = 宿主无 `ctx.effect`,已告警)
|
|
3637
|
+
*/
|
|
3638
|
+
function registerDisposer(ctx, label, cleanup) {
|
|
3639
|
+
const effect = ctx?.effect;
|
|
3640
|
+
if (typeof effect !== "function") {
|
|
3641
|
+
if (!warnedLabels.has(label)) {
|
|
3642
|
+
warnedLabels.add(label);
|
|
3643
|
+
console.warn(`[serenity-hooks] ✗ 资源拆卸未装配(${label}):ctx.effect 不可用(宿主契约缺失)`);
|
|
3644
|
+
}
|
|
3645
|
+
return false;
|
|
3646
|
+
}
|
|
3647
|
+
try {
|
|
3648
|
+
effect.call(ctx, () => () => {
|
|
3649
|
+
try {
|
|
3650
|
+
cleanup();
|
|
3651
|
+
} catch (err) {
|
|
3652
|
+
console.warn(`[serenity-hooks] ✗ 资源清理失败(${label}): ${String(err?.message ?? err)}`);
|
|
3653
|
+
}
|
|
3654
|
+
}, `dsh-serenity-hooks: ${label}`);
|
|
3655
|
+
return true;
|
|
3656
|
+
} catch (err) {
|
|
3657
|
+
console.warn(`[serenity-hooks] ✗ 资源拆卸注册失败(${label}): ${String(err?.message ?? err)}`);
|
|
3658
|
+
return false;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
//#endregion
|
|
3331
3662
|
//#region src/autopilot-trajectory.ts
|
|
3332
3663
|
var autopilot_trajectory_exports = /* @__PURE__ */ __exportAll({
|
|
3333
3664
|
AUDIT_HISTORY_MAX: () => 50,
|
|
@@ -3678,6 +4009,12 @@ function registerAutopilot(ctx) {
|
|
|
3678
4009
|
try {
|
|
3679
4010
|
ctx.on("serenity/settings-changed", () => startTimer());
|
|
3680
4011
|
} catch {}
|
|
4012
|
+
registerDisposer(ctx, "autopilot 时钟", () => {
|
|
4013
|
+
if (timer) {
|
|
4014
|
+
clearInterval(timer);
|
|
4015
|
+
timer = null;
|
|
4016
|
+
}
|
|
4017
|
+
});
|
|
3681
4018
|
}
|
|
3682
4019
|
/**
|
|
3683
4020
|
* 目标 agent:从 SESSION.md 反向定位 dsh 会话(cwd 归属校验 + **bound 精确匹配优先**,
|
|
@@ -3697,8 +4034,8 @@ function resolveTargetAgent(ctx, mdPath) {
|
|
|
3697
4034
|
const idMatch = dirName.match(/--S(\d{3,})--/);
|
|
3698
4035
|
const sid = idMatch ? `S${idMatch[1]}` : null;
|
|
3699
4036
|
const targetRoot = findSerenityRoot(mdPath);
|
|
3700
|
-
const sessions = ctx
|
|
3701
|
-
const agents = ctx
|
|
4037
|
+
const sessions = hostSessions(ctx);
|
|
4038
|
+
const agents = hostAgents(ctx);
|
|
3702
4039
|
const candidates = [];
|
|
3703
4040
|
try {
|
|
3704
4041
|
for (const s of sessions?.list?.() ?? []) {
|
|
@@ -3764,7 +4101,7 @@ function diagnoseTargetUnavailable(ctx, mdPath) {
|
|
|
3764
4101
|
const sameCccTitles = [];
|
|
3765
4102
|
const agentMissing = { matched: false };
|
|
3766
4103
|
try {
|
|
3767
|
-
const sessions = ctx
|
|
4104
|
+
const sessions = hostSessions(ctx);
|
|
3768
4105
|
for (const s of sessions?.list?.() ?? []) {
|
|
3769
4106
|
const sess = s;
|
|
3770
4107
|
const cwd = sess?.header?.cwd ?? "";
|
|
@@ -3777,7 +4114,7 @@ function diagnoseTargetUnavailable(ctx, mdPath) {
|
|
|
3777
4114
|
const boundMatched = bound !== null && bound.dirName === dirName;
|
|
3778
4115
|
const titleMatched = sid !== null && title !== null && (title === sid || title.startsWith(`${sid}-`));
|
|
3779
4116
|
if (boundMatched || titleMatched) {
|
|
3780
|
-
if (!ctx
|
|
4117
|
+
if (!hostAgents(ctx)?.get?.(sess.id ?? "")) agentMissing.matched = true;
|
|
3781
4118
|
}
|
|
3782
4119
|
}
|
|
3783
4120
|
} catch {}
|
|
@@ -3790,7 +4127,7 @@ function resolveAutopilotRoot(ctx) {
|
|
|
3790
4127
|
const fromCwd = findSerenityRoot(process.cwd());
|
|
3791
4128
|
if (fromCwd) return fromCwd;
|
|
3792
4129
|
try {
|
|
3793
|
-
const sessions = ctx
|
|
4130
|
+
const sessions = hostSessions(ctx);
|
|
3794
4131
|
for (const s of sessions?.list?.() ?? []) {
|
|
3795
4132
|
const r = findSerenityRoot(s?.header?.cwd ?? "");
|
|
3796
4133
|
if (r) return r;
|
|
@@ -3843,7 +4180,7 @@ function getAutopilotStatus(root) {
|
|
|
3843
4180
|
function listLiveSessions(ctx) {
|
|
3844
4181
|
const out = [];
|
|
3845
4182
|
try {
|
|
3846
|
-
const sessions = ctx
|
|
4183
|
+
const sessions = hostSessions(ctx);
|
|
3847
4184
|
for (const s of sessions?.list?.() ?? []) {
|
|
3848
4185
|
const cwd = s?.header?.cwd ?? null;
|
|
3849
4186
|
out.push({
|
|
@@ -4457,7 +4794,7 @@ function rebuildReminderText(tokensK, thresholdK, escalated = false) {
|
|
|
4457
4794
|
/** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
|
|
4458
4795
|
function readContextPressure(ctx, session) {
|
|
4459
4796
|
try {
|
|
4460
|
-
const projections = ctx
|
|
4797
|
+
const projections = hostService(ctx, "sessionProjections");
|
|
4461
4798
|
if (!projections) return null;
|
|
4462
4799
|
const pressure = (projections.snapshot?.(session))?.values?.contextPressure;
|
|
4463
4800
|
if (!pressure || typeof pressure.projectedTokens !== "number") return null;
|
|
@@ -5874,10 +6211,10 @@ function resolveWorkspaceCore(sessionCwd, workspaceParam, listCwds, fallback) {
|
|
|
5874
6211
|
}
|
|
5875
6212
|
function resolveWorkspace(ctx, params) {
|
|
5876
6213
|
let sessionCwd;
|
|
5877
|
-
if (params.sessionId) sessionCwd = (ctx
|
|
6214
|
+
if (params.sessionId) sessionCwd = (hostSessions(ctx)?.get?.(params.sessionId))?.header?.cwd;
|
|
5878
6215
|
let listCwds = () => [];
|
|
5879
6216
|
try {
|
|
5880
|
-
const sessions = ctx
|
|
6217
|
+
const sessions = hostSessions(ctx);
|
|
5881
6218
|
if (typeof sessions?.list === "function") listCwds = () => sessions.list().map((s) => s.header?.cwd ?? "").filter(Boolean);
|
|
5882
6219
|
} catch {}
|
|
5883
6220
|
return resolveWorkspaceCore(sessionCwd, params.workspace, listCwds, process.cwd());
|
|
@@ -6032,8 +6369,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6032
6369
|
const settings = readAdvancedSettings();
|
|
6033
6370
|
let known = [];
|
|
6034
6371
|
try {
|
|
6035
|
-
|
|
6036
|
-
known = projectKnownWorkspaces(registry?.list?.() ?? [], settings.gateway.workspaces);
|
|
6372
|
+
known = projectKnownWorkspaces(hostService(ctx, "workspaceRegistry")?.list?.() ?? [], settings.gateway.workspaces);
|
|
6037
6373
|
} catch {}
|
|
6038
6374
|
sendJson$1(res, 200, {
|
|
6039
6375
|
config: toWire(settings),
|
|
@@ -6071,7 +6407,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6071
6407
|
workspace: url.searchParams.get("workspace") ?? void 0
|
|
6072
6408
|
});
|
|
6073
6409
|
const root = findSerenityRoot(workspace) ?? "";
|
|
6074
|
-
const { discoverCccs } = await import("./skiff-debug-
|
|
6410
|
+
const { discoverCccs } = await import("./skiff-debug-8pF60JBO.js").then((n) => n.i);
|
|
6075
6411
|
sendJson$1(res, 200, { cccs: await discoverCccs(ctx, root) });
|
|
6076
6412
|
} catch (err) {
|
|
6077
6413
|
sendJson$1(res, 400, { error: err.message ?? String(err) });
|
|
@@ -6101,7 +6437,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6101
6437
|
return;
|
|
6102
6438
|
}
|
|
6103
6439
|
const settings = readAdvancedSettings();
|
|
6104
|
-
const { readSimpleSettings } = await import("./settings-section-
|
|
6440
|
+
const { readSimpleSettings } = await import("./settings-section-BPk0e1du.js").then((n) => n.r);
|
|
6105
6441
|
const simple = readSimpleSettings();
|
|
6106
6442
|
const allowed = settings.publicAsk.allowed;
|
|
6107
6443
|
const port = simple.acpHttpPort ?? 3100;
|
|
@@ -6197,11 +6533,11 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6197
6533
|
sendJson$1(res, 400, { error: chk.error });
|
|
6198
6534
|
return;
|
|
6199
6535
|
}
|
|
6200
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
6536
|
+
const { readWeixinSettings } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6201
6537
|
const { weixinBridgeStatus } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
6202
6538
|
const settings = readWeixinSettings(chk.root);
|
|
6203
6539
|
const bridge = weixinBridgeStatus().find((b) => b.ccc === chk.root);
|
|
6204
|
-
const { readWeixinCredential } = await import("./weixin-route-
|
|
6540
|
+
const { readWeixinCredential } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6205
6541
|
const accounts = (settings.accounts ?? []).map((a) => ({
|
|
6206
6542
|
accountId: a.accountId,
|
|
6207
6543
|
name: a.name ?? void 0,
|
|
@@ -6233,7 +6569,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6233
6569
|
const { fetchQRCode } = await import("./weixin-api-OUCuw0Ws.js").then((n) => n.l);
|
|
6234
6570
|
const { randomUUID } = await import("node:crypto");
|
|
6235
6571
|
for (const [key, v] of weixinLogins) if (Date.now() - v.startedAt > WEIXIN_LOGIN_TTL_MS) weixinLogins.delete(key);
|
|
6236
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
6572
|
+
const { readWeixinSettings } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6237
6573
|
const qr = await fetchQRCode({ botType: readWeixinSettings(root).botType ?? void 0 });
|
|
6238
6574
|
const loginKey = randomUUID();
|
|
6239
6575
|
weixinLogins.set(loginKey, {
|
|
@@ -6254,7 +6590,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6254
6590
|
sendJson$1(res, 400, { error: "missing accountId" });
|
|
6255
6591
|
return;
|
|
6256
6592
|
}
|
|
6257
|
-
const { removeWeixinAccount } = await import("./weixin-route-
|
|
6593
|
+
const { removeWeixinAccount } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6258
6594
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
6259
6595
|
removeWeixinAccount(root, body.accountId);
|
|
6260
6596
|
syncCccBridge(ctx, root);
|
|
@@ -6267,8 +6603,8 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6267
6603
|
sendJson$1(res, 400, { error: "invalid routes (expected [{user, role}, ...])" });
|
|
6268
6604
|
return;
|
|
6269
6605
|
}
|
|
6270
|
-
const { saveWeixinRoutes } = await import("./weixin-route-
|
|
6271
|
-
const { readSkiffRoles } = await import("./skiff-role-
|
|
6606
|
+
const { saveWeixinRoutes } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6607
|
+
const { readSkiffRoles } = await import("./skiff-role-DlrbHPLD.js").then((n) => n.c);
|
|
6272
6608
|
const roles = readSkiffRoles(root);
|
|
6273
6609
|
for (const r of routes) if (!roles.has(r.role)) {
|
|
6274
6610
|
sendJson$1(res, 400, { error: `unknown role: ${r.role} (not in ${root} skiff.roles)` });
|
|
@@ -6280,7 +6616,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6280
6616
|
}
|
|
6281
6617
|
if (body.action === "set-enabled") {
|
|
6282
6618
|
const enabled = body.enabled === true;
|
|
6283
|
-
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-
|
|
6619
|
+
const { readWeixinSettings, setWeixinEnabled } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6284
6620
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
6285
6621
|
const settings = readWeixinSettings(root);
|
|
6286
6622
|
if (enabled && (settings.accounts ?? []).length === 0) {
|
|
@@ -6327,7 +6663,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6327
6663
|
return;
|
|
6328
6664
|
}
|
|
6329
6665
|
const { pollQRStatus } = await import("./weixin-api-OUCuw0Ws.js").then((n) => n.l);
|
|
6330
|
-
const { readWeixinSettings } = await import("./weixin-route-
|
|
6666
|
+
const { readWeixinSettings } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6331
6667
|
const settings = readWeixinSettings(login.root);
|
|
6332
6668
|
const status = await pollQRStatus({
|
|
6333
6669
|
baseUrl: void 0,
|
|
@@ -6342,7 +6678,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
6342
6678
|
});
|
|
6343
6679
|
return;
|
|
6344
6680
|
}
|
|
6345
|
-
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-
|
|
6681
|
+
const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./weixin-route-BJ4g4ZNq.js").then((n) => n.u);
|
|
6346
6682
|
const { syncCccBridge } = await Promise.resolve().then(() => weixin_bridge_exports);
|
|
6347
6683
|
const accountId = nextWeixinAccountId(settings);
|
|
6348
6684
|
upsertWeixinAccount(login.root, {
|
|
@@ -6859,6 +7195,38 @@ function workspaceDenyResponse(rpcId) {
|
|
|
6859
7195
|
});
|
|
6860
7196
|
}
|
|
6861
7197
|
/**
|
|
7198
|
+
* 判定 workspace 创建请求的端点(v1.30.6,review F-03)。
|
|
7199
|
+
*
|
|
7200
|
+
* 宿主 rc.1 的 Remote 端点由 `typertEndpoint({namespace,method})` 派生为
|
|
7201
|
+
* `workspace/create`,因此浏览器实际请求 `POST /api/workspace/create`
|
|
7202
|
+
* (packages/typert/registry/src/service.ts:63-69;client/connection/src/client/rpc.ts:44)。
|
|
7203
|
+
* 旧实现只匹配 `/api/workspace.create` → 该分支从未命中,外部白名单与禁建校验失效。
|
|
7204
|
+
* 保留旧端点以兼容旧宿主。
|
|
7205
|
+
*/
|
|
7206
|
+
function isWorkspaceCreatePath(pathname) {
|
|
7207
|
+
return pathname === "/api/workspace/create" || pathname === "/api/workspace.create";
|
|
7208
|
+
}
|
|
7209
|
+
/**
|
|
7210
|
+
* 从 workspace 创建请求体解析 `{ rpcId, path }`(v1.30.6,review F-03)。
|
|
7211
|
+
*
|
|
7212
|
+
* rc.1 wire 信封:`{ type:'client-request', rpcId, method:'workspace/create',
|
|
7213
|
+
* payload:{ args:{ path } } }`(packages/api/gateway/src/index.ts:955 `args: payload.args`);
|
|
7214
|
+
* 旧宿主为 `payload.path`。解析失败返回 rpcId='unknown' + path=undefined。
|
|
7215
|
+
*/
|
|
7216
|
+
function parseWorkspaceCreateBody(body) {
|
|
7217
|
+
try {
|
|
7218
|
+
const parsed = JSON.parse(body);
|
|
7219
|
+
const rpcId = typeof parsed.rpcId === "string" ? parsed.rpcId : "unknown";
|
|
7220
|
+
const raw = parsed.payload?.args?.path ?? parsed.payload?.path;
|
|
7221
|
+
return typeof raw === "string" ? {
|
|
7222
|
+
rpcId,
|
|
7223
|
+
path: raw
|
|
7224
|
+
} : { rpcId };
|
|
7225
|
+
} catch {
|
|
7226
|
+
return { rpcId: "unknown" };
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
7229
|
+
/**
|
|
6862
7230
|
* 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过)
|
|
6863
7231
|
*/
|
|
6864
7232
|
function injectPolyfillHtml(html) {
|
|
@@ -7102,14 +7470,10 @@ function startGateway(config, getAccounts, getDshCookie) {
|
|
|
7102
7470
|
}
|
|
7103
7471
|
const session = authed(req);
|
|
7104
7472
|
if (session !== void 0) {
|
|
7105
|
-
if (req.method === "POST" && url.pathname
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
try {
|
|
7110
|
-
const parsed = JSON.parse(body);
|
|
7111
|
-
if (typeof parsed.rpcId === "string") rpcId = parsed.rpcId;
|
|
7112
|
-
} catch {}
|
|
7473
|
+
if (req.method === "POST" && isWorkspaceCreatePath(url.pathname)) {
|
|
7474
|
+
readBody$1(req, 131072).then((body) => {
|
|
7475
|
+
const { rpcId, path } = parseWorkspaceCreateBody(body);
|
|
7476
|
+
if (!allowWorkspaceCreate) {
|
|
7113
7477
|
res.writeHead(403, { "content-type": "application/json" });
|
|
7114
7478
|
res.end(JSON.stringify({
|
|
7115
7479
|
type: "server-response",
|
|
@@ -7123,31 +7487,8 @@ function startGateway(config, getAccounts, getDshCookie) {
|
|
|
7123
7487
|
}
|
|
7124
7488
|
}
|
|
7125
7489
|
}));
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
res.end(JSON.stringify({
|
|
7129
|
-
type: "server-response",
|
|
7130
|
-
rpcId: "unknown",
|
|
7131
|
-
result: {
|
|
7132
|
-
ok: false,
|
|
7133
|
-
error: {
|
|
7134
|
-
code: "bad-request",
|
|
7135
|
-
message: "body too large",
|
|
7136
|
-
details: {}
|
|
7137
|
-
}
|
|
7138
|
-
}
|
|
7139
|
-
}));
|
|
7140
|
-
});
|
|
7141
|
-
return;
|
|
7142
|
-
}
|
|
7143
|
-
readBody$1(req, 131072).then((body) => {
|
|
7144
|
-
let rpcId = "unknown";
|
|
7145
|
-
let path;
|
|
7146
|
-
try {
|
|
7147
|
-
const parsed = JSON.parse(body);
|
|
7148
|
-
if (typeof parsed.rpcId === "string") rpcId = parsed.rpcId;
|
|
7149
|
-
if (typeof parsed.payload?.path === "string") path = parsed.payload.path;
|
|
7150
|
-
} catch {}
|
|
7490
|
+
return;
|
|
7491
|
+
}
|
|
7151
7492
|
if (!workspaceAllowed(allowWorkspaces, path)) {
|
|
7152
7493
|
res.writeHead(403, { "content-type": "application/json" });
|
|
7153
7494
|
res.end(workspaceDenyResponse(rpcId));
|
|
@@ -7372,7 +7713,7 @@ function buildDshCookieProvider(ctx, mainPort) {
|
|
|
7372
7713
|
let diagnosed = false;
|
|
7373
7714
|
return () => {
|
|
7374
7715
|
try {
|
|
7375
|
-
const connection = ctx
|
|
7716
|
+
const connection = hostService(ctx, "connection");
|
|
7376
7717
|
if (!connection || typeof connection.authenticatedUrl !== "function" || typeof connection.authorizeIndex !== "function") {
|
|
7377
7718
|
if (!diagnosed) {
|
|
7378
7719
|
diagnosed = true;
|
|
@@ -7404,7 +7745,7 @@ function registerGateway(ctx) {
|
|
|
7404
7745
|
let lastSig = null;
|
|
7405
7746
|
const sync = () => {
|
|
7406
7747
|
try {
|
|
7407
|
-
const webServer = ctx
|
|
7748
|
+
const webServer = hostWebServer(ctx);
|
|
7408
7749
|
if (!webServer?.port) return;
|
|
7409
7750
|
const enabled = readSimpleSettings().gatewayEnabled;
|
|
7410
7751
|
const settings = readAdvancedSettings();
|
|
@@ -7455,6 +7796,11 @@ function registerGateway(ctx) {
|
|
|
7455
7796
|
ctx.on("serenity/settings-changed", () => {
|
|
7456
7797
|
sync();
|
|
7457
7798
|
});
|
|
7799
|
+
registerDisposer(ctx, "gateway 第二监听器", () => {
|
|
7800
|
+
current?.dispose();
|
|
7801
|
+
current = null;
|
|
7802
|
+
lastSig = null;
|
|
7803
|
+
});
|
|
7458
7804
|
}
|
|
7459
7805
|
//#endregion
|
|
7460
7806
|
//#region src/rebuild.ts
|
|
@@ -7702,7 +8048,7 @@ function renameAfterRebuild(ctx, agent, pending) {
|
|
|
7702
8048
|
dirName,
|
|
7703
8049
|
mdPath
|
|
7704
8050
|
}, pending.summary);
|
|
7705
|
-
const titles = ctx
|
|
8051
|
+
const titles = hostService(ctx, "sessionTitle");
|
|
7706
8052
|
if (!titles || typeof titles.rename !== "function") {
|
|
7707
8053
|
console.warn("[serenity-hooks] rebuild 后重命名跳过: sessionTitle 服务不可用");
|
|
7708
8054
|
return;
|
|
@@ -7738,7 +8084,7 @@ function registerRebuildTurnHook(ctx) {
|
|
|
7738
8084
|
}
|
|
7739
8085
|
pendingRebuilds.delete(id);
|
|
7740
8086
|
try {
|
|
7741
|
-
const tokenMeter = ctx
|
|
8087
|
+
const tokenMeter = hostService(ctx, "tokenMeter");
|
|
7742
8088
|
const meter = tokenMeter && typeof tokenMeter.estimateMessage === "function" ? { estimateMessage: (m) => tokenMeter.estimateMessage(m) } : void 0;
|
|
7743
8089
|
if (performRebuild(agent.session, pending, meter)) {
|
|
7744
8090
|
renameAfterRebuild(ctx, agent, pending);
|
|
@@ -8096,14 +8442,14 @@ var AcpServer = class {
|
|
|
8096
8442
|
sessionId: result.sessionId
|
|
8097
8443
|
};
|
|
8098
8444
|
}
|
|
8099
|
-
/** session/cancel:{ sessionId } →
|
|
8445
|
+
/** session/cancel:{ sessionId } → 取消该会话的当前活动(Agent.cancel,宿主 rc.1 契约) */
|
|
8100
8446
|
cancel(params) {
|
|
8101
8447
|
const sessionId = typeof params?.sessionId === "string" ? params.sessionId : void 0;
|
|
8102
8448
|
if (!sessionId) return { cancelled: false };
|
|
8103
8449
|
const agent = getSkiffAgent(sessionId);
|
|
8104
8450
|
if (!agent) return { cancelled: false };
|
|
8105
8451
|
try {
|
|
8106
|
-
agent.
|
|
8452
|
+
agent.cancel({ kind: "user" });
|
|
8107
8453
|
} catch {}
|
|
8108
8454
|
return { cancelled: true };
|
|
8109
8455
|
}
|
|
@@ -8909,6 +9255,7 @@ function invokeWeixinHook(root, hookRel, event) {
|
|
|
8909
9255
|
var weixin_bridge_exports = /* @__PURE__ */ __exportAll({
|
|
8910
9256
|
handleIncoming: () => handleIncoming,
|
|
8911
9257
|
registerWeixinBridge: () => registerWeixinBridge,
|
|
9258
|
+
stopAllBridges: () => stopAllBridges,
|
|
8912
9259
|
stopCccBridge: () => stopCccBridge,
|
|
8913
9260
|
syncCccBridge: () => syncCccBridge,
|
|
8914
9261
|
weixinBridgeStatus: () => weixinBridgeStatus
|
|
@@ -9188,6 +9535,11 @@ function stopCccBridge(root) {
|
|
|
9188
9535
|
for (const loop of bridge.loops.values()) loop.stopped = true;
|
|
9189
9536
|
bridges.delete(root);
|
|
9190
9537
|
}
|
|
9538
|
+
/** 停止全部桥(插件 dispose) */
|
|
9539
|
+
function stopAllBridges() {
|
|
9540
|
+
for (const bridge of bridges.values()) for (const loop of bridge.loops.values()) loop.stopped = true;
|
|
9541
|
+
bridges.clear();
|
|
9542
|
+
}
|
|
9191
9543
|
/** 桥状态快照(面板数据源):每 CCC → 每账号 → 轮询健康 */
|
|
9192
9544
|
function weixinBridgeStatus() {
|
|
9193
9545
|
const out = [];
|
|
@@ -9208,7 +9560,7 @@ function weixinBridgeStatus() {
|
|
|
9208
9560
|
function registerWeixinBridge(ctx) {
|
|
9209
9561
|
const syncFromLive = () => {
|
|
9210
9562
|
try {
|
|
9211
|
-
const cwds = (ctx
|
|
9563
|
+
const cwds = (hostSessions(ctx)?.list?.() ?? []).map((s) => s.header?.cwd ?? "").filter(Boolean);
|
|
9212
9564
|
const roots = [...new Set(cwds.map((c) => findSerenityRoot(c))).values()].filter((r) => r !== null);
|
|
9213
9565
|
for (const root of roots) syncCccBridge(ctx, root);
|
|
9214
9566
|
} catch {}
|
|
@@ -9219,6 +9571,53 @@ function registerWeixinBridge(ctx) {
|
|
|
9219
9571
|
} catch {}
|
|
9220
9572
|
}
|
|
9221
9573
|
//#endregion
|
|
9574
|
+
//#region src/seams/lifecycle.ts
|
|
9575
|
+
/** 从 Agent / Session / 事件负载中取会话 id(形状宽容:未知结构返回 null) */
|
|
9576
|
+
function sessionIdOf(value) {
|
|
9577
|
+
const v = value;
|
|
9578
|
+
const id = v?.session?.id ?? v?.id ?? v?.header?.id;
|
|
9579
|
+
return typeof id === "string" && id !== "" ? id : null;
|
|
9580
|
+
}
|
|
9581
|
+
/** 单会话内存态清理(幂等;无对应状态即 no-op) */
|
|
9582
|
+
function cleanupSessionState(sessionId) {
|
|
9583
|
+
try {
|
|
9584
|
+
unregisterSkiffSession(sessionId);
|
|
9585
|
+
} catch {}
|
|
9586
|
+
try {
|
|
9587
|
+
clearActiveSessionInfo(sessionId);
|
|
9588
|
+
} catch {}
|
|
9589
|
+
}
|
|
9590
|
+
/**
|
|
9591
|
+
* 装配生命周期订阅与资源拆卸。
|
|
9592
|
+
* @param ctx 宿主插件上下文
|
|
9593
|
+
*/
|
|
9594
|
+
function registerLifecycle(ctx) {
|
|
9595
|
+
try {
|
|
9596
|
+
ctx.on("agent/disposed", (payload) => {
|
|
9597
|
+
const id = sessionIdOf(payload?.agent);
|
|
9598
|
+
if (id) cleanupSessionState(id);
|
|
9599
|
+
});
|
|
9600
|
+
} catch {}
|
|
9601
|
+
try {
|
|
9602
|
+
ctx.on("session/disposed", (session) => {
|
|
9603
|
+
const id = sessionIdOf(session);
|
|
9604
|
+
if (id) cleanupSessionState(id);
|
|
9605
|
+
});
|
|
9606
|
+
} catch {}
|
|
9607
|
+
const disposeAll = () => {
|
|
9608
|
+
try {
|
|
9609
|
+
stopSkiffDebugServer();
|
|
9610
|
+
} catch {}
|
|
9611
|
+
try {
|
|
9612
|
+
stopAcpHttpServer();
|
|
9613
|
+
} catch {}
|
|
9614
|
+
try {
|
|
9615
|
+
stopAllBridges();
|
|
9616
|
+
} catch {}
|
|
9617
|
+
};
|
|
9618
|
+
registerDisposer(ctx, "self-started resources (skiff-debug/acp/weixin)", disposeAll);
|
|
9619
|
+
}
|
|
9620
|
+
//#endregion
|
|
9222
9621
|
//#region src/index.ts
|
|
9223
9622
|
const name = "dsh-serenity-hooks";
|
|
9224
9623
|
/** 主动调用的服务;其余(agent 事件)随 harness 装配必然存在
|
|
@@ -9262,10 +9661,14 @@ const Config = z.object({
|
|
|
9262
9661
|
})
|
|
9263
9662
|
});
|
|
9264
9663
|
function apply(ctx, config) {
|
|
9664
|
+
try {
|
|
9665
|
+
const report = probeHostContract(ctx, readDshVersion());
|
|
9666
|
+
if (report.issues.length > 0) console.warn(`[serenity-hooks] ${summarizeHostContract(report)}`);
|
|
9667
|
+
} catch {}
|
|
9265
9668
|
if (config.tools) {
|
|
9266
9669
|
ctx.tools.register(ccFsTool);
|
|
9267
9670
|
ctx.tools.register(createSessionTool(ctx));
|
|
9268
|
-
ctx.tools.register(
|
|
9671
|
+
ctx.tools.register(createKitTool(ctx));
|
|
9269
9672
|
ctx.tools.register(gitTool);
|
|
9270
9673
|
ctx.tools.register(msmTool);
|
|
9271
9674
|
ctx.tools.register(praxisTool);
|
|
@@ -9300,6 +9703,7 @@ function apply(ctx, config) {
|
|
|
9300
9703
|
registerAcp(ctx);
|
|
9301
9704
|
registerAutopilot(ctx);
|
|
9302
9705
|
registerWeixinBridge(ctx);
|
|
9706
|
+
registerLifecycle(ctx);
|
|
9303
9707
|
}
|
|
9304
9708
|
/**
|
|
9305
9709
|
* F4 Skiff 调试服务装配:启停 = 人工(设置面板 Skiff 区块开关,settings 持久化)。
|
|
@@ -9341,7 +9745,7 @@ function registerSkiff(ctx) {
|
|
|
9341
9745
|
function resolveSkiffRoot(ctx) {
|
|
9342
9746
|
const liveRoots = [];
|
|
9343
9747
|
try {
|
|
9344
|
-
const sessions = ctx
|
|
9748
|
+
const sessions = hostSessions(ctx);
|
|
9345
9749
|
for (const s of sessions?.list?.() ?? []) {
|
|
9346
9750
|
const cwd = s?.header?.cwd;
|
|
9347
9751
|
if (typeof cwd === "string") {
|
|
@@ -9358,7 +9762,7 @@ function resolveSkiffRoot(ctx) {
|
|
|
9358
9762
|
/** 主 WebUI 端口(WebUI 链接;webServer 未装配回退 3080) */
|
|
9359
9763
|
function readWebPort(ctx) {
|
|
9360
9764
|
try {
|
|
9361
|
-
const ws = ctx
|
|
9765
|
+
const ws = hostWebServer(ctx);
|
|
9362
9766
|
return typeof ws?.port === "number" ? ws.port : 3080;
|
|
9363
9767
|
} catch {
|
|
9364
9768
|
return 3080;
|