@sema-agent/client-core 0.97.0 → 0.98.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.
- package/CHANGELOG.md +429 -0
- package/README.md +74 -36
- package/dist/adapt/arms.d.ts +1 -0
- package/dist/adapt/arms.js +128 -34
- package/dist/adapt/ids.d.ts +12 -1
- package/dist/adapt/ids.js +59 -8
- package/dist/adapt/instanceLedger.d.ts +50 -0
- package/dist/adapt/instanceLedger.js +147 -0
- package/dist/adapt/textStream.d.ts +1 -0
- package/dist/adapt/textStream.js +24 -7
- package/dist/adapt/toolCards.d.ts +27 -5
- package/dist/adapt/toolCards.js +39 -6
- package/dist/adapt/wireShapes.js +6 -2
- package/dist/adapt.d.ts +2 -0
- package/dist/adapt.js +8 -3
- package/dist/adapter/activeRunSelfHeal.d.ts +5 -0
- package/dist/adapter/activeRunSelfHeal.js +51 -28
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +27 -34
- package/dist/adapter/downstream/terminalToSdkResult.js +11 -4
- package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +5 -2
- package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -0
- package/dist/adapter/runStream.js +29 -13
- package/dist/adapter/types.d.ts +4 -4
- package/dist/agentSession/backgroundView.js +2 -1
- package/dist/agentSession/engineAgentRegistryRead.d.ts +2 -2
- package/dist/agentSession/engineAgentRegistryRead.js +12 -13
- package/dist/agentsWireCaps.d.ts +8 -6
- package/dist/agentsWireCaps.js +177 -162
- package/dist/approvalDecidedByCapability.d.ts +0 -4
- package/dist/approvalDecidedByCapability.js +0 -6
- package/dist/approvalsStreamLiveCapability.d.ts +0 -4
- package/dist/approvalsStreamLiveCapability.js +0 -6
- package/dist/assistantInbox.d.ts +34 -0
- package/dist/assistantInbox.js +118 -0
- package/dist/centerWiringCapability.js +9 -1
- package/dist/clientSlice.d.ts +0 -9
- package/dist/clientSlice.js +0 -3
- package/dist/cloudConfigWireCaps.d.ts +4 -2
- package/dist/cloudConfigWireCaps.js +81 -61
- package/dist/compensations.js +1 -1
- package/dist/connectorStatus.d.ts +21 -0
- package/dist/connectorStatus.js +112 -0
- package/dist/controlRouter.d.ts +14 -1
- package/dist/controlRouter.js +68 -17
- package/dist/coreValuePorts.d.ts +3 -3
- package/dist/decideReceipt.d.ts +2 -2
- package/dist/decideReceipt.js +5 -2
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/deviceExecutorManagementCapability.d.ts +0 -4
- package/dist/deviceExecutorManagementCapability.js +0 -6
- package/dist/deviceRebind.d.ts +49 -1
- package/dist/deviceRebind.js +221 -3
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +250 -76
- package/dist/engineAgentAbsence.d.ts +1 -1
- package/dist/engineAgentPanelStore.d.ts +5 -4
- package/dist/engineAgentPanelStore.js +3 -12
- package/dist/engineCapReader.d.ts +0 -4
- package/dist/engineCapReader.js +1 -5
- package/dist/engineCapsCache.d.ts +4 -0
- package/dist/engineCapsCache.js +17 -12
- package/dist/engineClosedSets.d.ts +1 -0
- package/dist/engineClosedSets.js +4 -1
- package/dist/engineErrorCodes.d.ts +9 -2
- package/dist/engineErrorCodes.js +9 -1
- package/dist/engineIdentity.d.ts +1 -0
- package/dist/engineIdentity.js +8 -0
- package/dist/engineRowRef.d.ts +28 -0
- package/dist/engineRowRef.js +111 -0
- package/dist/engineSessionMessages.d.ts +67 -0
- package/dist/engineSessionMessages.js +364 -0
- package/dist/engineToolDirectory.d.ts +18 -0
- package/dist/engineToolDirectory.js +85 -0
- package/dist/engineUrl.d.ts +18 -0
- package/dist/engineUrl.js +52 -0
- package/dist/env/uiLanguage.d.ts +9 -0
- package/dist/env/uiLanguage.js +35 -0
- package/dist/eventLeg.d.ts +92 -0
- package/dist/eventLeg.js +285 -0
- package/dist/executionLaneCapability.d.ts +0 -4
- package/dist/executionLaneCapability.js +3 -16
- package/dist/fileHistoryCaptureCapability.d.ts +0 -4
- package/dist/fileHistoryCaptureCapability.js +5 -7
- package/dist/fleet/fleetLedger.d.ts +0 -3
- package/dist/fleet/fleetLedger.js +21 -36
- package/dist/fleet/fleetProjection.d.ts +2 -2
- package/dist/fleet/fleetProjection.js +10 -8
- package/dist/frameBinding.d.ts +5 -0
- package/dist/frameBinding.js +23 -0
- package/dist/gateFailureCodes.d.ts +2 -0
- package/dist/gateFailureCodes.js +15 -0
- package/dist/gateOutcome.d.ts +3 -1
- package/dist/gateOutcome.js +25 -6
- package/dist/gateVocabulary.d.ts +5 -0
- package/dist/gateVocabulary.js +64 -2
- package/dist/generated/engineAskFaceTables.js +2 -2
- package/dist/generated/engineFactTables.d.ts +6 -3
- package/dist/generated/engineFactTables.js +22 -4
- package/dist/generated/engineNoticeTables.js +9 -0
- package/dist/generated/toolNameTables.d.ts +1 -0
- package/dist/generated/toolNameTables.js +1 -0
- package/dist/hitl/allowRuleShadow.d.ts +6 -1
- package/dist/hitl/allowRuleShadow.js +21 -17
- package/dist/hitl/approvalCardReading.d.ts +15 -0
- package/dist/hitl/approvalCardReading.js +154 -0
- package/dist/hitl/approvalCardReasons.d.ts +13 -0
- package/dist/hitl/approvalCardReasons.js +120 -0
- package/dist/hitl/approvalHolds.d.ts +47 -15
- package/dist/hitl/approvalHolds.js +134 -28
- package/dist/hitl/approvalResolution.d.ts +51 -26
- package/dist/hitl/approvalResolution.js +86 -122
- package/dist/hitl/approvalsFeed.js +6 -18
- package/dist/hitl/askClassPair.d.ts +2 -0
- package/dist/hitl/askClassPair.js +1 -0
- package/dist/hitl/askGateWire.d.ts +20 -2
- package/dist/hitl/askGateWire.js +49 -1
- package/dist/hitl/askParkRowRouting.d.ts +5 -0
- package/dist/hitl/askParkRowRouting.js +32 -1
- package/dist/hitl/askSettlementLine.d.ts +18 -0
- package/dist/hitl/askSettlementLine.js +79 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/automaticAnswerVerdict.js +21 -5
- package/dist/hitl/crashConverged.d.ts +11 -20
- package/dist/hitl/crashConverged.js +56 -0
- package/dist/hitl/crashConvergedNotice.d.ts +3 -1
- package/dist/hitl/crashConvergedNotice.js +48 -11
- package/dist/hitl/decisionConflict.js +3 -10
- package/dist/hitl/decisionRefusal.d.ts +40 -1
- package/dist/hitl/decisionRefusal.js +199 -42
- package/dist/hitl/frameRouter.d.ts +5 -2
- package/dist/hitl/frameRouter.js +20 -16
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +50 -13
- package/dist/hitl/hitlBridge.d.ts +1 -2
- package/dist/hitl/hitlBridge.js +10 -7
- package/dist/hitl/livePendingAsk.d.ts +3 -3
- package/dist/hitl/livePendingAsk.js +7 -18
- package/dist/hitl/parkResolver.d.ts +2 -24
- package/dist/hitl/parkResolver.js +61 -97
- package/dist/hitl/persistedRulesWire.d.ts +24 -0
- package/dist/hitl/persistedRulesWire.js +122 -2
- package/dist/hitl/planReviewWire.d.ts +26 -13
- package/dist/hitl/planReviewWire.js +112 -78
- package/dist/hitl/refusedToolNameProse.d.ts +0 -1
- package/dist/hitl/refusedToolNameProse.js +0 -3
- package/dist/hitl/replayedApproval.d.ts +4 -3
- package/dist/hitl/replayedApproval.js +7 -4
- package/dist/hitl/sessionPolicyDeliverable.js +2 -4
- package/dist/hitl/sessionPolicyWire.d.ts +32 -20
- package/dist/hitl/sessionPolicyWire.js +162 -52
- package/dist/hitl/standingRows.d.ts +20 -2
- package/dist/hitl/standingRows.js +69 -8
- package/dist/hitl/toolApprovalWire.d.ts +87 -152
- package/dist/hitl/toolApprovalWire.js +591 -380
- package/dist/hookMatcherVerdict.d.ts +6 -1
- package/dist/hookMatcherVerdict.js +14 -7
- package/dist/hooksWireCaps.d.ts +2 -2
- package/dist/hooksWireCaps.js +59 -55
- package/dist/i18n/messages.en.d.ts +121 -0
- package/dist/i18n/messages.en.js +119 -0
- package/dist/i18n/messages.zh.d.ts +3 -0
- package/dist/i18n/messages.zh.js +120 -0
- package/dist/i18n/translate.d.ts +25 -0
- package/dist/i18n/translate.js +127 -0
- package/dist/index.d.ts +53 -18
- package/dist/index.js +38 -10
- package/dist/inheritEnvCapability.d.ts +31 -0
- package/dist/inheritEnvCapability.js +104 -0
- package/dist/liveModelCatalog.d.ts +1 -2
- package/dist/liveModelCatalog.js +2 -6
- package/dist/liveQuestionStore.d.ts +2 -21
- package/dist/mcpLiveness.d.ts +2 -1
- package/dist/mcpProbeWire.d.ts +6 -1
- package/dist/mcpProbeWire.js +1 -1
- package/dist/mcpWireCaps.d.ts +13 -2
- package/dist/mcpWireCaps.js +48 -0
- package/dist/memoryEntriesWire.d.ts +4 -9
- package/dist/model/modelSupplyRules.d.ts +0 -2
- package/dist/model/modelSupplyRules.js +3 -12
- package/dist/model/modelsPolicy.d.ts +0 -5
- package/dist/model/modelsPolicy.js +6 -36
- package/dist/model/subagentModelWord.d.ts +31 -0
- package/dist/model/subagentModelWord.js +261 -0
- package/dist/notifications.d.ts +6 -0
- package/dist/notifications.js +71 -23
- package/dist/outputStyleSeatCapability.d.ts +0 -3
- package/dist/outputStyleSeatCapability.js +0 -3
- package/dist/ownKey.d.ts +15 -0
- package/dist/ownKey.js +60 -3
- package/dist/panelReduce.d.ts +15 -9
- package/dist/panelReduce.js +680 -549
- package/dist/panelStateHolder.d.ts +3 -7
- package/dist/panelStateHolder.js +14 -130
- package/dist/peerLaneCapability.d.ts +0 -4
- package/dist/peerLaneCapability.js +0 -6
- package/dist/permissionRulesWriteCapability.d.ts +0 -4
- package/dist/permissionRulesWriteCapability.js +0 -6
- package/dist/permissionWireCaps.d.ts +2 -2
- package/dist/permissionWireCaps.js +2 -0
- package/dist/permissionsAllowGrantCapability.d.ts +0 -4
- package/dist/permissionsAllowGrantCapability.js +0 -6
- package/dist/permissionsDeclaration.d.ts +0 -11
- package/dist/permissionsDeclaration.js +60 -53
- package/dist/pluginHooksWire.d.ts +1 -1
- package/dist/pluginHooksWire.js +3 -6
- package/dist/preemptWire.d.ts +44 -0
- package/dist/preemptWire.js +83 -0
- package/dist/printToolResultFrame.js +15 -4
- package/dist/replayBody.d.ts +3 -0
- package/dist/replayBody.js +14 -0
- package/dist/request/taskRequest.d.ts +0 -1
- package/dist/request/taskRequest.js +0 -3
- package/dist/resumeRefusalCopy.d.ts +2 -1
- package/dist/resumeRefusalCopy.js +38 -2
- package/dist/retryStatus.d.ts +8 -0
- package/dist/retryStatus.js +106 -1
- package/dist/rewindArchiveCapability.d.ts +0 -4
- package/dist/rewindArchiveCapability.js +2 -10
- package/dist/rewindArchiveReading.d.ts +0 -1
- package/dist/rewindArchiveReading.js +0 -3
- package/dist/runCancelContext.d.ts +2 -1
- package/dist/runCancelContext.js +4 -2
- package/dist/runReattachPolicy.d.ts +31 -0
- package/dist/runReattachPolicy.js +46 -0
- package/dist/runTerminal.d.ts +3 -6
- package/dist/runTerminal.js +8 -9
- package/dist/sandboxWire.d.ts +3 -1
- package/dist/sandboxWire.js +76 -27
- package/dist/scenarioWire.js +1 -1
- package/dist/seam.d.ts +24 -2
- package/dist/seam.js +15 -3
- package/dist/seatContract.d.ts +53 -10
- package/dist/seatContract.js +132 -81
- package/dist/sessionPolicyCapability.d.ts +0 -4
- package/dist/sessionPolicyCapability.js +0 -6
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -3
- package/dist/sessionSyncFileHistoryCapability.js +0 -3
- package/dist/skillToolListsCapability.d.ts +0 -3
- package/dist/skillToolListsCapability.js +0 -3
- package/dist/sqlEngineCapability.d.ts +0 -4
- package/dist/sqlEngineCapability.js +0 -6
- package/dist/steerPriorityCapability.d.ts +0 -3
- package/dist/steerPriorityCapability.js +0 -3
- package/dist/streamDrainingLegs.d.ts +4 -0
- package/dist/streamDrainingLegs.js +6 -1
- package/dist/subagent/engineCompactWire.d.ts +0 -1
- package/dist/subagent/engineCompactWire.js +7 -5
- package/dist/subagent/engineTaskHandleWire.js +4 -7
- package/dist/subagentContentStore.js +1 -11
- package/dist/submitReopen.d.ts +24 -0
- package/dist/submitReopen.js +75 -0
- package/dist/terminalStatuses.d.ts +3 -0
- package/dist/terminalStatuses.js +10 -2
- package/dist/toolResult.d.ts +10 -1
- package/dist/toolResult.js +34 -6
- package/dist/toolRoster.d.ts +0 -2
- package/dist/toolRoster.js +0 -2
- package/dist/turnFailurePresentation.d.ts +28 -0
- package/dist/turnFailurePresentation.js +205 -0
- package/dist/userInvokedSkillsCapability.d.ts +0 -3
- package/dist/userInvokedSkillsCapability.js +0 -3
- package/dist/webSearchBackendCapability.d.ts +0 -4
- package/dist/webSearchBackendCapability.js +0 -6
- package/dist/wireErrorTriage.js +5 -24
- package/dist/wireFailureShape.d.ts +62 -5
- package/dist/wireFailureShape.js +105 -5
- package/dist/wireRefusalCopy.d.ts +12 -2
- package/dist/wireRefusalCopy.js +50 -52
- package/dist/workflowsJournalCapability.d.ts +0 -4
- package/dist/workflowsJournalCapability.js +0 -6
- package/dist/writeProtectionCapability.d.ts +0 -4
- package/dist/writeProtectionCapability.js +0 -6
- package/docs/INTEGRATION-CLIENTS.md +1305 -46
- package/package.json +5 -4
- package/dist/attachmentSeatCapability.d.ts +0 -12
- package/dist/attachmentSeatCapability.js +0 -23
- package/dist/hitl/approvalOutcomeNote.d.ts +0 -10
- package/dist/hitl/approvalOutcomeNote.js +0 -29
- package/dist/sessionReadRedactionCapability.d.ts +0 -13
- package/dist/sessionReadRedactionCapability.js +0 -35
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { displayUntrusted } from '../displayUntrusted.js';
|
|
2
|
+
import { guardedRead, readOwnDataValue } from '../ownKey.js';
|
|
3
|
+
export function readRequiresRealApproval(carrier) {
|
|
4
|
+
if (typeof carrier !== 'object' || carrier === null)
|
|
5
|
+
return false;
|
|
6
|
+
let failed = false;
|
|
7
|
+
let failure;
|
|
8
|
+
try {
|
|
9
|
+
if (carrier.requiresRealApproval === true)
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
failed = true;
|
|
14
|
+
failure = e;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const risk = carrier.risk;
|
|
18
|
+
if (typeof risk === 'object' && risk !== null && risk.requiresRealApproval === true)
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
if (!failed)
|
|
23
|
+
failure = e;
|
|
24
|
+
failed = true;
|
|
25
|
+
}
|
|
26
|
+
if (failed)
|
|
27
|
+
throw failure;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const EMPTY = Object.freeze({});
|
|
31
|
+
const UNKNOWN_SHAPE_COHERENCE_TOLERANCE_MS = 1_000;
|
|
32
|
+
function nonNegativeFinite(v) {
|
|
33
|
+
return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : undefined;
|
|
34
|
+
}
|
|
35
|
+
export function approvalWindowKeysOf(carrier, opts) {
|
|
36
|
+
if (typeof carrier !== 'object' || carrier === null)
|
|
37
|
+
return EMPTY;
|
|
38
|
+
if (typeof opts !== 'object' || opts === null)
|
|
39
|
+
return EMPTY;
|
|
40
|
+
let current;
|
|
41
|
+
let anchor;
|
|
42
|
+
let unknownShape;
|
|
43
|
+
try {
|
|
44
|
+
current = opts.windowIsCurrent;
|
|
45
|
+
anchor = opts.receivedAtMs;
|
|
46
|
+
unknownShape = opts.unknownShape;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return EMPTY;
|
|
50
|
+
}
|
|
51
|
+
if (current !== true)
|
|
52
|
+
return EMPTY;
|
|
53
|
+
const read = (key) => {
|
|
54
|
+
try {
|
|
55
|
+
return nonNegativeFinite(carrier[key]);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const inMs = read('expiresInMs');
|
|
62
|
+
const atMs = read('expiresAtMs');
|
|
63
|
+
const nowMs = read('serverNowMs');
|
|
64
|
+
if (unknownShape === true) {
|
|
65
|
+
if (inMs === undefined || atMs === undefined || nowMs === undefined)
|
|
66
|
+
return EMPTY;
|
|
67
|
+
if (atMs <= 0 || nowMs <= 0)
|
|
68
|
+
return EMPTY;
|
|
69
|
+
if (Math.abs(inMs - Math.max(0, atMs - nowMs)) > UNKNOWN_SHAPE_COHERENCE_TOLERANCE_MS)
|
|
70
|
+
return EMPTY;
|
|
71
|
+
}
|
|
72
|
+
const receivedAtMs = nonNegativeFinite(anchor);
|
|
73
|
+
const out = {};
|
|
74
|
+
if (inMs !== undefined)
|
|
75
|
+
out.expiresInMs = inMs;
|
|
76
|
+
if (atMs !== undefined)
|
|
77
|
+
out.expiresAtMs = atMs;
|
|
78
|
+
if (nowMs !== undefined)
|
|
79
|
+
out.serverNowMs = nowMs;
|
|
80
|
+
if ((inMs !== undefined || atMs !== undefined) && receivedAtMs !== undefined)
|
|
81
|
+
out.receivedAtMs = receivedAtMs;
|
|
82
|
+
return Object.freeze(out);
|
|
83
|
+
}
|
|
84
|
+
const PROBE_CAUSE_SHOWN_MAX = 8;
|
|
85
|
+
const PROBE_CAUSE_ENTRY_MAX = 200;
|
|
86
|
+
function own(v, key) {
|
|
87
|
+
if (typeof v !== 'object' || v === null)
|
|
88
|
+
return undefined;
|
|
89
|
+
return guardedRead(() => readOwnDataValue(v, key));
|
|
90
|
+
}
|
|
91
|
+
function readOperands(v) {
|
|
92
|
+
if (typeof v !== 'object' || v === null)
|
|
93
|
+
return undefined;
|
|
94
|
+
const shown = own(v, 'shown');
|
|
95
|
+
const isList = guardedRead(() => Array.isArray(shown));
|
|
96
|
+
if (isList !== true)
|
|
97
|
+
return undefined;
|
|
98
|
+
const length = own(shown, 'length');
|
|
99
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
|
|
100
|
+
return undefined;
|
|
101
|
+
const items = [];
|
|
102
|
+
for (let i = 0; i < length; i++) {
|
|
103
|
+
const item = own(shown, String(i));
|
|
104
|
+
if (typeof item !== 'string')
|
|
105
|
+
return undefined;
|
|
106
|
+
items.push(item);
|
|
107
|
+
}
|
|
108
|
+
const total = own(v, 'total');
|
|
109
|
+
if (typeof total !== 'number' || !Number.isSafeInteger(total) || total < 0 || total < items.length)
|
|
110
|
+
return undefined;
|
|
111
|
+
return { shown: items, total };
|
|
112
|
+
}
|
|
113
|
+
function operandsText(label, o) {
|
|
114
|
+
const shown = o.shown.slice(0, PROBE_CAUSE_SHOWN_MAX).map((p) => displayUntrusted(p, { max: PROBE_CAUSE_ENTRY_MAX }));
|
|
115
|
+
if (shown.length === 0)
|
|
116
|
+
return `${label}: ${String(o.total)} total`;
|
|
117
|
+
const more = o.total - shown.length;
|
|
118
|
+
return more > 0 ? `${label}: ${shown.join(', ')} (+${String(more)} more)` : `${label}: ${shown.join(', ')}`;
|
|
119
|
+
}
|
|
120
|
+
export function probeCauseRowsOf(card) {
|
|
121
|
+
try {
|
|
122
|
+
if (typeof card !== 'object' || card === null)
|
|
123
|
+
return undefined;
|
|
124
|
+
const cause = own(card, 'probeCause');
|
|
125
|
+
if (typeof cause !== 'object' || cause === null)
|
|
126
|
+
return undefined;
|
|
127
|
+
if (guardedRead(() => Array.isArray(cause)) !== false)
|
|
128
|
+
return undefined;
|
|
129
|
+
const code = own(cause, 'code');
|
|
130
|
+
if (typeof code !== 'string' || code === '')
|
|
131
|
+
return undefined;
|
|
132
|
+
const roots = readOperands(own(cause, 'roots'));
|
|
133
|
+
if (roots === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
let fd;
|
|
136
|
+
try {
|
|
137
|
+
fd = Object.getOwnPropertyDescriptor(cause, 'further');
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
if (fd === undefined || (Object.hasOwn(fd, 'value') && fd.value === undefined))
|
|
143
|
+
return Object.freeze([operandsText('roots', roots)]);
|
|
144
|
+
if (!Object.hasOwn(fd, 'value'))
|
|
145
|
+
return undefined;
|
|
146
|
+
const further = readOperands(fd.value);
|
|
147
|
+
if (further === undefined)
|
|
148
|
+
return undefined;
|
|
149
|
+
return Object.freeze([operandsText('roots', roots), operandsText('further', further)]);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DenialLimitFallback } from './toolApprovalWire.js';
|
|
2
|
+
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
|
+
export type ApprovalCardReasonSource = (typeof APPROVAL_CARD_REASON_SOURCES)[number];
|
|
4
|
+
export interface ApprovalCardReasonLine {
|
|
5
|
+
readonly source: ApprovalCardReasonSource;
|
|
6
|
+
readonly text: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ApprovalCardReasonsOptions {
|
|
9
|
+
readonly maxLines?: number;
|
|
10
|
+
}
|
|
11
|
+
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[];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { displayUntrusted } from '../displayUntrusted.js';
|
|
2
|
+
import { approvalMandateDetail, askOriginNoteOf } from '../gateVocabulary.js';
|
|
3
|
+
import { readAskOrigin, readOwnDataValue, guardedRead } from '../ownKey.js';
|
|
4
|
+
import { allowRuleShadowSentence } from './allowRuleShadow.js';
|
|
5
|
+
import { mandatedApprovalDetailOf } from './automaticAnswerVerdict.js';
|
|
6
|
+
import { approvalBoundGrantsDetail, approvalIsMandated, approvalPriorCardsDetail, askProbeReasonDetail, classifierNoRulingDetail, readApprovalBoundGrants, readApprovalClassifierNoRuling, readApprovalMandate, readDenialLimitFallback, } from './toolApprovalWire.js';
|
|
7
|
+
export const APPROVAL_CARD_REASON_SOURCES = Object.freeze([
|
|
8
|
+
'payload_unreadable',
|
|
9
|
+
'prior_cards',
|
|
10
|
+
'message',
|
|
11
|
+
'mandated',
|
|
12
|
+
'mandate',
|
|
13
|
+
'origin',
|
|
14
|
+
'denial_limit',
|
|
15
|
+
'probe',
|
|
16
|
+
'allow_rule_shadow',
|
|
17
|
+
'classifier_no_ruling',
|
|
18
|
+
'bound_grants',
|
|
19
|
+
'more',
|
|
20
|
+
]);
|
|
21
|
+
export const APPROVAL_CARD_REASONS_DEFAULT_MAX_LINES = 4;
|
|
22
|
+
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.`;
|
|
26
|
+
}
|
|
27
|
+
export function denialLimitFallbackDetail(v) {
|
|
28
|
+
const d = readDenialLimitFallback(v);
|
|
29
|
+
if (d === undefined)
|
|
30
|
+
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('; ');
|
|
39
|
+
}
|
|
40
|
+
function ownData(card, key) {
|
|
41
|
+
return guardedRead(() => readOwnDataValue(card, key));
|
|
42
|
+
}
|
|
43
|
+
function readMaxLines(opts) {
|
|
44
|
+
const raw = typeof opts === 'object' && opts !== null ? guardedRead(() => readOwnDataValue(opts, 'maxLines')) : undefined;
|
|
45
|
+
return typeof raw === 'number' && Number.isSafeInteger(raw) && raw >= 1 ? raw : APPROVAL_CARD_REASONS_DEFAULT_MAX_LINES;
|
|
46
|
+
}
|
|
47
|
+
export function approvalCardReasons(card, opts) {
|
|
48
|
+
if (typeof card !== 'object' || card === null)
|
|
49
|
+
return Object.freeze([]);
|
|
50
|
+
const raw = [];
|
|
51
|
+
const add = (source, read) => {
|
|
52
|
+
let text;
|
|
53
|
+
try {
|
|
54
|
+
text = read();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
text = undefined;
|
|
58
|
+
}
|
|
59
|
+
raw.push({ source, text });
|
|
60
|
+
};
|
|
61
|
+
let denialLimitReadable = false;
|
|
62
|
+
try {
|
|
63
|
+
denialLimitReadable = readDenialLimitFallback(ownData(card, 'denialLimitFallback')) !== undefined;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
denialLimitReadable = false;
|
|
67
|
+
}
|
|
68
|
+
add('payload_unreadable', () => (ownData(card, 'cardPayloadUnreadable') === true ? PAYLOAD_UNREADABLE_DETAIL : undefined));
|
|
69
|
+
add('prior_cards', () => approvalPriorCardsDetail(card));
|
|
70
|
+
add('message', () => {
|
|
71
|
+
const msg = ownData(card, 'message');
|
|
72
|
+
if (typeof msg !== 'string' || msg.trim() === '')
|
|
73
|
+
return undefined;
|
|
74
|
+
const tool = ownData(card, 'toolName');
|
|
75
|
+
if (typeof tool === 'string' && msg.trim() === `approval required for "${tool}"`)
|
|
76
|
+
return undefined;
|
|
77
|
+
return displayUntrusted(msg, { max: MESSAGE_DISPLAY_MAX });
|
|
78
|
+
});
|
|
79
|
+
add('mandated', () => (approvalIsMandated(card) ? mandatedApprovalDetailOf(card) : undefined));
|
|
80
|
+
add('mandate', () => {
|
|
81
|
+
const word = readApprovalMandate(card);
|
|
82
|
+
return word !== undefined ? approvalMandateDetail(word) : undefined;
|
|
83
|
+
});
|
|
84
|
+
add('origin', () => {
|
|
85
|
+
const origin = readAskOrigin(card);
|
|
86
|
+
if (origin === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (origin === 'denial_limit_fallback' && denialLimitReadable)
|
|
89
|
+
return undefined;
|
|
90
|
+
return askOriginNoteOf(origin);
|
|
91
|
+
});
|
|
92
|
+
add('denial_limit', () => denialLimitFallbackDetail(ownData(card, 'denialLimitFallback')));
|
|
93
|
+
add('probe', () => askProbeReasonDetail(card));
|
|
94
|
+
add('allow_rule_shadow', () => allowRuleShadowSentence(card));
|
|
95
|
+
add('classifier_no_ruling', () => {
|
|
96
|
+
const word = readApprovalClassifierNoRuling(card);
|
|
97
|
+
return word !== undefined ? classifierNoRulingDetail(word) : undefined;
|
|
98
|
+
});
|
|
99
|
+
add('bound_grants', () => {
|
|
100
|
+
const grants = readApprovalBoundGrants(card);
|
|
101
|
+
return grants !== undefined ? approvalBoundGrantsDetail(grants) : undefined;
|
|
102
|
+
});
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
const lines = [];
|
|
105
|
+
for (const r of raw) {
|
|
106
|
+
if (r.text === undefined)
|
|
107
|
+
continue;
|
|
108
|
+
const text = displayUntrusted(r.text);
|
|
109
|
+
if (text === '' || seen.has(text))
|
|
110
|
+
continue;
|
|
111
|
+
seen.add(text);
|
|
112
|
+
lines.push(Object.freeze({ source: r.source, text }));
|
|
113
|
+
}
|
|
114
|
+
const max = readMaxLines(opts);
|
|
115
|
+
if (lines.length <= max)
|
|
116
|
+
return Object.freeze(lines);
|
|
117
|
+
const kept = lines.slice(0, max - 1);
|
|
118
|
+
kept.push(Object.freeze({ source: 'more', text: moreDetail(lines.length - kept.length) }));
|
|
119
|
+
return Object.freeze(kept);
|
|
120
|
+
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import type { ApprovalHolderIdentity
|
|
1
|
+
import type { ApprovalHolderIdentity } from './toolApprovalWire.js';
|
|
2
|
+
import type { ApprovalResolution, ApprovalResolutionLane } from './approvalResolution.js';
|
|
2
3
|
export declare const APPROVAL_SETTLED_HOWS: readonly ["answered", "interrupted", "closed_by_engine"];
|
|
3
4
|
export type ApprovalSettledHow = (typeof APPROVAL_SETTLED_HOWS)[number];
|
|
4
5
|
export declare const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
|
|
5
6
|
export interface SettledApprovalEntry {
|
|
6
7
|
readonly how: ApprovalSettledHow;
|
|
7
8
|
readonly parked: boolean;
|
|
8
|
-
readonly outcome:
|
|
9
|
+
readonly outcome: ApprovalResolution;
|
|
9
10
|
}
|
|
10
11
|
export interface DurableDecisionHold {
|
|
11
12
|
readonly lane: 'durable';
|
|
12
|
-
readonly
|
|
13
|
+
readonly identity: HoldIdentity;
|
|
14
|
+
phase: 'card' | 'respond';
|
|
15
|
+
readonly outcome: Promise<ApprovalResolution>;
|
|
13
16
|
presented: boolean;
|
|
14
17
|
readonly interrupt: () => void;
|
|
15
18
|
readonly cardSignal: AbortSignal;
|
|
@@ -18,7 +21,7 @@ export interface DurableDecisionHold {
|
|
|
18
21
|
export interface LiveCardHold {
|
|
19
22
|
readonly lane: 'live';
|
|
20
23
|
readonly identity: LiveCardIdentity;
|
|
21
|
-
readonly outcome: Promise<
|
|
24
|
+
readonly outcome: Promise<ApprovalResolution>;
|
|
22
25
|
phase: 'card' | 'respond';
|
|
23
26
|
superseded?: {
|
|
24
27
|
readonly parked: boolean;
|
|
@@ -27,17 +30,45 @@ export interface LiveCardHold {
|
|
|
27
30
|
readonly interrupt: () => void;
|
|
28
31
|
trailing?: Promise<boolean>;
|
|
29
32
|
}
|
|
30
|
-
export
|
|
31
|
-
|
|
33
|
+
export interface HostCardHold {
|
|
34
|
+
readonly lane: 'host';
|
|
35
|
+
readonly identity: HoldIdentity;
|
|
36
|
+
phase: 'card' | 'respond';
|
|
37
|
+
readonly released: Promise<'answered' | 'interrupted' | undefined>;
|
|
38
|
+
}
|
|
39
|
+
export type ApprovalHold = DurableDecisionHold | LiveCardHold | HostCardHold;
|
|
40
|
+
export interface LiveCardIdentity extends HoldIdentity {
|
|
32
41
|
readonly approvalId: string;
|
|
33
|
-
readonly toolCallId?: string;
|
|
34
|
-
readonly taskId?: string;
|
|
35
|
-
readonly sessionId?: string;
|
|
36
42
|
}
|
|
37
|
-
export
|
|
43
|
+
export interface HoldIdentity extends ApprovalHolderIdentity {
|
|
44
|
+
readonly askSig?: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function approvalHoldFor(who: HoldIdentity, except?: (h: ApprovalHold) => boolean): ApprovalHold | undefined;
|
|
38
47
|
export declare function settledForDurableLane(entry: SettledApprovalEntry | undefined): boolean;
|
|
39
|
-
export declare function liveCardAnsweredHow(
|
|
40
|
-
export
|
|
48
|
+
export declare function liveCardAnsweredHow(r: ApprovalResolution): 'answered' | 'interrupted' | undefined;
|
|
49
|
+
export type ApprovalHolderWait = {
|
|
50
|
+
readonly kind: 'settled';
|
|
51
|
+
readonly how: 'answered' | 'interrupted';
|
|
52
|
+
readonly outcome: ApprovalResolution;
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: 'free';
|
|
55
|
+
} | {
|
|
56
|
+
readonly kind: 'left';
|
|
57
|
+
};
|
|
58
|
+
export declare function awaitApprovalHolder(hold: ApprovalHold, signal: AbortSignal | undefined, asker: ApprovalResolutionLane): Promise<ApprovalHolderWait>;
|
|
59
|
+
export interface HostApprovalCardHold {
|
|
60
|
+
readonly identity: ApprovalHolderIdentity;
|
|
61
|
+
sending(): void;
|
|
62
|
+
release(settled?: 'answered' | 'interrupted'): void;
|
|
63
|
+
}
|
|
64
|
+
export type ApprovalCardClaim = {
|
|
65
|
+
readonly kind: 'held';
|
|
66
|
+
readonly hold: HostApprovalCardHold;
|
|
67
|
+
} | {
|
|
68
|
+
readonly kind: 'joined';
|
|
69
|
+
readonly outcome: ApprovalResolution;
|
|
70
|
+
};
|
|
71
|
+
export declare function claimApprovalCardFor(who: HoldIdentity, signal: AbortSignal | undefined): Promise<ApprovalCardClaim>;
|
|
41
72
|
export interface ApprovalDecisionHoldTable {
|
|
42
73
|
readonly inFlight: Map<string, ApprovalHold>;
|
|
43
74
|
readonly settled: Map<string, DurableSettledHold>;
|
|
@@ -54,8 +85,9 @@ export declare function settledApprovalOf(approvalId: string | undefined): Settl
|
|
|
54
85
|
export declare function clearSettledApprovals(): void;
|
|
55
86
|
export declare function anyApprovalSettled(): boolean;
|
|
56
87
|
export declare function noteEngineClosedApproval(approvalId: string, parked: boolean): void;
|
|
57
|
-
export declare function
|
|
88
|
+
export declare function noteHolderSettledApproval(approvalId: string, how: 'answered' | 'interrupted', outcome: ApprovalResolution): void;
|
|
89
|
+
export declare function settledLiveCardOutcome(approvalId: string): ApprovalResolution | undefined;
|
|
58
90
|
export declare function liveCardHoldOf(key: string): LiveCardHold | undefined;
|
|
59
|
-
export declare function holdLiveCard(identity: LiveCardIdentity, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<
|
|
60
|
-
export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<
|
|
91
|
+
export declare function holdLiveCard(identity: LiveCardIdentity, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<ApprovalResolution>): Promise<ApprovalResolution>;
|
|
92
|
+
export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ApprovalResolution>;
|
|
61
93
|
export declare function supersedeLiveCard(key: string, parked: boolean): boolean;
|
|
@@ -1,51 +1,149 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
2
|
import { liveFrameCallKey } from './gateIdentity.js';
|
|
3
|
-
import {
|
|
3
|
+
import { decideReceiptReopen } from '../decideReceipt.js';
|
|
4
4
|
export const APPROVAL_SETTLED_HOWS = Object.freeze(['answered', 'interrupted', 'closed_by_engine']);
|
|
5
5
|
export const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
|
|
6
6
|
const nonEmpty = (v) => typeof v === 'string' && v !== '';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
function sameApproval(who, h) {
|
|
8
|
+
const id = h.identity;
|
|
9
|
+
if (nonEmpty(who.approvalId) && nonEmpty(id.approvalId))
|
|
10
|
+
return who.approvalId === id.approvalId;
|
|
11
|
+
if (h.phase !== 'card')
|
|
12
|
+
return false;
|
|
13
|
+
if (!nonEmpty(who.taskId) || !nonEmpty(who.toolCallId) || id.taskId !== who.taskId || id.toolCallId !== who.toolCallId)
|
|
14
|
+
return false;
|
|
15
|
+
if (nonEmpty(who.sessionId) && nonEmpty(id.sessionId) && id.sessionId !== who.sessionId)
|
|
16
|
+
return false;
|
|
17
|
+
return !(nonEmpty(who.askSig) && nonEmpty(id.askSig) && id.askSig !== who.askSig);
|
|
18
|
+
}
|
|
19
|
+
export function approvalHoldFor(who, except) {
|
|
12
20
|
for (const h of approvalDecisionHolds.inFlight.values()) {
|
|
13
|
-
if (h
|
|
21
|
+
if (except?.(h) === true)
|
|
14
22
|
continue;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
continue;
|
|
18
|
-
if (nonEmpty(who.sessionId) && nonEmpty(id.sessionId) && id.sessionId !== who.sessionId)
|
|
19
|
-
continue;
|
|
20
|
-
return h;
|
|
23
|
+
if (sameApproval(who, h))
|
|
24
|
+
return h;
|
|
21
25
|
}
|
|
22
26
|
return undefined;
|
|
23
27
|
}
|
|
24
28
|
export function settledForDurableLane(entry) {
|
|
25
29
|
return entry !== undefined && !entry.parked;
|
|
26
30
|
}
|
|
27
|
-
export function liveCardAnsweredHow(
|
|
28
|
-
return answeredHowOf(approvalResolutionOf(o));
|
|
29
|
-
}
|
|
30
|
-
function answeredHowOf(r) {
|
|
31
|
+
export function liveCardAnsweredHow(r) {
|
|
31
32
|
if (r.kind === 'decided')
|
|
32
33
|
return 'answered';
|
|
33
34
|
return r.kind === 'unsettled' && r.cause === 'interrupted' ? 'interrupted' : undefined;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
const HOLDER_FREE = Object.freeze({ kind: 'free' });
|
|
37
|
+
function nothingSentHere(asker) {
|
|
38
|
+
return Object.freeze({ kind: 'not_sent', lane: asker, cause: 'retracted' });
|
|
39
|
+
}
|
|
40
|
+
function interruptedThere(asker) {
|
|
41
|
+
return Object.freeze({ kind: 'unsettled', lane: asker, cause: 'interrupted' });
|
|
42
|
+
}
|
|
43
|
+
export async function awaitApprovalHolder(hold, signal, asker) {
|
|
44
|
+
if (hold.lane === 'host')
|
|
45
|
+
return awaitHostHolder(hold, signal, asker);
|
|
36
46
|
const unsubscribe = followCallerSignal(signal, hold.interrupt);
|
|
37
|
-
let o;
|
|
38
47
|
try {
|
|
39
|
-
|
|
48
|
+
if (hold.lane === 'live') {
|
|
49
|
+
let o;
|
|
50
|
+
try {
|
|
51
|
+
o = await hold.outcome;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return HOLDER_FREE;
|
|
55
|
+
}
|
|
56
|
+
const stillHeld = approvalDecisionHolds.inFlight.get(liveFrameCallKey(hold.identity.approvalId)) === hold;
|
|
57
|
+
const entry = settledApprovalOf(hold.identity.approvalId);
|
|
58
|
+
if (!stillHeld && !settledForDurableLane(entry))
|
|
59
|
+
return HOLDER_FREE;
|
|
60
|
+
return Object.freeze({ kind: 'settled', how: entry?.how === 'answered' ? 'answered' : 'interrupted', outcome: o });
|
|
61
|
+
}
|
|
62
|
+
let r;
|
|
63
|
+
try {
|
|
64
|
+
r = await hold.outcome;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return HOLDER_FREE;
|
|
68
|
+
}
|
|
69
|
+
if (r.kind === 'decided') {
|
|
70
|
+
if (decideReceiptReopen(r.receipt) !== null)
|
|
71
|
+
return HOLDER_FREE;
|
|
72
|
+
return Object.freeze({ kind: 'settled', how: 'answered', outcome: r });
|
|
73
|
+
}
|
|
74
|
+
if (r.kind !== 'unsettled' || r.cause !== 'interrupted' || hold.trailing === undefined)
|
|
75
|
+
return HOLDER_FREE;
|
|
76
|
+
const landed = await hold.trailing.then(() => true, () => false);
|
|
77
|
+
return landed ? Object.freeze({ kind: 'settled', how: 'interrupted', outcome: r }) : HOLDER_FREE;
|
|
40
78
|
}
|
|
41
|
-
|
|
42
|
-
|
|
79
|
+
finally {
|
|
80
|
+
unsubscribe();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function awaitHostHolder(hold, signal, asker) {
|
|
84
|
+
let leave = () => { };
|
|
85
|
+
const left = new Promise((resolve) => {
|
|
86
|
+
leave = () => resolve('left');
|
|
87
|
+
});
|
|
88
|
+
const unsubscribe = followCallerSignal(signal, leave);
|
|
89
|
+
try {
|
|
90
|
+
const r = await Promise.race([hold.released, left]);
|
|
91
|
+
if (r === 'left')
|
|
92
|
+
return Object.freeze({ kind: 'left' });
|
|
93
|
+
if (r === undefined)
|
|
94
|
+
return HOLDER_FREE;
|
|
95
|
+
return Object.freeze({ kind: 'settled', how: r, outcome: r === 'interrupted' ? interruptedThere(asker) : nothingSentHere(asker) });
|
|
43
96
|
}
|
|
44
97
|
finally {
|
|
45
98
|
unsubscribe();
|
|
46
99
|
}
|
|
47
|
-
|
|
48
|
-
|
|
100
|
+
}
|
|
101
|
+
function holdHostCard(identity) {
|
|
102
|
+
const h = approvalDecisionHolds;
|
|
103
|
+
const key = `hitl-host:${++h.clock}`;
|
|
104
|
+
let resolveReleased = () => { };
|
|
105
|
+
const released = new Promise((resolve) => {
|
|
106
|
+
resolveReleased = resolve;
|
|
107
|
+
});
|
|
108
|
+
const frozen = Object.freeze({ ...identity });
|
|
109
|
+
const hold = { lane: 'host', identity: frozen, phase: 'card', released };
|
|
110
|
+
h.inFlight.set(key, hold);
|
|
111
|
+
let done = false;
|
|
112
|
+
return Object.freeze({
|
|
113
|
+
identity: frozen,
|
|
114
|
+
sending: () => {
|
|
115
|
+
if (!done)
|
|
116
|
+
hold.phase = 'respond';
|
|
117
|
+
},
|
|
118
|
+
release: (settled) => {
|
|
119
|
+
if (done)
|
|
120
|
+
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);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const CLAIM_MAX_ROUNDS = 8;
|
|
132
|
+
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 } });
|
|
145
|
+
}
|
|
146
|
+
return Object.freeze({ kind: 'held', hold: holdHostCard(who) });
|
|
49
147
|
}
|
|
50
148
|
export const approvalDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), outcomes: new Map(), clock: 0 };
|
|
51
149
|
export function settledApprovalOf(approvalId) {
|
|
@@ -57,14 +155,17 @@ export function clearSettledApprovals() {
|
|
|
57
155
|
export function anyApprovalSettled() {
|
|
58
156
|
return approvalDecisionHolds.outcomes.size > 0;
|
|
59
157
|
}
|
|
60
|
-
function noteSettled(approvalId, how, parked, outcome) {
|
|
158
|
+
function noteSettled(approvalId, how, parked, outcome, upgradesParked = false) {
|
|
61
159
|
if (typeof approvalId !== 'string' || approvalId === '')
|
|
62
160
|
return;
|
|
63
161
|
if (!APPROVAL_SETTLED_HOWS.includes(how))
|
|
64
162
|
return;
|
|
65
163
|
const t = approvalDecisionHolds.outcomes;
|
|
66
|
-
|
|
164
|
+
const prev = t.get(approvalId);
|
|
165
|
+
if (prev !== undefined && !(upgradesParked && prev.parked && how !== 'closed_by_engine'))
|
|
67
166
|
return;
|
|
167
|
+
if (prev !== undefined)
|
|
168
|
+
t.delete(approvalId);
|
|
68
169
|
t.set(approvalId, Object.freeze({ how, parked, outcome: Object.freeze({ ...outcome }) }));
|
|
69
170
|
while (t.size > SETTLED_APPROVALS_MAX_ENTRIES) {
|
|
70
171
|
const oldest = t.keys().next();
|
|
@@ -73,9 +174,14 @@ function noteSettled(approvalId, how, parked, outcome) {
|
|
|
73
174
|
t.delete(oldest.value);
|
|
74
175
|
}
|
|
75
176
|
}
|
|
76
|
-
|
|
177
|
+
function engineClosedOutcome() {
|
|
178
|
+
return Object.freeze({ kind: 'not_sent', lane: 'respond', cause: 'retracted' });
|
|
179
|
+
}
|
|
77
180
|
export function noteEngineClosedApproval(approvalId, parked) {
|
|
78
|
-
noteSettled(approvalId, 'closed_by_engine', parked,
|
|
181
|
+
noteSettled(approvalId, 'closed_by_engine', parked, engineClosedOutcome());
|
|
182
|
+
}
|
|
183
|
+
export function noteHolderSettledApproval(approvalId, how, outcome) {
|
|
184
|
+
noteSettled(approvalId, how, false, outcome, true);
|
|
79
185
|
}
|
|
80
186
|
export function settledLiveCardOutcome(approvalId) {
|
|
81
187
|
const e = settledApprovalOf(approvalId);
|