@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
@@ -1,3 +1,4 @@
1
+ import type { UiLanguage } from '../env/uiLanguage.js';
1
2
  import { type DenialLimitFallback } from './toolApprovalWire.js';
2
3
  export declare const APPROVAL_CARD_REASON_SOURCES: readonly ["payload_unreadable", "prior_cards", "message", "mandated", "mandate", "origin", "denial_limit", "probe", "allow_rule_shadow", "classifier_no_ruling", "bound_grants", "more"];
3
4
  export type ApprovalCardReasonSource = (typeof APPROVAL_CARD_REASON_SOURCES)[number];
@@ -9,5 +10,5 @@ export interface ApprovalCardReasonsOptions {
9
10
  readonly maxLines?: number;
10
11
  }
11
12
  export declare const APPROVAL_CARD_REASONS_DEFAULT_MAX_LINES = 4;
12
- export declare function denialLimitFallbackDetail(v: DenialLimitFallback | null | undefined): string | undefined;
13
- export declare function approvalCardReasons(card: object | null | undefined, opts?: ApprovalCardReasonsOptions | null): readonly ApprovalCardReasonLine[];
13
+ export declare function denialLimitFallbackDetail(v: DenialLimitFallback | null | undefined, lang?: UiLanguage): string | undefined;
14
+ export declare function approvalCardReasons(card: object | null | undefined, opts?: ApprovalCardReasonsOptions | null, lang?: UiLanguage): readonly ApprovalCardReasonLine[];
@@ -1,4 +1,5 @@
1
1
  import { displayUntrusted } from '../displayUntrusted.js';
2
+ import { msg, renderMessage } from '../i18n/translate.js';
2
3
  import { approvalMandateDetail, askOriginNoteOf } from '../gateVocabulary.js';
3
4
  import { readAskOrigin, readOwnDataValue, guardedRead } from '../ownKey.js';
4
5
  import { allowRuleShadowSentence } from './allowRuleShadow.js';
@@ -20,22 +21,20 @@ export const APPROVAL_CARD_REASON_SOURCES = Object.freeze([
20
21
  ]);
21
22
  export const APPROVAL_CARD_REASONS_DEFAULT_MAX_LINES = 4;
22
23
  const MESSAGE_DISPLAY_MAX = 300;
