@sema-agent/client-core 0.7.0 → 0.8.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/README.md +31 -11
- package/dist/adapt.js +36 -3
- package/dist/compensations.d.ts +63 -0
- package/dist/compensations.js +138 -0
- package/dist/detachWire.d.ts +141 -0
- package/dist/detachWire.js +182 -0
- package/dist/engineSessionParam.d.ts +6 -2
- package/dist/engineSessionParam.js +24 -18
- package/dist/engineWireTarget.d.ts +14 -0
- package/dist/engineWireTarget.js +53 -0
- package/dist/fleet/fleetLedger.d.ts +132 -0
- package/dist/fleet/fleetLedger.js +393 -0
- package/dist/fleet/fleetProjection.d.ts +181 -0
- package/dist/fleet/fleetProjection.js +258 -0
- package/dist/hooksWireCaps.d.ts +91 -0
- package/dist/hooksWireCaps.js +359 -0
- package/dist/host.d.ts +34 -2
- package/dist/host.js +6 -0
- package/dist/index.d.ts +56 -1
- package/dist/index.js +82 -1
- package/dist/liveInitToolFace.d.ts +53 -0
- package/dist/liveInitToolFace.js +251 -0
- package/dist/model/modelFamilies.json +63 -0
- package/dist/model/providerPresets.d.ts +46 -0
- package/dist/model/providerPresets.js +160 -0
- package/dist/model/providerPresets.json +1179 -0
- package/dist/seam.d.ts +19 -3
- package/dist/seam.js +14 -2
- package/dist/subagent/engineCompactWire.d.ts +12 -0
- package/dist/subagent/engineCompactWire.js +176 -0
- package/dist/subagent/engineDelegatedPrompt.d.ts +44 -0
- package/dist/subagent/engineDelegatedPrompt.js +205 -0
- package/dist/subagent/engineRowStopGate.d.ts +18 -0
- package/dist/subagent/engineRowStopGate.js +54 -0
- package/dist/subagent/engineSubagentOutput.d.ts +15 -0
- package/dist/subagent/engineSubagentOutput.js +98 -0
- package/dist/subagent/engineSubagentSteer.d.ts +10 -0
- package/dist/subagent/engineSubagentSteer.js +68 -0
- package/dist/subagent/engineSubagentTail.d.ts +40 -0
- package/dist/subagent/engineSubagentTail.js +241 -0
- package/dist/subagent/engineTaskHandleWire.d.ts +90 -0
- package/dist/subagent/engineTaskHandleWire.js +212 -0
- package/dist/workflowClient.d.ts +42 -0
- package/dist/workflowClient.js +467 -0
- package/dist/workflowMonitor.d.ts +94 -0
- package/dist/workflowMonitor.js +43 -0
- package/package.json +2 -2
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⇄ B6 批搬迁(2026-07-27,设计稿 §3 B6 · B2 判给后批的**宿主耦合六件**之一,也是最后一件大的):
|
|
3
|
+
* cli `src/sema/hooksWireCaps.ts` 整搬。B2 当时不搬的唯一理由 = 它读三层 settings 文件 +
|
|
4
|
+
* 工作区信任门,而那时还没有 `SettingsPort`;B4 把口定出来了(host.ts `SettingsPort`),本批接线。
|
|
5
|
+
*
|
|
6
|
+
* 🔴 搬迁四处差分(端资产上移到 port,行为一字节不变):
|
|
7
|
+
* ① `getSettingsForSource`(cli `utils/settings/settings.ts`)→ `hostSettings()?.getSettingsForSource`;
|
|
8
|
+
* ② `shouldSkipHookDueToTrust`(cli `utils/hooks.ts`,原文用 **lazy `require()`** 避免把大图拖进
|
|
9
|
+
* 静态依赖)→ `hostSettings()?.shouldSkipHookDueToTrust` —— port 化之后 lazy require 的理由
|
|
10
|
+
* 连同 `require` 本身一起消失(本包零 CJS,portability 门的产物断言盯着 `require(`);
|
|
11
|
+
* ③ `logForDebugging` → `hostLog('debug', …)`;
|
|
12
|
+
* ④ `process.env` 缺省参 → `hostEnv()`。
|
|
13
|
+
*
|
|
14
|
+
* 🔴 **SettingsPort 缺席的后果不是「少读一层配置」,是把治理门整个拆了**:
|
|
15
|
+
* `shouldSkipHookDueToTrust` 是**安全门**(未受信工作区的 hook 绝不上 wire)。端不实现它 =
|
|
16
|
+
* 信任门在引擎腿上不存在。所以本文件在 port 缺席时**整体 fail-closed 返回 undefined**
|
|
17
|
+
* (= 一条 hook 都不投),而不是「读不到 settings 就当没有治理策略、把 goal 钩子照投」。
|
|
18
|
+
* ⚠️ 这与壳侧原文那条「早启动就 fall through」的 catch **不同**,是本批唯一的语义差:
|
|
19
|
+
* 壳里那条 catch 的前提是「本地执行器仍会 per-fire 把门」(单进程);库里没有那个兜底,
|
|
20
|
+
* 端要么装 port 要么这条腿不启用。`hostPortMisses().settings` 会把漏装喊出来。
|
|
21
|
+
*
|
|
22
|
+
* 🔴 模块级状态:`wireSessionStopHookPrompt` / `wireSessionStopHookCcSemantics`(`/goal` 的会话级
|
|
23
|
+
* Stop 钩子锁存)——写口 `setWireSessionStopHook`(命令层)、读口 `hooksForWire`(每 turn 请求
|
|
24
|
+
* 构造期)与 `getWireSessionStopHook`(`/goal clear` 回显)全在本文件。两份实例 = 设了目标却
|
|
25
|
+
* 投不出去(而两边代码看着都对)。单实例清单新增项。
|
|
26
|
+
*
|
|
27
|
+
* ── 以下为原文件的领域说明(逐字保留)────────────────────────────────────────────────────────
|
|
28
|
+
*
|
|
29
|
+
* hooksWireCaps — project the user's settings-file hooks onto the wire (`TaskRequest.settings.hooks`).
|
|
30
|
+
*
|
|
31
|
+
* service 1.131.0 hook-runner 阶段一([495]①,契约 = @sema-ai/registry-core/hooks 0.1.51, CC 2.1.198
|
|
32
|
+
* 逐字形状):引擎消费 `body.settings.hooks`,当前点亮 PreToolUse + PostToolUse × command 条目(真
|
|
33
|
+
* /bin/sh -c,exit-2 阻断 stderr 喂模型,matcher 精确/`a|b`/锚定正则);其余事件配置合法不点亮
|
|
34
|
+
* (`hook_events_not_fired` 记账,后续阶段自动亮)。这正是 live 模式的空缺半场:工具在引擎执行,
|
|
35
|
+
* 壳本地永远 fire 不了工具链 hooks — 30 事件枚举里的 ENGINE_OWNED 常量(rc.36 壳半场)壳侧只做
|
|
36
|
+
* schema 合法,不触发,所以整份投影零双跑。
|
|
37
|
+
*
|
|
38
|
+
* GOVERNANCE GATES (对抗复审 rc.41 review HIGH — the wire must honor the SAME gates the local executor
|
|
39
|
+
* honors, or a fleet-managed policy is silently bypassed by the engine leg):
|
|
40
|
+
* - workspace trust: interactive sessions with the trust dialog unaccepted ship NO hooks
|
|
41
|
+
* (shouldSkipHookDueToTrust — the rc.36 "hooks 全被 trust 门禁" invariant, engine leg included);
|
|
42
|
+
* - `disableAllHooks` (managed/policy settings): ships NO hooks;
|
|
43
|
+
* - `allowManagedHooksOnly`: ONLY the policy-settings hooks ship; user/project/local are blocked.
|
|
44
|
+
*
|
|
45
|
+
* Projection semantics (CC settings merge, verbatim shapes):
|
|
46
|
+
* - Sources: policy → user → project → local settings files, per-event arrays CONCATENATED in that
|
|
47
|
+
* order (CC's own multi-source hook semantics — later scopes append, they don't shadow).
|
|
48
|
+
* - Values pass through AS-IS including malformed entries (对抗复审 MED: dropping them client-side is a
|
|
49
|
+
* silent-fallback; the service 400s fail-loud with a field path, which the turn error card shows).
|
|
50
|
+
* - Presence is the opt-in: no hooks configured ⇒ undefined ⇒ no stamp ⇒ request shape unchanged.
|
|
51
|
+
* - Session-scoped runtime hooks and plugin/skill hooks are NOT projected in phase one (they carry
|
|
52
|
+
* pluginRoot/skillRoot expansion contexts the wire contract doesn't model yet).
|
|
53
|
+
* - Multi-tenant deployments advertise `taskSettings.hooks:false` and warn+ignore server-side, so
|
|
54
|
+
* the shell does not pre-gate on capabilities (single-user TOC is always true — [495]① note ①).
|
|
55
|
+
*
|
|
56
|
+
* Secrets: hook processes run under the SERVICE's env allowlist (PATH/HOME/locale + CLAUDE_PROJECT_DIR)
|
|
57
|
+
* and never inherit engine secrets; user-defined variables must ride `settings.env` — nothing for the
|
|
58
|
+
* shell to scrub here (commands are the user's own text).
|
|
59
|
+
*/
|
|
60
|
+
import { hostLog, hostSettings } from './host.js';
|
|
61
|
+
import { hostEnv } from './hostEnv.js';
|
|
62
|
+
import { engineCapString } from './engineCapsCache.js';
|
|
63
|
+
const EDITABLE_HOOK_SOURCES = ['userSettings', 'projectSettings', 'localSettings'];
|
|
64
|
+
function hooksOfSource(source) {
|
|
65
|
+
try {
|
|
66
|
+
const s = hostSettings()?.getSettingsForSource(source);
|
|
67
|
+
return s?.hooks && typeof s.hooks === 'object' && !Array.isArray(s.hooks) ? s.hooks : null;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ── SESSION-SCOPED Stop hook overlay(`/goal`,任务 #75 · C-7,2026-07-26)───────────────────────
|
|
74
|
+
// CC 220 的 `/goal <condition>` 会往 `sessionHooksRegistry` 注册一条会话级 Stop×prompt 钩子
|
|
75
|
+
// (`t.sessionHooksRegistry.add(sessionId,"Stop","",{type:"prompt",prompt})`),那才是「干到条件满足
|
|
76
|
+
// 再停」的机制半边。CC 是单进程,注册进壳内 registry 就够。
|
|
77
|
+
//
|
|
78
|
+
// 🔴 **sema 是双进程壳,壳侧那条钩子在 live 模式 fire 不到**(实证,不是推测):壳的 Stop 钩子只有
|
|
79
|
+
// `src/query/stopHooks.ts handleStopHooks` 一个执行点,调用方是 `src/query.ts` —— 壳自己的 query
|
|
80
|
+
// 循环;live 模式 turn 循环在引擎里跑,壳的 query.ts 不参与。upstreamBridge 只 fire SubagentStop 且
|
|
81
|
+
// observe-only。所以引擎腿必须走 `settings.hooks` 投影,这个 overlay 就是那条腿。
|
|
82
|
+
//
|
|
83
|
+
// 🟢 **本 overlay 默认开**(2026-07-26 翻默认;逃生口 `SEMA_GOAL_STOP_HOOK_WIRE=0/false/no/off`)。
|
|
84
|
+
//
|
|
85
|
+
// 修前默认关,理由是两条**前提**——两条都已被 server 侧行为取证**证伪**(黑板 [1721],6 条行为
|
|
86
|
+
// 用例已进 server 仓;cli 仓另有 scripts/run-goal-stop-hook-live-test.mjs 的真引擎+真模型复验):
|
|
87
|
+
// ① 旧论断「SDK 类型 DEFERRED ⇒ 服务端会 400」**是把类型面当成了运行时校验面**,对 server
|
|
88
|
+
// 不成立:`parseHooksConfig({Stop:[{hooks:[{type:"prompt",prompt}]}]})` 无 error、真解析;
|
|
89
|
+
// prompt 形在 server 的条目白名单里与 command/http/agent **并列**
|
|
90
|
+
// (@sema-agent/server hooks/hook-runner `entry.type !== "command" && … !== "prompt" && …`
|
|
91
|
+
// 那道 type_not_supported 闸口把 prompt 放行,1.277.1 dist 对应 hook-runner.js:390)。
|
|
92
|
+
// 卡着的只是 **SDK / registry-core 的类型**(见下方 cast),与 server 运行时接受与否两件事。
|
|
93
|
+
// ② 旧论断「引擎侧 Stop×prompt 是否真 fire 未取证」已取证:**真 fire**(Stop 触发时 prompt
|
|
94
|
+
// 载体调用一次,`$ARGUMENTS` 已替换为 payload JSON)、**能拦停**(模型输出
|
|
95
|
+
// `{"decision":"block","reason":…}` → `{block: reason}`,core `runner/runtask.js:1663`
|
|
96
|
+
// `(spec.hooks ?? deps.hooks)?.stop` 起 stop gate,整条链通)、非决策 JSON 不拦(与 command
|
|
97
|
+
// 形 exit-0 非 JSON 同语义)、`stop_hook_active:true` 如实进 payload(模型可自己收手;core
|
|
98
|
+
// 另有 STOP_HOOK_BLOCK_CAP=8 连续拦停硬上限兜底,不会无限拦停)。
|
|
99
|
+
// ③ 最坏情况**不是** 400,而是**钩子不生效**:部署没组装 prompt 载体(`ctx.hookLlm` 缺席)⇒
|
|
100
|
+
// server 记账 `hook_entries_skipped{reason:"llm_carrier_unavailable"}` 并跳过,**非阻断**。
|
|
101
|
+
// ⇒ 默认开。等件(不阻断本腿):registry-core hooks 契约建模 prompt 形([1717]① 在飞)、SDK
|
|
102
|
+
// `SettingsHookCommand` 放开 prompt 形 —— 到位后把下方 cast 换成直类型即可。
|
|
103
|
+
//
|
|
104
|
+
// 纪律:
|
|
105
|
+
// - 只放 goal 这一条(单值,不是通用会话钩子通道)——通用会话/插件钩子仍按模块头所述不投影;
|
|
106
|
+
// - 走 hooksForWire() 的**同一套治理门**(trust / disableAllHooks / allowManagedHooksOnly):
|
|
107
|
+
// overlay 在门之后才合并,策略禁钩子时一并禁掉;
|
|
108
|
+
// - `/goal clear` 传 null 收回。
|
|
109
|
+
let wireSessionStopHookPrompt = null;
|
|
110
|
+
/**
|
|
111
|
+
* CC 语义档的**锁存位**(见 {@link setWireSessionStopHook})。投影形按它分道:
|
|
112
|
+
* 真 ⇒ 裸条件(引擎自己包 CC 形);假 ⇒ 壳自造的门 prompt(老引擎唯一能落地的形)。
|
|
113
|
+
*/
|
|
114
|
+
let wireSessionStopHookCcSemantics = false;
|
|
115
|
+
/** The env ESCAPE HATCH for the engine leg (see the block comment above). */
|
|
116
|
+
export const GOAL_STOP_HOOK_WIRE_ENV = 'SEMA_GOAL_STOP_HOOK_WIRE';
|
|
117
|
+
// ── 引擎档位:CC 220 Stop×prompt 完整语义(达标线 = CC_STOP_SEMANTICS_MIN_SERVER,见下)────────
|
|
118
|
+
//
|
|
119
|
+
// server 1.279.0 把 CC 220 的 prompt-hook 半场搬进了引擎(dist/hooks/cc-stop-prompt.js +
|
|
120
|
+
// branch-transcript.js,源码直读):
|
|
121
|
+
// ① Stop×prompt 触发时先取 `sctx.getBranch()`(core 1.419+ 就在 stop gate ctx 里传了它)渲染成
|
|
122
|
+
// 会话转录;**证据面为空则整条 prompt 条目被过滤掉、评估者根本不被调用**
|
|
123
|
+
// (`hook_entries_skipped{reason:"stop_prompt_no_evidence:<why>"}`)——这正是老引擎那条
|
|
124
|
+
// 「白烧一次模型调用、零效果」的反面;
|
|
125
|
+
// ② 调用时带 CC 220 **逐字**的系统提示(`ccPromptSystemFor("Stop")` = CC 的 full 档)+ CC 逐字的
|
|
126
|
+
// 条件包装(`wrapCondition`);
|
|
127
|
+
// ③ 判词 `{ok,reason,impossible}` 在**引擎翻译层**映射:ok ⇒ 放行、impossible ⇒ 记 warn 后**放行**
|
|
128
|
+
// (真 fail-open,既不 block 也不产 additionalContext)、其余 ⇒ block(reason 喂回模型)。
|
|
129
|
+
//
|
|
130
|
+
// ⇒ 到这一档,壳侧唯一正确的动作是把 CC `Xdr` 的**原形(裸条件)**投出去 —— 引擎会自己包。继续投
|
|
131
|
+
// 壳自造的门 prompt 只会被再包一层、并与 CC 系统提示要求的判词 schema 打架。
|
|
132
|
+
// 🔴 但老引擎**没有这套**(1.278.0 的 hooks/ 目录里既无 cc-stop-prompt 也无 branch-transcript,
|
|
133
|
+
// hook-llm 连 `system` 形参都没有):裸条件送过去评估者对本会话是瞎的,回散文 ⇒ 一次都不拦。
|
|
134
|
+
// 所以判档失败/探不到时**必须**退回老形,绝不「因为代码里写了 enforced 就真拦」。
|
|
135
|
+
//
|
|
136
|
+
// 🔴 **为什么达标线钉 1.279.3 而不是 CC 面首次出现的 1.279.0** —— 两道闸,缺一道都不许翻档:
|
|
137
|
+
//
|
|
138
|
+
// ① **core 下限**(1.279.1 才补上):CC Stop 语义在 1.279.0 就在了,但 1.279.1 才把
|
|
139
|
+
// `@sema-agent/core` 的下限提到 `^1.422.0`。core <1.422 上,一条只带 `additionalContext` 的
|
|
140
|
+
// Stop 反馈**不结束 + 不计数 + 清零计数** ⇒ block 与 additionalContext 交替时计数呈锯齿
|
|
141
|
+
// (`[0,1,0,1,0]`)、`STOP_HOOK_BLOCK_CAP` 永远到不了 ⇒ 无界循环;1.422 起它计入上限且不再清零。
|
|
142
|
+
// (注:goal 这条路本身产不出 additionalContext —— CC 判词的 ok/impossible 两支在引擎翻译层都
|
|
143
|
+
// 直接 `continue`;但那条通道对 command 形钩子是开着的,而**上限修好的是最坏值,不是这条通道的
|
|
144
|
+
// 性质**:它仍然会让那一轮不结束。所以措辞面一步都不许放松,见 cmd-goal 的 meta 四档。)
|
|
145
|
+
//
|
|
146
|
+
// ② **判词额度**(1.279.3 才补上):1.279.1/1.279.2 的引擎侧 hook 载体把 `max_tokens` 钳死在
|
|
147
|
+
// `Math.min(…, 1024)`(dist/hooks/hook-llm.js)。而 CC 的评估者系统提示要求「quote evidence
|
|
148
|
+
// from the transcript」,推理档模型的 thinking 还与输出**共用**这份额度 ⇒ 判词被截断(JSON 不
|
|
149
|
+
// 闭合)或干脆零 text 块 ⇒ `parseCcVerdict` 拿不到 ⇒ 引擎**丢弃整条判词并 fail-open 放行**。
|
|
150
|
+
// 🔴 这条缺口的形状与 ① 不同,但危害更隐蔽:它让 enforced 档对模型讲的那两句承诺在**守卫失效
|
|
151
|
+
// 的那些轮里变成空话,而且没有任何人知道**(引擎侧只留一行 `hook_llm_unparsed_verdict` /
|
|
152
|
+
// `hook_llm_failed{returned no content}` 记账,壳侧、用户侧、模型侧全都看不到)。
|
|
153
|
+
// 1.279.3 起 `maxOutputTokens` 由调用方声明,CC 评估者路声明 `CC_EVALUATOR_MAX_OUTPUT_TOKENS`
|
|
154
|
+
// = 4096,并对「无内容」重试一次(**不**重试「有内容但读不懂」)。
|
|
155
|
+
//
|
|
156
|
+
// ⇒ 达标线钉的是「**承诺能兑现**的那个组合」,不是「能力面在场」的那一版。差一档就翻,等于把用户
|
|
157
|
+
// 放到一个「守卫会静默失效」的搭配上,还对着模型把话说满。
|
|
158
|
+
export const CC_STOP_SEMANTICS_MIN_SERVER = '1.279.3';
|
|
159
|
+
/** `x.y.z` → 数值三元组;非法形 ⇒ null(预发标记按「未达标」处理,见下)。 */
|
|
160
|
+
function parseSemverCore(v) {
|
|
161
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v.trim());
|
|
162
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 引擎自报版本是否达到 CC Stop 语义线({@link CC_STOP_SEMANTICS_MIN_SERVER})。
|
|
166
|
+
*
|
|
167
|
+
* 🔴 三条纪律:
|
|
168
|
+
* · **数值比较**,不是字典序(`1.9.0` < `1.279.0`,字符串比较会判反);
|
|
169
|
+
* · **预发版不算达标**(`1.279.0-rc.1` 里那套可能还在改)——正则只认三段纯数字,带 `-` 的直接落
|
|
170
|
+
* null ⇒ false;
|
|
171
|
+
* · 读不出/探不到 ⇒ **false**(降级方向恒安全:老形在新引擎上仍能拦,新形在老引擎上一次都拦不住)。
|
|
172
|
+
*/
|
|
173
|
+
export function ccStopSemanticsFromVersion(version) {
|
|
174
|
+
if (typeof version !== 'string' || version.length === 0)
|
|
175
|
+
return false;
|
|
176
|
+
const got = parseSemverCore(version);
|
|
177
|
+
const min = parseSemverCore(CC_STOP_SEMANTICS_MIN_SERVER);
|
|
178
|
+
if (got === null || min === null)
|
|
179
|
+
return false;
|
|
180
|
+
for (let i = 0; i < 3; i++) {
|
|
181
|
+
if (got[i] !== min[i])
|
|
182
|
+
return got[i] > min[i];
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 本会话连着的引擎是否具备 CC Stop 语义?判据 = live 车道的 `/v1/capabilities` 自报 `version`
|
|
188
|
+
* (engineCapsCache 的 boot-kick 缓存,与 `/health.version` 同源)。
|
|
189
|
+
*
|
|
190
|
+
* 🔴 「未判即降级」:caps 探测是构造期 fire-and-forget,`/goal` 真被敲下时通常早已落袋;万一没落袋
|
|
191
|
+
* 就按老引擎走(meta 也同步退档)——**宁可少承诺,不可多承诺**。
|
|
192
|
+
*/
|
|
193
|
+
export function engineCcStopSemantics(env = hostEnv()) {
|
|
194
|
+
const base = env.SEMA_LIVE_BASEURL;
|
|
195
|
+
if (!base)
|
|
196
|
+
return false;
|
|
197
|
+
return ccStopSemanticsFromVersion(engineCapString(base, 'version'));
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Is the `/goal` Stop-hook engine leg armed? **Default ON** since 2026-07-26 — both of the premises
|
|
201
|
+
* that kept it opt-in were disproved by server-side behavior evidence ([1721]) and re-verified against
|
|
202
|
+
* a real engine + real model (cli scripts/run-goal-stop-hook-live-test.mjs). Set
|
|
203
|
+
* `SEMA_GOAL_STOP_HOOK_WIRE=0|false|no|off` to disarm (an operator escape hatch, not a default).
|
|
204
|
+
*/
|
|
205
|
+
export function isGoalStopHookWireArmed(env = hostEnv()) {
|
|
206
|
+
const v = env[GOAL_STOP_HOOK_WIRE_ENV]?.trim().toLowerCase();
|
|
207
|
+
if (v === undefined || v === '')
|
|
208
|
+
return true;
|
|
209
|
+
return !(v === '0' || v === 'false' || v === 'no' || v === 'off');
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Register (or clear, with `null`) the session-scoped `/goal` Stop prompt hook that rides
|
|
213
|
+
* `settings.hooks.Stop` to the engine. Single-valued: a new goal replaces the previous one,
|
|
214
|
+
* exactly like CC 220's `Xdr` (remove-then-add). Projection is gated by
|
|
215
|
+
* {@link isGoalStopHookWireArmed} — storing is always safe, sending is not (yet).
|
|
216
|
+
*/
|
|
217
|
+
export function setWireSessionStopHook(prompt, opts) {
|
|
218
|
+
wireSessionStopHookPrompt = prompt && prompt.length > 0 ? prompt : null;
|
|
219
|
+
wireSessionStopHookCcSemantics = wireSessionStopHookPrompt === null ? false : opts?.ccSemantics === true;
|
|
220
|
+
}
|
|
221
|
+
/** The currently registered `/goal` Stop prompt, or null (registered ≠ projected — see the gate). */
|
|
222
|
+
export function getWireSessionStopHook() {
|
|
223
|
+
return wireSessionStopHookPrompt;
|
|
224
|
+
}
|
|
225
|
+
/** 当前这条目标登记时锁存的档位(真 = 投裸条件,假 = 投壳自造的门)。 */
|
|
226
|
+
export function getWireSessionStopHookCcSemantics() {
|
|
227
|
+
return wireSessionStopHookCcSemantics;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* 把 `/goal` 的**原始条件**包成一条对引擎 prompt 载体**可执行**的 Stop 门 prompt。
|
|
231
|
+
*
|
|
232
|
+
* 🔴 适用面(2026-07-26 提货批后):**只用于未达标线的引擎档**(见 {@link CC_STOP_SEMANTICS_MIN_SERVER})。
|
|
233
|
+
* 达标档起引擎自己按 CC
|
|
234
|
+
* 220 逐字组装(系统提示 + 会话转录 + `wrapCondition`),那一档投的是**裸条件** —— 见
|
|
235
|
+
* {@link ccStopSemanticsFromVersion} 与 {@link hooksForWire} 的分道。本函数保留,是 tolerate-absent
|
|
236
|
+
* 的实体:老引擎上退回它,仍能拦一次;删掉它等于在老引擎上静默失效。
|
|
237
|
+
*
|
|
238
|
+
* 🔴 为什么必须包(2026-07-26 live 围栏实测,不是设计偏好):CC 单进程的 prompt 钩子执行器
|
|
239
|
+
* (`src/utils/hooks/execPromptHook.ts`)会给条件**套一层系统提示**(「You are evaluating a hook…
|
|
240
|
+
* 回 `{ok:true}` / `{ok:false,reason}`」)**并把整段会话前置**,所以裸条件在 CC 那边能被真评估。
|
|
241
|
+
* 引擎侧的载体两样都没有:
|
|
242
|
+
* · 无系统提示、无会话历史;
|
|
243
|
+
* · Stop payload 的 `transcript_path` 是**空串**(server basePayload),评估者对本会话是**瞎的**;
|
|
244
|
+
* · 判据 schema 也不同 —— core 只认 stdout JSON 的 `decision:"block"`。
|
|
245
|
+
* 实测(钉版 server 1.277.1 + core 1.416 + 真模型):裸条件 `the project contains a file named
|
|
246
|
+
* DONE.txt` 送过去,载体照样被调用,模型回的是**散文**(「I don't have direct access to your file
|
|
247
|
+
* system…」)⇒ `parseHookStdout` 拿不到 decision ⇒ **一次都不拦**。即:不包 = 每次停机白烧一次
|
|
248
|
+
* 模型调用、零效果。
|
|
249
|
+
*
|
|
250
|
+
* 所以这里把条件包成引擎契约里**能落地**的形状。诚实标注它**够不到 CC 的完整语义**:
|
|
251
|
+
* · 能做到:每个 turn 的**第一次**停机尝试被拦下,目标原文重新注入给模型,逼它自查后再收尾
|
|
252
|
+
* (`stop_hook_active` 为真时门自己收手 ⇒ 不会无限拦停);真正「条件是否成立」的判断交给
|
|
253
|
+
* **有会话、有工具的主模型**,而不是让瞎的评估者假装判断;
|
|
254
|
+
* · 做不到:CC 那种「拦到条件真成立为止」(需要引擎把 transcript 交给载体)、以及「条件满足后
|
|
255
|
+
* 自动清除目标」。这两条是**等件**,meta 文案因此不照抄 CC `Pwo`(见 cmd-goal.tsx)。
|
|
256
|
+
*
|
|
257
|
+
* 台账仍存**原始条件**({@link getWireSessionStopHook}),所以 `/goal clear` 读回的是用户写的那句。
|
|
258
|
+
*/
|
|
259
|
+
export function buildGoalStopHookPrompt(condition) {
|
|
260
|
+
const reason = `[session goal] Before you stop: verify that this goal actually holds — ${condition}. ` +
|
|
261
|
+
`If it does hold, say so plainly and finish. If it does not, keep working toward it and do not ` +
|
|
262
|
+
`pause to ask the user what to do.`;
|
|
263
|
+
return ('You are the Stop gate for an autonomous coding agent session. The user set this session goal, ' +
|
|
264
|
+
'which the agent is supposed to satisfy before it stops:\n' +
|
|
265
|
+
`<goal>\n${condition}\n</goal>\n` +
|
|
266
|
+
'Stop-hook input JSON: $ARGUMENTS\n' +
|
|
267
|
+
'Reply with RAW JSON only — no prose, no explanation, no code fences.\n' +
|
|
268
|
+
'If the input JSON has "stop_hook_active" set to true, this gate already fired once for this turn: ' +
|
|
269
|
+
'reply with exactly {"continue":true} so the agent may stop.\n' +
|
|
270
|
+
'Otherwise reply with exactly:\n' +
|
|
271
|
+
JSON.stringify({ decision: 'block', reason }));
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* The merged, GOVERNANCE-GATED settings-file hooks for the wire, or undefined when none apply.
|
|
275
|
+
* Per-event arrays concatenate across sources (policy → user → project → local).
|
|
276
|
+
*/
|
|
277
|
+
export function hooksForWire() {
|
|
278
|
+
// 🔴 port 缺席 = **整条腿 fail-closed**(见文件头第三段):端没装 SettingsPort 就没有信任门,
|
|
279
|
+
// 而信任门是安全门。`hostSettings()` 自己会计 miss 并喊一次,宿主自检读得到。
|
|
280
|
+
const settings = hostSettings();
|
|
281
|
+
if (settings === undefined)
|
|
282
|
+
return undefined;
|
|
283
|
+
// trust gate first (cheapest + broadest): untrusted interactive workspace ⇒ no hooks anywhere,
|
|
284
|
+
// the engine leg included — same invariant the local executor enforces per-fire.
|
|
285
|
+
try {
|
|
286
|
+
if (settings.shouldSkipHookDueToTrust()) {
|
|
287
|
+
hostLog('debug', 'hooksWireCaps: workspace not trusted — no hooks projected to the engine');
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
/* trust check threw (early boot) — fall through; 端侧本地执行器仍 per-fire 把门 */
|
|
293
|
+
}
|
|
294
|
+
let policy = null;
|
|
295
|
+
try {
|
|
296
|
+
policy = settings.getSettingsForSource('policySettings');
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
policy = null;
|
|
300
|
+
}
|
|
301
|
+
if (policy?.disableAllHooks === true) {
|
|
302
|
+
hostLog('debug', 'hooksWireCaps: disableAllHooks (managed) — no hooks projected to the engine');
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
const managedOnly = policy?.allowManagedHooksOnly === true;
|
|
306
|
+
const sources = managedOnly
|
|
307
|
+
? ['policySettings']
|
|
308
|
+
: ['policySettings', ...EDITABLE_HOOK_SOURCES];
|
|
309
|
+
const merged = {};
|
|
310
|
+
for (const source of sources) {
|
|
311
|
+
const hooks = hooksOfSource(source);
|
|
312
|
+
if (!hooks)
|
|
313
|
+
continue;
|
|
314
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
315
|
+
if (!Array.isArray(groups) || groups.length === 0)
|
|
316
|
+
continue;
|
|
317
|
+
// Verbatim passthrough INCLUDING malformed entries — the service is the single validator
|
|
318
|
+
// (400 fail-loud with a field path); a client-side drop would be a silent fallback.
|
|
319
|
+
merged[event] = [...(merged[event] ?? []), ...groups];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// `/goal` 的会话级 Stop×prompt 钩子(CC 220 Xdr)——在治理门之后合并,所以 trust /
|
|
323
|
+
// disableAllHooks / allowManagedHooksOnly 任一命中时它同样不投影(managedOnly 下用户态的
|
|
324
|
+
// goal 钩子也一并禁掉,与 EDITABLE_HOOK_SOURCES 被排除同理)。默认开,见上方 overlay 注释。
|
|
325
|
+
if (wireSessionStopHookPrompt !== null && !managedOnly && isGoalStopHookWireArmed()) {
|
|
326
|
+
// 🔴 这个双 cast 是**类型面的最小且诚实的局部处置**,不是「绕过校验」:
|
|
327
|
+
// · server **运行时接受** prompt 形(hook-runner 条目白名单与 command/http/agent 并列,
|
|
328
|
+
// [1721] 6 条行为用例 + cli 仓 live 围栏双证);
|
|
329
|
+
// · 卡着的是 **SDK 类型**:`SettingsHookCommand.type` 仍是字面量 `"command"`
|
|
330
|
+
// (@sema-agent/sdk dist/settings.d.ts,prompt 形 v1 标 DEFERRED);
|
|
331
|
+
// · **registry-core 建模 prompt 形在 [1717]① 等件中**,SDK 随后放开。
|
|
332
|
+
// 到位后这里换成直类型、去掉 cast(唯一改动点,调用面不动)。
|
|
333
|
+
const goalMatcher = {
|
|
334
|
+
matcher: '',
|
|
335
|
+
// 🔴 投影形**按引擎档位分道**(锁存于 set 那一刻,见 setWireSessionStopHook):
|
|
336
|
+
// · CC 语义档(server ≥ CC_STOP_SEMANTICS_MIN_SERVER)⇒ **裸条件** —— 与 CC `Xdr` 注册的
|
|
337
|
+
// `{type:"prompt",prompt:condition}` 逐字同形;引擎自己套 CC 系统提示 + 会话转录 +
|
|
338
|
+
// `wrapCondition`,再包一层只会打架;
|
|
339
|
+
// · 老引擎档 ⇒ 包装后的可执行门 prompt(裸条件在老引擎上一次都拦不住 —— 见
|
|
340
|
+
// buildGoalStopHookPrompt 的实测说明)。
|
|
341
|
+
// 台账两档都存**原始条件**,供 `/goal clear` 读回。
|
|
342
|
+
hooks: [
|
|
343
|
+
{
|
|
344
|
+
type: 'prompt',
|
|
345
|
+
prompt: wireSessionStopHookCcSemantics
|
|
346
|
+
? wireSessionStopHookPrompt
|
|
347
|
+
: buildGoalStopHookPrompt(wireSessionStopHookPrompt),
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
merged['Stop'] = [...(merged['Stop'] ?? []), goalMatcher];
|
|
352
|
+
}
|
|
353
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
354
|
+
}
|
|
355
|
+
/** 测试钩:清 `/goal` 锁存(壳里靠进程边界隔离;包内同进程多组断言必须能清)。 */
|
|
356
|
+
export function __resetHooksWireCapsForTests() {
|
|
357
|
+
wireSessionStopHookPrompt = null;
|
|
358
|
+
wireSessionStopHookCcSemantics = false;
|
|
359
|
+
}
|
package/dist/host.d.ts
CHANGED
|
@@ -30,8 +30,13 @@ import { type NotificationQueuePort } from './notifications.js';
|
|
|
30
30
|
export interface SettingsPort {
|
|
31
31
|
/** 按来源取 settings(cli `utils/settings/settings.ts` 的 `getSettingsForSource`)。 */
|
|
32
32
|
getSettingsForSource(source: string): unknown;
|
|
33
|
-
/**
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* 信任门:hook 是否因工作区未受信而跳过。**返回 true = 跳过**。
|
|
35
|
+
* 🔴 B6 更正:壳侧真身 `utils/hooks.shouldSkipHookDueToTrust()` 是**零参**的(门是工作区级、
|
|
36
|
+
* 不是逐条 hook 级)。B4 定口时按「逐条判」写了一个必填参,与真身不符 —— 改成可选参:
|
|
37
|
+
* 既容得下零参真身,也不妨碍将来某个端要做逐条判。库内唯一调用点(hooksWireCaps)不传参。
|
|
38
|
+
*/
|
|
39
|
+
shouldSkipHookDueToTrust(hook?: unknown): boolean;
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
37
42
|
* fs 口 —— scratchpad 目录族(cli `utils/permissions/filesystem.ts`)。
|
|
@@ -49,6 +54,29 @@ export interface TimersPort {
|
|
|
49
54
|
setTimer(ms: number, fn: () => void): unknown;
|
|
50
55
|
clearTimer(handle: unknown): void;
|
|
51
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* session 口(**B6 引入**)—— 「现在是哪个会话 / 哪个 run 在飞」。
|
|
59
|
+
*
|
|
60
|
+
* 为什么必须是端给:这两个量在壳里分别住在 `liveSessionStore`(引擎首轮 done 回填的权威值)+
|
|
61
|
+
* `bootstrap/state`(壳 boot 时生成的 uuidv7)与 `engineToolDetach`(pty 绑定的活跃 run),
|
|
62
|
+
* **三个都是宿主生命周期资产**;web 宿主是路由参数 + BFF 会话,桌面是主进程 session-host。
|
|
63
|
+
*
|
|
64
|
+
* 🔴 缺席后果**不是**「少个装饰」:`engineSessionParam()` 恒 undefined ⇒ durable 面
|
|
65
|
+
* (taskOutput/taskStop/subagentOutput)对 session-bound run 一律 **404 fail-closed**
|
|
66
|
+
* ([1501]§三 / [1498]③),整条子代读面静默哑掉。所以本口**计 miss**。
|
|
67
|
+
*
|
|
68
|
+
* 🔴 `currentSessionId()` 绝不返回空串 —— `?session=`(空值)按「带了且不匹配」判 404
|
|
69
|
+
* ([1501] 空串陷阱);端返空串时库当缺席处理(下面 `engineSessionParam` 有归一)。
|
|
70
|
+
*/
|
|
71
|
+
export interface SessionPort {
|
|
72
|
+
/** 当前会话 id(壳:引擎捕获值优先、退 boot-time id);两源都缺 ⇒ undefined。 */
|
|
73
|
+
currentSessionId(): string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* 当前**活跃引擎 run** 的 id(壳 = `engineToolDetach.getActiveEngineTaskId()`,由
|
|
76
|
+
* X-Task-Id 响应头早捕获)。没有在飞的 turn ⇒ undefined/null 都收。
|
|
77
|
+
*/
|
|
78
|
+
activeRunId(): string | undefined | null;
|
|
79
|
+
}
|
|
52
80
|
/** `installHost` 的入参 —— 全部可选,缺席即该能力不启用。 */
|
|
53
81
|
export interface HostPorts {
|
|
54
82
|
log?(level: AdapterLogLevel, msg: string, data?: unknown): void;
|
|
@@ -58,6 +86,8 @@ export interface HostPorts {
|
|
|
58
86
|
timers?: TimersPort;
|
|
59
87
|
settings?: SettingsPort;
|
|
60
88
|
fs?: FsPort;
|
|
89
|
+
/** B6:会话/活跃 run 口(缺席 ⇒ durable 子代面对 session-bound run 恒 404,见 SessionPort)。 */
|
|
90
|
+
session?: SessionPort;
|
|
61
91
|
}
|
|
62
92
|
/**
|
|
63
93
|
* 装配宿主口。**可多次调用做增量装配**(只覆盖本次给出的键),便于分模块装。
|
|
@@ -76,6 +106,8 @@ export declare function hostSettings(): SettingsPort | undefined;
|
|
|
76
106
|
export declare function hostFs(): FsPort | undefined;
|
|
77
107
|
/** 定时器口(缺席 = undefined,不计 miss:T40 竞速本就允许不启用)。 */
|
|
78
108
|
export declare function hostTimers(): TimersPort | undefined;
|
|
109
|
+
/** session 口(缺席 = undefined + 计 miss:漏装会让整条 durable 子代读面 404 静默哑掉)。 */
|
|
110
|
+
export declare function hostSession(): SessionPort | undefined;
|
|
79
111
|
/**
|
|
80
112
|
* 🔴 宿主自检出口 —— 恒应为空对象。非空 = 有口漏装,**对应的面已经在静默失效**。
|
|
81
113
|
* 含通知队列的 miss(从 notifications.ts 汇过来,宿主只需看这一个数)。
|
package/dist/host.js
CHANGED
|
@@ -32,6 +32,8 @@ export function installHost(ports) {
|
|
|
32
32
|
host.settings = ports.settings;
|
|
33
33
|
if ('fs' in ports)
|
|
34
34
|
host.fs = ports.fs;
|
|
35
|
+
if ('session' in ports)
|
|
36
|
+
host.session = ports.session;
|
|
35
37
|
if ('queue' in ports)
|
|
36
38
|
installNotificationQueuePort(ports.queue ?? null);
|
|
37
39
|
return () => {
|
|
@@ -58,6 +60,10 @@ export function hostFs() {
|
|
|
58
60
|
export function hostTimers() {
|
|
59
61
|
return host.timers;
|
|
60
62
|
}
|
|
63
|
+
/** session 口(缺席 = undefined + 计 miss:漏装会让整条 durable 子代读面 404 静默哑掉)。 */
|
|
64
|
+
export function hostSession() {
|
|
65
|
+
return host.session ?? miss('session');
|
|
66
|
+
}
|
|
61
67
|
/**
|
|
62
68
|
* 🔴 宿主自检出口 —— 恒应为空对象。非空 = 有口漏装,**对应的面已经在静默失效**。
|
|
63
69
|
* 含通知队列的 miss(从 notifications.ts 汇过来,宿主只需看这一个数)。
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,23 @@
|
|
|
5
5
|
* 沿革:@sema-agent/wire-cc-adapter 0.1.0 = seam 类型 + id 确定性派生 + 首批踩坑纯函数;
|
|
6
6
|
* 0.1.2(#52a)= adapt() 管线首批(纯投影臂全落 + 壳态耦合臂投影成 ChromeEvent + 差分守卫);
|
|
7
7
|
* 0.2.0 = 迁入 sema-client-core 独立仓并改名(旧 npm 名 deprecate 指本包);
|
|
8
|
+
* 0.8.0 = **B6 批**(设计稿 §3 B6,分两轮落):
|
|
9
|
+
* ——【前半,fleet/子代身份族】fleetClient 沿 §2.5 拆两半(帧体归库、连接归端):
|
|
10
|
+
* `fleet/fleetProjection` + `fleet/fleetLedger` · 子代 wire 族六件(`subagent/*`)·
|
|
11
|
+
* `engineSessionParam`(走新 `SessionPort`)· `engineWireTarget`(七份手抄 `wireConfig()` 收编
|
|
12
|
+
* 前六份,第七份 `planReviewWire` 属 B7)· G13 taskStop 409 五形 typed · Path B 记号时序修。
|
|
13
|
+
* ——【后半,余项收官】① `detachWire` **拆**搬(判定 + cancel-arm 台账进包,信号路径的裸 fetch
|
|
14
|
+
* 发射留宿主 —— 宪法例外 census C1-4;宿主取件口 `detachCancelArm()`)· `workflowClient` 整搬
|
|
15
|
+
* (投影器 + live 源)· `workflowMonitor` 契约(六个类型 + WPe,此前住在 TUI 渲染件里);
|
|
16
|
+
* ② **P4 残件三**:`hooksWireCaps`(SettingsPort 的第一个真消费者;port 缺席 = 整体 fail-closed)·
|
|
17
|
+
* `liveInitToolFace`(凭证换 §8-2 的 `resolveWireAuth`,三条动态 import 转静态)·
|
|
18
|
+
* **model 数据表**(`model/providerPresets.{ts,json}` + `modelFamilies.json` —— `modelBudgetRule`
|
|
19
|
+
* 封顶① 的 `perModelCap` 的唯一来源;与壳树副本有 byte-identity 漂移锁);
|
|
20
|
+
* ③ **P5**:T32/T34/T47/T48/T49(整条进包)与 T35/T36/T44(拆)逐条核实并配断言,新出
|
|
21
|
+
* `compensations.ts` 登记表(自检口 `compensationSplitViolations()` 恒应为空)+ **P1-2 归属回落
|
|
22
|
+
* 定案**(FIFO 回落**不删** = tolerate-absent 保护臂;typed 双键在场恒赢已成对反钉;退役条件
|
|
23
|
+
* 钉在达标线 ≥ server 1.290)。
|
|
24
|
+
* ⚠️ B6 落码事实更正:P5 那八条的**落码全在 B4**,B6 做的是核实 + 登记 + 定案,不是二次搬迁。
|
|
8
25
|
* 0.7.0 = **B5 批**(设计稿 §3 B5):① **配对台账折回**([1857] —— 0.6.0 把 `notifiedRuns`/
|
|
9
26
|
* `cardEnqueuedRuns` 放适配器实例,与清账/记账的另一半不共享前提 = 双卡/UI 零显示/模型二收,
|
|
10
27
|
* 本批整体折回 notifications.ts 的 module 台账);② **B/D/E 层搬入**(`toolResult.ts`:
|
|
@@ -50,7 +67,20 @@
|
|
|
50
67
|
* 本包放实例级是**有意的**(多 session 宿主上 module 形会让两个会话互吞 SubagentStart,见 DIVERGENCE-6)。
|
|
51
68
|
* —— B3 新增:adapter/runStream.ts(`inFlightTurns` 计数,读口 `isRunStreamActive()`;
|
|
52
69
|
* 两份实例 = 引擎滚动升级门恒读 false ⇒ **turn 中途热换引擎**,正是它当初要防的事)。
|
|
53
|
-
*
|
|
70
|
+
* —— B6 新增(前半):fleet/fleetLedger.ts(`liveBgViews`;两份 ⇒ 引擎温切门恒读空 ⇒ respawn
|
|
71
|
+
* 静默杀掉在飞 bg 子代)· subagent/engineDelegatedPrompt.ts(`transcriptIdByTask`;两份 ⇒
|
|
72
|
+
* Prompt 段恒留白而两边代码看着都对)· engineWireTarget.ts(`installed`)。
|
|
73
|
+
* —— B6 新增(后半,按「两份实例会怎样」从重到轻排):
|
|
74
|
+
* · **hooksWireCaps.ts**(`wireSessionStopHookPrompt` + `wireSessionStopHookCcSemantics`)——
|
|
75
|
+
* 写方 = `/goal` 命令层,读方 = 每 turn 的请求构造期 `hooksForWire()`。两份 ⇒ 目标设了、
|
|
76
|
+
* 投不出去,**而两边代码看着都对**(与 fleetLedger 同一种静默失效)。
|
|
77
|
+
* · **detachWire.ts**(`cancelArm`)—— 写方 = `-p` 发头那一刻,读方三个:`isDetachArmed`
|
|
78
|
+
* (400 分诊的第一道门)、`detachedTaskId`(分诊文案)、`detachCancelArm`(**宿主发射腿的
|
|
79
|
+
* 取件口**)。两份 ⇒ 头发了但 hint 判不出 + Ctrl+C 取消不掉,两个静默失效各一半。
|
|
80
|
+
* · liveInitToolFace.ts(`cached`)—— 两份只是多探一次 `/v1/capabilities/scenarios/:name`,
|
|
81
|
+
* **不是**静默失效;仍登记,免得下次读表的人以为它没状态。
|
|
82
|
+
* · model/providerPresets.ts(`presetIndexCache`)—— 由不可变 JSON 派生的纯 memo,两份只费内存。
|
|
83
|
+
* 🔴 **宿主装配**——B4 起总入口是 `installHost({log,probe,queue,timers,settings,fs,session})`
|
|
54
84
|
* (`installNotificationQueuePort()` 仍是队列的真源出口,`installHost({queue})` 直通它,两者不分裂);
|
|
55
85
|
* 自检口 `hostPortMisses()` 恒应为空对象,非空 = 有口漏装、对应的面已在静默失效。
|
|
56
86
|
* —— B2 起的既有说明:`installNotificationQueuePort()` ——
|
|
@@ -59,6 +89,13 @@
|
|
|
59
89
|
* (runStream 的两条切边:statusline 真 usage / plan-review 审批卡)· `ctx.setTimer`
|
|
60
90
|
* (T40 IDLE-FLUSH,不给 = 长工具参数生成期已生成内容仍押到下一帧才上屏)· `ctx.signal`
|
|
61
91
|
* (Esc 中断)· `ctx.log` / `ctx.probe`(诊断面)。
|
|
92
|
+
* —— B6 的两个**计 miss** 口(缺席不是「少个装饰」):`session`(缺 ⇒ durable 子代读面对
|
|
93
|
+
* session-bound run 一律 404 fail-closed)· `settings`(缺 ⇒ `hooksForWire()` 整体 fail-closed
|
|
94
|
+
* 返回 undefined —— 信任门是**安全门**,库不会「读不到策略就照投」)。
|
|
95
|
+
*
|
|
96
|
+
* 🔴 **补偿层账本**(B6 引入):`MIGRATED_COMPENSATIONS` + `compensationSplitViolations()` ——
|
|
97
|
+
* 哪条壳侧补偿拆成了「判定归库 / 执行留宿主」、对面挂在哪个 chrome 臂、上游到货后何时退休,
|
|
98
|
+
* 全做成数据。它**不是** `ADAPTER_DIVERGENCES`(那张表说的是 adapt 与 cli 行为**不同**的地方)。
|
|
62
99
|
*/
|
|
63
100
|
export * from './seam.js';
|
|
64
101
|
export * from './workflow.js';
|
|
@@ -120,3 +157,21 @@ export * from './limitsWire.js';
|
|
|
120
157
|
export * from './interactiveToolsWire.js';
|
|
121
158
|
export * from './headlessPermissionModeWire.js';
|
|
122
159
|
export * from './headlessReconnectWire.js';
|
|
160
|
+
export * from './fleet/fleetProjection.js';
|
|
161
|
+
export * from './fleet/fleetLedger.js';
|
|
162
|
+
export * from './subagent/engineDelegatedPrompt.js';
|
|
163
|
+
export * from './subagent/engineRowStopGate.js';
|
|
164
|
+
export * from './subagent/engineSubagentOutput.js';
|
|
165
|
+
export * from './subagent/engineSubagentSteer.js';
|
|
166
|
+
export * from './subagent/engineTaskHandleWire.js';
|
|
167
|
+
export * from './subagent/engineCompactWire.js';
|
|
168
|
+
export * from './subagent/engineSubagentTail.js';
|
|
169
|
+
export * from './engineSessionParam.js';
|
|
170
|
+
export * from './engineWireTarget.js';
|
|
171
|
+
export * from './detachWire.js';
|
|
172
|
+
export * from './workflowMonitor.js';
|
|
173
|
+
export * from './workflowClient.js';
|
|
174
|
+
export * from './hooksWireCaps.js';
|
|
175
|
+
export * from './liveInitToolFace.js';
|
|
176
|
+
export * from './model/providerPresets.js';
|
|
177
|
+
export * from './compensations.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,23 @@
|
|
|
5
5
|
* 沿革:@sema-agent/wire-cc-adapter 0.1.0 = seam 类型 + id 确定性派生 + 首批踩坑纯函数;
|
|
6
6
|
* 0.1.2(#52a)= adapt() 管线首批(纯投影臂全落 + 壳态耦合臂投影成 ChromeEvent + 差分守卫);
|
|
7
7
|
* 0.2.0 = 迁入 sema-client-core 独立仓并改名(旧 npm 名 deprecate 指本包);
|
|
8
|
+
* 0.8.0 = **B6 批**(设计稿 §3 B6,分两轮落):
|
|
9
|
+
* ——【前半,fleet/子代身份族】fleetClient 沿 §2.5 拆两半(帧体归库、连接归端):
|
|
10
|
+
* `fleet/fleetProjection` + `fleet/fleetLedger` · 子代 wire 族六件(`subagent/*`)·
|
|
11
|
+
* `engineSessionParam`(走新 `SessionPort`)· `engineWireTarget`(七份手抄 `wireConfig()` 收编
|
|
12
|
+
* 前六份,第七份 `planReviewWire` 属 B7)· G13 taskStop 409 五形 typed · Path B 记号时序修。
|
|
13
|
+
* ——【后半,余项收官】① `detachWire` **拆**搬(判定 + cancel-arm 台账进包,信号路径的裸 fetch
|
|
14
|
+
* 发射留宿主 —— 宪法例外 census C1-4;宿主取件口 `detachCancelArm()`)· `workflowClient` 整搬
|
|
15
|
+
* (投影器 + live 源)· `workflowMonitor` 契约(六个类型 + WPe,此前住在 TUI 渲染件里);
|
|
16
|
+
* ② **P4 残件三**:`hooksWireCaps`(SettingsPort 的第一个真消费者;port 缺席 = 整体 fail-closed)·
|
|
17
|
+
* `liveInitToolFace`(凭证换 §8-2 的 `resolveWireAuth`,三条动态 import 转静态)·
|
|
18
|
+
* **model 数据表**(`model/providerPresets.{ts,json}` + `modelFamilies.json` —— `modelBudgetRule`
|
|
19
|
+
* 封顶① 的 `perModelCap` 的唯一来源;与壳树副本有 byte-identity 漂移锁);
|
|
20
|
+
* ③ **P5**:T32/T34/T47/T48/T49(整条进包)与 T35/T36/T44(拆)逐条核实并配断言,新出
|
|
21
|
+
* `compensations.ts` 登记表(自检口 `compensationSplitViolations()` 恒应为空)+ **P1-2 归属回落
|
|
22
|
+
* 定案**(FIFO 回落**不删** = tolerate-absent 保护臂;typed 双键在场恒赢已成对反钉;退役条件
|
|
23
|
+
* 钉在达标线 ≥ server 1.290)。
|
|
24
|
+
* ⚠️ B6 落码事实更正:P5 那八条的**落码全在 B4**,B6 做的是核实 + 登记 + 定案,不是二次搬迁。
|
|
8
25
|
* 0.7.0 = **B5 批**(设计稿 §3 B5):① **配对台账折回**([1857] —— 0.6.0 把 `notifiedRuns`/
|
|
9
26
|
* `cardEnqueuedRuns` 放适配器实例,与清账/记账的另一半不共享前提 = 双卡/UI 零显示/模型二收,
|
|
10
27
|
* 本批整体折回 notifications.ts 的 module 台账);② **B/D/E 层搬入**(`toolResult.ts`:
|
|
@@ -50,7 +67,20 @@
|
|
|
50
67
|
* 本包放实例级是**有意的**(多 session 宿主上 module 形会让两个会话互吞 SubagentStart,见 DIVERGENCE-6)。
|
|
51
68
|
* —— B3 新增:adapter/runStream.ts(`inFlightTurns` 计数,读口 `isRunStreamActive()`;
|
|
52
69
|
* 两份实例 = 引擎滚动升级门恒读 false ⇒ **turn 中途热换引擎**,正是它当初要防的事)。
|
|
53
|
-
*
|
|
70
|
+
* —— B6 新增(前半):fleet/fleetLedger.ts(`liveBgViews`;两份 ⇒ 引擎温切门恒读空 ⇒ respawn
|
|
71
|
+
* 静默杀掉在飞 bg 子代)· subagent/engineDelegatedPrompt.ts(`transcriptIdByTask`;两份 ⇒
|
|
72
|
+
* Prompt 段恒留白而两边代码看着都对)· engineWireTarget.ts(`installed`)。
|
|
73
|
+
* —— B6 新增(后半,按「两份实例会怎样」从重到轻排):
|
|
74
|
+
* · **hooksWireCaps.ts**(`wireSessionStopHookPrompt` + `wireSessionStopHookCcSemantics`)——
|
|
75
|
+
* 写方 = `/goal` 命令层,读方 = 每 turn 的请求构造期 `hooksForWire()`。两份 ⇒ 目标设了、
|
|
76
|
+
* 投不出去,**而两边代码看着都对**(与 fleetLedger 同一种静默失效)。
|
|
77
|
+
* · **detachWire.ts**(`cancelArm`)—— 写方 = `-p` 发头那一刻,读方三个:`isDetachArmed`
|
|
78
|
+
* (400 分诊的第一道门)、`detachedTaskId`(分诊文案)、`detachCancelArm`(**宿主发射腿的
|
|
79
|
+
* 取件口**)。两份 ⇒ 头发了但 hint 判不出 + Ctrl+C 取消不掉,两个静默失效各一半。
|
|
80
|
+
* · liveInitToolFace.ts(`cached`)—— 两份只是多探一次 `/v1/capabilities/scenarios/:name`,
|
|
81
|
+
* **不是**静默失效;仍登记,免得下次读表的人以为它没状态。
|
|
82
|
+
* · model/providerPresets.ts(`presetIndexCache`)—— 由不可变 JSON 派生的纯 memo,两份只费内存。
|
|
83
|
+
* 🔴 **宿主装配**——B4 起总入口是 `installHost({log,probe,queue,timers,settings,fs,session})`
|
|
54
84
|
* (`installNotificationQueuePort()` 仍是队列的真源出口,`installHost({queue})` 直通它,两者不分裂);
|
|
55
85
|
* 自检口 `hostPortMisses()` 恒应为空对象,非空 = 有口漏装、对应的面已在静默失效。
|
|
56
86
|
* —— B2 起的既有说明:`installNotificationQueuePort()` ——
|
|
@@ -59,6 +89,13 @@
|
|
|
59
89
|
* (runStream 的两条切边:statusline 真 usage / plan-review 审批卡)· `ctx.setTimer`
|
|
60
90
|
* (T40 IDLE-FLUSH,不给 = 长工具参数生成期已生成内容仍押到下一帧才上屏)· `ctx.signal`
|
|
61
91
|
* (Esc 中断)· `ctx.log` / `ctx.probe`(诊断面)。
|
|
92
|
+
* —— B6 的两个**计 miss** 口(缺席不是「少个装饰」):`session`(缺 ⇒ durable 子代读面对
|
|
93
|
+
* session-bound run 一律 404 fail-closed)· `settings`(缺 ⇒ `hooksForWire()` 整体 fail-closed
|
|
94
|
+
* 返回 undefined —— 信任门是**安全门**,库不会「读不到策略就照投」)。
|
|
95
|
+
*
|
|
96
|
+
* 🔴 **补偿层账本**(B6 引入):`MIGRATED_COMPENSATIONS` + `compensationSplitViolations()` ——
|
|
97
|
+
* 哪条壳侧补偿拆成了「判定归库 / 执行留宿主」、对面挂在哪个 chrome 臂、上游到货后何时退休,
|
|
98
|
+
* 全做成数据。它**不是** `ADAPTER_DIVERGENCES`(那张表说的是 adapt 与 cli 行为**不同**的地方)。
|
|
62
99
|
*/
|
|
63
100
|
export * from './seam.js';
|
|
64
101
|
export * from './workflow.js';
|
|
@@ -140,3 +177,47 @@ export * from './limitsWire.js';
|
|
|
140
177
|
export * from './interactiveToolsWire.js';
|
|
141
178
|
export * from './headlessPermissionModeWire.js';
|
|
142
179
|
export * from './headlessReconnectWire.js';
|
|
180
|
+
// ── B6 批:fleet/子代身份族(2026-07-27)────────────────────────────────────────────────────────
|
|
181
|
+
// `fleetClient.ts` 沿设计稿 §2.5 的那一刀拆两半:**帧体归库、连接归端**。
|
|
182
|
+
// 🔴 单例清单新增三项(两份实例 = 面静默失效,不是报错):
|
|
183
|
+
// · `fleet/fleetLedger.ts` 的 `liveBgViews` —— 写=每个 createFleetLedger,读=引擎温切门
|
|
184
|
+
// (`readEngineActiveBgTasks`)与代际让位读面(`isFleetSessionScoped`)。两份 ⇒ 温切门恒读空
|
|
185
|
+
// ⇒ respawn 静默杀掉在飞 bg 子代。
|
|
186
|
+
// · `subagent/engineDelegatedPrompt.ts` 的 `transcriptIdByTask` —— 写=本包 fleetLedger 的行帧/
|
|
187
|
+
// 通知帧臂,读=取件腿。两份 ⇒ Prompt 段恒留白而两边代码看着都对。
|
|
188
|
+
// · `engineWireTarget.ts` 的 `installed` —— 非 Node 宿主的显式 wire 目标。
|
|
189
|
+
export * from './fleet/fleetProjection.js';
|
|
190
|
+
export * from './fleet/fleetLedger.js';
|
|
191
|
+
export * from './subagent/engineDelegatedPrompt.js';
|
|
192
|
+
export * from './subagent/engineRowStopGate.js';
|
|
193
|
+
export * from './subagent/engineSubagentOutput.js';
|
|
194
|
+
export * from './subagent/engineSubagentSteer.js';
|
|
195
|
+
export * from './subagent/engineTaskHandleWire.js';
|
|
196
|
+
export * from './subagent/engineCompactWire.js';
|
|
197
|
+
export * from './subagent/engineSubagentTail.js';
|
|
198
|
+
export * from './engineSessionParam.js';
|
|
199
|
+
export * from './engineWireTarget.js';
|
|
200
|
+
// B6 余项①:headless detach wire(**拆**:判定+cancel-arm 台账进包,信号路径裸 fetch 发射留宿主
|
|
201
|
+
// —— 设计稿 §3 表脚注「`:221` 裸 cancel = TUI 留」;宿主取件口 = `detachCancelArm()`)。
|
|
202
|
+
export * from './detachWire.js';
|
|
203
|
+
// B6 余项①:workflow 监视器契约(类型 + WPe 派生)与 MF-W live 源。
|
|
204
|
+
// 🔴 `workflowMonitor.ts` 是 `projectWorkflowRun()` 产物的**类型契约**——它此前住在 TUI 的
|
|
205
|
+
// `overrides/workflow-detail-dialog.tsx` 里,等于「库产出的形状,契约归渲染件所有」。
|
|
206
|
+
export * from './workflowMonitor.js';
|
|
207
|
+
export * from './workflowClient.js';
|
|
208
|
+
// ── B6 余项②(P4 残件三):B2 判给后批的宿主耦合件收官 + model 数据表 ──────────────────────────
|
|
209
|
+
// 🔴 `hooksWireCaps` 是 **SettingsPort 的第一个消费者**(B4 定的口,B6 才接上);port 缺席时它
|
|
210
|
+
// **整体 fail-closed 返回 undefined** —— 信任门是安全门,不是取值。单实例清单新增:
|
|
211
|
+
// `wireSessionStopHookPrompt`/`wireSessionStopHookCcSemantics`(`/goal` 会话级 Stop 钩子锁存)。
|
|
212
|
+
export * from './hooksWireCaps.js';
|
|
213
|
+
// 🔴 `liveInitToolFace` 的三条 fail-soft 动态 import 在包内变成静态 import(三件 B5 已在包内),
|
|
214
|
+
// try/catch 逐条保留(它护的另一半「函数自己抛」在包内照样成立)。单实例:探测缓存 `cached`。
|
|
215
|
+
export * from './liveInitToolFace.js';
|
|
216
|
+
// 🔴 model 数据表 —— `modelBudgetRule.defaultMaxTokensFor()` 的封顶① 参数 `perModelCap` 的**唯一**
|
|
217
|
+
// 来源。表不在包里,那条中央规则在 web/桌面上恒拿不到 cap ⇒ 对 deepseek-chat(8192)这类模型
|
|
218
|
+
// 超发 ⇒ 网关 400。搬迁期与壳树两份 JSON 由 pure 门的 byte-identity 断言锁住。
|
|
219
|
+
export * from './model/providerPresets.js';
|
|
220
|
+
// ── B6 余项③(P5):补偿层登记表 —— 把「哪条补偿拆了、拆缝对面是谁、什么时候能退休」做成数据。
|
|
221
|
+
// 🔴 它**不是** `ADAPTER_DIVERGENCES`(那张表说的是 adapt 与 cli 行为不同的地方;本表里的东西
|
|
222
|
+
// 两侧行为相同)。自检口 `compensationSplitViolations()` 恒应为空。
|
|
223
|
+
export * from './compensations.js';
|