@sema-agent/client-core 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -12
- package/dist/index.d.ts +16 -0
- package/dist/index.js +22 -0
- package/dist/request/printNotification.d.ts +33 -0
- package/dist/request/printNotification.js +116 -0
- package/dist/request/taskRequest.d.ts +17 -0
- package/dist/request/taskRequest.js +57 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,17 +23,19 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
23
23
|
|
|
24
24
|
## Scope
|
|
25
25
|
|
|
26
|
-
- **Today (0.
|
|
26
|
+
- **Today (0.10.0)** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
27
27
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
|
28
|
-
+ downstream projectors), the request-shaping surface
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
tool-approval wire, plan review, and the
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
plus everything that is genuinely
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
+ downstream projectors), the request-shaping surface **for both lanes** (interactive and the
|
|
29
|
+
headless `-p` print lane), the fleet/panel projections and the subagent wire family, the workflow
|
|
30
|
+
monitor contract + live source, the model catalog tables, and the **HITL family** (`hitl/`: the
|
|
31
|
+
D-1 approval bridge, the ask-gate stream bridge, the tool-approval wire, plan review, and the
|
|
32
|
+
pending-approvals push feed).
|
|
33
|
+
- **Planned** — what is still in the shell: the registry user-plane hand-copies (they retire onto
|
|
34
|
+
`@sema-agent/sdk/registry`, not into this package); plus everything that is genuinely
|
|
35
|
+
host-specific — Ink rendering and keyboard interaction, the print lane's stream-json projector
|
|
36
|
+
and stderr triage copy, filesystem persistence, pty/terminal, and the signal-path teardown.
|
|
37
|
+
The web session view (first target consumer) and the desktop client (second) consume the same
|
|
38
|
+
package.
|
|
37
39
|
|
|
38
40
|
## Dependencies
|
|
39
41
|
|
|
@@ -131,7 +133,7 @@ grew the host context to carry what a non-Node host must supply:
|
|
|
131
133
|
desktop host coalesces single-frame deltas at 16 ms; the 2.1.207 CLI line we track uses 100 ms),
|
|
132
134
|
so the cadence is host render policy while the merge *semantics* stay here, in one place.
|
|
133
135
|
|
|
134
|
-
0.6.0 – 0.
|
|
136
|
+
0.6.0 – 0.10.0 (batches B4 – B8) completed the frame dispatch and moved in everything that is not
|
|
135
137
|
host-specific. The blow-by-blow lives in the `src/index.ts` head comment (the source of truth); the
|
|
136
138
|
short version:
|
|
137
139
|
|
|
@@ -157,6 +159,20 @@ short version:
|
|
|
157
159
|
push channel over `approvals.stream()` that falls back to polling and periodically retries the
|
|
158
160
|
push leg — it deliberately does **not** take over the D-1 fetch, which must stay authoritative.
|
|
159
161
|
|
|
162
|
+
- **0.10.0** — the print lane (`-p`), the last batch on the migration bus. The request builder is
|
|
163
|
+
now the *only* construction point for both lanes: the shell's headless path used to assemble its
|
|
164
|
+
own `TaskRequest` literal, so the two lanes had silently drifted apart (`REQUEST_FIELD_MATRIX`
|
|
165
|
+
records every remaining difference field-by-field with its reason, plus a `gap` flag where the
|
|
166
|
+
difference is an omission rather than a decision). `unregisteredRequestKeys` makes that matrix
|
|
167
|
+
executable: the request that actually goes on the wire must account for every key it carries.
|
|
168
|
+
`request/printNotification` unifies the `task_notification` field set — the print exit now shares
|
|
169
|
+
the interactive lane's single judgement (`normalizeTaskNotification`) and carries the **residual
|
|
170
|
+
attribution** it used to drop (`stoppedBy`, `resumable`, `partial`, `exitCode`, `diagnostics`,
|
|
171
|
+
`result`, `lines`, `recentSteps`, `editedFiles`, `task_type`, `source`, `seq`, `injected`), which
|
|
172
|
+
matters most on the one lane with no human watching. Exactly one lane difference survives, and
|
|
173
|
+
it is CC's own: `status: killed` becomes `stopped` on the print exit. The stream-json projector
|
|
174
|
+
and the stderr triage copy deliberately stay in the shell.
|
|
175
|
+
|
|
160
176
|
Known intentional deltas from the CLI are enumerated in `ADAPTER_DIVERGENCES`. The *compensation*
|
|
161
177
|
ledger is a different table (`MIGRATED_COMPENSATIONS`): the entries there behave identically on
|
|
162
178
|
both sides — what it records is why a compensation exists and when it can be deleted.
|
|
@@ -165,7 +181,7 @@ both sides — what it records is why a compensation exists and when it can be d
|
|
|
165
181
|
|
|
166
182
|
```bash
|
|
167
183
|
npm install
|
|
168
|
-
node scripts/run-client-core-pure-test.mjs #
|
|
184
|
+
node scripts/run-client-core-pure-test.mjs # 1058 consumer-view checks (22 + 140 + 208 + 59 + 101 + 96 + 241 + 163 + 28); no CLI tree needed
|
|
169
185
|
node scripts/run-client-core-portability-test.mjs # 39 checks: kernel + A-layer + index closures, browser bundle
|
|
170
186
|
node scripts/run-client-core-diff-test.mjs # 1397 checks: differential + replay-id + ledger
|
|
171
187
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,21 @@
|
|
|
5
5
|
* 沿革:@sema-agent/wire-cc-adapter 0.1.0 = seam 类型 + id 确定性派生 + 首批踩坑纯函数;
|
|
6
6
|
* 0.1.2(#52a)= adapt() 管线首批(纯投影臂全落 + 壳态耦合臂投影成 ChromeEvent + 差分守卫);
|
|
7
7
|
* 0.2.0 = 迁入 sema-client-core 独立仓并改名(旧 npm 名 deprecate 指本包);
|
|
8
|
+
* 0.10.0 = **B8 批**(搬迁总线**末批**,设计稿 §8-5「print lane 收编」):
|
|
9
|
+
* ① **请求拼装合一收官**:B4 建的 `buildTaskRequest(input, lane)` 从此是 print lane
|
|
10
|
+
* (`seamQueryEngine.ask`)的**唯一**构造口 —— 壳里那份「`-p` 自己另拼一份」的字面量废除;
|
|
11
|
+
* 车道差异全在 `REQUEST_FIELD_MATRIX`(print 独有 finalVerification/limits/interactiveTools、
|
|
12
|
+
* TUI 独有 ultracode 等,逐条带理由与 `gap` 判断)。新增可执行判据 `unregisteredRequestKeys`
|
|
13
|
+
* —— **真上 wire 的那份**请求也要过表,再漏一个键就当场红(表以前只管得住构造器自己)。
|
|
14
|
+
* ② **`task_notification` 字段集合一**(`request/printNotification.ts`):print 出口帧与交互面
|
|
15
|
+
* 共用 `normalizeTaskNotification` 这**一把闸**(空 task_id 丢帧 / summary 兜底 / status 回落),
|
|
16
|
+
* 并补上**残局归因**(stoppedBy / resumable / partial / exitCode / diagnostics / result /
|
|
17
|
+
* lines / recentSteps / editedFiles / task_type / source / seq / injected)—— 修前这些位在
|
|
18
|
+
* `-p` 出口整段丢失,而 `-p` 恰恰是唯一没有人当场看着的车道。🔴 车道差异只剩 `status` 的
|
|
19
|
+
* killed⇒stopped 一条(CC 187 print 出口形,表里留注);去重键不出第二个名字,print 侧那份
|
|
20
|
+
* 手写算法废除,直接用 `taskNotificationDedupKeyFromWire`。
|
|
21
|
+
* ③ **留壳**(§8-5 明写,本批一行没动):`PrintStreamProjector`(stream-json 帧序/SSE 重铸)与
|
|
22
|
+
* 全部 stderr 分诊文案 —— 渲染面与人话面属端。
|
|
8
23
|
* 0.9.0 = **B7 批**(设计稿 §3 B7「完成事件单一权威 + HITL」):
|
|
9
24
|
* ① **HITL 族四件**(新目录 `hitl/`):`hitlBridge`(整搬,🔴 **D-1 两元组 verbatim 回显**
|
|
10
25
|
* 与「409 绝不自动重试」两段**逐字节**搬,pure 门 B7 段用冻结字面量 + 壳树双向对拍 +
|
|
@@ -200,3 +215,4 @@ export * from './hitl/askGateWire.js';
|
|
|
200
215
|
export * from './hitl/planReviewWire.js';
|
|
201
216
|
export * from './hitl/approvalsFeed.js';
|
|
202
217
|
export * from './compensations.js';
|
|
218
|
+
export * from './request/printNotification.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,21 @@
|
|
|
5
5
|
* 沿革:@sema-agent/wire-cc-adapter 0.1.0 = seam 类型 + id 确定性派生 + 首批踩坑纯函数;
|
|
6
6
|
* 0.1.2(#52a)= adapt() 管线首批(纯投影臂全落 + 壳态耦合臂投影成 ChromeEvent + 差分守卫);
|
|
7
7
|
* 0.2.0 = 迁入 sema-client-core 独立仓并改名(旧 npm 名 deprecate 指本包);
|
|
8
|
+
* 0.10.0 = **B8 批**(搬迁总线**末批**,设计稿 §8-5「print lane 收编」):
|
|
9
|
+
* ① **请求拼装合一收官**:B4 建的 `buildTaskRequest(input, lane)` 从此是 print lane
|
|
10
|
+
* (`seamQueryEngine.ask`)的**唯一**构造口 —— 壳里那份「`-p` 自己另拼一份」的字面量废除;
|
|
11
|
+
* 车道差异全在 `REQUEST_FIELD_MATRIX`(print 独有 finalVerification/limits/interactiveTools、
|
|
12
|
+
* TUI 独有 ultracode 等,逐条带理由与 `gap` 判断)。新增可执行判据 `unregisteredRequestKeys`
|
|
13
|
+
* —— **真上 wire 的那份**请求也要过表,再漏一个键就当场红(表以前只管得住构造器自己)。
|
|
14
|
+
* ② **`task_notification` 字段集合一**(`request/printNotification.ts`):print 出口帧与交互面
|
|
15
|
+
* 共用 `normalizeTaskNotification` 这**一把闸**(空 task_id 丢帧 / summary 兜底 / status 回落),
|
|
16
|
+
* 并补上**残局归因**(stoppedBy / resumable / partial / exitCode / diagnostics / result /
|
|
17
|
+
* lines / recentSteps / editedFiles / task_type / source / seq / injected)—— 修前这些位在
|
|
18
|
+
* `-p` 出口整段丢失,而 `-p` 恰恰是唯一没有人当场看着的车道。🔴 车道差异只剩 `status` 的
|
|
19
|
+
* killed⇒stopped 一条(CC 187 print 出口形,表里留注);去重键不出第二个名字,print 侧那份
|
|
20
|
+
* 手写算法废除,直接用 `taskNotificationDedupKeyFromWire`。
|
|
21
|
+
* ③ **留壳**(§8-5 明写,本批一行没动):`PrintStreamProjector`(stream-json 帧序/SSE 重铸)与
|
|
22
|
+
* 全部 stderr 分诊文案 —— 渲染面与人话面属端。
|
|
8
23
|
* 0.9.0 = **B7 批**(设计稿 §3 B7「完成事件单一权威 + HITL」):
|
|
9
24
|
* ① **HITL 族四件**(新目录 `hitl/`):`hitlBridge`(整搬,🔴 **D-1 两元组 verbatim 回显**
|
|
10
25
|
* 与「409 绝不自动重试」两段**逐字节**搬,pure 门 B7 段用冻结字面量 + 壳树双向对拍 +
|
|
@@ -257,3 +272,10 @@ export * from './hitl/approvalsFeed.js';
|
|
|
257
272
|
// 🔴 它**不是** `ADAPTER_DIVERGENCES`(那张表说的是 adapt 与 cli 行为不同的地方;本表里的东西
|
|
258
273
|
// 两侧行为相同)。自检口 `compensationSplitViolations()` 恒应为空。
|
|
259
274
|
export * from './compensations.js';
|
|
275
|
+
// ── B8 批:print lane 收编(2026-07-27,设计稿 §8-5 裁决 —— 总线末批)──────────────────────────
|
|
276
|
+
// `task_notification` 字段集合一:`-p` 的 SDK 出口帧从 6 位手写字面量换成与交互面**同一把闸**
|
|
277
|
+
// (normalizeTaskNotification)+ 一张逐字段对照表;残局归因(stoppedBy/resumable/partial/
|
|
278
|
+
// exitCode/…)从此不在 print 出口丢失。🔴 车道差异只剩 `status` 的 killed⇒stopped 一条(表里留注)。
|
|
279
|
+
// 去重键**不出第二个名字**:print 侧那份手写算法废除,直接用 `taskNotificationDedupKeyFromWire`。
|
|
280
|
+
// 请求面同批补上可执行判据 `unregisteredRequestKeys`(见 request/taskRequest.ts 文件末)。
|
|
281
|
+
export * from './request/printNotification.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** 帧身份位(壳侧从被投影的那条 SDK 消息上取,包内不铸号)。 */
|
|
2
|
+
export interface PrintFrameIdentity {
|
|
3
|
+
uuid?: unknown;
|
|
4
|
+
session_id?: unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* print 出口帧的**逐字段对照表** —— 与 `REQUEST_FIELD_MATRIX` 同一纪律:
|
|
8
|
+
* `cc:true` = CC 自己命名过的位,**改名即偏离 parity**;`cc:false` = 本次收编补上的归因位
|
|
9
|
+
* (wire 原名 = SDK `AgentEvent.task_notification` 词汇)。`from` 写明它从 wire 的哪一位来。
|
|
10
|
+
*/
|
|
11
|
+
export interface PrintNotificationFieldSpec {
|
|
12
|
+
/** print 出口帧上的键名。 */
|
|
13
|
+
field: string;
|
|
14
|
+
/** wire 侧来源键(`—` = 信封位/由归一层派生)。 */
|
|
15
|
+
from: string;
|
|
16
|
+
/** CC 逐字位(改名即偏离 parity)。 */
|
|
17
|
+
cc: boolean;
|
|
18
|
+
why: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const PRINT_NOTIFICATION_FIELD_MATRIX: readonly PrintNotificationFieldSpec[];
|
|
21
|
+
/**
|
|
22
|
+
* 引擎 `task_notification` 帧载荷 → CC `system/task_notification` 出口帧(print lane 唯一构造口)。
|
|
23
|
+
*
|
|
24
|
+
* 判定全部委托 `normalizeTaskNotification`(交互面同一把闸);缺席位一律**不铸空键**
|
|
25
|
+
* (tolerate-absent —— 老引擎不发的字段不该在出口变成 `undefined` 值键)。
|
|
26
|
+
* 返回 `null` = 该帧不该出口(task_id 空/缺)。
|
|
27
|
+
*/
|
|
28
|
+
export declare function taskNotificationToPrintFrame(n: Record<string, unknown>, identity?: PrintFrameIdentity): Record<string, unknown> | null;
|
|
29
|
+
/**
|
|
30
|
+
* 出口帧里出现了**没进上表**的键就点名它(与 `unregisteredRequestKeys` 同纪律):
|
|
31
|
+
* 顺手加一位很容易,加了之后「字段集合一」就又退化成一句注释 —— 加位必须先改表(带理由)。
|
|
32
|
+
*/
|
|
33
|
+
export declare function unregisteredPrintNotificationKeys(frame: Record<string, unknown>): string[];
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* request/printNotification.ts — **`task_notification` 字段集合一**(B8,多端改造设计稿 §8-5 裁决
|
|
3
|
+
* 第二件;§2.5 拆缝表 `seamQueryEngine.ts:690-708` 那一格)。
|
|
4
|
+
*
|
|
5
|
+
* ## 它解决的是什么
|
|
6
|
+
*
|
|
7
|
+
* 同一条引擎 `task_notification` 帧,壳里有**两个**消费者,各自决定「哪些字段算数」:
|
|
8
|
+
* · 交互 lane:`notifications.normalizeTaskNotification` → `renderTaskNotificationXml`
|
|
9
|
+
* (模型面 XML + 转录面),字段集 = `TaskNotificationFields` 15 位;
|
|
10
|
+
* · print lane(`-p` 的 SDK 出口):`seamQueryEngine.ask` 里**手写的一段对象字面量**,
|
|
11
|
+
* 只挑了 6 位(task_id / tool_use_id / status / output_file / summary / usage)。
|
|
12
|
+
*
|
|
13
|
+
* 后果是**残局归因在 `-p` 出口整段丢失**:引擎明明发了 `stoppedBy`(谁停的)/`resumable`
|
|
14
|
+
* (还能不能续)/`partial`(残果)/`exitCode` / `diagnostics` / `result` / `lines` /
|
|
15
|
+
* `recentSteps` / `editedFiles`,交互面 XML 里逐条都在,而**唯一没有人类当场看着的那条车道**
|
|
16
|
+
* ——集成/自动化用的 `-p --output-format stream-json`——拿不到任何归因位。
|
|
17
|
+
* (设计稿 §7.3(b) 记的是 fleet `bg_notification` lane 的同款不对称;print 出口这一处是
|
|
18
|
+
* B8 落码时逐行核出来的第二处,同族。)
|
|
19
|
+
*
|
|
20
|
+
* ## 合一的口径(逐字段留注 = `PRINT_NOTIFICATION_FIELD_MATRIX`)
|
|
21
|
+
*
|
|
22
|
+
* 🔴 **判据同源**:本模块**不重新判**任何一位 —— 先过交互面那把闸
|
|
23
|
+
* `normalizeTaskNotification`(空 task_id 丢帧 = RB-75 同族;summary 缺省兜底句;status 非串
|
|
24
|
+
* 回落 `completed`),再把归一结果映到 CC 的 print 帧信封上。两面从此只有**一份**判据。
|
|
25
|
+
*
|
|
26
|
+
* 🔴 **车道差异只剩一条**:`status` 的 `killed ⇒ stopped` 归一 —— 那是 CC 自己 print/SDK 出口的
|
|
27
|
+
* 形(FULL-SOURCE-187 line ~32320),XML 面按引擎原词渲染。这一条差异是**有理由的**,写在表里。
|
|
28
|
+
*
|
|
29
|
+
* **命名口径**:CC 已经命名过的 6 位保持 CC 逐字(`task_id` / `tool_use_id` / `output_file` /
|
|
30
|
+
* `status` / `summary` / `usage`);新增的归因位用 **wire 原名**(`stoppedBy` / `resumable` / …)
|
|
31
|
+
* —— 也就是 SDK `AgentEvent.task_notification` 的词汇表,`-p` 的 SDK 消费方与直连 SDK 的消费方
|
|
32
|
+
* 因此看到同一套键名,不必再学第三套。additive:老消费方按 key 取值,多出来的键不碍事。
|
|
33
|
+
*
|
|
34
|
+
* ## 留壳的部分(§8-5 明写)
|
|
35
|
+
*
|
|
36
|
+
* `PrintStreamProjector`(stream-json 帧序/SSE 重铸)与全部 stderr 分诊文案**留壳**:那是
|
|
37
|
+
* 渲染面与人话面,不是数据面。本模块只做「wire 帧 → CC 帧」这一跳。
|
|
38
|
+
*/
|
|
39
|
+
import { normalizeTaskNotification } from '../notifications.js';
|
|
40
|
+
export const PRINT_NOTIFICATION_FIELD_MATRIX = [
|
|
41
|
+
// ── 信封(CC system 帧形)────────────────────────────────────────────────────────────────────
|
|
42
|
+
{ field: 'type', from: '—', cc: true, why: "CC system 帧信封:{type:'system'}" },
|
|
43
|
+
{ field: 'subtype', from: '—', cc: true, why: "CC 完成通知的 subtype:'task_notification'(187 print/SDK 出口形)" },
|
|
44
|
+
{ field: 'uuid', from: '—', cc: true, why: '帧身份:壳侧从被投影的 SDK 消息取(包内不铸号)' },
|
|
45
|
+
{ field: 'session_id', from: '—', cc: true, why: 'CC 每帧带会话 id;壳侧透传' },
|
|
46
|
+
// ── CC 已命名的 6 位(逐字,改名即偏离 parity)──────────────────────────────────────────────
|
|
47
|
+
{ field: 'task_id', from: 'task_id', cc: true, why: 'CC 逐字 snake 名;空/缺 ⇒ 整帧丢弃(RB-75:空 id 会塌成共享桶互吞)' },
|
|
48
|
+
{ field: 'status', from: 'status', cc: true, why: "CC 逐字;**唯一车道差异** killed ⇒ stopped(187 print 出口形;XML 面按引擎原词渲染)" },
|
|
49
|
+
{ field: 'summary', from: 'summary', cc: true, why: '缺省走交互面同一句兜底(`Background task "<id>" <status>`)—— 修前 print 出口发空串' },
|
|
50
|
+
{ field: 'tool_use_id', from: 'toolUseId', cc: true, why: 'CC 逐字 snake 名(wire 是 camel);把通知钉回发起它的那次工具调用' },
|
|
51
|
+
{ field: 'output_file', from: 'output_file', cc: true, why: 'CC 逐字;后台输出落盘路径' },
|
|
52
|
+
{ field: 'usage', from: 'usage', cc: true, why: 'CC 逐字 optional;计量面(交互面 XML 反而不读它 —— 那是渲染面口径)' },
|
|
53
|
+
// ── B8 补上的残局归因位(wire 原名 = SDK task_notification 词汇)────────────────────────────
|
|
54
|
+
{ field: 'stoppedBy', from: 'stoppedBy', cc: false, why: '🔴 残局归因核心位:谁停的(用户/上限/引擎)。修前 `-p` 出口零携带' },
|
|
55
|
+
{ field: 'resumable', from: 'resumable', cc: false, why: '🔴 残局归因:还能不能 resume —— 自动化要靠它决定重试还是放弃' },
|
|
56
|
+
{ field: 'partial', from: 'partial', cc: false, why: 'server 1.258:被终止子代的残果(消费方须渲成不完整,不得当完整结果)' },
|
|
57
|
+
{ field: 'exitCode', from: 'exitCode', cc: false, why: '后台 bash 退出码(137=OOM 这类归因只在这一位上)' },
|
|
58
|
+
{ field: 'diagnostics', from: 'diagnostics', cc: false, why: '引擎侧诊断串(UNTRUSTED,service 已 redact)' },
|
|
59
|
+
{ field: 'result', from: 'result', cc: false, why: '完成结果正文(#49 C1:帧带 result 必入 XML,print 出口同理不得独漏)' },
|
|
60
|
+
{ field: 'lines', from: 'lines', cc: false, why: '尾部输出行(UNTRUSTED);交互面折进 <output-lines>' },
|
|
61
|
+
{ field: 'recentSteps', from: 'recentSteps', cc: false, why: '结构化残局步骤 {tool,target,outcome}[] —— **原样透传**,不降级成交互面那份显示串(SDK 消费方要结构)' },
|
|
62
|
+
{ field: 'editedFiles', from: 'editedFiles', cc: false, why: '结构化改档清单 {path,edits}[];同上原样透传' },
|
|
63
|
+
{ field: 'task_type', from: 'task_type', cc: false, why: 'lane 位(external/background_agent/…)—— 去重键的分域依据,消费方要能自己算同一把键' },
|
|
64
|
+
{ field: 'source', from: 'source', cc: false, why: 'external lane 的来源标识(交互面渲成开标签 from= 属性)' },
|
|
65
|
+
{ field: 'seq', from: 'seq', cc: false, why: '停机周期/批次计数([508]②)—— 与 task_id+status 一起构成去重键' },
|
|
66
|
+
{ field: 'injected', from: 'injected', cc: false, why: '[1818]§四:本帧**不**证明 XML 已进模型上下文;消费方须按此位判,不得按帧到达判' },
|
|
67
|
+
];
|
|
68
|
+
const PRINT_FRAME_KEYS = new Set(PRINT_NOTIFICATION_FIELD_MATRIX.map(r => r.field));
|
|
69
|
+
/**
|
|
70
|
+
* 引擎 `task_notification` 帧载荷 → CC `system/task_notification` 出口帧(print lane 唯一构造口)。
|
|
71
|
+
*
|
|
72
|
+
* 判定全部委托 `normalizeTaskNotification`(交互面同一把闸);缺席位一律**不铸空键**
|
|
73
|
+
* (tolerate-absent —— 老引擎不发的字段不该在出口变成 `undefined` 值键)。
|
|
74
|
+
* 返回 `null` = 该帧不该出口(task_id 空/缺)。
|
|
75
|
+
*/
|
|
76
|
+
export function taskNotificationToPrintFrame(n, identity = {}) {
|
|
77
|
+
const f = normalizeTaskNotification(n);
|
|
78
|
+
if (f === null)
|
|
79
|
+
return null;
|
|
80
|
+
const structured = (key) => Array.isArray(n[key]) && n[key].length > 0 ? { [key]: n[key] } : {};
|
|
81
|
+
return {
|
|
82
|
+
type: 'system',
|
|
83
|
+
subtype: 'task_notification',
|
|
84
|
+
task_id: f.taskId,
|
|
85
|
+
// CC 逐字:killed ⇒ stopped(唯一车道差异,表里有理由)。
|
|
86
|
+
status: f.status === 'killed' ? 'stopped' : f.status,
|
|
87
|
+
summary: f.summary,
|
|
88
|
+
...(f.toolUseId !== undefined ? { tool_use_id: f.toolUseId } : {}),
|
|
89
|
+
...(f.outputFile !== undefined ? { output_file: f.outputFile } : {}),
|
|
90
|
+
...(n.usage !== undefined ? { usage: n.usage } : {}),
|
|
91
|
+
// ── 残局归因(B8 补;归一层已做过类型与空值判定)──────────────────────────────────────
|
|
92
|
+
...(f.stoppedBy !== undefined ? { stoppedBy: f.stoppedBy } : {}),
|
|
93
|
+
...(f.resumable !== undefined ? { resumable: f.resumable } : {}),
|
|
94
|
+
...(f.partial !== undefined ? { partial: f.partial } : {}),
|
|
95
|
+
...(f.exitCode !== undefined ? { exitCode: f.exitCode } : {}),
|
|
96
|
+
...(f.diagnostics !== undefined ? { diagnostics: f.diagnostics } : {}),
|
|
97
|
+
...(f.result !== undefined ? { result: f.result } : {}),
|
|
98
|
+
...(f.lines !== undefined ? { lines: f.lines } : {}),
|
|
99
|
+
// 结构化两位取**裸 wire**(归一层把 recentSteps 折成了交互面的显示串,那是渲染口径)。
|
|
100
|
+
...structured('recentSteps'),
|
|
101
|
+
...structured('editedFiles'),
|
|
102
|
+
...(f.taskType !== undefined ? { task_type: f.taskType } : {}),
|
|
103
|
+
...(f.externalSource !== undefined ? { source: f.externalSource } : {}),
|
|
104
|
+
...(typeof n.seq === 'number' || typeof n.seq === 'string' ? { seq: n.seq } : {}),
|
|
105
|
+
...(typeof n.injected === 'boolean' ? { injected: n.injected } : {}),
|
|
106
|
+
...(identity.uuid !== undefined ? { uuid: identity.uuid } : {}),
|
|
107
|
+
...(identity.session_id !== undefined ? { session_id: identity.session_id } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 出口帧里出现了**没进上表**的键就点名它(与 `unregisteredRequestKeys` 同纪律):
|
|
112
|
+
* 顺手加一位很容易,加了之后「字段集合一」就又退化成一句注释 —— 加位必须先改表(带理由)。
|
|
113
|
+
*/
|
|
114
|
+
export function unregisteredPrintNotificationKeys(frame) {
|
|
115
|
+
return Object.keys(frame).filter(k => !PRINT_FRAME_KEYS.has(k));
|
|
116
|
+
}
|
|
@@ -133,3 +133,20 @@ export interface LiveDefaultsInput {
|
|
|
133
133
|
export declare function applyLiveRequestDefaults(req: TaskRequestLike, host: LiveDefaultsInput): TaskRequestLike;
|
|
134
134
|
/** 出口约束:构造结果确实是一个 `TaskRequest`(型只在这里碰 SDK,运行时零依赖)。 */
|
|
135
135
|
export type BuiltTaskRequest = TaskRequest;
|
|
136
|
+
/**
|
|
137
|
+
* ⇄ B8(§8-5 请求拼装合一的**可执行判据**)—— 一份请求体里出现了**没进车道表**的键就点名它。
|
|
138
|
+
*
|
|
139
|
+
* 为什么要有:B4 把字段集做成了 `REQUEST_FIELD_MATRIX` 这张表,但表当时只管住了
|
|
140
|
+
* `buildTaskRequest` 自己;端上真正上 wire 的那份请求仍可以在构造器之外多塞一个键(print lane
|
|
141
|
+
* 修前正是「自己另拼一份」),于是「表说了算」就退化成一句注释。本函数让**真上 wire 的那份**
|
|
142
|
+
* 也过一遍表:print lane 收编之后,`-p` 的请求体逐键都能报出登记出处,再漏就当场红。
|
|
143
|
+
*
|
|
144
|
+
* 判据口径(三类不点名):
|
|
145
|
+
* ① 车道表里的字段(`lanes` 含本车道);② `LIVE_DEFAULT_FIELDS` —— live 兜底层追加、两条车道
|
|
146
|
+
* 都经过,故本就不进车道表;③ `settings` 的子键按表认(`settings.<sub>`),但**交互车道**的
|
|
147
|
+
* `settings.<resolved>` 是 resolver 的 effective 快照 = **开放集**,子键不可枚举 ⇒ 整体放行。
|
|
148
|
+
*
|
|
149
|
+
* 🔴 它**不判**「该不该有」——只判「有没有登记」。补齐一条真实差异要改表(带理由),
|
|
150
|
+
* 而不是绕过本函数:改表这个动作本身就会在 diff 里显形,这正是本判据存在的意义。
|
|
151
|
+
*/
|
|
152
|
+
export declare function unregisteredRequestKeys(req: TaskRequestLike, lane: RequestLane): string[];
|
|
@@ -17,7 +17,10 @@ export const REQUEST_FIELD_MATRIX = [
|
|
|
17
17
|
{ field: 'promptProfile', lanes: ['interactive', 'print'], live: true, why: 'core 1.328 呈现轴 A/B 口,缺省不 stamp = 引擎缺省 simple' },
|
|
18
18
|
{ field: 'enableFork', lanes: ['interactive', 'print'], live: true, why: '显式布尔才 stamp([503]② fail-OPEN 教训:off 必须显式)' },
|
|
19
19
|
{ field: 'attachments', lanes: ['interactive', 'print'], live: true, why: 'design/133 + G1 缺省开的 turn 边界 attachment' },
|
|
20
|
-
|
|
20
|
+
// ⚠️ 这条 why 原文写的是本地技能目录的**字面路径**,B8 把本模块拉进壳的生产闭包之后,那串
|
|
21
|
+
// 字面量随 bundle 进了 `dist/sema.js` 并被壳的品牌门当场逮住(壳的 R11 codemod 只走自己
|
|
22
|
+
// 的 src,走不到 node_modules ⇒ 包里的 CC 路径字面 = 品牌面泄漏)。见本文件末的包内纪律。
|
|
23
|
+
{ field: 'skills', lanes: ['interactive', 'print'], live: true, why: '本地技能目录投影(布局取 CC 同款);两条车道都由 launchRepl 载入面注入' },
|
|
21
24
|
{ field: 'mcpServers', lanes: ['interactive', 'print'], live: true, why: '本地 .mcp.json 投影;service 单用户门决定是否兑现' },
|
|
22
25
|
{ field: 'settings.webSearch', lanes: ['interactive', 'print'], live: true, why: 'SEMA_WEBSEARCH_* / settings.json;per-request 配置赢过部署 env' },
|
|
23
26
|
{ field: 'settings.hooks', lanes: ['interactive', 'print'], live: true, why: '[495]① 用户 settings 文件 hooks 逐字上 wire' },
|
|
@@ -174,3 +177,56 @@ export function applyLiveRequestDefaults(req, host) {
|
|
|
174
177
|
}
|
|
175
178
|
return out;
|
|
176
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* ⇄ B8(§8-5 请求拼装合一的**可执行判据**)—— 一份请求体里出现了**没进车道表**的键就点名它。
|
|
182
|
+
*
|
|
183
|
+
* 为什么要有:B4 把字段集做成了 `REQUEST_FIELD_MATRIX` 这张表,但表当时只管住了
|
|
184
|
+
* `buildTaskRequest` 自己;端上真正上 wire 的那份请求仍可以在构造器之外多塞一个键(print lane
|
|
185
|
+
* 修前正是「自己另拼一份」),于是「表说了算」就退化成一句注释。本函数让**真上 wire 的那份**
|
|
186
|
+
* 也过一遍表:print lane 收编之后,`-p` 的请求体逐键都能报出登记出处,再漏就当场红。
|
|
187
|
+
*
|
|
188
|
+
* 判据口径(三类不点名):
|
|
189
|
+
* ① 车道表里的字段(`lanes` 含本车道);② `LIVE_DEFAULT_FIELDS` —— live 兜底层追加、两条车道
|
|
190
|
+
* 都经过,故本就不进车道表;③ `settings` 的子键按表认(`settings.<sub>`),但**交互车道**的
|
|
191
|
+
* `settings.<resolved>` 是 resolver 的 effective 快照 = **开放集**,子键不可枚举 ⇒ 整体放行。
|
|
192
|
+
*
|
|
193
|
+
* 🔴 它**不判**「该不该有」——只判「有没有登记」。补齐一条真实差异要改表(带理由),
|
|
194
|
+
* 而不是绕过本函数:改表这个动作本身就会在 diff 里显形,这正是本判据存在的意义。
|
|
195
|
+
*/
|
|
196
|
+
export function unregisteredRequestKeys(req, lane) {
|
|
197
|
+
/** live 兜底层的键名(表项写法含 `|` 备选位与括号说明,取裸键名)。 */
|
|
198
|
+
const liveDefaults = new Set(LIVE_DEFAULT_FIELDS.flatMap(row => row.split('|')).map(name => name.replace(/\(.*\)$/, '').trim()));
|
|
199
|
+
const laneRows = REQUEST_FIELD_MATRIX.filter(f => f.lanes.includes(lane));
|
|
200
|
+
/** 顶层键 → 表项(`resumeAt/rewindFiles/rewindFilesTo` 这类斜杠合写项逐键展开)。 */
|
|
201
|
+
const laneTopKeys = new Set();
|
|
202
|
+
for (const row of laneRows) {
|
|
203
|
+
if (row.field.startsWith('settings.'))
|
|
204
|
+
continue;
|
|
205
|
+
for (const key of row.field.split('/'))
|
|
206
|
+
laneTopKeys.add(key);
|
|
207
|
+
}
|
|
208
|
+
const laneSettingsKeys = new Set(laneRows
|
|
209
|
+
.filter(f => f.field.startsWith('settings.') && f.field !== 'settings.<resolved>')
|
|
210
|
+
.map(f => f.field.slice('settings.'.length)));
|
|
211
|
+
const settingsIsOpenSet = laneRows.some(f => f.field === 'settings.<resolved>');
|
|
212
|
+
const out = [];
|
|
213
|
+
for (const key of Object.keys(req)) {
|
|
214
|
+
if (key === 'settings') {
|
|
215
|
+
if (settingsIsOpenSet)
|
|
216
|
+
continue;
|
|
217
|
+
const settings = req.settings;
|
|
218
|
+
if (settings === null || typeof settings !== 'object')
|
|
219
|
+
continue;
|
|
220
|
+
for (const sub of Object.keys(settings)) {
|
|
221
|
+
if (laneSettingsKeys.has(sub) || liveDefaults.has(`settings.${sub}`))
|
|
222
|
+
continue;
|
|
223
|
+
out.push(`settings.${sub}`);
|
|
224
|
+
}
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (laneTopKeys.has(key) || liveDefaults.has(key))
|
|
228
|
+
continue;
|
|
229
|
+
out.push(key);
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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. Blackboard [1832] design axioms; [1651]/[1652]/[1653] signed seam design. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|