@sema-agent/client-core 0.82.7 → 0.83.1

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +15 -7
  3. package/dist/adapt/arms.d.ts +3 -2
  4. package/dist/adapt/arms.js +17 -11
  5. package/dist/adapt/ids.js +12 -8
  6. package/dist/adapt/textStream.d.ts +3 -2
  7. package/dist/adapt/toolCards.d.ts +2 -3
  8. package/dist/adapt/toolCards.js +1 -1
  9. package/dist/adapt.d.ts +1 -1
  10. package/dist/adapt.js +13 -6
  11. package/dist/adapter/activeRunSelfHeal.js +34 -17
  12. package/dist/adapter/downstream/eventToSdkMessage.js +1 -1
  13. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -4
  14. package/dist/adapter/downstream/terminalToSdkResult.js +46 -8
  15. package/dist/adapter/runStream.js +25 -8
  16. package/dist/clientSlice.d.ts +0 -5
  17. package/dist/displayUntrusted.d.ts +24 -0
  18. package/dist/displayUntrusted.js +1437 -0
  19. package/dist/engineNoticeCodes.js +4 -0
  20. package/dist/fleet/fleetRowAgentType.d.ts +0 -1
  21. package/dist/fleet/fleetRowAgentType.js +0 -3
  22. package/dist/fleetTaskDesc.js +3 -3
  23. package/dist/gateVocabulary.js +1 -1
  24. package/dist/hitl/approvalDecisionNoteAudit.js +4 -2
  25. package/dist/hitl/approvalResolution.js +2 -0
  26. package/dist/hitl/frameRouter.d.ts +3 -2
  27. package/dist/hitl/sessionPolicyDeliverable.d.ts +14 -0
  28. package/dist/hitl/sessionPolicyDeliverable.js +80 -0
  29. package/dist/hitl/toolApprovalWire.d.ts +1 -0
  30. package/dist/hitl/toolApprovalWire.js +56 -10
  31. package/dist/hooksWireCaps.d.ts +6 -1
  32. package/dist/hooksWireCaps.js +220 -13
  33. package/dist/host.d.ts +25 -0
  34. package/dist/index.d.ts +4 -0
  35. package/dist/index.js +3 -0
  36. package/dist/liveInitToolFace.d.ts +4 -2
  37. package/dist/liveInitToolFace.js +2 -2
  38. package/dist/model/catalogLoader.d.ts +1 -6
  39. package/dist/panelRunningHistory.d.ts +3 -2
  40. package/dist/peerFrames.d.ts +0 -1
  41. package/dist/peerFrames.js +2 -1
  42. package/dist/pluginHooksWire.d.ts +92 -0
  43. package/dist/pluginHooksWire.js +429 -0
  44. package/dist/printInitToolFace.js +1 -1
  45. package/dist/request/printNotification.js +26 -26
  46. package/dist/seam.d.ts +8 -2
  47. package/dist/seam.js +18 -7
  48. package/dist/subagent/engineSubagentResume.js +2 -1
  49. package/dist/systemReminderTag.d.ts +0 -1
  50. package/dist/systemReminderTag.js +0 -3
  51. package/dist/toolResult.js +1 -0
  52. package/dist/uuidV5.d.ts +3 -0
  53. package/dist/uuidV5.js +178 -0
  54. package/dist/webSearchWireCaps.d.ts +0 -3
  55. package/dist/webSearchWireCaps.js +21 -51
  56. package/dist/wireFailureShape.d.ts +2 -1
  57. package/docs/INTEGRATION-CLIENTS.md +794 -13
  58. package/package.json +2 -2
@@ -10,18 +10,17 @@ export type ToolResultOwner = 'leader' | 'subagent';
10
10
  export declare function toolResultOutcomeOf(toolName: string, end: ToolEndPayload | undefined, rawInput: PendingToolUse['rawInput'], owner: ToolResultOwner, ledgerTier?: (isError: boolean) => RichToolUseResult | null): ToolResultOutcome;
