@shgroup/dsh-serenity-hooks 1.47.4 → 1.48.0
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/agent-idle.d.ts +17 -3
- package/lib/{ccc-roots-NpTWo0-c.js → ccc-roots-Duk89a-h.js} +11 -0
- package/lib/ccc-roots.d.ts +12 -2
- package/lib/clock-runtime.d.ts +5 -2
- package/lib/cro-turns.d.ts +19 -3
- package/lib/index.js +257 -10
- package/lib/live-sessions.d.ts +14 -4
- package/lib/{message-source-yuCgrbU6.js → message-source-D72jte0u.js} +1 -1
- package/lib/opencode-provider.d.ts +53 -0
- package/lib/wake-scheduler.d.ts +3 -4
- 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.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——11 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/im-bridge/acc-diag;后两个按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.5-rc.2(deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
package/lib/agent-idle.d.ts
CHANGED
|
@@ -15,13 +15,27 @@
|
|
|
15
15
|
* 无超时:agent 工作多久等多久(handyman 可永续)。订阅后再查一次 `agent.status`——
|
|
16
16
|
* 宿主 `followup`/`steer` 会同步把 phase 置为 running(`agent.ts:181-201`),
|
|
17
17
|
* 因此此刻读到 idle 只可能是"唤醒已在订阅前收敛",立即结算以避免漏事件挂死。
|
|
18
|
+
*
|
|
19
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
20
|
+
* **`ctx` 参数类型从 `Context` 改为 `unknown`** ⇒ 消除本文件的 `W2`(宿主 `cordis` 类型)接触。
|
|
21
|
+
*
|
|
22
|
+
* **判据(本文件与前两个不同:它不是"纯透传",但仍适用)**:本模块**确实用** `ctx`,
|
|
23
|
+
* 但用法是**结构化读**而非类型化读 —— 原码第 47 行**本就写着**
|
|
24
|
+
* `(ctx as unknown as { on?: … }).on?.(…)`:它**主动绕过** `Context` 类型去取 `on`。
|
|
25
|
+
* ⇒ 那个 `Context` 注解**没有为这处调用提供任何类型保证**(调用点已弃用它),
|
|
26
|
+
* 它只贡献了一处 L1→宿主 的接触点。
|
|
27
|
+
* 🔵 **证据(可重跑)**:本文件**零处** `ctx.xxx` 类型化属性访问;
|
|
28
|
+
* 唯一的 `ctx` 使用点是被 `as unknown as` **双重断言**后的结构化调用。
|
|
29
|
+
* ⚠️ 与 `live-sessions.ts`/`ccc-roots.ts` 的区别:那两处是**纯透传**(连断言都没有);
|
|
30
|
+
* 本处是**结构化访问**。两者的共同结论一致:`Context` 注解未参与类型检查。
|
|
31
|
+
* 🔵 **`Agent` 类型 import 保留**:它**被真正使用**(`p?.agent === agent` 的同一性比较)
|
|
32
|
+
* ⇒ 属 §2.6 的 W2-类型,但本模块**不是**"只贡献接触点"那种 —— 它是真需要,故不在本次收敛面。
|
|
18
33
|
*/
|
|
19
|
-
import type { Context } from 'cordis';
|
|
20
34
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
21
35
|
/**
|
|
22
36
|
* 等待 agent 空闲或销毁。
|
|
23
|
-
* @param ctx
|
|
37
|
+
* @param ctx 宿主插件上下文(事件通道来源;**结构化访问**,故类型为 `unknown`,见文件头 I2 段)
|
|
24
38
|
* @param agent 目标 agent
|
|
25
39
|
* @returns 空闲、agent 销毁、或会话销毁时结算(永不 reject)
|
|
26
40
|
*/
|
|
27
|
-
export declare function waitAgentIdle(ctx:
|
|
41
|
+
export declare function waitAgentIdle(ctx: unknown, agent: Agent): Promise<void>;
|
|
@@ -22,6 +22,17 @@ import { basename } from "node:path";
|
|
|
22
22
|
*
|
|
23
23
|
* 不新增持久状态(Q5 裁决:**不新增"本机 CCC 名单"**、不扫盘、不写路径假设)——
|
|
24
24
|
* 枚举只读 **DSH 已有的三个来源**。
|
|
25
|
+
*
|
|
26
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
27
|
+
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触。
|
|
28
|
+
*
|
|
29
|
+
* **判据(与 `live-sessions.ts` 同款,但本文件要多过一道):** 约束文档 §2.7 原记本处
|
|
30
|
+
* `Context` 是"**实测真被用到**"(因它调 `hostService(ctx,…)`/`hostSessions(ctx)`)。
|
|
31
|
+
* 🔴 **该判断把"透传"与"解引用"混为一谈了** —— 实测本文件 `ctx` 的**唯一**去向是
|
|
32
|
+
* **原样透传**给两个 L0 取数口(签名均为 `ctx: unknown`),**零处** `ctx.xxx` 属性访问。
|
|
33
|
+
* ⇒ `Context` 这个 import **未参与任何类型检查**,只贡献了一处 L1→宿主 的接触点。
|
|
34
|
+
* 🔵 **证据(可重跑)**:grep 本文件 —— `ctx.` **0 命中**;`ctx` 命中全在
|
|
35
|
+
* 参数位/调用位(`hostService(ctx, …)` / `hostSessions(ctx)`)。
|
|
25
36
|
*/
|
|
26
37
|
var ccc_roots_exports = /* @__PURE__ */ __exportAll({
|
|
27
38
|
NO_CCC_FROM_AGENT_CWD: () => NO_CCC_FROM_AGENT_CWD,
|
package/lib/ccc-roots.d.ts
CHANGED
|
@@ -17,8 +17,18 @@
|
|
|
17
17
|
*
|
|
18
18
|
* 不新增持久状态(Q5 裁决:**不新增"本机 CCC 名单"**、不扫盘、不写路径假设)——
|
|
19
19
|
* 枚举只读 **DSH 已有的三个来源**。
|
|
20
|
+
*
|
|
21
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
22
|
+
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触。
|
|
23
|
+
*
|
|
24
|
+
* **判据(与 `live-sessions.ts` 同款,但本文件要多过一道):** 约束文档 §2.7 原记本处
|
|
25
|
+
* `Context` 是"**实测真被用到**"(因它调 `hostService(ctx,…)`/`hostSessions(ctx)`)。
|
|
26
|
+
* 🔴 **该判断把"透传"与"解引用"混为一谈了** —— 实测本文件 `ctx` 的**唯一**去向是
|
|
27
|
+
* **原样透传**给两个 L0 取数口(签名均为 `ctx: unknown`),**零处** `ctx.xxx` 属性访问。
|
|
28
|
+
* ⇒ `Context` 这个 import **未参与任何类型检查**,只贡献了一处 L1→宿主 的接触点。
|
|
29
|
+
* 🔵 **证据(可重跑)**:grep 本文件 —— `ctx.` **0 命中**;`ctx` 命中全在
|
|
30
|
+
* 参数位/调用位(`hostService(ctx, …)` / `hostSessions(ctx)`)。
|
|
20
31
|
*/
|
|
21
|
-
import type { Context } from 'cordis';
|
|
22
32
|
/**
|
|
23
33
|
* 工具/缝里"取会话工作目录"的那段形状的最小模型。
|
|
24
34
|
* 多数工具只带 `agent`(`exec.agent.session.header.cwd`);少数缝把 agent 本身传进来。
|
|
@@ -102,4 +112,4 @@ export interface ListCccsOptions {
|
|
|
102
112
|
* @param opts.defaultRoot 默认根(不在列表 → 置首);opts.withRoles 是否读 `skiff.roles`
|
|
103
113
|
* @returns 按上述顺序去重的 CCC 条目(无任何来源 → 空数组 + 可选默认根)
|
|
104
114
|
*/
|
|
105
|
-
export declare function listCccs(ctx:
|
|
115
|
+
export declare function listCccs(ctx: unknown, opts?: ListCccsOptions): Promise<CccEntry[]>;
|
package/lib/clock-runtime.d.ts
CHANGED
|
@@ -171,8 +171,11 @@ export interface ClockOptions<T> {
|
|
|
171
171
|
* `ticks`/`lastTickAt` 在**枚举到 live+enabled CCC 之后、任何 await 之前**同步记账;
|
|
172
172
|
* "无 CCC 可扫" 的那一拍**不计**(既有行为:`ticks` 停在 0 直到真有 CCC 可唤起)。
|
|
173
173
|
* 若把它交给工厂"body 前同步记账",就会**在闸之后无条件 +1**——那会把
|
|
174
|
-
* "无 CCC 可扫时不计数"
|
|
175
|
-
*
|
|
174
|
+
* "无 CCC 可扫时不计数"这条既有语义改掉(🔴 **回归钉在 `clock-runtime.test.ts`**:
|
|
175
|
+
* 『`bodyCountsTick=true` → 工厂不记账,由 body 决定(autopilot 语义:无目标则不计)』
|
|
176
|
+
* +『body 调 `countTick` 才计,且可在 await 之前同步计』—— 原文曾指向
|
|
177
|
+
* `autopilot-trajectory.test.ts`,**该文件已随 v1.35.0 退场、全仓不存在**
|
|
178
|
+
* ⇒ 2026-09-25 **只修引用**,语义与断言逐字未动)。
|
|
176
179
|
*
|
|
177
180
|
* ⚠️ 故本项**不是**"记账早晚"的风格开关,而是"**谁**知道该不该记"的归属划分:
|
|
178
181
|
* 知道"有没有目标"的只有 body ⇒ 由 body 决定何时记账。
|
package/lib/cro-turns.d.ts
CHANGED
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
* **查询时按轨迹聚合**(`listBoundSessionIds` → 「任一载体在跑」⇒ 该轨迹在跑)。
|
|
49
49
|
* ⚠️ 这也天然处理了「同一轨迹多载体」(S142 曾挂 4 条)的情形。
|
|
50
50
|
*/
|
|
51
|
-
import type { Context } from 'cordis';
|
|
52
51
|
/** 一次「在跑」记录的 TTL(见文件头的方向性判据) */
|
|
53
52
|
export declare const TURN_TTL_MS: number;
|
|
54
53
|
/** 标记「在跑」(`agent/created` + `agent/status=running` 调用) */
|
|
@@ -87,6 +86,23 @@ export declare function __resetCroTurnsForTest(): void;
|
|
|
87
86
|
*
|
|
88
87
|
* ⚠️ **失败不抛**:任一事件通道缺失 ⇒ 响亮日志 + 该订阅跳过(**apply 不可成为启动单点**,
|
|
89
88
|
* 与 `registerDeepseekVisionPatch` 等既有装配同款纪律)。
|
|
90
|
-
*
|
|
89
|
+
*
|
|
90
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
91
|
+
* **两个宿主 import 全部清除**(`Context`(cordis)+ `Agent`(`@deepseek-ai/dsh-agent`)),
|
|
92
|
+
* `ctx` 参数类型 → `unknown`。
|
|
93
|
+
*
|
|
94
|
+
* 🔴 **本处顺带更正 §2.7 的一处分档**:本文件原被列在"剩余 3 个属**真依赖**档"里,
|
|
95
|
+
* 但**逐符号查证后与那个判断不符** ——
|
|
96
|
+
* · `Context`:`ctx` 只被**结构化访问**(原码本就写着 `ctx as unknown as { on: … }`)
|
|
97
|
+
* ⇒ 该注解未提供任何类型保证(与 `agent-idle.ts` 同档)。
|
|
98
|
+
* · `Agent`:**导入后从未使用** —— 事件 payload 一律按 `{ agent?: unknown }` 解构,
|
|
99
|
+
* 再交给 `sessionIdOf(...)`(它收 `unknown`)。⇒ **纯粹是死 import**。
|
|
100
|
+
* ⇒ **判据(本件再证一次,且比上一件更硬)**:分档**必须逐符号看"它有没有参与类型检查"**,
|
|
101
|
+
* **不能按文件整档划分** —— 同一个文件里可以并存"真依赖符号"与"纯名义符号"。
|
|
102
|
+
* 🔵 **剩余 2 个文件**才是真依赖档:`rebuild.ts` 用 `Session`/`SessionEvent`/
|
|
103
|
+
* `deriveEventMessage`/`Agent` 等**真调用**的类型;`wake-scheduler.ts` 用 `Agent` 的
|
|
104
|
+
* `followup`/`steer`(**真调用**)⇒ 它们走 §2.7-③ **选项 (b)** 或把中立接口下沉到 L0,
|
|
105
|
+
* **另立一件**(D85 一次只改一件事)。
|
|
106
|
+
* @param ctx 插件上下文(**结构化访问** `on`,故类型为 `unknown`,见下方 I2 说明)
|
|
91
107
|
*/
|
|
92
|
-
export declare function registerCroTurnTracking(ctx:
|
|
108
|
+
export declare function registerCroTurnTracking(ctx: unknown): void;
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { a as hostSessions, c as hostWeb, i as hostService, l as hostWebServer, n as hostInjected, o as hostSettings, r as hostLlm, s as hostSubagents, t as hostAgents } from "./access-CNccNtgf.js";
|
|
3
3
|
import { a as findSerenityRoot, c as matchBlacklist, d as readCccName$1, f as readExclusiveTools, g as resolveInside, i as findGitRoot, l as pathInside, m as readTrajectorySkills, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readHandymanConfig, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-DG4Oc7I1.js";
|
|
4
|
-
import { i as cccRootForExec, n as agentCwdFor, o as listCccs, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD } from "./ccc-roots-
|
|
5
|
-
import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSession, O as localFileStamp, S as sessionsRoot, T as summarize, _ as namingTitleFor, a as pruneMissingBindings, b as resolveSessionByTitle, c as setBindingStore, d as clearActiveSessionInfo, f as createSession, g as listSessions, h as getActiveSessionInfo, i as listBoundSessionIds, k as localHuman, l as DEFAULT_SESSION_SCOPE, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionTrajectoryLabel, t as ACC_MESSAGE_KIND, u as TRAJECTORY_ACTIONS, v as parseSessionContextFromEvents, w as showSession, x as sessionEvents$1, y as readActiveSessionMd } from "./message-source-
|
|
4
|
+
import { i as cccRootForExec, n as agentCwdFor, o as listCccs, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD } from "./ccc-roots-Duk89a-h.js";
|
|
5
|
+
import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSession, O as localFileStamp, S as sessionsRoot, T as summarize, _ as namingTitleFor, a as pruneMissingBindings, b as resolveSessionByTitle, c as setBindingStore, d as clearActiveSessionInfo, f as createSession, g as listSessions, h as getActiveSessionInfo, i as listBoundSessionIds, k as localHuman, l as DEFAULT_SESSION_SCOPE, m as findSession, n as PLUGIN_SOURCE, o as readLastBound, p as extractSessionMdPathFromText, r as appendBound, s as resolveSessionTrajectoryLabel, t as ACC_MESSAGE_KIND, u as TRAJECTORY_ACTIONS, v as parseSessionContextFromEvents, w as showSession, x as sessionEvents$1, y as readActiveSessionMd } from "./message-source-D72jte0u.js";
|
|
6
6
|
import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, g as runLocalStore, h as readStore, 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-CWMRa36W.js";
|
|
7
7
|
import { a as unregisterSkiffSession$1, i as skiffSessionSnapshot$1, n as skiffRoleFor$1, o as ACC_VERSION, r as skiffSessionInfo$1, t as registerSkiffSession$1 } from "./skiff-registry-BjIX_KD1.js";
|
|
8
8
|
import { a as readSkiffRoles, c as roleMsmWhitelist, d as systemPromptSource, f as trajectorySubset, i as isSkiffSessionId, l as roleToolWhitelist, n as buildSkiffBasePrompt, o as resolveRoleSystemPrompt, r as createRolePromptReader, s as resolveSkiffKind } from "./skiff-role-KqQA6pkr.js";
|
|
@@ -1938,7 +1938,7 @@ function listActiveHandymen(root) {
|
|
|
1938
1938
|
//#region src/agent-idle.ts
|
|
1939
1939
|
/**
|
|
1940
1940
|
* 等待 agent 空闲或销毁。
|
|
1941
|
-
* @param ctx
|
|
1941
|
+
* @param ctx 宿主插件上下文(事件通道来源;**结构化访问**,故类型为 `unknown`,见文件头 I2 段)
|
|
1942
1942
|
* @param agent 目标 agent
|
|
1943
1943
|
* @returns 空闲、agent 销毁、或会话销毁时结算(永不 reject)
|
|
1944
1944
|
*/
|
|
@@ -5714,6 +5714,56 @@ function registerDisposer(ctx, label, cleanup) {
|
|
|
5714
5714
|
}
|
|
5715
5715
|
//#endregion
|
|
5716
5716
|
//#region src/cro-turns.ts
|
|
5717
|
+
/**
|
|
5718
|
+
* cro-turns.ts — 「这条轨迹是否正在跑轮次」的追踪(设计 §3.2,2026-09-19 S142)
|
|
5719
|
+
*
|
|
5720
|
+
* ## 为什么需要这个模块(CRO 唯一需要 ACC 新增的状态)
|
|
5721
|
+
*
|
|
5722
|
+
* CRO 程序要能回答「**已经在工作则停止**」这类问题 ⇒ 输入快照里必须有
|
|
5723
|
+
* 「**哪些载体正在跑轮次**」。而这件事**今天判不出来**:
|
|
5724
|
+
* 调度器只掌握**会话 live 与否**,而 **`live ≠ 在跑`**——
|
|
5725
|
+
* 一条会话可以 live 而空闲(本会话此刻就是活的反例)。
|
|
5726
|
+
*
|
|
5727
|
+
* ## 怎么夹出来(用**既有的**两个契约事件,不需要新宿主能力)
|
|
5728
|
+
*
|
|
5729
|
+
* | 事件 | 动作 | 出处 |
|
|
5730
|
+
* |---|---|---|
|
|
5731
|
+
* | `agent/created` | 标记「在跑」 | `host/contract.ts`(dsp 已在 `seams/context.ts` 订阅)。⚠️ v0.1.7 前名为 `agent/session-start`;新事件 `@mode = serial` ⇒ 处理函数须快速返回 |
|
|
5732
|
+
* | `agent/turn-stopping` | 清除标记 | `host/contract.ts:277`(dsp 已在 `rebuild.ts:442` 等 4 处订阅) |
|
|
5733
|
+
* | `agent/disposed` | **清该载体的项** | `host/contract.ts:280` |
|
|
5734
|
+
* | `session/disposed` | **清该载体的项** | `host/contract.ts:283` |
|
|
5735
|
+
*
|
|
5736
|
+
* ## 🔴 清理设计(**这是本模块最容易被做坏的地方**)
|
|
5737
|
+
*
|
|
5738
|
+
* ⚠️ 本容器栽过 **5 次**「只增不清」的病(wake-registry / keeper 三表 / pendingRebuilds …),
|
|
5739
|
+
* 而 `agent/disposed` 在 `HOST_EVENTS` 里的 impact 原文**恰好就是**
|
|
5740
|
+
* 「*per-会话内存态不清理(**长跑泄漏**)*」⇒ **本模块属于该已登记病族,必须在设计期就带清理**。
|
|
5741
|
+
*
|
|
5742
|
+
* **三层清理**:
|
|
5743
|
+
* 1. **正常路径**:`turn-stopping` ⇒ 清除该项;
|
|
5744
|
+
* 2. **载体销毁**:`agent/disposed` / `session/disposed` ⇒ 清除该项;
|
|
5745
|
+
* 3. 🔴 **异常路径(前两层都漏掉的兜底)**:读取时按 **TTL** 过滤——
|
|
5746
|
+
* 超过 `TURN_TTL_MS` 未收到 `turn-stopping` 的项**视为不在跑**(并顺手清除)。
|
|
5747
|
+
* 触发场景:进程被 kill / 卡死 / 事件丢失。
|
|
5748
|
+
*
|
|
5749
|
+
* ## TTL 取值的**方向性判据**(R↓,别用"感觉合适")
|
|
5750
|
+
*
|
|
5751
|
+
* 两种误判的代价**不对称**:
|
|
5752
|
+
* · **误报"在跑"**(实际没跑)⇒ CRO 可能**不唤起** ⇒ **漏排 = 硬故障**(链停滞);
|
|
5753
|
+
* · **误报"没跑"**(实际在跑)⇒ CRO 可能**多唤起**一次 ⇒ **多排 = 常态成本**(既有纪律:
|
|
5754
|
+
* 「漏排是硬故障、多排是常态成本」)。
|
|
5755
|
+
*
|
|
5756
|
+
* ⇒ **不确定时宁可报"没跑"**(选便宜的那种错)⇒ **TTL 不宜过长**。
|
|
5757
|
+
* 取 **30 分钟**:单轮静默超过 30 分钟仍无 `turn-stopping` 的情形罕见;
|
|
5758
|
+
* 而真发生了,代价只是一次可能多余的唤起(5min tick 限制频率,且 CRO 程序自己还能看
|
|
5759
|
+
* `pendingWakes` / `lastWakeAt` 二次判)。
|
|
5760
|
+
*
|
|
5761
|
+
* ## 键的选择
|
|
5762
|
+
*
|
|
5763
|
+
* **以载体(dsh 会话 id)为键**——因为事件本身就以 agent/session 为主语,直接映射最省。
|
|
5764
|
+
* **查询时按轨迹聚合**(`listBoundSessionIds` → 「任一载体在跑」⇒ 该轨迹在跑)。
|
|
5765
|
+
* ⚠️ 这也天然处理了「同一轨迹多载体」(S142 曾挂 4 条)的情形。
|
|
5766
|
+
*/
|
|
5717
5767
|
/** 一次「在跑」记录的 TTL(见文件头的方向性判据) */
|
|
5718
5768
|
const TURN_TTL_MS = 18e5;
|
|
5719
5769
|
/**
|
|
@@ -5783,7 +5833,24 @@ function runningCarriersOf(boundSessionIds, nowMs, ttlMs = TURN_TTL_MS) {
|
|
|
5783
5833
|
*
|
|
5784
5834
|
* ⚠️ **失败不抛**:任一事件通道缺失 ⇒ 响亮日志 + 该订阅跳过(**apply 不可成为启动单点**,
|
|
5785
5835
|
* 与 `registerDeepseekVisionPatch` 等既有装配同款纪律)。
|
|
5786
|
-
*
|
|
5836
|
+
*
|
|
5837
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
5838
|
+
* **两个宿主 import 全部清除**(`Context`(cordis)+ `Agent`(`@deepseek-ai/dsh-agent`)),
|
|
5839
|
+
* `ctx` 参数类型 → `unknown`。
|
|
5840
|
+
*
|
|
5841
|
+
* 🔴 **本处顺带更正 §2.7 的一处分档**:本文件原被列在"剩余 3 个属**真依赖**档"里,
|
|
5842
|
+
* 但**逐符号查证后与那个判断不符** ——
|
|
5843
|
+
* · `Context`:`ctx` 只被**结构化访问**(原码本就写着 `ctx as unknown as { on: … }`)
|
|
5844
|
+
* ⇒ 该注解未提供任何类型保证(与 `agent-idle.ts` 同档)。
|
|
5845
|
+
* · `Agent`:**导入后从未使用** —— 事件 payload 一律按 `{ agent?: unknown }` 解构,
|
|
5846
|
+
* 再交给 `sessionIdOf(...)`(它收 `unknown`)。⇒ **纯粹是死 import**。
|
|
5847
|
+
* ⇒ **判据(本件再证一次,且比上一件更硬)**:分档**必须逐符号看"它有没有参与类型检查"**,
|
|
5848
|
+
* **不能按文件整档划分** —— 同一个文件里可以并存"真依赖符号"与"纯名义符号"。
|
|
5849
|
+
* 🔵 **剩余 2 个文件**才是真依赖档:`rebuild.ts` 用 `Session`/`SessionEvent`/
|
|
5850
|
+
* `deriveEventMessage`/`Agent` 等**真调用**的类型;`wake-scheduler.ts` 用 `Agent` 的
|
|
5851
|
+
* `followup`/`steer`(**真调用**)⇒ 它们走 §2.7-③ **选项 (b)** 或把中立接口下沉到 L0,
|
|
5852
|
+
* **另立一件**(D85 一次只改一件事)。
|
|
5853
|
+
* @param ctx 插件上下文(**结构化访问** `on`,故类型为 `unknown`,见下方 I2 说明)
|
|
5787
5854
|
*/
|
|
5788
5855
|
function registerCroTurnTracking(ctx) {
|
|
5789
5856
|
const on = (event, handler) => {
|
|
@@ -6236,6 +6303,31 @@ function createClock(opts) {
|
|
|
6236
6303
|
//#endregion
|
|
6237
6304
|
//#region src/wake-scheduler.ts
|
|
6238
6305
|
/**
|
|
6306
|
+
* wake-scheduler.ts — trajectory 唤醒调度器(D58/D59/D60;S142 2026-09-13)
|
|
6307
|
+
*
|
|
6308
|
+
* 一个**中心调度器**(5min tick,复用 autopilot 的节律常量),处理 CCC 唤醒注册表里
|
|
6309
|
+
* 到期的条目:把「未来时刻 + 一条 message」投递给目标 trajectory,**fire-and-forget**
|
|
6310
|
+
* (无阻塞、无等待、无回执——发起方不 await,调度器不替模型结果负责)。
|
|
6311
|
+
*
|
|
6312
|
+
* 与 autopilot 的关系(D59):**两个 tick 各自独立**。autopilot 是每 CCC 单例的
|
|
6313
|
+
* 周期自唤醒,逻辑一行不碰;本模块只处理注册表的一次性唤醒条目。
|
|
6314
|
+
*
|
|
6315
|
+
* 投递路径(P-1 证据链见 SESSION.md §30.6):
|
|
6316
|
+
* 1. target(S### / 目录名)→ 目录名 + SESSION.md
|
|
6317
|
+
* 2. 目标 dsh 会话 id:`.bindings.json` 里绑定该目录名的会话(按绑定时间倒序,权威)
|
|
6318
|
+
* → 回退:live 会话标题匹配
|
|
6319
|
+
* 3. agent:**live 优先**(`ctx.agents.get`)→ 冷会话走 `ctx.sessionController.resolveAgent`
|
|
6320
|
+
* (宿主实现:live 优先 + resume 去重 + 由会话元数据恢复 preset)
|
|
6321
|
+
* → `sessionController` 缺席 ⇒ 仅投递 live 目标,**不静默**(v1.39.3:**归为"环境未就绪"**
|
|
6322
|
+
* 而非"投递失败",见 `WakeDeliveryResult.notReady` —— 启动那一拍早于懒服务就绪是常态)
|
|
6323
|
+
* 4. 投递:`agent.followup(...)`("Queue an ordinary follow-up turn and wake the driver")
|
|
6324
|
+
*
|
|
6325
|
+
* 守卫(2026-09-21 起**本钟无闸**):🔴 **恒武装**——原 `wakeSchedulerEnabled` 已砍,
|
|
6326
|
+
* 本钟不再有「可被误关的开关」;旧配置残留该键**静默忽略**,且**不**回退 `autopilotEnabled`|
|
|
6327
|
+
* CRO 阶段另有**独立**总闸 `croEnabled`(只停 CRO,**不影响**本注册表投递)|
|
|
6328
|
+
* 补跑窗口 2h(超窗置 missed 留痕)|同 tick 串行|唤起后维持 live(人类可介入)。
|
|
6329
|
+
*/
|
|
6330
|
+
/**
|
|
6239
6331
|
* 解析条目的 target(`S###` 或完整目录名)→ 目录名 + SESSION.md 路径。
|
|
6240
6332
|
* @param root CCC 根
|
|
6241
6333
|
* @param target 条目里的目标标识
|
|
@@ -16313,7 +16405,7 @@ function registerWeixinBridge(ctx) {
|
|
|
16313
16405
|
const syncFromLive = () => {
|
|
16314
16406
|
(async () => {
|
|
16315
16407
|
try {
|
|
16316
|
-
const { listCccs } = await import("./ccc-roots-
|
|
16408
|
+
const { listCccs } = await import("./ccc-roots-Duk89a-h.js").then((n) => n.a);
|
|
16317
16409
|
for (const entry of await listCccs(ctx)) syncCccBridge(ctx, entry.root);
|
|
16318
16410
|
} catch {}
|
|
16319
16411
|
})();
|
|
@@ -16439,7 +16531,7 @@ function matchCcc(input, candidates) {
|
|
|
16439
16531
|
}
|
|
16440
16532
|
/** 组装候选列表(`ccc-roots.listCccs` 投影 + `.serenity` 名;动态 import 保持本模块静态依赖轻量) */
|
|
16441
16533
|
async function collectCandidates(ctx) {
|
|
16442
|
-
const { listCccs } = await import("./ccc-roots-
|
|
16534
|
+
const { listCccs } = await import("./ccc-roots-Duk89a-h.js").then((n) => n.a);
|
|
16443
16535
|
const entries = await listCccs(ctx, { defaultRoot: process.cwd() });
|
|
16444
16536
|
const seen = /* @__PURE__ */ new Set();
|
|
16445
16537
|
const out = [];
|
|
@@ -16927,6 +17019,110 @@ function opencodeRouteHeaders() {
|
|
|
16927
17019
|
"x-opencode-project": "global"
|
|
16928
17020
|
};
|
|
16929
17021
|
}
|
|
17022
|
+
/**
|
|
17023
|
+
* 目录补丁表(**唯一真相源 = pi-ai 的 `dist/providers/data/opencode-go.json`**)。
|
|
17024
|
+
*
|
|
17025
|
+
* 录取判据:① 官方文档 <https://opencode.ai/docs/zh-cn/go/> 的端点表列了它;
|
|
17026
|
+
* ② pi-ai 目录里**没有**它 ⇒ 面板不显示、手加也没 thinking。
|
|
17027
|
+
*
|
|
17028
|
+
* `deepseek-v4.1-flash` 的各字段**逐字取自目录里 `deepseek-v4-flash`**(同族兄弟,
|
|
17029
|
+
* 同 `chat/completions` 端点 + 同 `thinkingFormat: deepseek`)——不自行发明规格。
|
|
17030
|
+
*/
|
|
17031
|
+
const OPENCODE_EXTRA_MODELS = { "deepseek-v4.1-flash": {
|
|
17032
|
+
name: "DeepSeek V4.1 Flash",
|
|
17033
|
+
api: "openai-completions",
|
|
17034
|
+
contextWindow: 1e6,
|
|
17035
|
+
maxTokens: 384e3,
|
|
17036
|
+
input: ["text"],
|
|
17037
|
+
reasoningEfforts: {
|
|
17038
|
+
low: "low",
|
|
17039
|
+
high: "high",
|
|
17040
|
+
max: "max"
|
|
17041
|
+
},
|
|
17042
|
+
compat: {
|
|
17043
|
+
supportsStore: false,
|
|
17044
|
+
supportsDeveloperRole: false,
|
|
17045
|
+
maxTokensField: "max_tokens",
|
|
17046
|
+
requiresReasoningContentOnAssistantMessages: true,
|
|
17047
|
+
thinkingFormat: "deepseek"
|
|
17048
|
+
}
|
|
17049
|
+
} };
|
|
17050
|
+
/**
|
|
17051
|
+
* opencode-go 上**已知不说 `openai-completions`** 的模型 id(安全闸)。
|
|
17052
|
+
*
|
|
17053
|
+
* 为什么需要它:给路由设 `api` 会**覆盖该路由每个条目的 `base.api`**
|
|
17054
|
+
* (`resolveRouteModels` 的 `request.api ?? base?.api ?? routeApi`)⇒
|
|
17055
|
+
* 若用户的 models 列表里混着异协议模型,设 `api` 会把它们**配坏**。
|
|
17056
|
+
* 故:列表里只要出现下表中的任一 id,本模块**放弃补模型**(响亮说明原因,不硬来)。
|
|
17057
|
+
*
|
|
17058
|
+
* 来源 = 官方文档端点表(`@ai-sdk/anthropic` 与 `@ai-sdk/openai`(Responses)两族)。
|
|
17059
|
+
*/
|
|
17060
|
+
const OPENCODE_GO_NON_COMPLETIONS = /* @__PURE__ */ new Set([
|
|
17061
|
+
"minimax-m3",
|
|
17062
|
+
"minimax-m2.7",
|
|
17063
|
+
"minimax-m2.5",
|
|
17064
|
+
"qwen3.8-max",
|
|
17065
|
+
"qwen3.8-flash",
|
|
17066
|
+
"qwen3.7-max",
|
|
17067
|
+
"qwen3.7-plus",
|
|
17068
|
+
"qwen3.6-plus",
|
|
17069
|
+
"grok-4.7",
|
|
17070
|
+
"grok-4.6",
|
|
17071
|
+
"gpt-6-luna",
|
|
17072
|
+
"gpt-5.6-luna",
|
|
17073
|
+
"muse-spark-1.3-contributor",
|
|
17074
|
+
"muse-spark-1.2-contributor"
|
|
17075
|
+
]);
|
|
17076
|
+
/**
|
|
17077
|
+
* 往**已有显式 `models` 列表**的路由里追加目录未收录的模型(**纯函数**)。
|
|
17078
|
+
*
|
|
17079
|
+
* 三条拒绝条件(每条都对应一个会让配置变坏的真实形态):
|
|
17080
|
+
* ① **没有显式 `models` 列表** ⇒ 该路由此刻服务的是 pi-ai 目录本体;
|
|
17081
|
+
* 一旦我们写出 `models`,**整个目录会被顶掉**(宿主的 `models` 是"整体替换"语义)
|
|
17082
|
+
* ⇒ 宁可不动。
|
|
17083
|
+
* ② **列表里出现已知异协议模型** ⇒ 设路由级 `api` 会覆盖它们的 `base.api`,把它们配坏。
|
|
17084
|
+
* ③ **路由已显式设了别的 `api`** ⇒ 同上,且那是用户的显式选择,不覆盖。
|
|
17085
|
+
*/
|
|
17086
|
+
function planExtraModels(profile) {
|
|
17087
|
+
const raw = profile.models;
|
|
17088
|
+
if (!Array.isArray(raw) || raw.length === 0) return {
|
|
17089
|
+
addedIds: [],
|
|
17090
|
+
skip: "catalog-served"
|
|
17091
|
+
};
|
|
17092
|
+
const ids = /* @__PURE__ */ new Set();
|
|
17093
|
+
for (const entry of raw) if (isPlainObject$1(entry) && typeof entry.id === "string") ids.add(entry.id);
|
|
17094
|
+
const missing = Object.entries(OPENCODE_EXTRA_MODELS).filter(([id]) => !ids.has(id));
|
|
17095
|
+
if (missing.length === 0) return { addedIds: [] };
|
|
17096
|
+
for (const id of ids) if (OPENCODE_GO_NON_COMPLETIONS.has(id)) return {
|
|
17097
|
+
addedIds: [],
|
|
17098
|
+
skip: "mixed-protocol"
|
|
17099
|
+
};
|
|
17100
|
+
const apis = new Set(missing.map(([, model]) => model.api));
|
|
17101
|
+
if (apis.size !== 1) return {
|
|
17102
|
+
addedIds: [],
|
|
17103
|
+
skip: "mixed-protocol"
|
|
17104
|
+
};
|
|
17105
|
+
const api = [...apis][0];
|
|
17106
|
+
const existingApi = profile.api;
|
|
17107
|
+
if (typeof existingApi === "string" && existingApi !== api) return {
|
|
17108
|
+
addedIds: [],
|
|
17109
|
+
skip: "api-conflict"
|
|
17110
|
+
};
|
|
17111
|
+
const appended = missing.map(([id, model]) => ({
|
|
17112
|
+
id,
|
|
17113
|
+
name: model.name,
|
|
17114
|
+
contextWindow: model.contextWindow,
|
|
17115
|
+
maxTokens: model.maxTokens,
|
|
17116
|
+
input: [...model.input],
|
|
17117
|
+
reasoningEfforts: { ...model.reasoningEfforts },
|
|
17118
|
+
compat: { ...model.compat }
|
|
17119
|
+
}));
|
|
17120
|
+
return {
|
|
17121
|
+
models: [...raw, ...appended],
|
|
17122
|
+
...existingApi === api ? {} : { api },
|
|
17123
|
+
addedIds: missing.map(([id]) => id)
|
|
17124
|
+
};
|
|
17125
|
+
}
|
|
16930
17126
|
function isPlainObject$1(value) {
|
|
16931
17127
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16932
17128
|
}
|
|
@@ -16996,7 +17192,23 @@ function planOpencodeAutoConfig(input) {
|
|
|
16996
17192
|
const missing = {};
|
|
16997
17193
|
for (const [name, value] of Object.entries(wanted)) if (!hasHeaderCaseInsensitive(existing, name)) missing[name] = value;
|
|
16998
17194
|
const keys = Object.keys(missing);
|
|
16999
|
-
|
|
17195
|
+
const extra = planExtraModels(profile);
|
|
17196
|
+
if (extra.skip !== void 0 && extra.skip !== "catalog-served") actions.push({
|
|
17197
|
+
kind: "skip-models",
|
|
17198
|
+
route,
|
|
17199
|
+
reason: extra.skip
|
|
17200
|
+
});
|
|
17201
|
+
else if (extra.addedIds.length > 0) actions.push({
|
|
17202
|
+
kind: "add-models",
|
|
17203
|
+
route,
|
|
17204
|
+
ids: extra.addedIds,
|
|
17205
|
+
api: extra.api ?? null
|
|
17206
|
+
});
|
|
17207
|
+
const routePatch = {};
|
|
17208
|
+
if (keys.length > 0) routePatch.headers = missing;
|
|
17209
|
+
if (extra.models !== void 0) routePatch.models = extra.models;
|
|
17210
|
+
if (extra.api !== void 0) routePatch.api = extra.api;
|
|
17211
|
+
if (keys.length === 0 && Object.keys(routePatch).length === 0) {
|
|
17000
17212
|
actions.push({
|
|
17001
17213
|
kind: "skip-route",
|
|
17002
17214
|
route,
|
|
@@ -17004,12 +17216,12 @@ function planOpencodeAutoConfig(input) {
|
|
|
17004
17216
|
});
|
|
17005
17217
|
continue;
|
|
17006
17218
|
}
|
|
17007
|
-
|
|
17008
|
-
actions.push({
|
|
17219
|
+
if (keys.length > 0) actions.push({
|
|
17009
17220
|
kind: "fill-headers",
|
|
17010
17221
|
route,
|
|
17011
17222
|
keys
|
|
17012
17223
|
});
|
|
17224
|
+
patchProviders[route] = routePatch;
|
|
17013
17225
|
}
|
|
17014
17226
|
if (!sawOpencodeRoute) {
|
|
17015
17227
|
const key = input.env[OPENCODE_API_KEY_ENV];
|
|
@@ -17036,6 +17248,8 @@ function describeOpencodeAction(action) {
|
|
|
17036
17248
|
case "fill-headers": return `补齐 ${action.route} 的路由头(${action.keys.length} 项:${action.keys.join(", ")})`;
|
|
17037
17249
|
case "create-route": return `创建 ${action.route} 路由(检测到 ${OPENCODE_API_KEY_ENV})+ 路由头`;
|
|
17038
17250
|
case "skip-route": return `${action.route} 路由头已齐备(跳过)`;
|
|
17251
|
+
case "add-models": return `为 ${action.route} 补目录未收录的模型:${action.ids.join(", ")}` + (action.api === null ? "" : `(并设路由 api=${action.api})`);
|
|
17252
|
+
case "skip-models": return `${action.route} 未补模型(${action.reason === "catalog-served" ? "该路由未声明 models 列表,补 models 会顶掉整个目录" : action.reason === "mixed-protocol" ? "该路由含异协议模型,设路由 api 会把它们配坏" : "该路由已显式设了别的 api"})`;
|
|
17039
17253
|
case "idle": return `无需动作(${action.reason})`;
|
|
17040
17254
|
}
|
|
17041
17255
|
}
|
|
@@ -17083,6 +17297,10 @@ async function applyOpencodeAutoConfigOnce(ctx, env) {
|
|
|
17083
17297
|
}
|
|
17084
17298
|
for (const action of plan.actions) {
|
|
17085
17299
|
if (action.kind === "idle" || action.kind === "skip-route") continue;
|
|
17300
|
+
if (action.kind === "skip-models") {
|
|
17301
|
+
console.log(`[serenity-hooks] · opencode 路由:${describeOpencodeAction(action)}`);
|
|
17302
|
+
continue;
|
|
17303
|
+
}
|
|
17086
17304
|
console.log(`[serenity-hooks] ✓ opencode 路由:${describeOpencodeAction(action)}`);
|
|
17087
17305
|
}
|
|
17088
17306
|
return {
|
|
@@ -17640,6 +17858,35 @@ function createImBridgeTool() {
|
|
|
17640
17858
|
//#endregion
|
|
17641
17859
|
//#region src/live-sessions.ts
|
|
17642
17860
|
/**
|
|
17861
|
+
* live-sessions.ts — 进程内 live 会话取数 + 精简诊断
|
|
17862
|
+
*
|
|
17863
|
+
* ── 为什么单独成模块(R↓,2026-09-15 S142「ACC 侧 autopilot 退场」S1 搬迁)──
|
|
17864
|
+
* 这两个函数原先住在 `autopilot-trajectory.ts`,但它们的**主语不是 autopilot**:
|
|
17865
|
+
* `listLiveSessions` 服务 `acc-diag` ① 段的"**哪个会话属于哪个 CCC**"(会话维度,
|
|
17866
|
+
* 不是 CCC 维度),而"**独立进程看不到运行时**"正是 `acc-diag` 存在的理由。
|
|
17867
|
+
* autopilot 退场后它们必须活下去 ⇒ 独立成模块,不再挂着已死机制的名字。
|
|
17868
|
+
*
|
|
17869
|
+
* ── 迁移边界(不搬什么,以及为什么)──────────────────────────────────────
|
|
17870
|
+
* 原 `diagLive` 还含两段,**均不搬**(其主语是 autopilot,随机制退场):
|
|
17871
|
+
* · `autopilotCccs`(逐 CCC 目标命中 / agent 定位诊断)—— 依赖 `readAutopilotSettings` /
|
|
17872
|
+
* `getAutopilotStatus` / `resolveTargetAgent`
|
|
17873
|
+
* · `panelResolved`(无参面板请求落到哪个 CCC)—— 面板区块随机制退场
|
|
17874
|
+
* 留下的 = **进程 cwd/CCC + live 会话清单**(`acc-diag` ① 段仍需要)。
|
|
17875
|
+
*
|
|
17876
|
+
* 判据纪律:本模块**不得**反向依赖 autopilot 判据层(那会让退场半途而废)。
|
|
17877
|
+
*
|
|
17878
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
17879
|
+
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触
|
|
17880
|
+
* (直接 import 宿主 `cordis` 的类型)。
|
|
17881
|
+
*
|
|
17882
|
+
* **为什么这是"真解耦"而不是"把类型藏起来"(判据):** `ctx` 在本模块**从不被解引用** ——
|
|
17883
|
+
* 它只被**原样透传**给 `hostSessions(ctx)`,而后者(`host/access.ts`,**L0**)的参数
|
|
17884
|
+
* 本就声明为 `ctx: unknown` ⇒ `Context` 这个 import **在语义上是多余的**,
|
|
17885
|
+
* 它没有参与任何类型检查,只贡献了一处 L1→宿主 的接触点。
|
|
17886
|
+
* 🔵 **证据**:grep 本文件 `ctx` 全部 5 处命中 —— 3 处在参数/调用位、2 处在注释,
|
|
17887
|
+
* **零处**属性访问(`ctx.xxx`)。
|
|
17888
|
+
*/
|
|
17889
|
+
/**
|
|
17643
17890
|
* 从 dsh 会话 log 读取标题(latest-wins `session/title` 事件)——
|
|
17644
17891
|
* **标题不在 `sessions.list()` 条目上**(wire/对象均无 title 字段;F3 命名经
|
|
17645
17892
|
* `sessionTitle.rename` 写进 session log),必须从 events 提取(rebuild 同款读取模式)。
|
|
@@ -17660,7 +17907,7 @@ function readSessionTitle(session) {
|
|
|
17660
17907
|
* CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
|
|
17661
17908
|
* 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
|
|
17662
17909
|
*
|
|
17663
|
-
* @param ctx
|
|
17910
|
+
* @param ctx 插件上下文(**只透传**给 L0 取数口,本模块不解引用 ⇒ 类型为 `unknown`)
|
|
17664
17911
|
* @returns 逐条 live 会话
|
|
17665
17912
|
*/
|
|
17666
17913
|
function listLiveSessions(ctx) {
|
package/lib/live-sessions.d.ts
CHANGED
|
@@ -15,8 +15,18 @@
|
|
|
15
15
|
* 留下的 = **进程 cwd/CCC + live 会话清单**(`acc-diag` ① 段仍需要)。
|
|
16
16
|
*
|
|
17
17
|
* 判据纪律:本模块**不得**反向依赖 autopilot 判据层(那会让退场半途而废)。
|
|
18
|
+
*
|
|
19
|
+
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
20
|
+
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触
|
|
21
|
+
* (直接 import 宿主 `cordis` 的类型)。
|
|
22
|
+
*
|
|
23
|
+
* **为什么这是"真解耦"而不是"把类型藏起来"(判据):** `ctx` 在本模块**从不被解引用** ——
|
|
24
|
+
* 它只被**原样透传**给 `hostSessions(ctx)`,而后者(`host/access.ts`,**L0**)的参数
|
|
25
|
+
* 本就声明为 `ctx: unknown` ⇒ `Context` 这个 import **在语义上是多余的**,
|
|
26
|
+
* 它没有参与任何类型检查,只贡献了一处 L1→宿主 的接触点。
|
|
27
|
+
* 🔵 **证据**:grep 本文件 `ctx` 全部 5 处命中 —— 3 处在参数/调用位、2 处在注释,
|
|
28
|
+
* **零处**属性访问(`ctx.xxx`)。
|
|
18
29
|
*/
|
|
19
|
-
import type { Context } from 'cordis';
|
|
20
30
|
/** live 会话条目(诊断用;标题从 events 读) */
|
|
21
31
|
export interface LiveSessionEntry {
|
|
22
32
|
id: string;
|
|
@@ -36,10 +46,10 @@ export declare function readSessionTitle(session: unknown): string | null;
|
|
|
36
46
|
* CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
|
|
37
47
|
* 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
|
|
38
48
|
*
|
|
39
|
-
* @param ctx
|
|
49
|
+
* @param ctx 插件上下文(**只透传**给 L0 取数口,本模块不解引用 ⇒ 类型为 `unknown`)
|
|
40
50
|
* @returns 逐条 live 会话
|
|
41
51
|
*/
|
|
42
|
-
export declare function listLiveSessions(ctx:
|
|
52
|
+
export declare function listLiveSessions(ctx: unknown): LiveSessionEntry[];
|
|
43
53
|
/**
|
|
44
54
|
* 进程内诊断(`acc-diag` ① 段数据源)——输出当前实例的进程归属 + live 会话清单。
|
|
45
55
|
*
|
|
@@ -51,4 +61,4 @@ export interface DiagLiveReport {
|
|
|
51
61
|
processCcc: string | null;
|
|
52
62
|
liveSessions: LiveSessionEntry[];
|
|
53
63
|
}
|
|
54
|
-
export declare function diagLive(ctx:
|
|
64
|
+
export declare function diagLive(ctx: unknown): DiagLiveReport;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as cccRootForCwd } from "./ccc-roots-
|
|
1
|
+
import { r as cccRootForCwd } from "./ccc-roots-Duk89a-h.js";
|
|
2
2
|
import { basename, dirname, join } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
//#region src/time.ts
|
|
@@ -16,6 +16,19 @@
|
|
|
16
16
|
* - **L2 建路由**:环境里有 `OPENCODE_API_KEY` 且没有任何 opencode 路由 → 建 `opencode-go` 并带全头。
|
|
17
17
|
* “有 key”就是用户意图的最强证据 → 不需要的人(没 key)零影响(不会平白多出一组模型)。
|
|
18
18
|
*
|
|
19
|
+
* ## L3 补模型(v1.48.0;S142 owner 具名令「做成自动吧」)
|
|
20
|
+
*
|
|
21
|
+
* pi-ai 目录**收录不全**(如 `deepseek-v4.1-flash` 已在官方端点表、目录里却没有)⇒
|
|
22
|
+
* 面板不显示;手加也不带推理(目录外模型 `base` 为 undefined ⇒ `reasoning` 恒 false,
|
|
23
|
+
* 见 {@link OPENCODE_EXTRA_MODELS} 的注释)⇒ **"能用但没 thinking"**。
|
|
24
|
+
* 本层把 {@link OPENCODE_EXTRA_MODELS} 里的模型补进该路由的 `models` 列表。
|
|
25
|
+
*
|
|
26
|
+
* 🔴 **三条拒绝条件**(每条都对应一个会把配置弄坏的真实形态,宁可不动):
|
|
27
|
+
* ① 路由**没有显式 `models` 列表** —— 补 `models` 会顶掉整个 pi-ai 目录(宿主是"整体替换"语义)
|
|
28
|
+
* ② 列表里含**已知异协议**模型 —— 设路由级 `api` 会覆盖它们的 `base.api`
|
|
29
|
+
* ③ 路由已显式设了**别的 `api`** —— 同上,且那是用户的显式选择
|
|
30
|
+
* 判据与来源见 `planExtraModels` / `OPENCODE_GO_NON_COMPLETIONS`。
|
|
31
|
+
*
|
|
19
32
|
* ## 注入通道(唯一可用且是官方的)
|
|
20
33
|
*
|
|
21
34
|
* `ctx.settings.update('llm-pi-ai', patch)` —— `dsh-settings` 的官方写入面:
|
|
@@ -68,6 +81,37 @@ export declare const OPENCODE_API_KEY_ENV = "OPENCODE_API_KEY";
|
|
|
68
81
|
* 头名大小写不敏感是 HTTP 语义,但**名字不同就是两个头**,故都要。
|
|
69
82
|
*/
|
|
70
83
|
export declare function opencodeRouteHeaders(): Record<string, string>;
|
|
84
|
+
/**
|
|
85
|
+
* 一个「pi-ai 目录尚未收录、但我们已知怎么配」的 opencode-go 模型。
|
|
86
|
+
*
|
|
87
|
+
* 🔴 **为什么必须有 `reasoningEfforts`**:`dsh-llm-pi-ai` 的 `resolveModelReasoning` 是
|
|
88
|
+
* `if (efforts === void 0) return { reasoning: base?.reasoning ?? false }` ——
|
|
89
|
+
* **目录里没有的模型 `base` 为 undefined ⇒ 一律判成非推理模型**("能用但没 thinking"的唯一成因)。
|
|
90
|
+
* 已收录的兄弟模型(如 `deepseek-v4-flash`)从目录的 `base` 拿到 `reasoning`/`thinkingLevelMap`,
|
|
91
|
+
* 故本表**只需收录目录没有的那些**。
|
|
92
|
+
*/
|
|
93
|
+
export interface OpencodeExtraModel {
|
|
94
|
+
readonly name: string;
|
|
95
|
+
/** 该模型说的 wire 协议;见 {@link OPENCODE_GO_NON_COMPLETIONS} 的安全闸 */
|
|
96
|
+
readonly api: string;
|
|
97
|
+
readonly contextWindow: number;
|
|
98
|
+
readonly maxTokens: number;
|
|
99
|
+
readonly input: readonly string[];
|
|
100
|
+
/** pi-ai 推理档 → wire 取值(除 `off` 外都必须非空;空值会被宿主 `invalid` 拒绝) */
|
|
101
|
+
readonly reasoningEfforts: Readonly<Record<string, string>>;
|
|
102
|
+
/** 该模型族的 compat(推理 wire 格式在这里;值取自目录里同族兄弟模型) */
|
|
103
|
+
readonly compat: Readonly<Record<string, unknown>>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 目录补丁表(**唯一真相源 = pi-ai 的 `dist/providers/data/opencode-go.json`**)。
|
|
107
|
+
*
|
|
108
|
+
* 录取判据:① 官方文档 <https://opencode.ai/docs/zh-cn/go/> 的端点表列了它;
|
|
109
|
+
* ② pi-ai 目录里**没有**它 ⇒ 面板不显示、手加也没 thinking。
|
|
110
|
+
*
|
|
111
|
+
* `deepseek-v4.1-flash` 的各字段**逐字取自目录里 `deepseek-v4-flash`**(同族兄弟,
|
|
112
|
+
* 同 `chat/completions` 端点 + 同 `thinkingFormat: deepseek`)——不自行发明规格。
|
|
113
|
+
*/
|
|
114
|
+
export declare const OPENCODE_EXTRA_MODELS: Readonly<Record<string, OpencodeExtraModel>>;
|
|
71
115
|
interface OpencodeAutoConfigInput {
|
|
72
116
|
/** `llm-pi-ai` 命名空间**解析后**的值(0.1.7 读面 `settings.describe()` 里按 `ns` 挑出的 `value`;undefined = 命名空间未注册) */
|
|
73
117
|
resolved: unknown;
|
|
@@ -86,6 +130,15 @@ type OpencodeAutoConfigAction = {
|
|
|
86
130
|
kind: 'skip-route';
|
|
87
131
|
route: string;
|
|
88
132
|
reason: 'headers-complete';
|
|
133
|
+
} | {
|
|
134
|
+
kind: 'add-models';
|
|
135
|
+
route: string;
|
|
136
|
+
ids: readonly string[];
|
|
137
|
+
api: string | null;
|
|
138
|
+
} | {
|
|
139
|
+
kind: 'skip-models';
|
|
140
|
+
route: string;
|
|
141
|
+
reason: 'catalog-served' | 'mixed-protocol' | 'api-conflict';
|
|
89
142
|
} | {
|
|
90
143
|
kind: 'idle';
|
|
91
144
|
reason: 'namespace-unregistered' | 'no-route';
|
package/lib/wake-scheduler.d.ts
CHANGED
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
* CRO 阶段另有**独立**总闸 `croEnabled`(只停 CRO,**不影响**本注册表投递)|
|
|
24
24
|
* 补跑窗口 2h(超窗置 missed 留痕)|同 tick 串行|唤起后维持 live(人类可介入)。
|
|
25
25
|
*/
|
|
26
|
-
import type { Context } from 'cordis';
|
|
27
26
|
import { type ClockRuntime } from './clock-runtime.js';
|
|
28
27
|
import { type WakeEntry } from './wake-registry.js';
|
|
29
28
|
/** 投递结果(写回条目的 lastResult;人读) */
|
|
@@ -86,7 +85,7 @@ export declare function buildWakeText(entry: WakeEntry, target: WakeTarget): str
|
|
|
86
85
|
* @param entry 唤醒条目
|
|
87
86
|
* @returns 投递结果(写回 lastResult)
|
|
88
87
|
*/
|
|
89
|
-
export declare function deliverWake(ctx:
|
|
88
|
+
export declare function deliverWake(ctx: unknown, root: string, entry: WakeEntry): Promise<WakeDeliveryResult>;
|
|
90
89
|
/**
|
|
91
90
|
* 即时消息正文(**刻意不复用** {@link buildWakeText}):唤醒文本以「**到点**(登记于…)」开头,
|
|
92
91
|
* 对即时投递那是**错的事实**。本函数显式区分"这是即时消息、不是到点唤醒"。
|
|
@@ -138,7 +137,7 @@ export declare function buildSendText(target: WakeTarget, message: string, sende
|
|
|
138
137
|
* @param sender 发起者轨迹标识
|
|
139
138
|
* @returns 投递结果(**同步**返回给调用方)
|
|
140
139
|
*/
|
|
141
|
-
export declare function sendToTrajectory(ctx:
|
|
140
|
+
export declare function sendToTrajectory(ctx: unknown, root: string, target: string, message: string, sender: string): Promise<WakeDeliveryResult>;
|
|
142
141
|
/**
|
|
143
142
|
* CRO 唤起消息正文(**刻意不复用** {@link buildWakeText} / {@link buildSendText})。
|
|
144
143
|
*
|
|
@@ -163,5 +162,5 @@ export declare function buildCroWakeText(dirName: string, mdPath: string, prompt
|
|
|
163
162
|
* 会话出现/settings 变化时热启动(与 autopilot 同款触发面,互不干扰)。
|
|
164
163
|
* @param ctx 插件上下文
|
|
165
164
|
*/
|
|
166
|
-
export declare function registerWakeScheduler(ctx:
|
|
165
|
+
export declare function registerWakeScheduler(ctx: unknown): void;
|
|
167
166
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:11 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/im-bridge/acc-diag)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|