@sema-agent/client-core 0.112.1 → 0.113.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 (178) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +36 -23
  3. package/dist/adapt/arms.js +7 -1
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +27 -1
  6. package/dist/adapter/activeRunSelfHeal.d.ts +7 -5
  7. package/dist/adapter/activeRunSelfHeal.js +38 -66
  8. package/dist/adapter/downstream/eventToSdkMessage.js +4 -0
  9. package/dist/adapter/downstream/terminalToSdkResult.d.ts +8 -0
  10. package/dist/adapter/downstream/terminalToSdkResult.js +36 -1
  11. package/dist/adapter/runStream.d.ts +2 -12
  12. package/dist/adapter/runStream.js +46 -19
  13. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  14. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  15. package/dist/attachmentOutcome.d.ts +35 -0
  16. package/dist/attachmentOutcome.js +192 -0
  17. package/dist/attachmentPathPlan.d.ts +67 -0
  18. package/dist/attachmentPathPlan.js +203 -0
  19. package/dist/attachmentRoute.d.ts +9 -1
  20. package/dist/attachmentRoute.js +23 -2
  21. package/dist/declared/supportFloorDeclared.d.ts +1 -1
  22. package/dist/declared/supportFloorDeclared.js +1 -0
  23. package/dist/deviceRebind.js +2 -3
  24. package/dist/displayUntrusted.js +126 -19
  25. package/dist/engineAddress.d.ts +1 -1
  26. package/dist/engineAddress.js +9 -6
  27. package/dist/engineErrorCodes.d.ts +3 -0
  28. package/dist/engineErrorCodes.js +4 -0
  29. package/dist/engineNoticeCodes.d.ts +34 -4
  30. package/dist/engineNoticeCodes.js +133 -6
  31. package/dist/engineUnavailable.d.ts +4 -2
  32. package/dist/engineUnavailable.js +99 -17
  33. package/dist/fleet/fleetLedger.d.ts +9 -3
  34. package/dist/fleet/fleetLedger.js +112 -16
  35. package/dist/fleet/fleetProjection.d.ts +5 -0
  36. package/dist/fleet/fleetProjection.js +9 -1
  37. package/dist/gateVocabulary.d.ts +7 -0
  38. package/dist/gateVocabulary.js +40 -14
  39. package/dist/generated/engineFactTables.d.ts +4 -2
  40. package/dist/generated/engineFactTables.js +198 -2
  41. package/dist/generated/engineNoticeTables.js +56 -8
  42. package/dist/generated/settingsSchemaFns.d.ts +7 -0
  43. package/dist/generated/settingsSchemaFns.js +11 -0
  44. package/dist/generated/settingsSchemaHash.d.ts +1 -0
  45. package/dist/generated/settingsSchemaHash.js +58 -0
  46. package/dist/generated/settingsSchemaOwnKeyRead.js +6 -1
  47. package/dist/generated/settingsSchemaTables.d.ts +3 -2
  48. package/dist/generated/settingsSchemaTables.js +14 -0
  49. package/dist/generated/settingsSchemaTightenVerdict.d.ts +1 -0
  50. package/dist/generated/settingsSchemaTightenVerdict.js +125 -0
  51. package/dist/headlessPermissionModeWire.d.ts +7 -2
  52. package/dist/headlessPermissionModeWire.js +77 -8
  53. package/dist/hitl/approvalDecisionDoor.d.ts +24 -0
  54. package/dist/hitl/approvalDecisionDoor.js +77 -0
  55. package/dist/hitl/approvalDelivery.d.ts +6 -0
  56. package/dist/hitl/approvalDelivery.js +57 -7
  57. package/dist/hitl/approvalResolution.d.ts +6 -3
  58. package/dist/hitl/approvalResolution.js +24 -2
  59. package/dist/hitl/approvalsFeed.js +1 -1
  60. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  61. package/dist/hitl/decisionSend.d.ts +7 -4
  62. package/dist/hitl/decisionSend.js +90 -41
  63. package/dist/hitl/frameRouter.d.ts +3 -3
  64. package/dist/hitl/frameRouter.js +5 -0
  65. package/dist/hitl/hitlHostSurface.d.ts +3 -2
  66. package/dist/hitl/hitlHostSurface.js +5 -3
  67. package/dist/hitl/legacyApprovalShapes.d.ts +142 -0
  68. package/dist/hitl/legacyApprovalShapes.js +1 -0
  69. package/dist/hitl/livePendingAsk.d.ts +4 -4
  70. package/dist/hitl/livePendingAsk.js +5 -3
  71. package/dist/hitl/parkResolver.js +1 -1
  72. package/dist/hitl/persistedRulesWire.js +2 -2
  73. package/dist/hitl/planReviewCard.d.ts +2 -1
  74. package/dist/hitl/planReviewCard.js +64 -1
  75. package/dist/hitl/planReviewWire.d.ts +30 -0
  76. package/dist/hitl/planReviewWire.js +204 -65
  77. package/dist/hitl/sessionPolicyWire.js +16 -7
  78. package/dist/hitl/subagentSource.d.ts +1 -0
  79. package/dist/hitl/subagentSource.js +11 -0
  80. package/dist/hitl/toolApprovalWire.d.ts +34 -10
  81. package/dist/hitl/toolApprovalWire.js +182 -33
  82. package/dist/hookMatcherVerdict.d.ts +2 -1
  83. package/dist/hookMatcherVerdict.js +3 -2
  84. package/dist/hooksReaderGenerations.d.ts +8 -0
  85. package/dist/hooksReaderGenerations.js +49 -0
  86. package/dist/hooksWireCaps.js +43 -18
  87. package/dist/hostSettingsSource.d.ts +1 -1
  88. package/dist/hostSettingsSource.js +2 -2
  89. package/dist/hostTurnSettings.d.ts +2 -2
  90. package/dist/hostTurnSettings.js +87 -47
  91. package/dist/humanTurn.d.ts +2 -1
  92. package/dist/humanTurn.js +44 -13
  93. package/dist/humanTurnRead.d.ts +7 -0
  94. package/dist/humanTurnRead.js +23 -14
  95. package/dist/humanTurnSentences.js +3 -0
  96. package/dist/i18n/messages.en.d.ts +91 -5
  97. package/dist/i18n/messages.en.js +98 -12
  98. package/dist/i18n/messages.zh.js +108 -15
  99. package/dist/index.d.ts +29 -15
  100. package/dist/index.js +17 -9
  101. package/dist/liveModelCatalog.js +1 -0
  102. package/dist/liveQuestionStore.d.ts +2 -0
  103. package/dist/liveQuestionStore.js +7 -2
  104. package/dist/localConfig/kernel.d.ts +2 -1
  105. package/dist/localConfig/kernel.js +12 -18
  106. package/dist/localConfig/mcp/edit.js +25 -27
  107. package/dist/localConfig/mcp/servers.js +5 -8
  108. package/dist/localConfig/modelPool.d.ts +6 -1
  109. package/dist/localConfig/modelPool.js +104 -27
  110. package/dist/localConfig/modelPoolWords.d.ts +2 -0
  111. package/dist/localConfig/modelPoolWords.js +1 -0
  112. package/dist/localConfig/settingsWrite.js +1 -14
  113. package/dist/localConfig/skills.js +9 -16
  114. package/dist/localEngineSpawn.d.ts +29 -1
  115. package/dist/localEngineSpawn.js +62 -1
  116. package/dist/mcpLiveness.d.ts +1 -1
  117. package/dist/mcpProbeWire.js +2 -2
  118. package/dist/mcpReconnect.js +2 -2
  119. package/dist/model/catalogLoader.js +3 -17
  120. package/dist/model/onboardJudgement.d.ts +8 -0
  121. package/dist/model/onboardJudgement.js +8 -3
  122. package/dist/model/onboardSteps.d.ts +19 -0
  123. package/dist/model/onboardSteps.js +76 -0
  124. package/dist/notifications.js +6 -0
  125. package/dist/ownKey.d.ts +4 -0
  126. package/dist/ownKey.js +61 -0
  127. package/dist/pathShape.d.ts +14 -0
  128. package/dist/pathShape.js +44 -0
  129. package/dist/planReviewCardFacts.d.ts +3 -0
  130. package/dist/planReviewCardFacts.js +20 -3
  131. package/dist/pluginHooksWire.d.ts +20 -8
  132. package/dist/pluginHooksWire.js +204 -86
  133. package/dist/prepareRefusal.d.ts +2 -0
  134. package/dist/prepareRefusal.js +11 -1
  135. package/dist/request/taskRequest.js +3 -1
  136. package/dist/resultFrameKeys.d.ts +1 -0
  137. package/dist/resultFrameKeys.js +3 -0
  138. package/dist/resumeRefusalCopy.js +3 -1
  139. package/dist/retryStatus.d.ts +11 -1
  140. package/dist/retryStatus.js +15 -4
  141. package/dist/runTerminal.d.ts +1 -0
  142. package/dist/runTerminal.js +15 -1
  143. package/dist/sdkWireTransit.d.ts +1 -1
  144. package/dist/seatContract.d.ts +12 -3
  145. package/dist/seatContract.js +24 -13
  146. package/dist/sessionEngine.js +1 -1
  147. package/dist/sessionHooksRoster.js +8 -6
  148. package/dist/sessionIdSegment.d.ts +0 -1
  149. package/dist/sessionIdSegment.js +1 -3
  150. package/dist/sessionMemoryErase.js +2 -2
  151. package/dist/sessionMemoryStatus.js +2 -1
  152. package/dist/sessionOccupant.d.ts +29 -0
  153. package/dist/sessionOccupant.js +78 -0
  154. package/dist/settingsHooksSalvage.d.ts +4 -1
  155. package/dist/settingsHooksSalvage.js +22 -9
  156. package/dist/skillOverrideChain.d.ts +3 -0
  157. package/dist/skillOverrideChain.js +31 -4
  158. package/dist/skillsWireCaps.d.ts +16 -2
  159. package/dist/skillsWireCaps.js +47 -20
  160. package/dist/subagent/agentStateDetail.js +32 -8
  161. package/dist/subagent/engineCompactWire.js +48 -36
  162. package/dist/subagent/engineDelegatedPrompt.d.ts +3 -2
  163. package/dist/subagent/engineDelegatedPrompt.js +65 -28
  164. package/dist/subagentEditOutcomes.d.ts +9 -0
  165. package/dist/subagentEditOutcomes.js +43 -0
  166. package/dist/supportFloor.d.ts +2 -1
  167. package/dist/supportFloor.js +1 -1
  168. package/dist/toolResult.js +4 -2
  169. package/dist/toolRoster.d.ts +1 -0
  170. package/dist/toolRoster.js +1 -0
  171. package/dist/usageAnalytics.js +2 -1
  172. package/dist/userAttachmentEnvelope.d.ts +11 -0
  173. package/dist/userAttachmentEnvelope.js +7 -2
  174. package/dist/userAttachmentSeat.d.ts +7 -1
  175. package/dist/userAttachmentSeat.js +44 -7
  176. package/dist/wiringInstructionsAttribution.js +8 -2
  177. package/docs/INTEGRATION-CLIENTS.md +1588 -16
  178. package/package.json +5 -5
