@sema-agent/client-core 0.14.0 → 0.16.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 +5 -3
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +38 -25
- package/dist/adapter/downstream/terminalToSdkResult.js +107 -58
- package/dist/adapter/runStream.js +12 -8
- package/dist/controlRouter.d.ts +11 -0
- package/dist/controlRouter.js +11 -0
- package/dist/detachWire.d.ts +6 -0
- package/dist/detachWire.js +6 -0
- package/dist/finalVerifyWire.d.ts +5 -0
- package/dist/finalVerifyWire.js +5 -0
- package/dist/fleet/fleetLedger.d.ts +9 -0
- package/dist/fleet/fleetLedger.js +10 -1
- package/dist/fleet/fleetProjection.d.ts +13 -1
- package/dist/fleet/fleetProjection.js +13 -1
- package/dist/fleetAgentPanelProjection.js +25 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +23 -0
- package/dist/interactiveToolsWire.d.ts +6 -1
- package/dist/interactiveToolsWire.js +6 -1
- package/dist/limitsWire.d.ts +77 -23
- package/dist/limitsWire.js +108 -35
- package/dist/model/catalogLoader.d.ts +170 -0
- package/dist/model/catalogLoader.js +382 -0
- package/dist/model/providerAuth.d.ts +155 -0
- package/dist/model/providerAuth.js +190 -0
- package/dist/model/providerPresets.d.ts +16 -0
- package/dist/notifications.d.ts +4 -0
- package/dist/notifications.js +134 -13
- package/dist/sandboxWire.d.ts +12 -1
- package/dist/sandboxWire.js +12 -1
- package/dist/scenarioWire.d.ts +12 -1
- package/dist/scenarioWire.js +12 -1
- package/dist/seatContract.js +25 -5
- package/dist/steering.js +11 -2
- package/dist/subagentContentStore.d.ts +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
23
23
|
|
|
24
24
|
## Scope
|
|
25
25
|
|
|
26
|
-
**Version:** 0.
|
|
26
|
+
**Version:** 0.16.0
|
|
27
27
|
|
|
28
28
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
29
29
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
|
@@ -53,7 +53,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
53
53
|
`SseIdleError`, `probeHealth`, `APIError` and `TaskStopConflictError` are imported as values in
|
|
54
54
|
five modules, and the browser bundle really bundles the SDK through (the portability guard would
|
|
55
55
|
exit 3 rather than quietly mark it external).
|
|
56
|
-
- The declared floor is `>=
|
|
56
|
+
- The declared floor is `>=4.1.0`, and it is *witnessed*: the guard checks that an actually
|
|
57
57
|
installed SDK at that line still exports every value-level symbol this package imports and still
|
|
58
58
|
declares `TaskStats.costMicroUsd` (the key `costOrNull` reads). A floor nobody ever ran is a
|
|
59
59
|
promise, not a contract.
|
|
@@ -220,18 +220,20 @@ public-surface guard checks that last one).
|
|
|
220
220
|
| `scripts/run-print-bash-iserror-test.mjs` | The print lane's Bash `is_error` authority (structured over regex) |
|
|
221
221
|
| `scripts/run-sdk-floor-test.mjs` | The SDK version floor — and, more to the point, that the *installed* type declarations still carry the keys this package reads |
|
|
222
222
|
| `scripts/run-engine-vocab-floor-test.mjs` | Engine-mirrored vocabularies (structured card whitelist, self-reported tool face, control verbs, recogniser sets) against the *installed* `@sema-agent/core` |
|
|
223
|
-
| `scripts/run-streamjson-timing-honesty-test.mjs` | Stream timing & terminal honesty ([2084]): held errored fs-write results release on model progress; wall-clock
|
|
223
|
+
| `scripts/run-streamjson-timing-honesty-test.mjs` | Stream timing & terminal honesty ([2084]): held errored fs-write results release on model progress; a wall-clock stop maps to `error_during_execution` with a truthful salvage note; the synthetic API-error assistant row carries the `<synthetic>` in-message sentinel. Also ([2489], core 5.8.0): the run-limit `errorCode` -> CC subtype map is pinned code by code (`limits.max_{cost,turns,tokens,walltime}_exceeded`), token/wall-clock stops keep the text the engine already produced, and the `failed` event arm shares the one mapping point. The 5.7 dual-vocabulary legs retired with server 6.0.0 (which bundles core 5.8.0); four **retirement negative controls** stand in their place — the retired `status:'timeout'` and the retired codes must fall to the honest fallback subtype and must never drop back to an empty success, so putting any of them back turns the gate red |
|
|
224
224
|
| `scripts/run-background-view-test.mjs` | `createBackgroundView` lifecycle: polling/notify pairing, per-source degrade (`501 → not-configured` vs `unavailable`), the capabilities `scheduler` probe, and dispose really aborting the in-flight fleet snapshot (pure projection lives in the pure suite's W-A segment) |
|
|
225
225
|
| `scripts/run-fleet-view-keys-test.mjs` | The fleet projection views, **both directions**: `FleetTaskView`/`FleetWorkflowView` ⇄ their key lists (compile-pinned) ⇄ what a maximal/minimal row really projects, plus a wire-key coverage ledger (every `FleetTaskRow` key is either projected or carries a written reason why not) and a drift ledger against the shell's render contract. A one-directional assignability check is blind to optional keys — which is how `startedAt` was silently dropped |
|
|
226
226
|
| `scripts/run-usage-verbatim-channel-test.mjs` | The two complementary usage disciplines (core 3.0.0 metering semantics): the CC `ModelUsage` mirror stays pure (five pinned keys, `totalInputTokens` has no seat), while the sema-owned channel forwards the engine `turn_end.usage` object **verbatim** (six keys, incl. `totalInputTokens`) via `last_turn_usage.engineUsage` / `handle.latestEngineUsage` — honest absence on pre-3.0.0 engines, no fabricated zeros |
|
|
227
227
|
| `scripts/run-plan-review-decide-verify-test.mjs` | `decidePlanReview`'s post-decide honesty ([2315]/[2316], engine RB-471 family): a 2xx from the decide endpoint is **not** a terminal — the wire re-pulls the task status and words the outcome by the real shape (still-locked / legal new gate / genuinely left park / unverified), never claiming success it hasn't earned. Driven against a real fake-engine HTTP server through the shipped dist |
|
|
228
228
|
| `scripts/run-shell-gate-durable-allow-test.mjs` | #110: the durable approval leg for **shell** gates. The tool_end HOLD/REJECT predicate must cover Bash the same way park detection already does (otherwise the park poison frame `Operation aborted` hits the transcript, `endedCalls` swallows the real replayed result, and the user who pressed Yes watches a command that really ran be reported as aborted); a replayed, already-decided park must resume reading the stream instead of being reported as a failed turn; `lastEventId` must track numeric `seq` too. Mutation-proven: each of the three fixes reverted turns the gate red |
|
|
229
229
|
| `scripts/run-hitl-gate-honesty-test.mjs` | [2393] the four HITL disciplines that a passing type-check cannot see. (1) The park predicate and the `tool_end` predicate must cover the **same** set — the park side admits a first-class `kind:'tool_approval'` gate for *any* tool name, and a `tool_end` frame carries no `kind`, so the frame-level judge falls back to the engine's exact abort marker; otherwise the poison frame hits the transcript and `markEnded` swallows the real replayed result (the #110 disease, reopened on kind-only gates). (2) The already-decided identity criterion is **one-shot**: its two inputs are monotonic, so without consumption one successful decide makes every later park failure — including a real `approvals.list` outage — read as "already resolved" until the 24-hop budget runs out and reports a cause that has nothing to do with what happened. (3) A `plan_review` card dismissed without an answer must be re-presentable: the idempotent re-arm short-circuit re-publishes the still-armed card, and a stale armed id (responder gone) re-arms from scratch rather than presenting a card nobody can answer. (4) `HitlSafetyError` is a safety signal — the `remember` fallback arm must re-raise it instead of auto-retrying the decide, while a plain unknown-key 400 still falls back. (5) The polling leg reschedules after an escaping throw and flips `mode()` to `idle` once it consistently fails, so the honesty surface stops reporting a dead feed as live |
|
|
230
|
+
| `scripts/run-notif-fleet-honesty-test.mjs` | [2393] the five notification/fleet disciplines a green type-check cannot see, each proven by reverting the fix. (1) The workflow-side dedup `return` keeps a count and a trace — without it "suppressed by design" and "a real completion swallowed because the runId minting changed" are the same observation. (2) `seq` normalisation has exactly one mint point, so a 0-based or fractional wire `seq` cannot make the watcher lane and the frame lane key the same completion differently (which would feed the model twice). (3) The TTL sweep defers to a probe arm that is still inside its own deadline — an entry recorded as "abandoned" must not be delivered a moment later — while an arm that has outlived its deadline never blocks the sweep, so the headless exit gate keeps its liveness. (4) The reset hook really clears every ledger it claims to (the sticky `prompt` ledger leaked across cases). (5) The fleet ledger counts all three drop paths (malformed / unknown frame type / isolation drop), and the panel projection's settled recycling is anchored on the settle instant and skips still-present rows, so the dedup token is never carried off with the entry (which would re-emit `end`) |
|
|
230
231
|
| `scripts/run-public-surface-test.mjs` | The outward promises: the npm export surface baseline (an **exact set**, both directions — a new export that never entered the baseline is one nobody watched leave, and deleting it later would not be red), the peer floor witness, and this README's claims |
|
|
231
232
|
| `scripts/run-client-core-message-branching-test.mjs` | §B8 (branching on error **text**) and §B10 (truthiness standing in for existence when the value can be `0`). AST + type-checker census over `src/`, a named ALLOW list carrying owner and expiry, a known-site floor, and two fixed corpora with a known verdict judged by the same classifier on every run |
|
|
232
233
|
| `scripts/run-client-core-failloud-test.mjs` | §C1/§C2: an empty `catch` with no comment anywhere inside it, a pure-swallow `catch` nobody reasoned about, and `void <write>` that really returns a Promise with no `.catch`. The exemption instrument is a comment saying why *this* failure may die; the documented-swallow count is a ratchet that only goes down |
|
|
233
234
|
| `scripts/run-client-core-typeshape-test.mjs` | Type discipline as a guard rather than a build side effect: the set of enabled strict knobs (one silently switched off is red), `tsc --noEmit`, and export-surface ratchets for inline anonymous shapes (≥3 members), `unknown` leaving the surface, and bare `unknown` returns — zero slack in either direction |
|
|
234
235
|
| `scripts/run-client-core-singleton-test.mjs` | Module-level singletons ⇄ `docs/refactor/p1-scan/singleton-manifest.json`, **both directions**: an unregistered singleton is red (registering it forces someone to answer "what if this got duplicated"), a stale entry is red, and the `dupRisk: high` count only goes down |
|
|
236
|
+
| `scripts/run-catalog-loader-gates-test.mjs` | The model-catalog candidate chain (`loadCatalogWithSources`) and the provider device-code seam: offline ⇒ `bundled` with an honest `online.reason`, a good source ⇒ `online` plus a cache write, a second offline run ⇒ `cacheHit`; the three hostile source shapes (malformed JSON, `schemaVersion: 99`, off-domain `http`) each fall through to the bundled table, and an off-allowlist target is **never dialled** — including a `302` to another host, proven by a real loopback server's hit counter staying at zero; a one-byte edit to `catalog.sha256` drops that source while an unavailable sidecar only warns; and the device-code poller's `pending → ok` / `expired` arms run against a real loopback HTTP server with an injected clock |
|
|
235
237
|
| `scripts/run-abortable-sleep-test.mjs` | The shared `abortableSleep(ms, signal)` leaf (consumed by `workflowClient.ts` and `agentSession/backgroundView.ts`'s poll backoff): normal timeout resolution, immediate wake-up on `abort` mid-wait, `clearTimeout` really firing on that path, and a post-resolve late abort staying a no-op |
|
|
236
238
|
|
|
237
239
|
Each suite carries a floor that only moves up — a refactor that stops executing a group of
|
|
@@ -21,35 +21,48 @@
|
|
|
21
21
|
* overlay 消费面(-p / 非 REPL)也走 SDKResultError 臂 —— 绝不投影成
|
|
22
22
|
* 空成功([884]A1,见下方分支注释)。
|
|
23
23
|
*
|
|
24
|
-
* ── 终态词表全枚举分流([909]B1,2026-07-16)
|
|
25
|
-
* 引擎终态 = core
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* subtype 词表(真源 pretty.js:765781-765786 zod enum)只有 4 词。对表:
|
|
24
|
+
* ── 终态词表全枚举分流([909]B1,2026-07-16;core 5.8.0 词表迁移 [2489],2026-08-04)──────────
|
|
25
|
+
* 引擎终态 = core TaskStatus 全集;done 帧 result.status 原样骑该词表(server runs.js
|
|
26
|
+
* append("done",{result:safe}) 透传)。此前只分流 failed/suspended/needs_review,timeout/blocked
|
|
27
|
+
* 落 success 臂 = TB 实锤「result 帧恒 success/is_error 恒 false,含 4 个引擎已记 timeout 的
|
|
28
|
+
* trial」。CC 2.1.207 SDKResultError subtype 词表(真源 pretty.js:765781-765786 zod enum)只有 4 词。
|
|
30
29
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
30
|
+
* ✅ **单代读面**(退役批,2026-08-04):core 5.8.0 是 BREAKING 大车(`status:'timeout'` 退役、
|
|
31
|
+
* 四个到限码全部改名),交付时点壳的引擎还捆 core 5.7,故读面曾同时认新旧两代,每处钉一个可 grep
|
|
32
|
+
* 的退役记号 + 一道逐文件逐次的等值登记门。**server 6.0.0 已捆 core 5.8.0 发车** ⇒ 窗口期关闭,
|
|
33
|
+
* 旧码腿(`budget.exceeded`/`budget.precall`/`limit.max_turns`)与 `status:'timeout'` 整臂本批删除
|
|
34
|
+
* (记号与登记门同批清空),读面与写面(limitsWire.ts)重新同为单一新形。旧形不再被认走这件事由
|
|
35
|
+
* `scripts/run-streamjson-timing-honesty-test.mjs` 的四条**退役负控**常驻看守(加回来就红)。
|
|
36
|
+
*
|
|
37
|
+
* TaskStatus errorCode → subtype is_error
|
|
38
|
+
* ────────────────────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
* completed — → 'success' false
|
|
40
|
+
* failed limits.max_cost_exceeded → 'error_max_budget_usd' true
|
|
41
|
+
* failed limits.max_turns_exceeded → 'error_max_turns' true
|
|
42
|
+
* failed limits.max_tokens_exceeded → 'error_during_execution' true
|
|
43
|
+
* (5.8.0 新增,且是**主限**。CC 4 词表里没有 token 预算词 —— error_max_budget_usd
|
|
44
|
+
* 是**美元**账不是 token 账,借用它会让消费者按成本口径读一个 token 口径的停因。
|
|
45
|
+
* ⇒ 诚实兜底词 + errorCode 超集透传辨认;[2489] 明记该终帧「result 带已产出正文」
|
|
46
|
+
* ⇒ 走 salvage 腿,正文不丢。)
|
|
47
|
+
* failed limits.max_walltime_exceeded → 'error_during_execution' true
|
|
48
|
+
* (5.8.0 起墙钟到限的**唯一**形;5.7 的 `status:'timeout'` + `limit.timeout`
|
|
49
|
+
* 是同一个停因的上一代形,server 6.0.0 捆 core 5.8.0 后已随本批整臂删除。
|
|
50
|
+
* 选型理由留档:CC 无 wall-clock deadline 概念。曾选 error_max_turns「唯一
|
|
51
|
+
* run-limit 族词」,[2084]②a 定谳那是帧内自相矛盾(errorCode 是墙钟码而
|
|
52
|
+
* num_turns 远小于上限)且对墙钟停指错旋钮(max-turns 是可恢复失败)⇒ 改诚实
|
|
53
|
+
* 兜底词。真实语义靠 errors[] 文案 + errorCode 透传辨认,不变。)
|
|
54
|
+
* failed output.invalid → 'error_max_structured_output_retries' true
|
|
55
|
+
* failed 其余(含 cancelled/conflict…) → 'error_during_execution' true
|
|
56
|
+
* blocked —(engine 只给 blockedReason) → 'error_during_execution' true
|
|
57
|
+
* suspended — → 'error_during_execution' true([884]A1)
|
|
58
|
+
* needs_review review.pending → 'error_during_execution' true([884]A1)
|
|
59
|
+
* 〈未知新词〉 — → 'error_during_execution' true(防御:
|
|
47
60
|
* 非 completed 的终态一律≠空成功;errors[] 里如实报出该 status 字面)
|
|
48
61
|
*
|
|
49
62
|
* errorCode 透传:引擎 errorCode 骑 additive seam 字段 `errorCode`(CC schema 无此字段——
|
|
50
|
-
* 与 success 臂的 `model` 同款超集姿势),集成面可精确分流(
|
|
51
|
-
*
|
|
52
|
-
* result,但把引擎已救回的最终文本静默丢弃对 -p 消费者是净损失。
|
|
63
|
+
* 与 success 臂的 `model` 同款超集姿势),集成面可精确分流(limits.max_tokens_exceeded vs
|
|
64
|
+
* cancelled …)。截断 salvage:写出窗兑现的 r.result(非空)骑 additive `result` 字段——CC error
|
|
65
|
+
* 帧无 result,但把引擎已救回的最终文本静默丢弃对 -p 消费者是净损失。
|
|
53
66
|
*
|
|
54
67
|
* 成本取数 = `TaskStats.costMicroUsd`(整数 micro-USD 单轨,SDK 1.0.0 / server 1.319.0 /
|
|
55
68
|
* core 2.0.0 起;legacy float `costUsd` 已删,见 costOrNull 头注)。零成本 is AMBIGUOUS
|
|
@@ -129,6 +129,76 @@ function degradedOf(r) {
|
|
|
129
129
|
const d = r.degraded;
|
|
130
130
|
return d !== undefined && d !== null && typeof d === 'object' ? d : undefined;
|
|
131
131
|
}
|
|
132
|
+
// ── 到限码词表(core 5.8.0 [2489] 新码单代)──────────────────────────────────────────────────
|
|
133
|
+
// 退役批(server 6.0.0 捆 core 5.8.0 发车,2026-08-04):三个集合里的 5.7 旧码成员
|
|
134
|
+
// (`budget.exceeded`/`budget.precall`/`limit.max_turns`)已删,只留新码。放成集合而不是就地 `||`
|
|
135
|
+
// 链的原因不变:词表变动集中在一处可数,不必满文件找 `===` 比较。
|
|
136
|
+
/** 成本到限 → CC `error_max_budget_usd`(唯一一个 CC 有专词的预算轴)。 */
|
|
137
|
+
const COST_EXCEEDED_CODES = new Set([
|
|
138
|
+
'limits.max_cost_exceeded',
|
|
139
|
+
]);
|
|
140
|
+
/** 轮数到限 → CC `error_max_turns`。 */
|
|
141
|
+
const TURNS_EXCEEDED_CODES = new Set([
|
|
142
|
+
'limits.max_turns_exceeded',
|
|
143
|
+
]);
|
|
144
|
+
/**
|
|
145
|
+
* 「截断」族到限码:CC 4 词表里**没有**对应词(token 账不是美元账、墙钟停不是轮数耗尽),
|
|
146
|
+
* 所以 subtype 落诚实兜底词,真实语义靠 errorCode 透传;且这两种停因都可能已产出正文
|
|
147
|
+
* ([2489] 对 max_tokens 明记 result 带正文;墙钟停是既有的写出窗 salvage 语义)⇒ 共用 salvage 腿。
|
|
148
|
+
*/
|
|
149
|
+
const CUTOFF_EXCEEDED_CODES = new Set([
|
|
150
|
+
'limits.max_tokens_exceeded',
|
|
151
|
+
'limits.max_walltime_exceeded',
|
|
152
|
+
]);
|
|
153
|
+
/**
|
|
154
|
+
* 🔴 **errorCode → CC subtype 的唯一映射点**。收编前这段 `||`/三元链在 done{failed} 与
|
|
155
|
+
* `failedToSdkResult` 两处各写一份,而且两份**并不同义**(事件臂只认 budget.*,连 limit.max_turns
|
|
156
|
+
* 都不认 ⇒ 同一个「轮数耗尽」经 done 帧来是 error_max_turns、经 failed 事件来是
|
|
157
|
+
* error_during_execution)。5.8.0 的词表迁移要在两处各改一遍正是这类漏配的温床,故先收成单点。
|
|
158
|
+
*/
|
|
159
|
+
function subtypeForErrorCode(code) {
|
|
160
|
+
if (code === undefined || code.length === 0)
|
|
161
|
+
return 'error_during_execution';
|
|
162
|
+
if (COST_EXCEEDED_CODES.has(code))
|
|
163
|
+
return 'error_max_budget_usd';
|
|
164
|
+
if (TURNS_EXCEEDED_CODES.has(code))
|
|
165
|
+
return 'error_max_turns';
|
|
166
|
+
// assemble-result.js:58 该 code 的语义字面就是 CC 这词:「did not produce a valid structured
|
|
167
|
+
// output within the retry limit」。
|
|
168
|
+
if (code === 'output.invalid')
|
|
169
|
+
return 'error_max_structured_output_retries';
|
|
170
|
+
return 'error_during_execution';
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 写出窗抢救文本:引擎已兑现的最终正文(`result` 优先,回落 `salvagedOutput`)。空白串当缺席
|
|
174
|
+
* —— 空串骑上 `result` 键就是「冒充有产出」。
|
|
175
|
+
*/
|
|
176
|
+
function salvagedTextOf(r) {
|
|
177
|
+
const salvagedOutput = r.salvagedOutput;
|
|
178
|
+
return typeof r.result === 'string' && r.result.trim().length > 0
|
|
179
|
+
? r.result
|
|
180
|
+
: typeof salvagedOutput === 'string' && salvagedOutput.trim().length > 0
|
|
181
|
+
? salvagedOutput
|
|
182
|
+
: undefined;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 截断臂(墙钟/ token 到限)的公共两位:errors[0] + 可选 salvage。
|
|
186
|
+
*
|
|
187
|
+
* [2084]②b:引擎的 cutoff 句承诺「cut off to preserve a write-out window」,但 pre-call throw 路径上
|
|
188
|
+
* 写出窗可能零兑现(core dist/brain/stream-engine 五处 throw,载体天生空)。抢救为空时补诚实尾注 ——
|
|
189
|
+
* 每一层都复述没兑现的承诺,消费者就无从分辨救没救回来。
|
|
190
|
+
*/
|
|
191
|
+
function cutoffParts(r, message, fallbackMessage, code) {
|
|
192
|
+
const salvaged = salvagedTextOf(r);
|
|
193
|
+
return {
|
|
194
|
+
errors: [
|
|
195
|
+
`${message ?? fallbackMessage} (${code})${salvaged === undefined ? ' — no output survived the cutoff' : ''}`,
|
|
196
|
+
],
|
|
197
|
+
// 写出窗 salvage(additive seam 字段):非空时随帧带出 —— CC error 帧无 result 键,但静默丢弃
|
|
198
|
+
// 已救回的答案对 -p 集成面是净损失(TB qe** 实证)。
|
|
199
|
+
...(salvaged !== undefined ? { salvagedResult: salvaged } : {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
132
202
|
/**
|
|
133
203
|
* 🔴 **CC `SDKResultError` 信封的唯一 mint 点**(REF-CC-055 / xlate-01,E1 单源构造)。
|
|
134
204
|
*
|
|
@@ -186,7 +256,7 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
186
256
|
if (typeof echoedSid === 'string' && echoedSid.length > 0)
|
|
187
257
|
ctx.sessionId = echoedSid;
|
|
188
258
|
// P1-5 — modelUsage/usage 的构造已收敛到 `flattenUsage` / `modelUsageFor`(REF-CC-055/062),
|
|
189
|
-
//
|
|
259
|
+
// 四个 error 臂经 `errorResult` 信封拿到同一份,success 臂在下方直接调 —— 同一输入同一输出。
|
|
190
260
|
// clay P0 壳半场(黑板 [450],2026-07-05):上游 4xx(如无 vision 模型收到 image)时
|
|
191
261
|
// 引擎的 done 帧本体带 result.status='failed' + errorMessage —— 传播链没吞,断点在这:
|
|
192
262
|
// 此前无条件投影 subtype:'success'/is_error:false,把失败洗成"成功空回复"= 用户看到的
|
|
@@ -194,10 +264,11 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
194
264
|
// TS2367 note: `TaskResult & { status?: string }` does NOT actually widen `status` — TS
|
|
195
265
|
// intersects the pre-existing required `RunStatus` with the added optional `string`, and
|
|
196
266
|
// `RunStatus & string` stays `RunStatus` (the narrower member wins). That's exactly why the
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
267
|
+
// status branches below (the SDK's `RunStatus` is a CLOSED union, while core's TaskStatus vocab
|
|
268
|
+
// moves on its own release train — see file header) used to type-error as "no overlap": the cast
|
|
269
|
+
// looked like a widening but wasn't one. `Omit` the narrow key first so the added wider
|
|
270
|
+
// `status?: string` actually applies — that is also what keeps the 未知终态 defensive arm below
|
|
271
|
+
// expressible at all.
|
|
201
272
|
const rr = r;
|
|
202
273
|
const durationMs = elapsedMs(ctx);
|
|
203
274
|
// [909]B1 — errorCode 透传(additive seam 字段;CC schema 无此键,与 success 臂 `model` 同款超集
|
|
@@ -206,24 +277,27 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
206
277
|
// TaskResult 五键最小消费批([1543] SDK-M,2026-07-23):additive seam 字段——
|
|
207
278
|
// · degraded = 模型降级链实录,**各终态恒 additive 附上**(见 `degradedOf` 头注 / REF-CC-056);
|
|
208
279
|
// · structuredOutput = outputSchema 任务的结构化产出(success 臂);
|
|
209
|
-
// · salvagedOutput = degenerate
|
|
280
|
+
// · salvagedOutput = degenerate/到限截断抢救出的最后模型文本(`cutoffParts` 的 salvage 回落源:
|
|
281
|
+
// failed + limits.max_{tokens,walltime}_exceeded 两族共用)。
|
|
210
282
|
const degraded = degradedOf(r);
|
|
211
|
-
/**
|
|
283
|
+
/** 四个 error 臂共享的固定位(信封其余 13 位见 `errorResult`)。 */
|
|
212
284
|
const errorBase = { durationMs, stats, model: r.model, errorCode, degraded };
|
|
213
285
|
if (rr.status === 'failed') {
|
|
214
|
-
// [909]B1 — failed 臂 subtype 语义化(见文件头对表)
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
? '
|
|
226
|
-
: '
|
|
286
|
+
// [909]B1 — failed 臂 subtype 语义化(见文件头对表);core 5.8.0([2489])起到限码全部改名,
|
|
287
|
+
// 映射本身已收进单点 `subtypeForErrorCode`(退役批后只认新码)。
|
|
288
|
+
// 5.8.0 的「截断」族到限(token/墙钟):subtype 只能落诚实兜底词,但这两种停因**可能已产出正文**
|
|
289
|
+
// —— [2489] 对 limits.max_tokens_exceeded 明记「result 带已产出正文」,墙钟停是既有写出窗语义,
|
|
290
|
+
// 两族共用 `cutoffParts`(salvage + 零抢救诚实尾注)。
|
|
291
|
+
const failedSubtype = subtypeForErrorCode(rr.errorCode);
|
|
292
|
+
if (rr.errorCode !== undefined && CUTOFF_EXCEEDED_CODES.has(rr.errorCode)) {
|
|
293
|
+
return errorResult(ctx, {
|
|
294
|
+
...errorBase,
|
|
295
|
+
subtype: failedSubtype,
|
|
296
|
+
...cutoffParts(r, rr.errorMessage, rr.errorCode === 'limits.max_tokens_exceeded'
|
|
297
|
+
? 'run exhausted its token budget'
|
|
298
|
+
: 'run hit its wall-clock limit', rr.errorCode),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
227
301
|
return errorResult(ctx, {
|
|
228
302
|
...errorBase,
|
|
229
303
|
subtype: failedSubtype,
|
|
@@ -239,40 +313,12 @@ export function doneToSdkResult(ev, ctx) {
|
|
|
239
313
|
],
|
|
240
314
|
});
|
|
241
315
|
}
|
|
242
|
-
//
|
|
243
|
-
// errorCode
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
if (rr.status === 'timeout') {
|
|
249
|
-
const salvagedOutput = r.salvagedOutput;
|
|
250
|
-
const salvaged = typeof r.result === 'string' && r.result.trim().length > 0
|
|
251
|
-
? r.result
|
|
252
|
-
: typeof salvagedOutput === 'string' && salvagedOutput.trim().length > 0
|
|
253
|
-
? salvagedOutput
|
|
254
|
-
: undefined;
|
|
255
|
-
return errorResult(ctx, {
|
|
256
|
-
...errorBase,
|
|
257
|
-
// [2084]②a(2026-07-30,repro/cli-walltime-timeout-reported-as-max-turns):此前硬编码
|
|
258
|
-
// error_max_turns(选型理由是「CC 词表唯一 run-limit 族词」),但那让同一帧自相矛盾
|
|
259
|
-
// (errorCode=limit.timeout + num_turns 远小于上限,test 实测 num_turns:2 且根本没设
|
|
260
|
-
// --max-turns),而且 max-turns 是**可恢复失败**——标准应对是调大 --max-turns 重试,对
|
|
261
|
-
// 墙钟停这恰好是那个帮不上忙的旋钮。改 error_during_execution(CC 词表内的诚实兜底);
|
|
262
|
-
// 真实语义仍靠 errorCode:"limit.timeout" + errors[] 文案判别(原设计轴不动)。
|
|
263
|
-
// 副产品:真 limit.max_turns 停(failed 臂)与墙钟停在 subtype 上重新可分。
|
|
264
|
-
subtype: 'error_during_execution',
|
|
265
|
-
// [2084]②b:引擎的 cutoff 句承诺「cut off to preserve a write-out window」,但 pre-call
|
|
266
|
-
// throw 路径上写出窗可能零兑现(core dist/brain/stream-engine 五处 throw,载体天生空)。
|
|
267
|
-
// 抢救为空时补诚实尾注——每一层都复述没兑现的承诺,消费者就无从分辨救没救回来。
|
|
268
|
-
errors: [
|
|
269
|
-
`${rr.errorMessage ?? 'run hit its wall-clock deadline and was finalized by the engine'} (${rr.errorCode ?? 'limit.timeout'})${salvaged === undefined ? ' — no output survived the cutoff' : ''}`,
|
|
270
|
-
],
|
|
271
|
-
// 写出窗 salvage(additive seam 字段):deadline 前引擎救回的最终文本非空时随帧带出——
|
|
272
|
-
// CC error 帧无 result 键,但静默丢弃已救回的答案对 -p 集成面是净损失(TB qe** 实证)。
|
|
273
|
-
...(salvaged !== undefined ? { salvagedResult: salvaged } : {}),
|
|
274
|
-
});
|
|
275
|
-
}
|
|
316
|
+
// 留档(退役批,2026-08-04):此处曾有一条 `rr.status === 'timeout'` 的窗口期臂 —— 5.7 的墙钟
|
|
317
|
+
// 停是 `status:'timeout'` + `errorCode:'limit.timeout'`([909]B1;此前落 success 臂 = TB 的
|
|
318
|
+
// 「4 个引擎已记 timeout 的 trial 全 subtype:success」)。core 5.8.0 把这个终态词整体退役,墙钟停
|
|
319
|
+
// 改走上面的 failed + `limits.max_walltime_exceeded`(同处置,共用 `cutoffParts`);server 6.0.0
|
|
320
|
+
// 捆 core 5.8.0 发车后旧形不再上 wire,本臂随之删除。未知终态(含万一漏网的 'timeout')不会掉回
|
|
321
|
+
// 空成功 —— 下方兜底臂把任何非 completed 终态都投影成 SDKResultError。
|
|
276
322
|
// [909]B1 — blocked 终态:agent 自报无法推进(assemble-result.js:99,只带 blockedReason 无
|
|
277
323
|
// errorCode)。此前落 success 臂(TB db-wal-recovery__uuH5cAg 实证:引擎终态 blocked、帧却
|
|
278
324
|
// subtype:success)。≠空成功 → SDKResultError,blockedReason 如实进 errors[]。
|
|
@@ -370,9 +416,12 @@ export function failedToSdkResult(ev, ctx) {
|
|
|
370
416
|
// Flatten the 4 CC error subtypes onto the single neutral errorCode.
|
|
371
417
|
// error_max_budget_usd is a non-error "budget exceeded" notice, not a crash
|
|
372
418
|
// (contract 02 §2.11) — the renderer branches on subtype.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
419
|
+
//
|
|
420
|
+
// 🔴 单点(core 5.8.0 [2489]):此臂此前手写「只认 budget.*」的两分支,与 done{failed} 臂
|
|
421
|
+
// 的四分支**并不同义**(同一个轮数耗尽经 done 帧来是 error_max_turns、经 failed 事件来是
|
|
422
|
+
// error_during_execution)。现在两条路共用 `subtypeForErrorCode` —— 到限码与 CC 词表的对应关系
|
|
423
|
+
// 只有一处定义,5.8.0 的改名、以及本批的旧码退役,都只改了一处。
|
|
424
|
+
const subtype = subtypeForErrorCode(ev.errorCode);
|
|
376
425
|
// 🔴 REF-CC-055:此臂**不是另一种信封**,是 `errorResult` 在 `stats === undefined` 时的退化实例
|
|
377
426
|
// (`num_turns: 0` ≡ `stats?.turns ?? 0`、`total_cost_usd: null` ≡ `costOrNull(undefined)`、
|
|
378
427
|
// 五键零 usage ≡ `flattenUsage(undefined)`、`modelUsage: {}` ≡ `modelUsageFor(undefined, undefined)`)。
|
|
@@ -381,7 +430,7 @@ export function failedToSdkResult(ev, ctx) {
|
|
|
381
430
|
subtype,
|
|
382
431
|
durationMs: elapsedMs(ctx),
|
|
383
432
|
// [909]B1 — errorCode 透传(additive seam 字段;done 臂同款):`failed` 事件的 cancelled/
|
|
384
|
-
//
|
|
433
|
+
// limits.* 等引擎值原样给集成面。
|
|
385
434
|
errorCode: ev.errorCode,
|
|
386
435
|
// errorCode + errorMessage flattened into the single CC errors[] string.
|
|
387
436
|
// Rewind-family codes(core 1.292 [833]:resume_at.before_target_not_user /
|
|
@@ -284,20 +284,24 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
284
284
|
// 额外合成一条 assistant 错误消息(CC 的 isApiErrorMessage 红字渲染线现成),
|
|
285
285
|
// 用户永远能看到 turn 为什么失败。
|
|
286
286
|
const failedResult = ev.type === 'done' ? ev.result : null;
|
|
287
|
-
// [909]B1 —
|
|
287
|
+
// [909]B1 — failed/blocked 终态同享可见性行(此前只 status:'failed' 有行,墙钟停/blocked
|
|
288
288
|
// 在 REPL 面完全静默)。park(suspended/needs_review)不进此行:有各自 overlay/文案线,
|
|
289
289
|
// 非 provider 故障([884]A1 断言零 assistant 行)。
|
|
290
|
+
//
|
|
291
|
+
// 留档(退役批,2026-08-04):此处曾有第三腿 `status === 'timeout'`(5.7 的墙钟停形)。
|
|
292
|
+
// core 5.8.0([2489])把该终态词退役 —— 墙钟到限改成 `status:'failed'` +
|
|
293
|
+
// `errorCode:'limits.max_walltime_exceeded'`,而 failed 腿本来就在(errorMessage ?? errorCode
|
|
294
|
+
// ?? 'run failed'),新形的可见性**已经**由它覆盖;server 6.0.0 捆 core 5.8.0 发车后旧形不再
|
|
295
|
+
// 上 wire,该腿随之删除,不留静默缺口。
|
|
290
296
|
const errText = ev.type === 'failed'
|
|
291
297
|
? (ev.errorMessage ?? ev.errorCode ?? 'run failed')
|
|
292
298
|
: failedResult?.status === 'failed'
|
|
293
299
|
? (failedResult.errorMessage ?? failedResult.errorCode ?? 'run failed')
|
|
294
|
-
: failedResult?.status === '
|
|
295
|
-
? (failedResult.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
: 'Run blocked (the agent reported it cannot proceed)')
|
|
300
|
-
: null;
|
|
300
|
+
: failedResult?.status === 'blocked'
|
|
301
|
+
? (failedResult.blockedReason
|
|
302
|
+
? `Run blocked: ${failedResult.blockedReason}`
|
|
303
|
+
: 'Run blocked (the agent reported it cannot proceed)')
|
|
304
|
+
: null;
|
|
301
305
|
if (errText) {
|
|
302
306
|
// 件2b(中断事故修复批 G,2026-07-15,症状2 第二环):409 active-run 拒绝的专属人话。引擎在
|
|
303
307
|
// session 已有挂起 run(上一 turn 的 AskUserQuestion park 未解锁等)时把
|
package/dist/controlRouter.d.ts
CHANGED
|
@@ -208,5 +208,16 @@ export interface ErrCodes {
|
|
|
208
208
|
* REF-CC-135(TYPESHAPE-15,P3 wave1 卡C-argv):判型单源此前返回 3 个裸 unknown(B2/B4 违规——调用方
|
|
209
209
|
* `code === 404`(status 才是数字位)这类拼写型 bug 能编译期全绿恒 false)。窄化成具名 `ErrCodes`,
|
|
210
210
|
* 非匹配类型一律降到 undefined(而不是把垃圾值原样放行)。
|
|
211
|
+
*
|
|
212
|
+
* 🔴 [2393] sweep-F6(2026-08-03 全窗复审)**这不只是类型收窄,是运行期丢值**(裁定:保留行为,
|
|
213
|
+
* 但把它记成可核的承诺):旧实现 `return { code: o.errorCode, name: o.name, status: o.status ?? o.statusCode }`
|
|
214
|
+
* 是原样透传。今天,包外消费者若拿到的错误对象上 `status` 是数字**串**(某些传输层/代理把 status
|
|
215
|
+
* 序列化成字符串)、或 `errorCode` 是数值码,读到的一律是 `undefined`。
|
|
216
|
+
* 方向对(「拿到一个不好用的值」→「诚实地什么都没有」;下游 `code === 404` 那类拼写型 bug 也因此
|
|
217
|
+
* 编译期就红),但它是**公面行为变更**:本函数是 npm 导出的判型单源,三端各自的错误对象形需要复核。
|
|
218
|
+
* ⇒ 已进 `docs/refactor/README.md` 的提货单/BREAKING 清单(此前只列四条 wire BREAKING,签名级与
|
|
219
|
+
* 行为级的这几条靠人工列表兜底,而列表没列)。
|
|
220
|
+
* 判据:pure 门 B5 段那组钉现在也钉「脏形一律降 undefined、绝不悄悄回落成 0/空串」——
|
|
221
|
+
* 谁要把某一种脏形改回透传,必须先在那里显形。
|
|
211
222
|
*/
|
|
212
223
|
export declare function errCodes(e: unknown): ErrCodes;
|
package/dist/controlRouter.js
CHANGED
|
@@ -212,6 +212,17 @@ export class ControlRouter {
|
|
|
212
212
|
* REF-CC-135(TYPESHAPE-15,P3 wave1 卡C-argv):判型单源此前返回 3 个裸 unknown(B2/B4 违规——调用方
|
|
213
213
|
* `code === 404`(status 才是数字位)这类拼写型 bug 能编译期全绿恒 false)。窄化成具名 `ErrCodes`,
|
|
214
214
|
* 非匹配类型一律降到 undefined(而不是把垃圾值原样放行)。
|
|
215
|
+
*
|
|
216
|
+
* 🔴 [2393] sweep-F6(2026-08-03 全窗复审)**这不只是类型收窄,是运行期丢值**(裁定:保留行为,
|
|
217
|
+
* 但把它记成可核的承诺):旧实现 `return { code: o.errorCode, name: o.name, status: o.status ?? o.statusCode }`
|
|
218
|
+
* 是原样透传。今天,包外消费者若拿到的错误对象上 `status` 是数字**串**(某些传输层/代理把 status
|
|
219
|
+
* 序列化成字符串)、或 `errorCode` 是数值码,读到的一律是 `undefined`。
|
|
220
|
+
* 方向对(「拿到一个不好用的值」→「诚实地什么都没有」;下游 `code === 404` 那类拼写型 bug 也因此
|
|
221
|
+
* 编译期就红),但它是**公面行为变更**:本函数是 npm 导出的判型单源,三端各自的错误对象形需要复核。
|
|
222
|
+
* ⇒ 已进 `docs/refactor/README.md` 的提货单/BREAKING 清单(此前只列四条 wire BREAKING,签名级与
|
|
223
|
+
* 行为级的这几条靠人工列表兜底,而列表没列)。
|
|
224
|
+
* 判据:pure 门 B5 段那组钉现在也钉「脏形一律降 undefined、绝不悄悄回落成 0/空串」——
|
|
225
|
+
* 谁要把某一种脏形改回透传,必须先在那里显形。
|
|
215
226
|
*/
|
|
216
227
|
export function errCodes(e) {
|
|
217
228
|
if (!e || typeof e !== 'object')
|
package/dist/detachWire.d.ts
CHANGED
|
@@ -76,6 +76,12 @@ export declare const versionSupportsDetach: (v: string | undefined) => boolean;
|
|
|
76
76
|
* (REF-CC-141 dup-02 单源:`0`/`false`/`no`/`off`/`none`,大小写不敏感)disables the header. Any other
|
|
77
77
|
* value (empty/'true'/typo) = default ON — fail-soft, a settings typo never flips the lane silently
|
|
78
78
|
* to the strict-alignment (disconnect-kills-run) mode (finalVerifyWire 纪律).
|
|
79
|
+
*
|
|
80
|
+
* 🔴 [2393] sweep-F12(2026-08-03 全窗复审)**承诺范围如实**:上面那句话是在 off 集只有
|
|
81
|
+
* `{false,0}` 的时代写的,当时它覆盖 `no`/`off`/`none` 三个拼写(它们落进「任何其它值 ⇒ 默认 ON」)。
|
|
82
|
+
* REF-CC-141 把 off 集扩到五词之后,**这三个拼写现在会**把车道翻成 disconnect-kills-run。
|
|
83
|
+
* 扩集本身是既定方向(用户写 `no`/`off`/`none` 时意图确实是「关」),这里只把范围说清楚:
|
|
84
|
+
* 「typo 绝不静默翻车道」现在指的是 `{false,0,no,off,none}` **之外**的拼写。
|
|
79
85
|
*/
|
|
80
86
|
export declare function headlessDetachDisabledByEnv(env?: EnvLike): boolean;
|
|
81
87
|
/**
|
package/dist/detachWire.js
CHANGED
|
@@ -78,6 +78,12 @@ export const versionSupportsDetach = versionSupportsLimits;
|
|
|
78
78
|
* (REF-CC-141 dup-02 单源:`0`/`false`/`no`/`off`/`none`,大小写不敏感)disables the header. Any other
|
|
79
79
|
* value (empty/'true'/typo) = default ON — fail-soft, a settings typo never flips the lane silently
|
|
80
80
|
* to the strict-alignment (disconnect-kills-run) mode (finalVerifyWire 纪律).
|
|
81
|
+
*
|
|
82
|
+
* 🔴 [2393] sweep-F12(2026-08-03 全窗复审)**承诺范围如实**:上面那句话是在 off 集只有
|
|
83
|
+
* `{false,0}` 的时代写的,当时它覆盖 `no`/`off`/`none` 三个拼写(它们落进「任何其它值 ⇒ 默认 ON」)。
|
|
84
|
+
* REF-CC-141 把 off 集扩到五词之后,**这三个拼写现在会**把车道翻成 disconnect-kills-run。
|
|
85
|
+
* 扩集本身是既定方向(用户写 `no`/`off`/`none` 时意图确实是「关」),这里只把范围说清楚:
|
|
86
|
+
* 「typo 绝不静默翻车道」现在指的是 `{false,0,no,off,none}` **之外**的拼写。
|
|
81
87
|
*/
|
|
82
88
|
export function headlessDetachDisabledByEnv(env = hostEnv()) {
|
|
83
89
|
return envFlagOff(env[HEADLESS_DETACH_ENV]);
|
|
@@ -44,6 +44,11 @@ export declare function parseNoFinalVerifyArgv(argv: string[]): boolean;
|
|
|
44
44
|
* (REF-CC-141 dup-02 单源:`0`/`false`/`no`/`off`/`none`,大小写不敏感)disables the stamp。
|
|
45
45
|
* 其它任何值(含空串/'true'/typo)= 默认态 on——fail-soft:一个 settings typo 只会落回默认开,
|
|
46
46
|
* 绝不 brick 任何 run(scenarioWire 的 settings-lane 纪律)。
|
|
47
|
+
*
|
|
48
|
+
* 🔴 [2393] sweep-F12(2026-08-03 全窗复审)**承诺范围如实**(与 detachWire 同形):上面这句是在
|
|
49
|
+
* off 集只有 `{false,0}` 的时代写的;REF-CC-141 扩到五词之后,`no`/`off`/`none` 三个拼写**已经**
|
|
50
|
+
* 会真的关掉这一 stamp,不再落回「默认开」。「typo 只会落回默认开」现在指的是
|
|
51
|
+
* `{false,0,no,off,none}` **之外**的拼写。
|
|
47
52
|
*/
|
|
48
53
|
export declare function headlessFinalVerifyDisabledByEnv(env?: EnvLike): boolean;
|
|
49
54
|
/**
|
package/dist/finalVerifyWire.js
CHANGED
|
@@ -20,6 +20,11 @@ export function parseNoFinalVerifyArgv(argv) {
|
|
|
20
20
|
* (REF-CC-141 dup-02 单源:`0`/`false`/`no`/`off`/`none`,大小写不敏感)disables the stamp。
|
|
21
21
|
* 其它任何值(含空串/'true'/typo)= 默认态 on——fail-soft:一个 settings typo 只会落回默认开,
|
|
22
22
|
* 绝不 brick 任何 run(scenarioWire 的 settings-lane 纪律)。
|
|
23
|
+
*
|
|
24
|
+
* 🔴 [2393] sweep-F12(2026-08-03 全窗复审)**承诺范围如实**(与 detachWire 同形):上面这句是在
|
|
25
|
+
* off 集只有 `{false,0}` 的时代写的;REF-CC-141 扩到五词之后,`no`/`off`/`none` 三个拼写**已经**
|
|
26
|
+
* 会真的关掉这一 stamp,不再落回「默认开」。「typo 只会落回默认开」现在指的是
|
|
27
|
+
* `{false,0,no,off,none}` **之外**的拼写。
|
|
23
28
|
*/
|
|
24
29
|
export function headlessFinalVerifyDisabledByEnv(env = hostEnv()) {
|
|
25
30
|
return envFlagOff(env[HEADLESS_FINAL_VERIFY_ENV]);
|
|
@@ -125,6 +125,15 @@ export interface FleetLedgerStatus {
|
|
|
125
125
|
workflowCount: number;
|
|
126
126
|
/** REF-CC-051(fleet2-13):畸形帧/通知早退的留痕计数(C5 静默丢数据零容忍)。 */
|
|
127
127
|
droppedMalformed: number;
|
|
128
|
+
/** 🔴 [2393] notif-F12(2026-08-03 全窗复审):REF-CC-051 只补了「畸形」一类,补完之后新的不
|
|
129
|
+
* 对称是 —— `default:` 未知帧型这条**同样会丢数据**的路径连一行 debug 都没有,而策略性丢弃
|
|
130
|
+
* (foreign/unowned)有 debug 却无计数。三条丢弃路径现在三个方向都可读:
|
|
131
|
+
* · `droppedUnknownFrame` = 未知帧型(开集,**正当**;>0 = 上游发了本端还不认识的帧型,
|
|
132
|
+
* 是提货信号,不是错误);
|
|
133
|
+
* · `droppedForeignBgNotification` = 隔离优先丢弃的外来 bg 通知(**正当但需可查**:
|
|
134
|
+
* 判别锚一旦错了,自家通知会整批落进这一格)。 */
|
|
135
|
+
droppedUnknownFrame: number;
|
|
136
|
+
droppedForeignBgNotification: number;
|
|
128
137
|
}
|
|
129
138
|
export interface FleetLedger {
|
|
130
139
|
/** 消费一帧(consumer-side active-set update-in-place / remove)。 */
|
|
@@ -115,6 +115,9 @@ export function createFleetLedger(hooks = {}, opts = {}) {
|
|
|
115
115
|
const retained = new Map();
|
|
116
116
|
/** REF-CC-051(fleet2-13):畸形帧/通知早退计数(与同文件的策略性丢弃 debug 行对称留痕)。 */
|
|
117
117
|
let droppedMalformed = 0;
|
|
118
|
+
/** [2393] notif-F12:另两条丢弃路径的计数(未知帧型 / 隔离优先丢弃)。 */
|
|
119
|
+
let droppedUnknownFrame = 0;
|
|
120
|
+
let droppedForeignBgNotification = 0;
|
|
118
121
|
let connected = false;
|
|
119
122
|
let version = null;
|
|
120
123
|
let scoped = null;
|
|
@@ -272,7 +275,10 @@ export function createFleetLedger(hooks = {}, opts = {}) {
|
|
|
272
275
|
applyBgNotification(frame.notification);
|
|
273
276
|
break;
|
|
274
277
|
default:
|
|
275
|
-
// 未知未来帧 ——
|
|
278
|
+
// 未知未来帧 —— 开集,忽略。**但要留痕**([2393] notif-F12):忽略也是丢数据,
|
|
279
|
+
// 与同文件另两类丢弃(畸形 / 策略性)对称记账,否则「上游发了新帧型」在本端完全不可观察。
|
|
280
|
+
droppedUnknownFrame++;
|
|
281
|
+
debug(`[fleet-frame] UNKNOWN frame type ignored(type=${String(frame.type)})`);
|
|
276
282
|
// ⚠️ 老 SDK 时代这里还兜着「平铺形 bg_notification / 字面 hook_notice」两条 belt;
|
|
277
283
|
// 0.0.117 的 FleetFrame 两者都是具名臂,belt 随之退役(旧回放素材由端自己升级)。
|
|
278
284
|
break;
|
|
@@ -309,6 +315,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
|
|
|
309
315
|
typeof n.parentTaskId === 'string' &&
|
|
310
316
|
n.parentTaskId.length > 0 &&
|
|
311
317
|
!isOwnEngineRun(n.parentTaskId)) {
|
|
318
|
+
droppedForeignBgNotification++; // [2393] notif-F12:策略性丢弃也要有可读的量,不只 debug 行
|
|
312
319
|
debug(`[fleet-frame] bg_notification DROPPED foreign/unowned parentRun(taskId=${n.taskId} parentRun=${n.parentTaskId})`);
|
|
313
320
|
return;
|
|
314
321
|
}
|
|
@@ -463,6 +470,8 @@ export function createFleetLedger(hooks = {}, opts = {}) {
|
|
|
463
470
|
taskCount: taskMap.size,
|
|
464
471
|
workflowCount: wfMap.size,
|
|
465
472
|
droppedMalformed,
|
|
473
|
+
droppedUnknownFrame,
|
|
474
|
+
droppedForeignBgNotification,
|
|
466
475
|
}),
|
|
467
476
|
dispose() {
|
|
468
477
|
disposed = true;
|
|
@@ -133,7 +133,8 @@ export declare const TERMINAL_FLEET_TASK_STATUSES: ReadonlySet<string>;
|
|
|
133
133
|
* 这一支确实是死分支。
|
|
134
134
|
* · 但**后果方向不对称**([anchor-on-the-deciding-quantity]「先定这个量门控动作的正负号」):
|
|
135
135
|
* 本支是**保护型**映射(把非活非终态的 parked 映到 'awaiting approval');删掉之后
|
|
136
|
-
* 它落 `default`
|
|
136
|
+
* 它落 `default`(⚠️ [2393] notif-F5 纠:`default` 已由 REF-CC-043 改渲 `'idle'`,下面那段
|
|
137
|
+
* 是权威;本行原写「渲成 `running` = 谎报活跃」是**退役后没跟的旧行为描述**)。core 1.382 已有该状态([1561],
|
|
137
138
|
* design/153 §7.1),server 只是尚未投影 ⇒ 这不是「猜测层」,是**预挂**。
|
|
138
139
|
* 删的收益 = 一行;删的代价 = server 补上透传那天静默说谎。
|
|
139
140
|
* ⇒ 保留 + 把「候 server 预挂」的注释换成上面这条可复核的直证。定谳权交回 PM。
|
|
@@ -142,6 +143,17 @@ export declare const TERMINAL_FLEET_TASK_STATUSES: ReadonlySet<string>;
|
|
|
142
143
|
* 未知未来状态被谎报成「活跃」,后果方向是**伤害型**(用户会以为一个已经僵住/退场的子代还在跑)。
|
|
143
144
|
* 改渲 `'idle'`(闭集里本就存在的「非活跃」词,187 用它表示「非活跃、非终态、非阻塞」)—— 门
|
|
144
145
|
* `ALWAYS_EMITTED_TASK_VIEW_KEYS` 不受影响,只是同一个已知渲染词的语义更诚实。
|
|
146
|
+
* 🔴 [2393] notif-F5(2026-08-03 全窗复审)**补记宿主后果**(上一版裁决注只写到「同一个已知
|
|
147
|
+
* 渲染词」,而下游语义比这句话大 —— 宪法三问的「谁受害」那一栏漏了这两处,补在这里以免下一棒
|
|
148
|
+
* 照着「只是换个词」去判断):
|
|
149
|
+
* · 壳 `overrides/fleet-tree.tsx`:`idle` 分支**短路 elapsed 与 token 两列**(返回
|
|
150
|
+
* `elapsed:'idle'`、`tokenText:''`),`running` 则渲真 elapsed/tokens;
|
|
151
|
+
* · 壳 `overrides/fleet-view-screen.tsx`:`deriveBands` 把 `idle` 计进 **awaiting**(「等你处理」)
|
|
152
|
+
* 档,`running` 计进 **working** 档。
|
|
153
|
+
* ⇒ status 缺席/脏的行现在会:丢掉 elapsed/tokens + 在表头被数成「awaiting」。方向仍然对
|
|
154
|
+
* (谎报活跃 = 伤害型,谎报不活跃 = 保护型),但它是一次**跨仓渲染语义**改动;三端提货单已点名
|
|
155
|
+
* (`docs/refactor/README.md`「提货单 / 破坏性变更清单」B 组),宿主侧另有一条陈述漂移待改
|
|
156
|
+
* (同表「宿主侧待办」:壳 engineBgShellPanelStore 的兜底方向陈述已成假话)。
|
|
145
157
|
* `s` 的参数类型同批收窄:`| undefined` 半支从未被真实调用点使用过(唯一生产调用点
|
|
146
158
|
* `projectTasks` 传的是 SDK 必填闭集 `FleetTaskRow['status']`),继续留着是死分支。
|
|
147
159
|
*/
|
|
@@ -20,7 +20,8 @@ export const TERMINAL_FLEET_TASK_STATUSES = new Set([
|
|
|
20
20
|
* 这一支确实是死分支。
|
|
21
21
|
* · 但**后果方向不对称**([anchor-on-the-deciding-quantity]「先定这个量门控动作的正负号」):
|
|
22
22
|
* 本支是**保护型**映射(把非活非终态的 parked 映到 'awaiting approval');删掉之后
|
|
23
|
-
* 它落 `default`
|
|
23
|
+
* 它落 `default`(⚠️ [2393] notif-F5 纠:`default` 已由 REF-CC-043 改渲 `'idle'`,下面那段
|
|
24
|
+
* 是权威;本行原写「渲成 `running` = 谎报活跃」是**退役后没跟的旧行为描述**)。core 1.382 已有该状态([1561],
|
|
24
25
|
* design/153 §7.1),server 只是尚未投影 ⇒ 这不是「猜测层」,是**预挂**。
|
|
25
26
|
* 删的收益 = 一行;删的代价 = server 补上透传那天静默说谎。
|
|
26
27
|
* ⇒ 保留 + 把「候 server 预挂」的注释换成上面这条可复核的直证。定谳权交回 PM。
|
|
@@ -29,6 +30,17 @@ export const TERMINAL_FLEET_TASK_STATUSES = new Set([
|
|
|
29
30
|
* 未知未来状态被谎报成「活跃」,后果方向是**伤害型**(用户会以为一个已经僵住/退场的子代还在跑)。
|
|
30
31
|
* 改渲 `'idle'`(闭集里本就存在的「非活跃」词,187 用它表示「非活跃、非终态、非阻塞」)—— 门
|
|
31
32
|
* `ALWAYS_EMITTED_TASK_VIEW_KEYS` 不受影响,只是同一个已知渲染词的语义更诚实。
|
|
33
|
+
* 🔴 [2393] notif-F5(2026-08-03 全窗复审)**补记宿主后果**(上一版裁决注只写到「同一个已知
|
|
34
|
+
* 渲染词」,而下游语义比这句话大 —— 宪法三问的「谁受害」那一栏漏了这两处,补在这里以免下一棒
|
|
35
|
+
* 照着「只是换个词」去判断):
|
|
36
|
+
* · 壳 `overrides/fleet-tree.tsx`:`idle` 分支**短路 elapsed 与 token 两列**(返回
|
|
37
|
+
* `elapsed:'idle'`、`tokenText:''`),`running` 则渲真 elapsed/tokens;
|
|
38
|
+
* · 壳 `overrides/fleet-view-screen.tsx`:`deriveBands` 把 `idle` 计进 **awaiting**(「等你处理」)
|
|
39
|
+
* 档,`running` 计进 **working** 档。
|
|
40
|
+
* ⇒ status 缺席/脏的行现在会:丢掉 elapsed/tokens + 在表头被数成「awaiting」。方向仍然对
|
|
41
|
+
* (谎报活跃 = 伤害型,谎报不活跃 = 保护型),但它是一次**跨仓渲染语义**改动;三端提货单已点名
|
|
42
|
+
* (`docs/refactor/README.md`「提货单 / 破坏性变更清单」B 组),宿主侧另有一条陈述漂移待改
|
|
43
|
+
* (同表「宿主侧待办」:壳 engineBgShellPanelStore 的兜底方向陈述已成假话)。
|
|
32
44
|
* `s` 的参数类型同批收窄:`| undefined` 半支从未被真实调用点使用过(唯一生产调用点
|
|
33
45
|
* `projectTasks` 传的是 SDK 必填闭集 `FleetTaskRow['status']`),继续留着是死分支。
|
|
34
46
|
*/
|