11
11
  export interface ToolResultEnvelopeKeys {
12
12
  readonly tool_use_result?: ToolResultBody['toolUseResult'];
13
- readonly toolUseResult?: ToolResultBody['toolUseResult'];
14
13
  readonly _sema_degraded?: ToolResultBody['degraded'];
15
14
  }
16
15
  export declare function toolResultEnvelopeParts(o: ToolResultOutcome): ToolResultEnvelopeKeys;
17
- export interface PendingToolUse {
16
+ interface PendingToolUse {
18
17
  id: string;
19
18
  name: string;
20
19
  rawInput: unknown;
21
20
  eventId?: string;
22
21
  parentToolCallId?: string;
23
22
  }
24
- export interface ToolEndPayload {
23
+ interface ToolEndPayload {
25
24
  output?: unknown;
26
25
  structured?: unknown;
27
26
  truncated?: boolean;
@@ -4,7 +4,7 @@ import { chrome, mainLane, transcript } from './ids.js';
4
4
  import { flattenWireOutput } from './wireShapes.js';
5
5
  import { isCcToolDenialKind } from '../gateVocabulary.js';
6
6
  function toolUseResultParts(v) {
7
- return v !== undefined ? { tool_use_result: v, toolUseResult: v } : {};
7
+ return v !== undefined ? { tool_use_result: v } : {};
8
8
  }
9
9
  export function toolResultOutcomeOf(toolName, end, rawInput, owner, ledgerTier) {
10
10
  const isError = end?.isError ?? false;
package/dist/adapt.d.ts CHANGED
@@ -14,7 +14,7 @@ export declare const ADAPTER_COVERAGE: {
14
14
  readonly b5: readonly ["B 层 structuredToToolUseResult 14 case(ask-user-question/bash/edit/create/update/text/grep/glob/notebook-edit/cron-create/cron-delete/cron-list/agent/task-output)+ workflow-run 登记副作用", "D 层 wireOutputToBody 3 臂(bash / taskoutput / 泛化);structured 白名单在场时 T11+T14 正则退位,缺席保回落", "E 层 4 臂:#117a bg Bash 回执 + #158 Monitor 回执(同臂按工具名分,registration.source 判别;判定在库→chrome bgshell_register)· TodoWrite oldTodos 富卡 · ReportFindings 按名认领 · Remember→memory_saved 系统消息", "tool_end_result → user tool_result 正常路径(卡本体);result 与 turn 末两处开卡兜底关闭(abort 不兜底,cli 同)", "task/task-list structured → chrome task_ledger_sync{source:\"structured\"}(判定归一在库、落盘留宿主)", "T7 诚实缺席(§8-4:不搬 mock 合成器,改 degraded 标记)· T20 diff hunks 进包(src/diff/patch.ts)"];
15
15
  readonly todo: readonly ["T13 的 **D 层** 半场(taskoutput block content)未随 structured 退位:退位会改 block content 形状(cli 那里是裸对象 = 件1 隐患),属行为面改动,无真实需求驱动 ⇒ 见 DIVERGENCE-8", "macrotask 让渡(cli 在提交前让出宏任务给 Ink;宿主渲染策略,不进库)"];
16
16
  };
17
- export declare const ADAPTER_DIVERGENCES: readonly [string, string, string, string, string, string, string, string, string, string];
17
+ export declare const ADAPTER_DIVERGENCES: readonly [string, string, string, string, string, string, string, string, string, string, string, string];
18
18
  export interface WireToCcAdapterWithLedger extends WireToCcAdapter {
19
19
  exportLedger(): AdapterLedgerState & Record<string, unknown>;
20
20
  importLedger(state: Partial<AdapterLedgerState> | Record<string, unknown>): number;
package/dist/adapt.js CHANGED
@@ -69,7 +69,7 @@ export const ADAPTER_DIVERGENCES = [
69
69
  'DIVERGENCE-1:`user` 帧透传——cli 的 switch 无 user 臂(default 丢弃),adapt 原样透传。' +
70
70
  '现役 wire 不产 user 帧(eventToSdkMessage 无该出口),故对真流是惰性的;保留是为了 desktop 重放存量转录。',
71
71
  'DIVERGENCE-2:transcript uuid 取值形——cli 铸随机 v4 / 原样透传帧 uuid,adapt 一律走 ' +
72
- 'deriveTranscriptId(稳定键前缀形 `wid_*`)。差分守卫按「别名结构」比对(同一侧内的复用/区分关系),不比字面值。',
72
+ 'deriveTranscriptId(由稳定键确定性派生的 UUID 形,同一稳定键恒得同一值)。差分守卫按「别名结构」比对(同一侧内的复用/区分关系),不比字面值。',
73
73
  'DIVERGENCE-3:合并节流的**分片边界**不比对——cli 用 Date.now() 真时钟,adapt 用 ctx.now();' +
74
74
  '且 B3 起节拍可由宿主 `ctx.coalesceIntervalMs` 覆盖(缺席=cli 同值 100ms)。' +
75
75
  '守卫比对的是 chrome 增量按 channel 拼接后的全文与顺序(语义不变量),不是 chunk 切分。',
@@ -101,11 +101,18 @@ export const ADAPTER_DIVERGENCES = [
101
101
  '(框架里的尾换行等是渲染副产物)。T13/T24 的 **B 层**半场同款惰性退位(structured 带齐 ' +
102
102
  'status+content 就根本不解模型面文本);T13 的 **D 层** block-content 半场本批**未**退位 —— ' +
103
103
  '那一步会改 block content 形状(cli 在这里是裸对象,件1 隐患),无真实需求驱动,如实留白。',
104
- 'DIVERGENCE-9(B5):`tool_result` 消息的 uuid —— cli 在缺 `eventId` 时铸随机 v4,本包派生 ' +
105
- '`wtc_<toolUseId>`(DIVERGENCE-2 同族:同流重放 ⇒ 同 id 序列,[1653])。守卫按别名结构比,不比字面。',
106
- 'DIVERGENCE-10(0.81.0,C-R85 / CC-131 过渡 B):`tool_result` 消息上本包同时铸 SDK 面键 `tool_use_result` 与 ' +
107
- 'CC 内部转录面驼峰 `toolUseResult`(同引用);壳自有实现只铸驼峰。守卫在两键同在场且同引用时按驼峰比、' +
108
- '略过 snake 键(两侧对称,壳换钉 0.81.0 后两侧同形仍等);0.82.0 删驼峰后本条自然零触发 ⇒ 自退役。',
104
+ 'DIVERGENCE-9(B5):`tool_result` 消息的 uuid —— cli 在缺 `eventId` 时铸随机 v4,本包由 toolUseId ' +
105
+ '确定性派生 UUID 形(DIVERGENCE-2 同族:同流重放 ⇒ 同 id 序列,[1653])。守卫按别名结构比,不比字面。',
106
+ 'DIVERGENCE-10(0.81.0 起,0.83.0 定形,CC-131):`tool_result` 消息上本包只铸 SDK 面键 `tool_use_result` ' +
107
+ '(0.81.0–0.82.x 曾与 CC 内部转录面驼峰 `toolUseResult` 同引用并铸,0.83.0 删驼峰);cli 侧仍带驼峰 —— 所钉的本包' +
108
+ '旧版本两键同铸,升级后在转录入口从 `tool_use_result` 同值起别名。守卫在两键同在场且同引用时略过驼峰键、' +
109
+ '按 snake 比(两侧对称);cli 侧不再带驼峰后本条自然零触发 ⇒ 自退役。',
110
+ 'DIVERGENCE-11(0.83.0,C-R103):system 转录行的到达时戳 —— 本包只给 user / assistant 行补 `timestamp`' +
111
+ '(agent-types 只在这两臂声明),system 行不补(入参自带的照旧原样过境);壳的接收口给每条转录行补到达时刻(只补缺席)。' +
112
+ '守卫在逐字比对前只从壳侧 system 行上去掉这一位;本包侧 system 行若带 `timestamp`,照样判不等。',
113
+ 'DIVERGENCE-12(0.83.0,C-R103):记忆写入 —— 本包发 chrome `memory_saved` 臂(`notes` = 笔记原文,`id` 由这次写入的 wire ' +
114
+ '稳定键派生),转录面零行;壳侧是一条 system/memory_saved 转录行(`writtenPaths` 装同一份笔记)。守卫把壳侧这类行移出逐字比对,' +
115
+ '改比:事件 `notes` ⇔ 行 `writtenPaths` 逐项相等、位置相等(之前已覆盖转录行的条数);`id` 不与旧行 uuid 比(本版不承诺相等)。',
109
116
  ];
110
117
  class WireToCcAdapterImpl {
111
118
  ledger = createAdapterInstanceLedger();
@@ -631,35 +631,52 @@ export function activeRunSelfHealRow(outcome, signal, copy, origin, follow) {
631
631
  return base + governanceOriginClause(signal);
632
632
  }
633
633
  }
634
+ const INJECTED_LEAD = 'A follow-up message sema sent on its own (not one you typed)';
634
635
  function injectedSubmissionRow(outcome, following = false) {
635
- const handle = 'taskId' in outcome && outcome.taskId ? ` (run ${outcome.taskId})` : '';
636
+ const lead = INJECTED_LEAD;
637
+ const id = 'taskId' in outcome && outcome.taskId ? ` (id ${outcome.taskId})` : '';
636
638
  if (outcome.kind === 'running-steer-failed') {
637
639
  return outcome.delivery === 'unknown'
638
- ? `A system notification was handed to the run that is already working${handle}, but the engine did not confirm delivery ` +
639
- `(${outcome.detail}) — that run may or may not have received it; sema did not retry (a steer cannot be re-sent safely). ` +
640
- `Watch that run for what it does next.`
641
- : `A system notification was NOT delivered: the engine refused it for the run that is already working${handle} ` +
642
- `(${outcome.detail}); sema did not retry. The model was not told about it.`;
640
+ ? `${lead} was passed to the reply already in progress${id}, but its delivery could not be confirmed ` +
641
+ `(${outcome.detail}) — that reply may or may not have received it. sema did not retry, because sending it twice ` +
642
+ `is not safe; watch that reply for what it does next.`
643
+ : `${lead} was not delivered: passing it to the reply already in progress${id} was refused (${outcome.detail}). ` +
644
+ `sema did not retry, so the model has not seen it.`;
643
645
  }
644
646
  if ((outcome.kind === 'ask-reopen-failed' || outcome.kind === 'plan-review-reopen-failed') && outcome.decisionInFlight === true) {
645
- return (`A system notification was NOT delivered: this session is still held by an earlier run${handle} whose decision ` +
646
- `is still on its way to the engine, so sema did not retry. The model was not told about it.`);
647
+ return (`${lead} was not delivered: this session is still busy with an earlier reply${id}, and your answer to the decision ` +
648
+ `it is waiting on is still on its way. sema did not retry, so the model has not seen it.`);
649
+ }
650
+ if (outcome.kind === 'running-steered' && outcome.delivery === 'queued') {
651
+ const queuedOn = `${lead} is queued on an earlier reply${id} that is paused waiting for a decision`;
652
+ if (outcome.reopened !== null && reopenDelivered(outcome.reopened)) {
653
+ return `${queuedOn}; sema has shown that decision card, and the message is picked up once you answer it.`;
654
+ }
655
+ if (reopenRefusedForDecisionInFlight(outcome.reopened)) {
656
+ return `${queuedOn}; your answer to it is still on its way, and the message is picked up once that answer is applied.`;
657
+ }
658
+ return `${queuedOn} sema could not show here; the message is picked up only once that decision is made.`;
659
+ }
660
+ if (outcome.kind === 'running-steered' && outcome.delivery === 'parked_for_wake') {
661
+ return (`${lead} has not reached the model: the earlier reply${id} had already finished, so the message was set aside on it ` +
662
+ `instead of starting a new reply, and sema did not retry it.`);
647
663
  }
648
664
  switch (selfHealSubmissionDisposition(outcome)) {
649
665
  case 'held-for-decision':
650
- return (`A system notification could not be delivered while an earlier turn${handle} is waiting on a ` +
651
- `decision — sema kept it queued and will deliver it after you answer the open card.`);
666
+ return (`${lead} is on hold: this session is busy with an earlier reply${id} that is waiting on a decision. ` +
667
+ `sema kept the message queued and will send it after you answer the open card.`);
652
668
  case 'resending':
653
- return `A system notification was held while the earlier run${handle} was cancelled; sema is delivering it now.`;
669
+ return (`${lead} was held back because this session was busy with an earlier reply${id}; the session is free again, ` +
670
+ `so sema is sending the message now — nothing for you to do.`);
654
671
  case 'handed-off':
655
672
  return following
656
- ? `A system notification was handed to the run that is already working${handle} instead of starting ` +
657
- `a new turn — sema is following that run and will surface whatever it asks for next.`
658
- : `A system notification was handed to the run that is already working${handle} instead of starting ` +
659
- `a new turn — watch that run for what it does with it.`;
673
+ ? `${lead} was passed to the reply already in progress${id} instead of starting a new one. ` +
674
+ `sema is following that reply and will show whatever it asks for next; nothing for you to do now.`
675
+ : `${lead} was passed to the reply already in progress${id} instead of starting a new one — ` +
676
+ `watch that reply for what it does with it.`;
660
677
  case 'not-delivered':
661
- return (`A system notification was NOT delivered: this session is still held by an earlier run${handle} and ` +
662
- `sema could not put a decision for it on screen, so it did not retry. The model was not told about it.`);
678
+ return (`${lead} was not delivered: this session was still busy with an earlier reply${id}, and there was no open card ` +
679
+ `you could answer to free it. sema did not retry, so the model has not seen it.`);
663
680
  }
664
681
  }
665
682
  function decisionInFlightRow(parkedOn) {
@@ -161,7 +161,7 @@ export function eventToSdkMessage(ev, ctx) {
161
161
  : {}),
162
162
  ...(freedTokens !== undefined ? { _sema_freed_tokens: freedTokens } : {}),
163
163
  },
164
- ...(Array.isArray(attachedFiles) && attachedFiles.length > 0 ? { attachedFiles } : {}),
164
+ ...(Array.isArray(attachedFiles) && attachedFiles.length > 0 ? { _sema_attached_files: attachedFiles } : {}),
165
165
  })));
166
166
  }
167
167
  case 'tool_end': {
@@ -1,6 +1,7 @@
1
1
  import type { AgentEvent, DeniedBy, TaskStats } from '@sema-agent/sdk';
2
2
  import { type CcToolDenialKind } from '../../gateVocabulary.js';
3
3
  import { type SDKMessage, type EmitContext } from '../types.js';
4
+ import type { TerminalReason } from '@sema-agent/agent-types';
4
5
  import { type SemaModelUsage } from './turnUsageToModelUsage.js';
5
6
  export interface SemaFlatUsage {
6
7
  readonly inputTokens: number;
@@ -90,15 +91,12 @@ export declare function subagentUsageIsPartial(row: {
90
91
  } | null | undefined, opts?: {
91
92
  readonly tablePartial?: boolean;
92
93
  }): boolean;
93
- export interface SemaNestedUsageByTask {
94
- readonly rows: Readonly<Record<string, SemaSubagentUsageRow>>;
95
- readonly partial: boolean;
96
- }
97
94
  export interface SemaTerminalModelUsage extends SemaModelUsage {
98
95
  readonly _sema_total_input_tokens?: number;
99
96
  readonly _sema_usage_basis?: string;
100
97
  readonly _sema_cache_write_tokens_long?: number;
101
98
  }
99
+ export declare function terminalReasonForResult(msg: SDKMessage): TerminalReason | undefined;
102
100
  export declare function doneToSdkResult(ev: Extract<AgentEvent, {
103
101
  type: 'done';
104
102
  }>, ctx: EmitContext, observed?: TerminalStreamObserved): SDKMessage;
@@ -1,6 +1,7 @@
1
1
  import { isCcToolDenialKind, isCcToolDenialKindADenial, isGateDeniedByWord } from '../../gateVocabulary.js';
2
2
  import { stamp } from '../types.js';
3
3
  import { putOwnKey } from '../../ownKey.js';
4
+ import { REDACTION_MARKER_PREFIX, washCredentials } from '../../displayUntrusted.js';
4
5
  import { readEffectiveReasoning, readEffectiveMemoryScopes } from '../../effectiveFacts.js';
5
6
  import { isReviewPark, readRunTerminal, runTerminalCode, runTerminalGateToolName, } from '../../runTerminal.js';
6
7
  import { toCcModelUsage } from './turnUsageToModelUsage.js';
@@ -30,7 +31,6 @@ function nonEmptyStr(v) {
30
31
  return typeof v === 'string' && v.length > 0 ? v : undefined;
31
32
  }
32
33
  export const TOOL_INPUT_JOIN_MAX_CALLS = 4096;
33
- const REDACTION_TOKEN_PREFIX = '\u00abredacted';
34
34
  function isTransportPlaceholderLeaf(v) {
35
35
  return v === '[circular]' || v === '[depth-limit]';
36
36
  }
@@ -46,7 +46,7 @@ function joinableToolInput(args) {
46
46
  if (++nodes > TOOL_INPUT_SCAN_MAX_NODES || depth > TOOL_INPUT_SCAN_MAX_DEPTH)
47
47
  return undefined;
48
48
  if (typeof v === 'string') {
49
- if (v.includes(REDACTION_TOKEN_PREFIX) || isTransportPlaceholderLeaf(v))
49
+ if (v.includes(REDACTION_MARKER_PREFIX) || isTransportPlaceholderLeaf(v))
50
50
  return undefined;
51
51
  continue;
52
52
  }
@@ -413,6 +413,41 @@ function subtypeForErrorCode(code) {
413
413
  return 'error_max_structured_output_retries';
414
414
  return 'error_during_execution';
415
415
  }
416
+ function terminalReasonOf(subtype, isError) {
417
+ switch (subtype) {
418
+ case 'success':
419
+ return isError === false ? 'completed' : undefined;
420
+ case 'error_max_turns':
421
+ return 'max_turns';
422
+ case 'error_max_budget_usd':
423
+ return 'budget_exhausted';
424
+ case 'error_max_structured_output_retries':
425
+ return 'structured_output_retry_exhausted';
426
+ default:
427
+ return undefined;
428
+ }
429
+ }
430
+ function terminalReasonParts(subtype, isError) {
431
+ const reason = terminalReasonOf(subtype, isError);
432
+ return reason !== undefined ? { terminal_reason: reason } : {};
433
+ }
434
+ export function terminalReasonForResult(msg) {
435
+ try {
436
+ const m = msg;
437
+ if (typeof m !== 'object' || m === null || Array.isArray(m))
438
+ return undefined;
439
+ const rec = m;
440
+ if (rec.type !== 'result')
441
+ return undefined;
442
+ return terminalReasonOf(rec.subtype, rec.is_error);
443
+ }
444
+ catch {
445
+ return undefined;
446
+ }
447
+ }
448
+ function selectedModelParts(model) {
449
+ return typeof model === 'string' && model.length > 0 ? { _sema_selected_model: model } : {};
450
+ }
416
451
  function salvagedTextOf(r) {
417
452
  const salvagedOutput = r.salvagedOutput;
418
453
  return typeof r.result === 'string' && r.result.trim().length > 0
@@ -448,10 +483,12 @@ function errorResult(ctx, parts) {
448
483
  ...costFactParts(parts.stats, parts.observed),
449
484
  ...reemitEffectiveFacts(parts.facts),
450
485
  ...nestedUsageByTaskParts(parts.stats, parts.observed?.nestedUsageByTask),
451
- errors: [...parts.errors],
452
- ...(parts.errorCode !== undefined && parts.errorCode.length > 0 ? { errorCode: parts.errorCode } : {}),
453
- ...(parts.degraded !== undefined ? { degraded: parts.degraded } : {}),
454
- ...(parts.salvagedResult !== undefined ? { result: parts.salvagedResult } : {}),
486
+ errors: parts.errors.map(washCredentials),
487
+ ...terminalReasonParts(parts.subtype, true),
488
+ ...(parts.errorCode !== undefined && parts.errorCode.length > 0 ? { _sema_error_code: parts.errorCode } : {}),
489
+ ...(parts.degraded !== undefined ? { _sema_model_degraded: parts.degraded } : {}),
490
+ ...selectedModelParts(parts.model),
491
+ ...(parts.salvagedResult !== undefined ? { _sema_salvaged_result: parts.salvagedResult } : {}),
455
492
  ...(parts.outcomeUnknown === true ? { _sema_outcome: 'unknown' } : {}),
456
493
  });
457
494
  }
@@ -556,16 +593,17 @@ export function doneToSdkResult(ev, ctx, observed) {
556
593
  num_turns: stats?.turns ?? 0,
557
594
  result: r.result ?? '',
558
595
  ...structuredOutputParts(r),
559
- ...(degraded !== undefined ? { degraded } : {}),
596
+ ...(degraded !== undefined ? { _sema_model_degraded: degraded } : {}),
560
597
  stop_reason: null,
561
598
  total_cost_usd: costOrNull(stats),
562
599
  usage: flattenUsage(stats),
563
600
  modelUsage: modelUsageFor(stats, r.model),
564
601
  ...permissionDenialParts(stats, observed),
602
+ ...terminalReasonParts('success', false),
565
603
  ...costFactParts(stats, observed),
566
604
  ...effectiveFactParts(rec),
567
605
  ...nestedUsageByTaskParts(stats, observed?.nestedUsageByTask),
568
- ...(typeof r.model === 'string' ? { model: r.model } : {}),
606
+ ...selectedModelParts(r.model),
569
607
  });
570
608
  }
571
609
  export function failedToSdkResult(ev, ctx, observed) {
@@ -1,10 +1,11 @@
1
- import { eventSeq, } from './types.js';
1
+ import { eventSeq, stamp, } from './types.js';
2
2
  import { eventToSdkMessage, turnEndUsage } from './downstream/eventToSdkMessage.js';
3
3
  import { turnUsageToModelUsage } from './downstream/turnUsageToModelUsage.js';
4
4
  import { readRunCostFacts, terminalToSdkResult, TOOL_INPUT_JOIN_MAX_CALLS } from './downstream/terminalToSdkResult.js';
5
5
  import { coerceOutput, publishSubagentContentEvent } from '../subagentContentStore.js';
6
6
  import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isLimitsExceededCode } from '../engineErrorCodes.js';
7
7
  import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
8
+ import { washCredentials } from '../displayUntrusted.js';
8
9
  import { ccToolDenialKindForToolEnd, gateDeniedBy, gateOutcomeOf } from '../gateOutcome.js';
9
10
  import { isCcToolDenialKind, isCcToolDenialKindADenial, isGateDeniedByWord } from '../gateVocabulary.js';
10
11
  const mainLane = () => ({ lane: 'main' });
@@ -97,14 +98,14 @@ export function isModelOutputErrorRowText(text) {
97
98
  }
98
99
  export const OUTCOME_UNKNOWN_ROW_PREFIX = 'Outcome unknown';
99
100
  function syntheticTerminalRow(ctx, text) {
100
- return {
101
- session_id: ctx.sessionId ?? '',
102
- uuid: `err-${Date.now().toString(36)}`,
101
+ return stamp(ctx, {
102
+ uuid: undefined,
103
+ session_id: undefined,
103
104
  type: 'assistant',
104
- message: { role: 'assistant', model: '<synthetic>', content: [{ type: 'text', text }] },
105
+ message: { role: 'assistant', model: '<synthetic>', content: [{ type: 'text', text: washCredentials(text) }] },
105
106
  parent_tool_use_id: null,
106
- isApiErrorMessage: true,
107
- };
107
+ _sema_api_error_message: true,
108
+ });
108
109
  }
109
110
  export function isOutcomeUnknownRowText(text) {
110
111
  return text.trimStart().startsWith(`${OUTCOME_UNKNOWN_ROW_PREFIX}:`);
@@ -158,6 +159,21 @@ export function _resetDroppedFrameReportForTest() {
158
159
  export function _droppedFrameMemoSizeForTest() {
159
160
  return reportedDroppedTypes.size;
160
161
  }
162
+ function withStringFailureFields(ev) {
163
+ if (ev.type !== 'failed')
164
+ return ev;
165
+ const got = ev;
166
+ const badMessage = got.errorMessage !== undefined && typeof got.errorMessage !== 'string';
167
+ const badCode = got.errorCode !== undefined && typeof got.errorCode !== 'string';
168
+ if (!badMessage && !badCode)
169
+ return ev;
170
+ const { errorMessage, errorCode, ...rest } = ev;
171
+ return {
172
+ ...rest,
173
+ ...(badMessage ? {} : { errorMessage }),
174
+ ...(badCode ? {} : { errorCode }),
175
+ };
176
+ }
161
177
  let inFlightTurns = 0;
162
178
  export function isRunStreamActive() {
163
179
  return inFlightTurns > 0;
@@ -199,7 +215,8 @@ async function* runStreamInner(events, ctx, handle = {}) {
199
215
  return false;
200
216
  }
201
217
  };
202
- for await (const ev of events) {
218
+ for await (const rawEv of events) {
219
+ const ev = withStringFailureFields(rawEv);
203
220
  if (ev.type === 'tool_end' && ev.isError !== true)
204
221
  successfulToolEndObserved = true;
205
222
  const seq = eventSeq(ev);
@@ -1,8 +1,3 @@
1
- export interface ClientVerbSpec {
2
- verb: string;
3
- required: boolean;
4
- duty: string;
5
- }
6
1
  export interface ClientSliceLike {
7
2
  streamTask(req: unknown, opts?: {
8
3
  signal?: {
@@ -0,0 +1,24 @@
1
+ export declare const REDACTION_MARKER_PREFIX = "\u00ABredacted";
2
+ export declare function looksLikeKnownSecret(s: string): boolean;
3
+ export declare function washCredentials(text: string): string;
4
+ export type DisplayMark = 'escape' | 'dot' | 'space';
5
+ export interface DisplayFace {
6
+ readonly unsafe: RegExp | null;
7
+ readonly fold: boolean;
8
+ readonly mark: DisplayMark;
9
+ }
10
+ export declare const LEGACY_ESCAPE_FACE: DisplayFace;
11
+ export declare const LEGACY_LABEL_FACE: DisplayFace;
12
+ export declare const LEGACY_NAME_FACE: DisplayFace;
13
+ export declare function applyDisplayFace(text: string, face: DisplayFace): string;
14
+ export interface DisplayUntrustedOptions {
15
+ readonly credentialUrls?: boolean;
16
+ readonly credentialWords?: boolean;
17
+ readonly controls?: boolean;
18
+ readonly bidi?: boolean;
19
+ readonly foldLines?: boolean;
20
+ readonly keepLayout?: boolean;
21
+ readonly mark?: DisplayMark;
22
+ readonly max?: number;
23
+ }
24
+ export declare function displayUntrusted(text: string, opts?: DisplayUntrustedOptions): string;