23
- const PAYLOAD_UNREADABLE_DETAIL = 'This client cannot read the format of this approval card, so only its basic facts are shown — decide from those, or deny it if you are not sure what it will do.';
24
- function moreDetail(n) {
25
- return n === 1 ? '1 more reason for this card is not shown.' : `${String(n)} more reasons for this card are not shown.`;
24
+ function payloadUnreadableDetail(lang) {
25
+ return renderMessage(msg('approval.card.payloadUnreadable'), lang);
26
26
  }
27
- export function denialLimitFallbackDetail(v) {
27
+ function moreDetail(n, lang) {
28
+ return renderMessage(n === 1 ? msg('approval.card.more.one') : msg('approval.card.more.other', { count: n }), lang);
29
+ }
30
+ export function denialLimitFallbackDetail(v, lang) {
28
31
  const d = readDenialLimitFallback(v);
29
32
  if (d === undefined)
30
33
  return undefined;
31
- const parts = [
32
- 'auto-deny limit reached — auto mode denied enough calls on its own that the engine now wants a real human decision',
33
- `${String(d.consecutive)} consecutive / ${String(d.total)} total automatic denials so far`,
34
- `the limit that tripped: ${displayUntrusted(d.limit, { max: 24 })}`,
35
- ];
36
- if (d.autoDenyAfterMs > 0)
37
- parts.push(`the engine auto-denies it in ${String(Math.round(d.autoDenyAfterMs / 1000))}s if nobody answers`);
38
- return parts.join('; ');
34
+ const limit = displayUntrusted(d.limit, { max: 24 });
35
+ return renderMessage(d.autoDenyAfterMs > 0
36
+ ? msg('approval.card.denialLimit.withAutoDeny', { consecutive: d.consecutive, total: d.total, limit, seconds: Math.round(d.autoDenyAfterMs / 1000) })
37
+ : msg('approval.card.denialLimit.plain', { consecutive: d.consecutive, total: d.total, limit }), lang);
39
38
  }
40
39
  function ownData(card, key) {
41
40
  return guardedRead(() => readOwnDataValue(card, key));
@@ -44,7 +43,7 @@ function readMaxLines(opts) {
44
43
  const raw = typeof opts === 'object' && opts !== null ? guardedRead(() => readOwnDataValue(opts, 'maxLines')) : undefined;
45
44
  return typeof raw === 'number' && Number.isSafeInteger(raw) && raw >= 1 ? raw : APPROVAL_CARD_REASONS_DEFAULT_MAX_LINES;
46
45
  }
47
- export function approvalCardReasons(card, opts) {
46
+ export function approvalCardReasons(card, opts, lang) {
48
47
  if (typeof card !== 'object' || card === null)
49
48
  return Object.freeze([]);
50
49
  const raw = [];
@@ -65,21 +64,21 @@ export function approvalCardReasons(card, opts) {
65
64
  catch {
66
65
  denialLimitReadable = false;
67
66
  }
68
- add('payload_unreadable', () => (ownData(card, 'cardPayloadUnreadable') === true ? PAYLOAD_UNREADABLE_DETAIL : undefined));
69
- add('prior_cards', () => approvalPriorCardsDetail(card));
67
+ add('payload_unreadable', () => (ownData(card, 'cardPayloadUnreadable') === true ? payloadUnreadableDetail(lang) : undefined));
68
+ add('prior_cards', () => approvalPriorCardsDetail(card, lang));
70
69
  add('message', () => {
71
- const msg = ownData(card, 'message');
72
- if (typeof msg !== 'string' || msg.trim() === '')
70
+ const engineMessage = ownData(card, 'message');
71
+ if (typeof engineMessage !== 'string' || engineMessage.trim() === '')
73
72
  return undefined;
74
73
  const tool = ownData(card, 'toolName');
75
- if (typeof tool === 'string' && msg.trim() === `approval required for "${tool}"`)
74
+ if (typeof tool === 'string' && engineMessage.trim() === `approval required for "${tool}"`)
76
75
  return undefined;
77
- return displayUntrusted(msg, { max: MESSAGE_DISPLAY_MAX });
76
+ return displayUntrusted(engineMessage, { max: MESSAGE_DISPLAY_MAX });
78
77
  });
79
- add('mandated', () => (approvalIsMandated(card) ? mandatedApprovalDetailOf(card) : undefined));
78
+ add('mandated', () => (approvalIsMandated(card) ? mandatedApprovalDetailOf(card, lang) : undefined));
80
79
  add('mandate', () => {
81
80
  const word = readApprovalMandate(card);
82
- return word !== undefined ? approvalMandateDetail(word) : undefined;
81
+ return word !== undefined ? approvalMandateDetail(word, lang) : undefined;
83
82
  });
84
83
  add('origin', () => {
85
84
  const origin = readAskOrigin(card);
@@ -87,26 +86,26 @@ export function approvalCardReasons(card, opts) {
87
86
  return undefined;
88
87
  if (origin === 'denial_limit_fallback' && denialLimitReadable)
89
88
  return undefined;
90
- return askOriginNoteOf(origin);
89
+ return askOriginNoteOf(origin, lang);
91
90
  });
92
- add('denial_limit', () => denialLimitFallbackDetail(ownData(card, 'denialLimitFallback')));
93
- add('probe', () => askProbeReasonDetail(card));
94
- add('allow_rule_shadow', () => allowRuleShadowSentence(card));
91
+ add('denial_limit', () => denialLimitFallbackDetail(ownData(card, 'denialLimitFallback'), lang));
92
+ add('probe', () => askProbeReasonDetail(card, lang));
93
+ add('allow_rule_shadow', () => allowRuleShadowSentence(card, lang));
95
94
  add('classifier_no_ruling', () => {
96
95
  const word = readApprovalClassifierNoRuling(card);
97
- return word !== undefined ? classifierNoRulingDetail(word) : undefined;
96
+ return word !== undefined ? classifierNoRulingDetail(word, lang) : undefined;
98
97
  });
99
98
  add('bound_grants', () => {
100
99
  const grants = readApprovalBoundGrants(card);
101
- return grants !== undefined ? approvalBoundGrantsDetail(grants) : undefined;
100
+ return grants !== undefined ? approvalBoundGrantsDetail(grants, lang) : undefined;
102
101
  });
103
102
  const seen = new Set();
104
103
  const lines = [];
105
104
  for (const r of raw) {
106
105
  if (r.text === undefined)
107
106
  continue;
108
- const text = displayUntrusted(r.text);
109
- if (text === '' || seen.has(text))
107
+ const text = r.text;
108
+ if (text.trim() === '' || seen.has(text))
110
109
  continue;
111
110
  seen.add(text);
112
111
  lines.push(Object.freeze({ source: r.source, text }));
@@ -115,6 +114,6 @@ export function approvalCardReasons(card, opts) {
115
114
  if (lines.length <= max)
116
115
  return Object.freeze(lines);
117
116
  const kept = lines.slice(0, max - 1);
118
- kept.push(Object.freeze({ source: 'more', text: moreDetail(lines.length - kept.length) }));
117
+ kept.push(Object.freeze({ source: 'more', text: moreDetail(lines.length - kept.length, lang) }));
119
118
  return Object.freeze(kept);
120
119
  }
@@ -1,5 +1,6 @@
1
1
  import type { ApprovalHolderIdentity } from './toolApprovalWire.js';
2
2
  import type { ApprovalResolution, ApprovalResolutionLane } from './approvalResolution.js';
3
+ import { type DecisionLedgerOp } from './decisionLedger.js';
3
4
  export declare const APPROVAL_SETTLED_HOWS: readonly ["answered", "interrupted", "closed_by_engine"];
4
5
  export type ApprovalSettledHow = (typeof APPROVAL_SETTLED_HOWS)[number];
5
6
  export declare const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
@@ -29,6 +30,7 @@ export interface LiveCardHold {
29
30
  readonly cardSignal: AbortSignal;
30
31
  readonly interrupt: () => void;
31
32
  trailing?: Promise<boolean>;
33
+ ledgerOp?: DecisionLedgerOp;
32
34
  }
33
35
  export interface HostCardHold {
34
36
  readonly lane: 'host';
@@ -61,12 +63,25 @@ export interface HostApprovalCardHold {
61
63
  sending(): void;
62
64
  release(settled?: 'answered' | 'interrupted'): void;
63
65
  }
66
+ export declare function hostHoldIsActive(handle: HostApprovalCardHold): boolean;
67
+ export declare function takeHostHoldLedgerOp(handle: HostApprovalCardHold): DecisionLedgerOp | undefined;
68
+ export type ApprovalHoldAttempt = {
69
+ readonly kind: 'settled';
70
+ } | {
71
+ readonly kind: 'busy';
72
+ } | {
73
+ readonly kind: 'held';
74
+ readonly hold: HostApprovalCardHold;
75
+ };
76
+ export declare function tryHoldApprovalNow(who: HoldIdentity, signal: AbortSignal | undefined): ApprovalHoldAttempt;
64
77
  export type ApprovalCardClaim = {
65
78
  readonly kind: 'held';
66
79
  readonly hold: HostApprovalCardHold;
67
80
  } | {
68
81
  readonly kind: 'joined';
69
82
  readonly outcome: ApprovalResolution;
83
+ } | {
84
+ readonly kind: 'aborted';
70
85
  };
71
86
  export declare function claimApprovalCardFor(who: HoldIdentity, signal: AbortSignal | undefined): Promise<ApprovalCardClaim>;
72
87
  export interface ApprovalDecisionHoldTable {
@@ -85,9 +100,11 @@ export declare function settledApprovalOf(approvalId: string | undefined): Settl
85
100
  export declare function clearSettledApprovals(): void;
86
101
  export declare function anyApprovalSettled(): boolean;
87
102
  export declare function noteEngineClosedApproval(approvalId: string, parked: boolean): void;
103
+ export declare function settleEngineClosedApproval(frame: object, parked: boolean): boolean;
88
104
  export declare function noteHolderSettledApproval(approvalId: string, how: 'answered' | 'interrupted', outcome: ApprovalResolution): void;
89
105
  export declare function settledLiveCardOutcome(approvalId: string): ApprovalResolution | undefined;
90
106
  export declare function liveCardHoldOf(key: string): LiveCardHold | undefined;
91
107
  export declare function holdLiveCard(identity: LiveCardIdentity, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<ApprovalResolution>): Promise<ApprovalResolution>;
92
108
  export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ApprovalResolution>;
109
+ export declare function enterLiveRespondPhase(hold: LiveCardHold): void;
93
110
  export declare function supersedeLiveCard(key: string, parked: boolean): boolean;
@@ -1,6 +1,7 @@
1
1
  import { hostLog } from '../host.js';
2
2
  import { liveFrameCallKey } from './gateIdentity.js';
3
- import { decideReceiptReopen } from '../decideReceipt.js';
3
+ import { decideReceiptReopen, readDecideReceipt } from '../decideReceipt.js';
4
+ import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp, settleDecisionOp } from './decisionLedger.js';
4
5
  export const APPROVAL_SETTLED_HOWS = Object.freeze(['answered', 'interrupted', 'closed_by_engine']);
5
6
  export const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
6
7
  const nonEmpty = (v) => typeof v === 'string' && v !== '';
@@ -73,7 +74,7 @@ export async function awaitApprovalHolder(hold, signal, asker) {
73
74
  }
74
75
  if (r.kind !== 'unsettled' || r.cause !== 'interrupted' || hold.trailing === undefined)
75
76
  return HOLDER_FREE;
76
- const landed = await hold.trailing.then(() => true, () => false);
77
+ const landed = await hold.trailing.then((raw) => decideReceiptReopen(readDecideReceipt(raw)) === null, () => false);
77
78
  return landed ? Object.freeze({ kind: 'settled', how: 'interrupted', outcome: r }) : HOLDER_FREE;
78
79
  }
79
80
  finally {
@@ -98,7 +99,7 @@ async function awaitHostHolder(hold, signal, asker) {
98
99
  unsubscribe();
99
100
  }
100
101
  }
101
- function holdHostCard(identity) {
102
+ function holdHostCard(identity, signal) {
102
103
  const h = approvalDecisionHolds;
103
104
  const key = `hitl-host:${++h.clock}`;
104
105
  let resolveReleased = () => { };
@@ -109,41 +110,101 @@ function holdHostCard(identity) {
109
110
  const hold = { lane: 'host', identity: frozen, phase: 'card', released };
110
111
  h.inFlight.set(key, hold);
111
112
  let done = false;
112
- return Object.freeze({
113
+ let stopFollowing = () => { };
114
+ let ledgerOp;
115
+ const release = (settled) => {
116
+ if (done)
117
+ return;
118
+ done = true;
119
+ stopFollowing();
120
+ if (ledgerOp !== undefined) {
121
+ settleDecisionOp(ledgerOp, 'host');
122
+ ledgerOp.ownCallOpen = false;
123
+ }
124
+ const how = settled === 'answered' || settled === 'interrupted' ? settled : undefined;
125
+ if (how !== undefined && nonEmpty(frozen.approvalId))
126
+ noteSettled(frozen.approvalId, how, false, how === 'interrupted' ? interruptedThere('decide') : nothingSentHere('decide'), true);
127
+ if (h.inFlight.get(key) === hold)
128
+ h.inFlight.delete(key);
129
+ resolveReleased(how);
130
+ };
131
+ const handle = Object.freeze({
113
132
  identity: frozen,
114
133
  sending: () => {
115
- if (!done)
116
- hold.phase = 'respond';
117
- },
118
- release: (settled) => {
119
134
  if (done)
120
135
  return;
121
- done = true;
122
- const how = settled === 'answered' || settled === 'interrupted' ? settled : undefined;
123
- if (how !== undefined && nonEmpty(frozen.approvalId))
124
- noteSettled(frozen.approvalId, how, false, how === 'interrupted' ? interruptedThere('decide') : nothingSentHere('decide'), true);
125
- if (h.inFlight.get(key) === hold)
126
- h.inFlight.delete(key);
127
- resolveReleased(how);
136
+ if (hold.phase !== 'respond')
137
+ ledgerOp = openDecisionOp('tool_approval', ledgerKeysOfHold(frozen), 'handed', true);
138
+ hold.phase = 'respond';
139
+ stopFollowing();
140
+ },
141
+ release,
142
+ });
143
+ stopFollowing = followCallerSignal(signal, () => {
144
+ if (!done && hold.phase === 'card')
145
+ release();
146
+ });
147
+ hostHoldControls.set(handle, {
148
+ active: () => !done && h.inFlight.get(key) === hold,
149
+ takeLedgerOp: () => {
150
+ const op = ledgerOp;
151
+ ledgerOp = undefined;
152
+ return op;
128
153
  },
129
154
  });
155
+ return handle;
156
+ }
157
+ const hostHoldControls = new WeakMap();
158
+ export function hostHoldIsActive(handle) {
159
+ return hostHoldControls.get(handle)?.active() === true;
160
+ }
161
+ export function takeHostHoldLedgerOp(handle) {
162
+ return hostHoldControls.get(handle)?.takeLedgerOp();
163
+ }
164
+ export function tryHoldApprovalNow(who, signal) {
165
+ const entry = settledApprovalOf(who.approvalId);
166
+ if (entry !== undefined && settledForDurableLane(entry))
167
+ return Object.freeze({ kind: 'settled' });
168
+ if (approvalHoldFor(who) !== undefined)
169
+ return Object.freeze({ kind: 'busy' });
170
+ return Object.freeze({ kind: 'held', hold: holdHostCard(who, signal) });
171
+ }
172
+ function callerSignalAborted(signal) {
173
+ if (signal === undefined || signal === null)
174
+ return false;
175
+ try {
176
+ return signal.aborted === true;
177
+ }
178
+ catch {
179
+ return false;
180
+ }
130
181
  }
131
- const CLAIM_MAX_ROUNDS = 8;
132
182
  export async function claimApprovalCardFor(who, signal) {
133
- for (let round = 0; round < CLAIM_MAX_ROUNDS; round++) {
134
- const entry = settledApprovalOf(who.approvalId);
135
- if (settledForDurableLane(entry) && entry !== undefined)
136
- return Object.freeze({ kind: 'joined', outcome: { ...entry.outcome, joinedInFlight: true } });
137
- const holder = approvalHoldFor(who);
138
- if (holder === undefined)
139
- return Object.freeze({ kind: 'held', hold: holdHostCard(who) });
140
- const wait = await awaitApprovalHolder(holder, signal, 'decide');
141
- if (wait.kind === 'settled')
142
- return Object.freeze({ kind: 'joined', outcome: { ...wait.outcome, joinedInFlight: true } });
143
- if (wait.kind === 'left')
144
- return Object.freeze({ kind: 'joined', outcome: { ...nothingSentHere('decide'), joinedInFlight: true } });
183
+ let leave = () => { };
184
+ const left = new Promise((resolve) => {
185
+ leave = () => resolve('aborted');
186
+ });
187
+ const unsubscribe = followCallerSignal(signal, leave);
188
+ try {
189
+ for (;;) {
190
+ if (callerSignalAborted(signal))
191
+ return Object.freeze({ kind: 'aborted' });
192
+ const entry = settledApprovalOf(who.approvalId);
193
+ if (settledForDurableLane(entry) && entry !== undefined)
194
+ return Object.freeze({ kind: 'joined', outcome: { ...entry.outcome, joinedInFlight: true } });
195
+ const holder = approvalHoldFor(who);
196
+ if (holder === undefined)
197
+ return Object.freeze({ kind: 'held', hold: holdHostCard(who, signal) });
198
+ const wait = await Promise.race([awaitApprovalHolder(holder, undefined, 'decide'), left]);
199
+ if (wait === 'aborted')
200
+ return Object.freeze({ kind: 'aborted' });
201
+ if (wait.kind === 'settled')
202
+ return Object.freeze({ kind: 'joined', outcome: { ...wait.outcome, joinedInFlight: true } });
203
+ }
204
+ }
205
+ finally {
206
+ unsubscribe();
145
207
  }
146
- return Object.freeze({ kind: 'held', hold: holdHostCard(who) });
147
208
  }
148
209
  export const approvalDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), outcomes: new Map(), clock: 0 };
149
210
  export function settledApprovalOf(approvalId) {
@@ -180,6 +241,20 @@ function engineClosedOutcome() {
180
241
  export function noteEngineClosedApproval(approvalId, parked) {
181
242
  noteSettled(approvalId, 'closed_by_engine', parked, engineClosedOutcome());
182
243
  }
244
+ export function settleEngineClosedApproval(frame, parked) {
245
+ let id;
246
+ try {
247
+ const d = typeof frame === 'object' && frame !== null ? Object.getOwnPropertyDescriptor(frame, 'approvalId') : undefined;
248
+ id = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
249
+ }
250
+ catch {
251
+ return false;
252
+ }
253
+ if (!nonEmpty(id))
254
+ return false;
255
+ noteEngineClosedApproval(id, parked);
256
+ return supersedeLiveCard(liveFrameCallKey(id), parked);
257
+ }
183
258
  export function noteHolderSettledApproval(approvalId, how, outcome) {
184
259
  noteSettled(approvalId, how, false, outcome, true);
185
260
  }
@@ -210,6 +285,8 @@ export function holdLiveCard(identity, callerSignal, present) {
210
285
  const hold = { lane: 'live', identity: Object.freeze({ ...identity }), outcome, phase: 'card', cardSignal: cut.signal, interrupt };
211
286
  h.inFlight.set(key, hold);
212
287
  const release = () => {
288
+ if (hold.ledgerOp !== undefined)
289
+ closeDecisionOp(hold.ledgerOp, 'package');
213
290
  if (h.inFlight.get(key) === hold)
214
291
  h.inFlight.delete(key);
215
292
  };
@@ -276,6 +353,14 @@ function followCallerSignal(signal, onAbort) {
276
353
  }
277
354
  };
278
355
  }
356
+ function ledgerKeysOfHold(identity) {
357
+ return decisionLedgerKeysOf({ taskId: identity.taskId, approvalId: identity.approvalId, callId: identity.toolCallId });
358
+ }
359
+ export function enterLiveRespondPhase(hold) {
360
+ if (hold.ledgerOp === undefined)
361
+ hold.ledgerOp = openDecisionOp('tool_approval', ledgerKeysOfHold(hold.identity), 'posting', true);
362
+ hold.phase = 'respond';
363
+ }
279
364
  export function supersedeLiveCard(key, parked) {
280
365
  const hold = liveCardHoldOf(key);
281
366
  if (hold === undefined || hold.phase !== 'card' || hold.superseded !== undefined)
@@ -1,5 +1,5 @@
1
- import type { CORE_MODE_ARBITRATION } from '../generated/engineFactTables.js';
2
- export declare const DEPLOYMENT_LIST_ASK_CLASSES: readonly (keyof typeof CORE_MODE_ARBITRATION)[];
1
+ import { CORE_ASK_CLASSES } from '../generated/engineFactTables.js';
2
+ export declare const DEPLOYMENT_LIST_ASK_CLASSES: readonly (typeof CORE_ASK_CLASSES)[number][];
3
3
  export type AskClassPairReading = {
4
4
  readonly state: 'pair';
5
5
  readonly askClass: string;
@@ -1,3 +1,4 @@
1
+ import { CORE_ASK_CLASSES } from '../generated/engineFactTables.js';
1
2
  export const DEPLOYMENT_LIST_ASK_CLASSES = Object.freeze(['ask_rule']);
2
3
  const ABSENT = Object.freeze({ state: 'absent' });
3
4
  const MALFORMED = Object.freeze({ state: 'malformed' });
@@ -35,8 +36,23 @@ export function readAskClassPairState(v) {
35
36
  return MALFORMED;
36
37
  if (classes[0] !== askClass)
37
38
  return MALFORMED;
39
+ if (!inCanonicalOrder(classes))
40
+ return MALFORMED;
38
41
  return Object.freeze({ state: 'pair', askClass, classes: Object.freeze(classes) });
39
42
  }
43
+ function inCanonicalOrder(classes) {
44
+ const order = CORE_ASK_CLASSES;
45
+ let last = -1;
46
+ for (const c of classes) {
47
+ const rank = order.indexOf(c);
48
+ if (rank < 0)
49
+ continue;
50
+ if (rank <= last)
51
+ return false;
52
+ last = rank;
53
+ }
54
+ return true;
55
+ }
40
56
  export function readAskClassPair(v) {
41
57
  const r = readAskClassPairState(v);
42
58
  return r.state === 'pair' ? { askClass: r.askClass, classes: r.classes } : undefined;
@@ -6,6 +6,7 @@ import { abortableSleep } from '../abortableSleep.js';
6
6
  import { hostLog } from '../host.js';
7
7
  import { decideAcceptedNotResolved, readDecideReceipt } from '../decideReceipt.js';
8
8
  import { APPROVAL_EXPIRED } from '../engineErrorCodes.js';
9
+ import { isNotFoundFailure } from '../wireErrorTriage.js';
9
10
  import { readDecisionRefusalAsk } from './decisionRefusal.js';
10
11
  import { resumeReopenFromError } from './suspendedReopen.js';
11
12
  import { refusedParkFromError, resumeRefusalFromError } from '../resumeRefusalCopy.js';
@@ -192,7 +193,7 @@ export function lateDecideOutcomeOf(reply, opts) {
192
193
  const r = readDecisionRefusalAsk(e);
193
194
  if (r.reading !== 'settled' && !(r.reading === 'not_settled' && r.gateMoved))
194
195
  return LATE_UNKNOWN;
195
- const probeWorthy = status === 404 || status === 410 || (typeof code === 'string' && code.startsWith('not_found.'));
196
+ const probeWorthy = status === 404 || status === 410 || isNotFoundFailure(e) !== false;
196
197
  return Object.freeze(probeWorthy ? { kind: 'settled-elsewhere', probeWorthy: true } : { kind: 'settled-elsewhere' });
197
198
  }
198
199
  catch {
@@ -1,10 +1,11 @@
1
- export declare function askEndingCauseClause(cause: string | undefined): string | undefined;
1
+ import type { UiLanguage } from '../env/uiLanguage.js';
2
+ export declare function askEndingCauseClause(cause: string | undefined, lang?: UiLanguage): string | undefined;
2
3
  export interface AskSettlementInput {
3
4
  readonly cause?: string | undefined;
4
5
  readonly toolName?: string | undefined;
5
6
  readonly parked?: boolean | undefined;
6
7
  }
7
- export declare function askSettlementLine(settled: AskSettlementInput): string | undefined;
8
+ export declare function askSettlementLine(settled: AskSettlementInput, lang?: UiLanguage): string | undefined;
8
9
  export type AskAcceptedFollow = 'following' | 'already-following' | 'not-following';
9
10
  export interface AskAcceptedInput {
10
11
  readonly decision?: string | undefined;
@@ -13,6 +14,15 @@ export interface AskAcceptedInput {
13
14
  readonly follow?: AskAcceptedFollow | undefined;
14
15
  readonly executed?: string | undefined;
15
16
  }
16
- export declare function askAcceptedLine(accepted: AskAcceptedInput): string;
17
+ export declare function askAcceptedLine(accepted: AskAcceptedInput, lang?: UiLanguage): string;
17
18
  export type AskParkedCardForm = 'approval' | 'question' | 'queue_no_deadline';
18
- export declare function askParkedCardNote(form?: string | undefined, toolName?: string | undefined): string;
19
+ export declare function askParkedCardNote(form?: string | undefined, toolName?: string | undefined, lang?: UiLanguage): string;
20
+ export interface AskHumanSettledInput {
21
+ readonly kind?: string | undefined;
22
+ readonly decision?: string | undefined;
23
+ readonly taskId?: string | undefined;
24
+ readonly late?: boolean | undefined;
25
+ readonly follow?: AskAcceptedFollow | undefined;
26
+ readonly executed?: string | undefined;
27
+ }
28
+ export declare function askHumanSettlementLine(settled: AskHumanSettledInput, lang?: UiLanguage): string | undefined;
@@ -1,79 +1,99 @@
1
1
  import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
2
+ import { msg, renderMessage } from '../i18n/translate.js';
2
3
  import { guardedRead, readOwnDataValue } from '../ownKey.js';
3
- const ENDING_CAUSE_CLAUSES = Object.freeze({
4
- routing_failure_fail_closed: 'the engine could not route it to anywhere that can answer it, so it closed it',
5
- run_cancelled: 'the run that asked for it was cancelled',
6
- run_exited: 'the run that asked for it exited before anyone answered',
7
- batch_aborted: 'the batch of tool calls it belonged to was aborted',
8
- batch_sibling_withdrawn: 'another approval in the same batch was refused or withdrawn, which withdrew this one too',
9
- batch_bound_elsewhere: 'the decision for its batch was bound to a different approval',
10
- adhoc_leg_no_durable_domain: 'this kind of request cannot be kept waiting for an answer on this deployment',
11
- orphan_ttl_exceeded: "it was left without an owner past the engine's time limit",
12
- crashed_before_park: 'the engine process stopped before it could keep the request waiting',
13
- shutdown_before_park: 'the engine shut down before it could keep the request waiting',
14
- ttl_expired: 'nobody answered within the approval window',
15
- broadcast_lost: 'it never reached any open connection',
16
- stream_disconnected: 'the connection carrying this card dropped',
17
- undeliverable: 'it could not be delivered to any approval surface',
4
+ const ENDING_CAUSE_KEYS = Object.freeze({
5
+ routing_failure_fail_closed: 'approval.settle.cause.routingFailureFailClosed',
6
+ run_cancelled: 'approval.settle.cause.runCancelled',
7
+ run_exited: 'approval.settle.cause.runExited',
8
+ batch_aborted: 'approval.settle.cause.batchAborted',
9
+ batch_sibling_withdrawn: 'approval.settle.cause.batchSiblingWithdrawn',
10
+ batch_bound_elsewhere: 'approval.settle.cause.batchBoundElsewhere',
11
+ adhoc_leg_no_durable_domain: 'approval.settle.cause.adhocLegNoDurableDomain',
12
+ orphan_ttl_exceeded: 'approval.settle.cause.orphanTtlExceeded',
13
+ crashed_before_park: 'approval.settle.cause.crashedBeforePark',
14
+ shutdown_before_park: 'approval.settle.cause.shutdownBeforePark',
15
+ ttl_expired: 'approval.settle.cause.ttlExpired',
16
+ broadcast_lost: 'approval.settle.cause.broadcastLost',
17
+ stream_disconnected: 'approval.settle.cause.streamDisconnected',
18
+ undeliverable: 'approval.settle.cause.undeliverable',
18
19
  });
19
- export function askEndingCauseClause(cause) {
20
- return typeof cause === 'string' && Object.hasOwn(ENDING_CAUSE_CLAUSES, cause) ? ENDING_CAUSE_CLAUSES[cause] : undefined;
20
+ function endingCauseKeyOf(cause) {
21
+ return typeof cause === 'string' && Object.hasOwn(ENDING_CAUSE_KEYS, cause) ? ENDING_CAUSE_KEYS[cause] : undefined;
22
+ }
23
+ export function askEndingCauseClause(cause, lang) {
24
+ const key = endingCauseKeyOf(cause);
25
+ return key !== undefined ? renderMessage(msg(key), lang) : undefined;
21
26
  }
22
27
  function ownSlot(o, key) {
23
28
  if (typeof o !== 'object' || o === null)
24
29
  return undefined;
25
30
  return guardedRead(() => readOwnDataValue(o, key));
26
31
  }
27
- function subjectOf(toolName, named, fallback) {
32
+ function subjectOf(toolName, named, generic) {
28
33
  const quoted = typeof toolName === 'string' ? guardedRead(() => quoteUntrustedForSentence(toolName, 80)) : undefined;
29
- return quoted === undefined ? fallback : named(quoted);
34
+ return quoted === undefined ? msg(generic) : msg(named, { tool: quoted });
30
35
  }
31
- const SETTLEMENT_TAIL = ' Nothing was approved from this card; if the step is still needed, ask for it again.';
32
- export function askSettlementLine(settled) {
36
+ export function askSettlementLine(settled, lang) {
33
37
  if (ownSlot(settled, 'parked') === true)
34
38
  return undefined;
35
39
  const cause = ownSlot(settled, 'cause');
36
40
  if (typeof cause !== 'string' || cause === '')
37
41
  return undefined;
38
- const subject = subjectOf(ownSlot(settled, 'toolName'), (q) => `the approval request for ${q}`, 'this approval request');
39
- const clause = askEndingCauseClause(cause);
40
- const why = clause !== undefined ? `: ${clause}` : " (the engine's reason is one this build does not know yet)";
41
- return `${subject} ended on the engine without an answer${why}.${SETTLEMENT_TAIL}`;
42
+ const subject = subjectOf(ownSlot(settled, 'toolName'), 'approval.settle.subject.named', 'approval.settle.subject.generic');
43
+ const clauseKey = endingCauseKeyOf(cause);
44
+ return renderMessage(clauseKey !== undefined ? msg('approval.settle.endedKnown', { subject, clause: msg(clauseKey) }) : msg('approval.settle.endedUnknown', { subject }), lang);
42
45
  }
43
46
  const TASK_ID_MAX = 64;
44
- const EXECUTED_TAILS = Object.freeze({
45
- allowed: ' The engine also reports that the already-decided step was ALLOWED when it ran.',
46
- denied: ' The engine also reports that the already-decided step was DENIED at execution time (a deployment review can overturn an approval after the fact).',
47
+ const EXECUTED_TAIL_KEYS = Object.freeze({
48
+ allowed: 'approval.settle.executed.allowed',
49
+ denied: 'approval.settle.executed.denied',
47
50
  });
48
- const FOLLOW_CLAUSES = Object.freeze({
49
- following: 'sema is following that run here',
50
- 'already-following': 'this screen is already following that run',
51
+ const FOLLOW_CLAUSE_KEYS = Object.freeze({
52
+ following: 'approval.settle.follow.following',
53
+ 'already-following': 'approval.settle.follow.alreadyFollowing',
51
54
  });
52
- export function askAcceptedLine(accepted) {
55
+ export function askAcceptedLine(accepted, lang) {
53
56
  const decision = ownSlot(accepted, 'decision');
54
- const what = decision === 'approve' ? 'approved' : decision === 'deny' ? 'denied' : 'answered';
55
- const late = ownSlot(accepted, 'late') === false ? '' : ' late';
57
+ const what = msg(decision === 'approve' ? 'approval.settle.what.approved' : decision === 'deny' ? 'approval.settle.what.denied' : 'approval.settle.what.answered');
58
+ const late = ownSlot(accepted, 'late') === false ? '' : msg('approval.settle.late');
56
59
  const rawTask = ownSlot(accepted, 'taskId');
57
60
  const shownTask = typeof rawTask === 'string' ? guardedRead(() => displayUntrusted(rawTask, { max: TASK_ID_MAX, blanks: true })) : undefined;
58
- const where = shownTask === undefined || shownTask === '' ? '' : ` (task ${shownTask})`;
61
+ const where = shownTask === undefined || shownTask === '' ? '' : msg('approval.settle.where', { task: shownTask });
59
62
  const follow = ownSlot(accepted, 'follow');
60
63
  const executed = ownSlot(accepted, 'executed');
61
- const tail = executed === 'allowed' || executed === 'denied' ? EXECUTED_TAILS[executed] : '';
64
+ const tail = executed === 'allowed' || executed === 'denied' ? msg(EXECUTED_TAIL_KEYS[executed]) : '';
62
65
  if (follow === 'following' || follow === 'already-following') {
63
- return `${what}${late} — the engine took this decision${where}; ${FOLLOW_CLAUSES[follow]}, so what it does next shows up below.${tail}`;
66
+ return renderMessage(msg('approval.settle.acceptedFollowing', { what, late, where, follow: msg(FOLLOW_CLAUSE_KEYS[follow]), tail }), lang);
64
67
  }
65
- return (`${what}${late} — the engine took this decision${where}. Whether the parked call has resumed yet is not something this reply can say; ` +
66
- `this screen is not following that run, so what it does next will not show up here by itself.${tail}`);
68
+ return renderMessage(msg('approval.settle.acceptedNotFollowing', { what, late, where, tail }), lang);
67
69
  }
68
- const PARKED_CARD_NOTES = Object.freeze({
69
- approval: "This step was already parked in the engine's durable queue and waiting for a decision before this card went up. Yes approves it now and lets the step run late; No abandons the step — the model gets a refusal and can take another route.",
70
- question: "This step was already parked in the engine's durable queue and waiting for a decision before this card went up. Answering now sends your decision late and lets the parked step continue with it; closing this card without answering abandons the step — the model gets a refusal and can take another route.",
71
- queue_no_deadline: "{subject} is in the engine's durable queue (no deadline was reported for it). It waits for your decision — nothing has been decided on your behalf.",
70
+ const PARKED_CARD_NOTE_KEYS = Object.freeze({
71
+ approval: 'approval.settle.parkedNote.approval',
72
+ question: 'approval.settle.parkedNote.question',
73
+ queue_no_deadline: 'approval.settle.parkedNote.queueNoDeadline',
72
74
  });
73
- export function askParkedCardNote(form, toolName) {
75
+ export function askParkedCardNote(form, toolName, lang) {
74
76
  const key = form === 'question' || form === 'queue_no_deadline' ? form : 'approval';
75
- const template = PARKED_CARD_NOTES[key];
76
77
  if (key !== 'queue_no_deadline')
77
- return template;
78
- return template.split('{subject}').join(subjectOf(toolName, (q) => `the approval for ${q}`, 'this approval'));
78
+ return renderMessage(msg(PARKED_CARD_NOTE_KEYS[key]), lang);
79
+ return renderMessage(msg(PARKED_CARD_NOTE_KEYS[key], { subject: subjectOf(toolName, 'approval.settle.parkedSubject.named', 'approval.settle.parkedSubject.generic') }), lang);
80
+ }
81
+ export function askHumanSettlementLine(settled, lang) {
82
+ const kind = ownSlot(settled, 'kind');
83
+ if (kind === 'accepted-not-resolved')
84
+ return askAcceptedLine(settled, lang);
85
+ if (kind === 'expired-reaped')
86
+ return renderMessage(msg('approval.settleHuman.expiredReaped'), lang);
87
+ if (kind === 'settled-elsewhere')
88
+ return renderMessage(msg('approval.settleHuman.settledElsewhere'), lang);
89
+ if (kind !== 'resumed')
90
+ return undefined;
91
+ const decision = ownSlot(settled, 'decision');
92
+ const what = msg(decision === 'approve' ? 'approval.settle.what.approved' : decision === 'deny' ? 'approval.settle.what.denied' : 'approval.settle.what.answered');
93
+ const rawTask = ownSlot(settled, 'taskId');
94
+ const shownTask = typeof rawTask === 'string' ? guardedRead(() => displayUntrusted(rawTask, { max: TASK_ID_MAX, blanks: true })) : undefined;
95
+ const where = shownTask === undefined || shownTask === '' ? '' : msg('approval.settle.where', { task: shownTask });
96
+ const executed = ownSlot(settled, 'executed');
97
+ const tail = executed === 'allowed' || executed === 'denied' ? msg(EXECUTED_TAIL_KEYS[executed]) : '';
98
+ return renderMessage(msg(ownSlot(settled, 'late') === false ? 'approval.settleHuman.resumed' : 'approval.settleHuman.resumedLate', { what, where, tail }), lang);
79
99
  }