@@ -6,11 +6,13 @@ import type { ApprovalDenySettledBy, ToolApprovalRespondAckView, ToolApprovalRes
6
6
  import { type DecisionRefusalReading, type RefusalRouteContext } from './decisionRefusal.js';
7
7
  import { type DeliveredReading, type LadderSafetyStop } from './attributionLadder.js';
8
8
  import { type CheckpointRowVerdictReading } from '../checkpointRowVerdict.js';
9
+ import type { UiLanguage } from '../env/uiLanguage.js';
10
+ import { type ApprovalDecisionDoor } from './approvalDecisionDoor.js';
9
11
  export declare const APPROVAL_RESOLUTION_LANES: readonly ["respond", "decide", "ask"];
10
12
  export type ApprovalResolutionLane = (typeof APPROVAL_RESOLUTION_LANES)[number];
11
- export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "wrong_gate", "fetch_failed", "no_input", "card_unavailable", "safety_stop", "row_unresumable", "withdrawn"];
13
+ export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "wrong_gate", "fetch_failed", "no_input", "card_unavailable", "safety_stop", "row_unresumable", "withdrawn", "no_verb", "card_unreadable"];
12
14
  export type ApprovalNotSentCause = (typeof APPROVAL_NOT_SENT_CAUSES)[number];
13
- export declare const APPROVAL_UNSETTLED_CAUSES: readonly ["engine_refused", "engine_error", "no_answer", "transport_exhausted", "parking_in_progress", "interrupted", "orchestration", "unreadable"];
15
+ export declare const APPROVAL_UNSETTLED_CAUSES: readonly ["engine_refused", "engine_error", "no_answer", "transport_exhausted", "parking_in_progress", "interrupted", "orchestration", "unreadable", "route_missing"];
14
16
  export type ApprovalUnsettledCause = (typeof APPROVAL_UNSETTLED_CAUSES)[number];
15
17
  export interface ApprovalDenyFacts {
16
18
  readonly settledBy?: ApprovalDenySettledBy;
@@ -45,6 +47,7 @@ export interface ApprovalResolutionNotSent extends ApprovalResolutionBase {
45
47
  readonly kind: 'ok';
46
48
  }>;
47
49
  readonly toldBy?: 'host';
50
+ readonly door?: ApprovalDecisionDoor;
48
51
  readonly detail?: string;
49
52
  }
