@sema-agent/client-core 0.108.0 → 0.110.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.
Files changed (179) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/README.md +77 -40
  3. package/dist/adapt/arms.js +41 -13
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +37 -16
  6. package/dist/adapter/activeRunSelfHeal.js +1 -1
  7. package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
  8. package/dist/agentSession/engineAgentRegistryRead.js +2 -0
  9. package/dist/bashExitReading.d.ts +12 -0
  10. package/dist/bashExitReading.js +44 -0
  11. package/dist/checkpointRowVerdict.d.ts +1 -1
  12. package/dist/checkpointRowVerdict.js +4 -2
  13. package/dist/compensations.js +1 -1
  14. package/dist/declared/supportFloorDeclared.js +1 -1
  15. package/dist/displayUntrusted.js +132 -14
  16. package/dist/engineAgentAbsence.d.ts +2 -0
  17. package/dist/engineAgentAbsence.js +28 -0
  18. package/dist/engineErrorCodes.d.ts +7 -0
  19. package/dist/engineErrorCodes.js +7 -0
  20. package/dist/engineHealthFacts.d.ts +26 -0
  21. package/dist/engineHealthFacts.js +63 -4
  22. package/dist/engineInlineTaskStats.d.ts +4 -3
  23. package/dist/engineInlineTaskStats.js +18 -2
  24. package/dist/engineNoticeCodes.d.ts +138 -0
  25. package/dist/engineNoticeCodes.js +662 -55
  26. package/dist/engineRepoint.d.ts +140 -0
  27. package/dist/engineRepoint.js +285 -0
  28. package/dist/engineRowRef.js +2 -0
  29. package/dist/engineSessionMessages.d.ts +2 -1
  30. package/dist/engineSessionMessages.js +5 -0
  31. package/dist/errorSupplementRoles.js +5 -1
  32. package/dist/fleet/fleetProjection.js +3 -2
  33. package/dist/fleetTaskDesc.js +3 -3
  34. package/dist/generated/engineFactTables.d.ts +3 -0
  35. package/dist/generated/engineFactTables.js +18 -0
  36. package/dist/generated/engineNoticeTables.js +3 -0
  37. package/dist/generated/sdkFactTables.d.ts +1 -0
  38. package/dist/generated/sdkFactTables.js +4 -0
  39. package/dist/hitl/approvalAskers.d.ts +6 -0
  40. package/dist/hitl/approvalAskers.js +30 -0
  41. package/dist/hitl/approvalCardReasons.js +25 -4
  42. package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
  43. package/dist/hitl/approvalRequestFrameReading.js +90 -0
  44. package/dist/hitl/approvalResolution.d.ts +8 -1
  45. package/dist/hitl/approvalResolution.js +4 -0
  46. package/dist/hitl/approvalsFeed.js +1 -1
  47. package/dist/hitl/askGateWire.js +0 -2
  48. package/dist/hitl/askParkRowRouting.js +5 -2
  49. package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
  50. package/dist/hitl/automaticAnswerVerdict.js +18 -4
  51. package/dist/hitl/decisionConflict.d.ts +2 -1
  52. package/dist/hitl/decisionConflict.js +10 -4
  53. package/dist/hitl/decisionRefusal.js +1 -0
  54. package/dist/hitl/decisionSend.d.ts +3 -0
  55. package/dist/hitl/decisionSend.js +29 -11
  56. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  57. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  58. package/dist/hitl/frameRouter.js +13 -19
  59. package/dist/hitl/gateLedger.d.ts +0 -4
  60. package/dist/hitl/gateLedger.js +0 -19
  61. package/dist/hitl/hitlBridge.d.ts +5 -0
  62. package/dist/hitl/hitlBridge.js +13 -9
  63. package/dist/hitl/hitlHostSurface.d.ts +1 -0
  64. package/dist/hitl/hitlHostSurface.js +7 -2
  65. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  66. package/dist/hitl/hostWithdrawal.js +38 -0
  67. package/dist/hitl/livePendingAsk.js +17 -16
  68. package/dist/hitl/localAllowRule.js +2 -1
  69. package/dist/hitl/noAllowCause.d.ts +1 -1
  70. package/dist/hitl/noAllowCause.js +4 -2
  71. package/dist/hitl/parkExpiryJudgements.js +26 -1
  72. package/dist/hitl/parkOwnership.d.ts +2 -0
  73. package/dist/hitl/parkOwnership.js +48 -8
  74. package/dist/hitl/parkResolver.js +32 -16
  75. package/dist/hitl/parkedUnresumable.d.ts +13 -0
  76. package/dist/hitl/parkedUnresumable.js +80 -0
  77. package/dist/hitl/permissionRuleScope.d.ts +4 -0
  78. package/dist/hitl/permissionRuleScope.js +31 -0
  79. package/dist/hitl/persistedRulesWire.d.ts +2 -0
  80. package/dist/hitl/persistedRulesWire.js +13 -12
  81. package/dist/hitl/planReviewWire.js +14 -4
  82. package/dist/hitl/sessionAllowRoute.js +5 -4
  83. package/dist/hitl/sessionPolicyWire.js +6 -4
  84. package/dist/hitl/standingRows.d.ts +2 -5
  85. package/dist/hitl/standingRows.js +30 -15
  86. package/dist/hitl/toolApprovalWire.d.ts +42 -9
  87. package/dist/hitl/toolApprovalWire.js +267 -44
  88. package/dist/hooksWireCaps.js +35 -4
  89. package/dist/hostSettingsSource.js +2 -0
  90. package/dist/hostTurnSettings.d.ts +10 -4
  91. package/dist/hostTurnSettings.js +112 -5
  92. package/dist/i18n/messages.en.d.ts +251 -0
  93. package/dist/i18n/messages.en.js +254 -2
  94. package/dist/i18n/messages.zh.js +264 -2
  95. package/dist/index.d.ts +48 -12
  96. package/dist/index.js +29 -7
  97. package/dist/inheritEnvCapability.js +2 -0
  98. package/dist/inputAuthor.d.ts +16 -0
  99. package/dist/inputAuthor.js +44 -0
  100. package/dist/interruptRefusalNote.d.ts +2 -0
  101. package/dist/interruptRefusalNote.js +17 -0
  102. package/dist/liveModelCatalog.d.ts +2 -0
  103. package/dist/liveModelCatalog.js +21 -7
  104. package/dist/liveQuestionStore.d.ts +4 -1
  105. package/dist/liveQuestionStore.js +12 -3
  106. package/dist/localEngineJudge.d.ts +75 -0
  107. package/dist/localEngineJudge.js +242 -0
  108. package/dist/localEngineOccupancy.d.ts +88 -0
  109. package/dist/localEngineOccupancy.js +269 -0
  110. package/dist/localEngineSeatReading.d.ts +29 -0
  111. package/dist/localEngineSeatReading.js +294 -0
  112. package/dist/localEngineSpawn.d.ts +113 -0
  113. package/dist/localEngineSpawn.js +486 -0
  114. package/dist/localEngineStep.d.ts +286 -0
  115. package/dist/localEngineStep.js +1086 -0
  116. package/dist/localEngineStopLadder.d.ts +29 -0
  117. package/dist/localEngineStopLadder.js +139 -0
  118. package/dist/localEngineSwapCard.d.ts +43 -0
  119. package/dist/localEngineSwapCard.js +205 -0
  120. package/dist/localEngineTiming.d.ts +8 -0
  121. package/dist/localEngineTiming.js +8 -0
  122. package/dist/mcpEngineLeg.d.ts +3 -0
  123. package/dist/mcpEngineLeg.js +38 -1
  124. package/dist/mcpPanel.js +2 -0
  125. package/dist/mcpReconnect.d.ts +9 -1
  126. package/dist/mcpReconnect.js +132 -20
  127. package/dist/mcpWireCaps.js +2 -2
  128. package/dist/memoryEntriesWire.d.ts +1 -1
  129. package/dist/memoryEntriesWire.js +5 -0
  130. package/dist/modelCapabilityProbe.js +4 -3
  131. package/dist/modsFace.d.ts +8 -1
  132. package/dist/modsFace.js +27 -0
  133. package/dist/noticePresentation.d.ts +2 -0
  134. package/dist/noticePresentation.js +79 -1
  135. package/dist/notifications.d.ts +17 -6
  136. package/dist/notifications.js +67 -19
  137. package/dist/ownKey.d.ts +5 -0
  138. package/dist/ownKey.js +8 -1
  139. package/dist/peerFrames.d.ts +40 -2
  140. package/dist/peerFrames.js +158 -3
  141. package/dist/pluginHooksWire.d.ts +2 -1
  142. package/dist/pluginHooksWire.js +56 -1
  143. package/dist/replayBody.js +10 -2
  144. package/dist/request/taskRequest.d.ts +5 -0
  145. package/dist/request/taskRequest.js +138 -21
  146. package/dist/resumeRefusalCopy.d.ts +1 -3
  147. package/dist/resumeRefusalCopy.js +2 -1
  148. package/dist/retryStatus.d.ts +2 -2
  149. package/dist/retryStatus.js +2 -0
  150. package/dist/ruleReadings.js +1 -1
  151. package/dist/seam.d.ts +24 -2
  152. package/dist/seam.js +15 -2
  153. package/dist/seatContract.d.ts +125 -10
  154. package/dist/seatContract.js +373 -7
  155. package/dist/sessionBackground.js +4 -1
  156. package/dist/sessionBackgroundHandoff.js +2 -1
  157. package/dist/sessionEngine.d.ts +2 -0
  158. package/dist/sessionEngine.js +111 -0
  159. package/dist/sessionMemoryStatus.d.ts +2 -1
  160. package/dist/sessionSyncFileHistory.d.ts +4 -0
  161. package/dist/sessionSyncFileHistory.js +28 -0
  162. package/dist/sseIdleTriage.d.ts +13 -1
  163. package/dist/sseIdleTriage.js +24 -6
  164. package/dist/subagent/engineSubagentTail.js +6 -0
  165. package/dist/subagentContentStore.d.ts +12 -0
  166. package/dist/subagentContentStore.js +72 -11
  167. package/dist/terminalStatuses.d.ts +1 -0
  168. package/dist/terminalStatuses.js +4 -1
  169. package/dist/toolResult.js +18 -5
  170. package/dist/turnFailurePresentation.js +9 -2
  171. package/dist/typePins.d.ts +11 -0
  172. package/dist/usageAnalytics.d.ts +17 -0
  173. package/dist/usageAnalytics.js +44 -2
  174. package/dist/wireFailureShape.d.ts +22 -5
  175. package/dist/wireFailureShape.js +47 -26
  176. package/dist/wireRefusalCopy.d.ts +31 -1
  177. package/dist/wireRefusalCopy.js +167 -2
  178. package/docs/INTEGRATION-CLIENTS.md +3269 -21
  179. package/package.json +5 -5
