@sema-agent/client-core 0.109.0 → 0.111.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 (236) hide show
  1. package/CHANGELOG.md +342 -0
  2. package/README.md +88 -32
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +45 -11
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +20 -15
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/attachmentRoute.d.ts +20 -0
  16. package/dist/attachmentRoute.js +39 -0
  17. package/dist/attachmentsWireCaps.js +1 -1
  18. package/dist/builtinOutputStyles.d.ts +12 -0
  19. package/dist/builtinOutputStyles.js +147 -0
  20. package/dist/compactionStatus.d.ts +9 -0
  21. package/dist/compactionStatus.js +29 -5
  22. package/dist/controlRouter.js +2 -1
  23. package/dist/coreValuePorts.d.ts +39 -1
  24. package/dist/coreValuePorts.js +10 -0
  25. package/dist/declared/supportFloorDeclared.js +1 -1
  26. package/dist/displayUntrusted.d.ts +1 -0
  27. package/dist/displayUntrusted.js +138 -14
  28. package/dist/engineErrorCodes.d.ts +3 -0
  29. package/dist/engineErrorCodes.js +3 -0
  30. package/dist/engineHealthFacts.d.ts +20 -0
  31. package/dist/engineHealthFacts.js +42 -1
  32. package/dist/engineHealthProbeVerdict.d.ts +23 -0
  33. package/dist/engineHealthProbeVerdict.js +115 -0
  34. package/dist/engineInlineTaskStats.d.ts +4 -3
  35. package/dist/engineInlineTaskStats.js +18 -2
  36. package/dist/engineNoticeCodes.d.ts +10 -0
  37. package/dist/engineNoticeCodes.js +36 -6
  38. package/dist/engineRepoint.d.ts +147 -0
  39. package/dist/engineRepoint.js +310 -0
  40. package/dist/engineRowRef.d.ts +3 -0
  41. package/dist/engineRowRef.js +13 -1
  42. package/dist/engineSessionMessages.d.ts +2 -1
  43. package/dist/engineSessionMessages.js +5 -0
  44. package/dist/engineSwapCrossing.d.ts +57 -0
  45. package/dist/engineSwapCrossing.js +297 -0
  46. package/dist/engineUnavailable.d.ts +44 -0
  47. package/dist/engineUnavailable.js +492 -0
  48. package/dist/errorSupplementRoles.js +2 -1
  49. package/dist/eventLeg.js +3 -3
  50. package/dist/fleet/fleetProjection.js +3 -2
  51. package/dist/generated/engineFactTables.d.ts +2 -0
  52. package/dist/generated/engineFactTables.js +12 -0
  53. package/dist/generated/sdkFactTables.d.ts +2 -1
  54. package/dist/generated/sdkFactTables.js +5 -0
  55. package/dist/generated/settingsSchemaTables.d.ts +4 -0
  56. package/dist/generated/settingsSchemaTables.js +1 -0
  57. package/dist/hitl/approvalAskers.d.ts +6 -0
  58. package/dist/hitl/approvalAskers.js +30 -0
  59. package/dist/hitl/approvalCardReasons.js +6 -3
  60. package/dist/hitl/approvalDelivery.d.ts +1 -0
  61. package/dist/hitl/approvalDelivery.js +1 -1
  62. package/dist/hitl/approvalRequestFrameReading.d.ts +7 -0
  63. package/dist/hitl/approvalRequestFrameReading.js +11 -0
  64. package/dist/hitl/approvalResolution.d.ts +9 -2
  65. package/dist/hitl/approvalResolution.js +6 -1
  66. package/dist/hitl/askGateWire.js +5 -8
  67. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  68. package/dist/hitl/decisionConflict.js +6 -3
  69. package/dist/hitl/decisionRefusal.d.ts +2 -0
  70. package/dist/hitl/decisionRefusal.js +8 -8
  71. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  72. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  73. package/dist/hitl/decisionSend.d.ts +10 -0
  74. package/dist/hitl/decisionSend.js +57 -17
  75. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  76. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  77. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  78. package/dist/hitl/elicitationRefusal.js +23 -0
  79. package/dist/hitl/frameRouter.d.ts +2 -2
  80. package/dist/hitl/frameRouter.js +12 -33
  81. package/dist/hitl/gateIdentity.d.ts +5 -0
  82. package/dist/hitl/gateIdentity.js +23 -0
  83. package/dist/hitl/gateLedger.d.ts +0 -4
  84. package/dist/hitl/gateLedger.js +0 -19
  85. package/dist/hitl/hitlBridge.d.ts +7 -0
  86. package/dist/hitl/hitlBridge.js +14 -8
  87. package/dist/hitl/hitlHostSurface.d.ts +3 -1
  88. package/dist/hitl/hitlHostSurface.js +14 -4
  89. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  90. package/dist/hitl/hostWithdrawal.js +38 -0
  91. package/dist/hitl/localAllowRedemption.d.ts +67 -0
  92. package/dist/hitl/localAllowRedemption.js +256 -0
  93. package/dist/hitl/parkResolver.js +32 -13
  94. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  95. package/dist/hitl/persistedRulesWire.js +72 -2
  96. package/dist/hitl/planReviewWire.d.ts +3 -0
  97. package/dist/hitl/planReviewWire.js +37 -10
  98. package/dist/hitl/policyDenyCause.d.ts +1 -1
  99. package/dist/hitl/policyDenyCause.js +1 -1
  100. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  101. package/dist/hitl/resumeRunningCard.js +1 -0
  102. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  103. package/dist/hitl/screenCardCarriers.js +41 -0
  104. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  105. package/dist/hitl/screenDecisionCards.js +362 -0
  106. package/dist/hitl/sessionPolicyWire.js +6 -4
  107. package/dist/hitl/standingRows.d.ts +4 -1
  108. package/dist/hitl/standingRows.js +33 -1
  109. package/dist/hitl/toolApprovalWire.d.ts +64 -15
  110. package/dist/hitl/toolApprovalWire.js +470 -125
  111. package/dist/hooksWireCaps.js +31 -1
  112. package/dist/hostSettingNoticeArm.d.ts +3 -0
  113. package/dist/hostSettingNoticeArm.js +12 -0
  114. package/dist/hostTurnSettings.d.ts +13 -4
  115. package/dist/hostTurnSettings.js +150 -10
  116. package/dist/humanTurnRead.js +11 -1
  117. package/dist/i18n/messages.en.d.ts +212 -0
  118. package/dist/i18n/messages.en.js +217 -5
  119. package/dist/i18n/messages.zh.js +235 -5
  120. package/dist/i18n/translate.d.ts +3 -0
  121. package/dist/i18n/translate.js +78 -10
  122. package/dist/i18n/zh.d.ts +1 -0
  123. package/dist/i18n/zh.js +1 -0
  124. package/dist/index.d.ts +56 -16
  125. package/dist/index.js +40 -12
  126. package/dist/inheritEnvCapability.js +2 -0
  127. package/dist/inputAuthor.d.ts +16 -0
  128. package/dist/inputAuthor.js +44 -0
  129. package/dist/interactiveHalt.js +2 -1
  130. package/dist/interactiveInterrupt.js +20 -2
  131. package/dist/interruptRefusalNote.d.ts +2 -0
  132. package/dist/interruptRefusalNote.js +17 -0
  133. package/dist/liveModelCatalog.d.ts +2 -0
  134. package/dist/liveModelCatalog.js +23 -9
  135. package/dist/liveQuestionStore.d.ts +4 -1
  136. package/dist/liveQuestionStore.js +12 -3
  137. package/dist/localEngineSeatReading.d.ts +2 -2
  138. package/dist/localEngineSeatReading.js +5 -3
  139. package/dist/localEngineSpawn.d.ts +0 -7
  140. package/dist/localEngineSpawn.js +0 -92
  141. package/dist/localEngineStep.d.ts +2 -2
  142. package/dist/localEngineStep.js +2 -0
  143. package/dist/mcpEngineLeg.d.ts +3 -0
  144. package/dist/mcpEngineLeg.js +38 -1
  145. package/dist/mcpPanel.js +2 -0
  146. package/dist/mcpReconnect.d.ts +9 -1
  147. package/dist/mcpReconnect.js +124 -19
  148. package/dist/mcpWireCaps.js +1 -1
  149. package/dist/model/engineModelWords.d.ts +3 -0
  150. package/dist/model/engineModelWords.js +13 -0
  151. package/dist/model/subagentModelWord.d.ts +21 -2
  152. package/dist/model/subagentModelWord.js +100 -29
  153. package/dist/modelCapabilityProbe.js +7 -43
  154. package/dist/modsFace.d.ts +8 -1
  155. package/dist/modsFace.js +27 -0
  156. package/dist/notifications.d.ts +36 -6
  157. package/dist/notifications.js +170 -20
  158. package/dist/ownKey.d.ts +6 -5
  159. package/dist/ownKey.js +31 -7
  160. package/dist/peerFrames.d.ts +40 -2
  161. package/dist/peerFrames.js +158 -3
  162. package/dist/pendingUserRow.d.ts +4 -0
  163. package/dist/pendingUserRow.js +16 -0
  164. package/dist/permissionsDeclaration.d.ts +1 -0
  165. package/dist/permissionsDeclaration.js +11 -1
  166. package/dist/planReviewCardFacts.d.ts +3 -0
  167. package/dist/planReviewCardFacts.js +52 -2
  168. package/dist/pluginHooksWire.d.ts +10 -1
  169. package/dist/pluginHooksWire.js +96 -7
  170. package/dist/printToolResultFrame.js +4 -2
  171. package/dist/promptBlockedCopy.js +11 -6
  172. package/dist/promptSuggestionBatch.d.ts +7 -0
  173. package/dist/promptSuggestionBatch.js +9 -0
  174. package/dist/promptSuggestionsTail.d.ts +39 -0
  175. package/dist/promptSuggestionsTail.js +116 -0
  176. package/dist/replayBody.js +10 -2
  177. package/dist/request/taskRequest.d.ts +5 -0
  178. package/dist/request/taskRequest.js +142 -21
  179. package/dist/requestCoordinate.d.ts +1 -0
  180. package/dist/requestCoordinate.js +13 -0
  181. package/dist/resumeRefusalCopy.d.ts +1 -3
  182. package/dist/resumeRefusalCopy.js +2 -1
  183. package/dist/retryStatus.d.ts +2 -2
  184. package/dist/retryStatus.js +2 -0
  185. package/dist/rewindArchiveReading.js +4 -4
  186. package/dist/runCancelContext.d.ts +2 -1
  187. package/dist/runCancelContext.js +1 -0
  188. package/dist/runPermissionModeSwitch.js +2 -2
  189. package/dist/scenarioRefusal.js +11 -13
  190. package/dist/scenarioSkills.js +11 -6
  191. package/dist/seam.d.ts +27 -2
  192. package/dist/seam.js +17 -1
  193. package/dist/seatContract.d.ts +189 -12
  194. package/dist/seatContract.js +401 -9
  195. package/dist/sessionBackground.js +2 -1
  196. package/dist/sessionBackgroundHandoff.js +2 -1
  197. package/dist/sessionCostReading.d.ts +70 -0
  198. package/dist/sessionCostReading.js +150 -0
  199. package/dist/sessionEngine.d.ts +2 -0
  200. package/dist/sessionEngine.js +111 -0
  201. package/dist/sessionHooksRoster.d.ts +4 -0
  202. package/dist/sessionHooksRoster.js +158 -0
  203. package/dist/sessionMemoryStatus.d.ts +2 -1
  204. package/dist/sessionMemoryStatus.js +2 -2
  205. package/dist/sessionReadRedaction.d.ts +7 -0
  206. package/dist/sessionReadRedaction.js +26 -0
  207. package/dist/sessionSyncFileHistory.d.ts +4 -0
  208. package/dist/sessionSyncFileHistory.js +27 -0
  209. package/dist/settingsHooksSalvage.js +2 -1
  210. package/dist/sseIdleTriage.d.ts +13 -1
  211. package/dist/sseIdleTriage.js +25 -6
  212. package/dist/storeDurabilityWord.d.ts +15 -0
  213. package/dist/storeDurabilityWord.js +18 -0
  214. package/dist/storePostureCapability.d.ts +7 -3
  215. package/dist/storePostureCapability.js +69 -39
  216. package/dist/subagent/engineSubagentResume.js +2 -2
  217. package/dist/subagent/engineSubagentTail.js +8 -1
  218. package/dist/subagentContentStore.d.ts +11 -0
  219. package/dist/subagentContentStore.js +28 -2
  220. package/dist/terminalStatuses.d.ts +1 -0
  221. package/dist/terminalStatuses.js +4 -1
  222. package/dist/toolResult.d.ts +1 -0
  223. package/dist/toolResult.js +2 -0
  224. package/dist/turnFailurePresentation.js +9 -2
  225. package/dist/typePins.d.ts +11 -0
  226. package/dist/usageAnalytics.d.ts +17 -0
  227. package/dist/usageAnalytics.js +44 -2
  228. package/dist/userAttachmentEnvelope.d.ts +2 -1
  229. package/dist/userAttachmentEnvelope.js +10 -0
  230. package/dist/wireErrorTriage.js +2 -1
  231. package/dist/wireFailureShape.d.ts +6 -5
  232. package/dist/wireFailureShape.js +150 -38
  233. package/dist/wireRefusalCopy.d.ts +16 -5
  234. package/dist/wireRefusalCopy.js +119 -86
  235. package/docs/INTEGRATION-CLIENTS.md +4228 -21
  236. package/package.json +7 -3
