@sema-agent/client-core 0.73.3 → 0.73.4
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 +18 -0
- package/README.md +3 -3
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +30 -17
- package/dist/adapter/downstream/terminalToSdkResult.js +60 -8
- package/dist/adapter/runStream.js +40 -2
- package/dist/hitl/approvalsFeed.d.ts +12 -0
- package/dist/hitl/approvalsFeed.js +69 -2
- package/dist/toolResult.js +5 -0
- package/docs/INTEGRATION-CLIENTS.md +59 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -49,6 +49,24 @@
|
|
|
49
49
|
> 挡住 ⇒ 本批把它机械化——④a0 对 `pending` 行**要求段头已是日期形**(`(未发布)` 直接红),阶段一
|
|
50
50
|
> commit 漏转在发布前就红,不再靠人记。
|
|
51
51
|
|
|
52
|
+
## 0.73.4(2026-09-19)
|
|
53
|
+
|
|
54
|
+
> 主题:**终帧 `permission_denials` 第三键的来源**(CC-34 重开)、**审批 feed「欠一次看」重试**(CC-62)与 **workflow run 级错误码**(CC-63)。**patch**:型面纯 additive(+1 常量,公面 1032 → 1033;`TerminalStreamObserved` 具名化并 +1 可选位;`SemaPermissionDenial` +1 可选位;TaskOutput 卡 `task` +1 超集键;`ApprovalsFeedOptions` +1 可选位;`ApprovalsFeedStats` +1 计数);零 wire 新键;peer 不动。
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **更正 0.73.2 的一句发布说明**:该版写「`permission_denials` 自动升级收口」。实情是引擎 ≥7.23.0 只把**调用 id** 串上了拒绝账本(`_sema_permission_denials[].tool_use_id` 自那时起带值),被拒调用的**入参对象**不在账本的任何一面 ⇒ CC 形三键必填的 `permission_denials` 数组在 0.73.2 / 0.73.3 上**仍然恒空**、`_sema_permission_denials_absent: true` 恒在场。黑盒验收按字面判了 FAIL,判得对。
|
|
59
|
+
- **终帧拒绝清单按 `toolCallId` join 同一条流的 `tool_start` 入参**:`runStream` 在流内记「`toolCallId` → `tool_start.args`」本流快照(只存引用;上限 `TOOL_INPUT_JOIN_MAX_CALLS` = 4096,溢出后单向停记;同一个 id 见到两份不同入参 ⇒ 该 id 永久出表),终局作为 `terminalToSdkResult` 第三参的新可选位 `toolInputByCallId` 交给铸点。join 得上 ∧ 入参是普通对象 ∧ **没被传输层动过** ⇒ 该行三键齐,进 CC 数组,并带来源位 `_sema_tool_input_source: 'tool_start'`;每条拒绝都铸成 CC 形时 `_absent` 不落。其余(行无 id / 本流没见过那只帧:重连后的流、宿主自建管线没交快照 / 入参非对象 / 入参字符串叶里带替换记号 `«redacted…»` 或恰为 `[circular]` / `[depth-limit]` / 超深超大)一律留在 `_sema_permission_denials`,`_absent: true` 照落。账本自己带 `toolInput` 的那天,账本那份恒优先。
|
|
60
|
+
- 读法提醒:审批人**改写过入参**的调用,`tool_start` 帧上是改写后的那份;被拒的调用没有改写,所以拒绝清单上的 `tool_input` 就是模型发出的入参。
|
|
61
|
+
- 被替换记号挡掉的行**少铸不错铸**:入参里本来就含这些字面的极端情形会一并被挡,代价是该行只在 sema 载体上。
|
|
62
|
+
- **审批 feed:push 侧取件没看成不再无人补看**(`startApprovalsFeed`)。push 腿收到事件后那一次 `list()` 若失败(抛出,或回体的 `livePending` 段读不懂),此前只计 `listErrors`;其后的 heartbeat 不触发取件 ⇒ 没有别的事件时,那张待批的卡一直不出现。现在记一笔欠账,按 base、2×base、4×base … 退避重试(新可选位 `pushTakeRetryMs`,缺省 1000;间隔封顶 30 s;`<= 0` = 关),**不按次数放弃**;销账只认**比欠账更晚发起**的取件看成(更早发起、迟到回来的成功看的是失败之前的世界,不销账;反过来,更晚发起的一眼已经看成之后才迟到的旧失败不再记欠账);回体坏形让取件整次抛出的(例:`pending` 不是数组),同样记欠账再原样抛出 —— 既有的逃逸语义(轮询腿连续逃逸计数、`refresh()` 的 rejection)不变。起手那次取件与 `refresh()` 同理。新计数 `stats().pushTakeRetries`。
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- **workflow run 级错误码(CC-63)**:引擎(core ≥7.17.1)在 TaskOutput 结构卡**顶层**铸 run 级 `errorCode`(与 `completionId` 同层;有值才带),本包此前只认得模型面 JSON 里 per-agent 的 `agent_runs[].errorCode`。现在 `structuredToToolUseResult` 的 task-output 臂把它原词透传到 `toolUseResult.task._sema_error_code`;缺席 / 空串 / 非字符串 ⇒ 键不铸(run 无码,不编码);内层 `details` 上的同名键不认。只在**结构面**这条腿上有:模型面正文与列表行都不带这一格。
|
|
67
|
+
- `TOOL_INPUT_JOIN_MAX_CALLS`;具名型 `TerminalStreamObserved`(`doneToSdkResult` / `failedToSdkResult` / `terminalToSdkResult` 第三参此前的内联型,结构不变 +1 可选位)。
|
|
68
|
+
- 门:`run-permission-denial-projection-test.mjs` +F 段(join 两半各有正控、五种被动过的形、扫描预算、账本优先、per-stream、两份不同入参、上限与单向闩、公开入口端到端);`run-live-pending-ask-test.mjs` +S3r 段十九格(含四格确定性乱序、三格坏形回体);`run-client-core-pure-test.mjs` +⑩S4d–S4h(run 级错误码)。变异反证二十四枚逐格见红(另三枚等价变异:账本在场时 join 分支本就不走;空 id 由读侧同一道非空闸挡;销账与摘要的先后在逃逸续记欠账后不改变可观察行为)。
|
|
69
|
+
|
|
52
70
|
## 0.73.3(2026-09-19)
|
|
53
71
|
|
|
54
72
|
> 主题:**CC-61 sdk 9.7.1 提货 + `capabilities.approvalsStreamLive` 四态读口**(server 7.87.1)与 **CC-60 五条模型面文案锚的包侧半场**(下游锚登记表 A-D3 / A-D4 / A-D5 / A-D11 / A-D13,自 1.0.116 起挂在本包名下)。**patch**:型面纯 additive(+6 函数,公面 1026 → 1032;测试钩 48 → 49;`EngineTaskStatusAttachment.status` 联合 +`parked`;`parseModelFacingBash` 返回形 +可选 `note`;`mergeTaskOutput` +可选第三参;devDep sdk ^9.7.1,**peer 地板 >=9.6.0 不动**;零 wire 新键)。接入面 §65。
|
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.73.
|
|
38
|
+
**Version:** 0.73.4
|
|
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
|
|
@@ -316,7 +316,7 @@ public-surface guard checks that last one).
|
|
|
316
316
|
| `scripts/run-approvals-stream-live-capability-test.mjs` | The engine's live-approval-push self-description (`capabilities.approvalsStreamLive`, engine ≥7.87.1), read the same four-state way as its four sibling capability readers: an absent key is reported as not reported (never folded into `false`), the value must be a strict boolean, and the one decision the feed consumer needs — whether it must keep pulling suspended asks itself — is answered by `livePendingNeedsReconcile`, which only says no when the engine explicitly says it pushes. |
|
|
317
317
|
| `scripts/run-model-facing-anchors-test.mjs` | Five places where model-facing engine text is parsed back into card slots: the task-output cursor semantics machine-readable key wins over the spool marker text (and its absence is never read as `full`), the `--- result (…) ---` line accepts a parenthesised note, the bash header accepts the engine's exit-note and image variants and carries the note into the same benign-exit slot the structured path uses, the notification seed scan treats both producers' terminal words as terminal, and the parked background-task phrase is recognised. |
|
|
318
318
|
| `scripts/run-execution-lane-capability-test.mjs` | The deployment execution-lane self-description (`capabilities.executionLane`, engine ≥7.86.0), read the same four-state way as its three sibling capability readers. An absent key is reported as "not reported" and never as "tools do not run on this host": on an older engine the client keeps inferring the lane the way it did before, because reading absence as `false` would silently stop every host deployment from sending skill `baseDir`. The implication is one-way (`toolsOnThisHost: false` means `baseDir` is never sent; `true` is only a necessary condition), `provider` is read as an open non-empty string rather than a hand-copied closed set and is sanitised and bounded before display, `toolsOnThisHost` must be a strict boolean, extra members never cross, the tee never throws, stale generations are dropped whole, and the single "do tools run here" predicate uses the bit when it is present and the caller's own inference, unchanged, when it is not |
|
|
319
|
-
| `scripts/run-live-pending-ask-test.mjs` | Suspended in-stream asks (the `livePending` section of `GET /v1/approvals`): an absent key reads as not reported (never an empty list or a zero count), an unreadable section leaves the previous snapshot in place, rows are narrow-read (malformed rows dropped and counted, optional flags only-if-true, extra members never cross), the feed digest covers the section so an ask appearing or settling produces a snapshot, the host-gated reconcile cadence re-lists while the push leg is connected and backs off when nothing changes, the tracker surfaces each `approvalId` once and reports it gone once, and the decision runs through the same respond chain as the in-stream frame leg — with the card stating that the tool arguments are not visible and edits on that card never forwarded. |
|
|
319
|
+
| `scripts/run-live-pending-ask-test.mjs` | Suspended in-stream asks (the `livePending` section of `GET /v1/approvals`): an absent key reads as not reported (never an empty list or a zero count), an unreadable section leaves the previous snapshot in place, rows are narrow-read (malformed rows dropped and counted, optional flags only-if-true, extra members never cross), the feed digest covers the section so an ask appearing or settling produces a snapshot, the host-gated reconcile cadence re-lists while the push leg is connected and backs off when nothing changes, the tracker surfaces each `approvalId` once and reports it gone once, and the decision runs through the same respond chain as the in-stream frame leg — with the card stating that the tool arguments are not visible and edits on that card never forwarded. Since 0.73.4 a push-side fetch that did not get a readable body (thrown, or an unreadable `livePending` section) is owed a look: it is retried with doubling back-off until one fetch succeeds (never abandoned by count), cancelled by any successful fetch, cleared on stop, and switchable off. |
|
|
320
320
|
| `scripts/run-leader-conflict-test.mjs` | The leader-run terminal `needs_human` + `result.conflict` (engine ≥7.83.0), read once for all three shells. A `result` without a `conflict` key is reported as `none` and worded as "no conflict details" rather than "no conflict": on the wire it is indistinguishable from an older engine that never reports one, and the reader does not pick a side. A `conflict` that is present but unreadable is a third word, never folded into `none`, because a tree that carries conflict markers must not be rendered as clean. `filesTruncated` is honoured only as `true` (absent means the list is complete; any other value makes the section unreadable), `workers[].applied` is passed through as the boolean fact it is, `salvaged[].patch` is handed to the save path byte-for-byte (an artifact, not screen text; a malformed row is dropped alone, and a salvage list that is present but cannot be fully read is flagged rather than rendered as "no patches"), and `rejHead` — the one on-screen diagnostic — is escaped and bounded before display. The record never throws on hostile input, extra members do not cross, arrays are fresh copies, and the single sentence minted here names the base, the files, which branches landed and which did not, and how many patches can be saved, without ever suggesting a retry: `needs_human` is a run waiting for a person, and the no-details sentence says plainly that it is not proof of a merge |
|
|
321
321
|
| `scripts/run-mcp-reconnect-test.mjs` | The in-session MCP re-dial verb (`POST /v1/sessions/:id/mcp/reconnect`, engine ≥7.85.0), consumed. The single discriminant is `outcome` and all three answers are HTTP 200, so the reader branches on the word and never on the status; the `unsupported` answer carries exactly five keys and the reader refuses to invent a zero or an empty list for the four fields the engine did not produce, while `accepted` / `refused` treat those four as required and go malformed when one is missing. The tool roster follows the **presence** of `toolNames` (absent = untouched, empty = withdrawn), the connection record passes `errorCode` through as an open set, and every remote-authored string is sanitised and bounded before display. Failures are classified by `errorCode` alone, a missing code is reported as unknown rather than guessed, the verb never throws, and the request-side guard (non-empty name, ≤190 chars) stops a call that the contract would reject anyway. The capability bit reads absent as "cannot tell" rather than "unavailable", and the one sentence the contract insists every UI carries — that re-dialing is a transaction, not a refresh — is minted here once |
|
|
322
322
|
| `scripts/run-core-value-ports-test.mjs` | The port-injection seam for ten **engine value-level** facilities (autonomous-loop prompt assembly, permission-rule loosening, tool-policy composition, protocol/retired-name/grammar lookups, rule compilation, the discussion workflow name). This package cannot re-export them (the engine barrel drags Node built-ins into the browser bundle), so it declares the ports and honest-absence readers; a Node host installs the engine's own functions verbatim. The guard pins: every reader returns `undefined` when nothing is installed (never a fabricated empty array or default policy), arguments and results pass through by reference, engine errors propagate unchanged, partial installs read partially, restore functions unwind to the previous bag, and the module source has zero engine imports |
|
|
@@ -374,7 +374,7 @@ public-surface guard checks that last one).
|
|
|
374
374
|
| `scripts/run-classifier-status-test.mjs` | What state the auto-mode classifier is in **on this session** — the question a doctor line, a model settings page and a permission card’s status row all ask, and a different question from the one the approval card asks (*why am I being asked right now*), so the sentences are pinned mutually distinct from that face’s as well as from each other. The session-level half of this reading — a breaker record the engine used to keep — was **retired upstream**, and the guard now holds that retirement from **both** sides: the engine's own declarations must really no longer carry it (a fact coming back would mean the removal here was the wrong disposition, and that deserves a conversation rather than silence), and this package must carry no alias, no state word and no leftover narrowing for it — a reading kept alive for something nobody emits any more is a promise the interface cannot keep, and it left the doctor line advertising a state it can never reach. What remains is ordered by the quantity that actually decides whether the classifier is running: the fact from **this round** first, then whether this leg is armed — a decider is minted per run, so a later leg can be armed again. Not armed, and a section that never arrived, both answer **undefined** rather than *available*; that arming question has its own field and answering it twice grows a second ledger. Arming and availability are also **two words, not one**: the engine says a decider was minted *for this leg*, which is an assembly-time fact, while whether that decider answers any given round is a **per-call** one — so an armed leg reads `armed` and only a positive per-call fact (an ask whose origin is the classifier's own denial-bound fallback, which by construction stands *after* the classifier ran) reads `available`. Every other ask origin is refused as evidence and for a stated reason rather than out of caution: several are ones the classifier is structurally forbidden to answer, and for the rest a surviving ask is precisely the case where it did **not** resolve one — so reading availability off them would be a guess. The projection is a **whitelist**, so an older engine still sending the retired member loses it at the boundary while the two live facts beside it ride through untouched. Rendering never throws and never impersonates: a state word this client does not know — including the retired one, which a restored view can still carry — reaches an honest fallback that names it verbatim, carries no invented explanation of a mechanism that no longer exists, and is proven distinct from all three real sentences; prototype keys reach that same fallback rather than a function body, checked against a real out-of-table word so the comparison cannot hold vacuously |
|
|
375
375
|
| `scripts/run-compaction-boundary-projection-test.mjs` | The compaction divider and the one frame that makes its anchor resolvable. The trigger word is passed through as an **open set** instead of being folded to two: the engine deliberately stopped flattening its third value (a compaction that was not optional — a prompt-too-long recovery or trim pressure) and carries what the hook layer saw, so folding it again at the package boundary re-introduces exactly what upstream had just removed, while a consumer branching on *is it manual* keeps its behaviour byte for byte. Only an unreadable word (absent, empty, non-string) falls back — that is *could not read it*, not *read it and did not recognise it*. Two superset keys ride the metadata and neither fabricates: the preserved-segment anchor is minted only when its id really reads out, because half an anchor sends the host looking up an empty string in its map, and the clamp ratio is a **disclosure** whose real zero is a fact rather than an absence. The clamp ratio also carries a registered exit condition — the service really sends it while the SDK arm has no seat for it yet, so the read is defensive and this guard reds the day that seat appears, forcing a re-check instead of leaving a cast to rot. The committed-message frame moves out of *deliberately not projected*: that classification was true about transcript rows and false about **positioning**, since the engine states that consumers build their own id-to-message map from this frame to place the divider — projecting the anchor without it hands the host something it cannot resolve. It becomes a neutral internal arm and an optional chrome ledger event, never a transcript row (the frame carries no body, so minting one would put words in the engine's mouth), with both required ids narrowed and a malformed frame recorded rather than half-minted |
|
|
376
376
|
| `scripts/run-cost-absence-projection-test.mjs` | Telling **declared free** apart from **never priced**, in both directions, because the package was getting each one wrong in the opposite way. The engine separates them on the wire — an absent cost means some spend had no price table, an explicit zero means the model declared itself free — and the result projector used to require a *positive* number, so a genuinely free run could not say so; while the per-model mirror folded absence to zero, so an unpriced run told a billing consumer it cost nothing. The total is now reported as the engine stated it, with absence and non-finite values alone reading as unknown, and a negative passed through rather than corrected, since a refund is a legal figure and the package is not a second accountant. The per-model figure keeps the CC shape intact — that field is a required number and *unknown* is simply not expressible in it — so the value stays zero and a **companion superset bit** carries the distinction, which means the two are read together and a reader that only ever looked at the number is unchanged; the bit is minted only in the absent case and never as `false`, since a key present with a false value reads as a third state. The same mint point serves both the wire's per-model split and the synthesised current-model row, so neither can drift. Alongside it the cache-write figure stops being a hardcoded zero and reads the field the wire has always carried, in both the flat usage and the synthesised row, and all four flat token slots move from a null-coalesce to a finite-number guard — the stats object has an open index signature and the wire is JSON, so a string or an infinity would otherwise land in a slot the types promise is a number, compiling green and surfacing only when something sums it |
|
|
377
|
-
| `scripts/run-permission-denial-projection-test.mjs` | The terminal result's **permission-denial list** being the wire's real one rather than a hardcoded empty array. The session vocabulary carries a list of tool calls that were denied; the projector used to mint `[]` in both the success arm and the error envelope, which folded two different statements into one — *nothing was denied on this run* and *this frame carries no such ledger at all* (an older engine, a rejection envelope, a failure event that arrives without stats) looked identical. Each denied gate on the wire's human-review ledger now becomes one record, in wire order, carrying the keys the wire can actually honour: the tool name when it reported one, and a superset field with the engine's own short, redacted one-line summary of the call's input. **Two lists, deliberately.** The reference shape requires three fields on every element — tool name, call id, and the full input object — and the wire's ledger carries only the first. Filling the other two with an empty string and an empty object would be invention; putting a half-filled element into the reference array would break the element contract, and a strict consumer validating the stream drops the *whole* result message rather than one field. So the reference array admits only fully-formed records — empty today, and filling itself the day the wire grows the two missing fields, with no code change — while every record the wire really has rides a superset carrier beside it. A contract check pins today's absence, so that day turns this guard red on purpose. The companion bit means *this reference list cannot be claimed complete*: no ledger, an unreadable row, an unrecognised decision word (a rejected plan is not a denied tool call, and a row with no decision at all is not a judgement), or a record that could not be fully formed. Only its absence lets a reader say *zero denials*; it is never minted as `false`. Rows that cannot be read drop themselves rather than the whole ledger, and both arms go through one mint point so they cannot drift |
|
|
377
|
+
| `scripts/run-permission-denial-projection-test.mjs` | The terminal result's **permission-denial list** being the wire's real one rather than a hardcoded empty array. The session vocabulary carries a list of tool calls that were denied; the projector used to mint `[]` in both the success arm and the error envelope, which folded two different statements into one — *nothing was denied on this run* and *this frame carries no such ledger at all* (an older engine, a rejection envelope, a failure event that arrives without stats) looked identical. Each denied gate on the wire's human-review ledger now becomes one record, in wire order, carrying the keys the wire can actually honour: the tool name when it reported one, and a superset field with the engine's own short, redacted one-line summary of the call's input. **Two lists, deliberately.** The reference shape requires three fields on every element — tool name, call id, and the full input object — and the wire's ledger carries only the first. Filling the other two with an empty string and an empty object would be invention; putting a half-filled element into the reference array would break the element contract, and a strict consumer validating the stream drops the *whole* result message rather than one field. So the reference array admits only fully-formed records — empty today, and filling itself the day the wire grows the two missing fields, with no code change — while every record the wire really has rides a superset carrier beside it. A contract check pins today's absence, so that day turns this guard red on purpose. The companion bit means *this reference list cannot be claimed complete*: no ledger, an unreadable row, an unrecognised decision word (a rejected plan is not a denied tool call, and a row with no decision at all is not a judgement), or a record that could not be fully formed. Only its absence lets a reader say *zero denials*; it is never minted as `false`. Rows that cannot be read drop themselves rather than the whole ledger, and both arms go through one mint point so they cannot drift. Since 0.73.4 the third CC key is sourced from the same stream's `tool_start` frame, joined by call id: a row joins only when the frame was seen on this stream, its arguments are a plain object, and no string leaf carries a transport replacement token or a cycle / depth placeholder (scan budgeted); both halves have positive controls (a fully joined list drops the discriminator, a partially joined one keeps it), the ledger's own input wins when present, the snapshot is per-stream and capped with a one-way overflow latch, and an id seen with two different argument objects never joins |
|
|
378
378
|
| `scripts/run-cost-reconcile-projection-test.mjs` | The **end-of-run cost reconciliation** reaching consumers at all. The engine splits a run's spend on the wire — the task's own cost, which deliberately excludes delegated sub-agents, the delegated total itself, and the within-task compaction subtotal that sits inside the own figure — and states two reconciliation identities for them. The package used to project none of it, so a cost view could only ever see one number and under-reported both delegated and compaction spend. Both structures are now projected onto the result as superset fields in the wire's integer micro-currency unit, read key by key, with unreadable keys dropped individually, an entirely unreadable structure omitted rather than emitted empty, and unknown categories passed through since the vocabulary belongs upstream. The delegated cost stays **absent when it was never priced**, never a fabricated zero. The same reader also feeds a terminal chrome arm carrying the three parts plus the reconciled total, so the two faces can never compute different answers; the reconciled total is minted only when both sides are known, and otherwise a discriminator bit says which side is unknown. **The reference field for total cost keeps its meaning** — it remains the task's own spend and the delegated total is not folded into it — because that is a shape the wider ecosystem reads; the reconciled figure is offered beside it, not in place of it. A frame that carries no stats emits no arm at all, and the existing rule that in-stream per-turn usage is not published for sub-flows is pinned unchanged, since delegated spend arrives once, at the end. The bit that says those figures are a lower bound is **per stream**, not per context: the emit context belongs to the caller and may be reused across streams, so a gap observed on one run is no evidence at all about the next one — the observation is held for the duration of one stream and handed to both projection faces by value, and the guard drives a reused context both sequentially and concurrently to prove neither direction leaks |
|
|
379
379
|
| `scripts/run-task-progress-terminal-projection-test.mjs` | The one tick that says a delegated child **finished**. The engine fires exactly one final beat carrying a terminal face, and says in the same breath why it exists — so a consumer sees the row finish instead of watching it vanish after the last running beat — but the package's projection whitelist had no seat for that field and its adapter still carried the older premise in a comment, so the terminal beat arrived byte-identical to another running one: the panel row stayed up waiting for a defensive sweep (which only ever settles rows bound to a card still open this turn) or for a separate notification frame. The status now rides through as an **open set** with the vocabulary left upstream, while the question *which words are terminal* is answered by a closed pair on the adapter side — an unrecognised new word takes the running path, because guessing it terminal ends a row that is still working whereas one extra running beat merely renders late. A terminal beat settles the row directly under the lane proof its binding gives it (not the main lane a notification would use, and not by card id, since the engine is naming a child rather than closing a card), freezes the inline group-row twin in the same beat so a later sweep cannot reset the real tool count, clears the session-resident ledger, and fires the stop hook only for a child whose start really fired. It does not mark the row live or emit a second progress beat, and it shares the settled-row ledger with the other two settle legs so a replay or a double-delivery cannot produce a second end. Three things are pinned **unchanged**: a running beat, an absent status (older engines never send the field, and reading absence as terminal would make every child row disappear on its first beat), and the workflow lane gate, which still runs before any of this |
|
|
380
380
|
| `scripts/run-assistant-arm-identity-test.mjs` | The identity keys on an assistant row, and an explicit account of the two that are **deliberately not** there. What the renderer received was a bare role-and-content object, so a dozen consumer sites downstream were each estimating what the message envelope should have told them. The id is taken from the engine's own event id rather than minted locally, because it has to be **the same value** on the live leg and on a durable replay — a freshly minted one would make a replayed message look new to a host's dedup and to rewind — and when the wire carries none the key is simply absent rather than filled with a random stand-in wearing an identity it does not have; it is also kept distinct from the envelope's own local render key, which is a different identity. The model name comes from what the host pinned when it opened the stream (the request was the host's to build) and is never guessed, since a wrong model name is worse than none once a billing or capability face looks it up. Usage and stop reason are **not** minted on this arm, and the reason is frame order rather than effort: content arms arrive before the turn's closing frame, so at the moment the arm is emitted the engine has not yet said what the round cost — anything put there would be an estimate, which is the very thing this work exists to remove — and synthesising a follow-up assistant update when the real figure lands is also refused, because that shape does not exist upstream and would place a message in the transcript the engine never sent. Their real values leave through the turn's own neutral arm as two superset keys, the usage one reusing the **same single mint point** the footer rollup already folds so the two faces cannot diverge, and the stop reason passed through verbatim as an open set — the machine signal for *was this turn cut short*, previously blind on both the stream and the trace. The existing behaviours beside them are pinned too: no arm at all when usage is wholly absent, and the sub-flow cut-out that keeps a child's turn from driving the leader's face |
|
|
@@ -123,6 +123,22 @@ export interface SemaFlatUsage {
|
|
|
123
123
|
*/
|
|
124
124
|
readonly _sema_cache_write_tokens_long?: number;
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* **这一条流**的收口快照(per-stream 的按次入参,绝不挂在调用方可复用的 `EmitContext` 上)。
|
|
128
|
+
* 每一格缺席 ⇒ 这次投影没有对应的流内面,只读终帧 `stats` 那一半。
|
|
129
|
+
*/
|
|
130
|
+
export interface TerminalStreamObserved {
|
|
131
|
+
readonly usageMissingObserved?: boolean;
|
|
132
|
+
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
133
|
+
/**
|
|
134
|
+
* 0.73.4 —— 本流见过的 `tool_start` 帧:`toolCallId → args`(帧上那只对象的**同一个引用**,不复制)。
|
|
135
|
+
* 拒绝账本行给不出入参对象,而被拒那次调用的入参已经在同一条流上;终帧铸拒绝行时按 id join
|
|
136
|
+
* (判据见 {@link joinableToolInput})。同一个 id 出现过两份不同入参的,收集方必须**不放进来**。
|
|
137
|
+
*/
|
|
138
|
+
readonly toolInputByCallId?: ReadonlyMap<string, unknown>;
|
|
139
|
+
}
|
|
140
|
+
/** 本流 `toolCallId → args` 快照的条数上限;溢出的调用不记 ⇒ 那一行 join 不上,留在 sema 载体(诚实缺席)。 */
|
|
141
|
+
export declare const TOOL_INPUT_JOIN_MAX_CALLS = 4096;
|
|
126
142
|
/**
|
|
127
143
|
* D-1 / L-192①(0.66.0)—— 一条被拒记录的**全可选**形:CC `SDKPermissionDenial`
|
|
128
144
|
* (agent-types `permissions.d.ts`,真形 `{tool_name: string; tool_use_id: string;
|
|
@@ -133,13 +149,14 @@ export interface SemaFlatUsage {
|
|
|
133
149
|
* 的 gate 行是具名 `HumanReviewGateRow` 六格(`kind` / `waitMs` / `decision?` / `toolName?` /
|
|
134
150
|
* `toolArg?` / `toolCallId?`,`task-result.d.ts` 真字节)。`toolCallId` 是**行身份**(core 逐字:
|
|
135
151
|
* 「absent = not joinable … Never a placeholder」—— 缺席 = 铸点没有调用 id 可给:探针行 / 非
|
|
136
|
-
* tool_approval 的耐久门 / 老引擎;**不是**「没有调用」)
|
|
137
|
-
* (sdk `types.d.ts` 同一格逐字记着「`tool_input`/`toolInput` **不在** gate ledger 上」)。⇒
|
|
138
|
-
*
|
|
152
|
+
* tool_approval 的耐久门 / 老引擎;**不是**「没有调用」)。被拒时的**入参对象**账本上**没有**
|
|
153
|
+
* (sdk `types.d.ts` 同一格逐字记着「`tool_input`/`toolInput` **不在** gate ledger 上」)。⇒ 第三键的
|
|
154
|
+
* 来源是**同一条流的 `tool_start` 帧**(0.73.4;按 `toolCallId` join,判据见 {@link joinableToolInput});
|
|
155
|
+
* join 不上 ⇒ 那一键**缺席**,绝不铸 `{}`:一个空对象在 CC 形上读起来是「这次调用的入参是空的」,那是编的。
|
|
139
156
|
* 🔴 **两键都声明在这里**(不是假 affordance):mint 点对它们是**开集宽读** —— `toolCallId` 在
|
|
140
157
|
* 7.23.0 串上账本那天 `tool_use_id` 就**自动**开始带值(见 {@link permissionDenialParts} 的自动
|
|
141
|
-
* 升级腿,门里钉着 7.23.0 的真字节正控)
|
|
142
|
-
* (老引擎 / 不可 join 的行两键缺席)。
|
|
158
|
+
* 升级腿,门里钉着 7.23.0 的真字节正控);账本上的 `toolInput` 仍等上游(哪天串上,它恒优先于流内
|
|
159
|
+
* join 的那份)。消费端读它们**必须按可选位读**(老引擎 / 不可 join 的行两键缺席)。
|
|
143
160
|
* 🔴 `_sema_tool_arg` = core 已经**脱敏并截短**的一行入参摘要(`primaryActivityArg` 同一道口),
|
|
144
161
|
* 它是 CC「denied: Bash(rm …)」那行显示唯一拿得到的材料。UNTRUSTED-for-display:只渲染,
|
|
145
162
|
* 绝不回喂模型、绝不当鉴权判据。
|
|
@@ -151,6 +168,11 @@ export interface SemaPermissionDenial {
|
|
|
151
168
|
readonly tool_use_id?: string;
|
|
152
169
|
/** 被拒调用的**完整入参**(⇐ `gates[].toolInput`,今天 wire 上没有 ⇒ 恒缺席;非对象一律不铸)。 */
|
|
153
170
|
readonly tool_input?: Record<string, unknown>;
|
|
171
|
+
/**
|
|
172
|
+
* 0.73.4 —— `tool_input` 的来源位,**只在**它来自同一条流的 `tool_start` 帧时在场(账本自己带的那份不铸本位)。
|
|
173
|
+
* 读法:在场 = 这只对象是流内帧上的入参(审批人改写过入参的调用,帧上是改写后的那份)。
|
|
174
|
+
*/
|
|
175
|
+
readonly _sema_tool_input_source?: 'tool_start';
|
|
154
176
|
/** 被拒调用的一行入参摘要(⇐ `gates[].toolArg`,core 侧已脱敏截短);缺席 = 这条腿没串入参。 */
|
|
155
177
|
readonly _sema_tool_arg?: string;
|
|
156
178
|
}
|
|
@@ -371,19 +393,13 @@ export declare function doneToSdkResult(ev: Extract<AgentEvent, {
|
|
|
371
393
|
* 累加表(见 {@link nestedUsageByTaskParts})。两格都是 per-stream 的事实,缺席 ⇒ 这次投影没有流内面
|
|
372
394
|
* (只读 `stats` 那一半、两个分表键都不铸),**绝不**回头去读调用方对象上可能残留的上一条流。
|
|
373
395
|
*/
|
|
374
|
-
observed?:
|
|
375
|
-
readonly usageMissingObserved?: boolean;
|
|
376
|
-
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
377
|
-
}): SDKMessage;
|
|
396
|
+
observed?: TerminalStreamObserved): SDKMessage;
|
|
378
397
|
/** `failed` → SDKResultError (contract 02 §2.11 / 08 CS-11). */
|
|
379
398
|
export declare function failedToSdkResult(ev: Extract<AgentEvent, {
|
|
380
399
|
type: 'failed';
|
|
381
400
|
}>, ctx: EmitContext,
|
|
382
401
|
/** 0.67.2:同 {@link doneToSdkResult} 的第三参。 */
|
|
383
|
-
observed?:
|
|
384
|
-
readonly usageMissingObserved?: boolean;
|
|
385
|
-
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
386
|
-
}): SDKMessage;
|
|
402
|
+
observed?: TerminalStreamObserved): SDKMessage;
|
|
387
403
|
/**
|
|
388
404
|
* Dispatch a terminal AgentEvent to its SDKResult arm.
|
|
389
405
|
*
|
|
@@ -395,7 +411,4 @@ export declare function terminalToSdkResult(ev: Extract<AgentEvent, {
|
|
|
395
411
|
type: 'done';
|
|
396
412
|
} | {
|
|
397
413
|
type: 'failed';
|
|
398
|
-
}>, ctx: EmitContext, observed?:
|
|
399
|
-
readonly usageMissingObserved?: boolean;
|
|
400
|
-
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
401
|
-
}): SDKMessage;
|
|
414
|
+
}>, ctx: EmitContext, observed?: TerminalStreamObserved): SDKMessage;
|
|
@@ -62,6 +62,48 @@ function flattenUsage(stats) {
|
|
|
62
62
|
function nonEmptyStr(v) {
|
|
63
63
|
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
64
64
|
}
|
|
65
|
+
/** 本流 `toolCallId → args` 快照的条数上限;溢出的调用不记 ⇒ 那一行 join 不上,留在 sema 载体(诚实缺席)。 */
|
|
66
|
+
export const TOOL_INPUT_JOIN_MAX_CALLS = 4096;
|
|
67
|
+
/** 传输层的替换记号前缀(闭形 `«redacted»` / `«redacted:<词>»` / `«redacted:<词>:partial»`;按前缀认 = 多认不少认)。 */
|
|
68
|
+
const REDACTION_TOKEN_PREFIX = '\u00abredacted';
|
|
69
|
+
/** 传输层把环 / 超深子树换成的两个占位串(只认「叶值恰等于」)。 */
|
|
70
|
+
function isTransportPlaceholderLeaf(v) {
|
|
71
|
+
return v === '[circular]' || v === '[depth-limit]';
|
|
72
|
+
}
|
|
73
|
+
/** 扫描预算:节点数与深度任一超限 ⇒ 按「判不出干净」处置(不铸)。 */
|
|
74
|
+
const TOOL_INPUT_SCAN_MAX_NODES = 20000;
|
|
75
|
+
const TOOL_INPUT_SCAN_MAX_DEPTH = 80;
|
|
76
|
+
/**
|
|
77
|
+
* 流内 `tool_start.args` 能不能当 CC `tool_input` 用。三道闸,任一不过 ⇒ `undefined`(该行留在 sema 载体):
|
|
78
|
+
* ① 是普通对象(CC 形是 `Record<string, unknown>`;字符串 / 数组 / null 都不是);
|
|
79
|
+
* ② **没被传输层动过**:帧上的入参在到达客户端之前过一道保形脱敏(结构与键名不动,字符串叶里的凭据形
|
|
80
|
+
* 子串换成记号;环与超深子树换成占位串)。被换过的对象**不是**那次调用的入参 —— 铸进 CC 键等于
|
|
81
|
+
* 声称「模型就是拿这只对象调的工具」。方向是少铸不错铸:入参里本来就含这些字面的极端情形会被
|
|
82
|
+
* 一并挡掉,代价是那一行留在 sema 载体;
|
|
83
|
+
* ③ 扫描在预算内走完(超大 / 超深 / 自环的对象判不出干净 ⇒ 不铸)。
|
|
84
|
+
*/
|
|
85
|
+
function joinableToolInput(args) {
|
|
86
|
+
if (args === null || typeof args !== 'object' || Array.isArray(args))
|
|
87
|
+
return undefined;
|
|
88
|
+
let nodes = 0;
|
|
89
|
+
const stack = [{ v: args, depth: 0 }];
|
|
90
|
+
while (stack.length > 0) {
|
|
91
|
+
const { v, depth } = stack.pop();
|
|
92
|
+
if (++nodes > TOOL_INPUT_SCAN_MAX_NODES || depth > TOOL_INPUT_SCAN_MAX_DEPTH)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof v === 'string') {
|
|
95
|
+
if (v.includes(REDACTION_TOKEN_PREFIX) || isTransportPlaceholderLeaf(v))
|
|
96
|
+
return undefined;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (v === null || typeof v !== 'object')
|
|
100
|
+
continue;
|
|
101
|
+
const children = Array.isArray(v) ? v : Object.values(v);
|
|
102
|
+
for (const c of children)
|
|
103
|
+
stack.push({ v: c, depth: depth + 1 });
|
|
104
|
+
}
|
|
105
|
+
return args;
|
|
106
|
+
}
|
|
65
107
|
/**
|
|
66
108
|
* 「这条 gate 是**被拒**的」判词表。
|
|
67
109
|
* 🔴 **识别表,不是合法值表**(同 `engineErrorCodes` 的开集纪律):core 今天在两处铸这一格 ——
|
|
@@ -91,21 +133,24 @@ const GATE_NON_DENIAL_DECISIONS = new Set(['allow', 'approve', 'edit', 'reject']
|
|
|
91
133
|
* (agent-types `permissions.d.ts` 与壳侧 zod `SDKPermissionDenialSchema` 皆然),而 wire 的 gate
|
|
92
134
|
* 账本(7.23.0 起六格)给不出 `tool_input`。两条路都不能走:**补零补空 = 编造**;**塞半条记录 = 破坏元素契约**
|
|
93
135
|
* ——严格消费方 `safeParse` 会把**整条 result 帧**判非法(丢一格 vs 丢整帧,后者严重得多)。
|
|
94
|
-
* ⇒ CC
|
|
95
|
-
* `
|
|
96
|
-
*
|
|
136
|
+
* ⇒ CC 数组只收**三键齐全**的记录,wire 上真有的每一条走 sema 载体 `_sema_permission_denials`,
|
|
137
|
+
* 键按能兑现的铸。`tool_use_id` ⇐ 账本行的 `toolCallId`(引擎 ≥7.23.0);`tool_input` ⇐ 账本行的
|
|
138
|
+
* `toolInput`(今天没有)**或**同一条流 `tool_start` 帧上的入参(0.73.4;调用方把本流快照作为按次入参
|
|
139
|
+
* 交进来,没交 = 没有流内面 = 与 0.73.3 之前逐字同)。🔴 0.73.2 的发布说明曾把「`tool_use_id` 到货」写成
|
|
140
|
+
* 「CC 数组自动填满」—— 那是错的:缺第三键的行进不了 CC 数组,直到本版的流内 join 才真有三键齐的行。
|
|
97
141
|
*
|
|
98
142
|
* 🔴 判别位 `_sema_permission_denials_absent` = **「CC 那条清单不可声称完整」**,四条路径:
|
|
99
143
|
* ① 账本整个读不出(`humanReview` 缺席 / 坏形 / `gates` 不是数组 / 无 stats);
|
|
100
144
|
* ② 有读不出的行(非对象);
|
|
101
145
|
* ③ 有**认不出的判词**(两张判词表之外的词 / 判词缺席)—— 不猜它是不是拒绝;
|
|
102
|
-
* ④ 有被拒记录**没能铸成 CC 形**(
|
|
146
|
+
* ④ 有被拒记录**没能铸成 CC 形**(行无 `toolCallId` / 本流没见过那只 `tool_start`(重连后的流、
|
|
147
|
+
* 宿主自建管线没交快照)/ 入参不是普通对象 / 入参被传输层动过)。
|
|
103
148
|
* 读法因此是两键合读:`[]` + 判别位缺席 = **零拒绝**(正面事实);`[]` + 判别位 `true` = 别渲肯定句。
|
|
104
149
|
* 判别位**只在该说话时铸,绝不铸 `false`**(本包 additive 一贯纪律)。
|
|
105
150
|
*
|
|
106
151
|
* 逐行独立:坏行只丢自己,绝不丢整本账(但会让清单失去「完整」资格,见 ②③)。
|
|
107
152
|
*/
|
|
108
|
-
function permissionDenialParts(stats) {
|
|
153
|
+
function permissionDenialParts(stats, toolInputByCallId) {
|
|
109
154
|
const hr = stats?.humanReview;
|
|
110
155
|
const gates = hr !== null && typeof hr === 'object' ? hr.gates : undefined;
|
|
111
156
|
// 账本整个读不出(缺席 / 坏形 / 无 stats)—— 诚实缺席,不猜。
|
|
@@ -139,15 +184,22 @@ function permissionDenialParts(stats) {
|
|
|
139
184
|
// 上游若用别的名字补上,门里的缺席证据当天红 ⇒ 那一批改读真名。绝不自铸值。
|
|
140
185
|
const toolUseId = nonEmptyStr(row.toolCallId);
|
|
141
186
|
const rawInput = row.toolInput;
|
|
142
|
-
const
|
|
187
|
+
const ledgerInput = rawInput !== null && typeof rawInput === 'object' && !Array.isArray(rawInput)
|
|
143
188
|
? rawInput
|
|
144
189
|
: undefined;
|
|
190
|
+
// 0.73.4:账本没带 ⇒ 按行身份去本流快照里 join(账本那份在场时恒优先;`has` 先判,绝不把
|
|
191
|
+
// 「没见过这只 id」与「见过、入参是 undefined」混成一格)。
|
|
192
|
+
const joinedInput = ledgerInput === undefined && toolUseId !== undefined && toolInputByCallId?.has(toolUseId) === true
|
|
193
|
+
? joinableToolInput(toolInputByCallId.get(toolUseId))
|
|
194
|
+
: undefined;
|
|
195
|
+
const toolInput = ledgerInput ?? joinedInput;
|
|
145
196
|
// 键都读不出时仍然 push 一条空记录:**条数**(「这条 run 被拒了几次」)是这本账最要紧的
|
|
146
197
|
// 事实,丢掉它等于把一次真实发生的拒绝抹成没发生;键按能兑现的铸,不编。
|
|
147
198
|
denials.push({
|
|
148
199
|
...(toolName !== undefined ? { tool_name: toolName } : {}),
|
|
149
200
|
...(toolUseId !== undefined ? { tool_use_id: toolUseId } : {}),
|
|
150
201
|
...(toolInput !== undefined ? { tool_input: toolInput } : {}),
|
|
202
|
+
...(joinedInput !== undefined ? { _sema_tool_input_source: 'tool_start' } : {}),
|
|
151
203
|
...(toolArg !== undefined ? { _sema_tool_arg: toolArg } : {}),
|
|
152
204
|
});
|
|
153
205
|
}
|
|
@@ -725,7 +777,7 @@ function errorResult(ctx, parts) {
|
|
|
725
777
|
usage: flattenUsage(parts.stats),
|
|
726
778
|
modelUsage: modelUsageFor(parts.stats, parts.model),
|
|
727
779
|
// D-1 / L-192①:两句话不再折成一句 —— 清单 + 「有没有这本账」的判别位,见 permissionDenialParts。
|
|
728
|
-
...permissionDenialParts(parts.stats),
|
|
780
|
+
...permissionDenialParts(parts.stats, parts.observed?.toolInputByCallId),
|
|
729
781
|
// D-3 / B-068:失败/到限/park 的 run 一样花过钱,账不因结局不好就不报。
|
|
730
782
|
...costFactParts(parts.stats, parts.observed),
|
|
731
783
|
// CC-06:引擎生效面两键(失败臂同带)。
|
|
@@ -946,7 +998,7 @@ observed) {
|
|
|
946
998
|
// MF-25/P1-5 — the per-model usage split (wire `stats.modelUsage`, or the synthesized current-model row).
|
|
947
999
|
modelUsage: modelUsageFor(stats, r.model),
|
|
948
1000
|
// D-1 / L-192①:同形第二处 —— 与错误信封共用**同一个** mint 点(修前两处各一个字面量 [])。
|
|
949
|
-
...permissionDenialParts(stats),
|
|
1001
|
+
...permissionDenialParts(stats, observed?.toolInputByCallId),
|
|
950
1002
|
// D-3 / B-068:成本明细与子代那本账(micro-USD 原值);`total_cost_usd` 语义一字不动。
|
|
951
1003
|
...costFactParts(stats, observed),
|
|
952
1004
|
// CC-06(0.71.3):`_sema_effective_reasoning` / `_sema_effective_memory_scopes`。
|
|
@@ -2,7 +2,7 @@ import { eventSeq, } from './types.js';
|
|
|
2
2
|
import { eventToSdkMessage, turnEndUsage } from './downstream/eventToSdkMessage.js';
|
|
3
3
|
import { turnUsageToModelUsage } from './downstream/turnUsageToModelUsage.js';
|
|
4
4
|
// D-3(0.66.0):终局对账臂与终帧超集键共用**同一个**成本读器(readRunCostFacts)。
|
|
5
|
-
import { readRunCostFacts, terminalToSdkResult } from './downstream/terminalToSdkResult.js';
|
|
5
|
+
import { readRunCostFacts, terminalToSdkResult, TOOL_INPUT_JOIN_MAX_CALLS } from './downstream/terminalToSdkResult.js';
|
|
6
6
|
import { coerceOutput, publishSubagentContentEvent } from '../subagentContentStore.js';
|
|
7
7
|
/**
|
|
8
8
|
* 409 session-busy 拒绝的 **canonical errorCode**([2377]C-1,server main `049ff2c`,随 5.0.0 发)。
|
|
@@ -414,6 +414,25 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
414
414
|
// 把**本流快照**同时交给两个投影口(终帧与对账臂),两面读同一份、且谁都读不到别人那份。
|
|
415
415
|
// 🔴 **不是**靠「终帧那一拍把 ctx 上那一位清掉」修的:并发的两条流会互相覆盖那次清除。
|
|
416
416
|
let usageMissingObserved = false;
|
|
417
|
+
// ── 0.73.4 —— 本流 `toolCallId → tool_start.args` 快照(终帧拒绝清单第三键的来源)──
|
|
418
|
+
// 拒绝账本行带得出「哪一次调用」(`toolCallId`),带不出那次调用的入参对象;入参在同一条流的
|
|
419
|
+
// `tool_start` 帧上。与上面两格同款:**本函数的局部量**,终局那一拍作为按次入参交给终帧铸点,
|
|
420
|
+
// 绝不挂 `ctx`(调用方可复用给多条流 ⇒ 上一条流的入参会 join 到下一条流的拒绝行上)。
|
|
421
|
+
// 只存帧上那只对象的引用(转录里的 `tool_use.input` 是同一只),不复制;有条数上限。
|
|
422
|
+
// 同一个 id 见到两份**不同**的入参 ⇒ 判不出被拒的是哪一份 ⇒ 该 id 永久出表(不回头)。
|
|
423
|
+
const toolInputByCallId = new Map();
|
|
424
|
+
const toolInputAmbiguous = new Set();
|
|
425
|
+
let toolInputOverflowed = false;
|
|
426
|
+
const sameArgs = (a, b) => {
|
|
427
|
+
if (a === b)
|
|
428
|
+
return true;
|
|
429
|
+
try {
|
|
430
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
417
436
|
for await (const ev of events) {
|
|
418
437
|
// event-id idempotency — drop a re-seen durable seq (contract 02 §1.1).
|
|
419
438
|
const seq = eventSeq(ev);
|
|
@@ -428,6 +447,25 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
428
447
|
}
|
|
429
448
|
seen.add(seq);
|
|
430
449
|
}
|
|
450
|
+
// 0.73.4:记本流的 `tool_start` 入参(主会话与子代内部调用同记 —— 拒绝账本是整条 run 的)。
|
|
451
|
+
if (ev.type === 'tool_start') {
|
|
452
|
+
const callId = ev.toolCallId;
|
|
453
|
+
if (typeof callId === 'string' && callId.length > 0 && !toolInputAmbiguous.has(callId)) {
|
|
454
|
+
const args = ev.args;
|
|
455
|
+
if (!toolInputByCallId.has(callId)) {
|
|
456
|
+
// 溢出是单向闩:一旦有调用没记上,之后的新 id 一律不记(出表腾出的空位不回填 ——
|
|
457
|
+
// 否则一只没记上首份入参的 id 会在重放时被当成首见,两份不同入参的判据就漏了)。
|
|
458
|
+
if (toolInputByCallId.size >= TOOL_INPUT_JOIN_MAX_CALLS)
|
|
459
|
+
toolInputOverflowed = true;
|
|
460
|
+
if (!toolInputOverflowed)
|
|
461
|
+
toolInputByCallId.set(callId, args);
|
|
462
|
+
}
|
|
463
|
+
else if (!sameArgs(toolInputByCallId.get(callId), args)) {
|
|
464
|
+
toolInputByCallId.delete(callId);
|
|
465
|
+
toolInputAmbiguous.add(callId);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
431
469
|
// C1 — SUBAGENT content divert (service 1.89 forwardSubagentEvents): a CONTENT event stamped with
|
|
432
470
|
// parentToolCallId belongs to a SUBAGENT run (EventIdentity contract: present ONLY on sub-agent
|
|
433
471
|
// events) — side-channel it to the 查看态 transcript store and keep it OFF the main pipeline
|
|
@@ -913,7 +951,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
913
951
|
// 字节的证据都不是;按那句话办,一条账数得全的 run 会被渲成「≥」并被宿主持久化成不完整状态
|
|
914
952
|
// (失效方向在这里**不是**安全的那一侧,它是在断言一件没发生的事)。
|
|
915
953
|
// ⇒ 观测位改为 per-stream 局部量,终帧按值收(第三参),与上面对账臂读的是同一份。
|
|
916
|
-
const resultFrame = terminalToSdkResult(ev, ctx, { usageMissingObserved, nestedUsageByTask });
|
|
954
|
+
const resultFrame = terminalToSdkResult(ev, ctx, { usageMissingObserved, nestedUsageByTask, toolInputByCallId });
|
|
917
955
|
// 0.72.12 CC-50(test [7541] G2-d「铸了就要有人读」):结果帧上的 `_sema_outcome:'unknown'`(终态记录整个
|
|
918
956
|
// 缺席 / 认不出的终态词;铸点与取舍见 terminalToSdkResult `ErrorResultParts.outcomeUnknown`)在**这里**被消费 ——
|
|
919
957
|
// 交互面合成一行 `Outcome unknown:` 终局行(句子与结果帧 `errors[0]` 单源),壳零接线即可见;它**不是**
|
|
@@ -91,6 +91,8 @@ export interface ApprovalsFeedStats {
|
|
|
91
91
|
snapshots: number;
|
|
92
92
|
/** 0.72.14:对账节拍真取件的次数(与 `polls` 分开记 —— 它不属于回落轮询腿)。 */
|
|
93
93
|
reconciles: number;
|
|
94
|
+
/** 0.73.4:「欠一次看」的重试取件次数(见 `ApprovalsFeedOptions.pushTakeRetryMs`)。 */
|
|
95
|
+
pushTakeRetries: number;
|
|
94
96
|
}
|
|
95
97
|
/**
|
|
96
98
|
* 0.72.14 **对账节拍**:悬挂的流内 ask 出现 / 结算时,较老的引擎不在 `approvals.stream` 上发任何事件 ⇒ push 腿连着也
|
|
@@ -132,6 +134,16 @@ export interface ApprovalsFeedOptions {
|
|
|
132
134
|
* · **`refresh()`** 不受管 —— 那是端自己按下的显式动作。
|
|
133
135
|
*/
|
|
134
136
|
shouldPoll?: () => boolean;
|
|
137
|
+
/**
|
|
138
|
+
* 0.73.4:**欠一次看**的重试基础间隔(ms;缺省 1000;`<= 0` = 关,与 0.73.3 之前同)。
|
|
139
|
+
*
|
|
140
|
+
* push 腿收到事件 = 引擎说「变了」;那一次 `list()` 若失败(抛出,或回体读不懂),这个「变了」就没人去看:
|
|
141
|
+
* 其后的 heartbeat 不触发取件,没有别的事件的话,那张待批的卡一直不出现。⇒ 任何一次 push 侧取件
|
|
142
|
+
* (起手那次、事件触发的、`refresh()`)没看成,就记一笔欠账并按 base、2×base、4×base … 退避重试,
|
|
143
|
+
* 间隔封顶 30 s 与 base 的较大者,**不按次数放弃**(有人可能正在等这张卡);**比欠账更晚发起**的任何一次
|
|
144
|
+
* 取件看成(哪条腿都算)即销账、撤定时器、间隔回到 base;更早发起、迟到回来的成功不销账。只在 push 模式记账 —— 轮询模式下轮询腿自己会再来。
|
|
145
|
+
*/
|
|
146
|
+
pushTakeRetryMs?: number;
|
|
135
147
|
/** 0.72.14:对账节拍(见 {@link ApprovalsFeedReconcileOptions})。缺省不起。 */
|
|
136
148
|
reconcile?: ApprovalsFeedReconcileOptions;
|
|
137
149
|
signal?: AbortSignal;
|
|
@@ -69,7 +69,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
69
69
|
let takeSeq = 0;
|
|
70
70
|
let lastObservedSeq = 0;
|
|
71
71
|
const stats = {
|
|
72
|
-
pushEvents: 0, heartbeats: 0, polls: 0, listErrors: 0, streamFailures: 0, snapshots: 0, reconciles: 0,
|
|
72
|
+
pushEvents: 0, heartbeats: 0, polls: 0, listErrors: 0, streamFailures: 0, snapshots: 0, reconciles: 0, pushTakeRetries: 0,
|
|
73
73
|
};
|
|
74
74
|
/** 域词表-14 收编:「怎么调 unref」的单一实现见 unrefTimer.ts,本函数只是就地起个短名。 */
|
|
75
75
|
function arm(t) {
|
|
@@ -101,6 +101,56 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
101
101
|
}
|
|
102
102
|
return verdict;
|
|
103
103
|
}
|
|
104
|
+
// ── 0.73.4 欠一次看(见 `pushTakeRetryMs` 头注)──
|
|
105
|
+
const pushTakeRetryBaseMs = opts?.pushTakeRetryMs ?? 1000;
|
|
106
|
+
const pushTakeRetryMaxMs = Math.max(pushTakeRetryBaseMs, 30_000);
|
|
107
|
+
let pushTakeRetryTimer;
|
|
108
|
+
let pushTakeRetryDelayMs = pushTakeRetryBaseMs;
|
|
109
|
+
/** 欠账锚在**发起序号**上:最近一次没看成的 push 侧取件是第几号(0 = 不欠)。 */
|
|
110
|
+
let owedSinceSeq = 0;
|
|
111
|
+
/** 已经看成的最晚发起序号(与欠账独立记):比它更早发起的失败是迟到的旧账,不记(对偶的乱序形)。 */
|
|
112
|
+
let lastLookedSeq = 0;
|
|
113
|
+
/**
|
|
114
|
+
* 销账只认**比欠账更晚发起**的取件:更早发起的那一次看的是失败之前的世界,它迟到的成功说明不了
|
|
115
|
+
* 「引擎说变了的那一下」已经有人看过(乱序是真的会发生的:慢 list / `refresh()` 与 push 重取撞车)。
|
|
116
|
+
*/
|
|
117
|
+
function noteTakeLooked(seq) {
|
|
118
|
+
if (seq > lastLookedSeq)
|
|
119
|
+
lastLookedSeq = seq;
|
|
120
|
+
if (seq <= owedSinceSeq)
|
|
121
|
+
return;
|
|
122
|
+
owedSinceSeq = 0;
|
|
123
|
+
if (pushTakeRetryTimer !== undefined) {
|
|
124
|
+
clearTimeout(pushTakeRetryTimer);
|
|
125
|
+
pushTakeRetryTimer = undefined;
|
|
126
|
+
}
|
|
127
|
+
pushTakeRetryDelayMs = pushTakeRetryBaseMs;
|
|
128
|
+
}
|
|
129
|
+
function noteTakeMissed(via, seq) {
|
|
130
|
+
if (via !== 'push' || pushTakeRetryBaseMs <= 0)
|
|
131
|
+
return;
|
|
132
|
+
if (seq < lastLookedSeq)
|
|
133
|
+
return; // 更晚发起的一眼已经看成 ⇒ 这次迟到的失败不欠
|
|
134
|
+
if (seq > owedSinceSeq)
|
|
135
|
+
owedSinceSeq = seq;
|
|
136
|
+
if (stopped || ac.signal.aborted)
|
|
137
|
+
return;
|
|
138
|
+
if (pushTakeRetryTimer !== undefined)
|
|
139
|
+
return; // 幂等:已经有一只在排
|
|
140
|
+
const delay = pushTakeRetryDelayMs;
|
|
141
|
+
pushTakeRetryDelayMs = Math.min(pushTakeRetryDelayMs * 2, pushTakeRetryMaxMs);
|
|
142
|
+
pushTakeRetryTimer = arm(setTimeout(() => {
|
|
143
|
+
pushTakeRetryTimer = undefined;
|
|
144
|
+
if (stopped || ac.signal.aborted)
|
|
145
|
+
return;
|
|
146
|
+
stats.pushTakeRetries++;
|
|
147
|
+
// 欠账不随模式走:期间若降级到轮询,这一次照取(轮询腿可能被 shouldPoll 关着)。
|
|
148
|
+
void take('push').catch(e => {
|
|
149
|
+
// 重试自己逃逸(回体坏形):欠账已在 take() 内续记并再排,这里只接住 rejection。
|
|
150
|
+
hostLog('debug', `approvalsFeed: push-take retry escaped (unreadable body?) — still owed: ${String(e)}`);
|
|
151
|
+
});
|
|
152
|
+
}, delay));
|
|
153
|
+
}
|
|
104
154
|
async function take(via,
|
|
105
155
|
/** [F4957-1 二审] 发起这次取件时的轮询代际(只有轮询腿传;push/refresh 不受代际管)。 */
|
|
106
156
|
leg) {
|
|
@@ -118,6 +168,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
118
168
|
if (reading.kind === 'malformed') {
|
|
119
169
|
stats.listErrors++;
|
|
120
170
|
hostLog('debug', `approvalsFeed: list() body has an unreadable livePending section (${via} leg) — snapshot not committed`);
|
|
171
|
+
noteTakeMissed(via, seq);
|
|
121
172
|
return;
|
|
122
173
|
}
|
|
123
174
|
if (reading.kind === 'present') {
|
|
@@ -129,11 +180,25 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
129
180
|
catch (e) {
|
|
130
181
|
stats.listErrors++;
|
|
131
182
|
hostLog('debug', `approvalsFeed: list() failed (${via} leg): ${String(e)}`);
|
|
183
|
+
noteTakeMissed(via, seq);
|
|
132
184
|
return;
|
|
133
185
|
}
|
|
134
186
|
if (stopped)
|
|
135
187
|
return;
|
|
136
|
-
|
|
188
|
+
// 🔴 摘要排在销账**之前**:回体坏形(例:`pending` 不是数组)会让它抛出、整次取件逃逸(各调用点自己接盘,
|
|
189
|
+
// 轮询腿的连续逃逸计数靠的就是这条路)。先销账再抛 = 一份读不懂的回体把欠账销了,之后再没人来看。
|
|
190
|
+
// 逃逸本身也是「没看成」:在**这里**统一记欠账再原样抛出,四个调用点(起手 / 事件 / refresh / 重试)不必各记各的。
|
|
191
|
+
let d;
|
|
192
|
+
try {
|
|
193
|
+
d = digestOf(rows, live);
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
noteTakeMissed(via, seq);
|
|
197
|
+
throw e;
|
|
198
|
+
}
|
|
199
|
+
// 看成了(回体读得懂)⇒ 销掉**更早发起**的欠账(判据在发起序号上,见 noteTakeLooked;与下面两道提交闸无关:
|
|
200
|
+
// 销账问的是「失败之后有没有人再看过」,提交闸问的是「这份结论还新不新」)。
|
|
201
|
+
noteTakeLooked(seq);
|
|
137
202
|
// 🔴 [F4957-1 二审 finding②] 代际闸必须排在**提交之前**:下面四行(lastDigest / revision /
|
|
138
203
|
// last / onSnapshot)一落,视图就已经被改了 —— 守卫排在 take() 之外只挡得住计数与续排,挡不住
|
|
139
204
|
// 回滚。乱序是真的会发生的:旧 poll 在途 → push 重连并发布新快照 → 旧 poll 才带着**更老**的
|
|
@@ -407,6 +472,8 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
407
472
|
clearTimeout(retryTimer);
|
|
408
473
|
if (reconcileTimer !== undefined)
|
|
409
474
|
clearTimeout(reconcileTimer);
|
|
475
|
+
if (pushTakeRetryTimer !== undefined)
|
|
476
|
+
clearTimeout(pushTakeRetryTimer);
|
|
410
477
|
opts?.signal?.removeEventListener('abort', onOuterAbort);
|
|
411
478
|
ac.abort();
|
|
412
479
|
},
|
package/dist/toolResult.js
CHANGED
|
@@ -896,6 +896,10 @@ modelText) {
|
|
|
896
896
|
};
|
|
897
897
|
}
|
|
898
898
|
const wfName = typeof inner?.name === 'string' && inner.name.length > 0 ? inner.name : undefined;
|
|
899
|
+
// 0.73.4(CC-63):**run 级**错误码。引擎在这张结构卡的**顶层**铸 `errorCode`(与 `completionId` 同层;有值才带,
|
|
900
|
+
// 缺席 = 这条 run 没有码),今天只有 workflow 形带。它不是 `agent_runs[].errorCode`(per-agent,住在模型面 JSON 里),
|
|
901
|
+
// 也不在内层 `details` 上。开集原词透传;缺席 / 空串 / 非字符串 ⇒ 键不铸,绝不编码。CC 的 task 形没有这一格 ⇒ 超集键。
|
|
902
|
+
const runErrorCode = typeof s.errorCode === 'string' && s.errorCode.length > 0 ? s.errorCode : undefined;
|
|
899
903
|
let output = bodyText ?? '';
|
|
900
904
|
if (taskType === 'workflow') {
|
|
901
905
|
// 终态轮询卡经过投影 = 模型 in-band 已收到结局 ⇒ 预 seed runId 去重(补发通道对同 runId 丢弃)。
|
|
@@ -916,6 +920,7 @@ modelText) {
|
|
|
916
920
|
description: wfName ?? taskId,
|
|
917
921
|
output,
|
|
918
922
|
...(error !== undefined ? { error } : {}),
|
|
923
|
+
...(runErrorCode !== undefined ? { _sema_error_code: runErrorCode } : {}),
|
|
919
924
|
},
|
|
920
925
|
},
|
|
921
926
|
};
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
| 项 | 值 | 真源 |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| 本包 | `@sema-agent/client-core` **0.73.
|
|
22
|
+
| 本包 | `@sema-agent/client-core` **0.73.4**(本批发布版 = **patch**:终帧 `permission_denials` 第三键 ⇐ 同一条流 `tool_start` 入参按 `toolCallId` join(三道闸;join 不上留 sema 载体;🔴 更正 0.73.2「自动升级收口」一句)+ 审批 feed push 侧取件失败的欠账退避重试;型面纯 additive、公面 1033、peer 不动;§66。上一版 0.73.3 = **patch**:CC-61 sdk 9.7.1 提货 + `capabilities.approvalsStreamLive` 四态读口(缺席 = 判不出;`livePendingNeedsReconcile` 只在引擎明说会推时才让对账节拍降兜底)+ CC-60 五条模型面文案锚包侧半场(cursorSemantics 机读位 / result 行注记 / bash 首行两变体与良性槽 / 通知 seed 并集 / parked 臂);型面纯 additive、公面 1032、peer 不动;§65。上一版 0.73.2 = **patch**:CC-59 悬挂 ask 全卡 —— `livePending[].frame`(server ≥7.87.0)两道闸窄读进视图,在场即走流内帧腿同一条路(入参可见、改写可转发),缺席保持盲卡;CC-34 收口(引擎半场随 7.87.0 到货,判据交黑盒);型面纯 additive、公面 1026、peer 不动;§64。上一版 0.73.1 = **patch**:CC-57 `decidePlanReview` outcome 尾句按「决断有没有生效」四态选(单铸点 `planReviewOutcomeTail`;此前本地拒发仍让模型汇报已拒绝)+ CC-56 cancel 的 409 按机器码分家(`cancelConflictFromError` 三形 / 新安全码 `cancel_run_not_running`)与提交面 429 用量窗读口 + 人话;型面纯 additive、公面 1026、peer 不动;§63。上一版 0.73.0 = **minor,型面 BREAKING**:fleet 视图八个数值键改可选 —— CC-52 `FleetTaskView.elapsedMs` / `tokens` / `queuedCount` 与 `FleetWorkflowView.doneCount` / `totalCount` / `failedCount` / `tokens` / `elapsedMs` 不再 `?? 0`(有键 = 真值含真 0,缺键 = 不知道;逐键缺席语义见 §62b)+ 台账补偿链无基数不铸;公面导出名零增减 1019、零 wire 变化、peer 不动;§62。上一版 0.72.14 = **patch**:悬挂的流内 ask 拉半场 —— CC-55 `startApprovalsFeed` 快照加 `livePending` 段并进变化摘要(键缺席 = 未报,不铸空)+ 视图 / 去重账 / 与流内帧腿同链的决断口(入参不可见上卡明说、改写不转发)+ 宿主闸对账节拍 + 待决计数读口(不知道 = null);型面纯 additive、公面 1019、peer 不动;§61。上一版 0.72.13 = **patch**:server 7.86.0 提货 —— CC-46 plan_review `permissionModeAfter` 发口(三选卡双闸:宿主声明只读起步 ∧ 引擎 ≥7.86.0;400 field_conflict 两种处置)+ CC-54 `capabilities.executionLane` 四态读面(键缺席 ≠ 不是本机车道;`toolsRunHere` 判据归包)+ CC-53 detach 版本闸三态;型面 additive(另撤回 0.72.12 的主通道 `absent` 臂,改独立缺席订阅口)、公面 1011、peer 不动;§60;**预告 0.73.0 BREAKING**:fleet 视图数值键改可选。上一版 0.72.12 = **patch**:「缺席被折成正向终态」按类清剿 —— CC-50 M1 头条(fleet 行缺席改发 `absent` 不再铸 `end{isError:false}`)+ 六站一次改齐(缺席 ⇒ `unknown`)+ 结果边界 fail-closed 取舍 + `_sema_outcome` + `Outcome unknown:` 行 + CC-51 M6(default 臂不再谎报 running)+ 普查门 + DS-06;型面 additive、公面 993、peer 不动;行为面三处真变化见 CHANGELOG 取舍段;§59。上一版 0.72.11 = **patch**:core 7.23.0 提货 —— CC-49 通告码 66 → 67 `config.write_protection_unresolved`(operator;兄弟码 `target_view_absent` = 没有视图,这枚 = 有视图解析不出)+ `permission_denials` 自动升级腿通电(`gates[].toolCallId` ⇒ `tool_use_id`;CC-34 引擎半场);型面零变化、公面 989、peer 不动、devDep core ~7.23.0;§58。上一版 0.72.10 = **patch**:CC-41 leader run 终局 `needs_human` + `result.conflict` 三形读面(none 缺席不可分 / unreadable 在场读不懂 / conflict;`salvaged[]` 原样给落盘口;措辞单源不提重试);型面纯 additive、公面 989、peer 不动;§57。上一版 0.72.9 = **patch**:server 7.85.0 提货 —— CC-30 会话内 MCP re-dial 消费口(`reconnectMcpServer` 三态 / unsupported 恒五键 / 交易文案单源)+ CC-39② memory-status `autoConsolidationArmed` 三态 + CC-45 resume 补充句渲保留秒数;型面纯 additive、公面 985、peer 不动;§56。上一版 0.72.8 = **patch**:清账三票 —— `memory:"off"` 请求键(CC-39③,server 7.84.0 S-405)+ `runStream` 第二条中性臂 `Model output error:`(CC-43)+ 注入件撞活 run 自动 steer 零卡(CC-44);型面纯 additive、公面 977、peer 不动;§55。上一版 0.72.7 = **patch**:core 7.22.0 提货 —— 通告码 66 `mcp.server_redialed` + 读器 `readMcpServerRedialed` + `wiring_manifest.readDeny` 第九段 `_sema_readDeny`(缺席 = 没有内置行判这条腿);型面纯 additive、公面 971、peer 不动、devDep core ~7.22.0;§54。上一版 0.72.6 = **patch**:cli L-379 包侧半场 —— `steerFollowIntent` steer 后续听意图三态(tail / after-decision / after-wake)+ `activeRunSelfHealRow` 续听声明第五参;型面纯 additive、公面 969、peer 不动;§53。上一版 0.72.5 = **patch**:server 7.82.1 提货 —— `capabilities.webSearch.backend` 部署默认 WebSearch 后端四态读面(键缺席 = 老引擎判不了 ≠ `none`);型面纯 additive、公面 968、peer 不动;§52。上一版 0.72.4 = **patch**:core 7.21.x 三件经 server 7.82.0 提货 —— `wiring_manifest.autoConsolidation` 第八段 `_sema_autoConsolidation`(缺席 = indeterminate 不折 false);型面纯 additive、公面 962、peer 不动;§51。上一版 0.72.3 = **patch**:core #863 记忆单系统半场 ① —— `projectAgentsForWire` 投影 `agents[].memory`(user|project|local 三词同映射引擎平面 agent:<type>);型面纯 additive、公面 962、零 wire 新键、peer 不动;§50。上一版 0.72.2 = **patch**:workflow 视图两票 —— `LiveWorkflowController.resolution()` 六态(snapshot() 的 null 三义拆开)+ `noteOwnWorkflowRun` own 台账同拍登记;型面纯 additive、公面 961、零 wire 键、peer 不动;§49。上一版 0.72.1 = **patch**:server 7.80.1 提货 —— 409 `conflict.resume_context_unavailable` 结构读数(`staleAfterSec` / `runId`;sdk 9.6.0 尚未带键,经 sdk 今天恒缺席)+ `SkillSpec.baseDir`(只在本机车道带);型面纯 additive、公面 960、peer 不动;§48。上一版 0.72.0 = **minor**:型面 BREAKING —— 39 个零消费导出退出公面(CC-32 退役批,[7419] §45 预告 / cli [7422] 同意 / web-client·desktop 过节拍沉默 = 同意;名单见 §47a S-1);运行期零 BREAKING、公面 956、peer 不动、devDep core ~7.20.1;随批:已退役名账 `removed` + export-liveness 门 G 段 + 冻结账门 ⑧c;§47。上一版 0.71.4 = **patch**:core 7.20.1 提货 —— `ENGINE_NOTICE_CODES` 65 + `wiring_manifest.writeProtection` 第七段 `_sema_writeProtection`;型面纯 additive、公面 995、peer 不动、devDep core `~7.20.1`;§46。上一版 0.71.3 = **patch**:门体系清账批 —— CC-24 export-liveness 门 + 39 个导出退役预告(0.72.0 删,表态制)/ CC-26 `as unknown as` 铸链棘轮 / CC-25 冻结账门 ⑧ 段 / CC-06 B-081 包侧半场(终帧 `_sema_effective_reasoning` / `_sema_effective_memory_scopes` + resume reopen 族 + `routePairingVerdict`);型面纯 additive、公面 986 → 995、peer 不动;§45。上一版 0.71.2 = **patch**:接入方验收两票归包 —— CC-28 B-132 decide 拒体三句去内部车道词 / CC-29 B-133 committedUuids 唯一性(重复 ⇒ 形 f);零型面变化、公面 986、peer 不动;§44。上一版 0.71.1 = **patch**:接入档打回修正 —— CC-27 `ApprovalCardRequest.readRootCandidate?` 帧→卡透传位 + 新导出型 `ReadRootCandidateView`;型面纯 additive、公面 986 不变、peer 不动;§43。上一版 0.71.0 = **minor**:型面 BREAKING 六件 —— peer sdk 地板 >=9.6.0 + agent-types >=0.3.0 / `SkillsListingTruncatedFactsView` 删 `requestedBudgetBytes?`·`laneClamped?` / `WiringManifestMcpEntry` → `WiringManifestMcpEntryView` / `MAX_AGENT_SKILLS` 删 / `decide.workflow_host_not_parked` 退役;运行期零 BREAKING。内容:core 7.20.0 提货(闭集 +5 六十四码)/ 🔴 CC-16 `text_segment_end.committedUuids[]` / CC-08 全五件(tool_disclosure / tool_progress 两 chrome 臂、wiring_manifest tools·hooks·lsp 进切片、tool_roster_delta 接上、context_usage 进切片、`readRootCandidate` 镜像)/ CC-19 `mcpEngineLegPresence` 归包 / CC-20 not_parked clean-cut;公面 986;§42 / 42y 八条)|
|
|
23
23
|
| peer:wire 契约 | `@sema-agent/sdk` **>=9.6.0**(value-level,非 type-only;**0.71.0 抬版**:`tool_disclosure` / `tool_progress` 两帧型与 `ToolApprovalFrame.readRootCandidate` 自 9.6.0 起;0.69.0 曾抬到 >=9.4.0(`reasoning_end` / `lastLegMcp`),0.60.0 抬到 >=8.4.0;地板由 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 与 public-surface 门 C-CC-3「不低于见证到的最低实装版」双钉)| `package.json` `peerDependencies` |
|
|
24
24
|
| peer:会话词汇表 | `@sema-agent/agent-types` **>=0.3.0**(type-only,零运行时;0.71.0 抬版:devDep 与 peer 同步,type-superset 门 ④ 撞名预警对着 0.3.0 的 .d.ts 求值) | 同上 |
|
|
25
25
|
| runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
|
|
26
|
-
| 公开导出面 | **
|
|
26
|
+
| 公开导出面 | **1033** 个运行期符号(+ 49 个测试钩;= 0.73.4 的值:相对 0.73.3 +1 —— `TOOL_INPUT_JOIN_MAX_CALLS`,见 §2b 域 1;1032 = 0.73.3 的值:相对 0.73.2 +6 —— CC-61 六件,名单见 §2b 域 9;1026 = 0.73.2 的值(导出名零增减);= 0.73.1 的值:相对 0.73.0 +7 —— CC-56 六件 / CC-57 一件,名单见 §2b 域 3 / 15;1019 = 0.73.0 的值(导出名零增减,BREAKING 在类型面);= 0.72.14 的值:相对 0.72.13 +8 —— CC-55 八件,名单见 §2b 域 3;1011 = 0.72.13 的值:相对 0.72.12 +18 —— CC-46 八件 / CC-54 六件 / CC-53 两件 / CC-50 缺席口两件,名单见 §2b 域 3 / 4 / 9 / 10;993 = 0.72.12 的值(测试钩 45):相对 0.72.11 +4 —— CC-50 `OUTCOME_UNKNOWN_ROW_PREFIX` / `isOutcomeUnknownRowText` / `TASK_NOTIFICATION_TERMINAL_STATUSES` / `isTaskNotificationTerminalStatus`;989 = 0.72.10 的值:相对 0.72.9 +4 —— CC-41 `LEADER_RUN_STATUSES` / `LEADER_REJ_HEAD_DISPLAY_MAX` / `projectLeaderConflict` / `leaderConflictDetail`;985 = 0.72.9 的值:相对 0.72.8 +8 —— CC-30 `MCP_RECONNECT_OUTCOMES` / `MCP_RECONNECT_TRANSACTION_NOTICE` / `projectMcpReconnectResult` / `classifyMcpReconnectFailure` / `reconnectMcpServer` / `projectMcpReconnectCapability` / `mcpReconnectOutcomeDetail`、CC-39② `readAutoConsolidationArmed`;977 = 0.72.8 的值:相对 0.72.7 +6 —— CC-39③ `MEMORY_OFF` / `memoryOffDeclarationField` / `memoryOffDeclared`、CC-43 `MODEL_OUTPUT_ERROR_PREFIX` / `isModelOutputErrorText` / `isModelOutputErrorRowText`;971 = 0.72.7 的值:相对 0.72.6 +2 —— CC-42 core 7.22.0 提货 `MCP_REDIAL_OUTCOMES` / `readMcpServerRedialed`;969 = 0.72.6 的值:相对 0.72.5 +1 —— CC-40 `steerFollowIntent`;968 = 0.72.5 的值:相对 0.72.4 +6 —— CC-31 server 7.82.1 提货 `capabilities.webSearch.backend` 四态读面:`WEB_SEARCH_BACKEND_NONE` / `projectWebSearchBackendCapability` / `observedWebSearchBackend` / `noteEngineCapsForWebSearchBackend` / `forgetWebSearchBackendReading` / `webSearchBackendDoctorDetail`;962 = 0.72.3 的值:相对 0.72.2 +1 —— CC-39① `AGENT_MEMORY_WORDS`;0.72.2 相对 0.72.1 +1 —— CC-37 `noteOwnWorkflowRun`;0.72.1 相对 0.72.0 +4 —— CC-33 server 7.80.1 提货:`RESUME_CONTEXT_UNAVAILABLE` / `resumeContextUnavailableFromError` / `resumeContextUnavailableContent` / `skillBaseDirOf`;0.72.0 相对 0.71.4 −39 —— CC-32 退役批删 export-liveness 登记 retire 的 39 个零消费导出(23 常量 + 16 函数,名单见 §47);995 = 0.71.3 的值:相对 0.71.2 +9 —— CC-06 `readEffectiveReasoning` / `readEffectiveMemoryScopes` / `resumeReopenFromError` / `resumeReopenContent` / `routePairingVerdict` 五函数 + `RESUME_REOPEN_CODES` / `RESUME_ENV_FAILED` / `RESUME_TOOL_UNAVAILABLE` / `RESUME_TOOL_CONTRACT_MISMATCH` 四常量;986 = 0.71.0 的值:相对 0.70.2 −`MAX_AGENT_SKILLS` −`DECIDE_WORKFLOW_HOST_NOT_PARKED` +`projectToolRosterDelta` +`mcpEngineLegPresence`;986 = 0.70.0 的值:相对 0.69.2 **一件 additive(`readSkillsListingTruncated`,§39)、零删除**;985 = 0.69.2 的值:相对 0.69.1 **三件 additive、零删除**(CC-13 `isSubFlowSegmentEnd` / `SEGMENT_END_IDENTITY_KEYS` / `droppedWhySentence`,§38);982 = 0.69.0 未发布版的值(原排 0.68.3,因 peer 地板抬升改 minor)= 相对 0.68.2 **十七件 additive、零删除**:`fetchMcpPanel`(CC-03 补件,§36e)+ `readWorkflowResumeAdmissionIncomplete`(CC-05 core 7.18.0 #755)+ `installCoreValuePorts`/`hasCoreValuePorts`/`hasCoreValuePort`/`coreValuePortMisses`/`CORE_VALUE_PORT_NAMES` + 十读口(CC-07 core 值级端口,§36d);0.68.2 相对 0.68.1 十三件 additive:`SEMA_SEGMENT_ID_KEY`(CC-01 段身份键,§35f)/ `projectMcpPanel`+`mcpPanelLastLegDetail`+`projectMcpSection`(CC-03 `/mcp` 面板读视图,§35g)/ `resolveTextSegmentAuthority`/`createTextSegmentLedger`(L-318 `text_end` 权威段替换的判决单源 + print 形段账,详见 §35b/§35c)/ `memoryCaptureDeclarationField`/`MEMORY_CAPTURE_OFF`(L-316 会话级隐私声明的字面唯一铸点,§35d)/ `engineUrl`/`probeHealth`(L-61 件④ SDK 纯工具面原样转口,§35d)/ `AgentClient`/`isApprovalRequestFrameV1`/`isApprovalRevokeFrameV1`(L-61 存量清零:SDK **wire 面**原样转口,§35e)。`0.68.1`/`0.68.0` 是 **952**,那批是相对 0.67.2 **二十件 additive、🔴 两件删除(BREAKING)**:additive = `TERMINAL_CAUSE_KINDS`/`isTerminalCauseKind`/`RUN_TERMINAL_NOT_SUCCESS_STATUSES`/`RUN_TERMINAL_STATUSES`(L-247 终态词两表分源,详见 §33b)/ `SEMA_SUBAGENT_USAGE_PARTIAL_KEY`/`subagentUsageIsPartial`(L-244 子代下界位自有名,§33g)/ `ASK_UNRESOLVABLE_CAUSES`/`readAskUnresolvable`/`DURABLE_MANDATE_SOURCES`/`readDurableGateUnavailable`(core 7.17.0 #709 ①② 两只通告事实窄读器,§33c)/ `WORKFLOW_PARK_TRUTH_UNREADABLE`/`WORKFLOW_PARK_NOT_PENDING`/`WORKFLOW_PARK_BINDING_BROKEN`/`WORKFLOW_PARK_REQUIRES_RUN_STORE`(四个单码常量 🔧 0.72.0 退役)/`WORKFLOW_PARK_REFUSAL_CODES`/`isWorkflowParkRefusalCode`/`readWorkflowParks`(core 7.17.0 #652 park 真相四码 + parks 三键读器,§33d)/ `ENGINE_STOP_REASONS`/`engineStopReasonToCc`(L-246 A13 停止原词映射下沉,§33g)/ `readCancelRequested`(L-230 读面,§33f);🔴 **删除两件** `TERMINAL_NOT_SUCCESS_STATUSES`/`TERMINAL_STATUSES`(随 L-247 两表分源更名成 `RUN_TERMINAL_*`,clean-cut **不留别名** —— 留一个别名等于让端可以继续按合起来的那张表读,分源就白做了,详见 §33b)。`0.67.0` 是 **934**,那批是相对 0.66.0 **七件 additive、🔴 两件删除**:additive = `CLASSIFIER_DENY_CAUSES`/`isClassifierDenyCause`/`classifierDenyCauseOf`/`classifierDenyCauseDetail`(core 7.14.0 退役 `classifierUnavailable` 之后的**拒绝面**继任读器与措辞,详见 §32b)/ `RULE_STORE_UNREADABLE_KINDS`/`isRuleStoreUnreadableKind`/`ruleStoreUnreadableDetail`(core 7.14.0 #688 C3 的规则店机制位三件,详见 §32d);🔴 **删除两件** `classifierUnavailableOf`/`classifierUnavailableDetail`(随 core 7.14.0 的 Retired keys 表整族退役,clean-cut 不留别名,详见 §32b;同批删的还有**型** `ClassifierUnavailableView`、卡位与帧位 `classifierUnavailable`、以及一条恒真的型面钉 `_askOriginWordsPin`)。`0.66.0` 是 **929**,那批是一件 additive、零删除:`readRunCostFacts`(终局成本事实的唯一读器:`costBreakdown` / `nested` 窄读 + 对账三段与两个判别位,终帧超集键与 chrome 对账臂共用它;详见 §31c);已发的 **0.65.x 十一件 additive、两件删除**:additive = `DECIDE_WORKFLOW_HOST_UNKNOWN`/`DECIDE_WORKFLOW_HOST_NOT_PARKED`/`DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED`/`DECIDE_WORKFLOW_LANE_CODES`/`readDecideReceipt`/`decideAcceptedNotResolved`/`decideRefusalFromError`(B-070 `/decide` 回执读面与三码人话,详见 §30h)/ `TERMINAL_NOT_SUCCESS_STATUSES`/`isTerminalNotSuccess`/`TERMINAL_STATUSES`/`isTerminalStatus`(L-215② 终局词汇单铸谓词,详见 §30j);🔴 **删除两件** `AUTO_MODE_BREAKER_CAUSES`/`classifierBreakerOf`(熔断族随 core 7.12.0 的 Removed(BREAKING) 整只退役,clean-cut 不留别名,详见 §30k②;同批删的还有**型** `ClassifierBreakerView` 与 `ClassifierStatusView.breaker` 一位)。`0.64.2` 是 **919**,那批是一件 additive、零删除:`readDenialLimitFallback`(限额回落卡的窄读器上公面,壳侧同判据副本退役,详见 §29③);`0.64.0` 是 **918**,那批是件①+件② 十三件 additive、零删除:`CLASSIFIER_STATUS_STATES`/`classifierBreakerOf`/`classifierStatusOf`/`classifierStatusDetail`(分类器状态面,详见 §28②)/ `THINKING_FORMATS`/`THINKING_FORMAT_WIRE_TWINS`/`THINKING_DISABLE_PROBE_ORDER`/`MODEL_PROBE_VERDICTS`/`PROBE_PROMPT`/`PROBE_MAX_TOKENS`(两个探针常量 🔧 0.72.0 退役)/`probeRequestBody`/`probeModelCapability`/`applyProbeToEntry`(模型思考能力探测,详见 §28①);`0.63.0` 是 **905**,那批是八件 additive、零删除:`projectServerGateKnobs`/`postureKnobSourceOf`/`postureSourceIsOperatorPinned`/`postureKnobDetail`/`POSTURE_SOURCE_WORDS`(S-178 三根 posture 旋钮读数,详见 §27b)/ `PERSISTED_RULE_BEHAVIORS`/`PERSISTED_RULE_BEHAVIOR_UNKNOWN`/`persistedRuleBehaviorOf`/`persistedRuleBehaviorLabel`/`revokeTargetFromPersistedRule`(三态规则身份,§27c)/ `engineIdentityOf`/`engineIdentityVerdict`/`engineIdentityChanged`/`engineIdentityChangedBy`/`ENGINE_IDENTITY_ANCHORS`(S-179 代际锚,§27d)/ `GATE_DENIED_BY_WORDS`/`gateDeniedByDetail`/`ASK_ORIGIN_WORDS`/`askOriginDetail`(门词汇 +2,§27e)/ `PERMISSION_RULE_ISSUE_CODES`/`RETIRED_PERMISSION_RULE_ISSUE_CODES`/`permissionRuleIssueDetail`(规则 lint 码表,§27f)/ `projectToolRoster`/`toolRosterNames`/`toolShimFromRoster`/`applyToolRosterDelta`(L-161 工具名册,§27g)/ `ENGINE_NOTICE_CODES`/`ENGINE_NOTICE_AUDIENCE`/`noticeAudienceOf`/`engineNoticeInCatalog`/`MCP_INJECTION_DROP_REASONS`/`readMcpInjectionDrop`(L-167 通告码册,§27h)/ `AUTO_MODE_UNAVAILABLE_CAUSES`/`AUTO_MODE_BREAKER_CAUSES`/`classifierUnavailableOf`/`classifierUnavailableDetail`(件⑧ 分类器不可用事实,§27h2);`0.62.0` 是 **869**,那批是五件 additive、**零删除**:`capForDisplay`(呈前消毒 + 封长的共用铸点,详见 §26⑦)/ `SEAT_SEND_MESSAGE_KEY_ORIGINS`(座位载荷逐键出身表,详见 §26③)/ `configureSubagentContentStore`+`subagentContentStoreConfig`+`SUBAGENT_CONTENT_STORE_DEFAULTS`(子代内容账本字节预算,测试钩 `__resetSubagentContentStoreConfigForTests` +1,详见 §26②);`0.61.0` 是 **864**,那批是三件 additive、**零删除**:`projectReadFacePosture`/`readFacePostureDetail`/`readFaceDisagreement`(S-167 READ 容纳面 operator 读器,详见 §25);`0.60.0` 是 **861**,那批是二十五件 additive、**零删除**:`readRunTerminal`/`runTerminalCode`/`runTerminalGateKind`/`runTerminalGateToolName`/`isReviewPark`/`REVIEW_PARK_GATE_KINDS` 六件终局因由读器 + `gateOutcomeOf`/`isDeniedGate`/`gateDeniedBy`/`gateApprover`/`isApprovalWindowExpiredGate`/`isDenialLimitAutoDeniedGate`/`isParkSlaExpiredGate`/`isHumanSettledGate` 八件门记录读器 + `projectWriteProtectionCapability`/`noteEngineCapsForWriteProtection`/`observedWriteProtection`/`forgetWriteProtectionReading`/`writeProtectionDoctorDetail`/`projectWriteProtectionPosture`/`writeProtectionPostureDetail` 七件写保护读面(测试钩 `__resetWriteProtectionReadingsForTests` +1)+ `readWireErrorCode` + 真机边界①的两件 `isGateParkedToolEnd`/`GATE_PARKED_ERROR_CODE` + 异源复审逼出的 `engineCapsGeneration`;`0.59.0` 是 **836**,那批是八件 additive:`RULE_OFFERS_ABSENCE_REASONS`/`DENIAL_LIMIT_KINDS` 两张闭词表再导出 + `engineCapValue` 能力位四态通用读口 + `projectSqlEngineCapability`/`observedSqlEngine`/`noteEngineCapsForSqlEngine`/`forgetSqlEngineReading`/`sqlEngineDoctorDetail` 五件 SQL 姿态读面(测试钩 `__resetSqlEngineReadingsForTests` +1);`0.58.0` 是 **828**,那批是三件 additive:`RULE_OFFER_MATCHES`/`RULE_OFFER_BATCH_MEMBER_KINDS`/`RULE_OFFER_UNCOVERED_REASONS` 三张闭词表再导出 + `RESUME_REFUSAL_CODES`/`RESUME_PLACEMENT_MISMATCH`/`resumeRefusalFromError`/`resumeRefusalContent` 四件文案铸点;`0.57.0` 是 **821**;`0.56.0`/`0.55.0` 是 **815**;已发 `0.54.0`(design/385 十件 additive 含 `AUTHORITY_ENVELOPE_TAGS` 🔧 0.72.0 退役);`0.52.0` 是 **805**(L-69⑨ 两件);`0.51.0` 是 **803**;`0.50.0` 是 **800**(S-81 五件);`0.49.0` 是 **795**;`0.48.0` 是 **794**;npm `0.47.0` 是 **790**,`0.46.0` 是 **787**,`0.44.0` 是 **783**,`0.43.1`/`0.43.0` 是 **776**,`0.42.0` 是 **771**,`0.41.0` 是 **767**,`0.39.0` 是 **766**,`0.38.0` 是 **764**;`0.37.0` 是 **753**,见 `CHANGELOG.md`) | `scripts/public-export-baseline.json` 的 `count` / `testHookCount` —— **别手抄进别处,以该文件为准** |
|
|
27
27
|
| 常驻门 | 以 `scripts/gates-manifest.json` 的 `suites` 长度为准(**本档不抄这个数**) | `scripts/gates-manifest.json`;`npm test` 的名单等值门与它逐名对账 |
|
|
28
28
|
| 沿革档 | 0.29.0 起建 `CHANGELOG.md`;更早批次记账在 `src/index.ts` 文件头 + `docs/REFACTOR-LEDGER.md` | — |
|
|
29
29
|
|
|
@@ -123,7 +123,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
123
123
|
|
|
124
124
|
> 📦 **随包 / 不随包**:npm 包只带 `dist` / `README` / `CHANGELOG` / 本档 / `LICENSE`。本档各处提到的 `docs/type-superset.json`、`scripts/*.json`、`scripts/run-*-test.mjs` 都是**仓内登记物与门,不随包发出** —— 装包的消费者找不到它们是预期;`_sema_` 超集键的**随包契约**就是本档各 §NNz 逐键处置表(键名 / 值域 / 缺席语义逐条在表里)。
|
|
125
125
|
>
|
|
126
|
-
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**
|
|
126
|
+
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**1033** 项)。
|
|
127
127
|
> 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
|
|
128
128
|
> **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
|
|
129
129
|
> 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
|
|
@@ -147,16 +147,16 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
147
147
|
`WorkflowsGateUnknownDenial` 四形**不在**基线里,`src/selfOrchestrationDenial.ts` 对基线贡献
|
|
148
148
|
**4** 项运行期导出(三个函数 + `SELF_ORCHESTRATION_RETRY_WITHOUT`)。
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
1033 项的内部构成(帮助端估读表大小;0.73.4 +1 常量(CC-34);0.73.3 +6 函数(CC-61;测试钩 +1);0.73.1 +2 常量 +5 函数(CC-56 / CC-57;测试钩 +1);0.72.14 +1 常量 +7 函数(CC-55);0.72.13 +4 常量 +14 函数(CC-46 / CC-54 / CC-53 / CC-50 缺席口);0.72.12 +2 常量 +2 函数(CC-50);0.72.10 +2 常量 +2 函数(CC-41);0.72.9 +2 常量 +6 函数(CC-30 / CC-39②);0.72.8 +2 常量 +4 函数(CC-39③ / CC-43);0.72.7 +1 常量 +1 函数(CC-42);0.72.6 +1 函数(CC-40);0.72.5 +1 常量 +5 函数(CC-31);0.72.3 +1 常量(CC-39①);0.72.2 +1 函数(CC-37);0.72.1 +1 常量 +3 函数(CC-33);0.72.0 −23 常量 −16 函数(CC-32 退役批);0.71.3 +4 常量 +5 函数(CC-06);0.71.0 −2 常量 `MAX_AGENT_SKILLS` / `DECIDE_WORKFLOW_HOST_NOT_PARKED` +2 函数 `projectToolRosterDelta` / `mcpEngineLegPresence`;0.70.0 起 +1 函数;0.69.2 起 +3:常量 1 / 函数 2):**298** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
|
|
151
151
|
(矩阵、键集、env 名、锚串)而非可调用物;**6** 项是 PascalCase 运行期值
|
|
152
152
|
(`ControlRouter` / `ControlSafetyError` / `HitlBridge` / `HitlSafetyError` / `DecideTransportRetryExhaustedError`);
|
|
153
153
|
**37** 项是 `*For(sessionKey, …)` 的 per-session 变体(0.72.0 −4:`clearArmedGateFor` / `onGateArmedFor` / `registerArmedGateFromQuestionIdFor` / `notePlanReviewAnsweredIfDecisiveFor` 退出公面)(§6;其中 `engineNamespaceKeyFor` 是命名巧合 —— 参数是 baseUrl 不是 sessionKey,见域 14)。
|
|
154
154
|
|
|
155
|
-
### 2b. 域图(16 域,逐域计数之和 =
|
|
155
|
+
### 2b. 域图(16 域,逐域计数之和 = 1033)
|
|
156
156
|
|
|
157
157
|
| # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
|
|
158
158
|
|---|---|---|---|---|---|
|
|
159
|
-
| 1 | **适配内核(下行主链)** |
|
|
159
|
+
| 1 | **适配内核(下行主链)** | 63 | `TOOL_INPUT_JOIN_MAX_CALLS`(0.73.4 CC-34:终帧拒绝清单第三键的本流快照上限;§66)· `OUTCOME_UNKNOWN_ROW_PREFIX` / `isOutcomeUnknownRowText`(0.72.12 CC-50:第三条中性终局行「结局不知道」,读结果帧 `_sema_outcome`;§59)· `MODEL_OUTPUT_ERROR_PREFIX` / `isModelOutputErrorText` / `isModelOutputErrorRowText`(0.72.8 CC-43:runStream 第二条中性臂 —— core malformed tool-call 一族的行身份;§55)· `readEffectiveReasoning` · `readEffectiveMemoryScopes`(0.71.3 CC-06:终帧 `_sema_effective_reasoning` / `_sema_effective_memory_scopes` 的窄读器,§45)· `isSubFlowSegmentEnd` · `SEGMENT_END_IDENTITY_KEYS` · `droppedWhySentence`(0.69.2 CC-13:三件出公面,§38)· `adapt` · `createWireToCcAdapter` · `runStream` · `eventToSdkMessage` · `terminalToSdkResult` · `SEMA_SEGMENT_ID_KEY`(0.68.2 CC-01:committed assistant 文本行顶层段身份键的唯一字面量出处,壳只读)· `projectMcpSection`(0.68.2 CC-03:`wiring_manifest.mcp[]` 与 `/mcp` 面板 `lastLegMcp.mcp[]` 共用的同一只条目读器)· `readRunCostFacts`(0.66.0:终局成本事实的**唯一读器** —— `TaskStats.costBreakdown` / `nested` 逐键窄读 + 对账三段(own/nested/compaction)与两个「没定价」判别位;终帧的两个超集键与 chrome 臂 `run_cost_reconciled` **共用它**,两面不会各算各的,详见 §31c)· `TERMINAL_CAUSE_KINDS`/`isTerminalCauseKind`(0.68.0 / L-247:**core 终局因由**闭集,四词 `completed|failed|blocked|paused`)· `RUN_TERMINAL_NOT_SUCCESS_STATUSES`/`isTerminalNotSuccess`/`RUN_TERMINAL_STATUSES`/`isTerminalStatus`(L-215② 终局词汇的**单铸谓词**,0.68.0 随 L-247 **两表分源**更名 —— `killed` 属 server run 行状态面、`paused` 属 core 因由面,合成一张会让两边加词时都读不出该改哪儿;`blocked` 是 agent 自报终态,`suspended`/`needs_review` 刻意不在表里,详见 §33b / §30j)· `SEMA_SUBAGENT_USAGE_PARTIAL_KEY`/`subagentUsageIsPartial`(0.68.0 / L-244:子代用量下界位的**自有名**与唯一判据,与终帧 `_sema_usage_lower_bound` 分名,详见 §33g) · `turnUsageToModelUsage` · `isRunStreamActive` · `ADAPTER_DIVERGENCES` · `readRunTerminal` / `runTerminalCode` / `runTerminalGateKind` / `runTerminalGateToolName` / `isReviewPark` / `REVIEW_PARK_GATE_KINDS`(0.60.0 / engine ≥7.64.0:一次 run **怎么结束的**唯一读器 —— 终局从八个平面键换成一条带标因由 `terminal`,四臂 + `unknown` 防御臂 + `plane` 两代字节标;🔴 不认识的终态词绝不折成成功、本包不提供「因由→五词 status」的兼容投影,详见 §24b)| 引擎 SSE `AgentEvent` → 端要渲的**双面输出**:transcript(`SDKMessage`)+ chrome(瞬态 `ChromeEvent`)。**本包存在的理由** | `src/adapt.ts`、`src/adapt/{arms,wireShapes,panelTasks}.ts`(经 `adapt.ts` 再导出)、`src/adapter/runStream.ts`、`src/adapter/downstream/*`、`src/adapter/types.ts`、`src/runTerminal.ts` |
|
|
160
160
|
| 2 | **seam 公共契约** | 2(其余为 type-only) | `CHROME_ARMS` · `deriveTranscriptId` | 公共词汇 + **id 确定性不变量**(同一条流重放 ⇒ 同一串 id)。`CHROME_ARMS` = 端「我要消费哪些 chrome 臂」的对照清单 | `src/seam.ts` |
|
|
161
161
|
| 3 | **HITL 决断卡链**(§4/§5 主战场) | 177 | `planReviewOutcomeTail`(0.73.1 CC-57:outcome 尾句六态单铸点;投递口闩;§63)· `readLivePendingRows` / `suspendedSubagentAsks` / `countApprovalsAwaitingDecision` / `createSuspendedAskTracker` / `livePendingRowToApprovalFrame` / `surfaceSuspendedAskAndRespond` / `surfaceEditRefusedOnBlindAsk` + `EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT`(0.72.14 CC-55:悬挂的流内 ask 读面 / 视图 / 去重账 / 决断口;§61)· `planReviewCardOptions` / `planReviewChoiceFromAnswer` / `versionSupportsPlanReviewModeAfter` / `isPlanReviewModeAfter` + `PLAN_REVIEW_APPROVE_AUTO_LABEL` / `PLAN_REVIEW_APPROVE_MANUAL_LABEL` / `PLAN_REVIEW_MODE_AFTER_WORDS` / `PLAN_REVIEW_MODE_AFTER_MIN_ENGINE`(0.72.13 CC-46:plan_review 批准后模式 `permissionModeAfter` 发口与三选卡双闸;§60)· `makeHitlCanUseTool` · `HitlBridge` · `findPendingForTask` · `readDecideReceipt`/`decideAcceptedNotResolved`/`decideRefusalFromError`(B-070:`/decide` 200 = **投递受理**不是「门已解决」;`executionOutcome` 缺席 = 未知,详见 §30h) · `HitlSafetyError` · `bridgeAskUserQuestionGates` · `surfaceToolApprovalFrameAndRespond` / `surfaceFsApprovalAndDecide` · `readToolApprovalRespondAck` · `installApprovalCardPort(For)` · `installHitlHostSurface(For)` · `armPlanReviewApproval` · `reopenPlanReviewCard` · `decidePlanReview` · `startApprovalsFeed` · `pendingRowIsOwnedByThisSession` · `approvalCallKey`/`liveFrameCallKey`/`planReviewQuestionId` · `registerArmedGateFor`/`wasGateArmedFor` · `waitForGateArmed(For)`/`onGateArmed`/`gateArmedWaitMs`(#244 F1 呈现回执事件源) · `planReviewArmedKey(For)`/`notePlanReviewAnswered(For)`/`notePlanReviewAnsweredIfDecisive`(A-024.4 plan 呈现分代) · `toolEndOutputText` · `isAskTool` · `waitForParkRowBirth` · `classifyAskParkRows` / `askParkRowArm` / `classifyAskParkChainFailure` · `readDecisionNoteAudit` / `decisionNoteAuditLine` · `resumeRunningOptions` / `resumeChoiceFromLabels` · `persistedRulesLaneAvailable`/`persistedRulesGovernanceAvailable` · `classifyRulesFailure` · `listAllPersistedRules` · `classifySkippedReason` · `readRulePersistOutcome`(#244 F2:persist-ack 读口与 `readToolApprovalRespondAck` 合成一处) · `parseLocalAllowRule`(durable 腿本地落规则窄化骨架,谓词经 `LocalAllowRuleDeps` 注入) · `readToolApprovalRespondRefusal`(#225 件5,0.42.0:respond 抛错的结构化原文读口 —— 三位各自防御读、各自缺席不铸、**三位皆缺席时整只返 `undefined`**;原样交还零加工,UNTRUSTED-for-display)· `installEditedRuleTextPrechecker` / `hasEditedRuleTextPrechecker` / `precheckEditedRuleText`([5076] 转出口,0.42.0:core 5.57.0 `precheckEditedRuleText` 的**端口注入形** —— 类型面 + 注入口 + 诚实缺席读口。🔴 **不是** value 级 re-export,理由见 §7 缺口 **P-34**;未装 ⇒ 返 `undefined`,绝不编一个 `{ok:true}`)· `surfaceRuleArmNotSent` / `RULE_NOT_SENT_WARN_TEXT` + `surfaceRuleArmRejected` / `RULE_NOT_SENT_REJECTED_WARN_TEXT`(#334,0.43.0:人在卡上按下的「不再询问」被整条丢弃时的诚实告知——**两条刻意分开**:前者=**引擎能力位未确认**(换台引擎/等探测就好),后者=**这次选择没过包内表核/互斥核**(表外文本/坏下标/两臂同场,换引擎也不会变) —— 编辑臂 `respondFreeFormRules` 与批臂 `respondBatchRuleOffers` 两条同形存量共用一条,与 `surfaceRememberNotApplied` 同族纪律:决断照送、只是规则没存,静默丢掉用户明确意图 = 让人以为功能坏了)· `projectCrashConverged`(L-38,0.49.0:`/v1/approvals` additive 键 `crashConverged` 的分桶投影 —— 崩溃收敛的孤儿审批读面,**缺席 ≠ 空数组**、分桶恰一个合取、坏行丢弃并计数,详见 §12;同批把 `ApprovalsResourceLike.list()` 的返回位 additive 放宽成 `ApprovalsListEnvelope`,老形 `{pending}` 仍可赋值)· `readRuleOffers` / `readRuleOfferSupply`(L-103/[C228],0.57.0 additive 导出:「不再询问」候选两代 wire 键的窄读器**公面化** —— 此前只经卡端口出包,不走卡端口架构的宿主只能自己重铸一把,而它承载的是兑付安全判据(原始下标不前移/逐条丢坏/闭集 kind/两代取舍序),详见 §20)· `RULE_OFFERS_ABSENCE_REASONS` / `DENIAL_LIMIT_KINDS`(0.59.0 / sdk 8.3.0 提货:`ruleOffersAbsence` 三词与 `denialLimitFallback.limit` 二词的**运行期**闭词表再导出 —— 与 0.58.0 那三张同一条纪律:端拿它做判定别再手抄字面量。⚠️ **本包自己不拿这两张表做窄读判定**(两处都按开集透传,词表属主是 core、server 已按闭集拒过),再导出只是给端的 `switch` 一份可数的表,端仍必须带 `default` 臂,详见 §23a)· `DecideTransportRetryExhaustedError`(Inkglow-1085 P0a:decide 出站瞬断重试耗尽的 typed 判别 —— HitlBridge 内建单次退避重试,耗尽走重呈臂不判死 turn;端一般只消费行为,不需要 instanceof) · `gateOutcomeOf` / `isDeniedGate` / `gateDeniedBy` / `gateApprover` / `isApprovalWindowExpiredGate` / `isDenialLimitAutoDeniedGate` / `isParkSlaExpiredGate` / `isHumanSettledGate`(0.60.0 / engine ≥7.64.0:`tool_end.gate` 整条门记录的窄读器 + 判定谓词 —— 取代退役的四个正交词 `settledBy`/`resolution`/`autoDenied`/`approver`。🔴 **判定归包**:「是不是审批窗自己走完的拒绝」旧形上是端各自拼的两键合取,而三条窗词现在分得开、合并读回去等于把刚修好的判别力扔掉;⚠️ 公面型一律带 `View` 后缀 —— 本文件的 `GateOutcome`(决断腿结局)与 sdk 的 `GateOutcome`(引擎门记录)**同名不同义**,详见 §24c)· `isGateParkedToolEnd`(「这次调用没在这里结算」的**第二形**谓词:gate 缺席 + `errorCode:"gate.parked"`;端判这件事必须与 `isApprovalWindowExpiredGate` **两形合读**) · `readWireErrorCode`(B-037 候包件②:decide/取件失败上引擎 wire 机器码的单源读口,结构视图读不 `instanceof`;与本包铸的 `code` 是两个命名空间,详见 §24g)| suspended→decide→resume 环。🔴 **D-1 两元组 verbatim 回显**是字节级断言的安全不变量,端**不许重实现它的任何一段**。🔴 键空间边界(web [C1] d3 拦截):`gateIdentity` 四常量两函数只覆盖 HITL questionId/callKey 空间;seat 的 `TOOL_PERMISSION_REQUEST_ID_DOMAINS`(`plan:` 等)是另一键空间,**两者绝不合并**(合并=座位校验器静默拒全部 plan-review 卡) | `src/hitl/hitlBridge.ts`、`toolApprovalWire.ts`、`askGateWire.ts`、`planReviewWire.ts`、`hitlHostSurface.ts`、`gateIdentity.ts`、`armedGateRegistry.ts`、`parkOwnership.ts`、`parkResolver.ts`、`approvalsFeed.ts`、`frameRouter.ts`(**只挑名导出** `toolEndOutputText`/`ENGINE_ABORT_TOOL_RESULT`/`isAskTool`/`HITL_REJECT_MESSAGE`/`HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE`)、`parkRowBirthWait.ts`、`approvalDecisionNoteAudit.ts`、`askParkRowRouting.ts`、`resumeRunningCard.ts`(#265 上收的判定层)、`persistedRulesWire.ts`、`localAllowRule.ts`(#244 F2 规则侧)、`crashConverged.ts`(L-38 崩溃收敛读面)、`src/gateOutcome.ts` |
|
|
162
162
|
| 4 | **子代 wire + 面板侧信道台账** | 88 | `subscribeEngineAgentPanelAbsence` / `publishEngineAgentPanelAbsence`(0.72.13:fleet 行缺席上报的**独立订阅口** —— 不上主通道,老消费者结构上收不到;§60 S-5)· `tailEngineSubagent` · `installSubagentActivitySink` · `installSubagentTailMetaSink`(#280 件2:tail meta 帧发布口,`contentFrames` 判别位载体)· `stopEngineTask` + `classifyTaskStopConflict` · `fetchEngineSubagentReport` · `steerEngineSubagent`(0.32.0 未发布 #280 件A:additive 第三参 `childTaskId` —— 端有行上下文时**应当**传,传了就走「台账优先 / 缺席即诚实缺席 + `noteBgOwnerAbsence` 留痕」的 Q3 口径,与 tail·taskOutput·subagentOutput 三腿同姿势、与孪生 resume 腿共用同一个 `resolveOwnerRunId` 判据;**不传**则逐字维持旧行为=回落在飞 run)· `resumeSettledSubagent` + `resolveSubagentResumeContext` + `resolveOwnerRunId` + `classifySubagentResumeFailure` + `subagentResumeAvailable`(#242 批 2 A-028.7:resume 判定半场上收,与 steer 孪生同居;取址三态 = 台账有行用行值 / 指名了行但台账缺席则**诚实缺席绝不回落在飞 run** / 没指名行才回落。出路文案归端)· `recordSubagentOwnerFromProgress` + `getBgParentRunOwner`(A-028.6:「子代 → 宿主 run」**单表**,宿主 run 必须由持 stream-local 值的调用方显式传入,包内绝不从 `activeEngineRunId()` 推断)· `noteBgOwnerAbsence`(#242 批 3 [4000] Q3=B:tail/taskOutput·taskStop/subagentOutput 三腿台账缺席即诚实缺席**绝不回落在飞 run**,缺席 warn 留痕每 (腿,taskId) 一条)· `clearBgTerminalFacts`(#242 批 3 扫码修:复活=新周期,旧周期终态事实作废——fleetLedger 复活两腿按尾段清账,factsAccepted 方向核不再拿上周期终态当先例)· `auditRetainWithoutWake`([4000] Q5:引擎宣示 `subagentResume` + 本端在付 `retainSubagentSessions` + 端未实现 `wakeSubagent` ⇒ 响亮一条;`CLIENT_VERBS.wakeSubagent` 维持 fail-soft)· `subscribeSubagentContent` · `subscribeEngineAgentPanel` · `publishQuestionFrame` / `respondToQuestion` · `configureSubagentContentStore` / `subagentContentStoreConfig` / `SUBAGENT_CONTENT_STORE_DEFAULTS`(0.62.0:子代内容账本的**字节预算** —— 此前只有条数帽而单条无上限,一条 base64 工具结果就能让一个子代槽吃到十几 MB;越限从最早的内容开始丢并留一条可渲染的 `truncated` 记录,总帽淘汰整条最久未用的账本。配置面 fail-loud,详见 §26②)| 驱动与观测委派子代;经 module 级台账喂活体 agent/task 面板。全部**能力位 gate**(§5b) | `src/subagent/*.ts`、`src/subagentContentStore.ts`、`src/engineAgentPanelStore.ts`、`src/engineInlineTaskStats.ts`、`src/engineToolLabelStore.ts`、`src/liveQuestionStore.ts` |
|
|
@@ -8827,7 +8827,7 @@ installCoreValuePorts({ resolveAutonomousLoopPrompt, loosenReasons, combinePolic
|
|
|
8827
8827
|
| # | 面 | 本包做了什么 | 端要做什么 |
|
|
8828
8828
|
|---|---|---|---|
|
|
8829
8829
|
| S-1 | **`livePending[].frame?`**(server ≥7.87.0;= 本来会 emit 的那只 `tool_approval` 帧;`row.frame.approvalId === row.approvalId` 恒成立) | **consumed**:视图 +`frame?`(三道闸:帧判别器 ∧ `type === 'tool_approval'` ∧ approvalId 相等;坏形丢该位不丢行);`surfaceSuspendedAskAndRespond` 在场即走流内帧腿全卡;摘要含在场性;tracker delta +`upgraded`(此前出过盲卡、这张快照上 `frame` 到了的行) | 常态零接线(server ≥7.87.0 帧从第一张快照就在,`appeared` 那一步直接出全卡)。🔴 **滚动升级中途形**(先盲后全):`ingest` 返回 `upgraded`;**包不替换已出的盲卡**(帧腿撤卡的唯一方式是给决断);你若有按 callKey 原地更新卡的能力,用 `upgraded[].frame.args` 刷新入参,否则让用户在盲卡上决定 |
|
|
8830
|
-
| S-2 | `permission_denials` 自动升级(CC-34) |
|
|
8830
|
+
| S-2 | `permission_denials` 自动升级(CC-34) | ~~收口~~ 🔴 **更正(0.73.4)**:到货的只有 `tool_use_id` 一键,CC 形数组仍恒空;第三键见 §66 S-1 | 零接线;headless 消费面读 `_sema_permission_denials[].tool_use_id`,缺席仍按 `_absent` 读 |
|
|
8831
8831
|
|
|
8832
8832
|
### 64z. 逐键处置表
|
|
8833
8833
|
|
|
@@ -8885,3 +8885,55 @@ installCoreValuePorts({ resolveAutonomousLoopPrompt, loosenReasons, combinePolic
|
|
|
8885
8885
|
3. peer sdk >=9.6.0 / agent-types >=0.3.0 不动;server <7.87.1 ⇒ `approvalsStreamLive` 键缺席 ⇒ `not_reported`。
|
|
8886
8886
|
|
|
8887
8887
|
**包侧缺口:** ⓪ CC-58 workflow 规模告警判定上收(窗 09-25);① CC-36 托管 MCP 活性三态读口(候 core 7.23.2 #874);② `LivePendingRow.contentKind` 消费(候消费场景);③ `staleAfterSec` / `runId` 经 sdk 到端(候 sdk 出键);④ 九件 wire 面经 sdk 声明(sdk 9.7.x 已追平六处;余候 sdk);⑤ `agents.listing_truncated` detail 读器候消费场景(by-design);⑥ `ClassifierSeat.defaulted?` 候 sdk 出键(by-design);⑦ §NNz「declined 键真零命中」机读第二刀(候形式化);⑧ 缺席上报附 `removeReason`(候壳透传);⑨ 三选卡宿主声明闸候 server 投影判别键;⑩ fleet task 行 `queuedCount` 候 sdk major 删 spec;⑪ 终态 run 记录上的 `usage.window_exhausted` 读面(另立);⑫ acceptEdits 双 approve 归属候 cli 复跑。
|
|
8888
|
+
|
|
8889
|
+
## §66 🆕 0.73.4 终帧 `permission_denials` 第三键(流内 `tool_start` join)+ 审批 feed「欠一次看」重试 + workflow run 级错误码;patch
|
|
8890
|
+
|
|
8891
|
+
> **patch**:型面纯 additive(+1 常量,公面 1032 → 1033;`TerminalStreamObserved` 具名化 +1 可选位;`SemaPermissionDenial` +`_sema_tool_input_source?`;`ApprovalsFeedOptions` +`pushTakeRetryMs?`;`ApprovalsFeedStats` +`pushTakeRetries`;零 wire 新键;peer 不动)。🔴 **更正 §64 S-2**:0.73.2 到货的只有 `tool_use_id` 一键,CC 形数组在 0.73.2 / 0.73.3 上仍恒空;三键齐的行自本版起才有。
|
|
8892
|
+
|
|
8893
|
+
### 66a. 处置表
|
|
8894
|
+
|
|
8895
|
+
| # | 面 | 本包做了什么 | 端要做什么 |
|
|
8896
|
+
|---|---|---|---|
|
|
8897
|
+
| S-1 | 终帧 `permission_denials`(CC 形,三键必填) | **consumed**:第三键 `tool_input` ⇐ 同一条流 `tool_start.args`,按账本行 `toolCallId` join;三道闸(普通对象 / 没被传输层动过 / 扫描预算内) | 走 `runStream` 的端**零接线**。自建管线直调 `terminalToSdkResult` 的端:把本流「`toolCallId` → `tool_start.args`」交进第三参 `toolInputByCallId`(per-stream,别挂共享对象;同 id 两份不同入参的别放进来);不交 = 与此前逐字同 |
|
|
8898
|
+
| S-2 | `_sema_permission_denials[]._sema_tool_input_source` | **additive 超集位**:只在 `tool_input` 来自流内帧时 = `'tool_start'` | 可不读;要区分来源时读它 |
|
|
8899
|
+
| S-3 | `_sema_permission_denials_absent` | 语义不变(= CC 那条清单不可声称完整);**现在真有缺席的时候了**:每条拒绝都 join 上时不落 | 读法不变:`[]` + 缺席 = 零拒绝;在场 = 别渲肯定句,条数看 sema 载体 |
|
|
8900
|
+
| S-5 | TaskOutput 结构卡顶层 `errorCode`(workflow run 级;core ≥7.17.1) | **consumed**:`toolUseResult.task._sema_error_code` 原词透传 | wf 卡 / 面板 run 行渲错误码读这一键;🔴 只在结构面这条腿上有 —— 模型面正文、列表行腿都不带,读列表行的面板仍拿不到 |
|
|
8901
|
+
| S-4 | `startApprovalsFeed` push 侧取件失败 | **fixed**:欠账退避重试到看成为止 | 零接线;要调基础间隔传 `pushTakeRetryMs`(`<= 0` 关);`reconcile` 照传不关(两者守的不是同一种漏看) |
|
|
8902
|
+
|
|
8903
|
+
### 66b. 缺席语义(逐键)
|
|
8904
|
+
|
|
8905
|
+
- `tool_input` 缺席(行只在 sema 载体)= **拿不出可信的入参对象**,五种原因之一:行无 `toolCallId`(探针行 / 非工具审批的耐久门 / 继承授权复用 / 引擎 <7.23.0)· 本流没见过那只 `tool_start`(重连后的新流、子代帧没转发、宿主没交快照)· 帧上入参不是普通对象 · 入参被传输层动过(字符串叶含 `«redacted…»` 记号,或叶值恰为 `[circular]` / `[depth-limit]`)· 对象超出扫描预算。**绝不**等于「入参为空」。
|
|
8906
|
+
- `_sema_tool_input_source` 缺席 = `tool_input` 不是来自流内帧(来自账本,或根本没有 `tool_input`)。
|
|
8907
|
+
- 审批人改写过入参的调用,`tool_start` 帧上是改写后的那份;被拒的调用没有改写。
|
|
8908
|
+
- `pushTakeRetries` = 0 ≠ 「从没失败过」,失败次数看 `listErrors`。
|
|
8909
|
+
- `_sema_error_code` 缺席 = 这条 run 没有码,或引擎早于出键版本,或这张卡不是从结构面来的(模型面回落腿)。**绝不**等于「成功」—— 成败看 `status`。
|
|
8910
|
+
|
|
8911
|
+
### 66c. 黑盒判据骨架
|
|
8912
|
+
|
|
8913
|
+
- **G1(join 正半)**:真引擎 ≥7.23.0,一次工具调用被审批人拒绝,入参不含凭据形 ⇒ 终帧 `permission_denials` 恰一条,`tool_use_id` === 转录 `tool_use.id`,`tool_input` 深等于转录该块的 `input`,`_sema_tool_input_source === 'tool_start'`,`_sema_permission_denials_absent` **不在场**。修前(0.73.3):数组空、`_absent: true`。
|
|
8914
|
+
- **G2(join 负半)**:同上但入参里带一段会被传输层替换的凭据形串 ⇒ `permission_denials` 空、`_sema_permission_denials` 一条(带 `tool_use_id`,无 `tool_input`)、`_absent: true`。
|
|
8915
|
+
- **G3(混合)**:两次拒绝一正一负 ⇒ CC 数组一条、sema 载体两条、`_absent: true`。
|
|
8916
|
+
- **G4(负控)**:引擎 <7.23.0(行无 `toolCallId`)⇒ 与 0.73.3 逐字同。
|
|
8917
|
+
- **G5(feed)**:假 client:起手 `list()` 成功 → 一只非 heartbeat 事件触发的 `list()` 抛一次 → 其后只有 heartbeat,不传 `reconcile` ⇒ 约 `pushTakeRetryMs` 后出第二张快照,`pushTakeRetries === 1`,其后零额外取件。修前:第二张快照永不出现。
|
|
8918
|
+
- **G7(run 级错误码)**:真引擎上一条以 run 级码收场的 workflow(例:agent 停在待批)⇒ 其 TaskOutput 卡 `task._sema_error_code` === 引擎结构面顶层 `errorCode`;正常完成的 run ⇒ 键不在场。
|
|
8919
|
+
- **G6(feed 不放弃)**:`list()` 连抛四次 ⇒ 第五次看成,间隔逐次翻倍;`pushTakeRetryMs: 0` ⇒ 与修前同。
|
|
8920
|
+
|
|
8921
|
+
### 66z. 逐键处置表
|
|
8922
|
+
|
|
8923
|
+
| # | 键 / 面 | 处置 | 坐标 / 理由 |
|
|
8924
|
+
|---|---|---|---|
|
|
8925
|
+
| 1 | `stats.humanReview.gates[].toolCallId` | **consumed(join 键)** | `src/adapter/downstream/terminalToSdkResult.ts` `#permissionDenialParts` |
|
|
8926
|
+
| 2 | `tool_start.args` | **consumed(第三键来源;三道闸)** | 同文件 `#joinableToolInput`;`src/adapter/runStream.ts` 本流快照 |
|
|
8927
|
+
| 3 | 传输层替换记号 / 环与深度占位串 | **consumed(只作「被动过」判据,不反解)** | 同上;按前缀 / 恰等于认,多认不少认 |
|
|
8928
|
+
| 4 | `gates[].toolInput` | **pending(上游未出键;预读位保留,在场恒优先)** | 同上 |
|
|
8929
|
+
| 6 | task-output 结构卡顶层 `errorCode` | **consumed** | `src/toolResult.ts` `#structuredToToolUseResult`;内层 `details.errorCode` 不认 |
|
|
8930
|
+
| 5 | `approvals.stream` 事件载荷 | **declined(一贯不解析)** | `src/hitl/approvalsFeed.ts`;任何非 heartbeat 事件只当「变了」 |
|
|
8931
|
+
|
|
8932
|
+
### 66y. 🔴 三端升级必读(固定段式,[C295])
|
|
8933
|
+
|
|
8934
|
+
1. 换钉 `^0.73.4`:走 `runStream` 的端零接线即得三键齐的 `permission_denials`;headless 输出面若此前因数组恒空而改读 `_sema_permission_denials`,两条都留着读(join 不上的行只在后者)。
|
|
8935
|
+
2. 自建管线直调终帧投影的端:第三参多交一格 `toolInputByCallId`(per-stream)。不交零变化。
|
|
8936
|
+
3. 审批 feed 零接线;`reconcile` 照传不关。wf 卡要渲 run 级错误码读 `task._sema_error_code`(缺席不渲)。peer 不动。
|
|
8937
|
+
|
|
8938
|
+
**包侧缺口:** ⓪ CC-58 workflow 规模告警判定上收(窗 09-25);① CC-36 托管 MCP 活性三态读口(候 core 7.23.2 #874);② 被传输层动过的入参无机读判别位(今按字面记号认;上游若出 present-iff 位,当拍改读);③ 重连后的流 join 不上此前的 `tool_start`(候 durable 回放带全帧的消费场景);其余同 §65。
|
|
8939
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.73.
|
|
3
|
+
"version": "0.73.4",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|