@sema-agent/client-core 0.98.0 → 0.100.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 +376 -0
  2. package/README.md +61 -35
  3. package/dist/adapt/arms.js +120 -55
  4. package/dist/adapt/panelTasks.d.ts +3 -2
  5. package/dist/adapt/panelTasks.js +34 -11
  6. package/dist/adapt/textStream.d.ts +1 -1
  7. package/dist/adapt/textStream.js +51 -28
  8. package/dist/adapter/activeRunSelfHeal.d.ts +11 -3
  9. package/dist/adapter/activeRunSelfHeal.js +210 -294
  10. package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
  11. package/dist/adapter/downstream/eventToSdkMessage.js +176 -12
  12. package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
  13. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  14. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  15. package/dist/adapter/runStream.js +82 -40
  16. package/dist/adapter/types.d.ts +2 -0
  17. package/dist/adapter/types.js +2 -1
  18. package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
  19. package/dist/agentsWireCaps.d.ts +7 -3
  20. package/dist/agentsWireCaps.js +133 -39
  21. package/dist/cloudConfigWireCaps.d.ts +10 -1
  22. package/dist/cloudConfigWireCaps.js +181 -65
  23. package/dist/compactionStatus.d.ts +34 -0
  24. package/dist/compactionStatus.js +196 -0
  25. package/dist/controlRouter.d.ts +4 -2
  26. package/dist/controlRouter.js +39 -51
  27. package/dist/coreValuePorts.d.ts +26 -1
  28. package/dist/coreValuePorts.js +10 -0
  29. package/dist/corruptSessionCopy.d.ts +2 -1
  30. package/dist/corruptSessionCopy.js +3 -3
  31. package/dist/decideFailureNote.js +3 -3
  32. package/dist/declared/supportFloorDeclared.js +1 -1
  33. package/dist/detachWire.js +3 -2
  34. package/dist/devicesFace.d.ts +34 -0
  35. package/dist/devicesFace.js +90 -0
  36. package/dist/displayUntrusted.d.ts +1 -0
  37. package/dist/displayUntrusted.js +153 -5
  38. package/dist/engineAgentPanelStore.d.ts +14 -4
  39. package/dist/engineAgentPanelStore.js +14 -0
  40. package/dist/engineCapReader.d.ts +2 -1
  41. package/dist/engineCapReader.js +7 -5
  42. package/dist/engineCapsCache.d.ts +8 -0
  43. package/dist/engineCapsCache.js +59 -6
  44. package/dist/engineCapsChange.d.ts +2 -0
  45. package/dist/engineCapsChange.js +9 -0
  46. package/dist/engineErrorCodes.d.ts +11 -2
  47. package/dist/engineErrorCodes.js +11 -12
  48. package/dist/engineRowRef.js +4 -0
  49. package/dist/engineSessionMessages.d.ts +1 -0
  50. package/dist/engineSessionMessages.js +7 -1
  51. package/dist/engineTargetSeat.d.ts +27 -0
  52. package/dist/engineTargetSeat.js +199 -0
  53. package/dist/engineToolDirectory.d.ts +3 -0
  54. package/dist/engineToolDirectory.js +4 -2
  55. package/dist/engineWireFor.js +3 -8
  56. package/dist/env/uiLanguage.d.ts +2 -1
  57. package/dist/env/uiLanguage.js +3 -2
  58. package/dist/eventLeg.d.ts +7 -5
  59. package/dist/eventLeg.js +68 -49
  60. package/dist/fileHistoryCaptureCapability.d.ts +2 -4
  61. package/dist/fileHistoryCaptureCapability.js +12 -22
  62. package/dist/fileHistoryLegNotice.d.ts +2 -1
  63. package/dist/fileHistoryLegNotice.js +3 -3
  64. package/dist/frameBinding.d.ts +3 -0
  65. package/dist/frameBinding.js +12 -0
  66. package/dist/gateOutcome.js +16 -2
  67. package/dist/gateVocabulary.d.ts +8 -7
  68. package/dist/gateVocabulary.js +71 -73
  69. package/dist/generated/engineFactTables.d.ts +18 -1
  70. package/dist/generated/engineFactTables.js +56 -0
  71. package/dist/generated/engineNoticeTables.js +12 -3
  72. package/dist/generated/settingsSchemaFns.d.ts +18 -0
  73. package/dist/generated/settingsSchemaFns.js +70 -0
  74. package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
  75. package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
  76. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
  77. package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
  78. package/dist/generated/settingsSchemaTables.d.ts +2 -0
  79. package/dist/generated/settingsSchemaTables.js +9 -0
  80. package/dist/generated/settingsSchemaTypes.d.ts +133 -0
  81. package/dist/generated/settingsSchemaTypes.js +1 -0
  82. package/dist/hitl/allowRuleShadow.d.ts +2 -1
  83. package/dist/hitl/allowRuleShadow.js +18 -15
  84. package/dist/hitl/approvalCardReasons.d.ts +3 -2
  85. package/dist/hitl/approvalCardReasons.js +29 -30
  86. package/dist/hitl/approvalHolds.d.ts +17 -0
  87. package/dist/hitl/approvalHolds.js +114 -29
  88. package/dist/hitl/askClassPair.d.ts +2 -2
  89. package/dist/hitl/askClassPair.js +16 -0
  90. package/dist/hitl/askGateWire.js +2 -1
  91. package/dist/hitl/askSettlementLine.d.ts +14 -4
  92. package/dist/hitl/askSettlementLine.js +67 -47
  93. package/dist/hitl/attributionLadder.d.ts +23 -0
  94. package/dist/hitl/attributionLadder.js +67 -0
  95. package/dist/hitl/automaticAnswerVerdict.d.ts +2 -1
  96. package/dist/hitl/automaticAnswerVerdict.js +8 -8
  97. package/dist/hitl/crashConvergedNotice.d.ts +5 -4
  98. package/dist/hitl/crashConvergedNotice.js +28 -41
  99. package/dist/hitl/decisionLedger.d.ts +55 -0
  100. package/dist/hitl/decisionLedger.js +156 -0
  101. package/dist/hitl/decisionRefusal.d.ts +1 -0
  102. package/dist/hitl/decisionRefusal.js +1 -1
  103. package/dist/hitl/decisionSend.d.ts +113 -0
  104. package/dist/hitl/decisionSend.js +579 -0
  105. package/dist/hitl/frameRouter.js +7 -8
  106. package/dist/hitl/hitlBridge.d.ts +1 -0
  107. package/dist/hitl/hitlBridge.js +21 -9
  108. package/dist/hitl/persistedRulesWire.d.ts +6 -5
  109. package/dist/hitl/persistedRulesWire.js +55 -73
  110. package/dist/hitl/planReviewWire.d.ts +5 -0
  111. package/dist/hitl/planReviewWire.js +53 -76
  112. package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
  113. package/dist/hitl/sessionPolicyDeliverable.js +49 -9
  114. package/dist/hitl/sessionPolicyWire.d.ts +2 -1
  115. package/dist/hitl/sessionPolicyWire.js +23 -24
  116. package/dist/hitl/suspendedReopen.d.ts +2 -1
  117. package/dist/hitl/suspendedReopen.js +7 -10
  118. package/dist/hitl/toolApprovalWire.d.ts +17 -5
  119. package/dist/hitl/toolApprovalWire.js +120 -147
  120. package/dist/hooksWireCaps.js +145 -56
  121. package/dist/host.d.ts +21 -0
  122. package/dist/hostTurnSettings.d.ts +110 -0
  123. package/dist/hostTurnSettings.js +692 -0
  124. package/dist/humanTurn.d.ts +15 -1
  125. package/dist/humanTurn.js +100 -12
  126. package/dist/humanTurnRead.d.ts +2 -0
  127. package/dist/humanTurnRead.js +22 -1
  128. package/dist/i18n/messages.en.d.ts +713 -0
  129. package/dist/i18n/messages.en.js +713 -0
  130. package/dist/i18n/messages.zh.js +854 -1
  131. package/dist/index.d.ts +45 -13
  132. package/dist/index.js +27 -9
  133. package/dist/inheritEnvCapability.d.ts +4 -2
  134. package/dist/inheritEnvCapability.js +12 -5
  135. package/dist/inheritEnvWire.d.ts +3 -2
  136. package/dist/inheritEnvWire.js +14 -16
  137. package/dist/injectionLaneNotice.d.ts +5 -0
  138. package/dist/injectionLaneNotice.js +46 -0
  139. package/dist/interactiveHalt.js +17 -6
  140. package/dist/liveModelCatalog.d.ts +2 -1
  141. package/dist/liveModelCatalog.js +14 -8
  142. package/dist/mcpPanel.d.ts +10 -1
  143. package/dist/mcpPanel.js +42 -13
  144. package/dist/mcpProbeCapability.d.ts +2 -1
  145. package/dist/mcpProbeCapability.js +7 -11
  146. package/dist/mcpProbeWire.d.ts +3 -1
  147. package/dist/mcpProbeWire.js +5 -0
  148. package/dist/mcpReconnect.js +2 -2
  149. package/dist/mcpToolToggles.d.ts +6 -0
  150. package/dist/mcpToolToggles.js +46 -0
  151. package/dist/mcpWireCaps.d.ts +43 -3
  152. package/dist/mcpWireCaps.js +194 -59
  153. package/dist/mcpWireTransport.d.ts +4 -0
  154. package/dist/mcpWireTransport.js +18 -0
  155. package/dist/memoryEntriesWire.js +5 -10
  156. package/dist/model/onboardJudgement.d.ts +93 -0
  157. package/dist/model/onboardJudgement.js +415 -0
  158. package/dist/model/tierVocabulary.d.ts +2 -0
  159. package/dist/model/tierVocabulary.js +17 -5
  160. package/dist/modelCapabilityProbe.d.ts +65 -0
  161. package/dist/modelCapabilityProbe.js +601 -0
  162. package/dist/modelIdentityResolvability.d.ts +5 -0
  163. package/dist/modelIdentityResolvability.js +49 -22
  164. package/dist/ownKey.d.ts +33 -0
  165. package/dist/ownKey.js +109 -0
  166. package/dist/panelReduce.d.ts +11 -1
  167. package/dist/panelReduce.js +95 -16
  168. package/dist/panelStateHolder.d.ts +1 -0
  169. package/dist/panelStateHolder.js +10 -1
  170. package/dist/permissionRuleIssue.d.ts +2 -1
  171. package/dist/permissionRuleIssue.js +23 -20
  172. package/dist/permissionRulesWriteCapability.d.ts +2 -1
  173. package/dist/permissionRulesWriteCapability.js +8 -6
  174. package/dist/pluginHooksWire.d.ts +46 -3
  175. package/dist/pluginHooksWire.js +220 -12
  176. package/dist/prepareRefusal.d.ts +8 -0
  177. package/dist/prepareRefusal.js +72 -0
  178. package/dist/printModelStatus.d.ts +63 -0
  179. package/dist/printModelStatus.js +155 -0
  180. package/dist/request/taskRequest.d.ts +16 -10
  181. package/dist/request/taskRequest.js +195 -58
  182. package/dist/resumeRefusalCopy.d.ts +14 -9
  183. package/dist/resumeRefusalCopy.js +89 -144
  184. package/dist/retryStatus.d.ts +55 -0
  185. package/dist/retryStatus.js +155 -19
  186. package/dist/ruleRefusalSentence.d.ts +4 -0
  187. package/dist/ruleRefusalSentence.js +58 -0
  188. package/dist/sandboxWire.d.ts +2 -1
  189. package/dist/sandboxWire.js +55 -21
  190. package/dist/scenarioRefusal.d.ts +10 -0
  191. package/dist/scenarioRefusal.js +111 -0
  192. package/dist/seam.d.ts +48 -9
  193. package/dist/seam.js +54 -5
  194. package/dist/seatContract.d.ts +272 -14
  195. package/dist/seatContract.js +711 -23
  196. package/dist/selfOrchestrationDenial.js +7 -19
  197. package/dist/sessionIdSegment.d.ts +6 -0
  198. package/dist/sessionIdSegment.js +24 -0
  199. package/dist/sessionMemoryStatus.js +6 -11
  200. package/dist/sessionPolicyCapability.d.ts +2 -1
  201. package/dist/sessionPolicyCapability.js +8 -6
  202. package/dist/settingsCredentialKeyPaths.d.ts +8 -0
  203. package/dist/settingsCredentialKeyPaths.js +219 -0
  204. package/dist/settingsHooksSalvage.d.ts +44 -0
  205. package/dist/settingsHooksSalvage.js +211 -0
  206. package/dist/skillToolListsCapability.d.ts +2 -1
  207. package/dist/skillToolListsCapability.js +8 -6
  208. package/dist/skillsWireCaps.d.ts +0 -3
  209. package/dist/skillsWireCaps.js +1 -2
  210. package/dist/sseIdleTriage.js +3 -2
  211. package/dist/subagent/engineSubagentResume.js +15 -9
  212. package/dist/subagentContentStore.d.ts +4 -0
  213. package/dist/subagentContentStore.js +157 -27
  214. package/dist/toolHistoryMismatch.js +3 -2
  215. package/dist/toolRoster.d.ts +3 -2
  216. package/dist/toolRoster.js +21 -20
  217. package/dist/toolsFlagNames.d.ts +8 -0
  218. package/dist/toolsFlagNames.js +110 -0
  219. package/dist/turnFailurePresentation.d.ts +12 -1
  220. package/dist/turnFailurePresentation.js +220 -15
  221. package/dist/usageAnalytics.js +6 -11
  222. package/dist/userAttachmentEnvelope.d.ts +1 -0
  223. package/dist/userAttachmentEnvelope.js +37 -7
  224. package/dist/userAttachmentSeat.d.ts +21 -0
  225. package/dist/userAttachmentSeat.js +75 -0
  226. package/dist/userInvokedSkillsCapability.d.ts +11 -1
  227. package/dist/userInvokedSkillsCapability.js +31 -7
  228. package/dist/wireErrorTriage.d.ts +7 -0
  229. package/dist/wireErrorTriage.js +23 -1
  230. package/dist/wireFailureShape.js +2 -1
  231. package/dist/wireRefusalCopy.d.ts +62 -16
  232. package/dist/wireRefusalCopy.js +307 -148
  233. package/dist/workflowsJournalCapability.d.ts +2 -1
  234. package/dist/workflowsJournalCapability.js +8 -6
  235. package/docs/INTEGRATION-CLIENTS.md +951 -42
  236. package/package.json +5 -5
