@sema-agent/client-core 0.45.0 → 0.46.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 +20 -0
- package/README.md +2 -1
- package/dist/printToolResultFrame.js +6 -1
- package/dist/toolResult.js +22 -3
- package/dist/wireErrorTriage.d.ts +41 -0
- package/dist/wireErrorTriage.js +88 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,26 @@
|
|
|
28
28
|
> 不许悄悄漂:豁免登记的 `releasedAt` 与 `FROZEN` 账上 0.36.0 那一行逐字相等;本段(点名版本号
|
|
29
29
|
> `0.36.0` + 关键字「勘误」)必须还在这份头注里 —— 删掉本段而不同批把门侧豁免一起处理,门当场红。
|
|
30
30
|
|
|
31
|
+
## 0.46.0(2026-08-30)
|
|
32
|
+
|
|
33
|
+
两件修复批(#354/#355 双 P0 现网案的 client-core 半场):
|
|
34
|
+
|
|
35
|
+
- **`isResumeAtRejection` 文本兼容腿**(#355 D1,`4fa8617`):server 预检腿的 codeless 4xx 把
|
|
36
|
+
`resume_at` 真子码降级进人话文本,机读前缀腿判 false ⇒ #166 去锚自动重发整臂跳过 ⇒ 1.0.93
|
|
37
|
+
现网会话永久死锁。新增文本腿只认闭集两对「状态 × 括号收尾子码」(`404×(resume_at.unknown_event)`
|
|
38
|
+
/ `422×(resume_at.no_session)`),消费点 `endsWith` 串尾锚(闭集+串尾双件堵回显注入);
|
|
39
|
+
`message`/`errorMessage` 双载体各自独立判。**过渡垫片**:server 已在发端补结构化 `code`
|
|
40
|
+
(46e6e94,随 7.51;[5445] 承诺两句括号尾文案不动),最低 server 版本推过 7.51 后本腿整条退役
|
|
41
|
+
——退役义务在此登记。
|
|
42
|
+
- **Bash 良性非零退出注记三臂消费**(#354 渲染案,`fc5eb5b`):引擎对 grep/rg 无命中族显式给
|
|
43
|
+
`isError:false` + `structured.returnCodeInterpretation`,而 `wireOutputToBody`(T11)/
|
|
44
|
+
`structuredToToolUseResult`/`printToolResultFrame` 三处错误位派生只看 `exitCode !== 0`,注记
|
|
45
|
+
整族被丢 ⇒ 良性形渲红「Error: Exit code 1」。三臂同判别量收敛:注记在场 ⇒ 非零退出不翻错+
|
|
46
|
+
注记过境 `toolUseResult`(宿主卡零输出分支可渲「No matches found」);无注记非零照翻([1948]
|
|
47
|
+
裁定不动)。新门 `run-bash-benign-exit-interpretation-test.mjs`(11 checks)。
|
|
48
|
+
|
|
49
|
+
已知局限(本版新增):无。
|
|
50
|
+
|
|
31
51
|
## 0.45.0(2026-08-29)
|
|
32
52
|
|
|
33
53
|
**批面**:①design/285 **批 0-3 全四批** —— fleet 会话锚 per-key 化(在册局限 **P-31** 的正位解,🟡 部分闭合),
|
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.46.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
|
|
@@ -230,6 +230,7 @@ public-surface guard checks that last one).
|
|
|
230
230
|
| `scripts/run-seat-contract-keys-test.mjs` | The seat IPC contract: verb list ↔ SPEC ↔ types, element-wise |
|
|
231
231
|
| `scripts/run-approval-frame-keys-test.mjs` | The tool-approval frame key mirror, element-wise against the SDK's runtime anchor (one carve-out: AHEAD_OF_ANCHOR entries — keys the server already emits but the SDK anchor has not caught up to — may lead by one generation; the gate turns red the day the SDK catches up, forcing the entry's removal) |
|
|
232
232
|
| `scripts/run-print-bash-iserror-test.mjs` | The print lane's Bash `is_error` authority (structured over regex) |
|
|
233
|
+
| `scripts/run-bash-benign-exit-interpretation-test.mjs` | Benign non-zero Bash exits (`returnCodeInterpretation`) stay non-errors across all three derivation arms, and the annotation transits to the card |
|
|
233
234
|
| `scripts/run-sdk-floor-test.mjs` | The SDK version floor — and, more to the point, that the *installed* type declarations still carry the keys this package reads |
|
|
234
235
|
| `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` |
|
|
235
236
|
| `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) |
|
|
@@ -27,7 +27,12 @@ import { flattenWireOutput } from './adapt/wireShapes.js';
|
|
|
27
27
|
function bashExitCodeFailed(toolName, text, structured) {
|
|
28
28
|
if (typeof toolName !== 'string' || toolName.toLowerCase() !== 'bash')
|
|
29
29
|
return false;
|
|
30
|
-
const
|
|
30
|
+
const st = structured;
|
|
31
|
+
// 良性非零退出注记在场(grep/rg 无命中族)⇒ 非错——CC 同款:interpretation 形走 data
|
|
32
|
+
// 路径非 error 路径(pretty223:394251);与 toolResult.ts T11 同判别量(2026-08-27)。
|
|
33
|
+
if (typeof st?.returnCodeInterpretation === 'string' && st.returnCodeInterpretation.length > 0)
|
|
34
|
+
return false;
|
|
35
|
+
const ec = st?.exitCode;
|
|
31
36
|
if (typeof ec === 'number')
|
|
32
37
|
return ec !== 0;
|
|
33
38
|
const m = /^exit code: (\d+)\b/.exec(text);
|
package/dist/toolResult.js
CHANGED
|
@@ -386,7 +386,14 @@ export function wireOutputToBody(toolName, output, isError, truncated, flatten,
|
|
|
386
386
|
}
|
|
387
387
|
: parseModelFacingBash(text);
|
|
388
388
|
if (slots) {
|
|
389
|
-
|
|
389
|
+
// 良性非零退出注记(2026-08-27,1.0.93 现网案):引擎对 grep/rg 无命中族显式给
|
|
390
|
+
// isError:false + structured.returnCodeInterpretation('No matches found')。注记在场 ⇒
|
|
391
|
+
// 非零退出不再翻 failed(CC 同款:interpretation 形走 data 路径非 error 路径,
|
|
392
|
+
// pretty223:394251/815147 直证)。[1948] 裁定不动:无注记的非零退出照旧按退出码补位。
|
|
393
|
+
const benignInterp = typeof s?.returnCodeInterpretation === 'string' && s.returnCodeInterpretation.length > 0
|
|
394
|
+
? s.returnCodeInterpretation
|
|
395
|
+
: undefined;
|
|
396
|
+
const failed = isError || (slots.exitCode != null && slots.exitCode !== 0 && benignInterp === undefined);
|
|
390
397
|
if (failed) {
|
|
391
398
|
// 失败卡走 UserToolErrorMessage,它读 BLOCK content 且非字符串会被吞成
|
|
392
399
|
// "Tool execution failed" —— 失败路径必须给字符串(cli 同,对抗复审 d126727#3)。
|
|
@@ -404,7 +411,14 @@ export function wireOutputToBody(toolName, output, isError, truncated, flatten,
|
|
|
404
411
|
return {
|
|
405
412
|
content: [processedStdout, slots.stderr.trim()].filter(Boolean).join('\n'),
|
|
406
413
|
isError: false,
|
|
407
|
-
|
|
414
|
+
// 注记过境:BashToolResultMessage 蓝本在零输出分支渲 returnCodeInterpretation
|
|
415
|
+
// (『No matches found』而非『(No output)』),缺席时行为不变。
|
|
416
|
+
toolUseResult: {
|
|
417
|
+
stdout,
|
|
418
|
+
stderr: slots.stderr,
|
|
419
|
+
interrupted: false,
|
|
420
|
+
...(benignInterp !== undefined ? { returnCodeInterpretation: benignInterp } : {}),
|
|
421
|
+
},
|
|
408
422
|
};
|
|
409
423
|
}
|
|
410
424
|
const stdout = truncated ? `${text}\n…(truncated)` : text;
|
|
@@ -518,13 +532,18 @@ modelText) {
|
|
|
518
532
|
if (typeof s.stdout !== 'string' && typeof s.stderr !== 'string')
|
|
519
533
|
return null;
|
|
520
534
|
const exitCode = typeof s.exitCode === 'number' ? s.exitCode : null;
|
|
535
|
+
// 良性非零退出注记同臂(与 wireOutputToBody T11 同判别量,CC pretty223:394251 同源)。
|
|
536
|
+
const benignInterp = typeof s.returnCodeInterpretation === 'string' && s.returnCodeInterpretation.length > 0
|
|
537
|
+
? s.returnCodeInterpretation
|
|
538
|
+
: undefined;
|
|
521
539
|
return {
|
|
522
540
|
toolUseResult: {
|
|
523
541
|
stdout: typeof s.stdout === 'string' ? s.stdout : '',
|
|
524
542
|
stderr: typeof s.stderr === 'string' ? s.stderr : '',
|
|
525
543
|
interrupted: false,
|
|
544
|
+
...(benignInterp !== undefined ? { returnCodeInterpretation: benignInterp } : {}),
|
|
526
545
|
},
|
|
527
|
-
...(exitCode !== null && exitCode !== 0 ? { isError: true } : {}),
|
|
546
|
+
...(exitCode !== null && exitCode !== 0 && benignInterp === undefined ? { isError: true } : {}),
|
|
528
547
|
};
|
|
529
548
|
}
|
|
530
549
|
case 'edit': {
|
|
@@ -49,6 +49,47 @@ export declare function isPreStreamDrainingReject(err: unknown): boolean;
|
|
|
49
49
|
* #166 — `resume_at.*` 错误码族:提交带的 resumeAt 锚服务端不认(Esc 杀锚未持久化的典型形)。
|
|
50
50
|
* 🔴 刻意**不**用 `isRewindFamilyCode`(那是含 `rewind_snapshot.` 的宽形):去锚自动重发的安全性
|
|
51
51
|
* 论证只对 resume_at 族做过,放宽属行为变更(RESUME_AT_ERROR_CODE_PREFIX 头注)。
|
|
52
|
+
*
|
|
53
|
+
* ## #355 D1 —— 两条载体腿(2026-08-27)
|
|
54
|
+
* 同一个语义在 wire 上有**两种形**,此前只认第一种:
|
|
55
|
+
* · **机读形**(既有腿,一字未动):core prepare-throw 经 `resumeAtHttpStatus` 落 4xx,
|
|
56
|
+
* `errorCode` **原样透传**真子码(`resume_at.not_found` / `resume_at.before_target_not_user` …)
|
|
57
|
+
* ⇒ 前缀命中。
|
|
58
|
+
* · **包装形**(本腿新增):server 的**预检腿**在 core 之前就 4xx —— `boot/resolve-spec.ts`
|
|
59
|
+
* 抛 `HttpError(404, "resumeAt: no such message in this session (resume_at.unknown_event)")`
|
|
60
|
+
* 且**不带** `code`,于是 `http/send.ts` 的 `httpErrorCode(404)` 盖上按状态码派生的粗码
|
|
61
|
+
* `not_found.resource`,真子码**只活在人话里**。SDK `classifyApiError` 把 body 的 `error`
|
|
62
|
+
* 原样搬进 `APIError.message`(scrub 只动 Bearer/presigned),所以壳收到的是
|
|
63
|
+
* `{status:404, errorCode:'not_found.resource', message:'…(resume_at.unknown_event)'}`。
|
|
64
|
+
* 旧判据 false ⇒ #166 去锚重发整臂跳过 ⇒ 会话每条消息秒死 404、永久死锁(1.0.93 现网案)。
|
|
65
|
+
*
|
|
66
|
+
* ## 放宽的论证(engineErrorCodes `RESUME_AT_ERROR_CODE_PREFIX` 头注要求「放宽须论证」)
|
|
67
|
+
* 本腿放宽的是**载体**(码位 → 码位∪人话),不是**族**:
|
|
68
|
+
* ① 三重合取收敛误触面:`status === 404`(引擎真应答了,不是网络/内部错)+ 人话里出现
|
|
69
|
+
* **同一个族前缀**字面 + 调用点自己的 `req.resumeAt !== undefined`(seamQuery #166 臂)
|
|
70
|
+
* —— 没带锚的提交压根进不来,与「404 里恰好出现 `resume_at.` 字样」撞车要三件同时成立。
|
|
71
|
+
* ② 族边界一字未松,而且文本腿收成**闭集**:只认 `RESUME_AT_TEXT_COMPAT` 里那两对
|
|
72
|
+
* 「状态 × 括号收尾子码」——`404 × (resume_at.unknown_event)` 与
|
|
73
|
+
* `422 × (resume_at.no_session)`,正是 `boot/resolve-spec.ts` 两处**不带 `code`** 的 throw。
|
|
74
|
+
* `rewind_snapshot.` / `rewind_files_to.` 邻族不入内(邻族的自动重发安全性没论证过);
|
|
75
|
+
* 开集形与裸子串形都已被异源复审证伪(token 边界 + 回显注入两条,论证见
|
|
76
|
+
* {@link RESUME_AT_TEXT_COMPAT})。带 code 的那一路(core prepare-throw 经 `resumeAtHttpStatus`
|
|
77
|
+
* 落 4xx)`errorCode` 原样透传,早被 ① 接住,压根不经过这里。
|
|
78
|
+
* ③ 422 那一支不是「顺手放宽」:它与 404 支同为预检腿的 codeless throw,处置也同款 —— 去锚重发在
|
|
79
|
+
* `no_session` 上不但安全、而且**正是正解**(没有可分支的 session,新起一轮本来就是对的)。
|
|
80
|
+
* 原形把它留在门外、负控还把「422 恒 false」钉成期望值 —— 那不是负控,是把残余固化成契约。
|
|
81
|
+
* ④ 误触的代价上界很低而漏判的代价是死锁:误命中最多多发一次**去锚的**同一条提交(pre-stream
|
|
82
|
+
* 零副作用,与 draining 同一铁律边界)+ 一行人话;漏判 = 会话再也发不出消息。
|
|
83
|
+
* ⑤ 刻意**不**加「粗码必须等于 `request.unprocessable` / `not_found.resource`」这层收窄:那张
|
|
84
|
+
* 状态码→粗码表是 server `http/send.ts` 的实现细节(会随版本增删),把判据押在它上面等于给自己
|
|
85
|
+
* 种一颗静默失效的雷;判别力已由 ② 的闭集承担。
|
|
86
|
+
*
|
|
87
|
+
* 🔴 根治在发端、且**已经落地**:server 给那四条拒绝子码补上了结构化 `code` 参,新 worker 一律走
|
|
88
|
+
* 机读腿 ①。本腿因此是**对已发布旧 server 的过渡垫片**(1.0.93 现网用户跑的正是那些),不是长期形;
|
|
89
|
+
* 等最低 server 版本推过去之后可以整条退役。
|
|
90
|
+
*
|
|
91
|
+
* 🔴 只影响本函数的判定,`RESUME_AT_ERROR_CODE_PREFIX` 常量与它的另一个消费点
|
|
92
|
+
* `isRewindFamilyCode`(rewind 可自解族的**码**判别)一字未动。
|
|
52
93
|
*/
|
|
53
94
|
export declare function isResumeAtRejection(err: unknown): boolean;
|
|
54
95
|
/**
|
package/dist/wireErrorTriage.js
CHANGED
|
@@ -87,14 +87,101 @@ export function isPreStreamDrainingReject(err) {
|
|
|
87
87
|
const e = err;
|
|
88
88
|
return typeof e?.status === 'number' && e.status === 503 && e.errorCode === DRAINING_ERROR_CODE;
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* #355 D1 文本兼容腿认的**闭集**:server 预检腿两处 codeless throw 的「状态 × 子码」配对,
|
|
92
|
+
* 且必须以 server 自己那种**括号收尾**的形出现(`…(resume_at.unknown_event)`)。
|
|
93
|
+
*
|
|
94
|
+
* 🔴 为什么是闭集而不是开集(异源复审第四轮 [medium] 真病修):这条腿的定位是「**已发布**的旧
|
|
95
|
+
* server 的兼容垫片」——一个**历史封闭**的集合。新码一律带结构化 `errorCode`(server 已在发端补齐
|
|
96
|
+
* 四条子码的 `code` 参),走前缀腿 ① 直接命中,根本不经过这里。开集在这里换不来任何前瞻性,却把
|
|
97
|
+
* 判据暴露在**回显注入**下。
|
|
98
|
+
*
|
|
99
|
+
* 🔴 回显注入是真事(亲读 server `config.ts:formatUnmatchableToolNames`):它把**用户送来的**工具名
|
|
100
|
+
* 逐字拼进错误文本(`… names tool(s) …: <用户的名字> → <guidance>`,多条以 `; ` 连接),再由
|
|
101
|
+
* `resolve-spec` 翻成一个**普通 codeless 422**。于是「任意 422 + 文本里出现 resume_at.<任意成员>」
|
|
102
|
+
* 这种开集判据,可以被一条名叫 `resume_at.foo` 的权限规则触发 —— 而那一拍用户的 `resumeAt` 锚是
|
|
103
|
+
* **有效**的,后果是:壳把有效锚删掉、渲一行「恢复点失效」(与事实不符 = 编造归因)、再重发一次。
|
|
104
|
+
*
|
|
105
|
+
* 堵法是**闭集 + 串尾锚**两件一起:
|
|
106
|
+
* · 闭集挡住 `resume_at.<任意成员>`;
|
|
107
|
+
* · 串尾锚(消费点用 `endsWith`)挡住「用户把整串 `(resume_at.no_session)` 连括号写进权限名」——
|
|
108
|
+
* 那一形里回显内容后面必然还跟着 ` → guidance`,永远到不了串尾。少了串尾锚,闭集单独是漏的
|
|
109
|
+
* (异源复审第五轮实测反证)。
|
|
110
|
+
* 串尾成立的依据是实证不是假设:7.42.0–7.50.0 七份已发布 server 产物里,这两句的码后面紧跟的就是
|
|
111
|
+
* 字符串字面量的结束引号。
|
|
112
|
+
*
|
|
113
|
+
* 字面单源:两枚子码由 {@link RESUME_AT_ERROR_CODE_PREFIX} 拼出,常量改了这里自动跟。
|
|
114
|
+
* 认不出 ⇒ 退化成「不自动去锚重发」= 修这一批之前的行为,绝不会更坏。
|
|
115
|
+
*/
|
|
116
|
+
const RESUME_AT_TEXT_COMPAT = [
|
|
117
|
+
{ status: 404, marker: `(${RESUME_AT_ERROR_CODE_PREFIX}unknown_event)` },
|
|
118
|
+
{ status: 422, marker: `(${RESUME_AT_ERROR_CODE_PREFIX}no_session)` },
|
|
119
|
+
];
|
|
90
120
|
/**
|
|
91
121
|
* #166 — `resume_at.*` 错误码族:提交带的 resumeAt 锚服务端不认(Esc 杀锚未持久化的典型形)。
|
|
92
122
|
* 🔴 刻意**不**用 `isRewindFamilyCode`(那是含 `rewind_snapshot.` 的宽形):去锚自动重发的安全性
|
|
93
123
|
* 论证只对 resume_at 族做过,放宽属行为变更(RESUME_AT_ERROR_CODE_PREFIX 头注)。
|
|
124
|
+
*
|
|
125
|
+
* ## #355 D1 —— 两条载体腿(2026-08-27)
|
|
126
|
+
* 同一个语义在 wire 上有**两种形**,此前只认第一种:
|
|
127
|
+
* · **机读形**(既有腿,一字未动):core prepare-throw 经 `resumeAtHttpStatus` 落 4xx,
|
|
128
|
+
* `errorCode` **原样透传**真子码(`resume_at.not_found` / `resume_at.before_target_not_user` …)
|
|
129
|
+
* ⇒ 前缀命中。
|
|
130
|
+
* · **包装形**(本腿新增):server 的**预检腿**在 core 之前就 4xx —— `boot/resolve-spec.ts`
|
|
131
|
+
* 抛 `HttpError(404, "resumeAt: no such message in this session (resume_at.unknown_event)")`
|
|
132
|
+
* 且**不带** `code`,于是 `http/send.ts` 的 `httpErrorCode(404)` 盖上按状态码派生的粗码
|
|
133
|
+
* `not_found.resource`,真子码**只活在人话里**。SDK `classifyApiError` 把 body 的 `error`
|
|
134
|
+
* 原样搬进 `APIError.message`(scrub 只动 Bearer/presigned),所以壳收到的是
|
|
135
|
+
* `{status:404, errorCode:'not_found.resource', message:'…(resume_at.unknown_event)'}`。
|
|
136
|
+
* 旧判据 false ⇒ #166 去锚重发整臂跳过 ⇒ 会话每条消息秒死 404、永久死锁(1.0.93 现网案)。
|
|
137
|
+
*
|
|
138
|
+
* ## 放宽的论证(engineErrorCodes `RESUME_AT_ERROR_CODE_PREFIX` 头注要求「放宽须论证」)
|
|
139
|
+
* 本腿放宽的是**载体**(码位 → 码位∪人话),不是**族**:
|
|
140
|
+
* ① 三重合取收敛误触面:`status === 404`(引擎真应答了,不是网络/内部错)+ 人话里出现
|
|
141
|
+
* **同一个族前缀**字面 + 调用点自己的 `req.resumeAt !== undefined`(seamQuery #166 臂)
|
|
142
|
+
* —— 没带锚的提交压根进不来,与「404 里恰好出现 `resume_at.` 字样」撞车要三件同时成立。
|
|
143
|
+
* ② 族边界一字未松,而且文本腿收成**闭集**:只认 `RESUME_AT_TEXT_COMPAT` 里那两对
|
|
144
|
+
* 「状态 × 括号收尾子码」——`404 × (resume_at.unknown_event)` 与
|
|
145
|
+
* `422 × (resume_at.no_session)`,正是 `boot/resolve-spec.ts` 两处**不带 `code`** 的 throw。
|
|
146
|
+
* `rewind_snapshot.` / `rewind_files_to.` 邻族不入内(邻族的自动重发安全性没论证过);
|
|
147
|
+
* 开集形与裸子串形都已被异源复审证伪(token 边界 + 回显注入两条,论证见
|
|
148
|
+
* {@link RESUME_AT_TEXT_COMPAT})。带 code 的那一路(core prepare-throw 经 `resumeAtHttpStatus`
|
|
149
|
+
* 落 4xx)`errorCode` 原样透传,早被 ① 接住,压根不经过这里。
|
|
150
|
+
* ③ 422 那一支不是「顺手放宽」:它与 404 支同为预检腿的 codeless throw,处置也同款 —— 去锚重发在
|
|
151
|
+
* `no_session` 上不但安全、而且**正是正解**(没有可分支的 session,新起一轮本来就是对的)。
|
|
152
|
+
* 原形把它留在门外、负控还把「422 恒 false」钉成期望值 —— 那不是负控,是把残余固化成契约。
|
|
153
|
+
* ④ 误触的代价上界很低而漏判的代价是死锁:误命中最多多发一次**去锚的**同一条提交(pre-stream
|
|
154
|
+
* 零副作用,与 draining 同一铁律边界)+ 一行人话;漏判 = 会话再也发不出消息。
|
|
155
|
+
* ⑤ 刻意**不**加「粗码必须等于 `request.unprocessable` / `not_found.resource`」这层收窄:那张
|
|
156
|
+
* 状态码→粗码表是 server `http/send.ts` 的实现细节(会随版本增删),把判据押在它上面等于给自己
|
|
157
|
+
* 种一颗静默失效的雷;判别力已由 ② 的闭集承担。
|
|
158
|
+
*
|
|
159
|
+
* 🔴 根治在发端、且**已经落地**:server 给那四条拒绝子码补上了结构化 `code` 参,新 worker 一律走
|
|
160
|
+
* 机读腿 ①。本腿因此是**对已发布旧 server 的过渡垫片**(1.0.93 现网用户跑的正是那些),不是长期形;
|
|
161
|
+
* 等最低 server 版本推过去之后可以整条退役。
|
|
162
|
+
*
|
|
163
|
+
* 🔴 只影响本函数的判定,`RESUME_AT_ERROR_CODE_PREFIX` 常量与它的另一个消费点
|
|
164
|
+
* `isRewindFamilyCode`(rewind 可自解族的**码**判别)一字未动。
|
|
94
165
|
*/
|
|
95
166
|
export function isResumeAtRejection(err) {
|
|
96
167
|
const e = err;
|
|
97
|
-
|
|
168
|
+
if (typeof e?.errorCode === 'string' && e.errorCode.startsWith(RESUME_AT_ERROR_CODE_PREFIX))
|
|
169
|
+
return true;
|
|
170
|
+
// 文本兼容腿:只认闭集里那两对「状态 × 括号收尾子码」(见 {@link RESUME_AT_TEXT_COMPAT})。
|
|
171
|
+
const marker = RESUME_AT_TEXT_COMPAT.find((c) => c.status === e?.status)?.marker;
|
|
172
|
+
if (marker === undefined)
|
|
173
|
+
return false;
|
|
174
|
+
// 两个人话载体**各自独立判**(异源复审 [medium] 真病修:原形取「第一个非空」,而 SDK
|
|
175
|
+
// `classifyApiError` 的 `message` 恒非空(缺席时回落 `HTTP <status>`)⇒ `errorMessage` 那条腿在
|
|
176
|
+
// 「泛化 message + 细节在 errorMessage」的包装形下恒不可达 = 死腿)。
|
|
177
|
+
// · `message` = SDK `APIError` 的正身(壳 seamQuery catch 到的就是它,生产真形);
|
|
178
|
+
// · `errorMessage` = 同一句话在 wire 体 / `TaskResult` 上的键位(轮询腿把体直接当错误对象抛时的形)。
|
|
179
|
+
// 结构读不 instanceof:三端共用面,desktop IPC / web 跨 bundle 的同名错误是 plain object。
|
|
180
|
+
// 🔴 `endsWith` 而不是 `includes`(异源复审第五轮 [medium] 真病修):server 自己那两句**以码收尾**
|
|
181
|
+
// (亲验 7.42.0–7.50.0 七份已发布产物,码后紧跟的就是字符串字面量的结束引号);而回显形里用户内容
|
|
182
|
+
// 后面必然还跟着 ` → guidance`。只认串尾 ⇒ 用户就算把整串 `(resume_at.no_session)` 连括号一起
|
|
183
|
+
// 写进权限名,它在错误文本里也永远不在串尾,注入这条路彻底堵死。
|
|
184
|
+
return [e?.message, e?.errorMessage].some((t) => typeof t === 'string' && t.endsWith(marker));
|
|
98
185
|
}
|
|
99
186
|
/**
|
|
100
187
|
* 第 `attempt`(0-based)次 draining 重试前的等待:`err.retryAfterMs`(SDK 若带)封顶优先
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.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",
|