@sema-agent/client-core 0.60.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.md +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/readFacePosture.d.ts +53 -0
- package/dist/readFacePosture.js +120 -0
- package/docs/INTEGRATION-CLIENTS.md +115 -7
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -49,6 +49,59 @@
|
|
|
49
49
|
> 挡住 ⇒ 本批把它机械化——④a0 对 `pending` 行**要求段头已是日期形**(`(未发布)` 直接红),阶段一
|
|
50
50
|
> commit 漏转在发布前就红,不再靠人记。
|
|
51
51
|
|
|
52
|
+
## 0.61.0(2026-09-08)
|
|
53
|
+
|
|
54
|
+
> 内容批(隔离树交付):`@sema-agent/sdk` **8.5.0 提货** —— operator 面
|
|
55
|
+
> `GET /v1/diagnostics/wiring` 新增 `readFace: ReadFacePosture`(server ≥7.65.0 / S-167):这台部署
|
|
56
|
+
> READ 容纳面此刻的生效档 **+ 这一档是谁定的**(来源四词 + 一句给运维的指路句)。**纯 additive**:
|
|
57
|
+
> 新键、无退役、无形变;老引擎(<7.65.0)上整键缺席,读器按缺席处置,不是 BREAKING。**本段只记内容**
|
|
58
|
+
> (0.60.0 先例逐字):`package.json.version` 未动、README `Version` 行未动、`FROZEN` 未动 —— bump 与
|
|
59
|
+
> 段头转日期形归**发包批**(阶段一义务见本档头注;两阶段协议下 `pending` 行要求段头**已是日期形**,
|
|
60
|
+
> 所以内容批插 `pending` 行必红)。peer 地板/devDep/README 地板句/门常量 `FLOOR` **四处同批**
|
|
61
|
+
> (`run-sdk-floor-test` ①c 互绑)。
|
|
62
|
+
> 接入面详报见 `docs/INTEGRATION-CLIENTS.md` **§25**(读器签名/输入形/缺席语义 + 三句措辞表 +
|
|
63
|
+
> 壳换装清单 + 黑盒判据 + 门表)。
|
|
64
|
+
|
|
65
|
+
### peer 地板 `>=8.4.0` → `>=8.5.0`(非 BREAKING;新增位是唯一理由)
|
|
66
|
+
|
|
67
|
+
- **`@sema-agent/sdk` peer 地板 `>=8.4.0` → `>=8.5.0`**(devDep `^8.5.0` 同批;地板与 peer 声明由
|
|
68
|
+
`run-sdk-floor-test.mjs` **三位逐位等值**互绑)。硬理由见该门的 `FLOOR` 注:本包新模块
|
|
69
|
+
`src/readFacePosture.ts` **type-only import** `ReadFacePosture` 并用它钉编译期对账,<8.5.0 上
|
|
70
|
+
`tsc` 报「没有导出成员」。`Capabilities` 顶层键仍为 94(型面本身未动,只加了语义注)——本次抬版
|
|
71
|
+
对 `run-engine-caps-ledger-test.mjs` 的台账**零改**。
|
|
72
|
+
|
|
73
|
+
### 新读面:`diagnostics.wiring.readFace: ReadFacePosture` 的三端共用窄读器(S-167)
|
|
74
|
+
|
|
75
|
+
- 🆕 **`src/readFacePosture.ts`**:`projectReadFacePosture(wiring)` → `ReadFacePostureView`
|
|
76
|
+
(`face: 'open'|'roots'|null; source: string; note: string`)`| undefined`。整键缺席(老 worker,
|
|
77
|
+
server <7.65.0)与在场但形坏,**同一个** `undefined`(不猜「为什么答不出来」——那不是这一位该回答
|
|
78
|
+
的)。`face` 闭三态,`source` 按**开集**读(server 闭四词 `env`/`center`/`posture`/`engine-default`
|
|
79
|
+
之外的值原样透传,不窄读成枚举),`note` 允许空串(自由文本,空文本本身也是一句读数)。
|
|
80
|
+
- 🆕 `readFacePostureDetail(view, { reachable })`:三态措辞**唯一铸点**,三句逐字互异(黑盒锚)——
|
|
81
|
+
①在场:原样渲档位 + 来源 + 指路句(`note`/`source` 呈前**先转义、后按转义结果封长** ≤256/≤40 ——
|
|
82
|
+
反过来做会让一段纯控制字符的原文转义后膨胀出预算,车内异源复审 [medium] 抓住并修根,同批修同一处
|
|
83
|
+
`readFaceDisagreement` 的 `capFace` 呈现);②`reachable:false`:「未观测」;③`reachable:true` 但键
|
|
84
|
+
仍缺席:「不报」——🔴 **不武断咎为版本**:整键缺席(老引擎)与在场但形坏(≥7.65.0 引擎送出本读器
|
|
85
|
+
解不出来的响应)在 `projectReadFacePosture` 折成同一个 `undefined`,句子因此同时点出两种成因
|
|
86
|
+
(车内异源复审 [medium] 抓住的第二条:旧句把「形坏」武断诊断成「引擎太老」,误导故障排查)。
|
|
87
|
+
**没有一句**等于「这台部署没有 READ 档」——那句话只有 `view.face === null` 时才说得出口,三态
|
|
88
|
+
各自不蕴含它。
|
|
89
|
+
- 🆕 `readFaceDisagreement(capFace, posture)`:纯函数,只答**分歧**(两者确实不同才开口,一致不是
|
|
90
|
+
新闻不铸正面「相符」句)。与租户面 `capabilities.readFace` **刻意不合流**——两位是同一份 boot
|
|
91
|
+
产物的两个投影,在场时点天然不同源,渲染归端。
|
|
92
|
+
- 公开导出面 **861 → 864**(+3,零删除,零测试钩变动)。
|
|
93
|
+
|
|
94
|
+
### 门
|
|
95
|
+
|
|
96
|
+
- 🆕 `scripts/run-read-face-posture-projection-test.mjs`:三态措辞逐字互异 + 四 source 开集读 +
|
|
97
|
+
`face:null` 正面事实 + 十种坏形不抛 + `note`/`source` 转义膨胀不突破封长预算(先转义后截长,
|
|
98
|
+
且不劈开转义 token 或合法代理对)+ 「不报」句不武断咎为版本(整键缺席与在场形坏折成同一
|
|
99
|
+
`undefined` 时,句子须同时点出两种成因)+ `readFaceDisagreement` 正反两格 + sdk `openapi.yaml`
|
|
100
|
+
的 `ReadFacePosture` 组件真字节(必填三键 + `additionalProperties:false`)。79 checks(车内
|
|
101
|
+
异源复审 [medium]×2 跟修:上述转义顺序与「不报」句措辞两条即修根产物)。
|
|
102
|
+
- `scripts/run-sdk-floor-test.mjs`:地板 `8.5.0` 与 peer 声明**三位逐位等值**互绑。
|
|
103
|
+
- `scripts/run-public-surface-test.mjs`:公面导出基线 **861 → 864**(测试钩数未动)。
|
|
104
|
+
|
|
52
105
|
## 0.60.0(2026-09-08)
|
|
53
106
|
|
|
54
107
|
> 内容批(2026-09-08,隔离树交付):四件 —— ①`@sema-agent/sdk` **8.4.0 提货**(engine ≥7.64.0 的
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
35
35
|
|
|
36
36
|
## Scope
|
|
37
37
|
|
|
38
|
-
**Version:** 0.
|
|
38
|
+
**Version:** 0.61.0
|
|
39
39
|
|
|
40
40
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
41
41
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
|
@@ -67,7 +67,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
67
67
|
against — the tables live upstream precisely so this package does not keep a second copy that can
|
|
68
68
|
fall behind. The browser bundle really bundles the SDK through (the portability guard would
|
|
69
69
|
exit 3 rather than quietly mark it external).
|
|
70
|
-
- The declared floor is `>=8.
|
|
70
|
+
- The declared floor is `>=8.5.0`, and it is *witnessed*: the guard checks that an actually
|
|
71
71
|
installed SDK at that line still exports every value-level symbol this package imports and still
|
|
72
72
|
declares `TaskStats.costMicroUsd` (the key `costOrNull` reads). A floor nobody ever ran is a
|
|
73
73
|
promise, not a contract.
|
|
@@ -237,6 +237,7 @@ public-surface guard checks that last one).
|
|
|
237
237
|
| `scripts/run-engine-caps-ledger-test.mjs` | A per-key disposition ledger for `GET /v1/capabilities`. The SDK's `Capabilities` grew from 74 keys to 93 in one release and nothing on the board could see it: this package consumes that table through four synchronous readers, and *nineteen new positions arriving while the package does not move* is exactly the disease shape this repo keeps logging on other axes — the fact is already on the wire, the package boundary is the cell that swallows it, and no client can read it however they write their side. So the ledger is reconciled **element-wise against the SDK interface in both directions**: a key the SDK added with no ledger row is red (someone must classify it), and a row for a key the SDK removed is red too (a registration that no longer does anything). Each row then has to survive its own claim — a `read` row names the source file, and the **code** there (comments stripped) must really mention the key, because prose asserting an alignment is the classic way these guards go hollow; a `not_read` row must have **zero** read sites in the tree, so wiring one up while the ledger still says the package ignores it is red rather than invisible. The census behind those two directions recognises five call shapes, each of which really occurs here — a reader whose base argument carries its own parentheses, a direct `caps.<key>`, a narrowing cast, an own-property read helper, and a `*_CAP` constant — and proves it on fabricated samples first, since a census that recognises one shape reports "nothing here" for the other four. What the guard deliberately does **not** judge is whether a position *ought* to be read: that is a design call, and the ledger only pins that every capability was looked at once by a person and that what they wrote down does not contradict the code |
|
|
238
238
|
| `scripts/run-sql-engine-capability-test.mjs` | The SQL-posture read face and the four-state capability reader underneath it. One capability cell here carries **four different things**, and each one points an operator somewhere else: nothing has been observed yet in this process (a one-shot doctor run is always in that state), the response arrived but carries no such key (an older engine), the engine explicitly answered `null` — *this deployment has no SQL backend*, which is a **positive fact** rather than an absence — and a full reading. Fold any two together and the screen states something flatly, confidently, and wrongly, so every positive control here is paired with a control pointing the opposite way, and the four sentences the doctor row can print are checked to be pairwise distinct and non-implying. The reading itself is narrowed no tighter than the mint: `txnMode: null` is a **legal value** — two of the three engines always report it that way, and the upstream type note names reading it as "optimistic" as the error — so treating it as malformed would throw away the entire reading for ordinary deployments, which is the same disease this repo logged when a consumer's domain was narrower than the producer's. A response that cannot be parsed **clears** the cell rather than leaving the previous engine's answer in place, and a separate invalidation port exists for the case the generation latch cannot catch — a same-port respawn whose new probe never succeeded, where the stale reading would otherwise be answered as current fact. Untrusted values (the isolation string is read back from a database server variable) are sanitised and bounded before display, and the bound is applied **before** escaping so a visible escape never gets cut in half. Finally the export names are themselves a guard: the shell still carries a copy that is meant to go red on the package's same-named export and be swapped out, so renaming anything here would silently disarm that lock |
|
|
239
239
|
| `scripts/run-terminal-cause-projection-test.mjs` | The `7.64.0` wire reshape, projected. A run's ending stopped being eight parallel flat keys and became **one tagged cause** (`completed | failed | blocked | paused`), and a tool call's gate stopped being four orthogonal words and became **one record** (`disposition` / `settlement?` / `origin?`). Both are read in exactly one place in this package, and this guard pins them at **two levels**, because the dangerous seam is "the reader was updated, the consumer was not": each terminal arm is checked on the reader *and* on the `subtype` / `is_error` / `errors[]` the projector actually emits. Two properties carry most of the weight. First, a terminal word this reader does not know is **never** laundered into an empty success — it lands on an `unknown` arm carrying the word verbatim, while a payload with no terminal word at all (the mock lane) keeps the success arm exactly as before, which is the one and only case the reader answers `null`. Second, the three window words (`approval_window_expired`, `denial_limit_window_expired`, `park_sla_expired`) must each be told apart by a different predicate: the previous generation collapsed all three onto one `timeout`, and re-merging them would throw away the discrimination this reshape just restored. Two byte generations are read by one reader, keyed on the discriminator upstream nailed (`"terminal" in result`): the current cause form, and the **flat** form that a current engine still emits on two lanes — replayed persisted bytes, which the service passes through verbatim rather than back-filling, and the service's own rejection envelope. A cause-form payload that also carries stale flat keys must ignore them entirely: keeping one compatibility read is what gives a single fact two sources. The same file also pins the MCP delivery verdict and HTTP status riding the wiring manifest, the four-state write-protection reading (where three of the four states mean *cannot tell*, and none of them may be printed as "there is no table"), and the park-reopen fetch identity: that predicate is asserted through the **real entry point**, since the defect being fixed was precisely a call site wired to a different predicate than the one that routed the row there |
|
|
240
|
+
| `scripts/run-read-face-posture-projection-test.mjs` | The operator-face `readFace: ReadFacePosture` reader (server >=7.65.0). Three ways of "can't say" are pinned to three different, literal sentences, and none of them may read as "nothing is pinned" — that statement belongs to exactly one case, `face: null`, which is a positive fact reported by the engine, not an absence: not having read an operator response yet, having read one from an engine too old to report the key, and the engine actually saying nothing is pinned are three different next steps for an operator and must not collapse into each other. `source` is read as an open set (the server's closed four words plus an escape hatch) rather than narrowed to an enum, so a new word added upstream is not silently turned into a bad reading. The free-text `note` is sanitized and length-capped before it is ever rendered. A companion pure function flags disagreement between this face and the tenant-facing `capabilities.readFace` — silent only when the two actually agree, honest-absent when either side cannot be read at all, never asserting agreement as a fact. The gate's last leg reads the installed SDK's own `openapi.yaml` directly rather than restating the schema in prose, so the package's leniency cannot quietly drift from the real contract |
|
|
240
241
|
| `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` |
|
|
241
242
|
| `scripts/run-limits-env-failloud-test.mjs` | `SEMA_HEADLESS_*` env-lane limits reject invalid values as loudly as the flag lane (no silent "no budget" runs) |
|
|
242
243
|
| `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 |
|
package/dist/index.d.ts
CHANGED
|
@@ -151,6 +151,7 @@ export * from './engineCapsCache.js';
|
|
|
151
151
|
export * from './sqlEngineCapability.js';
|
|
152
152
|
export * from './writeProtectionCapability.js';
|
|
153
153
|
export * from './runTerminal.js';
|
|
154
|
+
export * from './readFacePosture.js';
|
|
154
155
|
export * from './gateOutcome.js';
|
|
155
156
|
export * from './engineToolLabelStore.js';
|
|
156
157
|
export * from './fleetTaskDesc.js';
|
package/dist/index.js
CHANGED
|
@@ -168,6 +168,11 @@ export * from './sqlEngineCapability.js';
|
|
|
168
168
|
export * from './writeProtectionCapability.js';
|
|
169
169
|
// 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局因由与门记录两只**判定归包**的读器。
|
|
170
170
|
export * from './runTerminal.js';
|
|
171
|
+
// 0.61.0(engine ≥7.65.0 / sdk 8.5.0 / S-167):READ 容纳面档位 + 来源的 operator 面窄读器
|
|
172
|
+
// (`WiringDiagnostics.readFace`)。与 `writeProtectionCapability` / `gateOutcome` 同构同纪律
|
|
173
|
+
// (防御读 / 唯一措辞铸点 / UNTRUSTED-for-display);与租户面 `capabilities.readFace` 刻意不合流,
|
|
174
|
+
// 只带一个纯比较函数,渲染归端。
|
|
175
|
+
export * from './readFacePosture.js';
|
|
171
176
|
export * from './gateOutcome.js';
|
|
172
177
|
export * from './engineToolLabelStore.js';
|
|
173
178
|
export * from './fleetTaskDesc.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* operator 面读数(sdk `ReadFacePosture` 的防御读视图;结构逐字同源,见文件尾编译期对账钉)。
|
|
3
|
+
*/
|
|
4
|
+
export interface ReadFacePostureView {
|
|
5
|
+
/** 这台部署此刻的生效档。`null` = 没钉,引擎默认接管——与租户面同一份产物、同一句「没钉」。 */
|
|
6
|
+
face: 'open' | 'roots' | null;
|
|
7
|
+
/** 这一档是谁定的。server 闭四词(`env` / `center` / `posture` / `engine-default`),**按开集读**
|
|
8
|
+
* ——server 闭集之外的值原样透传,不窄读成枚举(那会在 server 加词当天把一个合法读数判没)。 */
|
|
9
|
+
source: string;
|
|
10
|
+
/** 给运维的指路句。UNTRUSTED-for-display:呈前消毒 + 封长,见 {@link readFacePostureDetail}。 */
|
|
11
|
+
note: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* `wiring.readFace` → 读数;**畸形一律 `undefined`**,绝不抛出。
|
|
15
|
+
*
|
|
16
|
+
* 🔴 整键缺席(老 worker,server <7.65.0)与在场但形坏,消费端拿到的都是同一个 `undefined` ——
|
|
17
|
+
* 两者对端说的是同一句话(「这一面这一次答不出来」)。「为什么答不出来」不是这一位该回答的,
|
|
18
|
+
* 是调用方自己知道的事(它有没有拿到 operator 响应;见 {@link readFacePostureDetail} 的
|
|
19
|
+
* `opts.reachable` 参数,那条信息只能由调用方——不是本读器——提供)。
|
|
20
|
+
* ⚠️ `face` 只认三态字面量(闭集,坏词一律判畸形);`source` 非空串即收(开集,理由见上);`note`
|
|
21
|
+
* 允许空串(它是自由文本,空文本本身也是一句读数,不是「读不出」)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function projectReadFacePosture(wiring: unknown): ReadFacePostureView | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* 三态措辞的**唯一铸点**(三端共用一句话;别在各端的行装配里另写一遍——与
|
|
26
|
+
* `writeProtectionDoctorDetail` / `sqlEngineDoctorDetail` 同一条纪律)。
|
|
27
|
+
*
|
|
28
|
+
* 🔴 三句刻意逐字互异(黑盒锚):
|
|
29
|
+
* ① `view` 在场 —— `read face <open|roots|not pinned> (source: <source> — <note>)`;
|
|
30
|
+
* ② `opts.reachable === false` —— 「未观测」:这次进程没读到 operator 响应,与「这台部署没有
|
|
31
|
+
* READ 档」是两件事,消费端不许把它读成后者;
|
|
32
|
+
* ③ `reachable:true` 但 `view` 仍缺席 —— 「不报」:响应读到了,只是这一位读不出来。
|
|
33
|
+
* 🔴 **不武断咎为版本**:`projectReadFacePosture` 把「整键缺席(老引擎,<7.65.0)」与
|
|
34
|
+
* 「在场但形坏(≥7.65.0 的引擎送出一个本读器解不出来的响应)」折成同一个 `undefined`
|
|
35
|
+
* (见该函数顶注),句子因此**不能**替其中一种情形撒谎——「需要更新引擎」对第二种情形是一句
|
|
36
|
+
* 误导故障排查的假话。措辞同时点出两种成因,不擅自替调用方选一种。
|
|
37
|
+
*/
|
|
38
|
+
export declare function readFacePostureDetail(view: ReadFacePostureView | undefined, opts: {
|
|
39
|
+
reachable: boolean;
|
|
40
|
+
}): string;
|
|
41
|
+
/**
|
|
42
|
+
* 租户面 `capabilities.readFace` 与本面 `posture.face` 分歧时的一句话;**只答分歧,不答一致**
|
|
43
|
+
* ——一致不是新闻(两位的在场时点天然不同源,「相符」证不了什么也不该被渲成保证),本函数只在
|
|
44
|
+
* 两者**确实不同**时才开口。
|
|
45
|
+
*
|
|
46
|
+
* 🔴 两个入参**任一**答不出来 ⇒ `undefined`(诚实缺席,不是「没有分歧」——见不到两位就判不了):
|
|
47
|
+
* `posture === undefined`(operator 面这一次读不到,见 {@link projectReadFacePosture})或
|
|
48
|
+
* `capFace` 既不是三态字面量也不是任意字符串(租户面那一位整键缺席时调用方会传 `undefined`,
|
|
49
|
+
* 这里按同一条闸处理,不单独分支)。
|
|
50
|
+
* 🔴 `capFace` 按**开集**读,与租户面型面同宽:除 `open`/`roots`/`null` 外的任意字符串仍当一个
|
|
51
|
+
* **能判别的词**收(不是「读不懂」),因为 sdk 的租户面型面本就留了同一个 `(string & {})` 逃生口。
|
|
52
|
+
*/
|
|
53
|
+
export declare function readFaceDisagreement(capFace: unknown, posture: ReadFacePostureView | undefined): string | undefined;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { escapeDisplayControlChars } from './fleetTaskDesc.js';
|
|
2
|
+
/** `note` 上屏前的封长(UTF-16 单元,按**转义后**的字节数算——见 {@link capForDisplay})。它是
|
|
3
|
+
* 一句人话不是一个词,给得比 source/face 宽。 */
|
|
4
|
+
const READ_FACE_NOTE_MAX = 256;
|
|
5
|
+
/** `source` / 分歧句里词形的封长(与本包其余 detail 铸点同值同理由;同样按转义后字节数算)。 */
|
|
6
|
+
const READ_FACE_WORD_MAX = 40;
|
|
7
|
+
/**
|
|
8
|
+
* UNTRUSTED-for-display 呈前处理:**先转义、后按转义结果封长**——顺序反过来会让预算失守
|
|
9
|
+
* (`escapeDisplayControlChars` 把每个不可见字符改写成 6 字符的 `\uXXXX`,截长在前会把一段纯
|
|
10
|
+
* 控制字符的原文送成六倍长的显示串;文件头注有完整推导)。
|
|
11
|
+
*
|
|
12
|
+
* 封长点额外避开两种会留下断裂片段的位置:
|
|
13
|
+
* · 一枚 `escapeDisplayControlChars` 铸出的 `\uXXXX` token 中间(留半截 `\u00` 比整枚不渲更坏 ——
|
|
14
|
+
* 它看起来像一个完整答案的开头);
|
|
15
|
+
* · 一对合法代理对(如 emoji)中间——`DISPLAY_UNSAFE` 只转义**孤**代理项,合法对被放行原样透传,
|
|
16
|
+
* 在这里截断会人为地把它拆成一枚裸高位代理项留在末尾。
|
|
17
|
+
*/
|
|
18
|
+
function capForDisplay(raw, max) {
|
|
19
|
+
const escaped = escapeDisplayControlChars(raw);
|
|
20
|
+
if (escaped.length <= max)
|
|
21
|
+
return escaped;
|
|
22
|
+
let cut = max;
|
|
23
|
+
const tokenStart = escaped.lastIndexOf('\\', cut - 1);
|
|
24
|
+
if (tokenStart >= 0 && tokenStart + 6 > cut && /^\\u[0-9A-F]{4}$/.test(escaped.slice(tokenStart, tokenStart + 6))) {
|
|
25
|
+
cut = tokenStart;
|
|
26
|
+
}
|
|
27
|
+
if (cut > 0) {
|
|
28
|
+
const code = escaped.charCodeAt(cut - 1);
|
|
29
|
+
if (code >= 0xd800 && code <= 0xdbff)
|
|
30
|
+
cut -= 1;
|
|
31
|
+
}
|
|
32
|
+
return escaped.slice(0, cut);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `wiring.readFace` → 读数;**畸形一律 `undefined`**,绝不抛出。
|
|
36
|
+
*
|
|
37
|
+
* 🔴 整键缺席(老 worker,server <7.65.0)与在场但形坏,消费端拿到的都是同一个 `undefined` ——
|
|
38
|
+
* 两者对端说的是同一句话(「这一面这一次答不出来」)。「为什么答不出来」不是这一位该回答的,
|
|
39
|
+
* 是调用方自己知道的事(它有没有拿到 operator 响应;见 {@link readFacePostureDetail} 的
|
|
40
|
+
* `opts.reachable` 参数,那条信息只能由调用方——不是本读器——提供)。
|
|
41
|
+
* ⚠️ `face` 只认三态字面量(闭集,坏词一律判畸形);`source` 非空串即收(开集,理由见上);`note`
|
|
42
|
+
* 允许空串(它是自由文本,空文本本身也是一句读数,不是「读不出」)。
|
|
43
|
+
*/
|
|
44
|
+
export function projectReadFacePosture(wiring) {
|
|
45
|
+
if (typeof wiring !== 'object' || wiring === null || Array.isArray(wiring))
|
|
46
|
+
return undefined;
|
|
47
|
+
if (!('readFace' in wiring))
|
|
48
|
+
return undefined;
|
|
49
|
+
const rf = wiring.readFace;
|
|
50
|
+
if (typeof rf !== 'object' || rf === null || Array.isArray(rf))
|
|
51
|
+
return undefined;
|
|
52
|
+
const r = rf;
|
|
53
|
+
if (r.face !== 'open' && r.face !== 'roots' && r.face !== null)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (typeof r.source !== 'string' || r.source.length === 0)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (typeof r.note !== 'string')
|
|
58
|
+
return undefined;
|
|
59
|
+
return { face: r.face, source: r.source, note: r.note };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 三态措辞的**唯一铸点**(三端共用一句话;别在各端的行装配里另写一遍——与
|
|
63
|
+
* `writeProtectionDoctorDetail` / `sqlEngineDoctorDetail` 同一条纪律)。
|
|
64
|
+
*
|
|
65
|
+
* 🔴 三句刻意逐字互异(黑盒锚):
|
|
66
|
+
* ① `view` 在场 —— `read face <open|roots|not pinned> (source: <source> — <note>)`;
|
|
67
|
+
* ② `opts.reachable === false` —— 「未观测」:这次进程没读到 operator 响应,与「这台部署没有
|
|
68
|
+
* READ 档」是两件事,消费端不许把它读成后者;
|
|
69
|
+
* ③ `reachable:true` 但 `view` 仍缺席 —— 「不报」:响应读到了,只是这一位读不出来。
|
|
70
|
+
* 🔴 **不武断咎为版本**:`projectReadFacePosture` 把「整键缺席(老引擎,<7.65.0)」与
|
|
71
|
+
* 「在场但形坏(≥7.65.0 的引擎送出一个本读器解不出来的响应)」折成同一个 `undefined`
|
|
72
|
+
* (见该函数顶注),句子因此**不能**替其中一种情形撒谎——「需要更新引擎」对第二种情形是一句
|
|
73
|
+
* 误导故障排查的假话。措辞同时点出两种成因,不擅自替调用方选一种。
|
|
74
|
+
*/
|
|
75
|
+
export function readFacePostureDetail(view, opts) {
|
|
76
|
+
if (view !== undefined) {
|
|
77
|
+
const faceWord = view.face === null ? 'not pinned' : view.face;
|
|
78
|
+
const source = capForDisplay(view.source, READ_FACE_WORD_MAX);
|
|
79
|
+
const note = capForDisplay(view.note, READ_FACE_NOTE_MAX);
|
|
80
|
+
return note.length > 0
|
|
81
|
+
? `read face ${faceWord} (source: ${source} — ${note})`
|
|
82
|
+
: `read face ${faceWord} (source: ${source})`;
|
|
83
|
+
}
|
|
84
|
+
if (!opts.reachable) {
|
|
85
|
+
return "read face not observed (this end could not read the engine's diagnostics)";
|
|
86
|
+
}
|
|
87
|
+
return 'read face not reported by this engine (an engine below 7.65.0, or a response this end could not parse)';
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 租户面 `capabilities.readFace` 与本面 `posture.face` 分歧时的一句话;**只答分歧,不答一致**
|
|
91
|
+
* ——一致不是新闻(两位的在场时点天然不同源,「相符」证不了什么也不该被渲成保证),本函数只在
|
|
92
|
+
* 两者**确实不同**时才开口。
|
|
93
|
+
*
|
|
94
|
+
* 🔴 两个入参**任一**答不出来 ⇒ `undefined`(诚实缺席,不是「没有分歧」——见不到两位就判不了):
|
|
95
|
+
* `posture === undefined`(operator 面这一次读不到,见 {@link projectReadFacePosture})或
|
|
96
|
+
* `capFace` 既不是三态字面量也不是任意字符串(租户面那一位整键缺席时调用方会传 `undefined`,
|
|
97
|
+
* 这里按同一条闸处理,不单独分支)。
|
|
98
|
+
* 🔴 `capFace` 按**开集**读,与租户面型面同宽:除 `open`/`roots`/`null` 外的任意字符串仍当一个
|
|
99
|
+
* **能判别的词**收(不是「读不懂」),因为 sdk 的租户面型面本就留了同一个 `(string & {})` 逃生口。
|
|
100
|
+
*/
|
|
101
|
+
export function readFaceDisagreement(capFace, posture) {
|
|
102
|
+
if (posture === undefined)
|
|
103
|
+
return undefined;
|
|
104
|
+
if (capFace !== null && typeof capFace !== 'string')
|
|
105
|
+
return undefined;
|
|
106
|
+
if (capFace === posture.face)
|
|
107
|
+
return undefined;
|
|
108
|
+
const capWord = capFace === null ? 'not pinned' : capForDisplay(capFace, READ_FACE_WORD_MAX);
|
|
109
|
+
const wireWord = posture.face === null ? 'not pinned' : posture.face;
|
|
110
|
+
return `capabilities says ${capWord}, wiring says ${wireWord}`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* **编译期对账钉**(不出公面):sdk 的 `ReadFacePosture` 必须能赋给本视图 —— 这是本包 sdk 地板抬到
|
|
114
|
+
* 8.5.0 的直接理由:<8.5.0 上这个名字根本不存在,`tsc` 报「没有导出成员」。
|
|
115
|
+
*
|
|
116
|
+
* 🔴 反向**刻意不钉**(本视图不必能赋给 sdk 形):`source` 在本视图上放宽成任意 `string`(开集读,
|
|
117
|
+
* 见字段注),那是**故意比铸点宽**——窄读域只许等于或宽于铸点域,钉反向会把这条纪律反过来判成错。
|
|
118
|
+
*/
|
|
119
|
+
const _readFacePostureShapePin = (w) => w;
|
|
120
|
+
void _readFacePostureShapePin;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
| peer:wire 契约 | `@sema-agent/sdk` **>=8.4.0**(value-level,非 type-only;**0.60.0 抬版**,四条硬理由见 §24a 与 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 注;上一次是 0.59.0 的 `>=8.3.0`)。🔴 支持窗同批收到 **engine ≥7.64.0**:sdk 8.4.0 与 7.63.0 及以前的 wire **不同窗** | `package.json` `peerDependencies` |
|
|
24
24
|
| peer:会话词汇表 | `@sema-agent/agent-types` **>=0.2.0**(type-only,零运行时) | 同上 |
|
|
25
25
|
| runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
|
|
26
|
-
| 公开导出面 | **
|
|
26
|
+
| 公开导出面 | **864** 个运行期符号(+ 43 个测试钩;= 工作树当下的值 = 0.61.0 三件 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.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
|
|
|
@@ -111,7 +111,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
111
111
|
|
|
112
112
|
## §2 公共导出面地图(按域)
|
|
113
113
|
|
|
114
|
-
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**
|
|
114
|
+
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**864** 项)。
|
|
115
115
|
> 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
|
|
116
116
|
> **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
|
|
117
117
|
> 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
|
|
@@ -135,12 +135,12 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
135
135
|
`WorkflowsGateUnknownDenial` 四形**不在**基线里,`src/selfOrchestrationDenial.ts` 对基线贡献
|
|
136
136
|
**4** 项运行期导出(三个函数 + `SELF_ORCHESTRATION_RETRY_WITHOUT`)。
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
864 项的内部构成(帮助端估读表大小):**256** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
|
|
139
139
|
(矩阵、键集、env 名、锚串)而非可调用物;**5** 项是 PascalCase 运行期值
|
|
140
140
|
(`ControlRouter` / `ControlSafetyError` / `HitlBridge` / `HitlSafetyError` / `DecideTransportRetryExhaustedError`);
|
|
141
141
|
**41** 项是 `*For(sessionKey, …)` 的 per-session 变体(§6;其中 `engineNamespaceKeyFor` 是命名巧合 —— 参数是 baseUrl 不是 sessionKey,见域 14)。
|
|
142
142
|
|
|
143
|
-
### 2b. 域图(16 域,逐域计数之和 =
|
|
143
|
+
### 2b. 域图(16 域,逐域计数之和 = 864)
|
|
144
144
|
|
|
145
145
|
| # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
|
|
146
146
|
|---|---|---|---|---|---|
|
|
@@ -158,7 +158,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
158
158
|
| 12 | **workflow 与后台工作视图** | 19 | `projectWorkflowRun` · `createLiveWorkflowSource` · `ensureWorkflowActivityLedger` · `readWorkflowActivityLedger` · `stopWorkflowActivityLedger` · `resetWorkflowActivityLedgers` · `createBackgroundView` · `projectBackgroundView` · `recordWorkflowAgentTaskId` · `agentDisplayStatus` | 活过一个 turn 的长任务读面:workflow run + 跨 session 后台任务归一表(`assistant.tasks` 与 fleet SSE **两源独立降级**) | `src/workflow.ts`、`src/workflowClient.ts`、`src/workflowMonitor.ts`、`src/agentSession/backgroundView.ts`(+ 纯类型 `src/agentSession/contract.ts`) |
|
|
159
159
|
| 13 | **座位 IPC 契约** | 33 | `LOCAL_SESSIONS_SPEC` · `SEAT_METHOD_NAMES` · `SEAT_EVENT_TYPES` · `isLocalSessionEvent` · `isToolPermissionRequest` · `toolPermissionRequestId` · `SEAT_VALIDATOR_KEY_COVERAGE` | desktop↔web 座位 IPC 契约的**单一真源**(此前两边各一份、名字零重合 ⇒ 编译器永远不会告诉你它们漂了)。🔴 加 verb 忘了加 `LOCAL_SESSIONS_SPEC` **不报错**:preload 不注册 channel、渲染端读到 `undefined` | `src/seatContract.ts`(**零 import**,纯类型 + 常量 + 纯谓词)。🔴 **证据等级标注(0.42.0,test [5087] 的「语料**种类**缺口」/ cli [5088] 认领件)**:该文件里所有以「CC 如何如何」为形的断言(`212 methods` / `854-channel census` / 方法名逐字保留 / `fQe` 逐字段对照 / 一切 `.vite/build/index.chunk-*.js` 坐标)**证据等级 = 桌面 unpack,本地语料库不可复验** —— 本仓手边可复验的参照语料**只覆盖终端 CLI 形态**的静态产物,拿它去 grep 桌面壳里的符号只会零命中,而零命中在这里**既不证真也不证伪**。复核这些断言**不得**拿本仓语料当反证 |
|
|
160
160
|
| 14 | **宿主端口与会话槽** | 26 | `installHost` · `installHostFor` · `hostPortMisses(For)` · `DEFAULT_SESSION_KEY` · `hostEnv` · `unrefTimer` · `parseLocaleTag` / `pickUiLanguage`(#244 F4 族D A-028.20:locale tag 手术单源 + UI 语言判定;与 `resolveRegionHint` 双出口成文 —— 语言偏好域 en/zh ≠ 地址可达域 cn/intl/unknown,`zh-Hant` 前者 zh 后者 intl 是设计)· `engineNamespaceKeyFor` / `mergeSessionMapRecord` / `mergeEngineEntry`(A-028.12:会话 id 映射单一键形 + merge 判定;存储经 `SessionMapStorePort` 归端 —— cli 文件锁/原子写,web localStorage)| 进程/端级装配层(settings/fs/queue/timers/session/log/probe),与 per-turn 的 `AdapterContext` **分层**。头注的判定规则:**这个能力每 turn 都会变吗?** 会 ⇒ `ctx`;不会 ⇒ `installHost` | `src/host.ts`、`src/hostEnv.ts`、`src/sessionSlot.ts`、`src/unrefTimer.ts`、`src/env/{localeGeo,localeTag,uiLanguage}.ts`、`src/sessionMap.ts` |
|
|
161
|
-
| 15 | **控制面与传输** |
|
|
161
|
+
| 15 | **控制面与传输** | 99 | `ControlRouter`(+ `ControlSafetyError`)· `makeEngineWireClient` + `resolveWireAuth` · `installEngineWireTarget` · `diagnoseSseIdleTear` / `isSseIdleError` · `attemptActiveRunSelfHeal` + `activeRunBusySignal` + `activeRunSelfHealRow` / `activeRunBusyHeadlessRow` · `kickEngineCapsProbe` / `engineCapTrue` / `invalidateEngineCaps(baseUrl, probe?)`(#307 S25:引擎温切后的 caps 生产失效口 —— kick 自带幂等闸,同 baseUrl 重启后不显式失效就永远读到旧引擎那一版能力位;调用方 = 壳的 respawn/restartEngine。🔴 **推荐两参形**:第二参给替代探测则「推进代际 + 注册新探测」在同一同步块内完成,失效与下一次 kick 之间那个「等待者读到未判」的窗按构造不存在;单参形保留给「只丢缓存、这一刻没有替代探测」的调用方,那种情形下读到未判是诚实结局) · `engineCapValue`(S-131,0.59.0:能力位的**四态**通用读口 `unobserved`/`not_reported`/`null`/`value` —— 既有三口把四种「读不出」全折成 fail-closed 一档,对**放行**问题是对的、对**读面/诊断**问题是错的:`null`(引擎明确说没有,正面事实)会与「老引擎不报」「还没探到」在屏上同形。`value` 位刻意不做形校验,形归各能力位自己的窄读器,详见 §23b)· `projectSqlEngineCapability` / `observedSqlEngine` / `noteEngineCapsForSqlEngine` / `forgetSqlEngineReading` / `sqlEngineDoctorDetail`(S-131,0.59.0:`Capabilities.sql`(server ≥7.60.0)的四态读面**从壳侧上收** —— 归层债,0.58.0 时它长在壳里正是因为包侧没有嵌套对象读口。🔴 **导出名与壳侧那份逐字同名 = drift-lock**;`txnMode: null` 是铸点域内的合法值不是畸形;换代失效口清成未观测而不是留旧值,详见 §23b)· `mapBrainStatusToRetry` · `waitForClaimRelease` + `CLAIM_RELEASED_STATES` / `CLAIM_HELD_STATES` · `atMostOnceFailureClass` / `readSteerDelivery` · `clearRunningChoiceOffer`(Inkglow-1085 P0b①:「Do nothing」登记的清口 —— 端的「重新打开操作菜单」入口;登记在场时 attemptActiveRunSelfHeal 不整卡重弹,not-parked 结局带 `alreadyOffered: true` 判别位,端据此降级渲一行)· `INTERACTIVE_WAY_OUT`(默认出路串单源)· `engineSessionParamFor`(design/285 批 0:`?session=` 派生的 **per-key** 形 —— `hostSessionFor(sessionKey)?.currentSessionId()` + [1501] 空串归一;零参 `engineSessionParam()` = 默认槽兼容层,取值链逐字等价)· `normalizeWirePrincipal`(A-028.10:principal 在场性 trim 原语 —— 全空白=缺席不发头,engineWireTarget 两臂/makeEngineWireClient/壳 livePrincipal 同尺)· `readSessionMemoryStatus` / `classifyMemoryStatusFailure` / `readCaptureOptOut` / `readLastCapture`(S-53 会话记忆姿态读面,0.48.0:失败分诊**码优先**——两个 404 分道 `not_found.session` / `not_found.route`,无码 404 不猜落 failed;五键逐键缺席语义两个合读器,`lastCapture` 三态的判别材料是 `committedCount` 不是本键;IO 归宿主注入 `MemoryStatusClientLike`,详见 §11) · `classifyTurnWireError` / `isWireTransportError` / `isPreStreamDrainingReject` / `isResumeAtRejection` / `drainingRetryDelayMs` / `scenarioDenyFromError` + `WIRE_NETWORK_ERROR_PATTERN`(A-028.11/.13:turn 错误分型判定半场,人话文案与渲染归端)· `resumeRetryLaterFromError`(L-102,0.57.0:resume 族**时间性拒绝**二码的判型半场 —— 这一族里唯一**带得出「等多久」**(`retryAfterSec`)的两个码。🔴 闭集**不是**「哪些码可以等」的名单:同族 `resume.row_recycling` 同样可等(窗口自清)只是没有秒数 ⇒ 本读口返回 `null` 只意味着没命中这两码,详见 §21) · `projectWriteProtectionCapability` / `observedWriteProtection` / `noteEngineCapsForWriteProtection` / `forgetWriteProtectionReading` / `writeProtectionDoctorDetail` / `projectWriteProtectionPosture` / `writeProtectionPostureDetail`(S-138,0.60.0:`Capabilities.writeProtection` (engine ≥7.63.0)的**四态**读面 + operator 面行表 —— 与上面 S-131 那一格**同构同纪律**。🔴 三位刻意不合成一个布尔;`not_reported` **不是**「这台部署没有写保护表」(座位缺席恰是缺省表在岗);逐行 name/kind 走**最小披露**只上 operator 面,详见 §24e)· `projectReadFacePosture` / `readFacePostureDetail` / `readFaceDisagreement`(S-167,0.61.0:operator 面 `diagnostics.wiring.readFace`(engine ≥7.65.0)的窄读器 —— 与上面S-138 那一格**同构同纪律**。🔴 三句「答不出来」逐字互异且没有一句等于「没有 READ 档」;`source` 按开集读;与租户面 `capabilities.readFace` 刻意不合流,只带一个纯比较函数,详见 §25)| 上行通道的**监管**半场(submit / steer / kill / 队列命令定序)+ 传输构造、caps 探测、SSE 断流分诊、**409 active-run 自愈** | `src/controlRouter.ts`、`steering.ts`、`sseIdleTriage.ts`、`retryStatus.ts`、`diagnostics.ts`、`engineWireSdk.ts`、`engineWireTarget.ts`、`src/principalWire.ts`、`src/wireErrorTriage.ts`、`engineSessionParam.ts`、`engineCapsCache.ts`、`liveInitToolFace.ts`、`adapter/activeRunSelfHeal.ts`、`src/sessionMemoryStatus.ts`、`src/writeProtectionCapability.ts`、`src/readFacePosture.ts` |
|
|
162
162
|
| 16 | **引擎词汇表与包自检** | 59 | `CONFIG_REFUSAL_CODES` / `isConfigRefusalCode` · `STOP_CONFLICT_CODES` · `isInterruptedToolEndCode` · `isRewindFamilyCode` · `CLIENT_VERBS` · `compensationSplitViolations` · `DELEGATION_CAP_CODES` / `isDelegationCapCode` / `DELEGATION_CONCURRENCY_CAP` / `DELEGATION_SESSION_CAP`(0.38.0 #318 件④:core 5.48.0 design/323 委派席位到限**两码,处置不对称禁合并** —— 并发帽=**可等**(兄弟结束即有位)/ 会话累计帽=**等也没用**(这条会话的配额用尽))· `CONFIG_DELEGATION_ENTRY_CAPS`(同批入 `CONFIG_REFUSAL_CODES` 识别表)· `delegationCapDispositionOf` / `MCP_SERVER_REVOKED`(0.39.0 载体到货消费件:core 5.50.0 补 `{ error: code, code }` 孪生拼法后两码真上 `tool_end.errorCode`,0.38.0「先立词不落消费分支」的已知局限自此解除;处置轴 `wait-for-slot` / `reuse-existing-or-await-reap` 机器可读(累计帽=retained-window 帐,行回收配额即回,处置=SendMessage 复用,**非**「换会话/永久耗尽」——0.38.0 段该句系勘误),未知 `delegation.*` 码 ⇒ `undefined`;`mcp.server_revoked` = 操作员 mid-session 吊销 server 后的工具面本地闸(被吊销的 server **名**今天不过 wire 境:detail.server 是进程内位,抬升腿只 lift code——归因渲染候 core 补 typed detail,已点名);载体门 = engine-vocab G3 腿锚 core dist 铸点)· `CAPABILITY_SELF_ORCHESTRATION_REQUIRED`(S-81,server 7.57.0:提交面的 selfOrchestration 准入拒绝码。🔴 **复用码** —— 与其它 `capability.*` 501 同体形而处置不同,消费点必须按**恰等**判、绝不放宽成前缀判;判型与「去键重发一次」归 `src/selfOrchestrationDenial.ts`,详见 §13) · `GATE_PARKED_ERROR_CODE`(0.60.0:「门把这次调用 park 了」的机读码单源 —— `frameRouter` 的连坐/中断判据与 `gateOutcome.isGateParkedToolEnd` 读同一个常量;真机黑盒直证 park 短路帧上**没有 `gate`**,这个码是那一形唯一的信号,详见 §24c)| 三端分臂共用的**去字面化** `errorCode` 词表(病根正是三端各抄一份字面);编译期 verb 闭合门;搬迁补偿登记表 | `src/engineErrorCodes.ts`(计数以 `scripts/public-export-baseline.json` 为准,别手抄;A-028.11/.13 补 `DRAINING_ERROR_CODE`/`SCENARIO_NOT_ALLOWED_ERROR_CODE`/`RESUME_AT_ERROR_CODE_PREFIX`;#318 件④ 补 `delegation.*` 族四位 + `config.delegation_entry_caps`;0.39.0 补三新码消费件三位);S-81 补 `capability.self_orchestration_required` 一位;L-102 补 `resume.*` 时间性拒绝二码 + 闭集 `RESUME_RETRY_LATER_CODES` 三位)、`src/classifierVerdictWire.ts`、`src/compensations.ts`、`src/clientSlice.ts` |
|
|
163
163
|
|
|
164
164
|
🔴 **`engineErrorCodes` 的开集纪律**(该文件头注逐字):这些 `ReadonlySet` / 前缀谓词一律是**识别表**,
|
|
@@ -3718,7 +3718,7 @@ toolName,或 fs 写 / shell 的**名字腿**)。于是:
|
|
|
3718
3718
|
| G4 | `-p` 下 plan-mode ExitPlanMode | 文案含「a plan review」,与 G3 那句**逐字不同** |
|
|
3719
3719
|
| G5 | 同上两形 | result 帧上 additive `errorCode` **整键缺席**(0.60.0 行为变化;修前是 `review.pending`) |
|
|
3720
3720
|
| G6 | 引擎发一个本客户端不认识的终局因由词 | result 帧 `is_error:true` 且 errors[] 里**逐字**出现那个词;绝不 `subtype:"success"` |
|
|
3721
|
-
| G7 | 跨 7.64.0 升级过的部署,读一条**升级前**的历史 run | `GET /v1/runs/:id`
|
|
3721
|
+
| G7 | 跨 7.64.0 升级过的部署,读一条**升级前**的历史 run | `GET /v1/runs/:id` 回体仍是平面形,而客户端投影出的终帧 **kind 语义相同**(park 仍是 park、failed 仍是 failed);不要求字节级同形——历史平面行的 additive `errorCode` 残留可与新行不同(test 双轨实测的窄形差异,记账不修) |
|
|
3722
3722
|
| G8 | 同一 session 已有活跃 run 时再提交 | 409 拒绝信封照旧被投影成「This session is locked by an earlier run…」那一行(拒绝信封没换形) |
|
|
3723
3723
|
| G9 | auto 模式下分类器限额回落窗走完自动拒一次 Bash | 转录/审计面渲「自动拒(限额回落)」那一句,**不是**「已转后台候批」 |
|
|
3724
3724
|
| G10 | 普通审批窗 TTL 到期 | 渲「窗到期」那一句;与 G9 **逐字不同**(三条窗词分得开) |
|
|
@@ -3731,7 +3731,7 @@ toolName,或 fs 写 / shell 的**名字腿**)。于是:
|
|
|
3731
3731
|
| G17 | 审批窗走完(默认 300s)未决走 park 路径 | `tool_end` 帧上**没有** `gate`、只有 `errorCode:"gate.parked"`;端仍渲得出「已转后台候批」那一句(读的是第二形谓词),**不**渲成「窗走完拒了」 |
|
|
3732
3732
|
| G18 | 同步提交直接撞上审批暂停 | 200 回体是 `{taskId, sessionId, status:"suspended"}`(既无八键也无 `terminal`,**连门都没有**);客户端投影成 park(is_error true),**绝不**是 success 或 failed;`GET /v1/runs/:id` 此时读不到 `result`,端继续等而不报终局 |
|
|
3733
3733
|
| G18b | 同步提交直接撞上 **plan 复核** | 同上但 `status:"needs_review"`;客户端**仍弹 plan-review 卡**、终帧措辞说「a plan review」而不是「an interactive answer」(那一形上只有这个词能分出两者) |
|
|
3734
|
-
| G19 | 三臂 decide(不带 / 带 `settledBy` / 带 `hostDecision`) | 三臂全 200 且 `gate.settlement`
|
|
3734
|
+
| G19 | 三臂 decide(不带 / 带 `settledBy` / 带 `hostDecision`) | 三臂全 200 且 `gate.settlement` 一致;客户端**推荐路径 `HitlBridge.decideTool`** 的发送面零这两个键(抓包直证);🔴 判据只覆盖这条路径——`makeEngineWireClient(...).approvals.decide` 是 SDK 透传层,调用方显式塞进去的键会原样上 wire(server 不读,属死字节);端**不许**拿裸 wire client 发 decide 带这两键 |
|
|
3735
3735
|
|
|
3736
3736
|
### 24j. 常驻门
|
|
3737
3737
|
|
|
@@ -3744,3 +3744,111 @@ toolName,或 fs 写 / shell 的**名字腿**)。于是:
|
|
|
3744
3744
|
| `scripts/run-public-surface-test.mjs` | peer 地板 / README 三处逐字核 + 公面导出基线 **836 → 861**(测试钩 42 → 43) |
|
|
3745
3745
|
| `scripts/run-client-core-typeshape-test.mjs` | 棘轮 unknown 出境 **277 → 282**(净 +5,逐格记账 + 同批退役 4 格);B4 与裸 unknown 返回**未动** |
|
|
3746
3746
|
| `scripts/run-client-core-singleton-test.mjs` | 清单 282 → **285** 条;dupRisk=high 上限 **95 → 96**(`writeProtectionCapability.readingByBase`,与 SQL 那一格同族) |
|
|
3747
|
+
|
|
3748
|
+
|
|
3749
|
+
## §25 🆕 sdk 8.5.0 提货 · READ 容纳面档位 + 来源的 operator 面窄读器(S-167,0.61.0)
|
|
3750
|
+
|
|
3751
|
+
本节一件:operator 面 `GET /v1/diagnostics/wiring` 新增 `readFace: ReadFacePosture`
|
|
3752
|
+
(server ≥7.65.0)—— 这台部署 READ 容纳面此刻的**生效档**,**+ 这一档是谁定的**。与 §24e 的
|
|
3753
|
+
`writeProtection` 半场同构同纪律(防御读 / 唯一措辞铸点 / UNTRUSTED-for-display),规模小得多:
|
|
3754
|
+
**纯 additive**,无退役、无形变,老引擎(<7.65.0)上整键缺席。
|
|
3755
|
+
|
|
3756
|
+
### 25a. peer 地板 `>=8.4.0` → `>=8.5.0`
|
|
3757
|
+
|
|
3758
|
+
| 面 | 0.60.x | 0.61.0 | 端要做什么 |
|
|
3759
|
+
|---|---|---|---|
|
|
3760
|
+
| peer 地板 | `@sema-agent/sdk >=8.4.0` | **`>=8.5.0`** | 提货前先抬自己的 sdk 依赖 |
|
|
3761
|
+
| 支持窗(本读器) | — | engine ≥7.65.0 | 老引擎上整键缺席,读器按缺席处置——**不是** BREAKING,提交面/其余读面零影响 |
|
|
3762
|
+
| `Capabilities` | 94 键 | 94 键(未动) | 型面本身未动,只加了语义注;`run-engine-caps-ledger-test` 台账零改 |
|
|
3763
|
+
|
|
3764
|
+
硬理由见 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 注:本包新模块
|
|
3765
|
+
`src/readFacePosture.ts` type-only import `ReadFacePosture` 并用它钉编译期对账
|
|
3766
|
+
(`_readFacePostureShapePin`)。<8.5.0 上这个名字根本不存在,`tsc` 报「没有导出成员」。
|
|
3767
|
+
|
|
3768
|
+
### 25b. 读器签名与形
|
|
3769
|
+
|
|
3770
|
+
```ts
|
|
3771
|
+
projectReadFacePosture(wiring: unknown): ReadFacePostureView | undefined
|
|
3772
|
+
|
|
3773
|
+
interface ReadFacePostureView {
|
|
3774
|
+
face: 'open' | 'roots' | null // 这台部署此刻的生效档;null = 没钉,引擎默认接管
|
|
3775
|
+
source: string // 这一档是谁定的(server 闭四词,按开集读)
|
|
3776
|
+
note: string // 给运维的指路句;UNTRUSTED-for-display
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
readFacePostureDetail(view: ReadFacePostureView | undefined, opts: { reachable: boolean }): string
|
|
3780
|
+
readFaceDisagreement(capFace: unknown, posture: ReadFacePostureView | undefined): string | undefined
|
|
3781
|
+
```
|
|
3782
|
+
|
|
3783
|
+
#### 输入形与缺席语义
|
|
3784
|
+
|
|
3785
|
+
| 输入 | 读数 | 端要知道的 |
|
|
3786
|
+
|---|---|---|
|
|
3787
|
+
| `wiring.readFace` 整键缺席 | `undefined` | 老 worker(server <7.65.0)。**不是**「这台部署没有 READ 档」——那句话只有 `face: null` 才说得出口 |
|
|
3788
|
+
| 在场但 `face` 不在三态里 / `source` 非串或空串 / `note` 非串 | `undefined`,**不抛出** | 防御读:回体形不对时整只判畸形,不留半个对象。整键缺席与形坏同判 `undefined`——「为什么答不出来」不是这一位该回答的,见 `opts.reachable` |
|
|
3789
|
+
| `face: null` | `{ face: null, source, note }`(present) | 🔴 **这才是**「没有钉档」的正面事实;与上面两条「答不出来」不是一回事 |
|
|
3790
|
+
| `source` 在 server 闭四词(`env`/`center`/`posture`/`engine-default`)之外 | 原样透传 | 开集读——server 闭集之外还有 `(string & {})` 逃生口,窄读成枚举会在 server 加词当天把一个合法读数判没 |
|
|
3791
|
+
| `note` 为空串 | `note: ''` | 空文本本身也是一句读数,不是「读不出」;投影层不截长(截长是显示层的事,见 25c) |
|
|
3792
|
+
|
|
3793
|
+
### 25c. 三态措辞(黑盒锚,`readFacePostureDetail` 唯一铸点)
|
|
3794
|
+
|
|
3795
|
+
| # | 条件 | 句子(逐字) | 端要知道的 |
|
|
3796
|
+
|---|---|---|---|
|
|
3797
|
+
| ① | `view` 在场 | `read face <open\|roots\|not pinned> (source: <source> — <note>)`(`note`/`source` 呈前**先转义、后按转义结果封长** ≤256/≤40,不劈开转义 token 或合法代理对;空 `note` 省去 `— note` 半句) | 唯一会出现「not pinned」字样的分支是 `face === null`,不是①②两种「未观测/不报」 |
|
|
3798
|
+
| ② | `view` 缺席 且 `opts.reachable === false` | `read face not observed (this end could not read the engine's diagnostics)` | 这次进程压根没读到 operator 响应;与「没有 READ 档」不是同一件事 |
|
|
3799
|
+
| ③ | `view` 缺席 且 `opts.reachable === true` | `read face not reported by this engine (an engine below 7.65.0, or a response this end could not parse)` | 响应读到了,但这一位读不出来——**不武断咎为版本**:整键缺席(老引擎)与在场但形坏(≥7.65.0 引擎送出本读器解不出来的响应)在 `projectReadFacePosture` 折成同一个 `undefined`,句子同时点出两种成因,不擅自替调用方选一种 |
|
|
3800
|
+
|
|
3801
|
+
①②③ 对运维是三条不同的下一步,句子逐字互异;`opts.reachable` **只能由调用方提供**——本读器
|
|
3802
|
+
不持有连接状态,不猜。
|
|
3803
|
+
|
|
3804
|
+
### 25d. 与租户面 `capabilities.readFace` 的关系(刻意不合流)
|
|
3805
|
+
|
|
3806
|
+
两位是**同一份 boot 产物**的两个投影:租户面(`Capabilities.readFace: 'open'|'roots'|(string&{})|null|undefined`)
|
|
3807
|
+
只报生效档;operator 面多出来源与指路句。两条读面各自独立在场/缺席(operator 面这一位到得更晚,
|
|
3808
|
+
且那个端点本身是 operator-gated)——本包**不**把它们合成一位。
|
|
3809
|
+
|
|
3810
|
+
`readFaceDisagreement(capFace, posture)` 只答**分歧**(两者确实不同才开口;一致不是新闻,不铸
|
|
3811
|
+
正面「相符」句):
|
|
3812
|
+
|
|
3813
|
+
| capFace | posture | 结果 |
|
|
3814
|
+
|---|---|---|
|
|
3815
|
+
| `'roots'` | `{face:'open', …}` | `capabilities says roots, wiring says open` |
|
|
3816
|
+
| `null` | `{face:'open', …}` | `capabilities says not pinned, wiring says open` |
|
|
3817
|
+
| `'open'` | `{face:'open', …}` | `undefined`(一致,不开口) |
|
|
3818
|
+
| 任意值 | `undefined`(operator 面读不到) | `undefined`(诚实缺席,不是「没有分歧」) |
|
|
3819
|
+
| `undefined` / 非字符串非 `null` 的畸形值 | 任意 | `undefined`(判不了,不猜) |
|
|
3820
|
+
|
|
3821
|
+
渲染归端:doctor 两位都渲时,以 **operator 面(`posture`)为准**,不一致时额外说一句
|
|
3822
|
+
`readFaceDisagreement` 给出的话。
|
|
3823
|
+
|
|
3824
|
+
### 25e. 🔴 壳换装清单(cli 1.0.105 待做;本批不改 cli 仓,只登记坐标)
|
|
3825
|
+
|
|
3826
|
+
| # | 壳侧坐标 | 现状 | 待做 |
|
|
3827
|
+
|---|---|---|---|
|
|
3828
|
+
| 1 | `sema-cli/src/commands/doctor/permissionsPostureRow.ts` 的 `PermissionsPostureReading`(`writeProtection: string \| null` 那一位旁) | 只有 `writeProtection` 段(0.60.0 已接线) | 同构加一位 `readFace: string \| null`,渲 `readFacePostureDetail(observedReadFacePosture(), { reachable })`(读口/tee 姿势与 `observedWriteProtection` 同形——本批**未**提供 tee/换代失效口,壳侧若要常驻观测需自行仿 `writeProtectionCapability.ts` 那一套或改为一次性直读 `GET /v1/diagnostics/wiring`) |
|
|
3829
|
+
| 2 | 同文件的行装配(渲 `write-protection ${…}` 那一行旁) | — | 同构加一行 `read-face ${row.readFace}`,措辞不再自拼(唯一铸点在包) |
|
|
3830
|
+
| 3 | `sema-cli/src/commands/doctor/axesRows.ts`(第 226 行附近,`writeProtection` 的动态 import 段) | 动态 `import('../../sema/writeProtectionCapability.js')` 取 `writeProtectionDoctorDetail`/`observedWriteProtection` | 同构动态 import `readFacePosture.js` 取 `readFacePostureDetail`;🔴 **绝不在壳里自铸这句话**——留位注自己写的红线,与 write protection 那一位同律 |
|
|
3831
|
+
| 4 | doctor 的 caps/wiring 单飞探测口 | 已 tee 给 write protection(第 8 行,§24h) | operator 面 `GET /v1/diagnostics/wiring` 是**独立请求**(不是 caps 探测的一部分)——壳需要另起一次 fetch 或复用既有 wiring 诊断腿,把响应体喂 `projectReadFacePosture` |
|
|
3832
|
+
| 5 | doctor 渲染层(两位都渲的场合) | — | 同批可选:调 `readFaceDisagreement(capabilities.readFace, posture)`,不一致时追加一行(25d) |
|
|
3833
|
+
|
|
3834
|
+
**端可以不动的**:`GET /v1/capabilities.readFace`(租户面)的现有消费点——本批未改它的型或语义,
|
|
3835
|
+
只是新增了 operator 面的第二个投影。
|
|
3836
|
+
|
|
3837
|
+
### 25f. 黑盒判据(test 视角;不引用内部实现)
|
|
3838
|
+
|
|
3839
|
+
| # | 构造 | 期望 |
|
|
3840
|
+
|---|---|---|
|
|
3841
|
+
| G-r1 | 单机零配置部署,`sema doctor` 对 engine ≥7.65.0 | operator 面读到 `read face open (source: posture — …)` 或 `(source: env — …)`(取决于是否设了 `READ_FACE` env),**不是**「not observed」/「not reported」 |
|
|
3842
|
+
| G-r2 | 用户设 `READ_FACE=roots` 后起引擎,`sema doctor` | `read face roots (source: env — …)`;`source` 恒为 `env`(env 赢过其余来源) |
|
|
3843
|
+
| G-r3 | `sema doctor` 一次性进程,引擎不可达 | 渲 `read face not observed (this end could not read the engine's diagnostics)`,**不说**「这台部署没有 READ 档」 |
|
|
3844
|
+
| G-r4 | `sema doctor` 对 engine <7.65.0(整键缺席)**或** engine ≥7.65.0 送出本读器解不出来的响应(在场但形坏) | 渲 `read face not reported by this engine (an engine below 7.65.0, or a response this end could not parse)`;与 G-r3 那句逐字不同;句子不武断咎为版本(两种输入投影层折成同一个 `undefined`,措辞不许替其中一种撒谎) |
|
|
3845
|
+
| G-r5 | 部署把租户面与 operator 面钉成不同档(如 center 面已切但进程未重启) | doctor 额外渲 `capabilities says <X>, wiring says <Y>` 一行;两位相符时**不**渲这一行 |
|
|
3846
|
+
|
|
3847
|
+
### 25g. 常驻门
|
|
3848
|
+
|
|
3849
|
+
| 门 | 守什么 |
|
|
3850
|
+
|---|---|
|
|
3851
|
+
| 🆕 `scripts/run-read-face-posture-projection-test.mjs` | 三态措辞逐字互异 + 四 source 开集读 + `face:null` 正面事实 + 坏形不抛 + `note` 截长/消毒 + `readFaceDisagreement` 正反两格 + sdk `openapi.yaml` 的 `ReadFacePosture` 组件真字节(必填三键 + `additionalProperties:false`) |
|
|
3852
|
+
| `scripts/run-sdk-floor-test.mjs` | 地板 `8.5.0` 与 peer 声明**三位逐位等值**互绑 |
|
|
3853
|
+
| `scripts/run-public-surface-test.mjs` | peer 地板 / README 三处逐字核 + 公面导出基线 **861 → 864**(测试钩数未动) |
|
|
3854
|
+
| `scripts/run-integration-doc-freshness-test.mjs` | 本节新增坐标在盘 + 入库;§0a/§2/§2b 计数与基线等值 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.0",
|
|
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",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@sema-agent/agent-types": ">=0.2.0",
|
|
34
|
-
"@sema-agent/sdk": ">=8.
|
|
34
|
+
"@sema-agent/sdk": ">=8.5.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@sema-agent/agent-types": "^0.2.0",
|
|
38
38
|
"@sema-agent/core": "~7.3.0",
|
|
39
|
-
"@sema-agent/sdk": "^8.
|
|
39
|
+
"@sema-agent/sdk": "^8.5.0",
|
|
40
40
|
"esbuild": "^0.27.4",
|
|
41
41
|
"typescript": "^6.0.2"
|
|
42
42
|
}
|