50
53
  export interface ApprovalResolutionUnsettled extends ApprovalResolutionBase {
@@ -61,7 +64,7 @@ export declare function approvalNotSentCause(r: ApprovalResolution): ApprovalNot
61
64
  export declare function approvalUnsettledCause(r: ApprovalResolution): ApprovalUnsettledCause | undefined;
62
65
  export declare const APPROVAL_NOTE_RETRACTED_DETAIL = "the card was retracted (this host no longer has a decision port for it)";
63
66
  export declare const APPROVAL_NOTE_EDIT_REFUSED_DETAIL: string;
64
- export declare function approvalResolutionDetail(r: ApprovalResolution): string | undefined;
67
+ export declare function approvalResolutionDetail(r: ApprovalResolution, lang?: UiLanguage): string | undefined;
65
68
  export declare function approvalResolutionStatusTag(r: ApprovalResolution): string;
66
69
  export declare function unsettledCauseOfReading(reading: DecisionRefusalReading): 'engine_refused' | 'engine_error' | 'no_answer';
67
70
  export interface DecisionFailureMint {
@@ -4,8 +4,10 @@ import { isSlotCarrier, thrownIsInstanceOf } from '../wireFailureShape.js';
4
4
  import { carrierReadOf, isLocalSafetyStop, localCodeReadOf, topCauseReadOf } from './attributionLadder.js';
5
5
  import { directFailureReadingOf, transportReadingOf } from './approvalDelivery.js';
6
6
  import { ownReadingSlot } from '../engineWireFor.js';
7
+ import { ROUTE_NOT_FOUND } from '../engineErrorCodes.js';
7
8
  import { CHECKPOINT_ROW_BAR_SENTENCE, unboundWayOutContent } from '../checkpointRowVerdict.js';
8
9
  import { msg, renderMessage } from '../i18n/translate.js';
10
+ import { ASK_DECISION_DOOR_SERVER_FROM } from './approvalDecisionDoor.js';
9
11
  export const APPROVAL_RESOLUTION_LANES = Object.freeze(['respond', 'decide', 'ask']);
10
12
  export const APPROVAL_NOT_SENT_CAUSES = Object.freeze([
11
13
  'retracted',
@@ -18,6 +20,8 @@ export const APPROVAL_NOT_SENT_CAUSES = Object.freeze([
18
20
  'safety_stop',
19
21
  'row_unresumable',
20
22
  'withdrawn',
23
+ 'no_verb',
24
+ 'card_unreadable',
21
25
  ]);
22
26
  export const APPROVAL_UNSETTLED_CAUSES = Object.freeze([
23
27
  'engine_refused',
@@ -28,6 +32,7 @@ export const APPROVAL_UNSETTLED_CAUSES = Object.freeze([
28
32
  'interrupted',
29
33
  'orchestration',
30
34
  'unreadable',
35
+ 'route_missing',
31
36
  ]);
32
37
  export function toolPermissionSettledElsewhereOf(first, sent) {
33
38
  return Object.freeze({ firstDecision: first, contrary: (first === 'deny') !== (sent === 'deny') });
@@ -57,6 +62,8 @@ const NOT_SENT_SENTENCES = Object.freeze({
57
62
  card_unavailable: 'no approval card could be shown on this host; nothing was sent',
58
63
  safety_stop: 'the decision was stopped by a local safety check before it was sent; nothing was sent',
59
64
  row_unresumable: CHECKPOINT_ROW_BAR_SENTENCE,
65
+ no_verb: renderMessage(msg('approval.notSent.noVerb')),
66
+ card_unreadable: renderMessage(msg('approval.notSent.cardUnreadable')),
60
67
  });
61
68
  function nonEmpty(v) {
62
69
  return typeof v === 'string' && v.length > 0 ? v : undefined;
@@ -72,7 +79,7 @@ function ownStr(o, key) {
72
79
  return undefined;
73
80
  }
74
81
  }
75
- export function approvalResolutionDetail(r) {
82
+ export function approvalResolutionDetail(r, lang) {
76
83
  let kind;
77
84
  try {
78
85
  kind = typeof r === 'object' && r !== null ? r.kind : undefined;
@@ -80,8 +87,20 @@ export function approvalResolutionDetail(r) {
80
87
  catch {
81
88
  return undefined;
82
89
  }
90
+ const cause = kind === 'not_sent' || kind === 'unsettled' ? ownStr(r, 'cause') : undefined;
91
+ if (kind === 'not_sent' && cause === 'no_verb') {
92
+ return ownStr(r, 'door') === 'respond'
93
+ ? renderMessage(msg('approval.notSent.noVerbOldServer', { version: ASK_DECISION_DOOR_SERVER_FROM }), lang)
94
+ : renderMessage(msg('approval.notSent.noVerb'), lang);
95
+ }
96
+ if (kind === 'unsettled' && cause === 'route_missing')
97
+ return renderMessage(msg('approval.routeMissing.line'), lang);
98
+ if (kind === 'not_sent' && cause === 'card_unreadable')
99
+ return renderMessage(msg('approval.notSent.cardUnreadable'), lang);
100
+ return approvalResolutionDataFaceDetail(r, kind, cause);
101
+ }
102
+ function approvalResolutionDataFaceDetail(r, kind, cause) {
83
103
  if (kind === 'not_sent') {
84
- const cause = ownStr(r, 'cause');
85
104
  if (cause === undefined || !Object.hasOwn(NOT_SENT_SENTENCES, cause))
86
105
  return undefined;
87
106
  const sentence = NOT_SENT_SENTENCES[cause];
@@ -167,6 +186,9 @@ export function resolutionOfDecisionFailure(e, m) {
167
186
  return { kind: 'unsettled', ...base, cause: 'engine_refused', safetyCode: 'binding_mismatch', refusal, ...detail };
168
187
  if (ownReadingSlot(m, 'transportExhausted') === true || thrownIsInstanceOf(e, DecideTransportRetryExhaustedError))
169
188
  return { kind: 'unsettled', ...base, cause: 'transport_exhausted', refusal, ...detail };
189
+ if (refusal.errorCode === ROUTE_NOT_FOUND && ownReadingSlot(m, 'afterRetry') !== true && ownReadingSlot(refusal, 'standing') !== 'ambiguous') {
190
+ return { kind: 'unsettled', ...base, cause: 'route_missing', refusal, ...detail };
191
+ }
170
192
  return { kind: 'unsettled', ...base, cause: unsettledCauseOfReading(refusal), refusal, ...detail };
171
193
  }
172
194
  export function directSafetyStopOf(e) {
@@ -69,7 +69,7 @@ function digestOf(rows, live, dropped = 0, droppedDurable = 0, peer = []) {
69
69
  const liveDigest = live === undefined
70
70
  ? 'absent'
71
71
  : live
72
- .map(r => [r.approvalId, r.toolName, String(r.expiresAtMs), r.sessionId ?? '', r.originTaskId ?? '', r.frame !== undefined ? 'f' : ''].join(LIVE_FIELD_SEP))
72
+ .map(r => [r.approvalId, r.toolName, String(r.expiresAtMs), r.sessionId ?? '', r.sourceSessionId ?? '', r.frame !== undefined ? 'f' : ''].join(LIVE_FIELD_SEP))
73
73
  .sort()
74
74
  .join(LIVE_ROW_SEP);
75
75
  return `${durableDigestOf(rows)}${LIVE_SECTION_SEP}${liveDigest}${LIVE_SECTION_SEP}${dropped}${LIVE_SECTION_SEP}${droppedDurable}${LIVE_SECTION_SEP}${peerDigestOf(peer)}`;
@@ -22,7 +22,7 @@ export type AutomaticAnswerVerdict = {
22
22
  readonly kind: 'unknown';
23
23
  readonly why: AutomaticAnswerUnknownWhy;
24
24
  };
25
- export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "target_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "org_rule" | "rule_unreadable" | "ask_rule" | "denial_limit_fallback" | "classifier_no_ruling" | "requires_user_interaction" | "cross_boundary" | "operator_forced" | "shell_floor" | "probe" | "write_protection_on_disk" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", CoreModeArbitrationRow>>;
25
+ export declare const MODE_ARBITRATION: Readonly<Record<"mode" | "hook" | "write_protection" | "target_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "org_rule" | "rule_unreadable" | "ask_rule" | "denial_limit_fallback" | "classifier_no_ruling" | "requires_user_interaction" | "cross_boundary" | "operator_forced" | "shell_floor" | "probe" | "write_protection_on_disk" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", CoreModeArbitrationRow>>;
26
26
  export declare function foldModeArbitrationRow(classes: Iterable<string>, askFace?: AutomaticAnswerAskFace): CoreModeArbitrationRow;
27
27
  export declare function askLevelVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
28
28
  export declare function automaticAnswerVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
@@ -5,12 +5,14 @@ import { type ToolPermissionAck } from '../seatContract.js';
5
5
  import { type AskGoneDetail } from '../decideReceipt.js';
6
6
  import type { ApprovalDecidedByReading } from '../approvalDecidedByCapability.js';
7
7
  import { type DecisionNoteAudit } from './approvalDecisionNoteAudit.js';
8
+ import type { LegacyToolApprovalRespondVerb } from './legacyApprovalShapes.js';
9
+ import { hitlAskIdempotencyKey, type ApprovalDecisionDoor, type ApprovalDecisionDoorReading } from './approvalDecisionDoor.js';
8
10
  import { type DecisionRefusalClassified, type LiveAskPendingReading } from './decisionRefusal.js';
9
11
  import { type HostApprovalCardHold } from './approvalHolds.js';
10
12
  import { type ApprovalCardDecision, type ToolApprovalRespondAckView, type ToolApprovalRespondParkingView } from './toolApprovalWire.js';
11
13
  export declare const HITL_DECISION_FAMILIES: readonly ["approval", "ask", "elicitation"];
12
14
  export type HitlDecisionFamily = (typeof HITL_DECISION_FAMILIES)[number];
13
- export declare const HITL_DECISION_NOT_APPLIED_CAUSES: readonly ["gone", "parked", "parking", "lane_disabled", "refused", "idempotency_crossed"];
15
+ export declare const HITL_DECISION_NOT_APPLIED_CAUSES: readonly ["gone", "parked", "parking", "lane_disabled", "refused", "idempotency_crossed", "route_missing"];
14
16
  export type HitlDecisionNotAppliedCause = (typeof HITL_DECISION_NOT_APPLIED_CAUSES)[number];
15
17
  export declare const HITL_DECISION_UNCONFIRMED_CAUSES: readonly ["transport", "server_error", "local_error", "aborted", "gone_after_retry"];
16
18
  export type HitlDecisionUnconfirmedCause = (typeof HITL_DECISION_UNCONFIRMED_CAUSES)[number];
@@ -45,7 +47,7 @@ export interface HitlElicitationDecisionRequest {
45
47
  }
46
48
  export type HitlDecisionSendRequest = HitlApprovalDecisionRequest | HitlAskDecisionRequest | HitlElicitationDecisionRequest;
47
49
  export interface HitlDecisionSendClient {
48
- readonly toolApprovals?: Partial<Pick<AgentClient['toolApprovals'], 'respond' | 'decideAsk'>>;
50
+ readonly toolApprovals?: Partial<LegacyToolApprovalRespondVerb & Pick<AgentClient['toolApprovals'], 'decideAsk'>>;
49
51
  readonly elicitations?: Partial<Pick<AgentClient['elicitations'], 'respond'>>;
50
52
  }
51
53
  export interface HitlDecisionGate {
@@ -60,6 +62,7 @@ export interface HitlDecisionSendDeps {
60
62
  readonly signal?: AbortSignal;
61
63
  readonly approvalDecisionNoteCapable?: boolean;
62
64
  readonly approvalDecidedBy?: ApprovalDecidedByReading;
65
+ readonly approvalDecisionDoor?: ApprovalDecisionDoorReading;
63
66
  readonly readPending?: (gate: HitlDecisionGate, opts: {
64
67
  readonly signal: AbortSignal;
65
68
  }) => Promise<LiveAskPendingReading>;
@@ -111,6 +114,7 @@ export interface HitlDecisionUnconfirmed extends HitlDecisionOutcomeBase {
111
114
  export interface HitlDecisionNotSent extends HitlDecisionOutcomeBase {
112
115
  readonly kind: 'not_sent';
113
116
  readonly cause: HitlDecisionNotSentCause;
117
+ readonly door?: ApprovalDecisionDoor;
114
118
  }
115
119
  export type HitlDecisionSendOutcome = HitlDecisionApplied | HitlDecisionSettledElsewhere | HitlDecisionNotApplied | HitlDecisionUnconfirmed | HitlDecisionNotSent;
116
120
  export declare const HITL_DECISION_READBACK_BUDGET_MS = 4000;
@@ -120,10 +124,9 @@ export interface HitlDecisionSendTiming {
120
124
  readonly parkingDelayMs?: number;
121
125
  }
122
126
  export declare function __setHitlDecisionSendTimingForTests(t?: HitlDecisionSendTiming): void;
123
- export declare function hitlAskIdempotencyKey(askId: string, verdict: 'approve' | 'deny'): string;
127
+ export { hitlAskIdempotencyKey };
124
128
  export declare function sendHitlDecision(request: HitlDecisionSendRequest, deps: HitlDecisionSendDeps): Promise<HitlDecisionSendOutcome>;
125
129
  export declare function toolPermissionSettledLine(fact: ToolPermissionSettledElsewhere | null | undefined, lang?: UiLanguage): string | undefined;
126
130
  export declare function toolPermissionUnconfirmedLine(ack: ToolPermissionAck | null | undefined, lang?: UiLanguage): string | undefined;
127
131
  export declare function toolPermissionUnconfirmedOf(thrown: {} | null | undefined, signal?: AbortSignal): ToolPermissionUnconfirmed | undefined;
128
132
  export declare function hitlDecisionOutcomeContent(outcome: HitlDecisionSendOutcome, lang?: UiLanguage): string | undefined;
129
- export {};
@@ -6,40 +6,28 @@ import { msg, renderMessage } from '../i18n/translate.js';
6
6
  import { askGoneFromError } from '../decideReceipt.js';
7
7
  import { appendOwnElement, guardedRead, readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
8
8
  import { effectiveCodeOf, ownReadingSlot } from '../engineWireFor.js';
9
- import { ASK_DECISION_LANE_DISABLED, ASK_GONE, ASK_GONE_PARKED, ASK_IDEMPOTENCY_CONFLICT, ASK_NOT_FOUND, ASK_PARKING_RETRY } from '../engineErrorCodes.js';
10
- import { isSlotCarrier, readErrorNumber, readErrorString, readThrownSlot } from '../wireFailureShape.js';
9
+ import { ASK_DECISION_LANE_DISABLED, ASK_GONE, ASK_GONE_PARKED, ASK_IDEMPOTENCY_CONFLICT, ASK_NOT_FOUND, ASK_PARKING_RETRY, ROUTE_NOT_FOUND } from '../engineErrorCodes.js';
10
+ import { readErrorNumber, readErrorString, readThrownSlot } from '../wireFailureShape.js';
11
11
  import { readDecisionNoteAudit } from './approvalDecisionNoteAudit.js';
12
+ import { approvalDecisionDoorOf, ASK_DECISION_DOOR_SERVER_FROM, ROUTE_MISSING_WIRING_HINT, hitlAskIdempotencyKey, noteRespondDoorSwitch } from './approvalDecisionDoor.js';
12
13
  import { carrierReadOf, deliverWithAttributionLadder, isLocalSafetyStop } from './attributionLadder.js';
13
- import { budgetSpent, deliverApprovalDecision, deliveryTiming, isAborted, newSendTally, PARKING_WAIT_CAP_MS, setDeliveryTimingForTests, sleepFor, settledTransportReadingOf, withTransportRetry, deliveryReadingOf, } from './approvalDelivery.js';
14
+ import { budgetSpent, deliverApprovalDecision, retryAfterMsOf, deliveryTiming, isAborted, newSendTally, PARKING_WAIT_CAP_MS, respondRouteGoneOf, setDeliveryTimingForTests, sleepFor, settledTransportReadingOf, withTransportRetry, deliveryReadingOf, } from './approvalDelivery.js';
14
15
  import { ASK_DECIDED_CODE, classifyDeliveredDecisionRefusal, decisionRefusalClosesOut, notPendingAfterRetry } from './decisionRefusal.js';
15
16
  import { sessionAllowEligible } from './sessionAllowRoute.js';
16
17
  import { addDecisionOpKeys, closeDecisionOp, decisionLedgerKeysOf, decisionOpsWithKey, openDecisionOp } from './decisionLedger.js';
17
18
  import { hostHoldIsActive, takeHostHoldLedgerOp, tryHoldApprovalNow } from './approvalHolds.js';
18
- import { allowNoteReadingOf, answerPayloadFor, decisionAttributionKeyOf, readApprovalDenySettledBy, readToolApprovalRespondAck, readToolApprovalRespondParking, replayVerdictFrom, settlementAckOf, } from './toolApprovalWire.js';
19
+ import { allowNoteReadingOf, answerPayloadFor, askDoorBodyOf, decisionAttributionKeyOf, readApprovalDenySettledBy, readAskDoorDecisionBody, readToolApprovalRespondAck, readToolApprovalRespondParking, replayVerdictFrom, settlementAckOf, } from './toolApprovalWire.js';
19
20
  export const HITL_DECISION_FAMILIES = Object.freeze(['approval', 'ask', 'elicitation']);
20
- export const HITL_DECISION_NOT_APPLIED_CAUSES = Object.freeze(['gone', 'parked', 'parking', 'lane_disabled', 'refused', 'idempotency_crossed']);
21
+ export const HITL_DECISION_NOT_APPLIED_CAUSES = Object.freeze(['gone', 'parked', 'parking', 'lane_disabled', 'refused', 'idempotency_crossed', 'route_missing']);
21
22
  export const HITL_DECISION_UNCONFIRMED_CAUSES = Object.freeze(['transport', 'server_error', 'local_error', 'aborted', 'gone_after_retry']);
22
23
  export const HITL_DECISION_NOT_SENT_CAUSES = Object.freeze(['aborted', 'duplicate_in_flight', 'invalid_request', 'no_verb', 'not_an_answer']);
23
24
  const PARKING_POLL_MAX = 2;
24
25
  const PARKING_POLL_DELAY_MS = 400;
25
26
  export const HITL_DECISION_READBACK_BUDGET_MS = 4_000;
26
- const IDEMPOTENCY_KEY_MAX = 255;
27
27
  export function __setHitlDecisionSendTimingForTests(t) {
28
28
  setDeliveryTimingForTests(t);
29
29
  }
30
- export function hitlAskIdempotencyKey(askId, verdict) {
31
- const key = `sema-ask:${askId}:${verdict}`;
32
- return key.length <= IDEMPOTENCY_KEY_MAX ? key : key.slice(0, IDEMPOTENCY_KEY_MAX);
33
- }
34
- function retryAfterMsOf(e) {
35
- if (!isSlotCarrier(e))
36
- return undefined;
37
- const ms = readErrorNumber(e, 'retryAfterMs');
38
- if (ms !== undefined && Number.isFinite(ms) && ms >= 0)
39
- return ms;
40
- const sec = readErrorNumber(e, 'retryAfterSec');
41
- return sec !== undefined && Number.isFinite(sec) && sec >= 0 ? sec * 1000 : undefined;
42
- }
30
+ export { hitlAskIdempotencyKey };
43
31
  function actorLabelOf(e) {
44
32
  const actor = readThrownSlot(e, 'actor', 'data');
45
33
  if (typeof actor !== 'object' || actor === null)
@@ -79,6 +67,11 @@ function answeredOutcome(e, status, reading, cls, family, ours, tally, noteCapab
79
67
  return { ...base, kind: 'unconfirmed', cause: 'gone_after_retry', status };
80
68
  if (isSafetyStopLayer(e, reading))
81
69
  return { ...at, kind: 'not_applied', cause: 'refused', ...refusalOf(e, reading) };
70
+ if (code === ROUTE_NOT_FOUND) {
71
+ if (tally.retried)
72
+ return { ...base, kind: 'unconfirmed', cause: 'gone_after_retry', status };
73
+ return { ...at, kind: 'not_applied', cause: 'route_missing' };
74
+ }
82
75
  if (family === 'ask') {
83
76
  switch (code) {
84
77
  case ASK_DECIDED_CODE: {
@@ -131,7 +124,7 @@ function refusalOf(e, reading) {
131
124
  const r = classifyDeliveredDecisionRefusal(e, reading);
132
125
  return r !== undefined ? { refusal: r } : {};
133
126
  }
134
- async function sendApprovalFamily(respond, approvalId, answer, ctx) {
127
+ async function sendApprovalFamily(respond, approvalId, answer, ctx, evidence) {
135
128
  const payload = answerPayloadFor(answer, 'respond', { approvalDecisionNoteCapable: ctx.noteCapable });
136
129
  if (payload === undefined)
137
130
  return { family: 'approval', attempts: ctx.tally.attempts, kind: 'not_sent', cause: 'not_an_answer' };
@@ -153,8 +146,11 @@ async function sendApprovalFamily(respond, approvalId, answer, ctx) {
153
146
  },
154
147
  });
155
148
  const retriedFact = ownReadingSlot(delivery, 'parkingRetried') === true ? { parkingRetried: true } : {};
156
- if (delivery.kind === 'thrown')
149
+ if (delivery.kind === 'thrown') {
150
+ if (evidence !== undefined)
151
+ evidence.routeGone = respondRouteGoneOf(delivery);
157
152
  return { ...outcomeOfThrown(delivery.error, 'approval', ours, ctx.signal, ctx.tally, ctx.noteCapable), ...extras, ...retriedFact };
153
+ }
158
154
  const raw = delivery.raw;
159
155
  const facts = { ...extras, ...retriedFact, ...(ladder.dropped ? { attributionDropped: true } : {}), ...(ladder.renamed ? { attributionRenamed: true } : {}) };
160
156
  const base = { family: 'approval', attempts: ctx.tally.attempts, ...facts };
@@ -189,30 +185,40 @@ function bodyFacts(answer, payload, rememberUnsupported) {
189
185
  };
190
186
  }
191
187
  function answerGateCannotTake(out) {
192
- return out.kind === 'not_applied' && (out.cause === 'gone' || out.cause === 'parking' || out.cause === 'lane_disabled');
188
+ return out.kind === 'not_applied' && (out.cause === 'gone' || out.cause === 'parking' || out.cause === 'lane_disabled' || out.cause === 'route_missing');
193
189
  }
194
- async function sendAskFamily(respondForFallback, decideAsk, req, ctx) {
190
+ async function sendAskFamily(respondForFallback, decideAsk, req, ctx, family = 'ask') {
195
191
  const payload = answerPayloadFor(req.answer, 'respond', { approvalDecisionNoteCapable: ctx.noteCapable });
196
192
  if (payload === undefined)
197
- return { family: 'ask', attempts: ctx.tally.attempts, kind: 'not_sent', cause: 'not_an_answer' };
198
- let fallbackRespond = respondForFallback;
199
- if (req.answer.kind === 'allow' && req.answer.allowSession === true && respondForFallback !== undefined && req.approvalId !== undefined) {
200
- const viaAnswerGate = await sendApprovalFamily(respondForFallback, req.approvalId, req.answer, ctx);
193
+ return { family, attempts: ctx.tally.attempts, kind: 'not_sent', cause: 'not_an_answer' };
194
+ let onAskDoor = ctx.door === 'ask_decision';
195
+ let fallbackRespond = onAskDoor ? undefined : respondForFallback;
196
+ if (!onAskDoor && req.answer.kind === 'allow' && req.answer.allowSession === true && respondForFallback !== undefined && req.approvalId !== undefined) {
197
+ const gate = { routeGone: false };
198
+ const viaAnswerGate = await sendApprovalFamily(respondForFallback, req.approvalId, req.answer, ctx, gate);
201
199
  if (!answerGateCannotTake(viaAnswerGate))
202
200
  return { ...viaAnswerGate, family: 'ask', allowSessionCarried: true };
203
- hostLog('debug', `decisionSend: session-allow-fallback ask=${req.askId} cause=${String(viaAnswerGate.cause)} via=ask-decide-once`);
201
+ if (gate.routeGone) {
202
+ onAskDoor = true;
203
+ noteRespondDoorSwitch(ctx.doorReading, 'decisionSend', req.askId);
204
+ if (isAborted(ctx.signal))
205
+ return { ...viaAnswerGate, family: 'ask' };
206
+ }
207
+ else {
208
+ hostLog('debug', `decisionSend: session-allow-fallback ask=${req.askId} cause=${String(viaAnswerGate.cause)} via=ask-decide-once`);
209
+ }
204
210
  fallbackRespond = undefined;
205
211
  }
206
212
  const verdict = verdictOf(payload.decision) ?? 'deny';
207
213
  const attribution = req.answer.kind === 'deny' ? decisionAttributionKeyOf(ctx.decidedBy, readApprovalDenySettledBy(req.answer)) : {};
208
- const body = {
209
- decision: verdict,
210
- idempotencyKey: hitlAskIdempotencyKey(req.askId, verdict),
211
- ...(verdict === 'approve' && payload.updatedInput !== undefined ? { updatedInput: payload.updatedInput } : {}),
214
+ const carriesRemember = onAskDoor && payload.decision === 'allow_session';
215
+ const wireWord = payload.decision === 'allow_session' && !carriesRemember ? 'allow' : payload.decision;
216
+ const body = askDoorBodyOf(wireWord, {
217
+ ...(payload.updatedInput !== undefined ? { updatedInput: payload.updatedInput } : {}),
212
218
  ...(payload.note !== undefined ? { note: payload.note } : {}),
213
219
  ...attribution,
214
- };
215
- const extras = bodyFacts(req.answer, payload, true);
220
+ }, req.askId);
221
+ const extras = bodyFacts(req.answer, payload, !onAskDoor);
216
222
  const ladder = { lastCarried: false, dropped: false, renamed: false };
217
223
  let current = body;
218
224
  const parkingDelay = deliveryTiming().parkingDelayMs ?? PARKING_POLL_DELAY_MS;
@@ -222,13 +228,22 @@ async function sendAskFamily(respondForFallback, decideAsk, req, ctx) {
222
228
  current = b;
223
229
  return withTransportRetry((signal) => decideAsk(req.taskId, req.askId, b, signal !== undefined ? { signal } : undefined), ctx.signal, ctx.tally);
224
230
  }, ctx.signal, ladder, undefined, { kind: 'delivered', readingOf: (thrown) => deliveryReadingOf(thrown, ctx.tally) });
231
+ const askDoorBody = onAskDoor ? readAskDoorDecisionBody(raw, req.askId, payload.decision) : undefined;
232
+ if (askDoorBody?.kind === 'earlier') {
233
+ return { family, attempts: ctx.tally.attempts, ...extras, kind: 'settled_elsewhere', firstDecision: askDoorBody.firstDecision === 'allow' ? 'approve' : 'deny', contrary: true };
234
+ }
235
+ if (askDoorBody?.kind === 'other_ask')
236
+ return { family, attempts: ctx.tally.attempts, ...extras, kind: 'unconfirmed', cause: 'local_error' };
237
+ const ack = askDoorBody?.kind === 'receipt' ? askDoorBody.ack : undefined;
225
238
  return {
226
- family: 'ask',
239
+ family,
227
240
  attempts: ctx.tally.attempts,
228
241
  ...extras,
229
242
  ...(ladder.dropped ? { attributionDropped: true } : {}),
230
243
  ...(ladder.renamed ? { attributionRenamed: true } : {}),
244
+ ...(carriesRemember ? { allowSessionCarried: true } : {}),
231
245
  kind: 'applied',
246
+ ...(ack !== undefined ? { ack } : {}),
232
247
  ...(ctx.noteCapable ? { noteAudit: readDecisionNoteAudit(raw) } : {}),
233
248
  };
234
249
  }
@@ -236,14 +251,14 @@ async function sendAskFamily(respondForFallback, decideAsk, req, ctx) {
236
251
  const settled = settledTransportReadingOf(e, ctx.signal, ctx.tally);
237
252
  const settledAnswer = ownReadingSlot(settled, 'answered');
238
253
  const code = settledAnswer !== undefined ? effectiveCodeOf(settledAnswer.code) : undefined;
239
- const wait = Math.max(parkingDelay * (poll + 1), retryAfterMsOf(settledAnswer?.layer ?? e) ?? 0);
254
+ const wait = Math.max(parkingDelay * (poll + 1), retryAfterMsOf((settledAnswer?.layer ?? e)) ?? 0);
240
255
  const safetyStop = settledAnswer !== undefined && isSafetyStopLayer(settledAnswer.layer, settled);
241
256
  if (code === ASK_PARKING_RETRY && !safetyStop && poll < PARKING_POLL_MAX && !isAborted(ctx.signal) && wait <= PARKING_WAIT_CAP_MS && !budgetSpent(ctx.tally, wait)) {
242
257
  await sleepFor(wait, ctx.signal);
243
258
  if (!isAborted(ctx.signal))
244
259
  continue;
245
260
  }
246
- const out = outcomeOfThrown(e, 'ask', verdict, ctx.signal, ctx.tally, ctx.noteCapable, settled);
261
+ const out = { ...outcomeOfThrown(e, 'ask', verdict, ctx.signal, ctx.tally, ctx.noteCapable, settled), family };
247
262
  if (out.kind === 'not_applied' && out.cause === 'lane_disabled' && code === ASK_DECISION_LANE_DISABLED && req.approvalId !== undefined) {
248
263
  if (fallbackRespond !== undefined && ctx.signal?.aborted !== true) {
249
264
  const fb = await sendApprovalFamily(fallbackRespond, req.approvalId, req.answer, ctx);
@@ -459,7 +474,16 @@ export async function sendHitlDecision(request, deps) {
459
474
  const respond = verbOf(toolApprovals, 'respond');
460
475
  const decideAsk = verbOf(toolApprovals, 'decideAsk');
461
476
  const elicit = verbOf(hostField(client, 'elicitations'), 'respond');
462
- if ((req.family === 'approval' && respond === undefined) || (req.family === 'ask' && decideAsk === undefined) || (req.family === 'elicitation' && elicit === undefined))
477
+ const doorReading = hostField(deps, 'approvalDecisionDoor');
478
+ const door = approvalDecisionDoorOf(doorReading);
479
+ if (door === 'ask_decision' && req.family === 'approval' && req.taskId === undefined)
480
+ return notSent('invalid_request');
481
+ const approvalVerbMissing = door === 'ask_decision' ? decideAsk === undefined : respond === undefined;
482
+ if (req.family === 'approval' && approvalVerbMissing)
483
+ return Object.freeze({ family, attempts: 0, kind: 'not_sent', cause: 'no_verb', door });
484
+ if (req.family === 'ask' && decideAsk === undefined)
485
+ return Object.freeze({ family, attempts: 0, kind: 'not_sent', cause: 'no_verb', door: 'ask_decision' });
486
+ if (req.family === 'elicitation' && elicit === undefined)
463
487
  return notSent('no_verb');
464
488
  if (req.family !== 'elicitation' && req.answer.kind !== 'allow' && req.answer.kind !== 'deny')
465
489
  return notSent('not_an_answer');
@@ -508,11 +532,27 @@ export async function sendHitlDecision(request, deps) {
508
532
  tally: newSendTally(),
509
533
  noteCapable: hostField(deps, 'approvalDecisionNoteCapable') === true,
510
534
  decidedBy: typeof decidedBy === 'object' && decidedBy !== null ? decidedBy : undefined,
535
+ door,
536
+ doorReading,
511
537
  };
512
538
  let out;
513
539
  try {
514
- if (req.family === 'approval') {
515
- out = await sendApprovalFamily(respond, req.approvalId, req.answer, ctx);
540
+ if (req.family === 'approval' && door === 'ask_decision') {
541
+ out = await sendAskFamily(undefined, decideAsk, { taskId: req.taskId, askId: req.approvalId, approvalId: req.approvalId, answer: req.answer }, ctx, 'approval');
542
+ }
543
+ else if (req.family === 'approval') {
544
+ const ev = { routeGone: false };
545
+ out = await sendApprovalFamily(respond, req.approvalId, req.answer, ctx, ev);
546
+ if (ev.routeGone) {
547
+ if (decideAsk === undefined || req.taskId === undefined) {
548
+ hostLog('warn', ROUTE_MISSING_WIRING_HINT);
549
+ }
550
+ else {
551
+ noteRespondDoorSwitch(ctx.doorReading, 'decisionSend', req.approvalId);
552
+ if (!isAborted(signal))
553
+ out = await sendAskFamily(undefined, decideAsk, { taskId: req.taskId, askId: req.approvalId, approvalId: req.approvalId, answer: req.answer }, { ...ctx, door: 'ask_decision' }, 'approval');
554
+ }
555
+ }
516
556
  }
517
557
  else if (req.family === 'ask') {
518
558
  out = await sendAskFamily(respond, decideAsk, { taskId: req.taskId, askId: req.askId, approvalId: req.approvalId, answer: req.answer }, ctx);
@@ -554,7 +594,7 @@ export async function sendHitlDecision(request, deps) {
554
594
  }
555
595
  const notApplied = out.kind !== 'applied' &&
556
596
  (out.pending === 'pending' ||
557
- (out.kind === 'not_applied' && (out.cause === 'lane_disabled' || out.cause === 'idempotency_crossed' || (out.cause === 'refused' && out.refusal?.standing === 'refused-pending'))));
597
+ (out.kind === 'not_applied' && (out.cause === 'lane_disabled' || out.cause === 'route_missing' || out.cause === 'idempotency_crossed' || (out.cause === 'refused' && out.refusal?.standing === 'refused-pending'))));
558
598
  if (notApplied)
559
599
  op.notApplied = true;
560
600
  closeDecisionOp(op, 'package');
@@ -617,6 +657,15 @@ export function hitlDecisionOutcomeContent(outcome, lang) {
617
657
  }
618
658
  if (kind === 'unconfirmed' && readOwnDataValue(outcome, 'cause') === 'gone_after_retry')
619
659
  return unconfirmedDecisionContent('gone_after_retry', lang);
660
+ if (kind === 'not_applied' && readOwnDataValue(outcome, 'cause') === 'route_missing')
661
+ return renderMessage(msg('approval.routeMissing.line'), lang);
662
+ if (kind === 'not_sent' && readOwnDataValue(outcome, 'cause') === 'no_verb' && readOwnDataValue(outcome, 'family') === 'approval') {
663
+ const door = readOwnDataValue(outcome, 'door');
664
+ if (door === 'respond')
665
+ return renderMessage(msg('approval.decision.notSentOldServer', { version: ASK_DECISION_DOOR_SERVER_FROM }), lang);
666
+ if (door === 'ask_decision')
667
+ return renderMessage(msg('approval.decision.notSentNewerAddress'), lang);
668
+ }
620
669
  return undefined;
621
670
  }
622
671
  catch {
@@ -1,6 +1,6 @@
1
1
  import type { AgentEvent } from '@sema-agent/sdk';
2
2
  import type { DecideOperationObserver, HitlClientLike } from './hitlBridge.js';
3
- import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
3
+ import { type RespondToolApprovalFn, type LegacyToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
4
4
  import type { ApprovalDenyFacts, ApprovalResolution } from './approvalResolution.js';
5
5
  import type { GateLedger, GateDenyAttribution } from './gateLedger.js';
6
6
  import type { NoAllowCause } from './noAllowCause.js';
@@ -35,9 +35,9 @@ export interface AskGateWireDeps {
35
35
  onParkReattach?: (e: ParkReattachNotice) => void;
36
36
  onBreak?: (event: ReattachDrainingEvent) => void;
37
37
  approvalLane?: ToolApprovalFrameLaneOpts;
38
- onToolApprovalOutcome?: (frame: ToolApprovalFrame, resolution: ApprovalResolution) => void | Promise<void>;
38
+ onToolApprovalOutcome?: (frame: LegacyToolApprovalFrame, resolution: ApprovalResolution) => void | Promise<void>;
39
39
  onDecideOperation?: DecideOperationObserver;
40
- onReplayedApproval?: (frame: ToolApprovalFrame, disposition: ReplayedApprovalDisposition) => void | Promise<void>;
40
+ onReplayedApproval?: (frame: LegacyToolApprovalFrame, disposition: ReplayedApprovalDisposition) => void | Promise<void>;
41
41
  engineGeneration?: () => number;
42
42
  }
43
43
  type GateKind = 'ask' | 'fs';
@@ -23,6 +23,7 @@ import { REJECT_MESSAGE } from '../adapt/wireShapes.js';
23
23
  import { RUN_CANCELLED_CODE } from '../runCancelContext.js';
24
24
  import { durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
25
25
  import { subagentSourceSessionOf } from './subagentSource.js';
26
+ import { approvalDecisionDoorOf } from './approvalDecisionDoor.js';
26
27
  export const HITL_REJECT_MESSAGE = REJECT_MESSAGE;
27
28
  export const HITL_POLICY_DENY_MESSAGE = 'Permission for this tool use was denied: it requires interactive approval, and permission prompts are ' +
28
29
  'not available in this session. The action was NOT performed. Do not claim it succeeded, and do not retry ' +
@@ -325,6 +326,10 @@ const TOOL_END_ARMS = [
325
326
  }
326
327
  async function routeToolApprovalFrame(ev, ctx) {
327
328
  const { led, deps } = ctx;
329
+ if (approvalDecisionDoorOf(guardedRead(() => readOwnDataValue(deps.approvalLane, 'approvalDecisionDoor'))) === 'ask_decision') {
330
+ hostLog('debug', `liveHitlAskWire: ${ev.type} frame ${String(guardedRead(() => readOwnDataValue(ev, 'approvalId')))} on a server that only has the ask decision door — old-family frame ignored (a replayed history row here; not a card, nothing sent)`);
331
+ return { kind: 'skip' };
332
+ }
328
333
  if (ev.type === 'tool_approval_complete') {
329
334
  if (settleEngineClosedApproval(ev, approvalCompleteParked(ev))) {
330
335
  const id = ev.approvalId;
@@ -34,8 +34,9 @@ export declare function surfaceEditNotForwarded(): void;
34
34
  export declare const APPROVAL_PARKING_WARN_TEXT = "This approval is being parked; answer it again on the parked card when it appears.";
35
35
  export declare function approvalParkingNoticeText(retryAfterMs?: number): string;
36
36
  export declare function surfaceApprovalParking(retryAfterMs?: number): void;
37
- export declare const COMPACT_NOT_RUNNING_WARN_TEXT = "engine run finished before compaction was applied; nothing was compacted";
38
- export declare function surfaceCompactNotRunning(sessionKey?: string): void;
37
+ export declare const COMPACT_NOT_RUNNING_WARN_TEXT: string;
38
+ export declare const COMPACT_WILL_RETRY_WARN_TEXT: string;
39
+ export declare function surfaceCompactNotRunning(sessionKey: string, phase: 'will_retry' | 'final'): void;
39
40
  export declare const REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT: string;
40
41
  export declare function surfaceReplayedApprovalListUnreadable(): void;
41
42
  export declare const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT: string;
@@ -3,6 +3,7 @@ import { hostLog } from '../host.js';
3
3
  import { unrefTimer } from '../unrefTimer.js';
4
4
  import { decideFailureNote } from '../decideFailureNote.js';
5
5
  import { editedApprovalRefusedLine } from './editedApprovalAcceptance.js';
6
+ import { msg, renderMessage } from '../i18n/translate.js';
6
7
  import { decisionRefusalSaysAskGone } from './decisionRefusal.js';
7
8
  import { deliveryTiming, RETRY_BUDGET_MS } from './approvalDelivery.js';
8
9
  const hostSurfaceByKey = createSessionSlot();
@@ -97,11 +98,12 @@ export function approvalParkingNoticeText(retryAfterMs) {
97
98
  export function surfaceApprovalParking(retryAfterMs) {
98
99
  surfaceSelfClearingWarn(APPROVAL_PARKING_KEY, approvalParkingNoticeText(retryAfterMs), APPROVAL_PARKING_TIMEOUT_MS);
99
100
  }
100
- export const COMPACT_NOT_RUNNING_WARN_TEXT = 'engine run finished before compaction was applied; nothing was compacted';
101
+ export const COMPACT_NOT_RUNNING_WARN_TEXT = renderMessage(msg('session.compaction.notRunning.final'));
102
+ export const COMPACT_WILL_RETRY_WARN_TEXT = renderMessage(msg('session.compaction.notRunning.willRetry'));
101
103
  const COMPACT_NOT_RUNNING_KEY = 'engine-compact-not-running';
102
104
  const COMPACT_NOT_RUNNING_TIMEOUT_MS = 10_000;
103
- export function surfaceCompactNotRunning(sessionKey = DEFAULT_SESSION_KEY) {
104
- surfaceSelfClearingWarn(COMPACT_NOT_RUNNING_KEY, COMPACT_NOT_RUNNING_WARN_TEXT, COMPACT_NOT_RUNNING_TIMEOUT_MS, sessionKey);
105
+ export function surfaceCompactNotRunning(sessionKey, phase) {
106
+ surfaceSelfClearingWarn(COMPACT_NOT_RUNNING_KEY, phase === 'will_retry' ? COMPACT_WILL_RETRY_WARN_TEXT : COMPACT_NOT_RUNNING_WARN_TEXT, COMPACT_NOT_RUNNING_TIMEOUT_MS, sessionKey);
105
107
  }
106
108
  export const REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT = "Could not read the pending-approval list, so an approval request read back from this run's event log was not raised as a question, " +
107
109
  'and no decision was sent for it.';