@@ -2,6 +2,10 @@ import { isNewEngineAgentPanelCycle, isStaleEngineAgentPanelEnd, MAX_HELD_WIRE_T
2
2
  import type { EngineAgentPanelCycleIdentity } from './cycleOrder.js';
3
3
  export { isNewEngineAgentPanelCycle, isStaleEngineAgentPanelEnd, MAX_HELD_WIRE_TICK_BEATS };
4
4
  export type { EngineAgentPanelCycleIdentity, EnginePanelTaskIdentity };
5
+ type PanelFinalityFlags = {
6
+ totalTokens?: true;
7
+ toolUses?: true;
8
+ };
5
9
  export type EngineAgentPanelEvent = {
6
10
  kind: 'tick';
7
11
  taskId: string;
@@ -16,6 +20,8 @@ export type EngineAgentPanelEvent = {
16
20
  cardBound?: true;
17
21
  wireTaskId?: string;
18
22
  taskIdOrigin?: 'fleet-row' | 'wire';
23
+ cycleSeq?: number;
24
+ final?: PanelFinalityFlags;
19
25
  } | {
20
26
  kind: 'fleet-row';
21
27
  taskId: string;
@@ -33,10 +39,7 @@ export type EngineAgentPanelEvent = {
33
39
  parentToolCallId?: string;
34
40
  wireTaskId?: string;
35
41
  taskIdOrigin?: 'wire';
36
- final?: {
37
- totalTokens?: true;
38
- toolUses?: true;
39
- };
42
+ final?: PanelFinalityFlags;
40
43
  } | {
41
44
  kind: 'end';
42
45
  taskId: string;
@@ -46,6 +49,7 @@ export type EngineAgentPanelEvent = {
46
49
  report?: string;
47
50
  wireTaskId?: string;
48
51
  taskIdOrigin?: 'fleet-row' | 'wire';
52
+ sessionId?: string;
49
53
  } | {
50
54
  kind: 'sweep';
51
55
  };
@@ -58,6 +62,12 @@ export declare function clearEnginePanelTaskResidentByWire(wireTaskId: string, c
58
62
  export declare function __resetEngineAgentPanelIdentityForTests(): void;
59
63
  export declare function __resetEnginePanelStateForTests(): void;
60
64
  export declare function publishEngineAgentPanelEvent(ev: EngineAgentPanelEvent): void;
65
+ export interface EngineAgentPanelStart {
66
+ taskId: string;
67
+ startedAt?: number;
68
+ workflowRunId?: string;
69
+ }
70
+ export declare function publishEngineAgentPanelStart(ev: EngineAgentPanelStart): void;
61
71
  export declare function subscribeEnginePanelRows(fn: (delta: PanelDelta) => void): () => void;
62
72
  export interface EngineAgentPanelAbsence {
63
73
  taskId: string;
@@ -1,4 +1,5 @@
1
1
  import { isNewEngineAgentPanelCycle, isStaleEngineAgentPanelEnd, panelIsResident, panelResolveTaskId, MAX_HELD_WIRE_TICK_BEATS, } from './panelReduce.js';
2
+ import { guardedRead, readOwnDataFiniteNumber, readOwnDataNonEmptyString } from './ownKey.js';
2
3
  import { clearEnginePanelIdentity, panelStateNow, stepEnginePanel, subscribeRows, resetEnginePanelHolder } from './panelStateHolder.js';
3
4
  export { isNewEngineAgentPanelCycle, isStaleEngineAgentPanelEnd, MAX_HELD_WIRE_TICK_BEATS };
4
5
  const _panelToolUsesLanePins = [true, true];
@@ -31,6 +32,19 @@ export function __resetEnginePanelStateForTests() {
31
32
  export function publishEngineAgentPanelEvent(ev) {
32
33
  stepEnginePanel(ev);
33
34
  }
35
+ export function publishEngineAgentPanelStart(ev) {
36
+ const taskId = guardedRead(() => readOwnDataNonEmptyString(ev, 'taskId'));
37
+ if (taskId === undefined)
38
+ return;
39
+ const startedAt = guardedRead(() => readOwnDataFiniteNumber(ev, 'startedAt'));
40
+ const workflowRunId = guardedRead(() => readOwnDataNonEmptyString(ev, 'workflowRunId'));
41
+ stepEnginePanel({
42
+ kind: 'start',
43
+ taskId,
44
+ ...(startedAt !== undefined ? { startedAt } : {}),
45
+ ...(workflowRunId !== undefined ? { workflowRunId } : {}),
46
+ });
47
+ }
34
48
  export function subscribeEnginePanelRows(fn) {
35
49
  return subscribeRows(fn);
36
50
  }
@@ -1,3 +1,4 @@
1
+ import type { UiLanguage } from './env/uiLanguage.js';
1
2
  interface EngineCapReaderSpec<R> {
2
3
  name: string;
3
4
  project: (caps: unknown) => R | undefined;
@@ -12,7 +13,7 @@ interface EngineCapReader<R> {
12
13
  observed(baseUrl: string | undefined): R | ReaderUnobserved | ReaderUnreadable;
13
14
  }
14
15
  export declare function createEngineCapReader<R>(spec: EngineCapReaderSpec<R>): EngineCapReader<R>;
15
- export declare function unreadableReadingDetail(subject?: string): string;
16
+ export declare function unreadableReadingDetail(subject?: string, lang?: UiLanguage): string;
16
17
  export type StoreSeat = {
17
18
  kind: 'object';
18
19
  store: object;
@@ -1,7 +1,10 @@
1
1
  import { engineCapsLandingOf } from './engineCapsCache.js';
2
+ import { msg, renderMessage } from './i18n/translate.js';
2
3
  import { ownDataField } from './ownKey.js';
3
- const unobserved = () => ({ kind: 'unobserved' });
4
- const unreadable = () => ({ kind: 'unreadable' });
4
+ const UNOBSERVED = Object.freeze({ kind: 'unobserved' });
5
+ const UNREADABLE = Object.freeze({ kind: 'unreadable' });
6
+ const unobserved = () => UNOBSERVED;
7
+ const unreadable = () => UNREADABLE;
5
8
  function freezeReading(reading, seen = new Set()) {
6
9
  if (reading === null || typeof reading !== 'object' || seen.has(reading))
7
10
  return reading;
@@ -49,9 +52,8 @@ export function createEngineCapReader(spec) {
49
52
  },
50
53
  };
51
54
  }
52
- export function unreadableReadingDetail(subject) {
53
- const head = typeof subject === 'string' && subject.length > 0 ? `${subject} could not be read` : 'could not be read';
54
- return `${head} — the engine's /v1/capabilities report arrived, but this entry is not in a form this client reads, so it is shown as unknown, not as absent; probing the same engine again will not change it while the engine reports it this way`;
55
+ export function unreadableReadingDetail(subject, lang) {
56
+ return renderMessage(typeof subject === 'string' && subject.length > 0 ? msg('notice.capability.unreadable.subject', { subject }) : msg('notice.capability.unreadable.bare'), lang);
55
57
  }
56
58
  export function storeSeatOf(caps) {
57
59
  try {
@@ -6,6 +6,11 @@ export type EngineCapsLanding = {
6
6
  } | {
7
7
  readonly kind: 'unreadable';
8
8
  };
9
+ export interface EngineCapsChange {
10
+ readonly kind: 'landed' | 'invalidated' | 'replaced';
11
+ readonly baseUrl: string;
12
+ readonly generation: number;
13
+ }
9
14
  export declare function engineCapsGeneration(baseUrl: string): number;
10
15
  export declare function engineCapsLandingOf(baseUrl: string | undefined): EngineCapsLanding;
11
16
  export declare function kickEngineCapsProbe(baseUrl: string, probe: () => Promise<unknown>): void;
@@ -42,6 +47,9 @@ export declare function engineCapString(baseUrl: string | undefined, key: string
42
47
  export declare function invalidateEngineCaps(baseUrl: string | undefined, probe?: () => Promise<unknown>): void;
43
48
  export type EngineCapsProbeRotation = 'started' | 'registered' | 'ignored';
44
49
  export declare function rotateEngineCapsProbe(baseUrl: string | undefined, probe: () => Promise<unknown>): EngineCapsProbeRotation;
50
+ export type EngineCapsReprobe = 'reprobed' | 'no_probe' | 'ignored';
51
+ export declare function invalidateEngineCapsAndReprobe(baseUrl: string | undefined): EngineCapsReprobe;
52
+ export declare function registerCapsChangeDelivery(baseUrl: string | undefined, deliver: (change: EngineCapsChange) => void): () => void;
45
53
  export declare function refreshEngineCapsOnce(baseUrl: string | undefined, opts?: {
46
54
  withinMs?: number;
47
55
  }): Promise<'settled' | 'timed_out' | 'no_probe'>;
@@ -4,11 +4,11 @@ const NOT_LANDED = Object.freeze({ kind: 'not_landed' });
4
4
  const capsByBase = new Map();
5
5
  const inFlight = new Set();
6
6
  const settleByBase = new Map();
7
- const genByBase = new Map();
7
+ const stateByBase = new Map();
8
8
  const probeByBase = new Map();
9
9
  const refreshByBase = new Map();
10
10
  function capsGeneration(baseUrl) {
11
- return genByBase.get(baseUrl) ?? 0;
11
+ return stateByBase.get(baseUrl)?.gen ?? 0;
12
12
  }
13
13
  export function engineCapsGeneration(baseUrl) {
14
14
  return capsGeneration(baseUrl);
@@ -169,6 +169,7 @@ function landCapsSnapshot(baseUrl, gen, caps) {
169
169
  ? Object.freeze({ kind: 'unreadable' })
170
170
  : Object.freeze({ kind: 'landed', caps: snapshot });
171
171
  capsByBase.set(baseUrl, record);
172
+ publishCapsChange(baseUrl, 'landed');
172
173
  return 'landed';
173
174
  }
174
175
  function snapshotCapsBody(body) {
@@ -355,18 +356,25 @@ export function invalidateEngineCaps(baseUrl, probe) {
355
356
  return;
356
357
  if (probe === undefined)
357
358
  probeByBase.delete(baseUrl);
358
- const hadState = capsByBase.has(baseUrl) ||
359
+ const hadLanding = capsByBase.has(baseUrl);
360
+ const hadState = hadLanding ||
359
361
  inFlight.has(baseUrl) ||
360
362
  settleByBase.has(baseUrl) ||
361
- genByBase.has(baseUrl);
363
+ stateByBase.get(baseUrl)?.gen !== undefined;
362
364
  if (hadState) {
363
- genByBase.set(baseUrl, capsGeneration(baseUrl) + 1);
365
+ const st = stateByBase.get(baseUrl);
366
+ if (st === undefined)
367
+ stateByBase.set(baseUrl, { gen: 1, subscribers: new Set() });
368
+ else
369
+ st.gen = capsGeneration(baseUrl) + 1;
364
370
  capsByBase.delete(baseUrl);
365
371
  inFlight.delete(baseUrl);
366
372
  settleByBase.get(baseUrl)?.wake();
367
373
  }
368
374
  if (probe !== undefined)
369
375
  kickEngineCapsProbe(baseUrl, probe);
376
+ if (hadLanding)
377
+ publishCapsChange(baseUrl, probe !== undefined ? 'replaced' : 'invalidated');
370
378
  }
371
379
  export function rotateEngineCapsProbe(baseUrl, probe) {
372
380
  try {
@@ -381,6 +389,51 @@ export function rotateEngineCapsProbe(baseUrl, probe) {
381
389
  return 'ignored';
382
390
  }
383
391
  }
392
+ export function invalidateEngineCapsAndReprobe(baseUrl) {
393
+ try {
394
+ if (typeof baseUrl !== 'string' || baseUrl === '')
395
+ return 'ignored';
396
+ const probe = probeByBase.get(baseUrl);
397
+ if (probe === undefined)
398
+ return 'no_probe';
399
+ invalidateEngineCaps(baseUrl, probe);
400
+ return 'reprobed';
401
+ }
402
+ catch {
403
+ return 'ignored';
404
+ }
405
+ }
406
+ export function registerCapsChangeDelivery(baseUrl, deliver) {
407
+ if (typeof baseUrl !== 'string' || baseUrl === '' || typeof deliver !== 'function')
408
+ return () => { };
409
+ let st = stateByBase.get(baseUrl);
410
+ if (st === undefined) {
411
+ st = { gen: undefined, subscribers: new Set() };
412
+ stateByBase.set(baseUrl, st);
413
+ }
414
+ const sub = { deliver, live: true };
415
+ const owner = st;
416
+ owner.subscribers.add(sub);
417
+ return () => {
418
+ if (!sub.live)
419
+ return;
420
+ sub.live = false;
421
+ owner.subscribers.delete(sub);
422
+ if (owner.subscribers.size === 0 && owner.gen === undefined && stateByBase.get(baseUrl) === owner)
423
+ stateByBase.delete(baseUrl);
424
+ };
425
+ }
426
+ function publishCapsChange(baseUrl, kind) {
427
+ const subs = stateByBase.get(baseUrl)?.subscribers;
428
+ if (subs === undefined || subs.size === 0)
429
+ return;
430
+ const change = Object.freeze({ kind, baseUrl, generation: capsGeneration(baseUrl) });
431
+ for (const sub of [...subs]) {
432
+ if (!sub.live)
433
+ continue;
434
+ sub.deliver(change);
435
+ }
436
+ }
384
437
  export async function refreshEngineCapsOnce(baseUrl, opts) {
385
438
  try {
386
439
  if (typeof baseUrl !== 'string' || baseUrl === '')
@@ -454,7 +507,7 @@ export function __resetEngineCapsCacheForTests() {
454
507
  capsByBase.clear();
455
508
  inFlight.clear();
456
509
  settleByBase.clear();
457
- genByBase.clear();
510
+ stateByBase.clear();
458
511
  probeByBase.clear();
459
512
  refreshByBase.clear();
460
513
  }
@@ -0,0 +1,2 @@
1
+ import { type EngineCapsChange } from './engineCapsCache.js';
2
+ export declare function onEngineCapsChange(baseUrl: string | undefined, listener: (change: EngineCapsChange) => void): () => void;
@@ -0,0 +1,9 @@
1
+ import { registerCapsChangeDelivery } from './engineCapsCache.js';
2
+ import { invokeHostCallbackIsolated } from './hostCallbackGuard.js';
3
+ export function onEngineCapsChange(baseUrl, listener) {
4
+ if (typeof listener !== 'function')
5
+ return () => { };
6
+ return registerCapsChangeDelivery(baseUrl, (change) => {
7
+ invokeHostCallbackIsolated('engineCapsChange: listener', () => listener(change));
8
+ });
9
+ }
@@ -16,8 +16,9 @@ export declare const CONFIG_MEMORY_PROJECT_KEY_SPELLING = "config.memory_project
16
16
  export declare const CONFIG_MEMORY_WRITE_SCOPE_MISMATCH = "config.memory_write_scope_mismatch";
17
17
  export declare const CONFIG_LEGACY_TOOL_NAME = "config.legacy_tool_name";
18
18
  export declare const CONFIG_IMAGE_INVALID = "config.image_invalid";
19
- export declare function imageInvalidFailureNoteOf(code: string | undefined): string | undefined;
20
19
  export declare const CONFIG_AGENT_UNKNOWN_TOOL = "config.agent.unknown_tool";
20
+ export declare const CONFIG_TOOL_FACE_CONTROL = "config.tool_face_control";
21
+ export declare const PROMPT_ASSEMBLED_ROLE_REFUSED = "prompt.assembled_role_refused";
21
22
  export declare const CONFIG_OUTPUT_STYLE_INVALID = "config.output_style_invalid";
22
23
  export declare const RULES_INERT_ENTRY = "rules.inert_entry";
23
24
  export declare const RULES_INVALID_SHAPE = "rules.invalid_shape";
@@ -35,6 +36,9 @@ export declare const TOOL_END_INTERRUPTED_OUTCOME_UNKNOWN = "interrupted_outcome
35
36
  export declare function isInterruptedToolEndCode(code: string | undefined): boolean;
36
37
  export declare const CAPABILITY_SELF_ORCHESTRATION_REQUIRED = "capability.self_orchestration_required";
37
38
  export declare const CAPABILITY_IMAGE_INDEX_REQUIRED = "capability.image_index_required";
39
+ export declare const CAPABILITY_K8S_LANE_REQUIRED = "capability.k8s_lane_required";
40
+ export declare const NOT_FOUND_IMAGE = "not_found.image";
41
+ export declare const REQUEST_PRECONDITION_UNMET = "request.precondition_unmet";
38
42
  export declare const GATE_PARKED_ERROR_CODE = "gate.parked";
39
43
  export declare const ACTIVE_RUN_BUSY_ERROR_CODE = "conflict.session_active_run";
40
44
  export declare const CONFLICT_IDEMPOTENCY_PAYLOAD_MISMATCH = "conflict.idempotency_payload_mismatch";
@@ -58,7 +62,6 @@ export declare const STOP_PARK_RESUME_WON = "stop.park_resume_won";
58
62
  export declare const STOP_NOT_LANDED = "stop.not_landed";
59
63
  export declare const STOP_NOT_LOCAL = "stop.not_local";
60
64
  export declare const STOP_PARKED = "stop.parked";
61
- export declare const STOP_CONFLICT_CODES: readonly ["stop.park_arbiter_unreachable", "stop.park_resume_won", "stop.not_landed", "stop.not_local", "stop.parked"];
62
65
  export declare const OUTPUT_INVALID = "output.invalid";
63
66
  export declare const RESUME_POLICY_FOLD_CODE = "resume_blocked_by_policy";
64
67
  export declare const STATUS_DERIVED_COARSE_CODES: readonly ["request.rejected", "auth.unauthorized", "auth.forbidden", "not_found.resource", "request.payload_too_large", "request.unprocessable", "limit.rate_exceeded", "capability.unavailable", "internal.error"];
@@ -111,6 +114,10 @@ export declare const DECIDE_ALREADY_RESOLVED_CODES: readonly string[];
111
114
  export declare function isDecideAlreadyResolvedCode(code: string | null | undefined): boolean;
112
115
  export declare const ASK_GONE = "gone.ask";
113
116
  export declare const ASK_GONE_PARKED = "gone.ask_parked";
117
+ export declare const ASK_NOT_FOUND = "not_found.ask";
118
+ export declare const ASK_IDEMPOTENCY_CONFLICT = "conflict.ask_idempotency";
119
+ export declare const ASK_PARKING_RETRY = "parking.ask_retry";
120
+ export declare const ASK_DECISION_LANE_DISABLED = "feature.approval_ask_disabled";
114
121
  export declare const ASK_GONE_DENIED_WORD: "routing_failure_fail_closed";
115
122
  export declare const RUN_DEATH_VERDICT_CODES: readonly ["run.stalled", "run.orphaned", "approval.expired"];
116
123
  export type { RunDeathVerdictCode };
@@ -128,6 +135,8 @@ export declare function isWorkflowParkRefusalCode(code: unknown): boolean;
128
135
  export declare const DRAINING_ERROR_CODE = "draining";
129
136
  export declare const SCENARIO_NOT_ALLOWED_ERROR_CODE = "scenario_not_allowed";
130
137
  export declare const SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE = "state.scenario_assignment_unknown";
138
+ export declare const USER_ATTACHMENT_INVALID_ERROR_CODE = "config.user_attachment_invalid";
139
+ export declare const USER_INVOKED_SKILL_UNKNOWN_ERROR_CODE = "config.user_invoked_skill_unknown";
131
140
  export declare const STREAM_MAX_DURATION = "STREAM_MAX_DURATION";
132
141
  export type ModelFallbackReason = 'inherit_no_tier_binding';
133
142
  export declare const MODEL_FALLBACK_INHERIT_NO_TIER_BINDING: ModelFallbackReason;
@@ -21,12 +21,9 @@ export const CONFIG_MEMORY_PROJECT_KEY_SPELLING = 'config.memory_project_key_spe
21
21
  export const CONFIG_MEMORY_WRITE_SCOPE_MISMATCH = 'config.memory_write_scope_mismatch';
22
22
  export const CONFIG_LEGACY_TOOL_NAME = 'config.legacy_tool_name';
23
23
  export const CONFIG_IMAGE_INVALID = 'config.image_invalid';
24
- const IMAGE_INVALID_FAILURE_NOTE = 'This run did not start: an image sent with the message was refused before the model was called — the engine’s own error text says which image and what is wrong with it. ' +
25
- 'Nothing was sent to the model; remove or replace that image, then send the message again.';
26
- export function imageInvalidFailureNoteOf(code) {
27
- return code === CONFIG_IMAGE_INVALID ? IMAGE_INVALID_FAILURE_NOTE : undefined;
28
- }
29
24
  export const CONFIG_AGENT_UNKNOWN_TOOL = 'config.agent.unknown_tool';
25
+ export const CONFIG_TOOL_FACE_CONTROL = 'config.tool_face_control';
26
+ export const PROMPT_ASSEMBLED_ROLE_REFUSED = 'prompt.assembled_role_refused';
30
27
  export const CONFIG_OUTPUT_STYLE_INVALID = 'config.output_style_invalid';
31
28
  export const RULES_INERT_ENTRY = 'rules.inert_entry';
32
29
  export const RULES_INVALID_SHAPE = 'rules.invalid_shape';
@@ -65,6 +62,9 @@ export function isInterruptedToolEndCode(code) {
65
62
  }
66
63
  export const CAPABILITY_SELF_ORCHESTRATION_REQUIRED = 'capability.self_orchestration_required';
67
64
  export const CAPABILITY_IMAGE_INDEX_REQUIRED = 'capability.image_index_required';
65
+ export const CAPABILITY_K8S_LANE_REQUIRED = 'capability.k8s_lane_required';
66
+ export const NOT_FOUND_IMAGE = 'not_found.image';
67
+ export const REQUEST_PRECONDITION_UNMET = 'request.precondition_unmet';
68
68
  export const GATE_PARKED_ERROR_CODE = 'gate.parked';
69
69
  export const ACTIVE_RUN_BUSY_ERROR_CODE = 'conflict.session_active_run';
70
70
  export const CONFLICT_IDEMPOTENCY_PAYLOAD_MISMATCH = 'conflict.idempotency_payload_mismatch';
@@ -111,13 +111,6 @@ export const STOP_PARK_RESUME_WON = 'stop.park_resume_won';
111
111
  export const STOP_NOT_LANDED = 'stop.not_landed';
112
112
  export const STOP_NOT_LOCAL = 'stop.not_local';
113
113
  export const STOP_PARKED = 'stop.parked';
114
- export const STOP_CONFLICT_CODES = Object.freeze([
115
- STOP_PARK_ARBITER_UNREACHABLE,
116
- STOP_PARK_RESUME_WON,
117
- STOP_NOT_LANDED,
118
- STOP_NOT_LOCAL,
119
- STOP_PARKED,
120
- ]);
121
114
  export const OUTPUT_INVALID = 'output.invalid';
122
115
  export const RESUME_POLICY_FOLD_CODE = 'resume_blocked_by_policy';
123
116
  export const STATUS_DERIVED_COARSE_CODES = SDK_STATUS_DERIVED_COARSE_CODES;
@@ -215,6 +208,10 @@ export function isDecideAlreadyResolvedCode(code) {
215
208
  }
216
209
  export const ASK_GONE = 'gone.ask';
217
210
  export const ASK_GONE_PARKED = 'gone.ask_parked';
211
+ export const ASK_NOT_FOUND = 'not_found.ask';
212
+ export const ASK_IDEMPOTENCY_CONFLICT = 'conflict.ask_idempotency';
213
+ export const ASK_PARKING_RETRY = 'parking.ask_retry';
214
+ export const ASK_DECISION_LANE_DISABLED = 'feature.approval_ask_disabled';
218
215
  export const ASK_GONE_DENIED_WORD = SDK_DENY_REASON_ROUTING_FAILURE;
219
216
  export const RUN_DEATH_VERDICT_CODES = SDK_RUN_DEATH_VERDICT_CODES;
220
217
  const _runDeathVerdictCodePin = true;
@@ -266,6 +263,8 @@ export function isWorkflowParkRefusalCode(code) {
266
263
  export const DRAINING_ERROR_CODE = 'draining';
267
264
  export const SCENARIO_NOT_ALLOWED_ERROR_CODE = 'scenario_not_allowed';
268
265
  export const SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE = 'state.scenario_assignment_unknown';
266
+ export const USER_ATTACHMENT_INVALID_ERROR_CODE = 'config.user_attachment_invalid';
267
+ export const USER_INVOKED_SKILL_UNKNOWN_ERROR_CODE = 'config.user_invoked_skill_unknown';
269
268
  export const STREAM_MAX_DURATION = 'STREAM_MAX_DURATION';
270
269
  export const MODEL_FALLBACK_INHERIT_NO_TIER_BINDING = 'inherit_no_tier_binding';
271
270
  export function asModelFallbackReason(v) {
@@ -93,6 +93,10 @@ export const TRANSCRIPT_ROW_SIDECAR_KEYS = Object.freeze([
93
93
  '_sema_interrupted_never_started',
94
94
  '_sema_approval_window_expired',
95
95
  '_sema_denial_limit_window_expired',
96
+ '_sema_api_error_status',
97
+ '_sema_api_error_code',
98
+ '_sema_provider_condition',
99
+ '_sema_terminal_owner',
96
100
  ]);
97
101
  export function parkHeldCallIdOf(carrier) {
98
102
  return readFrameStringKey(carrier, 'toolCallId');
@@ -32,6 +32,7 @@ export interface EngineHistoryToolResultBlock {
32
32
  readonly tool_use_id: string;
33
33
  readonly content: readonly EngineHistoryBlock[];
34
34
  readonly is_error: boolean;
35
+ readonly _sema_gate_parked?: true;
35
36
  }
36
37
  export interface EngineHistoryOpaqueBlock {
37
38
  readonly type: string;
@@ -1,3 +1,4 @@
1
+ import { isGateParkedToolEnd } from './gateOutcome.js';
1
2
  import { ownDataField, ownSendableField } from './ownKey.js';
2
3
  const isObjectLike = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
3
4
  function optionalOwn(o, key) {
@@ -286,7 +287,12 @@ function toolResultBody(row, tally) {
286
287
  blocks = mapBlocks(items, tally);
287
288
  }
288
289
  const isError = ownDataField(row, 'isError') === true;
289
- return { kind: 'message', role: 'user', body: { role: 'user', content: [{ type: 'tool_result', tool_use_id: id, content: blocks, is_error: isError }] } };
290
+ const parked = isGateParkedToolEnd(row);
291
+ return {
292
+ kind: 'message',
293
+ role: 'user',
294
+ body: { role: 'user', content: [{ type: 'tool_result', tool_use_id: id, content: blocks, is_error: isError, ...(parked ? { _sema_gate_parked: true } : {}) }] },
295
+ };
290
296
  }
291
297
  function projectRow(row, tally) {
292
298
  if (!isObjectLike(row))
@@ -0,0 +1,27 @@
1
+ import type { CloudLoginFailure, CloudLoginFailureReading, EipcStoreClient, EngineTargetRefusal, EngineTargetRefused, EngineTargetState, SeatLocalMcpReading } from './seatContract.js';
2
+ import type { McpProbeOutcome } from './mcpProbeWire.js';
3
+ import type { UiLanguage } from './env/uiLanguage.js';
4
+ export interface CloudLoginFailureContext {
5
+ readonly deadlineReached?: boolean;
6
+ readonly cancelled?: boolean;
7
+ }
8
+ export declare function cloudLoginFailureOf(err: {} | null | undefined, ctx?: CloudLoginFailureContext): CloudLoginFailureReading;
9
+ export declare function cloudLoginFailureText(failure: CloudLoginFailure | CloudLoginFailureReading, lang?: UiLanguage): string;
10
+ export declare function engineTargetRefusalText(refused: EngineTargetRefusal | EngineTargetRefused, lang?: UiLanguage): string;
11
+ export type EngineTargetReading = {
12
+ readonly kind: 'state';
13
+ readonly state: EngineTargetState;
14
+ } | {
15
+ readonly kind: 'not_reported';
16
+ } | {
17
+ readonly kind: 'malformed';
18
+ };
19
+ export interface EngineTargetWatch {
20
+ stop(): void;
21
+ isCurrent(generation: number): boolean;
22
+ }
23
+ export declare function watchEngineTarget(store: EipcStoreClient<EngineTargetState> | undefined, onState: (reading: EngineTargetReading, switched: boolean) => void): EngineTargetWatch;
24
+ export declare function seatMcpReadingOf(outcome: McpProbeOutcome, readAt: string, extra?: {
25
+ readonly restartRequired?: true;
26
+ readonly forcedAsk?: Readonly<Record<string, readonly string[]>>;
27
+ }): SeatLocalMcpReading;
@@ -0,0 +1,199 @@
1
+ import { CLOUD_LOGIN_FAILURES, ENGINE_TARGET_REFUSALS, LOCAL_SESSIONS_SPEC } from './seatContract.js';
2
+ import { ownDataField } from './ownKey.js';
3
+ import { readThrownSlot, readThrownStatusSlot } from './wireFailureShape.js';
4
+ import { displayUntrusted } from './displayUntrusted.js';
5
+ import { msg, renderMessage } from './i18n/translate.js';
6
+ const field = (v, key) => {
7
+ if (v === null || typeof v !== 'object')
8
+ return undefined;
9
+ try {
10
+ return ownDataField(v, key);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ };
16
+ const OAUTH_WORDS = ['access_denied', 'expired_token', 'invalid_grant'];
17
+ export function cloudLoginFailureOf(err, ctx) {
18
+ if (field(ctx, 'cancelled') === true)
19
+ return Object.freeze({ failure: 'cancelled' });
20
+ if (field(ctx, 'deadlineReached') === true)
21
+ return Object.freeze({ failure: 'expired_token' });
22
+ if (err !== null && typeof err === 'object') {
23
+ const code = readThrownSlot(err, 'code', 'data');
24
+ if (typeof code === 'string' && code.length > 0) {
25
+ const word = OAUTH_WORDS.find((w) => w === code);
26
+ return Object.freeze({ failure: word ?? 'other' });
27
+ }
28
+ const status = readThrownStatusSlot(err);
29
+ if (typeof status === 'number' && Number.isInteger(status) && status >= 100 && status <= 599)
30
+ return Object.freeze({ failure: 'refused', status });
31
+ }
32
+ return Object.freeze({ failure: 'transport' });
33
+ }
34
+ const LOGIN_KEYS = {
35
+ access_denied: 'config.cloudLogin.accessDenied',
36
+ expired_token: 'config.cloudLogin.expiredToken',
37
+ invalid_grant: 'config.cloudLogin.invalidGrant',
38
+ cancelled: 'config.cloudLogin.cancelled',
39
+ transport: 'config.cloudLogin.transport',
40
+ refused: 'config.cloudLogin.refused',
41
+ no_tokens: 'config.cloudLogin.noTokens',
42
+ other: 'config.cloudLogin.other',
43
+ };
44
+ export function cloudLoginFailureText(failure, lang) {
45
+ const word = typeof failure === 'string' ? failure : field(failure, 'failure');
46
+ const known = CLOUD_LOGIN_FAILURES.find((w) => w === word) ?? 'other';
47
+ if (known === 'refused') {
48
+ const status = typeof failure === 'string' ? undefined : field(failure, 'status');
49
+ if (typeof status === 'number' && Number.isInteger(status) && status >= 100 && status <= 599) {
50
+ return renderMessage(msg('config.cloudLogin.refusedStatus', { status }), lang);
51
+ }
52
+ }
53
+ return renderMessage(msg(LOGIN_KEYS[known]), lang);
54
+ }
55
+ const REFUSAL_KEYS = {
56
+ busy: 'config.engineTarget.busy',
57
+ switching: 'config.engineTarget.switching',
58
+ not_configured: 'config.engineTarget.notConfigured',
59
+ not_signed_in: 'config.engineTarget.notSignedIn',
60
+ probe_failed: 'config.engineTarget.probeFailed',
61
+ org_policy: 'config.engineTarget.orgPolicy',
62
+ url_refused: 'config.engineTarget.urlRefused',
63
+ login_failed: 'config.engineTarget.loginFailed',
64
+ storage_unavailable: 'config.engineTarget.storageUnavailable',
65
+ no_local_runtime: 'config.engineTarget.noLocalRuntime',
66
+ input: 'config.engineTarget.input',
67
+ };
68
+ export function engineTargetRefusalText(refused, lang) {
69
+ const word = typeof refused === 'string' ? refused : field(refused, 'refusal');
70
+ const known = ENGINE_TARGET_REFUSALS.find((w) => w === word);
71
+ if (known === undefined)
72
+ return renderMessage(msg('config.engineTarget.unknown'), lang);
73
+ if (known === 'login_failed' && typeof refused !== 'string') {
74
+ const failure = field(refused, 'failure');
75
+ if (CLOUD_LOGIN_FAILURES.some((w) => w === failure)) {
76
+ const status = field(refused, 'status');
77
+ return cloudLoginFailureText({ failure: failure, ...(typeof status === 'number' ? { status } : {}) }, lang);
78
+ }
79
+ }
80
+ return renderMessage(msg(REFUSAL_KEYS[known]), lang);
81
+ }
82
+ const checkState = (v) => {
83
+ const check = LOCAL_SESSIONS_SPEC.stores?.['engineTargetState']?.check;
84
+ return typeof check === 'function' && check(v) === true;
85
+ };
86
+ export function watchEngineTarget(store, onState) {
87
+ let current;
88
+ let trusted = false;
89
+ let active = true;
90
+ let off;
91
+ const deliver = (state) => {
92
+ if (!active)
93
+ return;
94
+ let ok = false;
95
+ try {
96
+ ok = checkState(state);
97
+ }
98
+ catch {
99
+ ok = false;
100
+ }
101
+ if (!ok) {
102
+ trusted = false;
103
+ onState(Object.freeze({ kind: 'malformed' }), false);
104
+ return;
105
+ }
106
+ const s = state;
107
+ const switched = current !== undefined && s.generation !== current;
108
+ current = s.generation;
109
+ trusted = true;
110
+ onState(Object.freeze({ kind: 'state', state: s }), switched);
111
+ };
112
+ const handle = {
113
+ stop: () => {
114
+ if (!active)
115
+ return;
116
+ active = false;
117
+ try {
118
+ if (typeof off === 'function')
119
+ off();
120
+ }
121
+ catch {
122
+ off = undefined;
123
+ }
124
+ },
125
+ isCurrent: (generation) => active && trusted && current !== undefined && generation === current,
126
+ };
127
+ if (store === undefined || store === null) {
128
+ onState(Object.freeze({ kind: 'not_reported' }), false);
129
+ active = false;
130
+ return handle;
131
+ }
132
+ try {
133
+ if (typeof store !== 'object' || typeof store.onStateChange !== 'function' || typeof store.getStateSync !== 'function') {
134
+ onState(Object.freeze({ kind: 'malformed' }), false);
135
+ active = false;
136
+ return handle;
137
+ }
138
+ off = store.onStateChange((state) => deliver(state));
139
+ }
140
+ catch {
141
+ onState(Object.freeze({ kind: 'malformed' }), false);
142
+ active = false;
143
+ return handle;
144
+ }
145
+ let now;
146
+ try {
147
+ now = store.getStateSync();
148
+ }
149
+ catch {
150
+ trusted = false;
151
+ if (active)
152
+ onState(Object.freeze({ kind: 'malformed' }), false);
153
+ return handle;
154
+ }
155
+ deliver(now);
156
+ return handle;
157
+ }
158
+ const MCP_DETAIL_MAX = 200;
159
+ export function seatMcpReadingOf(outcome, readAt, extra) {
160
+ const restart = field(extra, 'restartRequired') === true ? { restartRequired: true } : {};
161
+ const status = field(outcome, 'status');
162
+ if (status === 'face') {
163
+ const face = field(outcome, 'face');
164
+ const servers = field(face, 'servers');
165
+ if (Array.isArray(servers)) {
166
+ const forced = field(extra, 'forcedAsk');
167
+ const rows = [];
168
+ for (const row of servers) {
169
+ const name = field(row, 'name');
170
+ const st = field(row, 'status');
171
+ if (typeof name !== 'string' || name.length === 0 || typeof st !== 'string')
172
+ continue;
173
+ const tc = field(row, 'toolCount');
174
+ const liveness = field(field(row, 'liveness'), 'state');
175
+ const ec = field(row, 'errorCode');
176
+ const fa = field(forced, name);
177
+ const forcedAsk = Array.isArray(fa) ? fa.filter((n) => typeof n === 'string' && n.length > 0) : [];
178
+ rows.push(Object.freeze({
179
+ name,
180
+ status: st,
181
+ ...(typeof tc === 'number' && Number.isSafeInteger(tc) && tc >= 0 ? { toolCount: tc } : {}),
182
+ ...(typeof liveness === 'string' && liveness.length > 0 ? { liveness } : {}),
183
+ ...(typeof ec === 'string' && ec.length > 0 ? { errorCode: ec } : {}),
184
+ ...(forcedAsk.length > 0 ? { forcedAsk: [...forcedAsk] } : {}),
185
+ }));
186
+ }
187
+ return Object.freeze({ status: 'listed', servers: rows, readAt, ...restart });
188
+ }
189
+ }
190
+ const cause = status === 'refused' ? field(outcome, 'cause') : status === 'unknown' ? field(outcome, 'why') : undefined;
191
+ const message = field(outcome, 'message');
192
+ return Object.freeze({
193
+ status: 'unreadable',
194
+ cause: typeof cause === 'string' && cause.length > 0 ? cause : 'no_verdict',
195
+ ...(typeof message === 'string' && message.length > 0 ? { detail: displayUntrusted(message, { max: MCP_DETAIL_MAX }) } : {}),
196
+ readAt,
197
+ ...restart,
198
+ });
199
+ }
@@ -10,6 +10,9 @@ export type EngineToolDirectory = {
10
10
  readonly state: 'not_landed';
11
11
  } | {
12
12
  readonly state: 'unreadable';
13
+ } | {
14
+ readonly state: 'request_only';
15
+ readonly requestMcpServerPrefixes: ReadonlySet<string>;
13
16
  } | {
14
17
  readonly state: 'directory';
15
18
  readonly mcpServerPrefixes: ReadonlySet<string>;