@@ -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,8 @@
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 { classifyHealthProbe } from './engineHealthProbeVerdict.js';
4
+ import { THROWN_SLOT_UNREADABLE, readThrownSlot, thrownIsInstanceOf } from './wireFailureShape.js';
5
+ import { SDK_STREAM_ENDED_REASONS } from './generated/sdkFactTables.js';
4
6
  import { msg, renderMessage } from './i18n/translate.js';
5
7
  import { runNotFollowedNoticeRef } from './runFollow.js';
6
8
  export const SSE_HEARTBEAT_GRACE_ENV = 'SEMA_SSE_HEARTBEAT_GRACE_MS';
@@ -38,13 +40,30 @@ export function isSseIdleError(err) {
38
40
  return true;
39
41
  return readThrownSlot(err, 'name', 'data') === 'SseIdleError';
40
42
  }
43
+ function streamEndedUnknown(why) {
44
+ return Object.freeze({ kind: 'unknown', why });
45
+ }
46
+ function isStreamEndedClass(err) {
47
+ return thrownIsInstanceOf(err, StreamEndedWithoutTerminalError) || readThrownSlot(err, 'name', 'data') === 'StreamEndedWithoutTerminalError';
48
+ }
49
+ export function streamEndedReasonOf(err) {
50
+ if (!isStreamEndedClass(err))
51
+ return streamEndedUnknown('not_this_class');
52
+ const raw = readThrownSlot(err, 'reason', 'data');
53
+ if (raw === THROWN_SLOT_UNREADABLE || raw === undefined)
54
+ return streamEndedUnknown('reason_unreadable');
55
+ const reason = SDK_STREAM_ENDED_REASONS.find((w) => w === raw);
56
+ if (reason === undefined)
57
+ return streamEndedUnknown('reason_unrecognized');
58
+ const cause = readThrownSlot(err, 'cause', 'data');
59
+ return Object.freeze(cause === THROWN_SLOT_UNREADABLE || cause === undefined ? { kind: 'read', reason } : { kind: 'read', reason, cause: cause });
60
+ }
41
61
  export function isStreamEndedWithoutTerminalError(err) {
42
- if (thrownIsInstanceOf(err, StreamEndedWithoutTerminalError))
43
- return true;
44
- return readThrownSlot(err, 'name', 'data') === 'StreamEndedWithoutTerminalError';
62
+ const reading = streamEndedReasonOf(err);
63
+ return reading.kind !== 'unknown' || reading.why !== 'not_this_class';
45
64
  }