@@ -377,6 +377,117 @@ function methodOf(o, key) {
377
377
  return undefined;
378
378
  }
379
379
  }
380
+ function addressBindingOf(raw) {
381
+ if (typeof raw !== 'string')
382
+ return undefined;
383
+ const b = addressBinding(raw);
384
+ return b.ok && b.identityKind === 'address' ? b : undefined;
385
+ }
386
+ function enginesTableOf(record) {
387
+ const slot = slotReadOf(record, 'engines');
388
+ if (slot.presence === 'absent' || (slot.presence === 'present' && slot.value === undefined))
389
+ return 'absent';
390
+ return slot.presence === 'present' && isObj(slot.value) ? slot.value : 'unreadable';
391
+ }
392
+ function movedEngines(table, dropKey, putKey, entry) {
393
+ const out = {};
394
+ for (const k of Object.keys(table)) {
395
+ if (k === dropKey)
396
+ continue;
397
+ const slot = slotReadOf(table, k);
398
+ if (slot.presence !== 'present')
399
+ return undefined;
400
+ Object.defineProperty(out, k, { value: slot.value, writable: true, enumerable: true, configurable: true });
401
+ }
402
+ Object.defineProperty(out, putKey, { value: entry, writable: true, enumerable: true, configurable: true });
403
+ return out;
404
+ }
405
+ function repointStep(prior, from, to, fromNs, toNs, now) {
406
+ try {
407
+ const reading = readSessionEngine(prior);
408
+ if (reading.kind === 'none')
409
+ return { result: 'unaffected' };
410
+ if (reading.kind === 'unresolved' || prior === undefined)
411
+ return { result: 'unreadable' };
412
+ const table = enginesTableOf(prior);
413
+ if (reading.kind === 'bound') {
414
+ const id = reading.binding.identity;
415
+ if (id.identityKind !== 'address' || id.key !== from.key)
416
+ return { result: 'unaffected' };
417
+ if (table === 'absent' || table === 'unreadable')
418
+ return { result: 'unreadable' };
419
+ if (Object.hasOwn(table, to.key))
420
+ return { result: 'conflict' };
421
+ const entry = slotReadOf(table, from.key);
422
+ if (entry.presence !== 'present' || !isObj(entry.value))
423
+ return { result: 'unreadable' };
424
+ const engines = movedEngines(table, from.key, to.key, entry.value);
425
+ if (engines === undefined)
426
+ return { result: 'unreadable' };
427
+ return {
428
+ result: 'moved',
429
+ next: { ...prior, boundEngine: snapshotOf({ kind: reading.binding.kind, identity: to }), engines, updatedAt: now },
430
+ };
431
+ }
432
+ if (table === 'absent')
433
+ return { result: 'unaffected' };
434
+ if (table === 'unreadable')
435
+ return { result: 'unreadable' };
436
+ if (!Object.hasOwn(table, fromNs))
437
+ return { result: 'unaffected' };
438
+ if (Object.hasOwn(table, toNs))
439
+ return { result: 'conflict' };
440
+ const entry = slotReadOf(table, fromNs);
441
+ if (entry.presence !== 'present' || !isObj(entry.value))
442
+ return { result: 'unreadable' };
443
+ const engines = movedEngines(table, fromNs, toNs, entry.value);
444
+ if (engines === undefined)
445
+ return { result: 'unreadable' };
446
+ return { result: 'moved', next: { ...prior, engines, updatedAt: now } };
447
+ }
448
+ catch {
449
+ return { result: 'unreadable' };
450
+ }
451
+ }
452
+ export function repointSessionEngine(store, from, to) {
453
+ let readMergeWrite;
454
+ let fromB;
455
+ let toB;
456
+ try {
457
+ if (!isObj(store))
458
+ return 'request-invalid';
459
+ const rmw = methodOf(store, 'readMergeWrite');
460
+ if (rmw === undefined)
461
+ return 'request-invalid';
462
+ readMergeWrite = (build) => rmw.call(store, build);
463
+ const f = addressBindingOf(from);
464
+ const t = addressBindingOf(to);
465
+ if (f === undefined || t === undefined || f.key === t.key)
466
+ return 'request-invalid';
467
+ fromB = f;
468
+ toB = t;
469
+ }
470
+ catch {
471
+ return 'request-invalid';
472
+ }
473
+ const fromNs = engineNamespaceKeyFor(from);
474
+ const toNs = engineNamespaceKeyFor(to);
475
+ let step;
476
+ try {
477
+ const returned = readMergeWrite((prior) => {
478
+ step = repointStep(prior, fromB, toB, fromNs, toNs, new Date().toISOString());
479
+ return Object.hasOwn(step, 'next') ? step.next : undefined;
480
+ });
481
+ if (step === undefined)
482
+ return 'store-failed';
483
+ if (Object.hasOwn(step, 'next') && !isObj(returned))
484
+ return 'store-failed';
485
+ return step.result;
486
+ }
487
+ catch {
488
+ return 'store-failed';
489
+ }
490
+ }
380
491
  const REFUSAL_KEYS = {
381
492
  'not-wired': 'session.engine.dispatch.notWired',
382
493
  'scope-mismatch': 'session.engine.dispatch.scopeMismatch',
@@ -9,7 +9,8 @@ export interface MemoryStatusClientLike {
9
9
  } & SessionMemoryStatus>;
10
10
  };
