@sema-agent/client-core 0.11.12 → 0.11.14

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 CHANGED
@@ -23,7 +23,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
23
23
 
24
24
  ## Scope
25
25
 
26
- **Version:** 0.11.12
26
+ **Version:** 0.11.14
27
27
 
28
28
  - **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
29
29
  B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
@@ -213,6 +213,7 @@ itself (FAILED names + skipped names + arithmetic reconciliation). All-SKIP repo
213
213
  | `scripts/run-print-bash-iserror-test.mjs` | The print lane's Bash `is_error` authority (structured over regex) |
214
214
  | `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 |
215
215
  | `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` |
216
+ | `scripts/run-streamjson-timing-honesty-test.mjs` | Stream timing & terminal honesty ([2084]): held errored fs-write results release on model progress; wall-clock timeout maps to `error_during_execution` with a truthful salvage note; the synthetic API-error assistant row carries the `<synthetic>` in-message sentinel |
216
217
  | `scripts/run-public-surface-test.mjs` | The outward promises: the npm export surface baseline, the peer floor witness, and this README's claims |
217
218
 
218
219
  Each suite carries a floor that only moves up — a refactor that stops executing a group of
@@ -35,9 +35,11 @@
35
35
  * failed limit.max_turns → 'error_max_turns' true
36
36
  * failed output.invalid → 'error_max_structured_output_retries' true
37
37
  * failed 其余(含 cancelled/conflict…) → 'error_during_execution' true
38
- * timeout limit.timeout → 'error_max_turns' true
39
- * (CC 无 wall-clock deadline 概念;词表内最贴 = 唯一 run-limit 族词
40
- * error_max_turns。真实语义靠 errors[] 文案 + errorCode 透传辨认。)
38
+ * timeout limit.timeout → 'error_during_execution' true
39
+ * (CC 无 wall-clock deadline 概念。曾选 error_max_turns「唯一 run-limit 族词」,
40
+ * [2084]②a 定谳那是帧内自相矛盾(errorCode=limit.timeout+num_turns 远小于上限)
41
+ * 且对墙钟停指错旋钮(max-turns 是可恢复失败)⇒ 改诚实兜底词。
42
+ * 真实语义靠 errors[] 文案 + errorCode 透传辨认,不变。)
41
43
  * blocked —(engine 只给 blockedReason)→ 'error_during_execution' true
42
44
  * suspended — → 'error_during_execution' true([884]A1)
43
45
  * needs_review review.pending → 'error_during_execution' true([884]A1)
@@ -203,7 +203,14 @@ export function doneToSdkResult(ev, ctx) {
203
203
  uuid: undefined,
204
204
  session_id: undefined,
205
205
  type: 'result',
206
- subtype: 'error_max_turns',
206
+ // [2084]②a(2026-07-30,repro/cli-walltime-timeout-reported-as-max-turns):此前硬编码
207
+ // error_max_turns(选型理由是「CC 词表唯一 run-limit 族词」),但那让同一帧自相矛盾
208
+ // (errorCode=limit.timeout + num_turns 远小于上限,test 实测 num_turns:2 且根本没设
209
+ // --max-turns),而且 max-turns 是**可恢复失败**——标准应对是调大 --max-turns 重试,对
210
+ // 墙钟停这恰好是那个帮不上忙的旋钮。改 error_during_execution(CC 词表内的诚实兜底);
211
+ // 真实语义仍靠 errorCode:"limit.timeout" + errors[] 文案判别(原设计轴不动)。
212
+ // 副产品:真 limit.max_turns 停(failed 臂)与墙钟停在 subtype 上重新可分。
213
+ subtype: 'error_during_execution',
207
214
  duration_ms: durationMs,
208
215
  duration_api_ms: durationMs,
209
216
  is_error: true,
@@ -213,8 +220,11 @@ export function doneToSdkResult(ev, ctx) {
213
220
  usage,
214
221
  modelUsage,
215
222
  permission_denials: [],
223
+ // [2084]②b:引擎的 cutoff 句承诺「cut off to preserve a write-out window」,但 pre-call
224
+ // throw 路径上写出窗可能零兑现(core dist/brain/stream-engine 五处 throw,载体天生空)。
225
+ // 抢救为空时补诚实尾注——每一层都复述没兑现的承诺,消费者就无从分辨救没救回来。
216
226
  errors: [
217
- `${rr.errorMessage ?? 'run hit its wall-clock deadline and was finalized by the engine'} (${rr.errorCode ?? 'limit.timeout'})`,
227
+ `${rr.errorMessage ?? 'run hit its wall-clock deadline and was finalized by the engine'} (${rr.errorCode ?? 'limit.timeout'})${salvaged === undefined ? ' — no output survived the cutoff' : ''}`,
218
228
  ],
219
229
  ...errorCodeField,
220
230
  // 写出窗 salvage(additive seam 字段):deadline 前引擎救回的最终文本非空时随帧带出——
@@ -149,7 +149,11 @@ async function* runStreamInner(events, ctx, handle = {}) {
149
149
  session_id: ctx.sessionId ?? '',
150
150
  uuid: `err-${Date.now().toString(36)}`,
151
151
  type: 'assistant',
152
- message: { role: 'assistant', content: [{ type: 'text', text: rowText }] },
152
+ // [2084]③:message 内部要有判别哨兵——外层 isApiErrorMessage/err- uuid 只护住读信封的
153
+ // 消费者,而转录回喂/摘要器/评分器读的是 message 本体,没有 in-message 标记就会把这句
154
+ // 引擎文案当成模型自己说的话。CC 2.1.220 同款哨兵 = model:'<synthetic>'(下游投影层对
155
+ // string 型 model 原样保留,不再回填真模型名)。
156
+ message: { role: 'assistant', model: '<synthetic>', content: [{ type: 'text', text: rowText }] },
153
157
  parent_tool_use_id: null,
154
158
  isApiErrorMessage: true,
155
159
  };
@@ -100,6 +100,9 @@ export interface AskGateWireDeps {
100
100
  * tool_approval 帧(帧被吞、引擎按自身 fail-closed TTL 自决)——mock/旧引擎路径零影响。 */
101
101
  respondToolApproval?: RespondToolApprovalFn;
102
102
  }
103
+ /** core 对被 gate/连坐 abort 的 call 铸的 tool_end 载体(逐字;desktop session-host 真引擎实测
104
+ * 同款)——HOLD 谓词锚它做**精确等值**,普通工具错的输出是各自错误文案,永不进 HOLD。 */
105
+ export declare const ENGINE_ABORT_TOOL_RESULT = "Operation aborted";
103
106
  /** CC AskUserQuestion outputSchema 形状(答过的问题卡):toolResult 的 `ask-user-question` arm
104
107
  * 消费它渲真实答案卡(否则「诚实缺席」路径会把这张卡渲成结果不可用)。 */
105
108
  export interface AskAnsweredOutput {
@@ -38,6 +38,9 @@ function rejectMessageForRender() {
38
38
  }
39
39
  /** 一 turn 内最多循环这么多次 park(防御:引擎/模型病态连环提问时不无限 attach)。 */
40
40
  const MAX_GATE_HOPS = 24;
41
+ /** core 对被 gate/连坐 abort 的 call 铸的 tool_end 载体(逐字;desktop session-host 真引擎实测
42
+ * 同款)——HOLD 谓词锚它做**精确等值**,普通工具错的输出是各自错误文案,永不进 HOLD。 */
43
+ export const ENGINE_ABORT_TOOL_RESULT = 'Operation aborted';
41
44
  function isAskTool(name) {
42
45
  return typeof name === 'string' && name.replace(/[\s_-]+/g, '').toLowerCase() === 'askuserquestion';
43
46
  }
@@ -297,6 +300,21 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
297
300
  const seq = ev.id;
298
301
  if (typeof seq === 'string' && seq.length > 0)
299
302
  lastSeq = seq;
303
+ // ── [2084]① 扣留帧「模型推进即放行」(2026-07-30;同日 web 复审纠偏收窄触发集)──────────
304
+ // HOLD 只护 park 窗口。放行触发集 = host-lane **模型推进**帧:text_delta/reasoning_delta/
305
+ // tool_start —— park 路径上这三种在 done{suspended} 之前不会出现,真错路径上下一轮必来其一。
306
+ // 🔴 turn_end 明确**不在**触发集(初版在,web 复审真机轨迹逮红):core agent-loop 的 turn_end
307
+ // emit 在 executeToolCalls 之后**无 abort/suspend 守卫**(邻臂都有,唯此一处没有)⇒ 真 park
308
+ // 的帧序是 tool_end(毒化)→ message_committed → turn_end → done{suspended},turn_end 若放行
309
+ // 会把毒化帧漏上屏,且 flushHeld 的 endedCalls 记账让 decide 后重放的解答帧被当已收口丢弃
310
+ // (真实答案卡永不出现)。代价 = turn 末尾无后续推进帧的 abort 形帧等到终帧 flush,与修前同。
311
+ // parentToolCallId 在场 = sub-agent 行帧,park 期间孙代照常说话,不算 host 推进(负控①C)。
312
+ // (普通工具错自 [2084]①-b 谓词收窄后根本不进 HOLD,零滞后不依赖本触发集。)
313
+ if (heldAskEnds.size > 0 &&
314
+ (ev.type === 'text_delta' || ev.type === 'reasoning_delta' || ev.type === 'tool_start') &&
315
+ ev.parentToolCallId === undefined) {
316
+ yield* flushHeld();
317
+ }
300
318
  // ── server 1.191 同步帧腿([830]①):`tool_approval` 帧 → 三选卡 → respond ────────────────
301
319
  // 引擎此刻同步阻塞在帧上(fail-closed:无 respond/TTL 5min/abort ⇒ deny),await 卡决断安全。
302
320
  // 帧是 HITL 词汇非 AgentEvent arm——两种帧都不外泄到渲染管道。
@@ -411,8 +429,16 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
411
429
  yield { ...ev, output: rejectMessageForRender(), structured: undefined };
412
430
  continue;
413
431
  }
414
- heldAskEnds.set(ev.toolCallId, ev); // park 毒化帧,先 HOLD(gate 定性前不渲 Error 卡)
415
- continue;
432
+ // [2084]①-b HOLD 谓词收窄(2026-07-30,desktop 复审建议采纳):park 毒化帧的载体是
433
+ // **确切的引擎 abort 标记**(core 对被 gate/连坐 abort 的 call 铸
434
+ // `tool_end{isError:true, output:"Operation aborted"}`;desktop session-host 2026-07-29
435
+ // 对真引擎实测同款)。普通工具错(old_string 不命中/read-gate 拒绝…)输出是各自的错误
436
+ // 文案,**根本不该进 HOLD**——收窄后它们零滞后直达转录,不再依赖「推进信号放行」。
437
+ if (ev.output === ENGINE_ABORT_TOOL_RESULT) {
438
+ heldAskEnds.set(ev.toolCallId, ev); // park 毒化帧,先 HOLD(gate 定性前不渲 Error 卡)
439
+ continue;
440
+ }
441
+ // 普通失败:落到下方通用收口路径,立即上屏。
416
442
  }
417
443
  endedCalls.add(ev.toolCallId);
418
444
  heldAskEnds.delete(ev.toolCallId); // 正常收口(重放的 isError:false 解答帧)赢
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/client-core",
3
- "version": "0.11.12",
3
+ "version": "0.11.14",
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",