46
- export async function probeEngineAlive(baseUrl, timeoutMs = 2_000) {
47
- return (await sdkProbeHealth(baseUrl, timeoutMs)).alive;
65
+ export async function probeEngineAlive(baseUrl) {
66
+ return classifyHealthProbe(await sdkProbeHealth(baseUrl))?.kind === 'answered_ok';
48
67
  }
49
68
  export async function* diagnoseSseIdleTear(inner, baseUrl, signal) {
50
69
  try {
@@ -0,0 +1,15 @@
1
+ declare const STORE_DURABILITY_WORDS: readonly ["durable", "ephemeral", "unknown"];
2
+ export type EngineHealthStoreDurability = (typeof STORE_DURABILITY_WORDS)[number];
3
+ export type StoreDurabilityRead = {
4
+ readonly kind: 'word';
5
+ readonly durability: EngineHealthStoreDurability;
6
+ } | {
7
+ readonly kind: 'unrecognized';
8
+ readonly word: string;
9
+ } | {
10
+ readonly kind: 'absent';
11
+ } | {
12
+ readonly kind: 'unreadable';
13
+ };
14
+ export declare function readStoreDurability(section: object): StoreDurabilityRead;
15
+ export {};
@@ -0,0 +1,18 @@
1
+ import { ownDataField } from './ownKey.js';
2
+ const STORE_DURABILITY_WORDS = ['durable', 'ephemeral', 'unknown'];
3
+ export function readStoreDurability(section) {
4
+ try {
5
+ const raw = ownDataField(section, 'durability');
6
+ if (raw === undefined)
7
+ return Object.freeze({ kind: 'absent' });
8
+ if (typeof raw !== 'string' || raw === '')
9
+ return Object.freeze({ kind: 'unreadable' });
10
+ for (const w of STORE_DURABILITY_WORDS)
11
+ if (raw === w)
12
+ return Object.freeze({ kind: 'word', durability: w });
13
+ return Object.freeze({ kind: 'unrecognized', word: raw });
14
+ }
15
+ catch {
16
+ return Object.freeze({ kind: 'unreadable' });
17
+ }
18
+ }
@@ -1,8 +1,12 @@
1
1
  import { type SqlEngineCapabilityView } from './sqlEngineCapability.js';
2
+ import { type EngineHealthStoreDurability } from './storeDurabilityWord.js';
3
+ import type { UiLanguage } from './env/uiLanguage.js';
2
4
  export interface StorePostureView {
3
5
  declared: string;
4
6
  backend: string;
5
- durable: boolean;
7
+ durable?: boolean;
8
+ durability?: EngineHealthStoreDurability;
9
+ unrecognizedDurability?: string;
6
10
  degradedReason?: string;
7
11
  sql?: SqlEngineCapabilityView;
8
12
  }
@@ -19,5 +23,5 @@ export type StorePostureReading = {
19
23
  export declare function projectStorePostureCapability(caps: unknown): StorePostureReading | undefined;
20
24
  export declare function observedStorePosture(baseUrl?: string | undefined): StorePostureReading;
21
25
  export declare function storePersistence(reading: StorePostureReading): 'durable' | 'not_durable' | 'unknown';
22
- export declare function storePostureHeadWord(reading: StorePostureReading): string | undefined;
23
- export declare function storePostureDoctorDetail(reading: StorePostureReading): string;
26
+ export declare function storePostureHeadWord(reading: StorePostureReading, lang?: UiLanguage): string | undefined;
27
+ export declare function storePostureDoctorDetail(reading: StorePostureReading, lang?: UiLanguage): string;
@@ -3,6 +3,8 @@ import { engineWireTargetBaseUrl } from './engineWireTarget.js';
3
3
  import { storeSeatOf, createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
4
4
  import { projectSqlEngineCapability } from './sqlEngineCapability.js';
5
5
  import { ownDataField } from './ownKey.js';
6
+ import { readStoreDurability } from './storeDurabilityWord.js';
7
+ import { msg, renderMessage } from './i18n/translate.js';
6
8
  const nonEmpty = (v) => typeof v === 'string' && v !== '';
7
9
  export function projectStorePostureCapability(caps) {
8
10
  try {
@@ -21,8 +23,8 @@ function projectStorePostureOf(caps) {
21
23
  const store = seat.store;
22
24
  const declared = ownDataField(store, 'declared');
23
25
  const backend = ownDataField(store, 'backend');
24
- const durable = ownDataField(store, 'durable');
25
- if (!nonEmpty(declared) || !nonEmpty(backend) || typeof durable !== 'boolean')
26
+ const durability = durabilityOf(store, 'wire');
27
+ if (!nonEmpty(declared) || !nonEmpty(backend) || durability.kind === 'malformed')
26
28
  return undefined;
27
29
  const reason = ownDataField(store, 'degradedReason');
28
30
  if (reason !== undefined && !nonEmpty(reason))
@@ -35,12 +37,44 @@ function projectStorePostureOf(caps) {
35
37
  view: {
36
38
  declared,
37
39
  backend,
38
- durable,
40
+ ...(durability.kind === 'legacy' ? { durable: durability.durable } : {}),
41
+ ...(durability.kind === 'word' ? { durability: durability.durability } : {}),
42
+ ...(durability.kind === 'unrecognized' ? { unrecognizedDurability: durability.word } : {}),
39
43
  ...(nonEmpty(reason) ? { degradedReason: reason } : {}),
40
44
  ...(sqlReading.kind === 'present' ? { sql: sqlReading.view } : {}),
41
45
  },
42
46
  };
43
47
  }
48
+ const MALFORMED_DURABILITY = Object.freeze({ kind: 'malformed' });
49
+ function durabilityOf(section, side) {
50
+ try {
51
+ const word = readStoreDurability(section);
52
+ const durable = ownDataField(section, 'durable');
53
+ const unrecognized = side === 'view' ? ownDataField(section, 'unrecognizedDurability') : undefined;
54
+ const present = (word.kind === 'absent' ? 0 : 1) + (durable === undefined ? 0 : 1) + (unrecognized === undefined ? 0 : 1);
55
+ if (present !== 1)
56
+ return MALFORMED_DURABILITY;
57
+ if (word.kind === 'word')
58
+ return { kind: 'word', durability: word.durability };
59
+ if (word.kind === 'unrecognized')
60
+ return { kind: 'unrecognized', word: word.word };
61
+ if (typeof durable === 'boolean')
62
+ return { kind: 'legacy', durable };
63
+ if (nonEmpty(unrecognized))
64
+ return { kind: 'unrecognized', word: unrecognized };
65
+ return MALFORMED_DURABILITY;
66
+ }
67
+ catch {
68
+ return MALFORMED_DURABILITY;
69
+ }
70
+ }
71
+ function persistenceOf(d) {
72
+ if (d.kind === 'legacy')
73
+ return d.durable ? 'durable' : 'not_durable';
74
+ if (d.kind !== 'word')
75
+ return 'unknown';
76
+ return d.durability === 'durable' ? 'durable' : d.durability === 'ephemeral' ? 'not_durable' : 'unknown';
77
+ }
44
78
  const reader = createEngineCapReader({
45
79
  name: 'store',
46
80
  project: projectStorePostureCapability,
@@ -70,58 +104,54 @@ export function storePersistence(reading) {
70
104
  const r = presentView(reading);
71
105
  if (r?.kind !== 'present')
72
106
  return 'unknown';
73
- let durable;
74
- try {
75
- durable = r.view.durable;
76
- }
77
- catch {
78
- return 'unknown';
79
- }
80
- return durable === true ? 'durable' : durable === false ? 'not_durable' : 'unknown';
107
+ return persistenceOf(durabilityOf(r.view, 'view'));
81
108
  }
82
- const STORE_NOT_DURABLE_HEAD = 'NOT durable';
83
- export function storePostureHeadWord(reading) {
84
- return storePersistence(reading) === 'not_durable' ? STORE_NOT_DURABLE_HEAD : undefined;
109
+ const notDurableHead = () => msg('notice.capability.storePosture.notDurableHead');
110
+ export function storePostureHeadWord(reading, lang) {
111
+ return storePersistence(reading) === 'not_durable' ? renderMessage(notDurableHead(), lang) : undefined;
85
112
  }
86
113
  const WORD_DETAIL_MAX = 40;
87
114
  const word = (v) => capForDisplay(v, WORD_DETAIL_MAX);
88
- export function storePostureDoctorDetail(reading) {
89
- const unobserved = 'store posture not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this deployment persists its state is unknown';
115
+ export function storePostureDoctorDetail(reading, lang) {
116
+ const say = (ref) => renderMessage(ref, lang);
117
+ const unobserved = msg('notice.capability.storePosture.unobserved');
90
118
  const r = presentView(reading);
91
119
  if (r?.kind === 'unreadable')
92
- return unreadableReadingDetail('store posture');
93
- if (r?.kind === 'not_reported') {
94
- return 'store posture not reported by this engine; this does not say whether this deployment persists its state';
95
- }
120
+ return unreadableReadingDetail(say(msg('notice.capability.subject.storePosture')), lang);
121
+ if (r?.kind === 'not_reported')
122
+ return say(msg('notice.capability.storePosture.notReported'));
96
123
  if (r?.kind !== 'present')
97
- return unobserved;
124
+ return say(unobserved);
98
125
  let declared;
99
126
  let backend;
100
- let durable;
101
127
  let reason;
102
128
  try {
103
129
  declared = r.view.declared;
104
130
  backend = r.view.backend;
105
- durable = r.view.durable;
106
131
  reason = r.view.degradedReason;
107
132
  }
108
133
  catch {
109
- return unobserved;
110
- }
111
- if (!nonEmpty(declared) || !nonEmpty(backend) || typeof durable !== 'boolean')
112
- return unobserved;
113
- const assembled = declared === backend ? `store ${word(backend)}` : `store declared ${word(declared)}, running on ${word(backend)}`;
114
- if (durable) {
115
- return `${assembled} — durable: this replica persists its state, so it survives a restart`;
134
+ return say(unobserved);
116
135
  }
117
- const lost = `${STORE_NOT_DURABLE_HEAD}: this replica holds its state in memory and loses it on restart`;
136
+ const durability = durabilityOf(r.view, 'view');
137
+ if (!nonEmpty(declared) || !nonEmpty(backend) || durability.kind === 'malformed')
138
+ return say(unobserved);
139
+ const assembled = declared === backend
140
+ ? msg('notice.capability.storePosture.assembled', { backend: word(backend) })
141
+ : msg('notice.capability.storePosture.assembledDeclared', { declared: word(declared), backend: word(backend) });
142
+ if (durability.kind === 'unrecognized')
143
+ return say(msg('notice.capability.storePosture.unrecognized', { assembled, word: word(durability.word) }));
144
+ const persistence = persistenceOf(durability);
145
+ if (persistence === 'durable')
146
+ return say(msg('notice.capability.storePosture.durable', { assembled }));
147
+ if (persistence === 'unknown')
148
+ return say(msg('notice.capability.storePosture.unknown', { assembled }));
149
+ const line = msg(durability.kind === 'legacy' ? 'notice.capability.storePosture.memory' : 'notice.capability.storePosture.ephemeral', { assembled, head: notDurableHead() });
118
150
  if (!nonEmpty(reason))
119
- return `${assembled} — ${lost}`;
120
- if (reason === 'db_unreachable') {
121
- return `${assembled} — ${lost} (degraded: the declared database could not be opened at start-up, so the engine fell back rather than refusing to start)`;
122
- }
123
- if (reason === 'local_unwritable') {
124
- return `${assembled} — ${lost} (degraded: the default local file store could not be created — a read-only or full data directory is one possible cause, and this client cannot tell which applies)`;
125
- }
126
- return `${assembled} — ${lost} (degraded for a reason this client does not know yet: "${word(reason)}")`;
151
+ return say(line);
152
+ if (reason === 'db_unreachable')
153
+ return say(msg('notice.capability.storePosture.degraded.dbUnreachable', { line }));
154
+ if (reason === 'local_unwritable')
155
+ return say(msg('notice.capability.storePosture.degraded.localUnwritable', { line }));
156
+ return say(msg('notice.capability.storePosture.degraded.other', { line, reason: word(reason) }));
127
157
  }
@@ -1,7 +1,7 @@
1
1
  import { hostLog } from '../host.js';
2
2
  import { isNotFoundFailure, resumeRetryLaterFromError } from '../wireErrorTriage.js';
3
3
  import { readThrownSlot, THROWN_SLOT_UNREADABLE } from '../wireFailureShape.js';
4
- import { RESUME_SESSION_NOT_FOUND } from '../engineErrorCodes.js';
4
+ import { RESUME_SESSION_NOT_FOUND, STEERING_NOT_RUNNING } from '../engineErrorCodes.js';
5
5
  import { displayUntrusted } from '../displayUntrusted.js';
6
6
  import { engineCapTrue } from '../engineCapsCache.js';
7
7
  import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, sendOnce, settleInjected } from '../engineWireFor.js';
@@ -63,7 +63,7 @@ export function classifySubagentResumeFailure(e) {
63
63
  }
64
64
  if (errorCode === 'steering.still_running')
65
65
  return { reason: 'still-running', detail: message };
66
- if (errorCode === 'steering.not_running')
66
+ if (errorCode === STEERING_NOT_RUNNING)
67
67
  return { reason: 'not-resumable', detail: message };
68
68
  if (errorCode === 'steering.ambiguous_target')
69
69
  return { reason: 'ambiguous-target', detail: message };
@@ -4,9 +4,10 @@ import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, settleInjected
4
4
  import { engineCapStateVia } from '../engineCapsCache.js';
5
5
  import { engineSessionParamFor } from '../engineSessionParam.js';
6
6
  import { resolveOwnerContext } from './engineSubagentResume.js';
7
- import { coerceOutput, parentToolCallIdOf, publishSubagentContentEvent, settleSubagentContent, } from '../subagentContentStore.js';
7
+ import { coerceOutput, parentToolCallIdOf, publishSubagentContentEvent, settleSubagentContent, subagentToolEndDenialParts, } 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
  }
@@ -142,6 +143,7 @@ export function tailEngineSubagent(taskId, opts) {
142
143
  ...(out !== undefined ? { output: out } : {}),
143
144
  ...(d.isError === true ? { isError: true } : {}),
144
145
  ...(readFrameTrueMark(d, 'notExecuted') ? { notExecuted: true } : {}),
146
+ ...subagentToolEndDenialParts(d),
145
147
  });
146
148
  }
147
149
  else if (t === 'task_settled') {
@@ -149,6 +151,11 @@ export function tailEngineSubagent(taskId, opts) {
149
151
  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
152
  break;
151
153
  }
154
+ else if (t === 'task_progress' || t === 'task_started') {
155
+ const card = parentToolCallIdOf(taskId);
156
+ if (card !== undefined && card !== taskId && typeof d.model === 'string')
157
+ publishEngineInlineTaskModel(card, d.model);
158
+ }
152
159
  }
153
160
  }
154
161
  catch (e) {
@@ -1,3 +1,4 @@
1
+ import { type CcToolDenialKind } from './gateVocabulary.js';
1
2
  export type SubagentContentItem = {
2
3
  kind: 'text';
3
4
  text: string;
@@ -12,6 +13,8 @@ export type SubagentContentItem = {
12
13
  output?: string;
13
14
  isError?: boolean;
14
15
  notExecuted?: true;
16
+ denialKind?: CcToolDenialKind;
17
+ refusalNoted?: true;
15
18
  } | {
16
19
  kind: 'echo';
17
20
  text: string;
@@ -33,7 +36,13 @@ export interface SubagentContentEvent {
33
36
  output?: string | undefined;
34
37
  isError?: boolean | undefined;
35
38
  notExecuted?: true | undefined;
39
+ denialKind?: CcToolDenialKind | undefined;
40
+ refusalNoted?: true | undefined;
36
41
  }
42
+ export declare function subagentToolEndDenialParts(frame: object): {
43
+ denialKind?: CcToolDenialKind;
44
+ refusalNoted?: true;
45
+ };
37
46
  export declare function coerceOutput(v: unknown): string | undefined;
38
47
  export declare function subagentContentEventOf(ev: {
39
48
  readonly type: string;
@@ -97,6 +106,8 @@ export type SubagentViewSlot = {
97
106
  output?: string;
98
107
  isError?: boolean;
99
108
  notExecuted?: true;
109
+ denialKind?: CcToolDenialKind;
110
+ refusalNoted?: true;
100
111
  };
101
112
  export declare function planSubagentViewSlots(taskId: string): SubagentViewSlot[];
102
113
  export declare function parentToolCallIdOf(taskId: string): string | undefined;
@@ -1,7 +1,18 @@
1
1
  import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
2
- import { readFrameTrueMark, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
2
+ import { guardedRead, readFrameTrueMark, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
3
3
  import { cycleSeqValue } from './cycleOrder.js';
4
4
  import { toolEndOutputText } from './toolResultContent.js';
5
+ import { ccToolDenialKindForToolEnd, gateOutcomeOf } from './gateOutcome.js';
6
+ import { isCcToolDenialKind } from './gateVocabulary.js';
7
+ import { refusalNotedOf } from './engineRowRef.js';
8
+ export function subagentToolEndDenialParts(frame) {
9
+ const denialKind = guardedRead(() => ccToolDenialKindForToolEnd(frame));
10
+ const gate = guardedRead(() => gateOutcomeOf(frame));
11
+ return {
12
+ ...(denialKind !== undefined ? { denialKind } : {}),
13
+ ...(refusalNotedOf(denialKind, gate) ? { refusalNoted: true } : {}),
14
+ };
15
+ }
5
16
  export function coerceOutput(v) {
6
17
  return toolEndOutputText(v);
7
18
  }
@@ -26,6 +37,7 @@ export function subagentContentEventOf(ev) {
26
37
  output: coerceOutput(sub.output),
27
38
  isError: sub.isError,
28
39
  notExecuted: readFrameTrueMark(sub, 'notExecuted') ? true : undefined,
40
+ ...(t === 'tool_end' ? subagentToolEndDenialParts(ev) : {}),
29
41
  };
30
42
  }
31
43
  const MAX_ITEMS_PER_TASK = 200;
@@ -633,16 +645,26 @@ export function publishSubagentContentEvent(ev) {
633
645
  const outBefore = it.output;
634
646
  const errBefore = it.isError;
635
647
  const notExecBefore = it.notExecuted;
648
+ const denialBefore = it.denialKind;
649
+ const notedBefore = it.refusalNoted;
636
650
  it.output = ev.output ?? '';
637
651
  it.isError = ev.isError === true;
638
652
  if (ev.notExecuted === true)
639
653
  it.notExecuted = true;
640
654
  else
641
655
  delete it.notExecuted;
656
+ if (isCcToolDenialKind(ev.denialKind))
657
+ it.denialKind = ev.denialKind;
658
+ else
659
+ delete it.denialKind;
660
+ if (ev.refusalNoted === true)
661
+ it.refusalNoted = true;
662
+ else
663
+ delete it.refusalNoted;
642
664
  const delta = itemBytesOf(it) - before;
643
665
  s.itemBytes[at] = (s.itemBytes[at] ?? 0) + delta;
644
666
  addBytes(s, delta);
645
- stampPhase(s, it.id, 'end', ev.eventId, it.output !== outBefore || it.isError !== errBefore || it.notExecuted !== notExecBefore);
667
+ stampPhase(s, it.id, 'end', ev.eventId, it.output !== outBefore || it.isError !== errBefore || it.notExecuted !== notExecBefore || it.denialKind !== denialBefore || it.refusalNoted !== notedBefore);
646
668
  break;
647
669
  }
648
670
  const synthId = id ?? `${ev.taskId}-tool-${s.items.length}`;
@@ -654,6 +676,8 @@ export function publishSubagentContentEvent(ev) {
654
676
  output: ev.output ?? '',
655
677
  isError: ev.isError === true,
656
678
  ...(ev.notExecuted === true ? { notExecuted: true } : {}),
679
+ ...(isCcToolDenialKind(ev.denialKind) ? { denialKind: ev.denialKind } : {}),
680
+ ...(ev.refusalNoted === true ? { refusalNoted: true } : {}),
657
681
  });
658
682
  recordCut(s);
659
683
  stampPhase(s, synthId, 'end', ev.eventId, true);
@@ -698,6 +722,8 @@ export function planSubagentViewSlots(taskId) {
698
722
  ...(it.output !== undefined ? { output: it.output } : {}),
699
723
  ...(it.isError === true ? { isError: true } : {}),
700
724
  ...(it.notExecuted === true ? { notExecuted: true } : {}),
725
+ ...(it.denialKind !== undefined ? { denialKind: it.denialKind } : {}),
726
+ ...(it.refusalNoted === true ? { refusalNoted: true } : {}),
701
727
  });
702
728
  }
703
729
  else if (it.kind === 'text') {
@@ -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);
@@ -109,6 +109,7 @@ export interface ToolResultRowFlags {
109
109
  readonly interruptedNeverStarted: boolean;
110
110
  readonly approvalWindowExpired: boolean;
111
111
  readonly denialLimitWindowExpired: boolean;
112
+ readonly refusalNoted: boolean;
112
113
  }
113
114
  export declare function toolResultRowFlagsOf(row: object | null | undefined): ToolResultRowFlags;
114
115
  export type ToolKillCause = NonNullable<ReturnType<typeof toolKillCauseAcrossEngineIdentity>> | 'memory_pressure';
@@ -8,6 +8,7 @@ import { guardedRead, ownDataField, ownSnapshotWhenTyped, readFrameTrueMark, rea
8
8
  import { displayUntrusted } from './displayUntrusted.js';
9
9
  import { isCoreSemaTaskTerminal } from './terminalStatuses.js';
10
10
  import { CORE_ENGINE_CARD_TYPES } from './generated/engineFactTables.js';
11
+ import { SEMA_REFUSAL_NOTED_KEY } from './engineRowRef.js';
11
12
  export const STRUCTURED_DETAIL_TYPES = frozenSet(CORE_ENGINE_CARD_TYPES);
12
13
  const LEGACY_STRUCTURED_DETAIL_TYPE = 'task-output';
13
14
  export function structuredDetailType(structured) {
@@ -318,6 +319,7 @@ export function toolResultRowFlagsOf(row) {
318
319
  interruptedNeverStarted: readFrameTrueMark(row, '_sema_interrupted_never_started'),
319
320
  approvalWindowExpired: readFrameTrueMark(row, '_sema_approval_window_expired'),
320
321
  denialLimitWindowExpired: readFrameTrueMark(row, '_sema_denial_limit_window_expired'),
322
+ refusalNoted: readFrameTrueMark(row, SEMA_REFUSAL_NOTED_KEY),
321
323
  });
322
324
  }
323
325
  const TOOL_KILL_TEXT = {
@@ -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
+ }