@sema-agent/client-core 0.37.0 → 0.38.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/CHANGELOG.md +135 -0
- package/README.md +2 -2
- package/dist/adapt/arms.js +40 -0
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +2 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +94 -0
- package/dist/engineCapsCache.d.ts +33 -1
- package/dist/engineCapsCache.js +26 -4
- package/dist/engineErrorCodes.d.ts +20 -0
- package/dist/engineErrorCodes.js +44 -0
- package/dist/fleet/fleetProjection.d.ts +34 -0
- package/dist/fleet/fleetProjection.js +40 -0
- package/dist/hitl/hitlBridge.js +76 -17
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/model/modelSupplyRules.d.ts +102 -0
- package/dist/model/modelSupplyRules.js +149 -0
- package/dist/model/providerPresets.js +68 -12
- package/dist/seam.d.ts +48 -1
- package/dist/seam.js +7 -0
- package/dist/subagent/engineSubagentResume.d.ts +18 -0
- package/dist/subagent/engineSubagentResume.js +7 -0
- package/docs/INTEGRATION-CLIENTS.md +66 -16
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,141 @@
|
|
|
28
28
|
> 不许悄悄漂:豁免登记的 `releasedAt` 与 `FROZEN` 账上 0.36.0 那一行逐字相等;本段(点名版本号
|
|
29
29
|
> `0.36.0` + 关键字「勘误」)必须还在这份头注里 —— 删掉本段而不同批把门侧豁免一起处理,门当场红。
|
|
30
30
|
|
|
31
|
+
## 0.38.0(2026-08-21)
|
|
32
|
+
|
|
33
|
+
**#318 四件合批 + SDK 7.2.0 提货跟车。行为面五条**:一条新投影臂(additive)、一条超时预算收窄
|
|
34
|
+
(口径换代)、一条分层遮蔽真修(修前那条分支恒不可达)、两条**可分辨读口/分类**的加细。**
|
|
35
|
+
|
|
36
|
+
> 📌 **公面基线 753 → 764**(npm 上的 `0.37.0` 是 753)。11 个新导出全部 **additive**,零 BREAKING:
|
|
37
|
+
> `engineCapState`(+ 类型 `EngineCapState`)· `resolveEntryVision` / `computeDeleteBlockers` /
|
|
38
|
+
> `computeDeleteWarnings`(+ 四个具名类型)· `CONFIG_DELEGATION_ENTRY_CAPS` /
|
|
39
|
+
> `DELEGATION_CONCURRENCY_CAP` / `DELEGATION_SESSION_CAP` / `DELEGATION_CAP_CODES` /
|
|
40
|
+
> `isDelegationCapCode` · `wireCycleSeq` / `wireRetiredBy`。
|
|
41
|
+
> 🔴 **peer 地板抬到 `>=7.2.0`**(见下「SDK 7.2.0 提货」);装 <7.2.0 SDK 的端提货前先抬依赖。
|
|
42
|
+
|
|
43
|
+
- **🆕 件① `engine_notice` 投影臂(additive,server ≥7.36 / #310;黑板 [4679] 发布、[4682] cli 认领)**:
|
|
44
|
+
引擎的结构化通告里被 server 判为面向**本会话终端用户**的那一小撮,现在有了投影口 ——
|
|
45
|
+
`eventToSdkMessage` → 中性内部臂 `engine_notice` → `adapt()` → chrome 臂
|
|
46
|
+
`{ kind:'engine_notice', code, message, detail, sessionId?, ts?, eventId? }`。**不铸 transcript 行**
|
|
47
|
+
(通告是披露不是转录物,硬铸一行就是替引擎编一句它没说过的话)。
|
|
48
|
+
🔴 **开集是本件的承重设计,不是风格**:本层**一个码都不硬编** —— 没有识别表、没有 switch、
|
|
49
|
+
没有「已知才投」。server 的白名单会随 core 码册增长(起步三码 → core 5.47/5.48 的
|
|
50
|
+
`NOTICE_AUDIENCE` 到货后 hold 三码入册),任何在客户端复制一份白名单的实现都会在下一次
|
|
51
|
+
core 加码的当天开始静默丢帧。同理**不读能力位**:帧到了就投影(A-022「到帧即服务」),
|
|
52
|
+
拿一个探测位去 gate 一条**已经到手的事实**,只会在探测未判/失败时把真事实丢掉。
|
|
53
|
+
`detail` **整对象逐字透传**(禁挑键):逐码不同且开集,挑键 = 新码的事实在本层静默蒸发。
|
|
54
|
+
消费纪律五条(按 code+detail 渲 / message 仅 fallback / 未知码不许丢 / durable 重放幂等 /
|
|
55
|
+
`harvest_quarantined` 的 `moved` 与 `escalated` **不可相减**)写在 `seam.ts` 臂注释与
|
|
56
|
+
`docs/INTEGRATION-CLIENTS.md` §3f。**壳渲染半场不在本批**(cli 0.38.0 提货时做)。
|
|
57
|
+
🔴 **重放身份两个键(codex 对抗复审 [medium] 采纳,当窗修)**:首版只带 `eventId`,于是 body 不带
|
|
58
|
+
它时留给端的唯一去重口是 `code+ts` —— 而 `ts` 是 **server 观察时刻(ms)**,同毫秒同码的两条真通告
|
|
59
|
+
会被折成一条、跨重连的同一条又可能重复。而 SDK 的 SSE parser **本来就把 durable `id:`
|
|
60
|
+
(= `task_event.seq`)stamp 到 `ev.id` 上**(`dist/sse.js`),本包也早有规范访问口
|
|
61
|
+
(`adapter/types.eventSeq`)—— 有稳定序号却不带 = 本层自己把可靠性降级了。现补 `eventSeq` 位,
|
|
62
|
+
幂等序 = `eventId` > `eventSeq` > `code+ts`。🔴 **两键绝不合并**
|
|
63
|
+
([same-name-different-meaning-crosses-layers]:全局身份 vs per-task 序号)。
|
|
64
|
+
⚠️ **如实登记两条**(2026-08-21 亲验,接入文档宪法):① `engine_notice` **尚未进已发布 SDK 的
|
|
65
|
+
`AgentEvent` union** —— sdk 仓 `3d6aebc` 已写,但 npm `@sema-agent/sdk@7.2.0` 真 tarball 的 `dist/`
|
|
66
|
+
全树零命中(同批的 `FleetTaskRow.cycleSeq` 在场,证明抽检会说话)。故按 `workflow_complete` /
|
|
67
|
+
`human_input` 当年的先例走 **raw 预分派**,并留自退休钉:臂一进 union,`assertNeverArm` 编译期
|
|
68
|
+
真红,逼下一棒搬进 switch。② server 的 `ASSISTANT-WIRE-CONTRACT` 附录 D.3 仍写「起步白名单三码」,
|
|
69
|
+
而 server main 的 `ENGINE_NOTICE_WIRE_CODES` 已是六码 —— **对本包与端零影响**(正因为消费面按
|
|
70
|
+
开集写),已按宪法回报 server。
|
|
71
|
+
- **🔴 件② `decide` 超时重试总窗收窄:10 分钟 → 90 秒(口径换代,不是调参)**。
|
|
72
|
+
旧值按 [4664] 的**长调用**口径开:legacy 任务级 decide 在响应前同步跑整条 resume(模型往返无上限),
|
|
73
|
+
60s 帽结构性必炸,所以超时是**常态**。server #316([4687],随 7.37.0 发车)把 200 体从终局形改成
|
|
74
|
+
**受理回执**(受理点在 core `resumeStream` 解析后,一切会变成拒绝的判定仍同步发生;实测受理即回
|
|
75
|
+
≈23ms)⇒ **超时从常态变真异常**,总窗随之收窄。
|
|
76
|
+
🔴 **新值锚在「真正决定发数的量」上而不是拍一个整数**:SDK 的 per-attempt 帽(60s)本包不可配,
|
|
77
|
+
而每一发超时类失败本身就吃满一个帽 ⇒ 实际发数由「总窗 ÷ 帽」决定且量化很粗 —— 窗 ≤1 帽 = **零重试**
|
|
78
|
+
(重试环变死结构)、>2 帽 = 3 发起步一路回到分钟级、(1 帽, 2 帽] = **恰一次重试**。取
|
|
79
|
+
`60_000 × 1.5`(**写成「帽 × 系数」而不是裸 90000**,SDK 改帽时推导自动跟手),落在区间中部而不
|
|
80
|
+
贴边界。可预算最坏墙钟 ≈121s。**重试环结构一字未动**(超时臂仍走窗判),[4675] 的「绝不造第二层
|
|
81
|
+
节奏叠乘」照旧成立 —— 本批没有新增任何计时器/退避层。
|
|
82
|
+
🔴 **两条残余同步形如实登记**(收窄不等于长调用消失了,[4687] 逐字):终局形仍存在于 ①pre-7.37
|
|
83
|
+
的 server、②≥7.37 但没有 durable run 行可跟的部署。本包是三端共用件,故重试环保留、总窗不设 0;
|
|
84
|
+
这两形上窗尽的代价是**重呈臂**(同一张卡重交用户)而不是把 turn 判死。
|
|
85
|
+
🔴 **「至少重试一次」改由发数保证(codex 对抗复审 [medium] 采纳,当窗修)**:per-attempt 帽是
|
|
86
|
+
**宿主构造 client 时**定的(本桥吃注入 client,`HitlClientLike` 根本不暴露 `timeoutMs`)⇒
|
|
87
|
+
「总窗 ÷ 帽 = 发数」这条推导只在 SDK **缺省值**上成立,不能当本共用件的性质 —— 一个 120s client
|
|
88
|
+
的宿主在首发超时那一刻 elapsed 已 ≥ 窗,**一次重试都拿不到**,而上面那两条残余同步形恰恰最需要
|
|
89
|
+
它。故补第二个界 `DECIDE_TIMEOUT_MIN_ATTEMPTS = 2`,终止条件改为**合取**(发数达标 ∧ 窗尽)。
|
|
90
|
+
⚠️ 这**不是** [4675] 的「第二层节奏叠乘」:零新增定时器/退避层,退避仍是同一条 backoff 链,
|
|
91
|
+
只是同一个循环上的第二个终止条件。三种 client 配置都有界且都拿得到那次重试:120s ⇒ 2 发/~240s、
|
|
92
|
+
缺省 60s ⇒ 2 发/~121s、30s ⇒ 3 发/~92s。常驻套件同批加 F6-b4(窗=0 且每发都超时 ⇒ 必须恰在
|
|
93
|
+
第 2 发后耗尽),F6-b3 的期望值随行为改动同批更新并写明数字变化的来由。
|
|
94
|
+
- **🔴 件③(a)`inferFamily` 的 `vision` 分层遮蔽修 —— 一条恒不可达的分支**(cli [4752] 自领缺口1)。
|
|
95
|
+
病:`vision` 位只写在 `modelFamilies.json` 上,而那张表只有**第④层**读;可任何真实的 `deepseek-*`
|
|
96
|
+
id 在**第①层**(preset 大表精确)或**第③层**(家族主干包含)就已命中并 return,而这两层的构造器
|
|
97
|
+
`hitOf` 不带 vision 位 ⇒ 第④层结构性不可达 ⇒ `deepseek4` 行的 `vision: false` **永远读不出来**,
|
|
98
|
+
下游 `MODEL_VISION=false` stamp 恒不发生(两处注释自述的行为是死的)。
|
|
99
|
+
修的形 = 把 `vision` 从「第④层的一个字段」提成**与容量证据层正交的一次独立查表**:容量轴
|
|
100
|
+
(ctx/maxTokens/perModelCap)四层证据强者先赢,**一字不动**;vision 轴恒查 family 表,与哪一层命中
|
|
101
|
+
**无关**。理由:preset 大表根本没有 vision 列,主干匹配拿的是**别的模型**的行 —— 两者都不是
|
|
102
|
+
「这个 id 能不能看图」的证据。🔴 **诚实缺席不变**:表上没标 ⇒ 键不 stamp(`kimi`/`qwen` 仍缺席),
|
|
103
|
+
绝不顺手补成 `false` 去封死一个真能力。
|
|
104
|
+
⚠️ **新契约形(cli 侧套内已钉前提断言,提货时同批改)**:`inferFamily(id)?.vision` 现在对**任何**
|
|
105
|
+
证据层的命中都可能在场(此前只有第④层),`vision` 与 `perModelCap` 的在场性**互相独立**。
|
|
106
|
+
- **🆕 件③(b)`engineCapState(baseUrl, key)` —— 能力位的可分辨读口(additive)**(cli [4752] 自领缺口2)。
|
|
107
|
+
`engineCapTrue` 只回 true/false,`false` 同时承载「引擎明说没有」「还没探」「探测在飞」「探测失败」
|
|
108
|
+
四件事。对**放行判据**这个塌缩刻意且正确(fail-closed);对**自检/诊断面**(doctor)它是谎报 ——
|
|
109
|
+
这也是 doctor 此前无法复用共享缓存的直接原因。新读口给闭集五态:`true`/`false`(引擎明说)、
|
|
110
|
+
`unprobed`(没有已落地的 caps;四种成因**故意合并**,因为对调用方是同一个动作)、`absent`
|
|
111
|
+
(caps 已落地但没这个键)、`non_boolean`(键在但值不是布尔 —— 不并进 `absent`,那会是假话)。
|
|
112
|
+
🔴 **放行语义一字未变**:`engineCapTrue` 现在就是 `engineCapState(...) === 'true'` 的单源实现。
|
|
113
|
+
- **🆕 件③(c)Model Hub 供给面三纯函数上收(additive)**:`resolveEntryVision` /
|
|
114
|
+
`computeDeleteBlockers` / `computeDeleteWarnings` 从 cli `modelChannels.ts` 搬进
|
|
115
|
+
`model/modelSupplyRules.ts`(新模块,**零 IO**)。三端 Model Hub 要回答的是同样三个问题
|
|
116
|
+
(vision 生效值+来源 / 删了会不会断链 / 删了会不会悄悄改语义),各写一遍 = 三个 Hub 对
|
|
117
|
+
「同一条档能不能删」给出三个答案,而其中两个要等用户按下 y 之后才被发现。
|
|
118
|
+
读盘那半场留各端;`doc === null` 的两义(没读到 vs 读不出来)**在调用方分流** —— 读不出来时
|
|
119
|
+
调用方自产 `unreadable` 阻断,fail-closed 拒删比「猜没人指着」安全。
|
|
120
|
+
⚠️ **上收差分两条(行为零改动)**:`resolveEntryVision` 的返回型由内联匿名对象改为具名
|
|
121
|
+
`EntryVisionResolution`(本仓 typeshape 门 B4 棘轮),两个 compute 的入参同理具名 —— **结构逐字
|
|
122
|
+
相同**,cli 剪切 import 时零适配。🔴 与件③(a)**同批落地是刻意的**:修前
|
|
123
|
+
`inferFamily(deepseek-*)?.vision` 结构性恒 `undefined`,`source:'family'` 这一档对 deepseek 族
|
|
124
|
+
**从来没走到过** —— 只上收不修 = 把一条死分支原样搬进三端。
|
|
125
|
+
- **🆕 件④ 两族 coded 拒绝(additive;[4743] core 5.48.0 @cli 点名件的 client-core 半场)**:
|
|
126
|
+
① **委派席位到限两码** `delegation.concurrency_cap` / `delegation.session_cap`(core 5.48.0
|
|
127
|
+
design/323 的 `RunnerDeps.delegationEntryCaps`,CC 对齐缺省 20/200)入词表,配 `DELEGATION_CAP_CODES`
|
|
128
|
+
识别表 + `isDelegationCapCode` 开集前缀谓词。🔴 **两码处置不对称,禁合并**:并发帽 = **可等**
|
|
129
|
+
(兄弟结束就有位,同一条命令过一会儿照样成)/ 会话累计帽 = **等也没用**(这棵树这辈子的配额用完了)
|
|
130
|
+
—— 合并成一句「委派失败」会让一半用户去干等、另一半去改配置。
|
|
131
|
+
② `config.delegation_entry_caps`(坏旋钮响亮拒)入 `CONFIG_REFUSAL_CODES` 识别表(前缀谓词本来
|
|
132
|
+
就认它;识别表是**登记**,让提货 diff 里一眼看出这版加了哪个词)。
|
|
133
|
+
③ **SendMessage 复活裁决两格** `resume.row_recycling` → `row-contended`(行正在被裁决,
|
|
134
|
+
**窗口自清,过一会儿再发就成**)/ `resume.row_gone` → `row-gone`(行已被终态 GC 收走,
|
|
135
|
+
**等也没用,重开新 agent**)。此前两码双双落进开集兜底 `error`,core 明明给了**两个不同的下一步**,
|
|
136
|
+
到客户端只剩一句泛泛失败。判定归包、文案归端。
|
|
137
|
+
🔴 **载体缺口如实登记(2026-08-21 亲验,不在本包侧路补救)**:core 把
|
|
138
|
+
`delegation.concurrency_cap` / `delegation.session_cap` 铸进 Task 工具结果体的 `details.error`,
|
|
139
|
+
而 core 的 `errorCode` 抬升腿只读 `details.code` → `details.errorKind` **两个拼法**,
|
|
140
|
+
`structuredFrom` 又要求 `details.type` 落在 `CC_DETAIL_TYPES` 里(这条 detail 连 `type` 都没有)
|
|
141
|
+
⇒ **今天这两码在 wire 上既不在 `tool_end.errorCode` 也不在 `structured`**,只剩模型面文案。
|
|
142
|
+
故本批**先立词、不落消费分支** —— 按文案反解正是 `engineErrorCodes.ts` 存在的理由要根除的东西
|
|
143
|
+
([cross-repo-fix-at-source-constitution]:载体缺口在 core,下游不许侧路补救)。已列上游诉求。
|
|
144
|
+
- **🔴 SDK 提货 7.1.0 → 7.2.0(peer 地板同批抬齐)+ `FleetTaskRow` 两新 wire 键投影**:
|
|
145
|
+
sdk 7.2.0 给 `FleetTaskRow` 声明了 `cycleSeq` / `retiredBy`(#261 §2,**server ≥7.25.0 早已在 wire
|
|
146
|
+
上发**,只是 SDK 类型迟到 ⇒ 本层此前把两键整个丢弃)。抬地板不是「顺手跟版本」:
|
|
147
|
+
`FLEET_TASK_ROW_WIRE_KEY_TUPLE` 与 `keyof FleetTaskRow` 是**编译期双向等值**钉,补键之后在
|
|
148
|
+
<7.2.0 的实装物上整包编译不过。
|
|
149
|
+
两位现已投进 `FleetTaskView`:`cycleSeq` = 代际号(fresh spawn 就是 cycle 1,**SendMessage 复活即 +1**;
|
|
150
|
+
同 id 帧更大 ⇒ 复活、更小 ⇒ 前代迟到帧)、`retiredBy` = **非亲报终态的投影者**(在场 = 这条终态是
|
|
151
|
+
对账腿从 durable run 行读出来的,发布方死了 —— **幽灵行与正常收尾唯一的 wire 判据**;读侧开集)。
|
|
152
|
+
🔴 **两位都只在场才落键**:`cycleSeq` 缺席 = 「这条行**没有代际概念**」(同步委派子代 / workflow
|
|
153
|
+
agent / 顶层 run 行),**不是第一代** —— 读成 1 会把「首帧迟到」误判成「复活」;`retiredBy` 缺席 =
|
|
154
|
+
发布方亲报。任何一个补默认值都会把一个诚实缺席翻译成假话。壳渲染契约尚未声明这两键,已登记在
|
|
155
|
+
`scripts/run-fleet-view-keys-test.mjs` 的 `SHELL_UNDECLARED_LEDGER`(壳提货补声明,本表当天删两条)。
|
|
156
|
+
|
|
157
|
+
### 已知局限(本版新增)
|
|
158
|
+
|
|
159
|
+
- **`engine_notice` 只有投影半场,没有渲染半场** —— 端不接 chrome 臂 `engine_notice` 的话,
|
|
160
|
+
这类披露在该宿主上**看不见**(不是报错)。cli 渲染腿排在 0.38.0 提货批。
|
|
161
|
+
- **`delegation.*` 两码今天在 wire 上没有机器可读载体**(见件④ ③),词表已立但消费分支候 core 补
|
|
162
|
+
`details.code` 孪生拼法(或抬升腿兼读 `details.error`)后再接。
|
|
163
|
+
- **`engineCapState` 的 `unprobed` 不区分四种成因**(没 kick / 在飞 / 探测失败 / 刚失效)——
|
|
164
|
+
刻意合并,分开需要引擎没给的信息;要「探测到底跑没跑」得靠 `engineCapsSettled` 的时序而不是读口。
|
|
165
|
+
|
|
31
166
|
## 0.37.0(2026-08-19)
|
|
32
167
|
|
|
33
168
|
**#307 双扫台账余量四条(S43/S44/S47/S25)。行为面**四条**:三条是「上游给了本层没接」的补接
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
35
35
|
|
|
36
36
|
## Scope
|
|
37
37
|
|
|
38
|
-
**Version:** 0.
|
|
38
|
+
**Version:** 0.38.0
|
|
39
39
|
|
|
40
40
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
41
41
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
|
@@ -65,7 +65,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
65
65
|
`SseIdleError`, `probeHealth`, `APIError` and `TaskStopConflictError` are imported as values in
|
|
66
66
|
five modules, and the browser bundle really bundles the SDK through (the portability guard would
|
|
67
67
|
exit 3 rather than quietly mark it external).
|
|
68
|
-
- The declared floor is `>=7.
|
|
68
|
+
- The declared floor is `>=7.2.0`, and it is *witnessed*: the guard checks that an actually
|
|
69
69
|
installed SDK at that line still exports every value-level symbol this package imports and still
|
|
70
70
|
declares `TaskStats.costMicroUsd` (the key `costOrNull` reads). A floor nobody ever ran is a
|
|
71
71
|
promise, not a contract.
|
package/dist/adapt/arms.js
CHANGED
|
@@ -254,6 +254,45 @@ const humanInputArm = function* (m) {
|
|
|
254
254
|
...(str(m.eventId) !== undefined ? { eventId: str(m.eventId) } : {}),
|
|
255
255
|
});
|
|
256
256
|
};
|
|
257
|
+
/**
|
|
258
|
+
* #310 / #318 件① —— `engine_notice` 引擎结构化通告(server ≥7.36,契约 = ASSISTANT-WIRE-CONTRACT 附录 D)。
|
|
259
|
+
*
|
|
260
|
+
* 姿势抄同族的 `human_input`(引擎侧发生了一件事、壳记一笔的非持久帧,归 chrome;**不走 attachment**:
|
|
261
|
+
* 通告要带结构化事实与幂等键,折成一行文案会逼消费端反解 code)。
|
|
262
|
+
*
|
|
263
|
+
* 🔴 **开集:一个码都不判**。这里没有识别表、没有 switch、没有「已知才投」—— `code` 认不认得是
|
|
264
|
+
* **渲染面**的事,投影面只负责把事实原样送到。`detail` 同理**整对象透传**(禁挑键:逐码不同且开集,
|
|
265
|
+
* 挑键 = 新码的事实在本层静默蒸发)。
|
|
266
|
+
* 🔴 **不读能力位**:帧到了就投影(A-022「到帧即服务」)。拿探测位 gate 一条已经到手的事实,只会在
|
|
267
|
+
* 探测未判/失败时把真事实丢掉。
|
|
268
|
+
* 🔴 子流帧(带 `parentToolCallId`)同族让位:不上 leader 面。
|
|
269
|
+
* ⚠️ 第二道 `code` 在场判(投影层已判 malformed):同 `human_input` 的 carrier 二道判,防的是**非投影
|
|
270
|
+
* 口喂进来的帧**(宿主自建管线 / 重放存量转录),不是重复判据。
|
|
271
|
+
*/
|
|
272
|
+
const engineNoticeArm = function* (m) {
|
|
273
|
+
if (typeof m.parentToolCallId === 'string')
|
|
274
|
+
return;
|
|
275
|
+
const code = typeof m.code === 'string' ? m.code : '';
|
|
276
|
+
if (code.length === 0)
|
|
277
|
+
return;
|
|
278
|
+
const rawDetail = m.detail;
|
|
279
|
+
const detail = typeof rawDetail === 'object' && rawDetail !== null && !Array.isArray(rawDetail)
|
|
280
|
+
? rawDetail
|
|
281
|
+
: {};
|
|
282
|
+
yield chrome({
|
|
283
|
+
kind: 'engine_notice',
|
|
284
|
+
laneProof: MAIN,
|
|
285
|
+
code,
|
|
286
|
+
message: typeof m.message === 'string' ? m.message : '',
|
|
287
|
+
detail,
|
|
288
|
+
...(typeof m.sessionId === 'string' && m.sessionId.length > 0 ? { sessionId: m.sessionId } : {}),
|
|
289
|
+
...(typeof m.ts === 'number' && Number.isFinite(m.ts) ? { ts: m.ts } : {}),
|
|
290
|
+
...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
|
|
291
|
+
// durable 重放的第二层身份(SDK 从 SSE `id:` stamp 的 per-task 序号)—— 两个键两个命名空间,
|
|
292
|
+
// 见投影层同位的长注。消费端幂等序:eventId > eventSeq > (都缺才退) code+ts。
|
|
293
|
+
...(typeof m.eventSeq === 'string' && m.eventSeq.length > 0 ? { eventSeq: m.eventSeq } : {}),
|
|
294
|
+
});
|
|
295
|
+
};
|
|
257
296
|
const promptSuggestionsArm = function* (m) {
|
|
258
297
|
// T57 第五处断闸(#47 矩阵 #5 同族):子流(parentToolCallId 标)的建议绝不骑主 composer。
|
|
259
298
|
if (typeof m.parentToolCallId === 'string')
|
|
@@ -714,6 +753,7 @@ export const ARMS = new Map([
|
|
|
714
753
|
['steering_injected', steeringInjectedArm],
|
|
715
754
|
['workspace_changed', workspaceChangedArm],
|
|
716
755
|
['human_input', humanInputArm],
|
|
756
|
+
['engine_notice', engineNoticeArm],
|
|
717
757
|
['prompt_suggestions', promptSuggestionsArm],
|
|
718
758
|
['retry_status', retryStatusArm],
|
|
719
759
|
]);
|
package/dist/adapt.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface AdapterLedgerState {
|
|
|
50
50
|
/** 本批覆盖清单——差分守卫按它区分「已覆盖臂必须逐字段等价」与「已声明未覆盖臂」。 */
|
|
51
51
|
export declare const ADAPTER_COVERAGE: {
|
|
52
52
|
/** 已落码的帧臂。 */
|
|
53
|
-
readonly frames: readonly ["assistant", "user", "stream_event", "turn_usage", "result", "system", "task_notification", "workflow_complete", "diagnostics", "steering_injected", "workspace_changed", "human_input", "prompt_suggestions", "retry_status", "task_progress", "tool_end_result(label 补位 + 关卡 settle + response-id 复位 + 开卡台账出栈 + 经 cards.close 铸 tool_result)"];
|
|
53
|
+
readonly frames: readonly ["assistant", "user", "stream_event", "turn_usage", "result", "system", "task_notification", "workflow_complete", "diagnostics", "steering_injected", "workspace_changed", "human_input", "engine_notice", "prompt_suggestions", "retry_status", "task_progress", "tool_end_result(label 补位 + 关卡 settle + response-id 复位 + 开卡台账出栈 + 经 cards.close 铸 tool_result)"];
|
|
54
54
|
/** 已落码臂产出的 transcript 消息类目(差分守卫的比对域)。 */
|
|
55
55
|
readonly transcriptKinds: readonly ["assistant_text", "assistant_thinking", "assistant_tool_use", "user_tool_result_decision", "user_task_notification", "system_passthrough"];
|
|
56
56
|
/** B3(0.5.0)新落码的臂/半场 —— 从 todo 移过来的,别再在 todo 里留同名条目。 */
|
package/dist/adapt.js
CHANGED
|
@@ -33,6 +33,8 @@ export const ADAPTER_COVERAGE = {
|
|
|
33
33
|
'workspace_changed',
|
|
34
34
|
// FIX⑦(2026-08-07):`human_input` 账本帧 —— 投 chrome 记账,**不铸 transcript 行**(不带正文)。
|
|
35
35
|
'human_input',
|
|
36
|
+
// #318 件①(2026-08-21):`engine_notice` 引擎结构化通告 —— 投 chrome(开集,一个码都不判)。
|
|
37
|
+
'engine_notice',
|
|
36
38
|
'prompt_suggestions',
|
|
37
39
|
'retry_status',
|
|
38
40
|
'task_progress',
|
|
@@ -42,6 +42,8 @@ export const INTERNAL_SDK_ARM_TYPES = new Set([
|
|
|
42
42
|
'prompt_suggestions',
|
|
43
43
|
'human_input',
|
|
44
44
|
'turn_usage',
|
|
45
|
+
// #310 / #318 件①:引擎结构化通告的会话面(raw 预分派铸点,见 eventToSdkMessage 顶部)。
|
|
46
|
+
'engine_notice',
|
|
45
47
|
]);
|
|
46
48
|
/** Wrap neutral content blocks in the CC `assistant` message envelope. */
|
|
47
49
|
function assistantArm(ctx, content) {
|
|
@@ -87,6 +89,24 @@ function assertNeverArm(_ev) {
|
|
|
87
89
|
* 旧写法 `if (msg)` 在新返回型上恒真(对象永远 truthy),所以这是**必须点名**的一类改动。
|
|
88
90
|
*/
|
|
89
91
|
export function eventToSdkMessage(ev, ctx) {
|
|
92
|
+
// ── `engine_notice` raw 预分派(#310 / #318 件①,server ≥7.36,契约 = ASSISTANT-WIRE-CONTRACT 附录 D)──
|
|
93
|
+
//
|
|
94
|
+
// 🔴 **为什么是 raw 预分派而不是一条 `case`**(与 `workflow_complete` / `human_input` 当年同因):
|
|
95
|
+
// 本臂**还没进已发布 SDK 的 `AgentEvent` union**。sdk 仓 `3d6aebc` 确实加了它,但那个 commit
|
|
96
|
+
// **尚未出包** —— 亲验 npm `@sema-agent/sdk@7.2.0`(latest,2026-08-16 发布)的真 tarball:
|
|
97
|
+
// `dist/` 全树零 `engine_notice`(而同批的 `FleetTaskRow.cycleSeq` 在,证明抽检会说话)。
|
|
98
|
+
// 在这样的 union 上写 `case 'engine_notice'` 是编译错,所以先走预分派。
|
|
99
|
+
// 🔴 **这不是「按源码将就接」**(接入文档宪法):消费契约取自 server 的**已发布**接入档
|
|
100
|
+
// (ASSISTANT-WIRE-CONTRACT 附录 D,server 7.36+)与 openapi `Event_engine_notice`,不是抄 sdk src。
|
|
101
|
+
// 档与实装的失真已如实记账(附录 D.3 仍写「起步白名单三码」,而 server main 的白名单已是六码 ——
|
|
102
|
+
// `memory.hold_opened` / `hold_released` / `hold_disposed` 随 core 5.47/5.48 的 `NOTICE_AUDIENCE`
|
|
103
|
+
// 入册)。**本层对此完全免疫**:白名单是 server 的投递判定,本层按开集消费,一个码都不硬编。
|
|
104
|
+
// 🔴 **到期复核(自退休,不靠人记)**:预分派用 `(ev as {type?:unknown})` 形读判别键,**不收窄** `ev`
|
|
105
|
+
// ⇒ 臂一进 union,switch 的 `default` 仍看得见它,B5 穷举断言 `assertNeverArm` **编译期真红**,
|
|
106
|
+
// 逼下一棒把它搬进 switch。搬进去时行为一字不改(下面的投影函数原样复用)。
|
|
107
|
+
if (ev.type === 'engine_notice') {
|
|
108
|
+
return engineNoticeProjection(ev, ctx);
|
|
109
|
+
}
|
|
90
110
|
switch (ev.type) {
|
|
91
111
|
// ── `human_input`(core 5.14.0 design/171 / server 7.4.0 SSE,[3017]/[3020])────────────
|
|
92
112
|
// 🔴 **到期复核已兑现(sdk 6.9.0 提货,2026-08-08)**:本臂此前是 switch **之前**的一条 raw
|
|
@@ -548,6 +568,80 @@ export function eventToSdkMessage(ev, ctx) {
|
|
|
548
568
|
return dropped('unknown_arm', String(ev.type ?? 'unknown'));
|
|
549
569
|
}
|
|
550
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* `engine_notice` → 中性内部通告臂(#310 / #318 件①,契约 = server `ASSISTANT-WIRE-CONTRACT` 附录 D
|
|
573
|
+
* + openapi `Event_engine_notice`)。
|
|
574
|
+
*
|
|
575
|
+
* ── 这是什么 ────────────────────────────────────────────────────────────────────────────────
|
|
576
|
+
* 引擎的**结构化通告**里,被 server 判为面向**本会话终端用户**的那一小撮,按 `sessionId` 路由到
|
|
577
|
+
* 这条会话的流上。live 与 durable **两腿都有**(server 三条 run 腿都挂了口)⇒ 断连重连的重放里
|
|
578
|
+
* 会**再看到它**,与 `workspace_changed` 同一条**幂等消费**纪律(附录 D.1 逐字)。
|
|
579
|
+
*
|
|
580
|
+
* ── 🔴 开集三条(本函数的全部判据,逐条都是「不许做什么」)────────────────────────────────
|
|
581
|
+
* ① **按 `code` + `detail` 消费,`message` 只作 fallback 展示**。core 明写
|
|
582
|
+
* `memory.session_polluted` 的 message 随 `memoryProvenance` 模式变文 ⇒ 按 message 文本匹配
|
|
583
|
+
* **必碎**(5.41 合流码形退役同教训)。所以本层把三者**分别**上臂,绝不把 detail 折进文案。
|
|
584
|
+
* ② **`code` 是开集,认不得也绝不丢帧**。server 的白名单会随 core 码册增长(起步三码 →
|
|
585
|
+
* core 5.47/5.48 的 hold 三码入册,附录 D.3 的表尚未跟上)。本层因此**一个码都不硬编**:
|
|
586
|
+
* 没有识别表、没有 switch、没有「已知才投」——认不认得是**渲染面**的判断,不是投影面的门。
|
|
587
|
+
* ⇒ 本函数对 `code` 唯一的要求是「非空串」(判别键本身缺席才叫畸形)。
|
|
588
|
+
* ③ **能力位不当两值门**:本臂不读任何 caps、不问「引擎支不支持 engine_notice」。帧到了就投影
|
|
589
|
+
* (A-022「到帧即服务」先例)—— 拿一个探测位去 gate 一条**已经到手的事实**,只会在探测未判/
|
|
590
|
+
* 失败时把真事实丢掉。
|
|
591
|
+
*
|
|
592
|
+
* ── 🔴 消费端纪律(写在臂上,因为三端各写一遍必漂)────────────────────────────────────────
|
|
593
|
+
* · `detail` 已过 server 的 `redactSecrets` + 尺寸 bound(自由文本 1000 字符;深 4 / 键 32 /
|
|
594
|
+
* 数组 32),**仍按外部串处理**(呈现面字符处理走消费方自己的单源)。
|
|
595
|
+
* · `ts` 是 **server 观察时刻**(ms epoch),**不是**引擎铸造时刻 —— `EngineNotice` 自身不带时间戳。
|
|
596
|
+
* · 🔴 `memory.harvest_quarantined` 的 `moved` 与 `escalated` **不可相减**(就地墓碑同时计入两者,
|
|
597
|
+
* core 顶注):两个数各自读、并列呈现,任何减法都会得出一个**不存在的量**。本层原样透传 detail
|
|
598
|
+
* 正是为了让这条纪律只在渲染面兑现一次,而不是被投影层先算一个差值出来。
|
|
599
|
+
* · **本帧的缺席不代表「没发生」**:非白名单码 / 缺 `sessionId` 的通告 server **如实不投**
|
|
600
|
+
* (宁缺席不串台),全族那一份始终在 server 的结构化日志里(运维面)。所以消费端**绝不许**
|
|
601
|
+
* 从「没收到 engine_notice」反推「记忆姿态正常」。
|
|
602
|
+
*
|
|
603
|
+
* ── 畸形判据(fail-closed 方向)────────────────────────────────────────────────────────────
|
|
604
|
+
* `code` 非串/空串 ⇒ `malformed`(判别键都没有的通告,渲出去只是一行没有主语的噪声,而 dropped
|
|
605
|
+
* 至少会经 `reportDroppedFrame` 留痕)。其余四键**各自**按诚实缺席处理:`message` 非串 ⇒ 空串
|
|
606
|
+
* (fallback 位缺席,渲染面据此走纯 code 呈现)、`detail` 非对象 ⇒ 空对象(**不是**丢帧:通告的
|
|
607
|
+
* 承重物是 code,detail 坏了不该连带把「这件事发生过」一起吞掉)、`sessionId` / `ts` 非法 ⇒ 键不
|
|
608
|
+
* stamp(绝不铸 `0` 这种看起来合法的假读数)。
|
|
609
|
+
*/
|
|
610
|
+
function engineNoticeProjection(ev, ctx) {
|
|
611
|
+
const code = typeof ev.code === 'string' && ev.code.length > 0 ? ev.code : undefined;
|
|
612
|
+
if (code === undefined)
|
|
613
|
+
return dropped('malformed', 'engine_notice');
|
|
614
|
+
const sessionId = typeof ev.sessionId === 'string' && ev.sessionId.length > 0 ? ev.sessionId : undefined;
|
|
615
|
+
const ts = typeof ev.ts === 'number' && Number.isFinite(ev.ts) ? ev.ts : undefined;
|
|
616
|
+
const rawDetail = ev.detail;
|
|
617
|
+
const detail = typeof rawDetail === 'object' && rawDetail !== null && !Array.isArray(rawDetail)
|
|
618
|
+
? rawDetail
|
|
619
|
+
: {};
|
|
620
|
+
return projected(stamp(ctx, armBody({
|
|
621
|
+
type: 'engine_notice',
|
|
622
|
+
code,
|
|
623
|
+
/** 🔴 fallback 展示位,**不是匹配键**(见本函数顶注 ①)。 */
|
|
624
|
+
message: typeof ev.message === 'string' ? ev.message : '',
|
|
625
|
+
/** 🔴 原样透传(禁挑键):`detail` 逐码不同且是**开集**,白名单挑键 = 新码的事实在本层静默蒸发。 */
|
|
626
|
+
detail,
|
|
627
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
628
|
+
...(ts !== undefined ? { ts } : {}),
|
|
629
|
+
// ── 重放身份:两个键、**两个不同的命名空间**,谁都不许顶替谁 ──────────────────────────
|
|
630
|
+
// durable 腿按 `Last-Event-ID` 续读会重放同一条通告 ⇒ 消费端必须能幂等。可用的身份有两层:
|
|
631
|
+
// · `eventId` —— core 铸的稳定事件身份(uuidv7 形)。wire 今天未必带。
|
|
632
|
+
// · `eventSeq` —— **SDK 从 SSE `id:` 字段 stamp 上来的 durable 序号**(= `task_event.seq`,
|
|
633
|
+
// 见 sdk `dist/sse.js` 的 `ev.id = frame.id`;本包的规范访问口就是 `adapter/types.eventSeq`)。
|
|
634
|
+
// 它对「同一条账本行」是稳定的,重放会带同一个值。
|
|
635
|
+
// 🔴 codex 对抗复审 [medium] 采纳(2026-08-21):首版只带 `eventId`,于是 body 不带它时本臂
|
|
636
|
+
// 给消费端留的唯一去重口是 `code+ts` —— 而 `ts` 是**server 观察时刻(ms)**,同一毫秒里同码
|
|
637
|
+
// 的两条不同通告会被折成一条(真事实丢失),跨重连的同一条又可能因为观察时刻不同而重复。
|
|
638
|
+
// 明明有一个稳定序号在手却不带,是本层自己把可靠性降级了。
|
|
639
|
+
// 🔴 **绝不合并成一个键**([same-name-different-meaning-crosses-layers]):`eventId` 是引擎铸的
|
|
640
|
+
// 全局身份,`eventSeq` 是 per-task 的单调序号 —— 塞进同一个字段名会让消费端拿两种语义当一种用。
|
|
641
|
+
...(typeof ev.eventId === 'string' && ev.eventId.length > 0 ? { eventId: ev.eventId } : {}),
|
|
642
|
+
...(typeof ev.id === 'string' && ev.id.length > 0 ? { eventSeq: ev.id } : {}),
|
|
643
|
+
})));
|
|
644
|
+
}
|
|
551
645
|
/**
|
|
552
646
|
* `human_input` → 中性内部账本臂(见 `eventToSdkMessage` 顶部的 raw 预分派注释)。
|
|
553
647
|
*
|
|
@@ -34,7 +34,39 @@ export declare function kickEngineCapsProbe(baseUrl: string, probe: () => Promis
|
|
|
34
34
|
* 绝不 reject(探测失败=位维持未判,调用方按缺席降级)。
|
|
35
35
|
*/
|
|
36
36
|
export declare function engineCapsSettled(baseUrl: string | undefined): Promise<void>;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* 能力位的**可分辨读数**(#318 件③,cli [4752] 自领缺口2)——{@link engineCapTrue} 的三态化底座。
|
|
39
|
+
*
|
|
40
|
+
* 病:`engineCapTrue` 只回 `true`/`false`,于是 `false` 同时承载**四件互不相同**的事 ——
|
|
41
|
+
* 「引擎明说没有」「还没探」「探测在飞」「探测失败」。对**放行判据**而言这个塌缩是正确且刻意的
|
|
42
|
+
* (缺席一律 fail-closed 不渲 affordance,见 engineCapTrue 的头注),但对**自检/诊断面**(doctor)
|
|
43
|
+
* 它是致命的:doctor 要报的正是「这台引擎到底说了什么」,把「不知道」印成「没有」就是谎报。
|
|
44
|
+
* 这也是 doctor 此前无法复用本共享缓存、只能自己再探一遍的直接原因。
|
|
45
|
+
*
|
|
46
|
+
* 🔴 **闭集,且每个成员各自可行动**(A1 禁哨兵值双义):
|
|
47
|
+
* · `true` / `false` —— caps **已落地**且该键字面为该布尔值 = **引擎明说**;
|
|
48
|
+
* · `unprobed` —— 该 base 此刻没有已落地的 caps(没 kick / 在飞 / 探测失败 / 刚被
|
|
49
|
+
* {@link invalidateEngineCaps} 作废)。🔴 四种成因**故意合并**:它们对调用方是同一个动作 ——
|
|
50
|
+
* `await` 一次 {@link engineCapsSettled} 再读,或按「不知道」呈现。分开需要引擎没给的信息;
|
|
51
|
+
* · `absent` —— caps 已落地,但**没有这个键**:引擎比本包旧(还没这条车道)或比本包新(改名了);
|
|
52
|
+
* · `non_boolean` —— caps 已落地、键也在,但值不是布尔。这一档**不并进 `absent`**:那会是假话
|
|
53
|
+
* (键在),而它指向的是真问题(引擎申报了一个本口读不动的形 —— 比如把 caps 位写成了字符串),
|
|
54
|
+
* 诊断面该看见它。
|
|
55
|
+
*
|
|
56
|
+
* 🔴 **本读口不改变任何放行语义**:{@link engineCapTrue} 逐字等价于 `engineCapState(...) === 'true'`
|
|
57
|
+
* (下面就是这么实现的,单源)。放行面**继续**用 `engineCapTrue` —— 拿三态去开放行分支,等于把
|
|
58
|
+
* fail-closed 改成「按成因区别对待」,那是另一件事,不在本口的授权内。
|
|
59
|
+
*/
|
|
60
|
+
export type EngineCapState = 'true' | 'false' | 'unprobed' | 'absent' | 'non_boolean';
|
|
61
|
+
/**
|
|
62
|
+
* 同步读口(三态化):该 base 的 caps 里 key 的**可分辨**读数。语义逐条见 {@link EngineCapState}。
|
|
63
|
+
*
|
|
64
|
+
* `baseUrl` 缺席 ⇒ `unprobed`(没有 base 就没有任何一次探测,这不是「引擎说没有」)。
|
|
65
|
+
*/
|
|
66
|
+
export declare function engineCapState(baseUrl: string | undefined, key: string): EngineCapState;
|
|
67
|
+
/** 同步读口:该 base 的 caps 里 key 是否字面 true(未判/缺键 = false)。
|
|
68
|
+
* 🔴 **语义一字不变**(#318 件③ 三态化后改为经 {@link engineCapState} 单源实现):放行面读的就是
|
|
69
|
+
* 这一口,四种「不知道」继续一律折成 `false` = fail-closed。要分辨成因请读 {@link engineCapState}。 */
|
|
38
70
|
export declare function engineCapTrue(baseUrl: string | undefined, key: string): boolean;
|
|
39
71
|
/**
|
|
40
72
|
* 同步读口(字符串键):该 base 的 caps 里 key 的字符串值,未判/缺键/非字符串 ⇒ undefined。
|
package/dist/engineCapsCache.js
CHANGED
|
@@ -143,11 +143,33 @@ export async function engineCapsSettled(baseUrl) {
|
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
/**
|
|
147
|
-
|
|
146
|
+
/**
|
|
147
|
+
* 同步读口(三态化):该 base 的 caps 里 key 的**可分辨**读数。语义逐条见 {@link EngineCapState}。
|
|
148
|
+
*
|
|
149
|
+
* `baseUrl` 缺席 ⇒ `unprobed`(没有 base 就没有任何一次探测,这不是「引擎说没有」)。
|
|
150
|
+
*/
|
|
151
|
+
export function engineCapState(baseUrl, key) {
|
|
148
152
|
if (!baseUrl)
|
|
149
|
-
return
|
|
150
|
-
|
|
153
|
+
return 'unprobed';
|
|
154
|
+
const caps = capsByBase.get(baseUrl);
|
|
155
|
+
if (caps === undefined)
|
|
156
|
+
return 'unprobed';
|
|
157
|
+
// 🔴 `in` 而不是 `!== undefined`:引擎显式发 `{"foo": undefined}` 在 JSON 上不可能,但本表也接
|
|
158
|
+
// 宿主注入的对象;`in` 问的是「键在不在」,正是 `absent` 这一档要答的问题。
|
|
159
|
+
if (!(key in caps))
|
|
160
|
+
return 'absent';
|
|
161
|
+
const v = caps[key];
|
|
162
|
+
if (v === true)
|
|
163
|
+
return 'true';
|
|
164
|
+
if (v === false)
|
|
165
|
+
return 'false';
|
|
166
|
+
return 'non_boolean';
|
|
167
|
+
}
|
|
168
|
+
/** 同步读口:该 base 的 caps 里 key 是否字面 true(未判/缺键 = false)。
|
|
169
|
+
* 🔴 **语义一字不变**(#318 件③ 三态化后改为经 {@link engineCapState} 单源实现):放行面读的就是
|
|
170
|
+
* 这一口,四种「不知道」继续一律折成 `false` = fail-closed。要分辨成因请读 {@link engineCapState}。 */
|
|
171
|
+
export function engineCapTrue(baseUrl, key) {
|
|
172
|
+
return engineCapState(baseUrl, key) === 'true';
|
|
151
173
|
}
|
|
152
174
|
/**
|
|
153
175
|
* 同步读口(字符串键):该 base 的 caps 里 key 的字符串值,未判/缺键/非字符串 ⇒ undefined。
|
|
@@ -59,11 +59,31 @@ export declare const CONFIG_STRATEGY_FIND_LIMIT_INVALID = "config.strategy_find_
|
|
|
59
59
|
export declare const CONFIG_STRATEGY_MAX_SIZE_INVALID = "config.strategy_max_size_invalid";
|
|
60
60
|
/** 未知的 limits 键(5.8.0 起:同时发新旧两代键会在这里当场失败,所以写面只发单一新形)。 */
|
|
61
61
|
export declare const CONFIG_LIMIT_UNKNOWN_KEY = "config.limit_unknown_key";
|
|
62
|
+
/**
|
|
63
|
+
* `RunnerDeps.delegationEntryCaps`(core 5.48.0 / design/323,[4743] @cli 点名件)的坏值拒。
|
|
64
|
+
*
|
|
65
|
+
* 两种拒绝形:成员不是正整数(NaN / 0 / 负数 / 分数 / 非数),或**解出的一对**满足
|
|
66
|
+
* `maxConcurrent > maxCumulativePerSession`(一棵树不可能同时跑得比它这辈子能创建的还多 ——
|
|
67
|
+
* 这是矛盾不是偏好)。缺席成员取 CC 对齐缺省(20 / 200)。
|
|
68
|
+
* 🔴 与本族其余成员同律:**坏旋钮响亮拒,绝不静默折回缺省**。
|
|
69
|
+
*/
|
|
70
|
+
export declare const CONFIG_DELEGATION_ENTRY_CAPS = "config.delegation_entry_caps";
|
|
62
71
|
/**
|
|
63
72
|
* 已知的配置拒绝码(**识别表,非白名单**)。判「这是不是一条配置拒绝」请用
|
|
64
73
|
* {@link isConfigRefusalCode} —— 它按 `config.` 前缀判,未来新成员自动落进来。
|
|
65
74
|
*/
|
|
66
75
|
export declare const CONFIG_REFUSAL_CODES: ReadonlySet<string>;
|
|
76
|
+
/** 并发帽:这棵树此刻活着的委派席位已达 `delegationEntryCaps.maxConcurrent`(CC 对齐缺省 20)。
|
|
77
|
+
* 🔴 处置 = **可等**(兄弟结束即有位),别渲成「配置要改」。 */
|
|
78
|
+
export declare const DELEGATION_CONCURRENCY_CAP = "delegation.concurrency_cap";
|
|
79
|
+
/** 会话累计帽:这棵树累计创建的委派席位已达 `maxCumulativePerSession`(CC 对齐缺省 200)。
|
|
80
|
+
* 🔴 处置 = **等也没用**(配额是这条会话这辈子的),别渲成「稍后重试」。 */
|
|
81
|
+
export declare const DELEGATION_SESSION_CAP = "delegation.session_cap";
|
|
82
|
+
/** 委派席位到限码识别表(**开集**:core 可能再加第三根轴)。两员处置不对称,消费点禁合并分支。 */
|
|
83
|
+
export declare const DELEGATION_CAP_CODES: ReadonlySet<string>;
|
|
84
|
+
/** `delegation.` 前缀谓词 —— **开集**判别(与 {@link isConfigRefusalCode} 同款)。缺席/空串 ⇒ false。
|
|
85
|
+
* 🔴 它只回答「这是不是一条委派席位拒绝」;**该等还是该换会话**必须按成员分,见两码各自的注释。 */
|
|
86
|
+
export declare function isDelegationCapCode(code: string | undefined): boolean;
|
|
67
87
|
/** `config.` 前缀谓词 —— **开集**判别:5.10.0 之后每一波「不许静默折叠」都会往这一族加词,
|
|
68
88
|
* 按前缀判的消费点不必跟车,按成员判的必须跟车。缺席/空串 ⇒ false。 */
|
|
69
89
|
export declare function isConfigRefusalCode(code: string | undefined): boolean;
|
package/dist/engineErrorCodes.js
CHANGED
|
@@ -67,6 +67,15 @@ export const CONFIG_STRATEGY_FIND_LIMIT_INVALID = 'config.strategy_find_limit_in
|
|
|
67
67
|
export const CONFIG_STRATEGY_MAX_SIZE_INVALID = 'config.strategy_max_size_invalid';
|
|
68
68
|
/** 未知的 limits 键(5.8.0 起:同时发新旧两代键会在这里当场失败,所以写面只发单一新形)。 */
|
|
69
69
|
export const CONFIG_LIMIT_UNKNOWN_KEY = 'config.limit_unknown_key';
|
|
70
|
+
/**
|
|
71
|
+
* `RunnerDeps.delegationEntryCaps`(core 5.48.0 / design/323,[4743] @cli 点名件)的坏值拒。
|
|
72
|
+
*
|
|
73
|
+
* 两种拒绝形:成员不是正整数(NaN / 0 / 负数 / 分数 / 非数),或**解出的一对**满足
|
|
74
|
+
* `maxConcurrent > maxCumulativePerSession`(一棵树不可能同时跑得比它这辈子能创建的还多 ——
|
|
75
|
+
* 这是矛盾不是偏好)。缺席成员取 CC 对齐缺省(20 / 200)。
|
|
76
|
+
* 🔴 与本族其余成员同律:**坏旋钮响亮拒,绝不静默折回缺省**。
|
|
77
|
+
*/
|
|
78
|
+
export const CONFIG_DELEGATION_ENTRY_CAPS = 'config.delegation_entry_caps';
|
|
70
79
|
/**
|
|
71
80
|
* 已知的配置拒绝码(**识别表,非白名单**)。判「这是不是一条配置拒绝」请用
|
|
72
81
|
* {@link isConfigRefusalCode} —— 它按 `config.` 前缀判,未来新成员自动落进来。
|
|
@@ -76,7 +85,42 @@ export const CONFIG_REFUSAL_CODES = new Set([
|
|
|
76
85
|
CONFIG_STRATEGY_FIND_LIMIT_INVALID,
|
|
77
86
|
CONFIG_STRATEGY_MAX_SIZE_INVALID,
|
|
78
87
|
CONFIG_LIMIT_UNKNOWN_KEY,
|
|
88
|
+
CONFIG_DELEGATION_ENTRY_CAPS,
|
|
79
89
|
]);
|
|
90
|
+
// ── 委派席位到限族(core 5.48.0 design/323,[4743] @cli 点名的「两新 coded 拒绝」)──────────────
|
|
91
|
+
//
|
|
92
|
+
// 语义:一次 Task/SendMessage 委派因为**席位帽**被拒 —— 不是配置坏了(那是上面的
|
|
93
|
+
// `config.delegation_entry_caps`),也不是失败了,而是「现在不行」。两码**处置不同,禁合并**:
|
|
94
|
+
// · 并发帽 ⇒ **等**:活着的兄弟结束就有位,同一条命令过一会儿照样成;
|
|
95
|
+
// · 会话累计帽 ⇒ **等也没用**:这棵树这辈子的配额用完了,要么换会话要么抬 deps 旋钮。
|
|
96
|
+
// 把两者渲成同一句「委派失败」会让第一种情形的用户去改配置,第二种情形的用户去干等。
|
|
97
|
+
//
|
|
98
|
+
// 🔴 **载体如实登记(2026-08-21 亲验,别按 `tool_end.errorCode` 写消费码)**:core 把这两码铸进
|
|
99
|
+
// Task 工具结果体的 `details.error`(`subagent.ts` 的 `capRefusal`),而 core 的 `errorCode`
|
|
100
|
+
// 抬升腿(`runner/runtask.ts`)只读 `details.code` → `details.errorKind` **两个拼法**,
|
|
101
|
+
// `structuredFrom` 又要求 `details.type` 落在 `CC_DETAIL_TYPES` 里(这条 detail 连 `type` 都没有)
|
|
102
|
+
// ⇒ **今天这两码在 wire 上既不在 `tool_end.errorCode`、也不在 `structured`**,只剩模型面文案
|
|
103
|
+
// ("Sub-agent not started in background: …")。
|
|
104
|
+
// ⇒ 本词表**先立词、不落消费分支**:按文案反解正是本文件存在的理由要根除的东西
|
|
105
|
+
// ([cross-repo-fix-at-source-constitution]:载体缺口在 core,下游不许侧路补救)。
|
|
106
|
+
// 上游诉求已登记(见 CHANGELOG 0.38.0 段):core 补 `details.code` 孪生拼法(或抬升腿兼读
|
|
107
|
+
// `details.error`)之后,消费分支在本包同批接上,**判定归本包、文案归端**。
|
|
108
|
+
/** 并发帽:这棵树此刻活着的委派席位已达 `delegationEntryCaps.maxConcurrent`(CC 对齐缺省 20)。
|
|
109
|
+
* 🔴 处置 = **可等**(兄弟结束即有位),别渲成「配置要改」。 */
|
|
110
|
+
export const DELEGATION_CONCURRENCY_CAP = 'delegation.concurrency_cap';
|
|
111
|
+
/** 会话累计帽:这棵树累计创建的委派席位已达 `maxCumulativePerSession`(CC 对齐缺省 200)。
|
|
112
|
+
* 🔴 处置 = **等也没用**(配额是这条会话这辈子的),别渲成「稍后重试」。 */
|
|
113
|
+
export const DELEGATION_SESSION_CAP = 'delegation.session_cap';
|
|
114
|
+
/** 委派席位到限码识别表(**开集**:core 可能再加第三根轴)。两员处置不对称,消费点禁合并分支。 */
|
|
115
|
+
export const DELEGATION_CAP_CODES = new Set([
|
|
116
|
+
DELEGATION_CONCURRENCY_CAP,
|
|
117
|
+
DELEGATION_SESSION_CAP,
|
|
118
|
+
]);
|
|
119
|
+
/** `delegation.` 前缀谓词 —— **开集**判别(与 {@link isConfigRefusalCode} 同款)。缺席/空串 ⇒ false。
|
|
120
|
+
* 🔴 它只回答「这是不是一条委派席位拒绝」;**该等还是该换会话**必须按成员分,见两码各自的注释。 */
|
|
121
|
+
export function isDelegationCapCode(code) {
|
|
122
|
+
return typeof code === 'string' && code.startsWith('delegation.');
|
|
123
|
+
}
|
|
80
124
|
/** `config.` 前缀谓词 —— **开集**判别:5.10.0 之后每一波「不许静默折叠」都会往这一族加词,
|
|
81
125
|
* 按前缀判的消费点不必跟车,按成员判的必须跟车。缺席/空串 ⇒ false。 */
|
|
82
126
|
export function isConfigRefusalCode(code) {
|
|
@@ -93,6 +93,18 @@ export interface FleetTaskView {
|
|
|
93
93
|
path: string;
|
|
94
94
|
edits: number;
|
|
95
95
|
}>;
|
|
96
|
+
/**
|
|
97
|
+
* #261 §2① 代际号(0.38.0 提货补投;取值规则见 {@link wireCycleSeq})。
|
|
98
|
+
* 同 id 帧 `cycleSeq` 更大 ⇒ **复活**(新代际,行内累计量重置);更小 ⇒ 前代迟到帧,忽略;
|
|
99
|
+
* **缺席 ⇒ 这条行没有代际概念**(不是第一代)。
|
|
100
|
+
*/
|
|
101
|
+
cycleSeq?: number;
|
|
102
|
+
/**
|
|
103
|
+
* #261 §2② 非亲报终态的投影者(0.38.0 提货补投;取值规则见 {@link wireRetiredBy})。
|
|
104
|
+
* **在场 = 这条终态是对账腿从 durable run 行读出来的**(发布方死了),缺席 = 发布方亲报。
|
|
105
|
+
* 读侧开集,未知词通渲。
|
|
106
|
+
*/
|
|
107
|
+
retiredBy?: string;
|
|
96
108
|
}
|
|
97
109
|
/** 终态行帧 / bg 通知帧共用的 usage 形(SDK `FleetTaskRow.usage` ≡ `FleetBgNotification.usage`)。 */
|
|
98
110
|
export interface FleetRowUsage {
|
|
@@ -230,6 +242,28 @@ export declare function wireEditedFiles(r: Pick<FleetTaskRow, 'editedFiles'>): A
|
|
|
230
242
|
}> | undefined;
|
|
231
243
|
/** 终态四键之 `stoppedBy`(开放枚举 verbatim:"user"/"parent"/"system"/…)。 */
|
|
232
244
|
export declare function wireStoppedBy(r: Pick<FleetTaskRow, 'stoppedBy'>): string | undefined;
|
|
245
|
+
/**
|
|
246
|
+
* #261 §2① `cycleSeq`(server ≥7.25.0 / core 5.36.0 #258;SDK **7.2.0** 才把它声明进
|
|
247
|
+
* `FleetTaskRow` ⇒ 本包 0.38.0 提货补投)—— 这一行的**代际号**,与 `FleetBgNotification.seq` /
|
|
248
|
+
* `task_progress.seq` **同域同轴**(fresh spawn 就是 cycle 1,每次 SendMessage 复活翻 +1)。
|
|
249
|
+
*
|
|
250
|
+
* 🔴 **缺席 = 「无此概念」,不是「第一代」**(SDK 头注逐字):没有 `a*` registry 行的 run ——
|
|
251
|
+
* 同步委派子代 / workflow `wa*` agent / **顶层 run 行** —— 根本没有代际。把缺席读成 1 的消费端
|
|
252
|
+
* 会把「首帧迟到」误判成「复活」。所以这里对**非正整数**一律整键缺席(0 / 负数 / 非有限数 /
|
|
253
|
+
* 非整数都不是合法代际号),绝不 `?? 0`、绝不 `?? 1`。
|
|
254
|
+
*/
|
|
255
|
+
export declare function wireCycleSeq(r: Pick<FleetTaskRow, 'cycleSeq'>): number | undefined;
|
|
256
|
+
/**
|
|
257
|
+
* #261 §2② `retiredBy`(server ≥7.25.0;SDK 7.2.0 声明)—— 这一帧的终态**不是发布方亲报**,
|
|
258
|
+
* 而是对账腿从 durable run 行**投影**出来的(发布方死了,行本会永久僵在活跃集里当幽灵)。
|
|
259
|
+
*
|
|
260
|
+
* 🔴 **发布方亲报的终态帧恒不带此键** ⇒ 两种终态在 wire 上可判:要区分「引擎说它完了」与
|
|
261
|
+
* 「我们从库里读出来它完了」时,这是**唯一**的判据([ghost-rows-need-upstream-liveness] 的
|
|
262
|
+
* wire 侧对位物 —— 此前本层把这一位整个丢掉,两种终态在视图上同形)。
|
|
263
|
+
* service 侧今天是单词闭集(`"reconcile"`),**读侧开集**:未知词原样透传(将来的第二个投影者
|
|
264
|
+
* 会是一个新词而不是改义),消费端 branch 已知值 + 通渲兜底,**绝不**按成员判死。
|
|
265
|
+
*/
|
|
266
|
+
export declare function wireRetiredBy(r: Pick<FleetTaskRow, 'retiredBy'>): string | undefined;
|
|
233
267
|
/** 终态四键之 `resumable`(仅 bg 子代有源;非 boolean ⇒ 缺席,**不当 false**)。 */
|
|
234
268
|
export declare function wireResumable(r: Pick<FleetTaskRow, 'resumable'>): boolean | undefined;
|
|
235
269
|
export interface ProjectTasksOptions {
|