@sema-agent/client-core 0.97.1 → 0.99.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 +607 -0
- package/README.md +96 -46
- package/dist/adapt/arms.d.ts +1 -0
- package/dist/adapt/arms.js +188 -53
- 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/panelTasks.d.ts +1 -1
- package/dist/adapt/panelTasks.js +2 -2
- 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 +9 -3
- package/dist/adapter/activeRunSelfHeal.js +205 -313
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +77 -36
- 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 +79 -48
- package/dist/adapter/types.d.ts +4 -4
- package/dist/adapter/types.js +2 -1
- package/dist/agentSession/backgroundView.js +2 -1
- package/dist/agentSession/engineAgentRegistryRead.d.ts +2 -2
- package/dist/agentSession/engineAgentRegistryRead.js +12 -13
- package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
- package/dist/agentsWireCaps.d.ts +8 -11
- package/dist/agentsWireCaps.js +196 -194
- 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 +5 -3
- package/dist/cloudConfigWireCaps.js +106 -64
- package/dist/compensations.js +1 -1
- package/dist/connectorStatus.d.ts +21 -0
- package/dist/connectorStatus.js +112 -0
- package/dist/controlRouter.d.ts +18 -3
- package/dist/controlRouter.js +106 -67
- package/dist/coreValuePorts.d.ts +29 -4
- package/dist/coreValuePorts.js +10 -0
- package/dist/corruptSessionCopy.d.ts +2 -1
- package/dist/corruptSessionCopy.js +3 -3
- package/dist/decideReceipt.d.ts +2 -2
- package/dist/decideReceipt.js +5 -2
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/detachWire.js +3 -2
- 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/devicesFace.d.ts +34 -0
- package/dist/devicesFace.js +90 -0
- package/dist/displayUntrusted.d.ts +2 -0
- package/dist/displayUntrusted.js +253 -76
- package/dist/engineAgentAbsence.d.ts +1 -1
- package/dist/engineAgentPanelStore.d.ts +12 -4
- package/dist/engineAgentPanelStore.js +16 -11
- package/dist/engineCapReader.d.ts +2 -5
- package/dist/engineCapReader.js +8 -10
- package/dist/engineCapsCache.d.ts +12 -0
- package/dist/engineCapsCache.js +76 -18
- package/dist/engineCapsChange.d.ts +2 -0
- package/dist/engineCapsChange.js +9 -0
- package/dist/engineClosedSets.d.ts +1 -0
- package/dist/engineClosedSets.js +4 -1
- package/dist/engineErrorCodes.d.ts +11 -3
- package/dist/engineErrorCodes.js +11 -8
- package/dist/engineIdentity.d.ts +1 -0
- package/dist/engineIdentity.js +8 -0
- package/dist/engineRowRef.d.ts +28 -0
- package/dist/engineRowRef.js +114 -0
- package/dist/engineSessionMessages.d.ts +68 -0
- package/dist/engineSessionMessages.js +370 -0
- package/dist/engineTargetSeat.d.ts +27 -0
- package/dist/engineTargetSeat.js +199 -0
- package/dist/engineToolDirectory.d.ts +0 -9
- package/dist/engineToolDirectory.js +1 -94
- package/dist/engineUrl.d.ts +18 -0
- package/dist/engineUrl.js +52 -0
- package/dist/engineWireFor.js +3 -8
- package/dist/env/uiLanguage.d.ts +11 -1
- package/dist/env/uiLanguage.js +38 -2
- package/dist/eventLeg.d.ts +100 -0
- package/dist/eventLeg.js +305 -0
- package/dist/executionLaneCapability.d.ts +0 -4
- package/dist/executionLaneCapability.js +3 -16
- package/dist/fileHistoryCaptureCapability.d.ts +2 -5
- package/dist/fileHistoryCaptureCapability.js +13 -21
- package/dist/fileHistoryLegNotice.d.ts +2 -1
- package/dist/fileHistoryLegNotice.js +3 -3
- 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 +8 -0
- package/dist/frameBinding.js +35 -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 +41 -8
- package/dist/gateVocabulary.d.ts +10 -4
- package/dist/gateVocabulary.js +107 -47
- package/dist/generated/engineAskFaceTables.js +2 -2
- package/dist/generated/engineFactTables.d.ts +22 -3
- package/dist/generated/engineFactTables.js +51 -4
- package/dist/generated/engineNoticeTables.js +18 -3
- package/dist/generated/settingsSchemaFns.d.ts +6 -0
- package/dist/generated/settingsSchemaFns.js +57 -0
- package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
- package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
- package/dist/generated/settingsSchemaTables.d.ts +1 -0
- package/dist/generated/settingsSchemaTables.js +4 -0
- package/dist/generated/settingsSchemaTypes.d.ts +105 -0
- package/dist/generated/settingsSchemaTypes.js +1 -0
- package/dist/generated/toolNameTables.d.ts +1 -0
- package/dist/generated/toolNameTables.js +1 -0
- package/dist/hitl/allowRuleShadow.d.ts +8 -2
- package/dist/hitl/allowRuleShadow.js +36 -29
- package/dist/hitl/approvalCardReading.d.ts +15 -0
- package/dist/hitl/approvalCardReading.js +154 -0
- package/dist/hitl/approvalCardReasons.d.ts +14 -0
- package/dist/hitl/approvalCardReasons.js +119 -0
- package/dist/hitl/approvalHolds.d.ts +50 -15
- package/dist/hitl/approvalHolds.js +177 -27
- 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 +17 -0
- package/dist/hitl/askGateWire.d.ts +20 -2
- package/dist/hitl/askGateWire.js +50 -1
- package/dist/hitl/askParkRowRouting.d.ts +5 -0
- package/dist/hitl/askParkRowRouting.js +32 -1
- package/dist/hitl/askSettlementLine.d.ts +28 -0
- package/dist/hitl/askSettlementLine.js +99 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +3 -2
- package/dist/hitl/automaticAnswerVerdict.js +29 -13
- package/dist/hitl/crashConverged.d.ts +11 -20
- package/dist/hitl/crashConverged.js +56 -0
- package/dist/hitl/crashConvergedNotice.d.ts +6 -3
- package/dist/hitl/crashConvergedNotice.js +48 -24
- 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 +27 -24
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +50 -13
- package/dist/hitl/hitlBridge.d.ts +2 -2
- package/dist/hitl/hitlBridge.js +13 -10
- 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 +25 -0
- package/dist/hitl/persistedRulesWire.js +104 -2
- package/dist/hitl/planReviewWire.d.ts +31 -13
- package/dist/hitl/planReviewWire.js +120 -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.d.ts +2 -0
- package/dist/hitl/sessionPolicyDeliverable.js +48 -7
- package/dist/hitl/sessionPolicyWire.d.ts +34 -21
- package/dist/hitl/sessionPolicyWire.js +179 -70
- package/dist/hitl/standingRows.d.ts +20 -2
- package/dist/hitl/standingRows.js +69 -8
- package/dist/hitl/suspendedReopen.d.ts +2 -1
- package/dist/hitl/suspendedReopen.js +7 -10
- package/dist/hitl/toolApprovalWire.d.ts +103 -156
- package/dist/hitl/toolApprovalWire.js +661 -411
- package/dist/hookMatcherVerdict.d.ts +6 -1
- package/dist/hookMatcherVerdict.js +14 -7
- package/dist/hooksWireCaps.d.ts +2 -2
- package/dist/hooksWireCaps.js +174 -81
- package/dist/host.d.ts +21 -0
- package/dist/hostTurnSettings.d.ts +71 -0
- package/dist/hostTurnSettings.js +291 -0
- package/dist/humanTurn.d.ts +14 -1
- package/dist/humanTurn.js +100 -12
- package/dist/humanTurnRead.d.ts +2 -0
- package/dist/humanTurnRead.js +22 -1
- package/dist/i18n/messages.en.d.ts +703 -0
- package/dist/i18n/messages.en.js +701 -0
- package/dist/i18n/messages.zh.d.ts +3 -0
- package/dist/i18n/messages.zh.js +837 -0
- package/dist/i18n/translate.d.ts +25 -0
- package/dist/i18n/translate.js +127 -0
- package/dist/index.d.ts +78 -29
- package/dist/index.js +54 -18
- package/dist/inheritEnvCapability.d.ts +32 -0
- package/dist/inheritEnvCapability.js +104 -0
- package/dist/inheritEnvWire.d.ts +3 -2
- package/dist/inheritEnvWire.js +12 -14
- package/dist/interactiveHalt.js +17 -6
- package/dist/liveModelCatalog.d.ts +3 -3
- package/dist/liveModelCatalog.js +15 -13
- package/dist/liveQuestionStore.d.ts +2 -21
- package/dist/mcpLiveness.d.ts +2 -1
- package/dist/mcpPanel.d.ts +10 -1
- package/dist/mcpPanel.js +40 -13
- package/dist/mcpProbeCapability.d.ts +2 -1
- package/dist/mcpProbeCapability.js +7 -11
- package/dist/mcpProbeWire.d.ts +9 -2
- package/dist/mcpProbeWire.js +6 -1
- package/dist/mcpWireCaps.d.ts +47 -2
- package/dist/mcpWireCaps.js +164 -16
- package/dist/memoryEntriesWire.d.ts +4 -9
- package/dist/memoryEntriesWire.js +5 -10
- 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/onboardJudgement.d.ts +93 -0
- package/dist/model/onboardJudgement.js +415 -0
- package/dist/model/subagentModelWord.d.ts +31 -0
- package/dist/model/subagentModelWord.js +261 -0
- package/dist/model/tierVocabulary.d.ts +2 -0
- package/dist/model/tierVocabulary.js +17 -5
- package/dist/modelCapabilityProbe.d.ts +64 -0
- package/dist/modelCapabilityProbe.js +600 -0
- package/dist/modelIdentityResolvability.d.ts +3 -0
- package/dist/modelIdentityResolvability.js +26 -17
- 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 +22 -0
- package/dist/ownKey.js +94 -3
- package/dist/panelReduce.d.ts +21 -10
- package/dist/panelReduce.js +728 -550
- 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/permissionRuleIssue.js +2 -0
- package/dist/permissionRulesWriteCapability.d.ts +2 -5
- package/dist/permissionRulesWriteCapability.js +8 -12
- 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 +46 -3
- package/dist/pluginHooksWire.js +219 -16
- package/dist/preemptWire.d.ts +44 -0
- package/dist/preemptWire.js +83 -0
- package/dist/printModelStatus.d.ts +63 -0
- package/dist/printModelStatus.js +155 -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 +11 -6
- package/dist/request/taskRequest.js +112 -19
- package/dist/resumeRefusalCopy.d.ts +13 -9
- package/dist/resumeRefusalCopy.js +81 -118
- package/dist/retryStatus.d.ts +45 -0
- package/dist/retryStatus.js +185 -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/scenarioRefusal.d.ts +9 -0
- package/dist/scenarioRefusal.js +111 -0
- package/dist/scenarioWire.js +1 -1
- package/dist/seam.d.ts +48 -2
- package/dist/seam.js +25 -4
- package/dist/seatContract.d.ts +311 -18
- package/dist/seatContract.js +824 -100
- package/dist/selfOrchestrationDenial.js +7 -19
- package/dist/sessionIdSegment.d.ts +6 -0
- package/dist/sessionIdSegment.js +24 -0
- package/dist/sessionMemoryStatus.js +6 -11
- package/dist/sessionPolicyCapability.d.ts +2 -5
- package/dist/sessionPolicyCapability.js +8 -12
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -3
- package/dist/sessionSyncFileHistoryCapability.js +0 -3
- package/dist/settingsHooksSalvage.d.ts +44 -0
- package/dist/settingsHooksSalvage.js +211 -0
- package/dist/skillToolListsCapability.d.ts +2 -4
- package/dist/skillToolListsCapability.js +8 -9
- package/dist/sqlEngineCapability.d.ts +0 -4
- package/dist/sqlEngineCapability.js +0 -6
- package/dist/sseIdleTriage.js +3 -2
- 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/engineSubagentResume.js +15 -9
- package/dist/subagent/engineTaskHandleWire.js +4 -7
- package/dist/subagentContentStore.d.ts +3 -0
- package/dist/subagentContentStore.js +22 -10
- 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/toolHistoryMismatch.js +3 -2
- package/dist/toolResult.d.ts +10 -1
- package/dist/toolResult.js +34 -6
- package/dist/toolRoster.d.ts +3 -4
- package/dist/toolRoster.js +21 -22
- package/dist/turnFailurePresentation.d.ts +39 -0
- package/dist/turnFailurePresentation.js +402 -0
- package/dist/usageAnalytics.js +6 -11
- package/dist/userAttachmentEnvelope.d.ts +1 -0
- package/dist/userAttachmentEnvelope.js +37 -7
- package/dist/userAttachmentSeat.d.ts +21 -0
- package/dist/userAttachmentSeat.js +75 -0
- package/dist/userInvokedSkillsCapability.d.ts +11 -4
- package/dist/userInvokedSkillsCapability.js +31 -10
- package/dist/webSearchBackendCapability.d.ts +0 -4
- package/dist/webSearchBackendCapability.js +0 -6
- package/dist/wireErrorTriage.d.ts +7 -0
- package/dist/wireErrorTriage.js +26 -23
- package/dist/wireFailureShape.d.ts +62 -5
- package/dist/wireFailureShape.js +106 -5
- package/dist/wireRefusalCopy.d.ts +25 -3
- package/dist/wireRefusalCopy.js +74 -80
- package/dist/workflowsJournalCapability.d.ts +2 -5
- package/dist/workflowsJournalCapability.js +8 -12
- package/dist/writeProtectionCapability.d.ts +0 -4
- package/dist/writeProtectionCapability.js +0 -6
- package/docs/INTEGRATION-CLIENTS.md +1775 -56
- package/package.json +6 -5
- 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
|
@@ -1,27 +1,31 @@
|
|
|
1
|
-
import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON,
|
|
1
|
+
import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, denyReasonForWire, findPendingForTask, readWireErrorCodeReading, } from './hitlBridge.js';
|
|
2
2
|
import { askParkRowArm } from './askParkRowRouting.js';
|
|
3
3
|
import { parkGateArmOf } from './frameRouter.js';
|
|
4
4
|
import { seatSessionRowOf } from './standingRows.js';
|
|
5
5
|
import { hostLog } from '../host.js';
|
|
6
6
|
import { guardedRead, readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
|
|
7
|
-
import { readAskClassPair } from './askClassPair.js';
|
|
8
|
-
import {
|
|
7
|
+
import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPair } from './askClassPair.js';
|
|
8
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
9
|
+
import { approvalWindowKeysOf, readRequiresRealApproval } from './approvalCardReading.js';
|
|
10
|
+
import { readAllowRuleShadowFromWire, retiredAllowRuleShadowKeyPresent, RETIRED_SHADOW_EVIDENCE_LABEL } from './allowRuleShadow.js';
|
|
9
11
|
import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
10
12
|
import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
|
|
11
13
|
import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
|
|
14
|
+
import { CORE_ALLOW_ENTRY_INERT_REASONS } from '../generated/engineFactTables.js';
|
|
15
|
+
import { msg, renderMessage } from '../i18n/translate.js';
|
|
12
16
|
import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
13
17
|
import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
|
|
14
18
|
import { displayUntrusted } from '../displayUntrusted.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
19
|
+
import { frameBindingOf } from '../frameBinding.js';
|
|
20
|
+
import { approvalDecisionHolds, approvalHoldFor, awaitApprovalHolder, claimApprovalCardFor, holdLiveCard, joinLiveCard, liveCardHoldOf, clearSettledApprovals, noteHolderSettledApproval, settledApprovalOf, settledForDurableLane, settledLiveCardOutcome } from './approvalHolds.js';
|
|
21
|
+
import { joinedResolution, resolutionOfDecisionFailure } from './approvalResolution.js';
|
|
22
|
+
import { parkRowDeadlineState } from './parkExpiryJudgements.js';
|
|
17
23
|
import { isApprovalMandateWord } from '../gateVocabulary.js';
|
|
18
24
|
import { denyAttributionRefusalFromError } from '../wireRefusalCopy.js';
|
|
19
25
|
import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
|
|
20
26
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { readDecisionRefusalAsk } from './decisionRefusal.js';
|
|
24
|
-
import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
27
|
+
import { wireFailureTextsOf } from '../wireFailureShape.js';
|
|
28
|
+
import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
|
|
25
29
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
26
30
|
import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, TOOL_APPROVAL_CARD_WORDS, } from '@sema-agent/sdk';
|
|
27
31
|
export function toolNameIsFsWrite(name) {
|
|
@@ -82,6 +86,14 @@ export function readApprovalDenySettledBy(decision) {
|
|
|
82
86
|
: undefined;
|
|
83
87
|
}
|
|
84
88
|
export const RETRACTED_CARD_DECISION_KIND = 'retracted';
|
|
89
|
+
function parkRowOf(taskId, row, gatedCallId) {
|
|
90
|
+
const d = parkRowDeadlineState(row, Date.now());
|
|
91
|
+
return Object.freeze({
|
|
92
|
+
taskId,
|
|
93
|
+
...(gatedCallId !== undefined && gatedCallId !== '' ? { gatedCallId } : {}),
|
|
94
|
+
...(d.kind !== 'none' ? { deadlineMs: d.deadlineMs } : {}),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
85
97
|
const cardPortByKey = createSessionSlot();
|
|
86
98
|
const cardPortMissesByKey = new Map();
|
|
87
99
|
function bumpCardPortMiss(sessionKey) {
|
|
@@ -149,14 +161,6 @@ export function rememberRefusalOf(error) {
|
|
|
149
161
|
function rememberRefusalIn(reading) {
|
|
150
162
|
return reading.state === 'code' ? REMEMBER_REFUSAL_CODES.find((c) => c === reading.code) : undefined;
|
|
151
163
|
}
|
|
152
|
-
function rememberFallbackKind(e) {
|
|
153
|
-
const reading = readWireErrorCodeReading(e);
|
|
154
|
-
if (rememberRefusalIn(reading) !== undefined)
|
|
155
|
-
return 'refused';
|
|
156
|
-
if (reading.state !== 'absent')
|
|
157
|
-
return undefined;
|
|
158
|
-
return readToolApprovalRespondRefusal(e)?.status === 400 ? 'legacy' : undefined;
|
|
159
|
-
}
|
|
160
164
|
function logAfterDecided(msg) {
|
|
161
165
|
try {
|
|
162
166
|
hostLog('debug', msg);
|
|
@@ -223,7 +227,7 @@ function heldDurableDecision(holdKey, readMark) {
|
|
|
223
227
|
const recent = approvalDecisionHolds.settled.get(holdKey);
|
|
224
228
|
return recent !== undefined && recent.at > readMark ? recent.hold : undefined;
|
|
225
229
|
}
|
|
226
|
-
function holdDurableDecision(holdKey, callerSignal, decide) {
|
|
230
|
+
function holdDurableDecision(holdKey, identity, callerSignal, decide) {
|
|
227
231
|
const h = approvalDecisionHolds;
|
|
228
232
|
const interruption = new AbortController();
|
|
229
233
|
let settled = false;
|
|
@@ -241,7 +245,7 @@ function holdDurableDecision(holdKey, callerSignal, decide) {
|
|
|
241
245
|
resolveOutcome = resolve;
|
|
242
246
|
rejectOutcome = reject;
|
|
243
247
|
});
|
|
244
|
-
const hold = { lane: 'durable', outcome, presented: false, interrupt, cardSignal: interruption.signal };
|
|
248
|
+
const hold = { lane: 'durable', identity: Object.freeze({ ...identity }), phase: 'card', outcome, presented: false, interrupt, cardSignal: interruption.signal };
|
|
245
249
|
h.inFlight.set(holdKey, hold);
|
|
246
250
|
const release = () => {
|
|
247
251
|
if (h.inFlight.get(holdKey) === hold)
|
|
@@ -283,7 +287,7 @@ async function joinDurableDecision(hold, deps, signal) {
|
|
|
283
287
|
catch {
|
|
284
288
|
}
|
|
285
289
|
}
|
|
286
|
-
return
|
|
290
|
+
return joinedResolution(o);
|
|
287
291
|
}
|
|
288
292
|
export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId, parkToolName) {
|
|
289
293
|
const decidedByReading = decidedByReadingSnapshotOf(deps);
|
|
@@ -299,19 +303,22 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
299
303
|
closeDurableRead(readMark);
|
|
300
304
|
}
|
|
301
305
|
if (!found.ok)
|
|
302
|
-
return { kind: '
|
|
306
|
+
return { kind: 'not_sent', lane: 'decide', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}) };
|
|
303
307
|
if (held !== undefined)
|
|
304
308
|
return joinDurableDecision(held, deps, signal);
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
309
|
+
const rowIdentity = parkRowHolderIdentity(found.pending, found.gatedCallId);
|
|
310
|
+
const otherHolder = approvalHoldFor(rowIdentity, (h) => h.lane === 'durable');
|
|
311
|
+
if (otherHolder !== undefined) {
|
|
312
|
+
const wait = await awaitApprovalHolder(otherHolder, signal, 'decide');
|
|
313
|
+
if (wait.kind === 'settled')
|
|
314
|
+
return joinedResolution(wait.outcome, found.gatedCallId);
|
|
315
|
+
if (wait.kind === 'left')
|
|
316
|
+
return { kind: 'not_sent', lane: 'decide', cause: 'retracted', ...(found.gatedCallId !== undefined ? { gatedCallId: found.gatedCallId } : {}), joinedInFlight: true };
|
|
310
317
|
return surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId, parkToolName);
|
|
311
318
|
}
|
|
312
319
|
const callKey = approvalCallKey(found.gatedCallId, taskId);
|
|
313
320
|
const fetched = found;
|
|
314
|
-
return holdDurableDecision(durableHoldKey(fetched.pending, callKey), signal, async (hold) => {
|
|
321
|
+
return holdDurableDecision(durableHoldKey(fetched.pending, callKey), rowIdentity, signal, async (hold) => {
|
|
315
322
|
const slot = {};
|
|
316
323
|
const out = await presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold, slot);
|
|
317
324
|
noteCallOutcome(slot, out);
|
|
@@ -319,34 +326,17 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
319
326
|
});
|
|
320
327
|
}
|
|
321
328
|
function parkRowHolderIdentity(row, gatedCallId) {
|
|
329
|
+
const askSig = carrierAskSig(row);
|
|
322
330
|
return {
|
|
323
331
|
...(typeof row.taskId === 'string' && row.taskId !== '' ? { taskId: row.taskId } : {}),
|
|
324
332
|
...(gatedCallId !== undefined && gatedCallId !== '' ? { toolCallId: gatedCallId } : {}),
|
|
325
333
|
...(typeof row.sessionId === 'string' && row.sessionId !== '' ? { sessionId: row.sessionId } : {}),
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
function fsOutcomeOfJoinedLiveCard(o, gatedCallId) {
|
|
329
|
-
if (o.aborted === true)
|
|
330
|
-
return { kind: 'aborted', gatedCallId, joinedInFlight: true };
|
|
331
|
-
if (o.decision === 'unresolved')
|
|
332
|
-
return { kind: 'retracted', gatedCallId, joinedInFlight: true };
|
|
333
|
-
return {
|
|
334
|
-
kind: 'decided',
|
|
335
|
-
gatedCallId,
|
|
336
|
-
...(o.decision === 'deny'
|
|
337
|
-
? {
|
|
338
|
-
denied: true,
|
|
339
|
-
...(o.denySettledBy !== undefined ? { denySettledBy: o.denySettledBy } : {}),
|
|
340
|
-
...(o.denyReason !== undefined ? { denyReason: o.denyReason } : {}),
|
|
341
|
-
}
|
|
342
|
-
: {}),
|
|
343
|
-
...(o.denyAttributionDropped === true ? { denyAttributionDropped: true } : {}),
|
|
344
|
-
...(o.denyAttributionRenamed === true ? { denyAttributionRenamed: true } : {}),
|
|
345
|
-
joinedInFlight: true,
|
|
334
|
+
...(askSig !== undefined ? { askSig } : {}),
|
|
346
335
|
};
|
|
347
336
|
}
|
|
348
337
|
async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold, ledgerSlot) {
|
|
349
338
|
const { pending, gatedCallId } = found;
|
|
339
|
+
const at = { lane: 'decide', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
|
|
350
340
|
const toolName = typeof pending.toolName === 'string' ? pending.toolName : '';
|
|
351
341
|
const streamArgs = gatedCallId !== undefined ? argsByCall.get(gatedCallId) : undefined;
|
|
352
342
|
const rowInput = pending.input;
|
|
@@ -359,7 +349,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
359
349
|
: undefined;
|
|
360
350
|
const args = streamArgs !== undefined && streamArgs !== null ? streamArgs : rowArgsGap !== undefined ? {} : rowInput;
|
|
361
351
|
if (typeof args !== 'object' || args === null) {
|
|
362
|
-
return { kind: '
|
|
352
|
+
return { kind: 'not_sent', ...at, cause: 'no_input', detail: 'gate has no tool input payload' };
|
|
363
353
|
}
|
|
364
354
|
const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
|
|
365
355
|
const ruleOffersReadOnly = readRuleOffers(pending.ruleOffers);
|
|
@@ -381,7 +371,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
381
371
|
}
|
|
382
372
|
: {}),
|
|
383
373
|
...(pending.governanceForced === true ? { governanceForced: true } : {}),
|
|
384
|
-
...(pending
|
|
374
|
+
...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
|
|
385
375
|
...(hasOwnStrictTrue(pending, 'mandated') ? { mandated: true } : {}),
|
|
386
376
|
...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(pending)),
|
|
387
377
|
...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(pending)),
|
|
@@ -402,92 +392,81 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
402
392
|
: {}),
|
|
403
393
|
allowSessionCarried: true,
|
|
404
394
|
grantBinding: readApprovalGrantBinding(pending),
|
|
395
|
+
parkRow: parkRowOf(taskId, pending, gatedCallId),
|
|
405
396
|
}));
|
|
397
|
+
hold.phase = 'respond';
|
|
406
398
|
if (rowArgsGap !== undefined && card.kind === 'allow' && card.updatedInput !== undefined) {
|
|
407
399
|
hostLog('error', `liveToolApprovalWire: approval card for ${gatedCallId ?? `task ${taskId}`} returned an edited approval, but the tool input is ${rowArgsGap === 'truncated' ? 'a size marker on the pending row' : 'missing from both the stream and the pending row'} — nothing sent (the park stays pending)`);
|
|
408
400
|
surfaceEditRefusedOnBlindAsk();
|
|
409
|
-
return { kind: '
|
|
401
|
+
return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: PARK_EDIT_REFUSED_REASON };
|
|
410
402
|
}
|
|
411
403
|
const decideSpan = bridge.openDecideOperation();
|
|
412
404
|
const decideOpts = { ...(signal ? { signal } : {}), operation: decideSpan };
|
|
413
405
|
try {
|
|
414
406
|
switch (card.kind) {
|
|
415
407
|
case 'failed':
|
|
416
|
-
return { kind: '
|
|
408
|
+
return { kind: 'not_sent', ...at, cause: 'card_unavailable', ...(typeof card.reason === 'string' && card.reason !== '' ? { detail: card.reason } : {}) };
|
|
417
409
|
case 'retracted':
|
|
418
410
|
hostLog('debug', `liveHitlAskWire: approval card retracted without a decision for ${gatedCallId ?? '(no call)'} — nothing sent`);
|
|
419
|
-
return { kind: '
|
|
411
|
+
return { kind: 'not_sent', ...at, cause: 'retracted' };
|
|
420
412
|
case 'aborted':
|
|
421
413
|
{
|
|
422
414
|
const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
|
|
423
415
|
hold.trailing = cancelDeny;
|
|
424
416
|
observeCancelByDeny(cancelDeny, taskId);
|
|
425
417
|
}
|
|
426
|
-
return { kind: '
|
|
418
|
+
return { kind: 'unsettled', ...at, cause: 'interrupted' };
|
|
427
419
|
case 'allow':
|
|
428
420
|
try {
|
|
429
421
|
let rememberRefused = false;
|
|
430
|
-
|
|
422
|
+
const approveBody = answerPayloadFor(card, 'decide');
|
|
423
|
+
const { remember: _remember, ...plainApproveBody } = approveBody;
|
|
424
|
+
void _remember;
|
|
425
|
+
emitAnswerCommitted(callKey, 'decide', card, approveBody);
|
|
426
|
+
if (approveBody.remember === 'session') {
|
|
431
427
|
let sessionReceipt;
|
|
432
428
|
let sessionDecided = false;
|
|
433
429
|
try {
|
|
434
|
-
const raw = await bridge.decideTool(
|
|
430
|
+
const raw = await bridge.decideTool(approveBody, gatedCallId, decideOpts, pending);
|
|
435
431
|
sessionReceipt = readDecideReceipt(raw);
|
|
436
432
|
sessionDecided = true;
|
|
437
433
|
}
|
|
438
434
|
catch (e) {
|
|
439
|
-
|
|
440
|
-
if (fallbackKind === undefined)
|
|
435
|
+
if (rememberRefusalOf(e) === undefined)
|
|
441
436
|
throw e;
|
|
442
|
-
rememberRefused =
|
|
437
|
+
rememberRefused = true;
|
|
443
438
|
logAfterDecided(`liveToolApprovalWire: decide(approve+remember) refused the remember part (${String(e)}) — falling back to plain approve`);
|
|
444
439
|
}
|
|
445
440
|
if (sessionDecided) {
|
|
446
441
|
if (sessionReceipt?.rememberApplied === false)
|
|
447
442
|
surfaceRememberNotApplied();
|
|
448
|
-
return { kind: 'decided',
|
|
443
|
+
return { kind: 'decided', ...at, decision: 'allow', ...(sessionReceipt !== undefined ? { receipt: sessionReceipt } : {}) };
|
|
449
444
|
}
|
|
450
445
|
}
|
|
451
|
-
const raw = await bridge.decideTool(
|
|
446
|
+
const raw = await bridge.decideTool(plainApproveBody, gatedCallId, decideOpts, pending);
|
|
452
447
|
const receipt = readDecideReceipt(raw);
|
|
453
448
|
if (rememberRefused)
|
|
454
449
|
surfaceRememberNotApplied();
|
|
455
|
-
return { kind: 'decided',
|
|
450
|
+
return { kind: 'decided', ...at, decision: 'allow', ...(receipt !== undefined ? { receipt } : {}) };
|
|
456
451
|
}
|
|
457
452
|
catch (e) {
|
|
458
|
-
const currentPending = readDecideCurrentPending(e);
|
|
459
|
-
const wireCodes = readWireCodes(e);
|
|
460
|
-
const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
|
|
461
|
-
const safetyCode = hitlSafetyCodeOf(e);
|
|
462
453
|
const failureTexts = wireFailureTextsOf(e);
|
|
463
454
|
hostLog('debug', `liveToolApprovalWire: decide(approve) failed: ${failureTexts.log}`);
|
|
464
|
-
return {
|
|
465
|
-
kind: 'failed',
|
|
466
|
-
stage: 'decide',
|
|
467
|
-
gatedCallId,
|
|
468
|
-
reason: `decide(approve) failed: ${failureTexts.reason}${decideFailureNote(e, 'approve')}`,
|
|
469
|
-
...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
|
|
470
|
-
...(currentPending !== undefined ? { currentPending } : {}),
|
|
471
|
-
...wireCodes,
|
|
472
|
-
...(wireRetriable ? { retriable: true } : {}),
|
|
473
|
-
...(safetyCode !== undefined ? { safetyCode } : {}),
|
|
474
|
-
};
|
|
455
|
+
return resolutionOfDecisionFailure(e, { lane: 'decide', route: 'approval_decide', gatedCallId, detail: `decide(approve) failed: ${failureTexts.reason}${decideFailureNote(e, 'approve')}` });
|
|
475
456
|
}
|
|
476
457
|
case 'deny': {
|
|
477
458
|
const denySettledByForWire = readApprovalDenySettledBy(card);
|
|
478
459
|
const attributionKeys = decisionAttributionKeyOf(decidedByReading, denySettledByForWire);
|
|
479
|
-
const denyReasonSnapshot = typeof
|
|
460
|
+
const denyReasonSnapshot = ((r) => (typeof r === 'string' ? r : undefined))(card.reason);
|
|
480
461
|
let lastSendCarriedAttribution = false;
|
|
481
462
|
try {
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
reason: denyReasonForWire(denyReasonSnapshot, `task ${taskId}`) ?? DEFAULT_DENY_REASON,
|
|
485
|
-
...attributionKeys,
|
|
486
|
-
};
|
|
463
|
+
const denyPayload = { decision: 'deny', reason: answerPayloadFor({ kind: 'deny', ...(denyReasonSnapshot !== undefined ? { reason: denyReasonSnapshot } : {}) }, 'decide').reason };
|
|
464
|
+
const denyBody = { ...denyPayload, ...attributionKeys };
|
|
487
465
|
let attributionDropped = false;
|
|
488
466
|
let attributionRenamed = false;
|
|
489
467
|
let raw;
|
|
490
468
|
lastSendCarriedAttribution = carriesAttributionKey(attributionKeys);
|
|
469
|
+
emitAnswerCommitted(callKey, 'decide', card, denyPayload);
|
|
491
470
|
try {
|
|
492
471
|
raw = await bridge.decideTool(denyBody, gatedCallId, decideOpts, pending);
|
|
493
472
|
}
|
|
@@ -527,39 +506,25 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
527
506
|
}
|
|
528
507
|
}
|
|
529
508
|
const receipt = readDecideReceipt(raw);
|
|
530
|
-
const
|
|
531
|
-
const denyReason = denyReasonSnapshot !== undefined && denyReasonSnapshot.trim() !== '' ? denyReasonSnapshot : undefined;
|
|
509
|
+
const deny = denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed);
|
|
532
510
|
return {
|
|
533
511
|
kind: 'decided',
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
...(
|
|
537
|
-
...(denyReason !== undefined ? { denyReason } : {}),
|
|
538
|
-
...(attributionDropped ? { denyAttributionDropped: true } : {}),
|
|
539
|
-
...(attributionRenamed ? { denyAttributionRenamed: true } : {}),
|
|
512
|
+
...at,
|
|
513
|
+
decision: 'deny',
|
|
514
|
+
...(deny !== undefined ? { deny } : {}),
|
|
540
515
|
...(receipt !== undefined ? { receipt } : {}),
|
|
541
516
|
};
|
|
542
517
|
}
|
|
543
518
|
catch (e) {
|
|
544
|
-
const currentPending = readDecideCurrentPending(e);
|
|
545
|
-
const wireCodes = readWireCodes(e);
|
|
546
|
-
const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
|
|
547
|
-
const safetyCode = hitlSafetyCodeOf(e);
|
|
548
|
-
const attributionRefusal = denyAttributionRefusalFromError(e, lastSendCarriedAttribution);
|
|
549
519
|
const failureTexts = wireFailureTextsOf(e);
|
|
550
520
|
hostLog('debug', `liveToolApprovalWire: decide(deny) failed: ${failureTexts.log}`);
|
|
551
|
-
return {
|
|
552
|
-
|
|
553
|
-
|
|
521
|
+
return resolutionOfDecisionFailure(e, {
|
|
522
|
+
lane: 'decide',
|
|
523
|
+
route: 'approval_decide',
|
|
554
524
|
gatedCallId,
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
...wireCodes,
|
|
559
|
-
...(wireRetriable ? { retriable: true } : {}),
|
|
560
|
-
...(safetyCode !== undefined ? { safetyCode } : {}),
|
|
561
|
-
...(attributionRefusal !== null ? { denyAttributionRefusal: attributionRefusal } : {}),
|
|
562
|
-
};
|
|
525
|
+
detail: `decide(deny) failed: ${failureTexts.reason}${decideFailureNote(e, 'deny')}`,
|
|
526
|
+
attributionCarried: lastSendCarriedAttribution,
|
|
527
|
+
});
|
|
563
528
|
}
|
|
564
529
|
}
|
|
565
530
|
}
|
|
@@ -610,6 +575,7 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
|
|
|
610
575
|
'probeReason',
|
|
611
576
|
'rememberRefused',
|
|
612
577
|
'boundGrantDigest',
|
|
578
|
+
'boundGrantAbsence',
|
|
613
579
|
'boundGrants',
|
|
614
580
|
'classifierNoRuling',
|
|
615
581
|
'ruleEvidence',
|
|
@@ -621,7 +587,7 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
|
|
|
621
587
|
'outcome',
|
|
622
588
|
'cause',
|
|
623
589
|
];
|
|
624
|
-
const _mirrorPin = [true
|
|
590
|
+
const _mirrorPin = [true];
|
|
625
591
|
void _mirrorPin;
|
|
626
592
|
const MANDATED_ABSENCE_WORD = 'mandated';
|
|
627
593
|
export function approvalIsMandated(v) {
|
|
@@ -686,20 +652,76 @@ function isToolApprovalDelegation(v) {
|
|
|
686
652
|
return d.agentName === undefined || typeof d.agentName === 'string';
|
|
687
653
|
}
|
|
688
654
|
export function isFromSubagent(frame) {
|
|
689
|
-
|
|
690
|
-
return true;
|
|
691
|
-
return typeof frame.sourceTaskId === 'string' && frame.sourceTaskId.length > 0;
|
|
655
|
+
return frame.fromSubagent === true;
|
|
692
656
|
}
|
|
693
|
-
const RESPOND_DECISIONS = ['allow', 'allow_session', 'deny'];
|
|
657
|
+
export const RESPOND_DECISIONS = Object.freeze(['allow', 'allow_session', 'deny']);
|
|
694
658
|
const _respondDecisionPins = [true, true];
|
|
695
659
|
void _respondDecisionPins;
|
|
696
660
|
function isRespondDecision(v) {
|
|
697
661
|
return typeof v === 'string' && RESPOND_DECISIONS.includes(v);
|
|
698
662
|
}
|
|
663
|
+
function respondDecisionOfAnswer(answer) {
|
|
664
|
+
if (answer.kind === 'allow')
|
|
665
|
+
return answer.allowSession ? 'allow_session' : 'allow';
|
|
666
|
+
return answer.kind === 'deny' ? 'deny' : undefined;
|
|
667
|
+
}
|
|
668
|
+
function respondNoteGate(reason, capable) {
|
|
669
|
+
if (reason === undefined || reason.trim() === '')
|
|
670
|
+
return { dropped: 'blank' };
|
|
671
|
+
if (!capable)
|
|
672
|
+
return { dropped: 'capability' };
|
|
673
|
+
if (reason.length > MAX_RESPOND_NOTE_CHARS)
|
|
674
|
+
return { dropped: 'oversize' };
|
|
675
|
+
return { note: reason };
|
|
676
|
+
}
|
|
677
|
+
export function answerPayloadFor(answer, lane, caps) {
|
|
678
|
+
const word = respondDecisionOfAnswer(answer);
|
|
679
|
+
if (word === undefined)
|
|
680
|
+
return undefined;
|
|
681
|
+
if (lane === 'decide') {
|
|
682
|
+
if (answer.kind !== 'allow') {
|
|
683
|
+
const reason = answer.kind === 'deny' && typeof answer.reason === 'string' ? answer.reason : undefined;
|
|
684
|
+
return Object.freeze({ decision: 'deny', reason: denyReasonForWire(reason, 'answer') ?? DEFAULT_DENY_REASON });
|
|
685
|
+
}
|
|
686
|
+
return Object.freeze({
|
|
687
|
+
decision: 'approve',
|
|
688
|
+
...(word === 'allow_session' ? { remember: 'session' } : {}),
|
|
689
|
+
...(answer.updatedInput !== undefined ? { updatedInput: answer.updatedInput } : {}),
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
if (answer.kind === 'allow')
|
|
693
|
+
return Object.freeze({ decision: word, ...(answer.updatedInput !== undefined ? { updatedInput: answer.updatedInput } : {}) });
|
|
694
|
+
const gate = respondNoteGate(answer.kind === 'deny' && typeof answer.reason === 'string' ? answer.reason : undefined, caps?.approvalDecisionNoteCapable === true);
|
|
695
|
+
return Object.freeze({ decision: word, ...('note' in gate ? { note: gate.note } : {}) });
|
|
696
|
+
}
|
|
697
|
+
const ANSWER_CARRIAGE_NONE = Object.freeze({ allowNote: 'none' });
|
|
698
|
+
export function answerCarriage(card) {
|
|
699
|
+
void card;
|
|
700
|
+
return ANSWER_CARRIAGE_NONE;
|
|
701
|
+
}
|
|
702
|
+
export function observeApprovalAnswerCommitted(listener) {
|
|
703
|
+
answerCommittedListeners.add(listener);
|
|
704
|
+
return () => {
|
|
705
|
+
answerCommittedListeners.delete(listener);
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
const answerCommittedListeners = new Set();
|
|
709
|
+
function emitAnswerCommitted(callKey, lane, answer, payload) {
|
|
710
|
+
if (answerCommittedListeners.size === 0)
|
|
711
|
+
return;
|
|
712
|
+
const event = Object.freeze({ callKey, lane, answer: Object.freeze({ ...answer }), payload });
|
|
713
|
+
for (const l of [...answerCommittedListeners])
|
|
714
|
+
invokeHostCallbackIsolated('liveToolApprovalWire: answer-committed listener', () => l(event));
|
|
715
|
+
}
|
|
716
|
+
export function isToolApprovalRespondReplayed(ack) {
|
|
717
|
+
return typeof ack === 'object' && ack !== null && ack.idempotent === true;
|
|
718
|
+
}
|
|
699
719
|
export function readToolApprovalRespondAck(v) {
|
|
700
720
|
if (v === null || typeof v !== 'object')
|
|
701
721
|
return undefined;
|
|
702
722
|
const o = v;
|
|
723
|
+
if (o.idempotent === true)
|
|
724
|
+
return o.delivery === undefined ? readReplayedAck(o) : undefined;
|
|
703
725
|
if (o.delivery !== 'applied')
|
|
704
726
|
return undefined;
|
|
705
727
|
if (typeof o.approvalId !== 'string' || o.approvalId.length === 0)
|
|
@@ -773,6 +795,48 @@ export function readToolApprovalRespondParking(v) {
|
|
|
773
795
|
},
|
|
774
796
|
};
|
|
775
797
|
}
|
|
798
|
+
function replayClaimOf(raw) {
|
|
799
|
+
const approvalId = guardedRead(() => readOwnDataNonEmptyString(raw, 'approvalId'));
|
|
800
|
+
const d = guardedRead(() => readOwnDataValue(raw, 'decision'));
|
|
801
|
+
let carriesDelivery = true;
|
|
802
|
+
try {
|
|
803
|
+
carriesDelivery = typeof raw === 'object' && raw !== null && Object.hasOwn(raw, 'delivery');
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
carriesDelivery = true;
|
|
807
|
+
}
|
|
808
|
+
return { ...(approvalId !== undefined ? { approvalId } : {}), ...(d === 'allow' || d === 'deny' ? { decision: d } : {}), carriesDelivery };
|
|
809
|
+
}
|
|
810
|
+
function ownIdempotentTrue(raw) {
|
|
811
|
+
if (typeof raw !== 'object' || raw === null)
|
|
812
|
+
return false;
|
|
813
|
+
try {
|
|
814
|
+
const d = Object.getOwnPropertyDescriptor(raw, 'idempotent');
|
|
815
|
+
return d !== undefined && Object.hasOwn(d, 'value') && d.value === true;
|
|
816
|
+
}
|
|
817
|
+
catch {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function readReplayedAck(o) {
|
|
822
|
+
if (typeof o.approvalId !== 'string' || o.approvalId.length === 0)
|
|
823
|
+
return undefined;
|
|
824
|
+
if (typeof o.askId !== 'string' || o.askId.length === 0)
|
|
825
|
+
return undefined;
|
|
826
|
+
if (o.decision !== 'allow' && o.decision !== 'deny')
|
|
827
|
+
return undefined;
|
|
828
|
+
if (typeof o.decidedAtMs !== 'number' || !Number.isFinite(o.decidedAtMs) || o.decidedAtMs < 0)
|
|
829
|
+
return undefined;
|
|
830
|
+
return {
|
|
831
|
+
approvalId: o.approvalId,
|
|
832
|
+
askId: o.askId,
|
|
833
|
+
idempotent: true,
|
|
834
|
+
decision: o.decision,
|
|
835
|
+
decidedAtMs: o.decidedAtMs,
|
|
836
|
+
...(typeof o.decisionNote === 'string' && o.decisionNote !== '' ? { decisionNote: o.decisionNote } : {}),
|
|
837
|
+
...(o.rememberApplied === false ? { rememberApplied: false } : {}),
|
|
838
|
+
};
|
|
839
|
+
}
|
|
776
840
|
function isNonEmptyStringArray(v) {
|
|
777
841
|
return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
|
|
778
842
|
}
|
|
@@ -852,58 +916,16 @@ function readDecidedByReadingOf(holder) {
|
|
|
852
916
|
function decidedByReadingSnapshotOf(holder) {
|
|
853
917
|
return approvalDecidedByAdmitted(readDecidedByReadingOf(holder)) ? Object.freeze({ kind: 'present', value: true }) : undefined;
|
|
854
918
|
}
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
860
|
-
catch {
|
|
861
|
-
return undefined;
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
const pickMachine = (key) => guardedRead(() => (typeof err === 'object' && err !== null ? readOwnDataValue(err, key) : undefined));
|
|
865
|
-
const rawStatus = pickMachine('status');
|
|
866
|
-
const rawCode = pickMachine('errorCode');
|
|
867
|
-
const status = typeof rawStatus === 'number' && Number.isFinite(rawStatus) ? rawStatus : undefined;
|
|
868
|
-
const errorCode = typeof rawCode === 'string' && rawCode.length > 0 ? rawCode : undefined;
|
|
869
|
-
const message = refusalTextOf(err, pick('message'));
|
|
870
|
-
if (status === undefined && errorCode === undefined && message === undefined)
|
|
919
|
+
function denyFactsOf(settledBy, reasonSnapshot, dropped, renamed) {
|
|
920
|
+
const reason = reasonSnapshot !== undefined && reasonSnapshot.trim() !== '' ? reasonSnapshot : undefined;
|
|
921
|
+
const attributionKey = dropped ? 'dropped' : renamed ? 'renamed' : undefined;
|
|
922
|
+
if (settledBy === undefined && reason === undefined && attributionKey === undefined)
|
|
871
923
|
return undefined;
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const unreadable = machine !== undefined && readDecisionRefusalAsk(machine).reading === 'unreadable';
|
|
877
|
-
const rest = {
|
|
878
|
-
...(status !== undefined ? { status } : {}),
|
|
879
|
-
...(errorCode !== undefined ? { errorCode } : {}),
|
|
880
|
-
...(message !== undefined ? { message } : {}),
|
|
881
|
-
...(retriable ? { retriable: true } : {}),
|
|
882
|
-
...(cause !== undefined || field !== undefined
|
|
883
|
-
? { extra: { ...(cause !== undefined ? { cause } : {}), ...(field !== undefined ? { field } : {}) } }
|
|
884
|
-
: {}),
|
|
885
|
-
...(unreadable ? { unreadable: true } : {}),
|
|
924
|
+
return {
|
|
925
|
+
...(settledBy !== undefined ? { settledBy } : {}),
|
|
926
|
+
...(reason !== undefined ? { reason } : {}),
|
|
927
|
+
...(attributionKey !== undefined ? { attributionKey } : {}),
|
|
886
928
|
};
|
|
887
|
-
if (status !== undefined)
|
|
888
|
-
return { ...rest, status };
|
|
889
|
-
if (errorCode !== undefined)
|
|
890
|
-
return { ...rest, errorCode };
|
|
891
|
-
return { ...rest, message: message };
|
|
892
|
-
}
|
|
893
|
-
const USELESS_REFUSAL_TEXTS = new Set(['[object Object]', 'null', 'undefined', '']);
|
|
894
|
-
function refusalTextOf(err, message) {
|
|
895
|
-
if (typeof message === 'string' && message.length > 0)
|
|
896
|
-
return message;
|
|
897
|
-
if (typeof err === 'string' && err.length > 0)
|
|
898
|
-
return err;
|
|
899
|
-
let coerced;
|
|
900
|
-
try {
|
|
901
|
-
coerced = String(err);
|
|
902
|
-
}
|
|
903
|
-
catch {
|
|
904
|
-
return undefined;
|
|
905
|
-
}
|
|
906
|
-
return USELESS_REFUSAL_TEXTS.has(coerced) ? undefined : coerced;
|
|
907
929
|
}
|
|
908
930
|
const MACHINE_CODE_SHAPE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
909
931
|
function logSafeErrorCode(code) {
|
|
@@ -947,23 +969,24 @@ function usableProbeReason(raw) {
|
|
|
947
969
|
return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
|
|
948
970
|
}
|
|
949
971
|
const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
|
|
950
|
-
export function askProbeReasonDetail(card) {
|
|
972
|
+
export function askProbeReasonDetail(card, lang) {
|
|
951
973
|
const prose = usableProbeReason(ownDataValue(card, 'probeReason'));
|
|
952
974
|
const shown = prose === undefined ? '' : displayUntrusted(prose);
|
|
953
|
-
const sentence = probeCauseSentence(ownDataValue(card, 'probeCause'), shown !== '');
|
|
975
|
+
const sentence = probeCauseSentence(ownDataValue(card, 'probeCause'), shown !== '', lang);
|
|
954
976
|
if (sentence === undefined)
|
|
955
977
|
return shown === '' ? undefined : shown;
|
|
956
|
-
return shown === '' ? sentence :
|
|
978
|
+
return shown === '' ? sentence : renderMessage(msg('approval.probe.proseJoin', { sentence, prose: shown }), lang);
|
|
957
979
|
}
|
|
958
|
-
function probeCauseSentence(cause, withProse) {
|
|
980
|
+
function probeCauseSentence(cause, withProse, lang) {
|
|
959
981
|
try {
|
|
960
982
|
if (!isWireRecordCarrier(cause))
|
|
961
983
|
return undefined;
|
|
962
984
|
const code = ownDataValue(cause, 'code');
|
|
963
|
-
const
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
985
|
+
const withCode = typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code);
|
|
986
|
+
const key = withProse
|
|
987
|
+
? withCode ? 'approval.probe.withProseCode' : 'approval.probe.withProse'
|
|
988
|
+
: withCode ? 'approval.probe.noProseCode' : 'approval.probe.noProse';
|
|
989
|
+
return renderMessage(withCode ? msg(key, { code }) : msg(key), lang);
|
|
967
990
|
}
|
|
968
991
|
catch {
|
|
969
992
|
return undefined;
|
|
@@ -1021,28 +1044,82 @@ export function readApprovalBoundGrants(v) {
|
|
|
1021
1044
|
return undefined;
|
|
1022
1045
|
return Object.freeze({ skill, allowedTools, disallowedTools });
|
|
1023
1046
|
}
|
|
1047
|
+
const READS_AS_NOTHING_HEAD = '<reads as nothing (';
|
|
1048
|
+
export function readBoundGrantEntry(entry) {
|
|
1049
|
+
if (typeof entry !== 'string' || entry === '')
|
|
1050
|
+
return undefined;
|
|
1051
|
+
if (!entry.startsWith('<'))
|
|
1052
|
+
return Object.freeze({ kind: 'rule' });
|
|
1053
|
+
if (entry.startsWith(READS_AS_NOTHING_HEAD)) {
|
|
1054
|
+
const close = entry.indexOf('): ', READS_AS_NOTHING_HEAD.length);
|
|
1055
|
+
const word = close > 0 ? entry.slice(READS_AS_NOTHING_HEAD.length, close) : undefined;
|
|
1056
|
+
if (word !== undefined && CORE_ALLOW_ENTRY_INERT_REASONS.includes(word)) {
|
|
1057
|
+
return Object.freeze({ kind: 'reads_as_nothing', reason: word });
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return Object.freeze({ kind: 'reads_as_nothing' });
|
|
1061
|
+
}
|
|
1062
|
+
const BOUND_GRANT_NOT_BINDABLE = 'not_bindable';
|
|
1063
|
+
const SERVER_CLASS_READ_CAP = 64;
|
|
1024
1064
|
function readApprovalGrantBinding(carrier) {
|
|
1025
1065
|
if (typeof carrier !== 'object' || carrier === null)
|
|
1026
1066
|
return 'unreadable';
|
|
1027
1067
|
try {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1068
|
+
const card = cardGrantBindingOf(carrier);
|
|
1069
|
+
const mayCall = serverMayReadCallLaw(carrier);
|
|
1070
|
+
if (cardGrantBindingOf(carrier) !== card)
|
|
1071
|
+
return 'unreadable';
|
|
1072
|
+
if (!mayCall)
|
|
1073
|
+
return card;
|
|
1074
|
+
const call = callGrantBindingOf(carrier);
|
|
1075
|
+
if (callGrantBindingOf(carrier) !== call || cardGrantBindingOf(carrier) !== card)
|
|
1034
1076
|
return 'unreadable';
|
|
1035
|
-
return
|
|
1077
|
+
return card === 'none' && call === 'none' ? 'none' : 'unreadable';
|
|
1036
1078
|
}
|
|
1037
1079
|
catch {
|
|
1038
1080
|
return 'unreadable';
|
|
1039
1081
|
}
|
|
1040
1082
|
}
|
|
1083
|
+
function cardGrantBindingOf(carrier) {
|
|
1084
|
+
if (Object.hasOwn(carrier, 'boundGrantDigest') || 'boundGrantDigest' in carrier) {
|
|
1085
|
+
const digest = ownDataValue(carrier, 'boundGrantDigest');
|
|
1086
|
+
return typeof digest === 'string' && digest.length > 0 ? 'bound' : 'unreadable';
|
|
1087
|
+
}
|
|
1088
|
+
const seen = carrier;
|
|
1089
|
+
return ownDataValue(carrier, 'boundGrantAbsence') === BOUND_GRANT_NOT_BINDABLE && seen.boundGrantAbsence === BOUND_GRANT_NOT_BINDABLE && seen.boundGrantDigest === undefined
|
|
1090
|
+
? 'none'
|
|
1091
|
+
: 'unreadable';
|
|
1092
|
+
}
|
|
1093
|
+
function callGrantBindingOf(carrier) {
|
|
1094
|
+
const tool = ownDataValue(carrier, 'toolName');
|
|
1095
|
+
return typeof tool === 'string' && tool.length > 0 && tool !== CORE_SKILL_TOOL_NAME && carrier.toolName === tool ? 'none' : 'unreadable';
|
|
1096
|
+
}
|
|
1097
|
+
function serverMayReadCallLaw(carrier) {
|
|
1098
|
+
const seen = carrier;
|
|
1099
|
+
const classes = seen.classes;
|
|
1100
|
+
if (!Array.isArray(classes))
|
|
1101
|
+
return false;
|
|
1102
|
+
const listClass = (c) => DEPLOYMENT_LIST_ASK_CLASSES.includes(c);
|
|
1103
|
+
if (listClass(seen.askClass))
|
|
1104
|
+
return true;
|
|
1105
|
+
const length = classes.length;
|
|
1106
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length > SERVER_CLASS_READ_CAP)
|
|
1107
|
+
return true;
|
|
1108
|
+
for (let i = 0; i < length; i++)
|
|
1109
|
+
if (listClass(classes[i]))
|
|
1110
|
+
return true;
|
|
1111
|
+
let n = 0;
|
|
1112
|
+
for (const c of classes) {
|
|
1113
|
+
if (listClass(c) || ++n > SERVER_CLASS_READ_CAP)
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1041
1118
|
const UNREADABLE_EVIDENCE_KEYS = Object.freeze({
|
|
1042
1119
|
frame: Object.freeze([
|
|
1043
1120
|
['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
|
|
1044
1121
|
['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffers'],
|
|
1045
|
-
['allowRuleShadowed', 'allowRuleShadow'], ['
|
|
1122
|
+
['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
|
|
1046
1123
|
]),
|
|
1047
1124
|
row: Object.freeze([
|
|
1048
1125
|
['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
|
|
@@ -1051,8 +1128,9 @@ const UNREADABLE_EVIDENCE_KEYS = Object.freeze({
|
|
|
1051
1128
|
]),
|
|
1052
1129
|
v1: Object.freeze([
|
|
1053
1130
|
['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
|
|
1054
|
-
['allowRuleShadowed', 'allowRuleShadow'], ['
|
|
1131
|
+
['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
|
|
1055
1132
|
]),
|
|
1133
|
+
generic: Object.freeze([['ruleOffersAbsence', 'ruleOffersAbsence'], ['askClass', 'askClass'], ['classes', 'classes']]),
|
|
1056
1134
|
});
|
|
1057
1135
|
function carrierHas(carrier, key) {
|
|
1058
1136
|
if (typeof carrier !== 'object' || carrier === null)
|
|
@@ -1075,17 +1153,21 @@ function v1RiskUnreadable(card) {
|
|
|
1075
1153
|
return true;
|
|
1076
1154
|
}
|
|
1077
1155
|
}
|
|
1078
|
-
function withUnreadableEvidence(carrier, leg, req) {
|
|
1079
|
-
const keys = new Set();
|
|
1156
|
+
function withUnreadableEvidence(carrier, leg, req, reported = []) {
|
|
1157
|
+
const keys = new Set(reported);
|
|
1080
1158
|
for (const [from, to] of UNREADABLE_EVIDENCE_KEYS[leg]) {
|
|
1081
1159
|
if (carrierHas(carrier, from) && !Object.hasOwn(req, to))
|
|
1082
1160
|
keys.add(from);
|
|
1083
1161
|
}
|
|
1084
1162
|
if (leg === 'row') {
|
|
1085
1163
|
const rd = ownDataValue(carrier, 'riskDescriptor');
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1164
|
+
if (carrierHas(rd, 'allowRuleShadowed') && !Object.hasOwn(req, 'allowRuleShadow'))
|
|
1165
|
+
keys.add('riskDescriptor.allowRuleShadowed');
|
|
1166
|
+
}
|
|
1167
|
+
if (leg !== 'generic' && !Object.hasOwn(req, 'allowRuleShadow')) {
|
|
1168
|
+
const shadowLeg = leg === 'row' ? 'row' : 'frame';
|
|
1169
|
+
if (retiredAllowRuleShadowKeyPresent(carrier, shadowLeg))
|
|
1170
|
+
keys.add(RETIRED_SHADOW_EVIDENCE_LABEL[shadowLeg]);
|
|
1089
1171
|
}
|
|
1090
1172
|
if (leg === 'v1' && v1RiskUnreadable(carrier))
|
|
1091
1173
|
keys.add('risk');
|
|
@@ -1097,40 +1179,62 @@ export function readApprovalClassifierNoRuling(v) {
|
|
|
1097
1179
|
return typeof w === 'string' && CAUSE_WORD.test(w) ? w : undefined;
|
|
1098
1180
|
}
|
|
1099
1181
|
const CLASSIFIER_NO_RULING_REASONS = Object.freeze({
|
|
1100
|
-
error: '
|
|
1101
|
-
timeout: '
|
|
1102
|
-
parse_error: '
|
|
1182
|
+
error: 'approval.classifier.noRulingError',
|
|
1183
|
+
timeout: 'approval.classifier.noRulingTimeout',
|
|
1184
|
+
parse_error: 'approval.classifier.noRulingParseError',
|
|
1103
1185
|
});
|
|
1104
1186
|
const _noRulingPin = [true, true];
|
|
1105
1187
|
void _noRulingPin;
|
|
1106
|
-
export function classifierNoRulingDetail(word) {
|
|
1188
|
+
export function classifierNoRulingDetail(word, lang) {
|
|
1107
1189
|
if (typeof word === 'string' && Object.hasOwn(CLASSIFIER_NO_RULING_REASONS, word)) {
|
|
1108
|
-
return
|
|
1190
|
+
return renderMessage(msg(CLASSIFIER_NO_RULING_REASONS[word]), lang);
|
|
1109
1191
|
}
|
|
1110
1192
|
if (typeof word === 'string' && CAUSE_WORD.test(word))
|
|
1111
|
-
return
|
|
1112
|
-
return '
|
|
1193
|
+
return renderMessage(msg('approval.classifier.noRulingOther', { reason: word }), lang);
|
|
1194
|
+
return renderMessage(msg('approval.classifier.noRulingBare'), lang);
|
|
1113
1195
|
}
|
|
1114
|
-
|
|
1115
|
-
export function approvalBoundGrantsDetail(grants) {
|
|
1196
|
+
export function approvalBoundGrantsDetail(grants, lang) {
|
|
1116
1197
|
const g = readApprovalBoundGrants({ boundGrants: grants });
|
|
1117
1198
|
if (g === undefined)
|
|
1118
1199
|
return undefined;
|
|
1119
1200
|
if (g.skill === null)
|
|
1120
|
-
return '
|
|
1121
|
-
const
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
|
|
1201
|
+
return renderMessage(msg('approval.boundGrants.noSkill'), lang);
|
|
1202
|
+
const isRule = (e) => readBoundGrantEntry(e)?.kind === 'rule';
|
|
1203
|
+
const a = g.allowedTools.filter(isRule).length;
|
|
1204
|
+
const d = g.disallowedTools.filter(isRule).length;
|
|
1205
|
+
const allowNothing = g.allowedTools.length - a;
|
|
1206
|
+
const denyNothing = g.disallowedTools.length - d;
|
|
1207
|
+
if (denyNothing > 0) {
|
|
1208
|
+
return renderMessage(denyNothing === 1 ? msg('approval.boundGrants.notActivated.one') : msg('approval.boundGrants.notActivated.other', { count: String(denyNothing) }), lang);
|
|
1209
|
+
}
|
|
1210
|
+
if (a === 0 && d === 0) {
|
|
1211
|
+
if (allowNothing > 0)
|
|
1212
|
+
return renderMessage(allowNothing === 1 ? msg('approval.boundGrants.listsNoToolsWithNothing.one') : msg('approval.boundGrants.listsNoToolsWithNothing.other', { count: String(allowNothing) }), lang);
|
|
1213
|
+
return renderMessage(msg('approval.boundGrants.listsNoTools'), lang);
|
|
1214
|
+
}
|
|
1215
|
+
const allow = a === 1 ? msg('approval.boundGrants.allowPart.one') : msg('approval.boundGrants.allowPart.other', { count: a });
|
|
1216
|
+
const deny = d === 1 ? msg('approval.boundGrants.denyPart.one') : msg('approval.boundGrants.denyPart.other', { count: d });
|
|
1217
|
+
if (allowNothing > 0) {
|
|
1218
|
+
return renderMessage(allowNothing === 1 ? msg('approval.boundGrants.activatesWithNothing.one', { allow, deny }) : msg('approval.boundGrants.activatesWithNothing.other', { allow, deny, count: String(allowNothing) }), lang);
|
|
1219
|
+
}
|
|
1220
|
+
return renderMessage(msg('approval.boundGrants.activates', { allow, deny }), lang);
|
|
1128
1221
|
}
|
|
1129
1222
|
const CALL_LEDGER_MAX_CALLS = 256;
|
|
1130
1223
|
const CALL_LEDGER_MAX_ASKS_PER_CALL = 8;
|
|
1131
1224
|
const callCardLedger = new Map();
|
|
1132
1225
|
function askSignatureOf(req) {
|
|
1133
|
-
return
|
|
1226
|
+
return askSigOf(req.classes, req.origin, req.mandate);
|
|
1227
|
+
}
|
|
1228
|
+
function askSigOf(classes, origin, mandate) {
|
|
1229
|
+
return JSON.stringify([classes !== undefined ? [...classes] : null, origin ?? null, mandate ?? null]);
|
|
1230
|
+
}
|
|
1231
|
+
function carrierAskSig(v) {
|
|
1232
|
+
try {
|
|
1233
|
+
return askSigOf(readAskClassPair(v)?.classes, readAskOrigin(v), readApprovalMandate(v));
|
|
1234
|
+
}
|
|
1235
|
+
catch {
|
|
1236
|
+
return undefined;
|
|
1237
|
+
}
|
|
1134
1238
|
}
|
|
1135
1239
|
function priorCardView(a) {
|
|
1136
1240
|
return Object.freeze({
|
|
@@ -1154,35 +1258,67 @@ function noteCardOnCall(callId, approvalId, req) {
|
|
|
1154
1258
|
}
|
|
1155
1259
|
}
|
|
1156
1260
|
let at = asks.findIndex((a) => a.sig === sig);
|
|
1261
|
+
let supersedes;
|
|
1157
1262
|
if (at < 0) {
|
|
1158
1263
|
asks.push({ sig, ...(approvalId !== undefined ? { approvalId } : {}), ...(req.origin !== undefined ? { origin: req.origin } : {}), ...(req.mandate !== undefined ? { mandate: req.mandate } : {}) });
|
|
1159
1264
|
while (asks.length > CALL_LEDGER_MAX_ASKS_PER_CALL)
|
|
1160
1265
|
asks.shift();
|
|
1161
1266
|
at = asks.length - 1;
|
|
1162
1267
|
}
|
|
1163
|
-
else
|
|
1164
|
-
asks[at]
|
|
1268
|
+
else {
|
|
1269
|
+
supersedes = supersedesOf(asks[at]);
|
|
1270
|
+
delete asks[at].answered;
|
|
1271
|
+
delete asks[at].answerFate;
|
|
1272
|
+
if (asks[at].approvalId === undefined && approvalId !== undefined)
|
|
1273
|
+
asks[at].approvalId = approvalId;
|
|
1165
1274
|
}
|
|
1166
1275
|
const entry = asks[at];
|
|
1167
1276
|
const prior = asks.slice(0, at);
|
|
1168
|
-
return { entry, ...(prior.length > 0 ? { prior: Object.freeze(prior.map(priorCardView)) } : {}) };
|
|
1277
|
+
return { entry, ...(prior.length > 0 ? { prior: Object.freeze(prior.map(priorCardView)) } : {}), ...(supersedes !== undefined ? { supersedes } : {}) };
|
|
1278
|
+
}
|
|
1279
|
+
function supersedesOf(a) {
|
|
1280
|
+
const settled = settledApprovalOf(a.approvalId);
|
|
1281
|
+
return Object.freeze({
|
|
1282
|
+
...(a.approvalId !== undefined ? { approvalId: a.approvalId } : {}),
|
|
1283
|
+
...(settled?.parked === true ? { parked: true } : {}),
|
|
1284
|
+
...(a.answered !== undefined && a.outcome === undefined ? { priorAnswer: a.answered, priorAnswerOutcome: a.answerFate ?? 'unconfirmed' } : {}),
|
|
1285
|
+
});
|
|
1169
1286
|
}
|
|
1170
1287
|
async function surfaceCallTrackedCard(callId, approvalId, slot, req) {
|
|
1171
1288
|
if (callId === undefined || callId === '')
|
|
1172
1289
|
return surfaceApprovalCard(req);
|
|
1173
|
-
const { entry, prior } = noteCardOnCall(callId, approvalId, req);
|
|
1290
|
+
const { entry, prior, supersedes } = noteCardOnCall(callId, approvalId, req);
|
|
1174
1291
|
slot.entry = entry;
|
|
1175
|
-
|
|
1292
|
+
const decision = await surfaceApprovalCard(prior === undefined && supersedes === undefined ? req : { ...req, ...(prior !== undefined ? { priorCardsForCall: prior } : {}), ...(supersedes !== undefined ? { supersedes } : {}) });
|
|
1293
|
+
const answered = respondDecisionOfAnswer(decision);
|
|
1294
|
+
if (answered !== undefined)
|
|
1295
|
+
entry.answered = answered;
|
|
1296
|
+
return decision;
|
|
1176
1297
|
}
|
|
1177
|
-
function noteCallOutcome(slot,
|
|
1298
|
+
function noteCallOutcome(slot, r) {
|
|
1178
1299
|
const entry = slot.entry;
|
|
1179
1300
|
if (entry === undefined)
|
|
1180
1301
|
return;
|
|
1181
|
-
|
|
1182
|
-
if (r.kind === 'decided')
|
|
1302
|
+
if (r.kind === 'decided') {
|
|
1183
1303
|
entry.outcome = r.decision === 'deny' ? 'denied' : 'allowed';
|
|
1304
|
+
delete entry.answerFate;
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const unsettledFate = {
|
|
1308
|
+
engine_refused: 'not_accepted',
|
|
1309
|
+
parking_in_progress: 'not_accepted',
|
|
1310
|
+
engine_error: 'unconfirmed',
|
|
1311
|
+
no_answer: 'unconfirmed',
|
|
1312
|
+
transport_exhausted: 'unconfirmed',
|
|
1313
|
+
interrupted: 'unconfirmed',
|
|
1314
|
+
orchestration: 'unconfirmed',
|
|
1315
|
+
unreadable: 'unconfirmed',
|
|
1316
|
+
};
|
|
1317
|
+
const next = r.kind === 'not_sent' ? 'not_sent' : Object.hasOwn(unsettledFate, r.cause) ? unsettledFate[r.cause] : 'unconfirmed';
|
|
1318
|
+
const fateRank = { not_sent: 0, not_accepted: 1, unconfirmed: 2 };
|
|
1319
|
+
entry.answerFate = entry.answerFate !== undefined && fateRank[entry.answerFate] > fateRank[next] ? entry.answerFate : next;
|
|
1184
1320
|
}
|
|
1185
|
-
export function approvalPriorCardsDetail(card) {
|
|
1321
|
+
export function approvalPriorCardsDetail(card, lang) {
|
|
1186
1322
|
const prior = ownDataValue(card, 'priorCardsForCall');
|
|
1187
1323
|
let isList = false;
|
|
1188
1324
|
try {
|
|
@@ -1200,13 +1336,36 @@ export function approvalPriorCardsDetail(card) {
|
|
|
1200
1336
|
for (let i = 0; i < length; i++)
|
|
1201
1337
|
items.push(ownDataValue(prior, String(i)));
|
|
1202
1338
|
const outcome = ownDataValue(items[length - 1], 'outcome');
|
|
1203
|
-
const earlier = outcome === 'allowed' ? '
|
|
1339
|
+
const earlier = msg(outcome === 'allowed' ? 'approval.priorCard.earlier.allowed' : outcome === 'denied' ? 'approval.priorCard.earlier.denied' : 'approval.priorCard.earlier.shown');
|
|
1204
1340
|
const fromRule = items.some((p) => ownDataValue(p, 'origin') === 'ask_rule');
|
|
1205
1341
|
const thisFromRule = ownDataValue(card, 'origin') === 'ask_rule';
|
|
1206
|
-
if (fromRule && !thisFromRule)
|
|
1207
|
-
return
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1342
|
+
if (fromRule && !thisFromRule)
|
|
1343
|
+
return renderMessage(msg('approval.priorCard.fromRule', { earlier }), lang);
|
|
1344
|
+
return renderMessage(msg('approval.priorCard.plain', { earlier }), lang);
|
|
1345
|
+
}
|
|
1346
|
+
const PRIOR_ANSWER_PHRASE = Object.freeze({
|
|
1347
|
+
allow: 'Yes',
|
|
1348
|
+
allow_session: 'Yes for this session',
|
|
1349
|
+
deny: 'No',
|
|
1350
|
+
});
|
|
1351
|
+
export function approvalSupersedesDetail(card) {
|
|
1352
|
+
const sup = ownDataValue(card, 'supersedes');
|
|
1353
|
+
if (typeof sup !== 'object' || sup === null)
|
|
1354
|
+
return undefined;
|
|
1355
|
+
const parked = ownDataValue(sup, 'parked') === true;
|
|
1356
|
+
const prior = ownDataValue(sup, 'priorAnswer');
|
|
1357
|
+
const head = parked
|
|
1358
|
+
? 'This card replaces an earlier one for the same request, which moved to the engine\'s waiting queue.'
|
|
1359
|
+
: 'This card replaces an earlier one for the same request.';
|
|
1360
|
+
if (typeof prior !== 'string' || !Object.hasOwn(PRIOR_ANSWER_PHRASE, prior))
|
|
1361
|
+
return head;
|
|
1362
|
+
const said = PRIOR_ANSWER_PHRASE[prior];
|
|
1363
|
+
const fate = ownDataValue(sup, 'priorAnswerOutcome');
|
|
1364
|
+
if (fate === 'not_sent')
|
|
1365
|
+
return `${head} Your earlier answer (${said}) did not reach the engine, so answer again here.`;
|
|
1366
|
+
if (fate === 'not_accepted')
|
|
1367
|
+
return `${head} Your earlier answer (${said}) was not accepted by the engine, so answer again here.`;
|
|
1368
|
+
return `${head} Your earlier answer (${said}) was not confirmed by the engine — it may already have taken effect, so check the request before answering again here.`;
|
|
1210
1369
|
}
|
|
1211
1370
|
function isNonNegativeFinite(v) {
|
|
1212
1371
|
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
@@ -1379,32 +1538,44 @@ function v1Bit(stamp) {
|
|
|
1379
1538
|
catch {
|
|
1380
1539
|
}
|
|
1381
1540
|
}
|
|
1382
|
-
|
|
1383
|
-
if
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
const
|
|
1387
|
-
const
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1541
|
+
const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
|
|
1542
|
+
'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
|
|
1543
|
+
'Edits are not accepted on this card: there is no original input to edit';
|
|
1544
|
+
function cardFactsOf(carrier, leg, opts) {
|
|
1545
|
+
const c = carrier;
|
|
1546
|
+
const full = leg !== 'generic';
|
|
1547
|
+
const asFrame = carrier;
|
|
1548
|
+
const fromSubagent = full
|
|
1549
|
+
? (() => {
|
|
1550
|
+
try {
|
|
1551
|
+
return isFromSubagent(asFrame);
|
|
1552
|
+
}
|
|
1553
|
+
catch {
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
})()
|
|
1557
|
+
: false;
|
|
1395
1558
|
const argsPart = (() => {
|
|
1396
1559
|
try {
|
|
1397
|
-
|
|
1560
|
+
let stream;
|
|
1561
|
+
let blind = false;
|
|
1562
|
+
if (leg === 'frame' && typeof opts === 'object' && opts !== null) {
|
|
1563
|
+
stream = opts.streamArgs;
|
|
1564
|
+
blind = opts.argsUnavailable === true;
|
|
1565
|
+
}
|
|
1566
|
+
const raw = stream !== undefined && stream !== null ? stream : c.args !== undefined && c.args !== null ? c.args : undefined;
|
|
1398
1567
|
if (typeof raw === 'object' && raw !== null)
|
|
1399
|
-
return { args: raw };
|
|
1568
|
+
return blind ? { args: raw, argsUnavailable: true } : { args: raw };
|
|
1400
1569
|
const fallback = argsFallbackOf(c.message, c.argsOmitted);
|
|
1401
|
-
|
|
1570
|
+
const note = blind ? SUSPENDED_ASK_ARGS_NOTE : fallback.omittedNote;
|
|
1571
|
+
return { args: fallback.args, argsUnavailable: true, ...(note !== undefined ? { wireNote: note } : {}) };
|
|
1402
1572
|
}
|
|
1403
1573
|
catch {
|
|
1404
1574
|
return { args: {}, argsUnavailable: true };
|
|
1405
1575
|
}
|
|
1406
1576
|
})();
|
|
1407
|
-
const out = { toolName: '',
|
|
1577
|
+
const out = { toolName: '', args: argsPart.args };
|
|
1578
|
+
let realApprovalUnreadable = false;
|
|
1408
1579
|
v1Bit(() => {
|
|
1409
1580
|
if (typeof c.toolName === 'string')
|
|
1410
1581
|
out.toolName = c.toolName;
|
|
@@ -1417,44 +1588,79 @@ export function approvalCardFactsOfV1(card) {
|
|
|
1417
1588
|
if (fromSubagent)
|
|
1418
1589
|
out.workerBadge = subagentBadgeFor(asFrame);
|
|
1419
1590
|
});
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1591
|
+
if (argsPart.wireNote !== undefined)
|
|
1592
|
+
out.wireNote = argsPart.wireNote;
|
|
1593
|
+
if (argsPart.argsUnavailable === true)
|
|
1594
|
+
out.argsUnavailable = true;
|
|
1595
|
+
if (full) {
|
|
1596
|
+
v1Bit(() => {
|
|
1597
|
+
if (c.governanceForced === true)
|
|
1598
|
+
out.governanceForced = true;
|
|
1599
|
+
});
|
|
1600
|
+
v1Bit(() => {
|
|
1601
|
+
const d = c.delegation;
|
|
1602
|
+
if (fromSubagent && isToolApprovalDelegation(d))
|
|
1603
|
+
out.delegation = d;
|
|
1604
|
+
});
|
|
1605
|
+
v1Bit(() => {
|
|
1606
|
+
const offers = readRuleOffers(c.ruleOffers);
|
|
1607
|
+
if (offers === undefined)
|
|
1608
|
+
return;
|
|
1609
|
+
if (leg === 'frame')
|
|
1610
|
+
out.ruleOffers = offers;
|
|
1611
|
+
else
|
|
1612
|
+
out.ruleOffersReadOnly = offers;
|
|
1613
|
+
});
|
|
1614
|
+
v1Bit(() => {
|
|
1615
|
+
const shadow = readAllowRuleShadowFromWire(carrier, 'frame');
|
|
1616
|
+
if (shadow !== undefined)
|
|
1617
|
+
out.allowRuleShadow = shadow;
|
|
1618
|
+
});
|
|
1619
|
+
v1Bit(() => {
|
|
1620
|
+
const pc = c.probeCause;
|
|
1621
|
+
if (isWireRecordCarrier(pc))
|
|
1622
|
+
out.probeCause = pc;
|
|
1623
|
+
});
|
|
1624
|
+
if (leg === 'frame') {
|
|
1625
|
+
v1Bit(() => {
|
|
1626
|
+
const r = readProbeReason(carrier);
|
|
1627
|
+
if (r !== undefined)
|
|
1628
|
+
out.probeReason = r;
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
v1Bit(() => {
|
|
1632
|
+
if (readApprovalRememberRefused(carrier))
|
|
1633
|
+
out.rememberRefused = true;
|
|
1634
|
+
});
|
|
1635
|
+
v1Bit(() => {
|
|
1636
|
+
const re = c.ruleEvidence;
|
|
1637
|
+
if (isWireRecordCarrier(re))
|
|
1638
|
+
out.ruleEvidence = re;
|
|
1639
|
+
});
|
|
1640
|
+
try {
|
|
1641
|
+
if (readRequiresRealApproval(carrier))
|
|
1642
|
+
out.requiresRealApproval = true;
|
|
1643
|
+
}
|
|
1644
|
+
catch {
|
|
1645
|
+
realApprovalUnreadable = true;
|
|
1646
|
+
}
|
|
1647
|
+
if (leg === 'frame') {
|
|
1648
|
+
v1Bit(() => {
|
|
1649
|
+
if (hasOwnStrictTrue(carrier, 'mandated'))
|
|
1650
|
+
out.mandated = true;
|
|
1651
|
+
});
|
|
1652
|
+
v1Bit(() => {
|
|
1653
|
+
const w = readApprovalMandate(carrier);
|
|
1654
|
+
if (w !== undefined)
|
|
1655
|
+
out.mandate = w;
|
|
1656
|
+
});
|
|
1657
|
+
v1Bit(() => {
|
|
1658
|
+
const rc = readReadRootCandidate(c.readRootCandidate);
|
|
1659
|
+
if (rc !== undefined)
|
|
1660
|
+
out.readRootCandidate = rc;
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1458
1664
|
v1Bit(() => {
|
|
1459
1665
|
if (c.inputHasBidi === true)
|
|
1460
1666
|
out.inputHasBidi = true;
|
|
@@ -1469,43 +1675,114 @@ export function approvalCardFactsOfV1(card) {
|
|
|
1469
1675
|
if (dlf !== undefined)
|
|
1470
1676
|
out.denialLimitFallback = dlf;
|
|
1471
1677
|
});
|
|
1678
|
+
if (leg === 'frame') {
|
|
1679
|
+
v1Bit(() => {
|
|
1680
|
+
const o = readAskOrigin(carrier);
|
|
1681
|
+
if (o !== undefined)
|
|
1682
|
+
out.origin = o;
|
|
1683
|
+
});
|
|
1684
|
+
v1Bit(() => {
|
|
1685
|
+
const w = readApprovalCardWord(carrier);
|
|
1686
|
+
if (w !== undefined)
|
|
1687
|
+
out.approvalCard = w;
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1472
1690
|
v1Bit(() => {
|
|
1473
|
-
const pair = readAskClassPair(
|
|
1691
|
+
const pair = readAskClassPair(carrier);
|
|
1474
1692
|
if (pair !== undefined) {
|
|
1475
1693
|
out.askClass = pair.askClass;
|
|
1476
1694
|
out.classes = pair.classes;
|
|
1477
1695
|
}
|
|
1478
1696
|
});
|
|
1479
|
-
|
|
1480
|
-
|
|
1697
|
+
if (leg === 'frame') {
|
|
1698
|
+
v1Bit(() => {
|
|
1699
|
+
const g = readApprovalBoundGrants(carrier);
|
|
1700
|
+
if (g !== undefined)
|
|
1701
|
+
out.boundGrants = g;
|
|
1702
|
+
});
|
|
1703
|
+
v1Bit(() => {
|
|
1704
|
+
const w = readApprovalClassifierNoRuling(carrier);
|
|
1705
|
+
if (w !== undefined)
|
|
1706
|
+
out.classifierNoRuling = w;
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
v1Bit(() => {
|
|
1710
|
+
const msg = ownDataValue(carrier, 'message');
|
|
1711
|
+
if (typeof msg === 'string' && msg.trim() !== '')
|
|
1712
|
+
out.message = msg;
|
|
1713
|
+
});
|
|
1714
|
+
if (leg === 'generic')
|
|
1715
|
+
out.cardPayloadUnreadable = true;
|
|
1716
|
+
if (leg === 'frame')
|
|
1717
|
+
out.allowSessionCarried = true;
|
|
1718
|
+
out.grantBinding = readApprovalGrantBinding(carrier);
|
|
1719
|
+
return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : []);
|
|
1720
|
+
}
|
|
1721
|
+
export function approvalCardFactsOfV1(card) {
|
|
1722
|
+
if (typeof card !== 'object' || card === null)
|
|
1723
|
+
return undefined;
|
|
1724
|
+
return Object.freeze(cardFactsOf(card, 'v1'));
|
|
1725
|
+
}
|
|
1726
|
+
export function approvalCardFactsOfFrame(frame, opts) {
|
|
1727
|
+
if (typeof frame !== 'object' || frame === null)
|
|
1728
|
+
return undefined;
|
|
1729
|
+
return Object.freeze(cardFactsOf(frame, 'frame', opts));
|
|
1730
|
+
}
|
|
1731
|
+
export function approvalCardFactsOfGeneric(card) {
|
|
1732
|
+
if (typeof card !== 'object' || card === null)
|
|
1733
|
+
return undefined;
|
|
1734
|
+
return Object.freeze(cardFactsOf(card, 'generic'));
|
|
1481
1735
|
}
|
|
1482
1736
|
const MAX_RESPOND_NOTE_CHARS = 2048;
|
|
1483
1737
|
export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
|
|
1738
|
+
const receivedAtMs = Date.now();
|
|
1484
1739
|
const approvalId = frame.approvalId;
|
|
1740
|
+
const entry = { frame, approvalId, identity: liveCardIdentityOf(approvalId, frame, run), respond, streamArgs, signal, lane, receivedAtMs };
|
|
1741
|
+
return enterLiveCard(entry);
|
|
1742
|
+
}
|
|
1743
|
+
function enterLiveCard(e) {
|
|
1744
|
+
const { approvalId, signal } = e;
|
|
1485
1745
|
const held = liveCardHoldOf(liveFrameCallKey(approvalId));
|
|
1486
1746
|
if (held !== undefined)
|
|
1487
1747
|
return joinLiveCard(held, signal);
|
|
1488
1748
|
const settled = settledLiveCardOutcome(approvalId);
|
|
1489
1749
|
if (settled !== undefined)
|
|
1490
1750
|
return Promise.resolve(settled);
|
|
1491
|
-
|
|
1751
|
+
const other = approvalHoldFor(e.identity, (h) => h.lane === 'live');
|
|
1752
|
+
if (other !== undefined)
|
|
1753
|
+
return joinOtherLaneHolder(e, other);
|
|
1754
|
+
return holdLiveCard(e.identity, signal, async (hold) => {
|
|
1492
1755
|
const slot = {};
|
|
1493
|
-
const out = await presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, slot);
|
|
1756
|
+
const out = await presentToolApprovalFrameAndRespond(e.frame, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
|
|
1494
1757
|
noteCallOutcome(slot, out);
|
|
1495
1758
|
return out;
|
|
1496
1759
|
});
|
|
1497
1760
|
}
|
|
1761
|
+
async function joinOtherLaneHolder(e, other) {
|
|
1762
|
+
const wait = await awaitApprovalHolder(other, e.signal, 'respond');
|
|
1763
|
+
if (wait.kind === 'settled') {
|
|
1764
|
+
noteHolderSettledApproval(e.approvalId, wait.how, wait.outcome);
|
|
1765
|
+
return joinedResolution(wait.outcome);
|
|
1766
|
+
}
|
|
1767
|
+
if (wait.kind === 'left')
|
|
1768
|
+
return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
|
|
1769
|
+
return enterLiveCard(e);
|
|
1770
|
+
}
|
|
1498
1771
|
function liveCardIdentityOf(approvalId, frame, run) {
|
|
1499
1772
|
const toolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
|
|
1500
1773
|
const fromSubagent = isFromSubagent(frame);
|
|
1501
|
-
const
|
|
1774
|
+
const bound = fromSubagent ? undefined : frameBindingOf(frame);
|
|
1775
|
+
const source = bound ?? run;
|
|
1776
|
+
const ownRun = fromSubagent ? frame.sourceTaskId : source?.taskId;
|
|
1502
1777
|
const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
|
|
1503
|
-
const sessionId = !fromSubagent && typeof
|
|
1778
|
+
const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
|
|
1779
|
+
const askSig = carrierAskSig(frame);
|
|
1504
1780
|
return {
|
|
1505
1781
|
approvalId,
|
|
1506
1782
|
...(toolCallId !== undefined ? { toolCallId } : {}),
|
|
1507
1783
|
...(taskId !== undefined ? { taskId } : {}),
|
|
1508
1784
|
...(sessionId !== undefined ? { sessionId } : {}),
|
|
1785
|
+
...(askSig !== undefined ? { askSig } : {}),
|
|
1509
1786
|
};
|
|
1510
1787
|
}
|
|
1511
1788
|
function readApprovalHolderIdentity(v) {
|
|
@@ -1527,107 +1804,63 @@ function readApprovalHolderIdentity(v) {
|
|
|
1527
1804
|
}
|
|
1528
1805
|
export async function joinApprovalHolder(identity, signal) {
|
|
1529
1806
|
const who = readApprovalHolderIdentity(identity);
|
|
1530
|
-
const hold =
|
|
1807
|
+
const hold = approvalHoldFor(who);
|
|
1531
1808
|
if (hold === undefined) {
|
|
1532
1809
|
const entry = settledApprovalOf(who.approvalId);
|
|
1533
|
-
return settledForDurableLane(entry) && entry !== undefined ? { kind: 'joined', outcome:
|
|
1810
|
+
return settledForDurableLane(entry) && entry !== undefined ? { kind: 'joined', outcome: joinedResolution(entry.outcome) } : { kind: 'free' };
|
|
1811
|
+
}
|
|
1812
|
+
const wait = await awaitApprovalHolder(hold, signal, 'decide');
|
|
1813
|
+
if (wait.kind === 'settled')
|
|
1814
|
+
return { kind: 'joined', outcome: joinedResolution(wait.outcome) };
|
|
1815
|
+
if (wait.kind === 'left')
|
|
1816
|
+
return { kind: 'joined', outcome: { kind: 'not_sent', lane: 'decide', cause: 'retracted', joinedInFlight: true } };
|
|
1817
|
+
return { kind: 'free' };
|
|
1818
|
+
}
|
|
1819
|
+
function readApprovalCardClaimSignal(v) {
|
|
1820
|
+
if (typeof v !== 'object' || v === null)
|
|
1821
|
+
return undefined;
|
|
1822
|
+
try {
|
|
1823
|
+
const d = Object.getOwnPropertyDescriptor(v, 'signal');
|
|
1824
|
+
const x = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
1825
|
+
return typeof x === 'object' && x !== null ? x : undefined;
|
|
1826
|
+
}
|
|
1827
|
+
catch {
|
|
1828
|
+
return undefined;
|
|
1534
1829
|
}
|
|
1535
|
-
const joined = await awaitLiveCardHolder(hold, signal);
|
|
1536
|
-
return joined !== undefined ? { kind: 'joined', outcome: joined } : { kind: 'free' };
|
|
1537
1830
|
}
|
|
1538
|
-
|
|
1831
|
+
export function claimApprovalCard(identity, options) {
|
|
1832
|
+
return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
|
|
1833
|
+
}
|
|
1834
|
+
async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
|
|
1539
1835
|
const frameToolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
|
|
1540
1836
|
const decidedByReading = decidedByReadingSnapshotOf(lane);
|
|
1541
|
-
const
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
if (typeof args !== 'object' || args === null) {
|
|
1550
|
-
argsFallback = true;
|
|
1551
|
-
const fallback = argsFallbackOf(frame.message, frame.argsOmitted);
|
|
1552
|
-
args = fallback.args;
|
|
1553
|
-
if (fallback.omittedNote !== undefined) {
|
|
1554
|
-
wireNote = fallback.omittedNote;
|
|
1555
|
-
hostLog('debug', `liveToolApprovalWire: frame ${approvalId} args omitted (>16KiB wire cap) — card falls back to ${fallback.pathRecovered ? 'the message-derived path' : 'an empty input (no path could be derived from the message)'}`);
|
|
1556
|
-
}
|
|
1557
|
-
if (lane?.argsUnavailable === true) {
|
|
1558
|
-
wireNote =
|
|
1559
|
-
"this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
|
|
1560
|
-
'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
|
|
1561
|
-
'Edits are not accepted on this card: there is no original input to edit';
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
const argsNotRealInput = argsFallback || lane?.argsUnavailable === true;
|
|
1565
|
-
const ruleOffers = readRuleOffers(frame.ruleOffers);
|
|
1566
|
-
const denialLimitFallback = readDenialLimitFallback(frame.denialLimitFallback);
|
|
1567
|
-
const frameShadow = readAllowRuleShadowFromWire(frame, 'frame');
|
|
1568
|
-
const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, withUnreadableEvidence(frame, 'frame', {
|
|
1569
|
-
toolName,
|
|
1570
|
-
args: args,
|
|
1837
|
+
const facts = cardFactsOf(frame, 'frame', { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) });
|
|
1838
|
+
const argsNotRealInput = facts.argsUnavailable === true;
|
|
1839
|
+
const ruleOffers = facts.ruleOffers;
|
|
1840
|
+
if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
|
|
1841
|
+
hostLog('debug', `liveToolApprovalWire: frame ${approvalId} args omitted (>16KiB wire cap) — card falls back to ${Object.hasOwn(facts.args, 'file_path') ? 'the message-derived path' : 'an empty input (no path could be derived from the message)'}`);
|
|
1842
|
+
}
|
|
1843
|
+
const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
|
|
1844
|
+
...facts,
|
|
1571
1845
|
callKey: liveFrameCallKey(approvalId),
|
|
1572
|
-
...(frameToolCallId !== undefined
|
|
1573
|
-
? { toolCallId: frameToolCallId }
|
|
1574
|
-
: {}),
|
|
1575
1846
|
signal: hold.cardSignal,
|
|
1576
|
-
...(
|
|
1577
|
-
|
|
1578
|
-
...(argsNotRealInput ? { argsUnavailable: true } : {}),
|
|
1579
|
-
...(frame.governanceForced === true ? { governanceForced: true } : {}),
|
|
1580
|
-
...(lane?.windowIsCurrent === true
|
|
1581
|
-
? {
|
|
1582
|
-
...(isNonNegativeFinite(frame.expiresInMs) ? { expiresInMs: frame.expiresInMs } : {}),
|
|
1583
|
-
...(isNonNegativeFinite(frame.expiresAtMs) ? { expiresAtMs: frame.expiresAtMs } : {}),
|
|
1584
|
-
...(isNonNegativeFinite(frame.serverNowMs) ? { serverNowMs: frame.serverNowMs } : {}),
|
|
1585
|
-
...(isNonNegativeFinite(frame.expiresInMs) || isNonNegativeFinite(frame.expiresAtMs)
|
|
1586
|
-
? { receivedAtMs: Date.now() }
|
|
1587
|
-
: {}),
|
|
1588
|
-
}
|
|
1589
|
-
: {}),
|
|
1590
|
-
...(isFromSubagent(frame) && isToolApprovalDelegation(frame.delegation)
|
|
1591
|
-
? { delegation: frame.delegation }
|
|
1592
|
-
: {}),
|
|
1593
|
-
...(ruleOffers !== undefined ? { ruleOffers } : {}),
|
|
1594
|
-
...((sh) => (sh !== undefined ? { allowRuleShadow: sh } : {}))(frameShadow),
|
|
1595
|
-
...(isWireRecordCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
|
|
1596
|
-
...((r) => (r !== undefined ? { probeReason: r } : {}))(readProbeReason(frame)),
|
|
1597
|
-
...(readApprovalRememberRefused(frame) ? { rememberRefused: true } : {}),
|
|
1598
|
-
...(isWireRecordCarrier(frame.ruleEvidence) ? { ruleEvidence: frame.ruleEvidence } : {}),
|
|
1599
|
-
...(frame.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
1600
|
-
...(hasOwnStrictTrue(frame, 'mandated') ? { mandated: true } : {}),
|
|
1601
|
-
...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(frame)),
|
|
1602
|
-
...(() => { const rc = readReadRootCandidate(frame.readRootCandidate); return rc !== undefined ? { readRootCandidate: rc } : {}; })(),
|
|
1603
|
-
...(frame.inputHasBidi === true ? { inputHasBidi: true } : {}),
|
|
1604
|
-
...(typeof frame.ruleOffersAbsence === 'string' && frame.ruleOffersAbsence !== ''
|
|
1605
|
-
? { ruleOffersAbsence: frame.ruleOffersAbsence }
|
|
1606
|
-
: {}),
|
|
1607
|
-
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
1608
|
-
...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(frame)),
|
|
1609
|
-
...((w) => (w !== undefined ? { approvalCard: w } : {}))(readApprovalCardWord(frame)),
|
|
1610
|
-
...(readAskClassPair(frame) ?? {}),
|
|
1611
|
-
...((g) => (g !== undefined ? { boundGrants: g } : {}))(readApprovalBoundGrants(frame)),
|
|
1612
|
-
...((w) => (w !== undefined ? { classifierNoRuling: w } : {}))(readApprovalClassifierNoRuling(frame)),
|
|
1613
|
-
allowSessionCarried: true,
|
|
1614
|
-
grantBinding: readApprovalGrantBinding(frame),
|
|
1615
|
-
}));
|
|
1847
|
+
...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs }),
|
|
1848
|
+
});
|
|
1616
1849
|
if (hold.superseded !== undefined) {
|
|
1617
1850
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
|
|
1618
1851
|
`the card came back ${card.kind}; nothing sent`);
|
|
1619
1852
|
if (hold.superseded.parked && (card.kind === 'allow' || card.kind === 'deny'))
|
|
1620
1853
|
surfaceApprovalParking();
|
|
1621
|
-
return {
|
|
1854
|
+
return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
|
|
1622
1855
|
}
|
|
1623
1856
|
if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
|
|
1624
1857
|
hostLog('error', `liveToolApprovalWire: ${approvalId} card returned an edited approval on a card whose arguments are not the tool's real input (${lane?.argsUnavailable === true ? 'args-unavailable ask' : 'arguments omitted or absent on the frame'}) — nothing sent (the ask stays pending)`);
|
|
1625
1858
|
surfaceEditRefusedOnBlindAsk();
|
|
1626
|
-
return {
|
|
1859
|
+
return { kind: 'not_sent', lane: 'respond', cause: 'edit_refused' };
|
|
1627
1860
|
}
|
|
1628
1861
|
if (card.kind === RETRACTED_CARD_DECISION_KIND) {
|
|
1629
1862
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
|
|
1630
|
-
return {
|
|
1863
|
+
return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
|
|
1631
1864
|
}
|
|
1632
1865
|
if (card.kind === 'aborted') {
|
|
1633
1866
|
hold.phase = 'respond';
|
|
@@ -1642,33 +1875,40 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
|
|
|
1642
1875
|
hold.trailing = interruptDeny.then((body) => readToolApprovalRespondParking(body) === undefined, () => false);
|
|
1643
1876
|
observeCancelByDeny(interruptDeny, approvalId);
|
|
1644
1877
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} card aborted — one tool-level deny issued to settle the pending ask`);
|
|
1645
|
-
return {
|
|
1878
|
+
return { kind: 'unsettled', lane: 'respond', cause: 'interrupted' };
|
|
1646
1879
|
}
|
|
1647
1880
|
hold.phase = 'respond';
|
|
1648
|
-
const
|
|
1881
|
+
const noteCapable = lane?.approvalDecisionNoteCapable === true;
|
|
1882
|
+
const denyReasonRead = card.kind === 'deny' ? ((r) => (typeof r === 'string' ? r : undefined))(card.reason) : undefined;
|
|
1883
|
+
const answerSnapshot = card.kind === 'deny'
|
|
1884
|
+
? ((by) => ({ kind: 'deny', ...(denyReasonRead !== undefined ? { reason: denyReasonRead } : {}), ...(by !== undefined ? { settledBy: by } : {}) }))(readApprovalDenySettledBy(card))
|
|
1885
|
+
: card;
|
|
1886
|
+
const basePayload = answerPayloadFor(answerSnapshot, 'respond', { approvalDecisionNoteCapable: noteCapable });
|
|
1887
|
+
const decision = basePayload?.decision ?? 'deny';
|
|
1888
|
+
if (card.kind === 'allow' && typeof card.note === 'string' && card.note.trim() !== '' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none') {
|
|
1889
|
+
hostLog('debug', `liveToolApprovalWire: ${approvalId} card carried a note on Yes (len=${card.note.length}) — this engine has no field that brings it to the model, so it was not sent`);
|
|
1890
|
+
}
|
|
1649
1891
|
const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
|
|
1650
1892
|
const attributionKeys = decision === 'deny' ? decisionAttributionKeyOf(decidedByReading, denySettledByForWire) : {};
|
|
1651
1893
|
const sentAttributionKey = carriesAttributionKey(attributionKeys);
|
|
1652
1894
|
let lastSendCarriedAttribution = sentAttributionKey;
|
|
1653
|
-
const denyReasonSnapshot =
|
|
1895
|
+
const denyReasonSnapshot = denyReasonRead;
|
|
1654
1896
|
if (card.kind === 'failed') {
|
|
1655
1897
|
hostLog('debug', `liveToolApprovalWire: approval card unavailable (${card.reason}) — fail-closed deny for ${approvalId}`);
|
|
1656
1898
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1899
|
+
const note = basePayload?.note;
|
|
1900
|
+
const noteGate = respondNoteGate(denyReasonSnapshot, noteCapable);
|
|
1901
|
+
if ('dropped' in noteGate && noteGate.dropped !== 'blank' && denyReasonSnapshot !== undefined) {
|
|
1902
|
+
if (noteGate.dropped === 'capability') {
|
|
1660
1903
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} card supplied a deny reason (len=${denyReasonSnapshot.length}) but the ` +
|
|
1661
1904
|
'approvalDecisionNote capability is not confirmed for this engine — note not sent (an unknown key would be ' +
|
|
1662
1905
|
'silently swallowed by an older server while still acking 200)');
|
|
1663
1906
|
}
|
|
1664
|
-
else
|
|
1907
|
+
else {
|
|
1665
1908
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} deny reason exceeds the decision_note cap ` +
|
|
1666
1909
|
`(len=${denyReasonSnapshot.length} > ${MAX_RESPOND_NOTE_CHARS}) — note not sent at all (the server rejects the WHOLE ` +
|
|
1667
1910
|
'respond with 400 over an oversize note, and a silently halved audit reason is worse than none)');
|
|
1668
1911
|
}
|
|
1669
|
-
else {
|
|
1670
|
-
note = denyReasonSnapshot;
|
|
1671
|
-
}
|
|
1672
1912
|
}
|
|
1673
1913
|
try {
|
|
1674
1914
|
let persistRule;
|
|
@@ -1735,7 +1975,7 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
|
|
|
1735
1975
|
}
|
|
1736
1976
|
const respondOpts = {
|
|
1737
1977
|
...(signal && !signal.aborted ? { signal } : {}),
|
|
1738
|
-
...(
|
|
1978
|
+
...(basePayload?.updatedInput !== undefined ? { updatedInput: basePayload.updatedInput } : {}),
|
|
1739
1979
|
...(persistRule !== undefined ? { persistRule } : {}),
|
|
1740
1980
|
...(persistRuleEdited !== undefined ? { persistRuleEdited } : {}),
|
|
1741
1981
|
...(persistRuleBatchOfferIndex !== undefined ? { persistRuleBatchOfferIndex } : {}),
|
|
@@ -1745,6 +1985,8 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
|
|
|
1745
1985
|
let attributionDropped = false;
|
|
1746
1986
|
let attributionRenamed = false;
|
|
1747
1987
|
let raw;
|
|
1988
|
+
if (basePayload !== undefined)
|
|
1989
|
+
emitAnswerCommitted(liveFrameCallKey(approvalId), 'respond', answerSnapshot, basePayload);
|
|
1748
1990
|
try {
|
|
1749
1991
|
raw = await respond(approvalId, decision, respondOpts);
|
|
1750
1992
|
}
|
|
@@ -1790,41 +2032,56 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
|
|
|
1790
2032
|
hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered 202 parking_in_progress${parking.retryAfterMs !== undefined ? ` (retryAfterMs=${parking.retryAfterMs})` : ''} — ` +
|
|
1791
2033
|
`the answer did NOT take effect${parking.decidePath !== undefined ? ' (the engine pointed at a decide path for the parked card)' : ''}; not re-sent, not reported as decided; the user is told to answer again on the parked card`);
|
|
1792
2034
|
surfaceApprovalParking(parking.retryAfterMs);
|
|
1793
|
-
return {
|
|
2035
|
+
return { kind: 'unsettled', lane: 'respond', cause: 'parking_in_progress', parking };
|
|
1794
2036
|
}
|
|
1795
2037
|
hostLog('error', `liveToolApprovalWire: 202 parking body for ${approvalId} is ${parkingBody.kind === 'parking' ? `for another approval (${parkingBody.view.approvalId})` : 'unreadable (approvalId is not usable)'} — ` +
|
|
1796
2038
|
'reporting the answer as NOT settled (never as decided, never as parked); not re-sent');
|
|
1797
|
-
return {
|
|
2039
|
+
return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
|
|
1798
2040
|
}
|
|
1799
2041
|
const parsed = readToolApprovalRespondAck(raw);
|
|
2042
|
+
const replayedForThisCard = parsed !== undefined && isToolApprovalRespondReplayed(parsed) && parsed.approvalId === approvalId;
|
|
2043
|
+
const claimsReplay = ownIdempotentTrue(raw) || (parsed !== undefined && isToolApprovalRespondReplayed(parsed));
|
|
2044
|
+
if (claimsReplay && !replayedForThisCard) {
|
|
2045
|
+
const claimed = replayClaimOf(raw);
|
|
2046
|
+
if (claimed.approvalId === approvalId && claimed.decision !== undefined && !claimed.carriesDelivery) {
|
|
2047
|
+
hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay (body not in the full replay shape) — the approval was already decided ` +
|
|
2048
|
+
`(${claimed.decision}); this answer was not delivered, reporting the earlier decision`);
|
|
2049
|
+
return { kind: 'decided', lane: 'respond', decision: claimed.decision };
|
|
2050
|
+
}
|
|
2051
|
+
hostLog('error', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay of an earlier decision that cannot be read or names another approval — ` +
|
|
2052
|
+
'reporting the answer as NOT settled (never as the decision this client sent); not re-sent');
|
|
2053
|
+
return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
|
|
2054
|
+
}
|
|
1800
2055
|
let ack = parsed;
|
|
1801
|
-
if (parsed !== undefined && (parsed.approvalId !== approvalId || parsed.decision !== decision)) {
|
|
2056
|
+
if (parsed !== undefined && (parsed.approvalId !== approvalId || (!isToolApprovalRespondReplayed(parsed) && parsed.decision !== decision))) {
|
|
1802
2057
|
hostLog('error', `liveToolApprovalWire: DISCARDING respond ack for ${approvalId} — it does not correlate ` +
|
|
1803
2058
|
`(ack.approvalId=${parsed.approvalId} ack.decision=${parsed.decision}, sent decision=${decision}); ` +
|
|
1804
2059
|
'treating as "no ack" (unknown) — never surfacing a safety notice off an unrelated receipt');
|
|
1805
2060
|
ack = undefined;
|
|
1806
2061
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
2062
|
+
const replayed = ack !== undefined && isToolApprovalRespondReplayed(ack) ? ack : undefined;
|
|
2063
|
+
let applied = ack !== undefined && replayed === undefined ? ack : undefined;
|
|
2064
|
+
if (applied !== undefined &&
|
|
2065
|
+
(applied.persistedRule !== undefined || applied.persistedRules !== undefined || applied.persistedRuleAnchors !== undefined)) {
|
|
1809
2066
|
const sentEditedArm = persistRule !== undefined && persistRuleEdited === true;
|
|
1810
2067
|
const expectedBatchCount = batchArmTarget?.rules.length;
|
|
1811
2068
|
const anchorKeyOnWire = typeof raw === 'object' &&
|
|
1812
2069
|
raw !== null &&
|
|
1813
2070
|
Object.prototype.hasOwnProperty.call(raw, 'persistedRuleAnchors');
|
|
1814
|
-
const anchors =
|
|
2071
|
+
const anchors = applied.persistedRuleAnchors;
|
|
1815
2072
|
const anchorsOk = anchors !== undefined
|
|
1816
2073
|
? persistRuleBatchOfferIndex !== undefined &&
|
|
1817
2074
|
expectedBatchCount !== undefined &&
|
|
1818
2075
|
anchors.every(a => a.offerIndex === persistRuleBatchOfferIndex && a.memberIndex < expectedBatchCount)
|
|
1819
2076
|
: !anchorKeyOnWire;
|
|
1820
|
-
const batchEchoOk =
|
|
2077
|
+
const batchEchoOk = applied.persistedRules !== undefined &&
|
|
1821
2078
|
expectedBatchCount !== undefined &&
|
|
1822
|
-
|
|
2079
|
+
applied.persistedRules.length === expectedBatchCount &&
|
|
1823
2080
|
anchorsOk;
|
|
1824
2081
|
const strayEchoes = [];
|
|
1825
|
-
if (
|
|
2082
|
+
if (applied.persistedRule !== undefined && !sentEditedArm)
|
|
1826
2083
|
strayEchoes.push('persistedRule');
|
|
1827
|
-
if (
|
|
2084
|
+
if (applied.persistedRules !== undefined && !batchEchoOk)
|
|
1828
2085
|
strayEchoes.push('persistedRules');
|
|
1829
2086
|
if (anchors !== undefined && !(anchorsOk && batchEchoOk))
|
|
1830
2087
|
strayEchoes.push('persistedRuleAnchors');
|
|
@@ -1833,60 +2090,53 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
|
|
|
1833
2090
|
`${approvalId} — they do not correlate with the persistence arm actually sent ` +
|
|
1834
2091
|
`(editedArm=${String(sentEditedArm)} batchArmMembers=${String(expectedBatchCount ?? 'none')} ` +
|
|
1835
2092
|
`sentOfferIndex=${String(persistRuleBatchOfferIndex ?? 'none')} ` +
|
|
1836
|
-
`echoedRules=${String(
|
|
2093
|
+
`echoedRules=${String(applied.persistedRules?.length ?? 'none')} ` +
|
|
1837
2094
|
`echoedAnchorOffer=${String(anchors?.[0]?.offerIndex ?? 'none')}); never telling the user a rule ` +
|
|
1838
2095
|
'was stored off a receipt that does not line up with what this client actually sent');
|
|
1839
|
-
const { persistedRule: _pr, persistedRules: _prs, persistedRuleAnchors: _pra, ...rest } =
|
|
2096
|
+
const { persistedRule: _pr, persistedRules: _prs, persistedRuleAnchors: _pra, ...rest } = applied;
|
|
1840
2097
|
void _pr;
|
|
1841
2098
|
void _prs;
|
|
1842
2099
|
void _pra;
|
|
1843
|
-
|
|
2100
|
+
applied = {
|
|
1844
2101
|
...rest,
|
|
1845
|
-
...(
|
|
1846
|
-
...(
|
|
2102
|
+
...(applied.persistedRule !== undefined && sentEditedArm ? { persistedRule: applied.persistedRule } : {}),
|
|
2103
|
+
...(applied.persistedRules !== undefined && batchEchoOk ? { persistedRules: applied.persistedRules } : {}),
|
|
1847
2104
|
...(anchors !== undefined && anchorsOk && batchEchoOk ? { persistedRuleAnchors: anchors } : {}),
|
|
1848
2105
|
};
|
|
2106
|
+
ack = applied;
|
|
1849
2107
|
}
|
|
1850
2108
|
}
|
|
1851
|
-
if (ruleArmDroppedForCaps)
|
|
2109
|
+
if (replayed === undefined && ruleArmDroppedForCaps)
|
|
1852
2110
|
surfaceRuleArmNotSent();
|
|
1853
|
-
if (ruleArmDroppedForCheck !== undefined)
|
|
2111
|
+
if (replayed === undefined && ruleArmDroppedForCheck !== undefined)
|
|
1854
2112
|
surfaceRuleArmRejected(ruleArmDroppedForCheck);
|
|
1855
|
-
if (decision === 'allow_session' &&
|
|
2113
|
+
if (decision === 'allow_session' && applied?.rememberApplied === false) {
|
|
1856
2114
|
logAfterDecided(`liveToolApprovalWire: ${approvalId} allow_session ack rememberApplied=false — grant not stored, surfacing honest notice`);
|
|
1857
2115
|
surfaceRememberNotApplied();
|
|
1858
2116
|
}
|
|
1859
|
-
if (respondOpts.updatedInput !== undefined &&
|
|
2117
|
+
if (respondOpts.updatedInput !== undefined && applied?.updatedInputForwarded === false) {
|
|
1860
2118
|
logAfterDecided(`liveToolApprovalWire: ${approvalId} edited args were NOT forwarded (ack.updatedInputForwarded=false) — the tool runs on the ORIGINAL input`);
|
|
1861
2119
|
surfaceEditNotForwarded();
|
|
1862
2120
|
}
|
|
1863
|
-
if (note !== undefined &&
|
|
2121
|
+
if (note !== undefined && applied !== undefined && applied.noteRecorded !== true) {
|
|
1864
2122
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} decision note was sent but not persisted ` +
|
|
1865
|
-
`(ack.noteRecorded=${String(
|
|
2123
|
+
`(ack.noteRecorded=${String(applied.noteRecorded)}) — decision stood; the audit note did not land on the ask row`);
|
|
1866
2124
|
}
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
: {}),
|
|
1875
|
-
}
|
|
1876
|
-
: {};
|
|
1877
|
-
return ack !== undefined ? { decision, ack, ...denyAttribution } : { decision, ...denyAttribution };
|
|
2125
|
+
if (replayed !== undefined) {
|
|
2126
|
+
hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay — the approval was already decided (${replayed.decision}); ` +
|
|
2127
|
+
'this answer was not delivered, reporting the earlier decision');
|
|
2128
|
+
return { kind: 'decided', lane: 'respond', decision: replayed.decision, ack: replayed };
|
|
2129
|
+
}
|
|
2130
|
+
const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed) : undefined;
|
|
2131
|
+
return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };
|
|
1878
2132
|
}
|
|
1879
2133
|
catch (e) {
|
|
1880
|
-
const
|
|
1881
|
-
const
|
|
2134
|
+
const r = resolutionOfDecisionFailure(e, { lane: 'respond', route: 'live_respond', attributionCarried: lastSendCarriedAttribution });
|
|
2135
|
+
const refusal = r.kind === 'unsettled' ? r.refusal : undefined;
|
|
1882
2136
|
hostLog('debug', `liveToolApprovalWire: respond(${decision}) failed for ${approvalId} ` +
|
|
1883
|
-
`(status=${
|
|
1884
|
-
`messageLen=${
|
|
1885
|
-
'the refusal
|
|
1886
|
-
return
|
|
1887
|
-
decision: 'unresolved',
|
|
1888
|
-
...(respondRefusal !== undefined ? { respondRefusal } : {}),
|
|
1889
|
-
...(attributionRefusal !== null ? { denyAttributionRefusal: attributionRefusal } : {}),
|
|
1890
|
-
};
|
|
2137
|
+
`(status=${refusal?.status ?? 'none'} errorCode=${logSafeErrorCode(refusal?.errorCode)} ` +
|
|
2138
|
+
`messageLen=${refusal?.message?.length ?? 0}) — engine self-settles (TTL/abort); ` +
|
|
2139
|
+
'the refusal reading is handed back on resolution.refusal for the host to surface');
|
|
2140
|
+
return r;
|
|
1891
2141
|
}
|
|
1892
2142
|
}
|