@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/kit-ops.d.ts
CHANGED
|
@@ -46,5 +46,5 @@ export interface RegistryHealthReport {
|
|
|
46
46
|
issues: string[];
|
|
47
47
|
}
|
|
48
48
|
export declare function checkRegistryHealth(root: string): RegistryHealthReport;
|
|
49
|
-
export declare function runKit(root: string | null, args: KitArgs): Promise<JsonValue>;
|
|
49
|
+
export declare function runKit(root: string | null, args: KitArgs, hostCtx?: unknown): Promise<JsonValue>;
|
|
50
50
|
export { loadSerenityConfig };
|
package/lib/localstore-ops.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export declare function checkLocalstoreGitCompliance(root: string): {
|
|
|
54
54
|
ok: boolean;
|
|
55
55
|
reason?: string;
|
|
56
56
|
};
|
|
57
|
+
/** 测试辅助:重置"坏 localstore"告警去重(生产零调用) */
|
|
58
|
+
export declare function __resetBrokenStoreWarningsForTest(): void;
|
|
57
59
|
/** 读取命名空间全部条目:credential → 扁平;config → 分节(剔除 credentials 保留节) */
|
|
58
60
|
export declare function readStore(root: string, scope: LocalStoreScope): Record<string, string> | Record<string, Record<string, string>>;
|
|
59
61
|
/** 校验凭据 key 合法(大写蛇形);不合法抛错 */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lifecycle.ts — 宿主生命周期订阅 + 资源拆卸(S142 review F-08)
|
|
3
|
+
*
|
|
4
|
+
* 两个此前缺失的收尾面:
|
|
5
|
+
*
|
|
6
|
+
* ① **会话/agent 销毁 → 清理 per-会话内存态**。dsp 有约 10 处 per-会话 Map
|
|
7
|
+
* (活跃 SESSION 作用域、skiff 注册表、keeper 计分、guard 白名单缓存、bootstrap
|
|
8
|
+
* 晋升状态、输出守卫状态…)。此前无人订阅 `agent/disposed` / `session/disposed`
|
|
9
|
+
* (宿主 0.1.2-rc.1 真实存在:`core/agent/src/runtime-types.ts:175`、
|
|
10
|
+
* `core/session/src/index.ts:62`),长跑进程里这些 Map 只增不减——且 `waitIdle`
|
|
11
|
+
* 在 agent 先被销毁时永不结算。
|
|
12
|
+
*
|
|
13
|
+
* ② **插件卸载/HMR/profile 重载 → 停掉自起的监听器与轮询器**。skiff 调试页、ACP
|
|
14
|
+
* HTTP 端点、微信桥轮询都是 dsp 自己起的资源;此前没有 `ctx.effect` 拆卸,
|
|
15
|
+
* 卸载后端口仍被占用(EADDRINUSE)、轮询器重复启动。
|
|
16
|
+
*
|
|
17
|
+
* 设计约束:清理与拆卸**一律吞掉异常**——生命周期回调里抛错会影响宿主的销毁流程
|
|
18
|
+
* (宿主对 `session/disposed` 监听器异常是"contained"语义,但没必要制造噪音)。
|
|
19
|
+
*/
|
|
20
|
+
import type { Context } from 'cordis';
|
|
21
|
+
/** 从 Agent / Session / 事件负载中取会话 id(形状宽容:未知结构返回 null) */
|
|
22
|
+
export declare function sessionIdOf(value: unknown): string | null;
|
|
23
|
+
/** 单会话内存态清理(幂等;无对应状态即 no-op) */
|
|
24
|
+
export declare function cleanupSessionState(sessionId: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* 装配生命周期订阅与资源拆卸。
|
|
27
|
+
* @param ctx 宿主插件上下文
|
|
28
|
+
*/
|
|
29
|
+
export declare function registerLifecycle(ctx: Context): void;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* system-prompt.ts —
|
|
2
|
+
* system-prompt.ts — Induction(成员装配机制,specs v1.5.0 命名)装配文本构造
|
|
3
|
+
*
|
|
4
|
+
* 机制:把新实例(新会话 / rebuild 重建 / 新 agent)引入为轨迹成员的装配机制——本文件装配的文本
|
|
5
|
+
* 即 Induction 骨架(8 块五层,specs README §5):A 主体定义(ACC/Principles/CCE)→ B 质量规范(EAP)
|
|
6
|
+
* → C 状态调节(状态块)→ D 工作供给(SKILL/Tools)→ E 任务指示(Session),稳定→易变装配序;
|
|
7
|
+
* Metaphor 为 A 层世界模型记忆钩渲染层(不计入 8 块骨架)。
|
|
3
8
|
*
|
|
4
9
|
* **完全对齐 opencode-serenity-plugin 的 system.transform 注入内容**(compacting.ts):
|
|
5
10
|
* 结构(v1.19.8 精简,S142 重建视角 R↓):
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as findSerenityRoot } from "./ccc-DAsSHsub.js";
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { basename, dirname, join } from "node:path";
|
|
3
4
|
//#region src/session-ops.ts
|
|
@@ -671,55 +672,108 @@ function qaCheck(root, key) {
|
|
|
671
672
|
if (verified) lines.push(" • Session looks clean — no issues detected");
|
|
672
673
|
return lines.join("\n");
|
|
673
674
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
if (!ev || ev.type !== "serenity/bound") return null;
|
|
682
|
-
const d = ev.data;
|
|
683
|
-
if (!d || typeof d.dirName !== "string" || typeof d.mdPath !== "string") return null;
|
|
684
|
-
return d;
|
|
675
|
+
/** 绑定文件相对 CCC 根的路径 */
|
|
676
|
+
const BINDINGS_REL_PATH = "AGENT_SESSIONS/.bindings.json";
|
|
677
|
+
const BINDINGS_VERSION = 1;
|
|
678
|
+
function sessionHeader(session) {
|
|
679
|
+
const header = session?.header;
|
|
680
|
+
if (!header || typeof header !== "object") return null;
|
|
681
|
+
return header;
|
|
685
682
|
}
|
|
686
|
-
/**
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
683
|
+
/** 绑定文件绝对路径(由会话 cwd 上溯 .serenity 定位 CCC 根);无法定位返回 null */
|
|
684
|
+
function bindingsPathFor(session) {
|
|
685
|
+
const header = sessionHeader(session);
|
|
686
|
+
if (typeof header?.cwd !== "string") return null;
|
|
687
|
+
const root = findSerenityRoot(header.cwd);
|
|
688
|
+
if (!root) return null;
|
|
689
|
+
return join(root, BINDINGS_REL_PATH);
|
|
690
|
+
}
|
|
691
|
+
function readBindingsFile(path) {
|
|
692
|
+
if (!existsSync(path)) return {
|
|
693
|
+
version: BINDINGS_VERSION,
|
|
694
|
+
sessions: {}
|
|
695
|
+
};
|
|
696
|
+
try {
|
|
697
|
+
const sessions = JSON.parse(readFileSync(path, "utf-8")).sessions;
|
|
698
|
+
if (!sessions || typeof sessions !== "object") return {
|
|
699
|
+
version: BINDINGS_VERSION,
|
|
700
|
+
sessions: {}
|
|
701
|
+
};
|
|
702
|
+
return {
|
|
703
|
+
version: BINDINGS_VERSION,
|
|
704
|
+
sessions
|
|
705
|
+
};
|
|
706
|
+
} catch {
|
|
707
|
+
return {
|
|
708
|
+
version: BINDINGS_VERSION,
|
|
709
|
+
sessions: {}
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
/** 原子写:同目录 tmp + rename(避免半写文件被后续读取) */
|
|
714
|
+
function writeBindingsFile(path, data) {
|
|
715
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
716
|
+
const tmp = `${path}.tmp`;
|
|
717
|
+
writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
|
|
718
|
+
renameSync(tmp, path);
|
|
719
|
+
}
|
|
720
|
+
/** 记录形状校验(容忍手改/旧数据) */
|
|
721
|
+
function asBoundRecord(value) {
|
|
722
|
+
const r = value;
|
|
723
|
+
if (!r || typeof r.dirName !== "string" || typeof r.mdPath !== "string") return null;
|
|
724
|
+
return r;
|
|
725
|
+
}
|
|
726
|
+
/** 旧形态:从会话日志扫最后一条 `serenity/bound`(兼容 v1.30.5 及更早写入的绑定) */
|
|
727
|
+
function readLegacyBoundFromEvents(session) {
|
|
692
728
|
const events = sessionEvents(session);
|
|
693
729
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
694
|
-
const
|
|
695
|
-
if (
|
|
730
|
+
const ev = events[i];
|
|
731
|
+
if (!ev || ev.type !== "serenity/bound") continue;
|
|
732
|
+
const rec = asBoundRecord(ev.data);
|
|
733
|
+
if (rec) return rec;
|
|
696
734
|
}
|
|
697
735
|
return null;
|
|
698
736
|
}
|
|
699
737
|
/**
|
|
700
|
-
*
|
|
701
|
-
*
|
|
702
|
-
|
|
738
|
+
* 读取会话当前绑定(权威,latest-wins):文件记录优先,无则回落旧事件形态。
|
|
739
|
+
* 无绑定返回 null。
|
|
740
|
+
*/
|
|
741
|
+
function readLastBound(session) {
|
|
742
|
+
const path = bindingsPathFor(session);
|
|
743
|
+
const id = sessionHeader(session)?.id;
|
|
744
|
+
if (path && typeof id === "string") {
|
|
745
|
+
const rec = asBoundRecord(readBindingsFile(path).sessions[id]);
|
|
746
|
+
if (rec) return rec;
|
|
747
|
+
}
|
|
748
|
+
return readLegacyBoundFromEvents(session);
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* 写入绑定(latest-wins 覆盖该会话记录)。
|
|
752
|
+
*
|
|
753
|
+
* 绑定持久化尽力而为:无法定位 CCC 根 / 无会话 id / 写盘失败 → 返回 false,不阻断主流程。
|
|
754
|
+
* @param session 目标 dsh 会话(需 `header.id` + `header.cwd` 可定位 CCC 根)
|
|
703
755
|
* @param action 绑定动作
|
|
704
756
|
* @param rec 绑定记录(dirName + mdPath 必填;sessionId 可选展示码)
|
|
705
|
-
* @returns 是否成功(append 抛错/会话不可用时 false——绑定失败不阻断主流程)
|
|
706
757
|
*/
|
|
707
758
|
function appendBound(session, action, rec) {
|
|
708
|
-
|
|
759
|
+
const path = bindingsPathFor(session);
|
|
760
|
+
const id = sessionHeader(session)?.id;
|
|
761
|
+
if (!path || typeof id !== "string") return false;
|
|
709
762
|
try {
|
|
710
|
-
const
|
|
711
|
-
|
|
763
|
+
const file = readBindingsFile(path);
|
|
764
|
+
file.sessions[id] = {
|
|
712
765
|
dirName: rec.dirName,
|
|
713
766
|
mdPath: rec.mdPath,
|
|
714
767
|
...rec.sessionId ? { sessionId: rec.sessionId } : {},
|
|
715
768
|
action,
|
|
716
769
|
at: Date.now(),
|
|
717
770
|
...rec.note ? { note: rec.note } : {}
|
|
718
|
-
}
|
|
771
|
+
};
|
|
772
|
+
writeBindingsFile(path, file);
|
|
719
773
|
return true;
|
|
720
774
|
} catch {
|
|
721
775
|
return false;
|
|
722
776
|
}
|
|
723
777
|
}
|
|
724
778
|
//#endregion
|
|
725
|
-
export {
|
|
779
|
+
export { summarize as C, showSession as S, readActiveSessionMd as _, archiveSessions as a, sessionsRoot as b, createSession as c, findSession as d, getActiveSessionInfo as f, qaCheck as g, parseSessionContextFromEvents as h, SESSION_ACTIONS as i, extractSessionMdPathFromText as l, listSessions as m, readLastBound as n, clearActiveSessionInfo as o, healthCheck as p, DEFAULT_SESSION_SCOPE as r, closeSession as s, appendBound as t, findLatestActiveSessionMd as u, resolveSessionByTitle as v, useSession as w, setActiveSessionInfo as x, sessionEvents as y };
|
package/lib/session-bound.d.ts
CHANGED
|
@@ -1,47 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* session-bound.ts — SESSION 绑定持久化(
|
|
2
|
+
* session-bound.ts — SESSION 绑定持久化(CCC 内 `AGENT_SESSIONS/.bindings.json`)
|
|
3
3
|
*
|
|
4
4
|
* 目标(S142 用户拍板,方案 v1.0):让「dsh 会话(载体)↔ SESSION(宁静号轨迹)」
|
|
5
5
|
* 的绑定坚固——LLM 不能因智力因素(幻觉/理解偏差)在过程中或 logbook rebuild 后
|
|
6
6
|
* 自行更换 SESSION。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* 存储形态(v1.30.6 起,S142 review F-01):绑定**不再写入 dsh 会话日志**。
|
|
9
|
+
* 原因(宿主契约实证):dsp 的自定义事件 `serenity/bound` 既不在宿主的
|
|
10
|
+
* `KNOWN_SESSION_EVENT_TYPES`(生成静态集,明确说明 out-of-repo 插件事件不在其中),
|
|
11
|
+
* 而 `Session.append` 构造事件时只写 `{type,seq,time,data,surfaceOp?,sourceEventSeqs?}`
|
|
12
|
+
* ——**没有写入 envelope `ignorable` 标记的通道**;宿主的 `assertEventsSupported`
|
|
13
|
+
* (session-persistence/coordinator.ts:1248-1252,调用点含恢复/HMR 路径)会拒绝任何
|
|
14
|
+
* "未知且非 ignorable" 的事件 → 含该事件的会话在冷加载时可能抛
|
|
15
|
+
* `SessionFormatUnsupportedError`。v1.29.1~v1.30.5 的设计文档虽写明要带
|
|
16
|
+
* `ignorable: true`(docs/session-binding-hardening-research.md:53),但类型层声明
|
|
17
|
+
* 不等于持久层标记,实际从未落盘。
|
|
13
18
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
19
|
+
* 现形态:每 CCC 一个 JSON 文件 `AGENT_SESSIONS/.bindings.json`(原子写:tmp + rename),
|
|
20
|
+
* 以 dsh 会话 id 为键,latest-wins 覆盖。**向后兼容**:读取时若文件中无该会话记录,
|
|
21
|
+
* 仍回落到扫描会话日志中的旧 `serenity/bound` 事件(老会话的绑定不丢)。
|
|
16
22
|
*
|
|
17
23
|
* 编码无关(U4):绑定锚 = **完整 AGENT_SESSIONS 目录名**(磁盘唯一存在),
|
|
18
24
|
* sessionId(S###/apaas-xxx/自定义)仅是派生展示字段——绝不假设 S 前缀。
|
|
19
25
|
*/
|
|
20
|
-
|
|
21
|
-
import type { SessionEventMap } from '@deepseek-ai/dsh-session/types';
|
|
22
|
-
declare module '@deepseek-ai/dsh-session/types' {
|
|
23
|
-
interface SessionEventMap {
|
|
24
|
-
/**
|
|
25
|
-
* Serenity SESSION binding — authoritative; appended on every binding change.
|
|
26
|
-
* Code-agnostic (keyed by full dir name), log-only (no surfaceOp).
|
|
27
|
-
*/
|
|
28
|
-
'serenity/bound': {
|
|
29
|
-
/** Full AGENT_SESSIONS dir name — the ONLY hard identity. */
|
|
30
|
-
dirName: string;
|
|
31
|
-
/** SESSION.md absolute path. */
|
|
32
|
-
mdPath: string;
|
|
33
|
-
/** Display code when parseable (S142 / apaas-26116 / custom); informational. */
|
|
34
|
-
sessionId?: string;
|
|
35
|
-
/** Why this binding record was written. */
|
|
36
|
-
action: 'activate' | 'switch' | 'create' | 'rebuild' | 'reconcile' | 'release';
|
|
37
|
-
/** Epoch ms. */
|
|
38
|
-
at: number;
|
|
39
|
-
/** Optional reason (e.g. 'auto from title'). */
|
|
40
|
-
note?: string;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
export type SessionBoundAction = SessionEventMap['serenity/bound']['action'];
|
|
26
|
+
export type SessionBoundAction = 'activate' | 'switch' | 'create' | 'rebuild' | 'reconcile' | 'release';
|
|
45
27
|
export interface SessionBoundRecord {
|
|
46
28
|
dirName: string;
|
|
47
29
|
mdPath: string;
|
|
@@ -50,29 +32,28 @@ export interface SessionBoundRecord {
|
|
|
50
32
|
at: number;
|
|
51
33
|
note?: string;
|
|
52
34
|
}
|
|
53
|
-
/**
|
|
35
|
+
/** 旧形态事件类型(v1.29.1~v1.30.5 写入会话日志;v1.30.6 起只读不写——兼容存量绑定) */
|
|
54
36
|
export declare const SESSION_BOUND_EVENT: "serenity/bound";
|
|
37
|
+
/** 绑定文件相对 CCC 根的路径 */
|
|
38
|
+
export declare const BINDINGS_REL_PATH = "AGENT_SESSIONS/.bindings.json";
|
|
39
|
+
/** 绑定文件绝对路径(由会话 cwd 上溯 .serenity 定位 CCC 根);无法定位返回 null */
|
|
40
|
+
export declare function bindingsPathFor(session: unknown): string | null;
|
|
55
41
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* 纯逻辑零 DSH 依赖(经 sessionEvents helper 读 snapshotEvents/events 兜底)。
|
|
42
|
+
* 读取会话当前绑定(权威,latest-wins):文件记录优先,无则回落旧事件形态。
|
|
43
|
+
* 无绑定返回 null。
|
|
59
44
|
*/
|
|
60
45
|
export declare function readLastBound(session: unknown): SessionBoundRecord | null;
|
|
61
|
-
/**
|
|
62
|
-
* 判定会话日志是否已有任何 bound 事件(供 reconcile 判定:无绑定才标题升级)。
|
|
63
|
-
*/
|
|
46
|
+
/** 判定会话是否已有绑定(供 reconcile 判定:无绑定才标题升级)。 */
|
|
64
47
|
export declare function hasAnyBound(session: unknown): boolean;
|
|
65
48
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
49
|
+
* 写入绑定(latest-wins 覆盖该会话记录)。
|
|
50
|
+
*
|
|
51
|
+
* 绑定持久化尽力而为:无法定位 CCC 根 / 无会话 id / 写盘失败 → 返回 false,不阻断主流程。
|
|
52
|
+
* @param session 目标 dsh 会话(需 `header.id` + `header.cwd` 可定位 CCC 根)
|
|
69
53
|
* @param action 绑定动作
|
|
70
54
|
* @param rec 绑定记录(dirName + mdPath 必填;sessionId 可选展示码)
|
|
71
|
-
* @returns 是否成功(append 抛错/会话不可用时 false——绑定失败不阻断主流程)
|
|
72
55
|
*/
|
|
73
|
-
export declare function appendBound(session:
|
|
74
|
-
append: (type: string, data: unknown) => unknown;
|
|
75
|
-
} | null | undefined, action: SessionBoundAction, rec: {
|
|
56
|
+
export declare function appendBound(session: unknown, action: SessionBoundAction, rec: {
|
|
76
57
|
dirName: string;
|
|
77
58
|
mdPath: string;
|
|
78
59
|
sessionId?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { a as hostSettings } from "./access-CdL6BAYj.js";
|
|
2
3
|
import z from "@deepseek-ai/schemastery";
|
|
3
4
|
//#region src/settings-section.ts
|
|
4
5
|
var settings_section_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -39,6 +40,8 @@ function entryDefaults(config) {
|
|
|
39
40
|
}
|
|
40
41
|
/** 运行时源(installSettingsSection 注入:settings scope 或 entry fallback) */
|
|
41
42
|
let simpleSource = null;
|
|
43
|
+
/** 缺 settings provider 的告警去重(进程级一次;降级路径可能被多次注册调用) */
|
|
44
|
+
let warnedNoSettingsProvider = false;
|
|
42
45
|
/** 进程级默认(无 settings 服务时的兜底) */
|
|
43
46
|
function defaultSimpleSettings() {
|
|
44
47
|
return {
|
|
@@ -79,11 +82,15 @@ function registerSettingsSection(ctx, config) {
|
|
|
79
82
|
} catch {}
|
|
80
83
|
}
|
|
81
84
|
};
|
|
82
|
-
const settingsAny = ctx
|
|
85
|
+
const settingsAny = hostSettings(ctx);
|
|
83
86
|
if (settingsAny) {
|
|
84
87
|
settingsAny.installSection.call(settingsAny, ctx, SERENITY_SETTINGS_NS, simpleSettingsSchema, entryDefaults(config), hooks);
|
|
85
88
|
return;
|
|
86
89
|
}
|
|
90
|
+
if (!warnedNoSettingsProvider) {
|
|
91
|
+
warnedNoSettingsProvider = true;
|
|
92
|
+
console.warn("[serenity-hooks] ✗ settings 服务不可用 → 简单配置降级为 entry 默认值(面板开关/阈值改动不生效;检查宿主 settings provider 是否装载)");
|
|
93
|
+
}
|
|
87
94
|
hooks.setSource(() => ({}));
|
|
88
95
|
hooks.onChange();
|
|
89
96
|
}
|
|
@@ -92,6 +92,8 @@ export declare const simpleSettingsSchema: z<Schemastery.ObjectS<{
|
|
|
92
92
|
}>>;
|
|
93
93
|
/** 从插件 Config 提取 entry 默认(settings base 层) */
|
|
94
94
|
export declare function entryDefaults(config: SimpleConfigFragment): SerenitySimpleSettings;
|
|
95
|
+
/** 测试辅助:重置"缺 settings provider"告警去重(生产零调用) */
|
|
96
|
+
export declare function __resetSettingsWarningsForTest(): void;
|
|
95
97
|
/** 进程级默认(无 settings 服务时的兜底) */
|
|
96
98
|
export declare function defaultSimpleSettings(): SerenitySimpleSettings;
|
|
97
99
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { a as findSerenityRoot, f as readHandymanConfig } from "./ccc-
|
|
3
|
-
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, n as buildSkiffBasePrompt, o as roleMsmWhitelist, r as isSkiffSessionId, u as trajectorySubset } from "./skiff-role-
|
|
4
|
-
import {
|
|
2
|
+
import { a as findSerenityRoot, f as readHandymanConfig } from "./ccc-DAsSHsub.js";
|
|
3
|
+
import { a as resolveRoleSystemPrompt, i as readSkiffRoles, n as buildSkiffBasePrompt, o as roleMsmWhitelist, r as isSkiffSessionId, u as trajectorySubset } from "./skiff-role-DlrbHPLD.js";
|
|
4
|
+
import { i as hostSessions, r as hostService, t as hostAgents } from "./access-CdL6BAYj.js";
|
|
5
|
+
import { c as createSession, n as readLastBound, t as appendBound, x as setActiveSessionInfo } from "./session-bound-D2ANqVn-.js";
|
|
5
6
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
6
7
|
import { basename, join } from "node:path";
|
|
7
8
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
@@ -210,6 +211,47 @@ function listActiveHandymen(root) {
|
|
|
210
211
|
return out;
|
|
211
212
|
}
|
|
212
213
|
//#endregion
|
|
214
|
+
//#region src/agent-idle.ts
|
|
215
|
+
/**
|
|
216
|
+
* 等待 agent 空闲或销毁。
|
|
217
|
+
* @param ctx 宿主插件上下文(事件通道来源)
|
|
218
|
+
* @param agent 目标 agent
|
|
219
|
+
* @returns 空闲、agent 销毁、或会话销毁时结算(永不 reject)
|
|
220
|
+
*/
|
|
221
|
+
function waitAgentIdle(ctx, agent) {
|
|
222
|
+
return new Promise((resolve) => {
|
|
223
|
+
let settled = false;
|
|
224
|
+
const disposers = [];
|
|
225
|
+
const finish = () => {
|
|
226
|
+
if (settled) return;
|
|
227
|
+
settled = true;
|
|
228
|
+
for (const dispose of disposers) try {
|
|
229
|
+
dispose();
|
|
230
|
+
} catch {}
|
|
231
|
+
resolve();
|
|
232
|
+
};
|
|
233
|
+
const subscribe = (name, cb) => {
|
|
234
|
+
try {
|
|
235
|
+
const off = ctx.on?.(name, cb);
|
|
236
|
+
if (typeof off === "function") disposers.push(off);
|
|
237
|
+
} catch {}
|
|
238
|
+
};
|
|
239
|
+
subscribe("agent/status", (payload) => {
|
|
240
|
+
const p = payload;
|
|
241
|
+
if (p?.agent === agent && p?.status === "idle") finish();
|
|
242
|
+
});
|
|
243
|
+
subscribe("agent/disposed", (payload) => {
|
|
244
|
+
if (payload?.agent === agent) finish();
|
|
245
|
+
});
|
|
246
|
+
const sessionId = agent.session?.id;
|
|
247
|
+
subscribe("session/disposed", (session) => {
|
|
248
|
+
const id = session?.id;
|
|
249
|
+
if (typeof id === "string" && id === sessionId) finish();
|
|
250
|
+
});
|
|
251
|
+
if (agent.status === "idle") finish();
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
213
255
|
//#region src/skiff-core.ts
|
|
214
256
|
const PLUGIN_SOURCE = {
|
|
215
257
|
kind: "plugin",
|
|
@@ -380,7 +422,7 @@ async function createSkiffAgent(ctx, root, roleName, role, defaultModel, session
|
|
|
380
422
|
/** 取 live agent(DSH 文档:`ctx.agents.get(id)` 返回 bare Agent;会话 live 时 resume/create 均不可用) */
|
|
381
423
|
function getLiveAgent(ctx, id) {
|
|
382
424
|
try {
|
|
383
|
-
return ctx
|
|
425
|
+
return hostAgents(ctx)?.get?.(id);
|
|
384
426
|
} catch {
|
|
385
427
|
return;
|
|
386
428
|
}
|
|
@@ -462,22 +504,6 @@ async function createOrResumeAgent(ctx, id, root, model, setup, fixedId) {
|
|
|
462
504
|
};
|
|
463
505
|
}
|
|
464
506
|
}
|
|
465
|
-
/** 等待 agent 空闲(agent/status → idle);无超时(agent 工作多久等多久,handyman 同款) */
|
|
466
|
-
function waitIdle(ctx, agent) {
|
|
467
|
-
return new Promise((resolve) => {
|
|
468
|
-
let settled = false;
|
|
469
|
-
let dispose = () => {};
|
|
470
|
-
const finish = () => {
|
|
471
|
-
if (settled) return;
|
|
472
|
-
settled = true;
|
|
473
|
-
dispose();
|
|
474
|
-
resolve();
|
|
475
|
-
};
|
|
476
|
-
dispose = ctx.on("agent/status", (payload) => {
|
|
477
|
-
if (payload.agent === agent && payload.status === "idle") finish();
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
507
|
/**
|
|
482
508
|
* 会话事件读取(v1.28.0 适配 0.1.2-rc.1):rc.1 Session 移除 `.events` 属性 → `snapshotEvents()` 方法。
|
|
483
509
|
* 兼容双形态(运行时 rc.1 snapshotEvents;测试替身 events)——同一函数两处语义,测试注入零侵入。
|
|
@@ -563,7 +589,7 @@ async function askSkiff(ctx, agent, question, eventsStart, options) {
|
|
|
563
589
|
}],
|
|
564
590
|
source: PLUGIN_SOURCE
|
|
565
591
|
}));
|
|
566
|
-
await
|
|
592
|
+
await waitAgentIdle(ctx, agent);
|
|
567
593
|
const answer = lastAssistantText(agent);
|
|
568
594
|
const trajectory = options?.includeTrajectory === false ? [] : eventsToTrajectory(sessionEvents(agent.session).slice(before));
|
|
569
595
|
return {
|
|
@@ -2324,15 +2350,15 @@ async function discoverCccs(ctx, defaultRoot) {
|
|
|
2324
2350
|
if (r && !roots.includes(r)) roots.push(r);
|
|
2325
2351
|
};
|
|
2326
2352
|
try {
|
|
2327
|
-
const registry = ctx
|
|
2353
|
+
const registry = hostService(ctx, "workspaceRegistry");
|
|
2328
2354
|
for (const ws of registry?.list?.() ?? []) pushRoot(ws?.path);
|
|
2329
2355
|
} catch {}
|
|
2330
2356
|
if (roots.length === 0) try {
|
|
2331
|
-
const sp = ctx
|
|
2357
|
+
const sp = hostService(ctx, "sessionPersistence");
|
|
2332
2358
|
for (const h of await sp?.list?.() ?? []) pushRoot(h?.cwd);
|
|
2333
2359
|
} catch {}
|
|
2334
2360
|
if (roots.length === 0) try {
|
|
2335
|
-
const sessions = ctx
|
|
2361
|
+
const sessions = hostSessions(ctx);
|
|
2336
2362
|
for (const s of sessions?.list?.() ?? []) pushRoot(s?.header?.cwd);
|
|
2337
2363
|
} catch {}
|
|
2338
2364
|
if (!roots.includes(defaultRoot)) roots.unshift(defaultRoot);
|
|
@@ -2748,4 +2774,4 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
2748
2774
|
}
|
|
2749
2775
|
}
|
|
2750
2776
|
//#endregion
|
|
2751
|
-
export {
|
|
2777
|
+
export { newStopToken as C, writeFailedStatus as D, splitModel as E, writeProgress as O, listActiveHandymen as S, requireWhitelistedModel as T, workspaceTrajectoryLine as _, startSkiffDebugServer as a, buildRoundPrompt as b, askSkiff as c, getSkiffAgent as d, skiffMsmGate as f, unregisterSkiffSession as g, skiffTrajectoryEnabled as h, skiff_debug_exports as i, skiffRoleFor$1 as k, createSkiffAgent as l, skiffSessionSnapshot as m, jscSafeJsonText as n, stopSkiffDebugServer as o, skiffSessionInfo as p, renderSkiffMarkdown as r, stripThink as s, discoverCccs as t, ensureSkiffSession as u, waitAgentIdle as v, readProgress as w, handymanProgressPaths as x, HANDYMAN_GUIDE as y };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { m as resolveInside, p as readUtf8, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
+
import { m as resolveInside, p as readUtf8, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-DAsSHsub.js";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
//#region src/skiff-role.ts
|
|
5
5
|
/**
|
|
@@ -54,7 +54,9 @@ function readSkiffRoles(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
54
54
|
systemPromptFile: typeof role.systemPromptFile === "string" ? role.systemPromptFile : void 0
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
} catch {
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.warn(`[serenity-hooks] ✗ skiff 角色配置读取失败(按无角色继续): ${String(err?.message ?? err)}`);
|
|
59
|
+
}
|
|
58
60
|
return out;
|
|
59
61
|
}
|
|
60
62
|
function trajectorySubset(role) {
|
package/lib/tools/handyman.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* 机制:ctx.agents.create()(带 setup 钩子)创建专用 agent(进程内),
|
|
14
14
|
* 每轮 followup → agent/status idle → 读 session.events 响应 → 写进度 → stop token 检查 →
|
|
15
|
-
* 未完成继续下一轮;followup
|
|
15
|
+
* 未完成继续下一轮;followup/等待空闲抛错(非正常停止)→ dispose 并重新 create agent
|
|
16
16
|
* (重启计数,≤100),同一轮重试。工厂模式:apply 时闭包捕获插件 ctx(工具 execute 无 ctx 参数)。
|
|
17
17
|
*
|
|
18
18
|
* preset 继承 + 工具收窄:setup 钩子里对子 agent 执行 agentPresets.composeFrom(对齐
|
package/lib/tools/kit.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* kit.ts — dashboard 真实 DSH 工具定义(defineTool)(v1.30:acc_kit → dashboard)
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
import type { Context } from 'cordis';
|
|
5
|
+
/**
|
|
6
|
+
* dashboard 工具(v1.30.7:闭包捕获 ctx → health 可附宿主契约探针 F-05)。
|
|
7
|
+
* ctx 仅用于**只读探针**(`ctx.get` / 属性读取),无副作用。
|
|
8
|
+
*/
|
|
9
|
+
export declare function createKitTool(ctx: Context): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
+
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-DAsSHsub.js";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -77,7 +77,9 @@ function checkLocalstoreGitCompliance(root) {
|
|
|
77
77
|
reason: `localstore.json must not be committed (localstore.gitTrack=deny default) but .gitignore does not cover it — add ${LOCALSTORE_FILENAME} to .gitignore (or set localstore.gitTrack=allow to explicitly permit)`
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
-
/**
|
|
80
|
+
/** 已告警的坏 localstore 路径(进程级去重;F-07:凭据文件损坏不得静默) */
|
|
81
|
+
const warnedBrokenStores = /* @__PURE__ */ new Set();
|
|
82
|
+
/** 读取全文件(顶层分节);文件不存在 → 空;**坏 JSON → 空 + 响亮告警**(每路径一次) */
|
|
81
83
|
function readAll(root) {
|
|
82
84
|
const p = localstorePath(root);
|
|
83
85
|
if (!existsSync(p)) return {};
|
|
@@ -85,7 +87,11 @@ function readAll(root) {
|
|
|
85
87
|
const v = JSON.parse(readFileSync(p, "utf-8").replace(/^\uFEFF/, ""));
|
|
86
88
|
if (v && typeof v === "object" && !Array.isArray(v)) return v;
|
|
87
89
|
return {};
|
|
88
|
-
} catch {
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (!warnedBrokenStores.has(p)) {
|
|
92
|
+
warnedBrokenStores.add(p);
|
|
93
|
+
console.warn(`[serenity-hooks] ✗ localstore 解析失败,已按空存储继续(其中凭据/配置对工具表现为"未设置",请修 JSON): ${p} — ${String(err?.message ?? err)}`);
|
|
94
|
+
}
|
|
89
95
|
return {};
|
|
90
96
|
}
|
|
91
97
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.30.
|
|
3
|
+
"version": "1.30.8",
|
|
4
4
|
"description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|