11
11
  }
12
- export type MemoryStatusFacts = Omit<SessionMemoryStatusResponse, 'sessionId' | 'memorySync'> & {
12
+ export type MemoryStatusFacts = SessionMemoryStatus & {
13
+ autoConsolidationArmed?: SessionMemoryStatusResponse['autoConsolidationArmed'];
13
14
  readonly memorySync?: MemorySyncFacts;
14
15
  };
15
16
  export interface MemorySyncFacts {
@@ -1,4 +1,8 @@
1
+ import type { RowBitsOutcome } from '@sema-agent/sdk';
2
+ import { type UiLanguage } from './env/uiLanguage.js';
1
3
  export declare const FILE_HISTORY_SKIPPED_NOTICE: string;
2
4
  export declare function fileHistorySyncNotice(result: unknown): string | undefined;
3
5
  export declare function fileHistorySyncNoticeIdentity(result: unknown): string | undefined;
4
6
  export declare function isFileHistoryBlobMissing(err: unknown): boolean;
7
+ export type PushRowBitsWord = RowBitsOutcome | 'writer_side';
8
+ export declare function sessionPushRowBitsNotice(result: {} | null | undefined, lang?: UiLanguage | (string & {})): string | undefined;
@@ -1,6 +1,8 @@
1
1
  import { NOT_FOUND_BLOB } from './engineErrorCodes.js';
2
2
  import { effectiveWireErrorCode } from './wireErrorTriage.js';
3
3
  import { readThrownSlot } from './wireFailureShape.js';
4
+ import { isUiLanguage } from './env/uiLanguage.js';
5
+ import { msg, renderMessage } from './i18n/translate.js';
4
6
  export const FILE_HISTORY_SKIPPED_NOTICE = 'File history was not copied: only the conversation was synced, because one of the two engines does not confirm that it can ' +
5
7
  'transfer file history (the record /rewind uses to restore files). Syncing this session again will not add it until ' +
6
8
  'the conversation has new turns and both engines can transfer file history.';
@@ -64,3 +66,29 @@ export function isFileHistoryBlobMissing(err) {
64
66
  return false;
65
67
  }
66
68
  }
69
+ function pushRowBitsSpeaks(word) {
70
+ switch (word) {
71
+ case 'writer_side':
72
+ return true;
73
+ case 'mirrored':
74
+ case 'unsupported':
75
+ case 'absent':
76
+ return false;
77
+ }
78
+ }
79
+ function isPushRowBitsWord(v) {
80
+ return v === 'mirrored' || v === 'unsupported' || v === 'absent' || v === 'writer_side';
81
+ }
82
+ export function sessionPushRowBitsNotice(result, lang) {
83
+ if (result === null || typeof result !== 'object')
84
+ return undefined;
85
+ try {
86
+ const word = getOwn(result, 'rowBits');
87
+ if (typeof word !== 'string' || !isPushRowBitsWord(word) || !pushRowBitsSpeaks(word))
88
+ return undefined;
89
+ return renderMessage(msg('session.sync.pushRowBitsWriterSide'), isUiLanguage(lang) ? lang : undefined);
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
@@ -1,4 +1,5 @@
1
1
  import { type EnvLike } from './hostEnv.js';
2
+ import { SDK_STREAM_ENDED_REASONS } from './generated/sdkFactTables.js';
2
3
  import { type RunNotFollowed } from './runFollow.js';
3
4
  export declare const SSE_HEARTBEAT_GRACE_ENV = "SEMA_SSE_HEARTBEAT_GRACE_MS";
4
5
  export declare const SSE_GRACE_MIN_MS = 5000;
@@ -8,8 +9,19 @@ export declare function resolveSseHeartbeatGraceMs(env?: EnvLike): number | unde
8
9
  export declare function markEngineAliveIdle<E extends Error>(err: E): E;
9
10
  export declare function engineAliveIdleElapsedMs(err: unknown): number | undefined;
10
11
  export declare function isSseIdleError(err: unknown): boolean;
12
+ export type StreamEndedReasonWord = (typeof SDK_STREAM_ENDED_REASONS)[number];
13
+ export type StreamEndedUnknownWhy = 'not_this_class' | 'reason_unreadable' | 'reason_unrecognized';
14
+ export type StreamEndedReading = {
15
+ readonly kind: 'read';
16
+ readonly reason: StreamEndedReasonWord;
17
+ readonly cause?: {} | null;
18
+ } | {
19
+ readonly kind: 'unknown';
20
+ readonly why: StreamEndedUnknownWhy;
21
+ };
22
+ export declare function streamEndedReasonOf(err: {} | null | undefined): StreamEndedReading;
11
23
  export declare function isStreamEndedWithoutTerminalError(err: {} | null | undefined): boolean;
12
- export declare function probeEngineAlive(baseUrl: string, timeoutMs?: number): Promise<boolean>;
24
+ export declare function probeEngineAlive(baseUrl: string): Promise<boolean>;
13
25
  export declare function diagnoseSseIdleTear<T>(inner: AsyncGenerator<T>, baseUrl: string, signal?: AbortSignal): AsyncGenerator<T>;
14
26
  export declare function engineAliveIdleContent(elapsedMs: number, detach?: {
15
27
  detached: true;
@@ -1,6 +1,7 @@
1
1
  import { hostEnv } from './hostEnv.js';
2
2
  import { SseIdleError, StreamEndedWithoutTerminalError, probeHealth as sdkProbeHealth } from '@sema-agent/sdk';
3
- import { readThrownSlot, thrownIsInstanceOf } from './wireFailureShape.js';
3
+ import { THROWN_SLOT_UNREADABLE, readThrownSlot, thrownIsInstanceOf } from './wireFailureShape.js';
4
+ import { SDK_STREAM_ENDED_REASONS } from './generated/sdkFactTables.js';
4
5
  import { msg, renderMessage } from './i18n/translate.js';
5
6
  import { runNotFollowedNoticeRef } from './runFollow.js';
6
7
  export const SSE_HEARTBEAT_GRACE_ENV = 'SEMA_SSE_HEARTBEAT_GRACE_MS';
@@ -38,13 +39,30 @@ export function isSseIdleError(err) {
38
39
  return true;
39
40
  return readThrownSlot(err, 'name', 'data') === 'SseIdleError';
40
41
  }
42
+ function streamEndedUnknown(why) {
43
+ return Object.freeze({ kind: 'unknown', why });
44
+ }
45
+ function isStreamEndedClass(err) {
46
+ return thrownIsInstanceOf(err, StreamEndedWithoutTerminalError) || readThrownSlot(err, 'name', 'data') === 'StreamEndedWithoutTerminalError';
47
+ }
48
+ export function streamEndedReasonOf(err) {
49
+ if (!isStreamEndedClass(err))
50
+ return streamEndedUnknown('not_this_class');
51
+ const raw = readThrownSlot(err, 'reason', 'data');
52
+ if (raw === THROWN_SLOT_UNREADABLE || raw === undefined)
53
+ return streamEndedUnknown('reason_unreadable');
54
+ const reason = SDK_STREAM_ENDED_REASONS.find((w) => w === raw);
55
+ if (reason === undefined)
56
+ return streamEndedUnknown('reason_unrecognized');
57
+ const cause = readThrownSlot(err, 'cause', 'data');
58
+ return Object.freeze(cause === THROWN_SLOT_UNREADABLE || cause === undefined ? { kind: 'read', reason } : { kind: 'read', reason, cause: cause });
59
+ }
41
60
  export function isStreamEndedWithoutTerminalError(err) {
42
- if (thrownIsInstanceOf(err, StreamEndedWithoutTerminalError))
43
- return true;
44
- return readThrownSlot(err, 'name', 'data') === 'StreamEndedWithoutTerminalError';
61
+ const reading = streamEndedReasonOf(err);
62
+ return reading.kind !== 'unknown' || reading.why !== 'not_this_class';
45
63
  }
46
- export async function probeEngineAlive(baseUrl, timeoutMs = 2_000) {
47
- return (await sdkProbeHealth(baseUrl, timeoutMs)).alive;
64
+ export async function probeEngineAlive(baseUrl) {
65
+ return (await sdkProbeHealth(baseUrl)).alive;
48
66
  }
49
67
  export async function* diagnoseSseIdleTear(inner, baseUrl, signal) {
50
68
  try {
@@ -7,6 +7,7 @@ import { resolveOwnerContext } from './engineSubagentResume.js';
7
7
  import { coerceOutput, parentToolCallIdOf, publishSubagentContentEvent, settleSubagentContent, } from '../subagentContentStore.js';
8
8
  import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
9
9
  import { readFrameTrueMark } from '../ownKey.js';
10
+ import { publishEngineInlineTaskModel } from '../engineInlineTaskStats.js';
10
11
  function usableTailClient(client) {
11
12
  return clientHasVerbs(client, [['capabilities'], ['runs', 'subagentStream']]);
12
13
  }
@@ -149,6 +150,11 @@ export function tailEngineSubagent(taskId, opts) {
149
150
  hostLog('debug', `[subagent-tail] ${taskId} settled (${String(d.status)}${typeof d.seq === 'number' ? ` seq=${d.seq}` : ''}${typeof d.summary === 'string' ? ` summary=${d.summary.slice(0, 80)}` : ''}) — close`);
150
151
  break;
151
152
  }
153
+ else if (t === 'task_progress' || t === 'task_started') {
154
+ const card = parentToolCallIdOf(taskId);
155
+ if (card !== undefined && card !== taskId && typeof d.model === 'string')
156
+ publishEngineInlineTaskModel(card, d.model);
157
+ }
152
158
  }
153
159
  }
154
160
  catch (e) {
@@ -49,6 +49,18 @@ export declare function __resetSubagentContentStoreConfigForTests(): void;
49
49
  export declare function bindSubagentIdentityReader(fn: (id: string) => readonly string[] | undefined): void;
50
50
  export declare function registerSubagentContentAlias(parentToolCallId: string, taskId: string): void;
51
51
  export declare function registerSubagentAlias(parentToolCallId: string, taskId: string): void;
52
+ export declare const SUBAGENT_ALIAS_SCOPES: readonly ["content", "full"];
53
+ export type SubagentAliasScope = (typeof SUBAGENT_ALIAS_SCOPES)[number];
54
+ export type SubagentAliasOutcome = 'new' | 'unchanged' | 'rejected';
55
+ export declare function applySubagentAlias(parentToolCallId: string, taskId: string, scope: SubagentAliasScope, seq?: number): SubagentAliasOutcome;
56
+ export interface SeatSubagentAlias {
57
+ readonly parentToolCallId: string;
58
+ readonly taskId: string;
59
+ readonly scope: SubagentAliasScope;
60
+ readonly seq?: number;
61
+ }
62
+ export declare function readSeatSubagentAlias(ev: object | null | undefined): SeatSubagentAlias | undefined;
63
+ export declare function noteSeatSubagentAlias(ev: object | null | undefined): 'registered' | 'ignored';
52
64
  export declare function publishSubagentContentEvent(ev: SubagentContentEvent): void;
53
65
  export declare function pushSubagentLocalEcho(taskId: string, text: string): void;
54
66
  export interface SubagentContentSnapshot {
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
2
- import { readFrameTrueMark } from './ownKey.js';
2
+ import { readFrameTrueMark, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
3
+ import { cycleSeqValue } from './cycleOrder.js';
3
4
  import { toolEndOutputText } from './toolResultContent.js';
4
5
  export function coerceOutput(v) {
5
6
  return toolEndOutputText(v);
@@ -398,7 +399,7 @@ function scheduleNotify(taskId) {
398
399
  }, NOTIFY_COALESCE_MS);
399
400
  }
400
401
  export function registerSubagentContentAlias(parentToolCallId, taskId) {
401
- aliasContentKey(parentToolCallId, taskId);
402
+ applySubagentAlias(parentToolCallId, taskId, 'content');
402
403
  }
403
404
  function aliasContentKey(parentToolCallId, taskId) {
404
405
  if (!parentToolCallId || !taskId || parentToolCallId === taskId)
@@ -415,10 +416,72 @@ function aliasContentKey(parentToolCallId, taskId) {
415
416
  return true;
416
417
  }
417
418
  export function registerSubagentAlias(parentToolCallId, taskId) {
419
+ applySubagentAlias(parentToolCallId, taskId, 'full');
420
+ }
421
+ export const SUBAGENT_ALIAS_SCOPES = ['content', 'full'];
422
+ export function applySubagentAlias(parentToolCallId, taskId, scope, seq) {
423
+ const hadContent = parentToTask.get(parentToolCallId) === taskId;
418
424
  if (!aliasContentKey(parentToolCallId, taskId))
419
- return;
420
- if (taskToParent.get(taskId)?.card !== parentToolCallId)
421
- taskToParent.set(taskId, { card: parentToolCallId, stamp: ++parentStampSeq });
425
+ return 'rejected';
426
+ let changed = !hadContent;
427
+ if (scope === 'full') {
428
+ const cycle = cycleSeqValue(seq);
429
+ const cur = taskToParent.get(taskId);
430
+ if (cur?.card !== parentToolCallId) {
431
+ const stale = cur !== undefined && cycle !== undefined && cur.seq !== undefined && cycle < cur.seq;
432
+ if (!stale) {
433
+ taskToParent.set(taskId, { card: parentToolCallId, stamp: ++parentStampSeq, ...(cycle !== undefined ? { seq: cycle } : {}) });
434
+ changed = true;
435
+ }
436
+ }
437
+ else if (cycle !== undefined && (cur.seq === undefined || cycle > cur.seq)) {
438
+ taskToParent.set(taskId, { card: cur.card, stamp: cur.stamp, seq: cycle });
439
+ }
440
+ }
441
+ return changed ? 'new' : 'unchanged';
442
+ }
443
+ export function readSeatSubagentAlias(ev) {
444
+ try {
445
+ if (typeof ev !== 'object' || ev === null)
446
+ return undefined;
447
+ if (readOwnDataValue(ev, 'kind') !== 'subagent_alias')
448
+ return undefined;
449
+ const parentToolCallId = readOwnDataNonEmptyString(ev, 'parentToolCallId');
450
+ const taskId = readOwnDataNonEmptyString(ev, 'taskId');
451
+ const scope = readOwnDataNonEmptyString(ev, 'scope');
452
+ if (parentToolCallId === undefined || taskId === undefined || scope === undefined)
453
+ return undefined;
454
+ if (!SUBAGENT_ALIAS_SCOPES.includes(scope))
455
+ return undefined;
456
+ const proof = readOwnDataValue(ev, 'laneProof');
457
+ if (typeof proof !== 'object' || proof === null)
458
+ return undefined;
459
+ if (readOwnDataValue(proof, 'lane') !== 'subagent')
460
+ return undefined;
461
+ if (readOwnDataNonEmptyString(proof, 'parentToolCallId') !== parentToolCallId)
462
+ return undefined;
463
+ const seqDesc = Object.getOwnPropertyDescriptor(ev, 'seq');
464
+ let seq;
465
+ if (seqDesc !== undefined) {
466
+ if (!Object.hasOwn(seqDesc, 'value'))
467
+ return undefined;
468
+ if (seqDesc.value !== undefined) {
469
+ seq = cycleSeqValue(seqDesc.value);
470
+ if (seq === undefined)
471
+ return undefined;
472
+ }
473
+ }
474
+ return { parentToolCallId, taskId, scope: scope, ...(seq !== undefined ? { seq } : {}) };
475
+ }
476
+ catch {
477
+ return undefined;
478
+ }
479
+ }
480
+ export function noteSeatSubagentAlias(ev) {
481
+ const alias = readSeatSubagentAlias(ev);
482
+ if (alias === undefined)
483
+ return 'ignored';
484
+ return applySubagentAlias(alias.parentToolCallId, alias.taskId, alias.scope, alias.seq) === 'new' ? 'registered' : 'ignored';
422
485
  }
423
486
  function replaySeen(s, ev) {
424
487
  const id = ev.eventId;
@@ -661,18 +724,16 @@ export function planSubagentViewSlots(taskId) {
661
724
  export function parentToolCallIdOf(taskId) {
662
725
  const ids = sameSubagentIds(taskId);
663
726
  let stated;
664
- let statedStamp = -1;
665
727
  for (const id of ids) {
666
728
  const hit = taskToParent.get(id);
667
729
  if (hit === undefined)
668
730
  continue;
669
- if (stated === undefined || hit.stamp > statedStamp) {
670
- stated = hit.card;
671
- statedStamp = hit.stamp;
672
- }
731
+ const newer = hit.seq !== undefined && stated?.seq !== undefined && hit.seq !== stated.seq ? hit.seq > stated.seq : hit.stamp > (stated?.stamp ?? -1);
732
+ if (stated === undefined || newer)
733
+ stated = hit;
673
734
  }
674
735
  if (stated !== undefined)
675
- return stated;
736
+ return stated.card;
676
737
  for (const id of ids) {
677
738
  const fromContent = tasks.get(id)?.parentToolCallId;
678
739
  if (fromContent !== undefined)
@@ -26,6 +26,7 @@ export type CoreSemaTaskTerminalStatus = (typeof CORE_SEMA_TASK_TERMINAL_STATUSE
26
26
  export declare function isCoreSemaTaskTerminal(status: unknown): status is CoreSemaTaskTerminalStatus;
27
27
  export declare const CORE_WORKFLOW_RUN_TERMINAL_STATUSES: readonly ["completed", "failed"];
28
28
  export declare function isCoreWorkflowRunTerminal(status: unknown): status is (typeof CORE_WORKFLOW_RUN_TERMINAL_STATUSES)[number];
29
+ export declare function isCoreWorkflowRunParkedEnd(status: unknown, errorCode: unknown): boolean;
29
30
  export declare const CORE_TASK_PROGRESS_TERMINAL_STATUSES: readonly ["completed", "failed"];
30
31
  export declare function isCoreTaskProgressTerminal(status: unknown): status is (typeof CORE_TASK_PROGRESS_TERMINAL_STATUSES)[number];
31
32
  export declare function isCoreTaskProgressTerminalNotSuccess(status: unknown): status is 'failed';
@@ -1,4 +1,4 @@
1
- import { CORE_TERMINAL_CAUSE_KINDS, CORE_TASK_NOTIFICATION_TERMINAL_STATUSES as GENERATED_TASK_NOTIFICATION_TERMINAL_STATUSES } from './generated/engineFactTables.js';
1
+ import { CORE_TERMINAL_CAUSE_KINDS, CORE_TASK_NOTIFICATION_TERMINAL_STATUSES as GENERATED_TASK_NOTIFICATION_TERMINAL_STATUSES, CORE_WORKFLOW_AGENT_PARKED_ERROR_CODE, } from './generated/engineFactTables.js';
2
2
  function memberOf(table, word) {
3
3
  return typeof word === 'string' && table.includes(word);
4
4
  }
@@ -53,6 +53,9 @@ export const CORE_WORKFLOW_RUN_TERMINAL_STATUSES = Object.freeze(['completed', '
53
53
  export function isCoreWorkflowRunTerminal(status) {
54
54
  return memberOf(CORE_WORKFLOW_RUN_TERMINAL_STATUSES, status);
55
55
  }
56
+ export function isCoreWorkflowRunParkedEnd(status, errorCode) {
57
+ return status === 'failed' && errorCode === CORE_WORKFLOW_AGENT_PARKED_ERROR_CODE;
58
+ }
56
59
  export const CORE_TASK_PROGRESS_TERMINAL_STATUSES = Object.freeze(['completed', 'failed']);
57
60
  export function isCoreTaskProgressTerminal(status) {
58
61
  return memberOf(CORE_TASK_PROGRESS_TERMINAL_STATUSES, status);
@@ -3,7 +3,7 @@ import { markEngineWorkflowNotified, registerOutstandingBgTask, registerOutstand
3
3
  import { parseWorkflowPollEnvelope, projectWorkflowTaskOutput } from './workflow.js';
4
4
  import { asModelFallbackReason } from './engineErrorCodes.js';
5
5
  import { frozenSet } from './frozenSet.js';
6
- import { bashExitOf, bashExitReadingFailed } from './bashExitReading.js';
6
+ import { bashCurtailedResultPart, bashCurtailmentOf, bashCurtailmentSentence, bashExitOf, bashExitReadingFailed } from './bashExitReading.js';
7
7
  import { guardedRead, ownDataField, ownSnapshotWhenTyped, readFrameTrueMark, readFrameTrueMarkReading, readOwnDataFiniteNumber, readOwnDataNonEmptyString, readOwnDataValue, toolResultBlockOf, } from './ownKey.js';
8
8
  import { displayUntrusted } from './displayUntrusted.js';
9
9
  import { isCoreSemaTaskTerminal } from './terminalStatuses.js';
@@ -370,6 +370,7 @@ export function wireOutputToBody(toolName, output, isError, truncated, flatten,
370
370
  let parsedMemo;
371
371
  const parseOnce = () => (parsedMemo === undefined ? (parsedMemo = parseModelFacingBash(text)) : parsedMemo);
372
372
  const exit = bashExitOf(structured, parseOnce);
373
+ const cut = bashCurtailmentOf(structured);
373
374
  const structuredSlots = sType === 'bash' && (typeof s?.stdout === 'string' || typeof s?.stderr === 'string');
374
375
  const textSlots = structuredSlots ? null : parseOnce();
375
376
  const slots = structuredSlots
@@ -392,26 +393,37 @@ export function wireOutputToBody(toolName, output, isError, truncated, flatten,
392
393
  }
393
394
  if (body.trim() === '' && text.trim() !== '')
394
395
  body = text;
396
+ else if (cut !== undefined) {
397
+ if (body.trim() === '')
398
+ body = bashCurtailmentSentence(cut);
399
+ else if (reason.trim() !== '')
400
+ body = `${bashCurtailmentSentence(cut)}\n${body}`;
401
+ }
395
402
  return { content: truncated ? `${body}\n…(truncated)` : body, isError: true };
396
403
  }
397
404
  const stdout = truncated ? `${slots.stdout}\n…(truncated)` : slots.stdout;
398
405
  const processedStdout = stdout.replace(/^(\s*\n)+/, '').trimEnd();
399
406
  return {
400
- content: [processedStdout, slots.stderr.trim()].filter(Boolean).join('\n'),
407
+ content: [cut !== undefined ? bashCurtailmentSentence(cut) : '', processedStdout, slots.stderr.trim()].filter(Boolean).join('\n'),
401
408
  isError: false,
402
409
  toolUseResult: {
403
410
  stdout,
404
- stderr: slots.stderr,
411
+ ...bashCurtailedResultPart(slots.stderr, cut),
405
412
  interrupted: false,
406
413
  ...(benignInterp !== undefined ? { returnCodeInterpretation: benignInterp } : {}),
407
414
  },
408
415
  };
409
416
  }
410
417
  const stdout = truncated ? `${text}\n…(truncated)` : text;
418
+ const cutSentence = cut !== undefined ? bashCurtailmentSentence(cut) : undefined;
411
419
  if (isError || bashExitReadingFailed(exit)) {
420
+ if (cutSentence !== undefined && text.trim() === '' && exit.exitCode !== 137) {
421
+ return { content: truncated ? `${cutSentence}\n…(truncated)` : cutSentence, isError: true };
422
+ }
412
423
  return { content: exit.exitCode === 137 ? `${stdout}\n${killedBySignalSentence(killCauseOf)}` : stdout, isError: true };
413
424
  }
414
- return { content: stdout, isError, toolUseResult: { stdout, stderr: '', interrupted: false } };
425
+ const okContent = cutSentence === undefined ? stdout : text.trim() === '' ? (truncated ? `${cutSentence}\n…(truncated)` : cutSentence) : `${cutSentence}\n${stdout}`;
426
+ return { content: okContent, isError, toolUseResult: { stdout, ...bashCurtailedResultPart('', cut), interrupted: false } };
415
427
  }
416
428
  if (name === 'taskoutput' || name === 'task_output') {
417
429
  const body = truncated ? `${text}\n…(truncated)` : text;
@@ -498,10 +510,11 @@ export function structuredToToolUseResult(structured, modelText, leaderLedger =
498
510
  return null;
499
511
  const exit = bashExitOf(structured, () => null);
500
512
  const benignInterp = exit.benignNote;
513
+ const cut = bashCurtailmentOf(structured);
501
514
  return {
502
515
  toolUseResult: {
503
516
  stdout: typeof s.stdout === 'string' ? s.stdout : '',
504
- stderr: typeof s.stderr === 'string' ? s.stderr : '',
517
+ ...bashCurtailedResultPart(typeof s.stderr === 'string' ? s.stderr : '', cut),
505
518
  interrupted: false,
506
519
  ...(benignInterp !== undefined ? { returnCodeInterpretation: benignInterp } : {}),
507
520
  },
@@ -1,6 +1,6 @@
1
1
  import { msg, renderMessage } from './i18n/translate.js';
2
2
  import { classifyTurnWireError, isPreStreamDrainingReject } from './wireErrorTriage.js';
3
- import { isSseIdleError } from './sseIdleTriage.js';
3
+ import { isSseIdleError, streamEndedReasonOf } from './sseIdleTriage.js';
4
4
  import { runNotFollowedOf } from './runFollow.js';
5
5
  import { isCodeClassifiedGateFailure } from './gateFailureCodes.js';
6
6
  import { CORRUPT_ROW_ERROR_CODE, CORRUPT_SESSION_ERROR_CODE, MEMORY_CONTROL_PLANE_CORRUPT, SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE } from './engineErrorCodes.js';
@@ -36,6 +36,13 @@ const TURN_FAILURE_HEADLINE_KEYS = Object.freeze({
36
36
  internal: 'notice.failure.headline.internal',
37
37
  cause_unreadable: 'notice.failure.headline.causeUnreadable',
38
38
  });
39
+ const STREAM_ENDED_HEADLINE_KEYS = Object.freeze({
40
+ empty_legs: 'notice.streamEnded.emptyLegs.headline',
41
+ consecutive_failures: 'notice.streamEnded.consecutiveFailures.headline',
42
+ });
43
+ function parkedHeadlineKeyOf(reading) {
44
+ return reading.kind === 'read' ? STREAM_ENDED_HEADLINE_KEYS[reading.reason] : TURN_FAILURE_HEADLINE_KEYS.parked;
45
+ }
39
46
  function waitSecOf(err) {
40
47
  const ms = readThrownSlot(err, 'retryAfterMs', 'data');
41
48
  if (typeof ms === 'number' && Number.isFinite(ms) && ms > 0)
@@ -128,7 +135,7 @@ export function turnFailurePresentation(err, evidence, notesCtx, lang) {
128
135
  }
129
136
  else if (verdict.kind === 'stream-ended-without-terminal') {
130
137
  shape = { category: 'parked', offer: false };
131
- headlineKey = TURN_FAILURE_HEADLINE_KEYS.parked;
138
+ headlineKey = parkedHeadlineKeyOf(streamEndedReasonOf(err));
132
139
  detail = wireFailureTextsOf(err).reason;
133
140
  }
134
141
  else if (verdict.kind === 'transport') {
@@ -8,4 +8,15 @@ type WritableKeys<T> = {
8
8
  }, P, never>;
9
9
  }[keyof T];
10
10
  export type AllReadonly<T> = [WritableKeys<T>] extends [never] ? true : false;
11
+ export type Same<A, B> = [Exclude<A, B>, Exclude<B, A>] extends [never, never] ? true : false;
12
+ export type KeysEq<A, B> = Same<keyof A, keyof B>;
13
+ export type Fits<A, B> = [A] extends [B] ? true : false;
14
+ export type Present<T> = [T] extends [never] ? false : true;
15
+ export type RequiredKeys<T> = {
16
+ [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
17
+ }[keyof T];
18
+ export type NeverValuedKeys<T> = {
19
+ [K in keyof T]-?: [Exclude<T[K], undefined>] extends [never] ? K : never;
20
+ }[keyof T];
21
+ export type NoNeverValues<T> = Same<NeverValuedKeys<T>, never>;
11
22
  export {};
@@ -144,3 +144,20 @@ export declare function projectUsageAnalytics(inputs: UsageAnalyticsInputs, wind
144
144
  export declare function readUsageAnalytics(client: UsageAnalyticsClientLike, window: UsageAnalyticsWindow, opts?: {
145
145
  signal?: AbortSignal;
146
146
  }): Promise<UsageAnalyticsView>;
147
+ export declare const CENTER_USAGE_COST_UNKNOWN_WHYS: readonly ["declared_unknown", "basis_unrecognized", "estimated_zero", "amount_absent"];
148
+ export type CenterUsageCostUnknownWhy = (typeof CENTER_USAGE_COST_UNKNOWN_WHYS)[number];
149
+ export interface CenterUsageCostAmount {
150
+ readonly value: number;
151
+ readonly bound: Extract<UsageBound, 'exact' | 'lower'>;
152
+ }
153
+ export interface CenterUsageCostAmountReading {
154
+ readonly state: 'amount';
155
+ readonly amount: CenterUsageCostAmount;
156
+ readonly unpriced: boolean;
157
+ }
158
+ export interface CenterUsageCostUnknownReading {
159
+ readonly state: 'unknown';
160
+ readonly why: CenterUsageCostUnknownWhy;
161
+ }
162
+ export type CenterUsageCostReading = CenterUsageCostAmountReading | CenterUsageCostUnknownReading;
163
+ export declare function centerUsageCostOf(body: object | null | undefined): CenterUsageCostReading;
@@ -1,4 +1,4 @@
1
- import { readThrownSlot, THROWN_SLOT_UNREADABLE } from './wireFailureShape.js';
1
+ import { readThrownSlot, slotReadOf, THROWN_SLOT_UNREADABLE } from './wireFailureShape.js';
2
2
  export const USAGE_SENTENCES = Object.freeze({
3
3
  analyticsUnavailable: 'Usage analytics aren’t available on this backend.',
4
4
  analyticsUnread: 'Usage analytics couldn’t be read just now.',
@@ -161,6 +161,9 @@ export function readUsageBreakdown(client, opts = {}) {
161
161
  export function usageBoundPrefix(bound) {
162
162
  return bound === 'lower' ? '≥' : bound === 'approximate' ? '≈' : '';
163
163
  }
164
+ function costBoundOf(precision, truncated) {
165
+ return precision === 'exact' && !truncated ? 'exact' : 'lower';
166
+ }
164
167
  const DAY_MS = 86_400_000;
165
168
  export const USAGE_HEATMAP_MAX_DAYS = 70;
166
169
  export function usageWindowOfRange(range, now) {
@@ -275,7 +278,7 @@ export function projectUsageAnalytics(inputs, window) {
275
278
  readings.tokens = { value: sv.tokensIn + sv.tokensOut, bound: !exact ? 'approximate' : sv.truncated ? 'lower' : 'exact' };
276
279
  }
277
280
  if (sv.cost !== undefined)
278
- readings.cost = { value: sv.cost, bound: exact && !sv.truncated ? 'exact' : 'lower' };
281
+ readings.cost = { value: sv.cost, bound: costBoundOf(sv.precision, sv.truncated) };
279
282
  const day = inputs.daySeries;
280
283
  if (day.kind === 'ok' && isWhole(day.value)) {
281
284
  const dayValues = new Map();
@@ -328,3 +331,42 @@ export async function readUsageAnalytics(client, window, opts = {}) {
328
331
  ]);
329
332
  return projectUsageAnalytics({ summary, daySeries, hourSeries, modelBreakdown }, window);
330
333
  }
334
+ const CENTER_COST_BASIS_WORDS = ['exact', 'lowerBound', 'unknown'];
335
+ export const CENTER_USAGE_COST_UNKNOWN_WHYS = Object.freeze(['declared_unknown', 'basis_unrecognized', 'estimated_zero', 'amount_absent']);
336
+ const unknownCost = (why) => ({ state: 'unknown', why });
337
+ const amountCost = (value, bound, unpriced) => ({ state: 'amount', amount: { value, bound }, unpriced });
338
+ const flagNotRuledOut = (r) => r.value === true || r.presence === 'unreadable';
339
+ export function centerUsageCostOf(body) {
340
+ try {
341
+ if (!isRecord(body))
342
+ return unknownCost('amount_absent');
343
+ const basis = slotReadOf(body, 'costBasis');
344
+ let word;
345
+ if (basis.presence === 'present') {
346
+ word = CENTER_COST_BASIS_WORDS.find((w) => w === basis.value);
347
+ if (word === undefined)
348
+ return unknownCost('basis_unrecognized');
349
+ }
350
+ else if (basis.presence === 'unreadable') {
351
+ return unknownCost('basis_unrecognized');
352
+ }
353
+ if (word === 'unknown')
354
+ return unknownCost('declared_unknown');
355
+ const cost = slotReadOf(body, 'costUsd').value;
356
+ if (!nonNeg(cost))
357
+ return unknownCost('amount_absent');
358
+ const est = slotReadOf(body, 'estimated');
359
+ const trunc = slotReadOf(body, 'truncated');
360
+ const unpriced = est.value === true;
361
+ if (word === 'lowerBound')
362
+ return amountCost(cost, 'lower', unpriced);
363
+ if (word === 'exact')
364
+ return flagNotRuledOut(est) || flagNotRuledOut(trunc) ? amountCost(cost, 'lower', unpriced) : amountCost(cost, 'exact', false);
365
+ if (unpriced && cost === 0)
366
+ return unknownCost('estimated_zero');
367
+ return amountCost(cost, costBoundOf(precisionOf(est.value), flagNotRuledOut(trunc)), unpriced);
368
+ }
369
+ catch {
370
+ return unknownCost('amount_absent');
371
+ }
372
+ }