@shgroup/dsh-serenity-hooks 1.30.5 → 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/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/{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/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.30.
|
|
3
|
+
"version": "1.30.8",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 container_fs/logbook/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/autopilot-trajectory + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)+ Skiff 认知子集角色(实验性)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/host/access.ts
|
|
2
|
+
/**
|
|
3
|
+
* host/access.ts — 宿主访问收口(S142 review F-06)
|
|
4
|
+
*
|
|
5
|
+
* 为什么存在:dsp 此前在 30+ 个文件里各自用 `(ctx as unknown as {…})` 断言读取宿主
|
|
6
|
+
* 服务——契约表达分散、失败模式各异(多数静默 undefined)。本模块是**唯一**读取宿主
|
|
7
|
+
* 的入口:所有 `ctx.get` / `ctx.<service>` 的形状断言集中在此,调用点只拿类型化结果。
|
|
8
|
+
*
|
|
9
|
+
* 约定:
|
|
10
|
+
* - 只读、无副作用;服务缺失一律返回 `undefined`(调用方决定降级策略),**不抛错**
|
|
11
|
+
* (宿主对插件 apply 抛错 = 整个 dsh 启动失败,访问层不能成为单点)。
|
|
12
|
+
* - 形状以 `AI_LAB/dsh-harness-public` @ 0.1.2-rc.1 为准;成员缺失由
|
|
13
|
+
* `host/contract.ts` 的探针在装载时与 `dashboard health` 中报告。
|
|
14
|
+
* - 迁移是渐进的:新代码必须走本模块;旧调用点逐轮收敛(F-06 轮次记录见 CHANGELOG)。
|
|
15
|
+
*/
|
|
16
|
+
/** 通用读取:`ctx.get(name)`(含异常吞掉——服务 getter 抛错视为不可用) */
|
|
17
|
+
function hostService(ctx, name) {
|
|
18
|
+
const c = ctx;
|
|
19
|
+
if (typeof c?.get !== "function") return void 0;
|
|
20
|
+
try {
|
|
21
|
+
return c.get(name);
|
|
22
|
+
} catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** 直接属性读取(injected 服务:`ctx.<name>`),属性缺失时回落 `ctx.get` */
|
|
27
|
+
function hostInjected(ctx, name) {
|
|
28
|
+
const direct = ctx?.[name];
|
|
29
|
+
if (direct !== void 0) return direct;
|
|
30
|
+
return hostService(ctx, name);
|
|
31
|
+
}
|
|
32
|
+
/** `ctx.sessions`(injected) */
|
|
33
|
+
function hostSessions(ctx) {
|
|
34
|
+
return hostInjected(ctx, "sessions");
|
|
35
|
+
}
|
|
36
|
+
/** `ctx.agents`(injected) */
|
|
37
|
+
function hostAgents(ctx) {
|
|
38
|
+
return hostInjected(ctx, "agents");
|
|
39
|
+
}
|
|
40
|
+
/** `ctx.webServer`(injected) */
|
|
41
|
+
function hostWebServer(ctx) {
|
|
42
|
+
return hostInjected(ctx, "webServer");
|
|
43
|
+
}
|
|
44
|
+
/** `ctx.settings`(injected;提供 settings 面板装配通道) */
|
|
45
|
+
function hostSettings(ctx) {
|
|
46
|
+
return hostInjected(ctx, "settings");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { hostSettings as a, hostSessions as i, hostInjected as n, hostWebServer as o, hostService as r, hostAgents as t };
|
package/lib/acp-core.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export declare class AcpServer {
|
|
|
77
77
|
* 3100 对外只提供问答,轨迹含工具结果等内部信息;3099 调试页另走 /ask 保留)
|
|
78
78
|
*/
|
|
79
79
|
private prompt;
|
|
80
|
-
/** session/cancel:{ sessionId } →
|
|
80
|
+
/** session/cancel:{ sessionId } → 取消该会话的当前活动(Agent.cancel,宿主 rc.1 契约) */
|
|
81
81
|
private cancel;
|
|
82
82
|
/** session/close:{ sessionId } → 释放 agent + 注册表清理(进程内) */
|
|
83
83
|
private closeSession;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-idle.ts — 等待 agent 空闲(含销毁竞速,S142 review F-08)
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独成模块:`askSkiff`(skiff 问答)与 `handyman`(杂工循环)都要"等这一轮做完",
|
|
5
|
+
* 此前各自复制了一份只监听 `agent/status → idle` 的实现。该实现有一个静默挂死:
|
|
6
|
+
* **agent 先被销毁(会话关闭/进程回收/显式 dispose)就永远不会再发 idle 事件**,
|
|
7
|
+
* 等待方 Promise 永不结算 → 问答/杂工永久卡住(宿主的 disposal 语义:
|
|
8
|
+
* `core/agent-loop/src/agent.ts` 的 wakeDriver 在 disposed 后不再 latch 唤醒)。
|
|
9
|
+
*
|
|
10
|
+
* 结算条件(三者任一,先到先算):
|
|
11
|
+
* ① `agent/status` 且 status === 'idle' —— 正常路径(本轮做完)
|
|
12
|
+
* ② `agent/disposed` 且是同一个 agent —— 销毁竞速(宿主 `runtime-types.ts:175`)
|
|
13
|
+
* ③ `session/disposed` 且是同一会话 —— 会话先于 agent 消失(宿主 `core/session/src/index.ts:62`)
|
|
14
|
+
*
|
|
15
|
+
* 无超时:agent 工作多久等多久(handyman 可永续)。订阅后再查一次 `agent.status`——
|
|
16
|
+
* 宿主 `followup`/`steer` 会同步把 phase 置为 running(`agent.ts:181-201`),
|
|
17
|
+
* 因此此刻读到 idle 只可能是"唤醒已在订阅前收敛",立即结算以避免漏事件挂死。
|
|
18
|
+
*/
|
|
19
|
+
import type { Context } from 'cordis';
|
|
20
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
21
|
+
/**
|
|
22
|
+
* 等待 agent 空闲或销毁。
|
|
23
|
+
* @param ctx 宿主插件上下文(事件通道来源)
|
|
24
|
+
* @param agent 目标 agent
|
|
25
|
+
* @returns 空闲、agent 销毁、或会话销毁时结算(永不 reject)
|
|
26
|
+
*/
|
|
27
|
+
export declare function waitAgentIdle(ctx: Context, agent: Agent): Promise<void>;
|
|
@@ -106,13 +106,34 @@ const DEFAULT_SERENITY_CONFIG_PATHS = [".opencode/serenity.json", ".dsh/serenity
|
|
|
106
106
|
function readUtf8(path) {
|
|
107
107
|
return readFileSync(path, "utf-8").replace(/^\uFEFF/, "");
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* 已告警过的损坏配置路径(进程级去重:同一坏文件不刷屏,但绝不静默)。
|
|
111
|
+
* S142 review F-07:此前损坏 → 静默返回 `{}`——安全模式黑名单/handyman 白名单/skiff 角色
|
|
112
|
+
* 全部**无声失效**(用户以为配了,实际没配)。守卫的输入失败必须响亮。
|
|
113
|
+
*/
|
|
114
|
+
const warnedBrokenConfigs = /* @__PURE__ */ new Set();
|
|
115
|
+
/**
|
|
116
|
+
* 读取 CCC 配置(第一个存在且可解析的候选路径)。
|
|
117
|
+
*
|
|
118
|
+
* 失败语义(F-07):文件存在但**解析失败** → 记录响亮告警(含路径与原因,每路径一次)
|
|
119
|
+
* 并返回 `{}`。选择 fail-open 而非抛错的理由:apply 期抛错 = 整个 dsh 启动失败
|
|
120
|
+
* (app-boot "plugin(s) failed to load"),而单个 CCC 配置损坏只应影响该 CCC 的功能降级;
|
|
121
|
+
* 告警 + `dashboard health` 让损坏可见可修。
|
|
122
|
+
* @param root CCC 根
|
|
123
|
+
* @param paths 候选相对路径(按序取第一个存在的)
|
|
124
|
+
* @returns 解析后的配置;无文件/损坏 → `{}`
|
|
125
|
+
*/
|
|
109
126
|
function loadSerenityConfig(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
110
127
|
for (const candidate of paths) {
|
|
111
128
|
const p = resolve(root, candidate);
|
|
112
129
|
if (!existsSync(p)) continue;
|
|
113
130
|
try {
|
|
114
131
|
return JSON.parse(readUtf8(p));
|
|
115
|
-
} catch {
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (!warnedBrokenConfigs.has(p)) {
|
|
134
|
+
warnedBrokenConfigs.add(p);
|
|
135
|
+
console.warn(`[serenity-hooks] ✗ CCC 配置解析失败,已按空配置继续(该 CCC 的安全模式黑名单/handyman 白名单/skiff 角色等全部失效,请修 JSON): ${p} — ${String(err?.message ?? err)}`);
|
|
136
|
+
}
|
|
116
137
|
return {};
|
|
117
138
|
}
|
|
118
139
|
}
|
package/lib/ccc.d.ts
CHANGED
|
@@ -201,6 +201,19 @@ export declare const DEFAULT_SERENITY_CONFIG_PATHS: string[];
|
|
|
201
201
|
* 写出的 BOM(\uFEFF)会让 JSON.parse 抛错(配置静默变空)或 frontmatter 检测失败(技能被丢弃)。
|
|
202
202
|
*/
|
|
203
203
|
export declare function readUtf8(path: string): string;
|
|
204
|
+
/** 测试辅助:清空"已告警"记忆(生产零调用) */
|
|
205
|
+
export declare function __resetBrokenConfigWarningsForTest(): void;
|
|
206
|
+
/**
|
|
207
|
+
* 读取 CCC 配置(第一个存在且可解析的候选路径)。
|
|
208
|
+
*
|
|
209
|
+
* 失败语义(F-07):文件存在但**解析失败** → 记录响亮告警(含路径与原因,每路径一次)
|
|
210
|
+
* 并返回 `{}`。选择 fail-open 而非抛错的理由:apply 期抛错 = 整个 dsh 启动失败
|
|
211
|
+
* (app-boot "plugin(s) failed to load"),而单个 CCC 配置损坏只应影响该 CCC 的功能降级;
|
|
212
|
+
* 告警 + `dashboard health` 让损坏可见可修。
|
|
213
|
+
* @param root CCC 根
|
|
214
|
+
* @param paths 候选相对路径(按序取第一个存在的)
|
|
215
|
+
* @returns 解析后的配置;无文件/损坏 → `{}`
|
|
216
|
+
*/
|
|
204
217
|
export declare function loadSerenityConfig(root: string, paths?: string[]): SerenityConfig;
|
|
205
218
|
export declare const SAFE_MODE_MARKER = ".serenity-safe-on";
|
|
206
219
|
export declare function isSafeModeOn(root: string): boolean;
|
package/lib/gateway-proxy.d.ts
CHANGED
|
@@ -29,6 +29,27 @@ export declare function filterWorkspaceList(body: string, allowPrefixes: readonl
|
|
|
29
29
|
export declare function workspaceAllowed(allowPrefixes: readonly string[], path: string | undefined): boolean;
|
|
30
30
|
/** 构造 workspace.create 拒绝的 JSON RPC 响应体(code=forbidden) */
|
|
31
31
|
export declare function workspaceDenyResponse(rpcId: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* 判定 workspace 创建请求的端点(v1.30.6,review F-03)。
|
|
34
|
+
*
|
|
35
|
+
* 宿主 rc.1 的 Remote 端点由 `typertEndpoint({namespace,method})` 派生为
|
|
36
|
+
* `workspace/create`,因此浏览器实际请求 `POST /api/workspace/create`
|
|
37
|
+
* (packages/typert/registry/src/service.ts:63-69;client/connection/src/client/rpc.ts:44)。
|
|
38
|
+
* 旧实现只匹配 `/api/workspace.create` → 该分支从未命中,外部白名单与禁建校验失效。
|
|
39
|
+
* 保留旧端点以兼容旧宿主。
|
|
40
|
+
*/
|
|
41
|
+
export declare function isWorkspaceCreatePath(pathname: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 从 workspace 创建请求体解析 `{ rpcId, path }`(v1.30.6,review F-03)。
|
|
44
|
+
*
|
|
45
|
+
* rc.1 wire 信封:`{ type:'client-request', rpcId, method:'workspace/create',
|
|
46
|
+
* payload:{ args:{ path } } }`(packages/api/gateway/src/index.ts:955 `args: payload.args`);
|
|
47
|
+
* 旧宿主为 `payload.path`。解析失败返回 rpcId='unknown' + path=undefined。
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseWorkspaceCreateBody(body: string): {
|
|
50
|
+
rpcId: string;
|
|
51
|
+
path?: string;
|
|
52
|
+
};
|
|
32
53
|
/**
|
|
33
54
|
* 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过)
|
|
34
55
|
*/
|
package/lib/gateway.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import type { Context } from 'cordis';
|
|
29
29
|
import { type Server } from 'node:http';
|
|
30
|
-
export { RANDOM_UUID_POLYFILL, injectPolyfillHtml, filterWorkspaceList, workspaceAllowed, workspaceDenyResponse, buildProxyHeaders, transformHtmlForProxy, } from './gateway-proxy.js';
|
|
30
|
+
export { RANDOM_UUID_POLYFILL, injectPolyfillHtml, filterWorkspaceList, workspaceAllowed, workspaceDenyResponse, isWorkspaceCreatePath, parseWorkspaceCreateBody, buildProxyHeaders, transformHtmlForProxy, } from './gateway-proxy.js';
|
|
31
31
|
export { loginPageHtml, verifyGatewayLogin, SESSION_TTL_MS, issueToken, revokeToken, validateToken, FAIL_LOCK_THRESHOLD, FAIL_LOCK_BASE_MS, FAIL_LOCK_MAX_MS, getFailState, resetFailState, isAccountLocked, recordLoginFailure, accountLockRemaining, newCsrfToken, isCsrfValid, csrfFromRequest, safeEqual, originAllowed, cookieValue, } from './gateway-auth.js';
|
|
32
32
|
declare module 'cordis' {
|
|
33
33
|
interface Events {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host/access.ts — 宿主访问收口(S142 review F-06)
|
|
3
|
+
*
|
|
4
|
+
* 为什么存在:dsp 此前在 30+ 个文件里各自用 `(ctx as unknown as {…})` 断言读取宿主
|
|
5
|
+
* 服务——契约表达分散、失败模式各异(多数静默 undefined)。本模块是**唯一**读取宿主
|
|
6
|
+
* 的入口:所有 `ctx.get` / `ctx.<service>` 的形状断言集中在此,调用点只拿类型化结果。
|
|
7
|
+
*
|
|
8
|
+
* 约定:
|
|
9
|
+
* - 只读、无副作用;服务缺失一律返回 `undefined`(调用方决定降级策略),**不抛错**
|
|
10
|
+
* (宿主对插件 apply 抛错 = 整个 dsh 启动失败,访问层不能成为单点)。
|
|
11
|
+
* - 形状以 `AI_LAB/dsh-harness-public` @ 0.1.2-rc.1 为准;成员缺失由
|
|
12
|
+
* `host/contract.ts` 的探针在装载时与 `dashboard health` 中报告。
|
|
13
|
+
* - 迁移是渐进的:新代码必须走本模块;旧调用点逐轮收敛(F-06 轮次记录见 CHANGELOG)。
|
|
14
|
+
*/
|
|
15
|
+
/** 通用读取:`ctx.get(name)`(含异常吞掉——服务 getter 抛错视为不可用) */
|
|
16
|
+
export declare function hostService<T = unknown>(ctx: unknown, name: string): T | undefined;
|
|
17
|
+
/** 直接属性读取(injected 服务:`ctx.<name>`),属性缺失时回落 `ctx.get` */
|
|
18
|
+
export declare function hostInjected<T = unknown>(ctx: unknown, name: string): T | undefined;
|
|
19
|
+
export interface HostSessionLike {
|
|
20
|
+
id?: string;
|
|
21
|
+
header?: {
|
|
22
|
+
cwd?: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
};
|
|
25
|
+
snapshotEvents?: () => unknown[];
|
|
26
|
+
}
|
|
27
|
+
export interface HostSessions {
|
|
28
|
+
list?: () => HostSessionLike[];
|
|
29
|
+
get?: (id: unknown) => HostSessionLike | undefined;
|
|
30
|
+
create?: (...args: unknown[]) => unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface HostAgents {
|
|
33
|
+
create?: (...args: unknown[]) => unknown;
|
|
34
|
+
get?: (id: string) => unknown;
|
|
35
|
+
resume?: (...args: unknown[]) => unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface HostWebServer {
|
|
38
|
+
register?: (registration: unknown) => unknown;
|
|
39
|
+
port?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface HostSettings {
|
|
42
|
+
installSection?: (...args: unknown[]) => unknown;
|
|
43
|
+
}
|
|
44
|
+
/** `ctx.sessions`(injected) */
|
|
45
|
+
export declare function hostSessions(ctx: unknown): HostSessions | undefined;
|
|
46
|
+
/** `ctx.agents`(injected) */
|
|
47
|
+
export declare function hostAgents(ctx: unknown): HostAgents | undefined;
|
|
48
|
+
/** `ctx.webServer`(injected) */
|
|
49
|
+
export declare function hostWebServer(ctx: unknown): HostWebServer | undefined;
|
|
50
|
+
/** `ctx.settings`(injected;提供 settings 面板装配通道) */
|
|
51
|
+
export declare function hostSettings(ctx: unknown): HostSettings | undefined;
|
|
52
|
+
/** 会话 cwd 列表(live 会话;形状不符时返回空数组而非抛错) */
|
|
53
|
+
export declare function hostSessionCwds(ctx: unknown): string[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host/contract.ts — 宿主契约声明 + 运行时探针(S142 review F-05/F-06)
|
|
3
|
+
*
|
|
4
|
+
* 为什么存在(R↓):dsp 与宿主(DeepSeek Harness)的契约此前以**散落的类型断言**
|
|
5
|
+
* 表达——37 个文件里 80 处 `as unknown as`,类型检查被断言抹掉、运行时无校验,
|
|
6
|
+
* 于是宿主改名/移除成员时 dsp **静默失效**:v1.30.5 的错误码漂移
|
|
7
|
+
* (`attachment-error` → `session/attachment-invalid`)与更早的 `Session.events`
|
|
8
|
+
* → `snapshotEvents()` 都是这一类。
|
|
9
|
+
*
|
|
10
|
+
* 本模块把契约集中声明为**数据**(单一真相源),并提供纯函数探针
|
|
11
|
+
* `probeHostContract(ctx)`:在插件装载时与 `dashboard health` 中核对
|
|
12
|
+
* "运行中的宿主是否仍满足 dsp 依赖的服务与成员"。
|
|
13
|
+
*
|
|
14
|
+
* 设计约束:
|
|
15
|
+
* - **零宿主 import**:只用结构化读取(`ctx.get` / 属性),因此 peer-only 打包
|
|
16
|
+
* (宿主依赖不进 dsp 的 node_modules)下也成立。
|
|
17
|
+
* - **事件名不在运行时探**:宿主事件是字符串键,`ctx.on` 对未知名不报错——
|
|
18
|
+
* 事件名的正确性由**编译期契约测试**保证(tests/host-contract.test.ts 用真实
|
|
19
|
+
* 宿主类型断言),此处只登记清单供文档/测试共用。
|
|
20
|
+
* - **缺失分级**:`required` = 缺失即 ACC 核心能力失效;否则为可降级能力。
|
|
21
|
+
*/
|
|
22
|
+
export type HostAccess = 'injected' | 'lazy';
|
|
23
|
+
export interface HostMember {
|
|
24
|
+
name: string;
|
|
25
|
+
kind: 'function' | 'value';
|
|
26
|
+
}
|
|
27
|
+
export interface HostServiceContract {
|
|
28
|
+
/** 稳定标识(报告用) */
|
|
29
|
+
id: string;
|
|
30
|
+
/** 服务名(`ctx.get(name)` 或 `ctx.<name>`) */
|
|
31
|
+
name: string;
|
|
32
|
+
/** injected = cordis inject 保证存在;lazy = 运行期按需取(可能 undefined) */
|
|
33
|
+
access: HostAccess;
|
|
34
|
+
/** 依赖的成员(空 = 只要求服务存在) */
|
|
35
|
+
members: readonly HostMember[];
|
|
36
|
+
/** 缺失时的后果(人读,进 health 报告) */
|
|
37
|
+
impact: string;
|
|
38
|
+
/** 缺失是否致命 */
|
|
39
|
+
required: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** dsp 依赖的宿主服务与成员(对照 AI_LAB/dsh-harness-public @ 0.1.2-rc.1 逐一核对) */
|
|
42
|
+
export declare const HOST_SERVICES: readonly HostServiceContract[];
|
|
43
|
+
export interface HostEventContract {
|
|
44
|
+
name: HostEventName;
|
|
45
|
+
/** 订阅方(文件) */
|
|
46
|
+
site: string;
|
|
47
|
+
impact: string;
|
|
48
|
+
required: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* **编译期契约**:事件名必须存在于宿主合并后的 `Events` 映射。
|
|
52
|
+
*
|
|
53
|
+
* 这是运行时无法验证的那一半——宿主事件是字符串键,`ctx.on('typo')` 不会报错,
|
|
54
|
+
* 只会静默不订阅。`satisfies readonly (keyof Events)[]` 让 `dsh-develop typecheck`
|
|
55
|
+
* 在名字写错/宿主改名时**编译失败**(v1.30.7,review F-04/F-05)。
|
|
56
|
+
* 该 import 为 type-only,编译期擦除,不产生运行时依赖。
|
|
57
|
+
*/
|
|
58
|
+
export declare const HOST_EVENT_NAMES: readonly ["agent/session-start", "agent/pre-step", "agent/turn-stopping", "agent/status", "agent/inbox/inserted", "session/event", "session/created", "tools/pre-execute", "tools/post-execute", "system-prompt/assemble"];
|
|
59
|
+
export type HostEventName = (typeof HOST_EVENT_NAMES)[number];
|
|
60
|
+
/**
|
|
61
|
+
* dsp 订阅的宿主事件清单(与 HOST_EVENT_NAMES 同源;impact/site 供 health 报告与文档)。
|
|
62
|
+
*/
|
|
63
|
+
export declare const HOST_EVENTS: readonly HostEventContract[];
|
|
64
|
+
/** dsp 被验证过的宿主版本范围(与 package.json peerDependencies 同源,单一真相源) */
|
|
65
|
+
export declare const REQUIRED_HOST_RANGE = "^0.1.2-rc.1";
|
|
66
|
+
export interface HostContractIssue {
|
|
67
|
+
id: string;
|
|
68
|
+
kind: 'service' | 'member' | 'version';
|
|
69
|
+
detail: string;
|
|
70
|
+
impact: string;
|
|
71
|
+
required: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface HostContractReport {
|
|
74
|
+
ok: boolean;
|
|
75
|
+
checked: number;
|
|
76
|
+
hostVersion: string | null;
|
|
77
|
+
versionOk: boolean;
|
|
78
|
+
issues: HostContractIssue[];
|
|
79
|
+
}
|
|
80
|
+
/** 最小 semver 比较(无依赖):返回 -1 / 0 / 1;解析失败返回 null */
|
|
81
|
+
export declare function compareSemver(a: string, b: string): number | null;
|
|
82
|
+
/**
|
|
83
|
+
* 宿主版本是否落在 dsp 被验证的范围(`^0.1.2-rc.1`)。
|
|
84
|
+
* 无版本信息 → 视为未知(ok=false,detail 说明),不猜测。
|
|
85
|
+
*/
|
|
86
|
+
export declare function checkHostVersion(version: string | null): {
|
|
87
|
+
ok: boolean;
|
|
88
|
+
detail: string;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* 探针:核对运行中的宿主是否仍满足 dsp 依赖的服务与成员。
|
|
92
|
+
*
|
|
93
|
+
* @param ctx 宿主插件上下文(真实 cordis Context;测试可传结构化替身)
|
|
94
|
+
* @param hostVersion 运行中宿主版本(`readDshVersion()` 结果;null = 未知)
|
|
95
|
+
* @returns 报告(`ok:false` 时 `issues` 逐条给出缺失项与后果)
|
|
96
|
+
*/
|
|
97
|
+
export declare function probeHostContract(ctx: unknown, hostVersion?: string | null): HostContractReport;
|
|
98
|
+
/** 单行摘要(启动告警用) */
|
|
99
|
+
export declare function summarizeHostContract(report: HostContractReport): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host/effect.ts — 插件资源拆卸登记(S142 review F-08)
|
|
3
|
+
*
|
|
4
|
+
* 宿主把"注册即效果"作为统一模型:每个贡献都走 `ctx.effect()` / `ctx.on()`,
|
|
5
|
+
* 注册返回 disposer,fiber 销毁时自动回收(`cordis` Context.effect,rc.1 全量使用,
|
|
6
|
+
* 如 `core/tools/src/index.ts:943`)。
|
|
7
|
+
*
|
|
8
|
+
* 本模块是 dsp 各装配点登记自起资源(端口监听器/定时器/轮询器)的**唯一入口**,
|
|
9
|
+
* 理由:
|
|
10
|
+
* - `ctx.effect` 是宿主成员,但**不可假定存在**(测试替身、极旧宿主、HMR 早期阶段)。
|
|
11
|
+
* 直接裸调会让 `apply` 抛错 = 整个 dsh 启动失败(app-boot "plugin(s) failed to load"),
|
|
12
|
+
* 这比"资源没拆卸"严重得多——所以缺失时**响亮降级**(记录 + 返回 false)。
|
|
13
|
+
* - 零依赖叶模块:gateway / autopilot 等模块只引它,不会因此被拖进 skiff/rebuild 的依赖链。
|
|
14
|
+
*/
|
|
15
|
+
/** 测试辅助:重置"未装配"告警去重(生产零调用) */
|
|
16
|
+
export declare function __resetDisposerWarningsForTest(): void;
|
|
17
|
+
/**
|
|
18
|
+
* 登记一个卸载时执行的清理。
|
|
19
|
+
* @param ctx 宿主插件上下文
|
|
20
|
+
* @param label 日志标签(定位是哪个资源未装配)
|
|
21
|
+
* @param cleanup 卸载时执行;抛错被吞掉(不阻断其它清理)
|
|
22
|
+
* @returns 是否成功装配(false = 宿主无 `ctx.effect`,已告警)
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerDisposer(ctx: unknown, label: string, cleanup: () => void): boolean;
|