@sema-agent/client-core 0.108.0 → 0.110.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 +294 -0
- package/README.md +77 -40
- package/dist/adapt/arms.js +41 -13
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +37 -16
- package/dist/adapter/activeRunSelfHeal.js +1 -1
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
- package/dist/agentSession/engineAgentRegistryRead.js +2 -0
- package/dist/bashExitReading.d.ts +12 -0
- package/dist/bashExitReading.js +44 -0
- package/dist/checkpointRowVerdict.d.ts +1 -1
- package/dist/checkpointRowVerdict.js +4 -2
- package/dist/compensations.js +1 -1
- package/dist/declared/supportFloorDeclared.js +1 -1
- package/dist/displayUntrusted.js +132 -14
- package/dist/engineAgentAbsence.d.ts +2 -0
- package/dist/engineAgentAbsence.js +28 -0
- package/dist/engineErrorCodes.d.ts +7 -0
- package/dist/engineErrorCodes.js +7 -0
- package/dist/engineHealthFacts.d.ts +26 -0
- package/dist/engineHealthFacts.js +63 -4
- package/dist/engineInlineTaskStats.d.ts +4 -3
- package/dist/engineInlineTaskStats.js +18 -2
- package/dist/engineNoticeCodes.d.ts +138 -0
- package/dist/engineNoticeCodes.js +662 -55
- package/dist/engineRepoint.d.ts +140 -0
- package/dist/engineRepoint.js +285 -0
- package/dist/engineRowRef.js +2 -0
- package/dist/engineSessionMessages.d.ts +2 -1
- package/dist/engineSessionMessages.js +5 -0
- package/dist/errorSupplementRoles.js +5 -1
- package/dist/fleet/fleetProjection.js +3 -2
- package/dist/fleetTaskDesc.js +3 -3
- package/dist/generated/engineFactTables.d.ts +3 -0
- package/dist/generated/engineFactTables.js +18 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/sdkFactTables.d.ts +1 -0
- package/dist/generated/sdkFactTables.js +4 -0
- package/dist/hitl/approvalAskers.d.ts +6 -0
- package/dist/hitl/approvalAskers.js +30 -0
- package/dist/hitl/approvalCardReasons.js +25 -4
- package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
- package/dist/hitl/approvalRequestFrameReading.js +90 -0
- package/dist/hitl/approvalResolution.d.ts +8 -1
- package/dist/hitl/approvalResolution.js +4 -0
- package/dist/hitl/approvalsFeed.js +1 -1
- package/dist/hitl/askGateWire.js +0 -2
- package/dist/hitl/askParkRowRouting.js +5 -2
- package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
- package/dist/hitl/automaticAnswerVerdict.js +18 -4
- package/dist/hitl/decisionConflict.d.ts +2 -1
- package/dist/hitl/decisionConflict.js +10 -4
- package/dist/hitl/decisionRefusal.js +1 -0
- package/dist/hitl/decisionSend.d.ts +3 -0
- package/dist/hitl/decisionSend.js +29 -11
- package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
- package/dist/hitl/editedApprovalAcceptance.js +26 -0
- package/dist/hitl/frameRouter.js +13 -19
- package/dist/hitl/gateLedger.d.ts +0 -4
- package/dist/hitl/gateLedger.js +0 -19
- package/dist/hitl/hitlBridge.d.ts +5 -0
- package/dist/hitl/hitlBridge.js +13 -9
- package/dist/hitl/hitlHostSurface.d.ts +1 -0
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/hostWithdrawal.d.ts +15 -0
- package/dist/hitl/hostWithdrawal.js +38 -0
- package/dist/hitl/livePendingAsk.js +17 -16
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +1 -1
- package/dist/hitl/noAllowCause.js +4 -2
- package/dist/hitl/parkExpiryJudgements.js +26 -1
- package/dist/hitl/parkOwnership.d.ts +2 -0
- package/dist/hitl/parkOwnership.js +48 -8
- package/dist/hitl/parkResolver.js +32 -16
- package/dist/hitl/parkedUnresumable.d.ts +13 -0
- package/dist/hitl/parkedUnresumable.js +80 -0
- package/dist/hitl/permissionRuleScope.d.ts +4 -0
- package/dist/hitl/permissionRuleScope.js +31 -0
- package/dist/hitl/persistedRulesWire.d.ts +2 -0
- package/dist/hitl/persistedRulesWire.js +13 -12
- package/dist/hitl/planReviewWire.js +14 -4
- package/dist/hitl/sessionAllowRoute.js +5 -4
- package/dist/hitl/sessionPolicyWire.js +6 -4
- package/dist/hitl/standingRows.d.ts +2 -5
- package/dist/hitl/standingRows.js +30 -15
- package/dist/hitl/toolApprovalWire.d.ts +42 -9
- package/dist/hitl/toolApprovalWire.js +267 -44
- package/dist/hooksWireCaps.js +35 -4
- package/dist/hostSettingsSource.js +2 -0
- package/dist/hostTurnSettings.d.ts +10 -4
- package/dist/hostTurnSettings.js +112 -5
- package/dist/i18n/messages.en.d.ts +251 -0
- package/dist/i18n/messages.en.js +254 -2
- package/dist/i18n/messages.zh.js +264 -2
- package/dist/index.d.ts +48 -12
- package/dist/index.js +29 -7
- package/dist/inheritEnvCapability.js +2 -0
- package/dist/inputAuthor.d.ts +16 -0
- package/dist/inputAuthor.js +44 -0
- package/dist/interruptRefusalNote.d.ts +2 -0
- package/dist/interruptRefusalNote.js +17 -0
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +21 -7
- package/dist/liveQuestionStore.d.ts +4 -1
- package/dist/liveQuestionStore.js +12 -3
- package/dist/localEngineJudge.d.ts +75 -0
- package/dist/localEngineJudge.js +242 -0
- package/dist/localEngineOccupancy.d.ts +88 -0
- package/dist/localEngineOccupancy.js +269 -0
- package/dist/localEngineSeatReading.d.ts +29 -0
- package/dist/localEngineSeatReading.js +294 -0
- package/dist/localEngineSpawn.d.ts +113 -0
- package/dist/localEngineSpawn.js +486 -0
- package/dist/localEngineStep.d.ts +286 -0
- package/dist/localEngineStep.js +1086 -0
- package/dist/localEngineStopLadder.d.ts +29 -0
- package/dist/localEngineStopLadder.js +139 -0
- package/dist/localEngineSwapCard.d.ts +43 -0
- package/dist/localEngineSwapCard.js +205 -0
- package/dist/localEngineTiming.d.ts +8 -0
- package/dist/localEngineTiming.js +8 -0
- package/dist/mcpEngineLeg.d.ts +3 -0
- package/dist/mcpEngineLeg.js +38 -1
- package/dist/mcpPanel.js +2 -0
- package/dist/mcpReconnect.d.ts +9 -1
- package/dist/mcpReconnect.js +132 -20
- package/dist/mcpWireCaps.js +2 -2
- package/dist/memoryEntriesWire.d.ts +1 -1
- package/dist/memoryEntriesWire.js +5 -0
- package/dist/modelCapabilityProbe.js +4 -3
- package/dist/modsFace.d.ts +8 -1
- package/dist/modsFace.js +27 -0
- package/dist/noticePresentation.d.ts +2 -0
- package/dist/noticePresentation.js +79 -1
- package/dist/notifications.d.ts +17 -6
- package/dist/notifications.js +67 -19
- package/dist/ownKey.d.ts +5 -0
- package/dist/ownKey.js +8 -1
- package/dist/peerFrames.d.ts +40 -2
- package/dist/peerFrames.js +158 -3
- package/dist/pluginHooksWire.d.ts +2 -1
- package/dist/pluginHooksWire.js +56 -1
- package/dist/replayBody.js +10 -2
- package/dist/request/taskRequest.d.ts +5 -0
- package/dist/request/taskRequest.js +138 -21
- package/dist/resumeRefusalCopy.d.ts +1 -3
- package/dist/resumeRefusalCopy.js +2 -1
- package/dist/retryStatus.d.ts +2 -2
- package/dist/retryStatus.js +2 -0
- package/dist/ruleReadings.js +1 -1
- package/dist/seam.d.ts +24 -2
- package/dist/seam.js +15 -2
- package/dist/seatContract.d.ts +125 -10
- package/dist/seatContract.js +373 -7
- package/dist/sessionBackground.js +4 -1
- package/dist/sessionBackgroundHandoff.js +2 -1
- package/dist/sessionEngine.d.ts +2 -0
- package/dist/sessionEngine.js +111 -0
- package/dist/sessionMemoryStatus.d.ts +2 -1
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +28 -0
- package/dist/sseIdleTriage.d.ts +13 -1
- package/dist/sseIdleTriage.js +24 -6
- package/dist/subagent/engineSubagentTail.js +6 -0
- package/dist/subagentContentStore.d.ts +12 -0
- package/dist/subagentContentStore.js +72 -11
- package/dist/terminalStatuses.d.ts +1 -0
- package/dist/terminalStatuses.js +4 -1
- package/dist/toolResult.js +18 -5
- package/dist/turnFailurePresentation.js +9 -2
- package/dist/typePins.d.ts +11 -0
- package/dist/usageAnalytics.d.ts +17 -0
- package/dist/usageAnalytics.js +44 -2
- package/dist/wireFailureShape.d.ts +22 -5
- package/dist/wireFailureShape.js +47 -26
- package/dist/wireRefusalCopy.d.ts +31 -1
- package/dist/wireRefusalCopy.js +167 -2
- package/docs/INTEGRATION-CLIENTS.md +3269 -21
- package/package.json +5 -5
|
@@ -1,13 +1,13 @@
|
|
|
1
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
|
-
import { seatSessionRowOf } from './standingRows.js';
|
|
4
|
+
import { seatAllowNoteCarriedOf, seatSessionRowOf } from './standingRows.js';
|
|
5
5
|
import { hostLog } from '../host.js';
|
|
6
6
|
import { trimEditedRuleText } from './editedRuleTextPrecheck.js';
|
|
7
7
|
import { appendOwnElement, guardedRead, readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
|
|
8
8
|
import { ownReadingSlot } from '../engineWireFor.js';
|
|
9
9
|
import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPair } from './askClassPair.js';
|
|
10
|
-
import { approvalAgentIdOf, approvalAskReasonsOf } from './approvalAskers.js';
|
|
10
|
+
import { approvalAgentIdOf, approvalAskReasonsOf, approvalRequestedByOf } from './approvalAskers.js';
|
|
11
11
|
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
12
12
|
import { approvalWindowKeysOf, readApprovalCardWord, readRequiresRealApproval } from './approvalCardReading.js';
|
|
13
13
|
import { readAllowRuleShadowFromWire, retiredAllowRuleShadowKeyPresent, RETIRED_SHADOW_EVIDENCE_LABEL } from './allowRuleShadow.js';
|
|
@@ -17,26 +17,29 @@ import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
|
|
|
17
17
|
import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_PERSISTED_RULE_MATCHES } from '../generated/engineFactTables.js';
|
|
18
18
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
19
19
|
import { sessionAllowCarried, sessionAllowEligible } from './sessionAllowRoute.js';
|
|
20
|
-
import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
20
|
+
import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceAllowNoteNotCarried, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
21
21
|
import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
|
|
22
22
|
import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
|
|
23
23
|
import { frameBindingOf } from '../frameBinding.js';
|
|
24
24
|
import { carriesAttributionKey, deliverWithAttributionLadder } from './attributionLadder.js';
|
|
25
25
|
import { deliverApprovalDecision, newSendTally } from './approvalDelivery.js';
|
|
26
26
|
import { approvalDecisionHolds, approvalHoldFor, awaitApprovalHolder, claimApprovalCardFor, enterLiveRespondPhase, holdLiveCard, joinLiveCard, liveCardHoldOf, clearSettledApprovals, noteHolderSettledApproval, settledApprovalOf, settledForDurableLane, settledLiveCardOutcome } from './approvalHolds.js';
|
|
27
|
-
import { directFailureVerdictOf, directSafetyStopOf, joinedResolution, resolutionOfDecisionFailure } from './approvalResolution.js';
|
|
27
|
+
import { directFailureVerdictOf, directSafetyStopOf, joinedResolution, resolutionOfDecisionFailure, toolPermissionSettledElsewhereOf } from './approvalResolution.js';
|
|
28
|
+
import { hostWithdrawalOf } from './hostWithdrawal.js';
|
|
28
29
|
import { parkRowDeadlineState } from './parkExpiryJudgements.js';
|
|
29
30
|
import { isApprovalMandateWord } from '../gateVocabulary.js';
|
|
30
31
|
import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
|
|
31
32
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
33
|
+
import { editedApprovalAcceptanceOf } from './editedApprovalAcceptance.js';
|
|
32
34
|
import { readPolicyDenyCause } from './policyDenyCause.js';
|
|
33
|
-
import { slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
35
|
+
import { inValueOf, slotPresenceOf, slotReadOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
34
36
|
import { APPROVAL_LIVE_FACE_KEYS, approvalLiveFaceOf, hasOwnStrictTrue } from './approvalLiveFace.js';
|
|
37
|
+
import { approvalFrameUnreadableWhyOf, approvalRequestFrameReadingThrowing, cardPayloadReadingOf, } from './approvalRequestFrameReading.js';
|
|
35
38
|
import { subagentSourceSessionOf } from './subagentSource.js';
|
|
36
39
|
import { isPlainRecord } from '../jsonFaithful.js';
|
|
37
40
|
import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
|
|
38
41
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
39
|
-
import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS,
|
|
42
|
+
import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, } from '@sema-agent/sdk';
|
|
40
43
|
export function toolNameIsFsWrite(name) {
|
|
41
44
|
const n = name.replace(/[\s_-]+/g, '').toLowerCase();
|
|
42
45
|
return (n === 'write' || n === 'filewrite' || n === 'edit' || n === 'fileedit' || n === 'multiedit' || n === 'notebookedit');
|
|
@@ -97,6 +100,16 @@ export function readApprovalDenySettledBy(decision) {
|
|
|
97
100
|
: undefined;
|
|
98
101
|
}
|
|
99
102
|
export const RETRACTED_CARD_DECISION_KIND = 'retracted';
|
|
103
|
+
export const WITHDRAWN_CARD_DECISION_KIND = 'withdrawn';
|
|
104
|
+
function withdrawnCardResolution(card, at, logTag) {
|
|
105
|
+
const w = hostWithdrawalOf(card);
|
|
106
|
+
if (w === undefined) {
|
|
107
|
+
hostLog('debug', `${logTag}: card-withdrawn-unreadable as=retracted`);
|
|
108
|
+
return { kind: 'not_sent', ...at, cause: 'retracted' };
|
|
109
|
+
}
|
|
110
|
+
hostLog('debug', `${logTag}: card-withdrawn-by-host verdict=${w.rowVerdict.kind}`);
|
|
111
|
+
return { kind: 'not_sent', ...at, cause: 'withdrawn', toldBy: w.toldBy, rowVerdict: w.rowVerdict };
|
|
112
|
+
}
|
|
100
113
|
function parkRowOf(taskId, row, gatedCallId) {
|
|
101
114
|
const d = parkRowDeadlineState(row, Date.now());
|
|
102
115
|
return Object.freeze({
|
|
@@ -173,6 +186,16 @@ export function rememberRefusalOf(error) {
|
|
|
173
186
|
function rememberRefusalIn(reading) {
|
|
174
187
|
return reading.state === 'code' ? REMEMBER_REFUSAL_CODES.find((c) => c === reading.code) : undefined;
|
|
175
188
|
}
|
|
189
|
+
const FEEDBACK_REFUSAL_CODES = Object.freeze(['feedback_not_in_proof']);
|
|
190
|
+
function feedbackRefusalOf(error) {
|
|
191
|
+
const reading = readWireErrorCodeReading(error);
|
|
192
|
+
return reading.state === 'code' && FEEDBACK_REFUSAL_CODES.some((c) => c === reading.code);
|
|
193
|
+
}
|
|
194
|
+
function approveBodyWithoutFeedback(body) {
|
|
195
|
+
const { feedback: _feedback, ...rest } = body;
|
|
196
|
+
void _feedback;
|
|
197
|
+
return rest;
|
|
198
|
+
}
|
|
176
199
|
function logAfterDecided(msg) {
|
|
177
200
|
try {
|
|
178
201
|
hostLog('debug', msg);
|
|
@@ -248,6 +271,14 @@ function cardInputOf(recorded, stream) {
|
|
|
248
271
|
return { from: 'stream', args: stream, shape };
|
|
249
272
|
return { from: 'none', shape, recorded };
|
|
250
273
|
}
|
|
274
|
+
function inputSourceBitOf(reading) {
|
|
275
|
+
return reading.from === 'recorded' ? { inputSource: 'record' } : reading.from === 'stream' ? { inputSource: 'stream' } : {};
|
|
276
|
+
}
|
|
277
|
+
function approvalInputSourceOf(card) {
|
|
278
|
+
const raw = ownDataValue(card, 'inputSource');
|
|
279
|
+
const known = { record: true, stream: true };
|
|
280
|
+
return typeof raw === 'string' && Object.hasOwn(known, raw) ? raw : undefined;
|
|
281
|
+
}
|
|
251
282
|
const CARD_ARGS_COMPARE_MAX_NODES = 20_000;
|
|
252
283
|
const CARD_ARGS_COMPARE_MAX_DEPTH = 256;
|
|
253
284
|
const NOT_COMPARABLE = Symbol('not comparable');
|
|
@@ -493,6 +524,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
493
524
|
const rowArgsGap = reading.from !== 'none' ? undefined : reading.shape === 'absent' ? 'absent' : reading.shape === 'size_marker' ? 'truncated' : undefined;
|
|
494
525
|
const args = reading.from !== 'none' ? reading.args : rowArgsGap !== undefined ? {} : reading.recorded;
|
|
495
526
|
const argsRewritten = reading.from === 'recorded' && reading.rewritten;
|
|
527
|
+
const argsFromModelCall = reading.from === 'stream';
|
|
496
528
|
if (typeof args !== 'object' || args === null) {
|
|
497
529
|
return { kind: 'not_sent', ...at, cause: 'no_input', detail: 'gate has no tool input payload' };
|
|
498
530
|
}
|
|
@@ -516,6 +548,8 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
516
548
|
}
|
|
517
549
|
: {}),
|
|
518
550
|
...(argsRewritten ? { argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : {}),
|
|
551
|
+
...(argsFromModelCall ? { argsUnavailable: true, wireNote: renderMessage(msg('approval.park.argsFromModelCall')) } : {}),
|
|
552
|
+
...inputSourceBitOf(reading),
|
|
519
553
|
...(pending.governanceForced === true ? { governanceForced: true } : {}),
|
|
520
554
|
...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
|
|
521
555
|
...approvalLiveFaceOf(pending).facts,
|
|
@@ -538,11 +572,13 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
538
572
|
allowSessionCarried: true,
|
|
539
573
|
grantBinding: readApprovalGrantBinding(pending),
|
|
540
574
|
parkRow: parkRowOf(taskId, pending, gatedCallId),
|
|
575
|
+
...(typeof taskId === 'string' && taskId !== '' ? { taskId } : {}),
|
|
541
576
|
});
|
|
577
|
+
const editAcceptance = editedApprovalAcceptanceOf(cardRequest);
|
|
542
578
|
const card = await surfaceCallTrackedCard(gatedCallId, undefined, ledgerSlot, cardRequest);
|
|
543
579
|
hold.phase = 'respond';
|
|
544
|
-
if (
|
|
545
|
-
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)`);
|
|
580
|
+
if (editAcceptance.state !== 'accepts' && card.kind === 'allow' && card.updatedInput !== undefined) {
|
|
581
|
+
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' : rowArgsGap === 'absent' ? 'missing from both the stream and the pending row' : 'not recorded on the pending row'} — nothing sent (the park stays pending)`);
|
|
546
582
|
surfaceEditRefusedOnBlindAsk();
|
|
547
583
|
return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: PARK_EDIT_REFUSED_REASON };
|
|
548
584
|
}
|
|
@@ -555,6 +591,8 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
555
591
|
case 'retracted':
|
|
556
592
|
hostLog('debug', `liveHitlAskWire: approval card retracted without a decision for ${gatedCallId ?? '(no call)'} — nothing sent`);
|
|
557
593
|
return { kind: 'not_sent', ...at, cause: 'retracted' };
|
|
594
|
+
case WITHDRAWN_CARD_DECISION_KIND:
|
|
595
|
+
return withdrawnCardResolution(card, at, 'liveHitlAskWire');
|
|
558
596
|
case 'aborted':
|
|
559
597
|
{
|
|
560
598
|
const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
|
|
@@ -575,11 +613,30 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
575
613
|
const { remember: _remember, ...plainApproveBody } = approveBody;
|
|
576
614
|
void _remember;
|
|
577
615
|
emitAnswerCommitted(callKey, 'decide', answered, approveBody);
|
|
616
|
+
let feedbackDropped = false;
|
|
617
|
+
const sendApprove = async (body) => {
|
|
618
|
+
const wire = feedbackDropped ? approveBodyWithoutFeedback(body) : body;
|
|
619
|
+
try {
|
|
620
|
+
return await bridge.decideTool(wire, gatedCallId, decideOpts, pending);
|
|
621
|
+
}
|
|
622
|
+
catch (e) {
|
|
623
|
+
if (wire.feedback === undefined || !feedbackRefusalOf(e))
|
|
624
|
+
throw e;
|
|
625
|
+
const verdict = directFailureVerdictOf(e, directSafetyStopOf(e));
|
|
626
|
+
if (verdict.yields) {
|
|
627
|
+
rememberStop = ownReadingSlot(verdict, 'directStop');
|
|
628
|
+
throw e;
|
|
629
|
+
}
|
|
630
|
+
feedbackDropped = true;
|
|
631
|
+
logAfterDecided(`liveToolApprovalWire: decide(approve+feedback) refused the feedback part (${String(e)}) — resending without the note`);
|
|
632
|
+
return await bridge.decideTool(approveBodyWithoutFeedback(wire), gatedCallId, decideOpts, pending);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
578
635
|
if (approveBody.remember === 'session') {
|
|
579
636
|
let sessionReceipt;
|
|
580
637
|
let sessionDecided = false;
|
|
581
638
|
try {
|
|
582
|
-
const raw = await
|
|
639
|
+
const raw = await sendApprove(approveBody);
|
|
583
640
|
sessionReceipt = readDecideReceipt(raw);
|
|
584
641
|
sessionDecided = true;
|
|
585
642
|
}
|
|
@@ -597,13 +654,17 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
597
654
|
if (sessionDecided) {
|
|
598
655
|
if (sessionReceipt?.rememberApplied === false)
|
|
599
656
|
surfaceRememberNotApplied();
|
|
657
|
+
if (feedbackDropped)
|
|
658
|
+
surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
|
|
600
659
|
return { kind: 'decided', ...at, decision: 'allow', ...(sessionReceipt !== undefined ? { receipt: sessionReceipt } : {}) };
|
|
601
660
|
}
|
|
602
661
|
}
|
|
603
|
-
const raw = await
|
|
662
|
+
const raw = await sendApprove(plainApproveBody);
|
|
604
663
|
const receipt = readDecideReceipt(raw);
|
|
605
664
|
if (rememberRefused || sessionDowngraded)
|
|
606
665
|
surfaceRememberNotApplied();
|
|
666
|
+
if (feedbackDropped)
|
|
667
|
+
surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
|
|
607
668
|
return { kind: 'decided', ...at, decision: 'allow', ...(receipt !== undefined ? { receipt } : {}) };
|
|
608
669
|
}
|
|
609
670
|
catch (e) {
|
|
@@ -685,8 +746,11 @@ function readReadRootCandidate(v) {
|
|
|
685
746
|
const o = v;
|
|
686
747
|
if (typeof o.dir !== 'string' || o.dir.length === 0 || o.clearsThisAsk !== true)
|
|
687
748
|
return undefined;
|
|
688
|
-
|
|
689
|
-
|
|
749
|
+
const covers = inValueOf(o, 'covers');
|
|
750
|
+
if (covers.state === 'unproven')
|
|
751
|
+
return undefined;
|
|
752
|
+
if (covers.state === 'present')
|
|
753
|
+
return covers.value === 'exact' ? { dir: o.dir, clearsThisAsk: true, covers: 'exact' } : undefined;
|
|
690
754
|
return { dir: o.dir, clearsThisAsk: true };
|
|
691
755
|
}
|
|
692
756
|
export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
|
|
@@ -764,6 +828,12 @@ export function toolPermissionRequestAskBits(card, requestId) {
|
|
|
764
828
|
const origin = readAskOrigin(card);
|
|
765
829
|
const ruleOffersAbsence = readOwnNonEmptyString(card, 'ruleOffersAbsence');
|
|
766
830
|
const askClassPair = readAskClassPair(card);
|
|
831
|
+
const probeLayer = probeCarrierOf(card);
|
|
832
|
+
const probe = probeFactsAt(probeLayer);
|
|
833
|
+
const onTop = probeLayer === card;
|
|
834
|
+
const requestedBy = onTop ? approvalRequestedByOf(card) : undefined;
|
|
835
|
+
const payload = onTop ? cardPayloadReadingOf(card) : undefined;
|
|
836
|
+
const inputSource = onTop ? approvalInputSourceOf(card) : undefined;
|
|
767
837
|
return {
|
|
768
838
|
...(mandated ? { mandated: true } : {}),
|
|
769
839
|
...(mandate !== undefined ? { mandate } : {}),
|
|
@@ -771,6 +841,12 @@ export function toolPermissionRequestAskBits(card, requestId) {
|
|
|
771
841
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
772
842
|
...(askClassPair ?? {}),
|
|
773
843
|
...(seatSessionRowOf(card, requestId) ? { sessionRow: true } : {}),
|
|
844
|
+
...(probe.prose !== undefined ? { probeReason: probe.prose } : {}),
|
|
845
|
+
...(probe.cause !== undefined ? { probeCause: probe.cause } : {}),
|
|
846
|
+
...(seatAllowNoteCarriedOf(card, requestId) ? { feedbackCapable: true } : {}),
|
|
847
|
+
...(requestedBy !== undefined ? { requestedBy } : {}),
|
|
848
|
+
...(payload !== undefined && payload.payloadUnreadable && payload.why !== undefined ? { frameReading: payload.why } : {}),
|
|
849
|
+
...(inputSource !== undefined ? { inputSource } : {}),
|
|
774
850
|
};
|
|
775
851
|
}
|
|
776
852
|
function isToolApprovalDelegation(v) {
|
|
@@ -797,6 +873,14 @@ function respondDecisionOfAnswer(answer) {
|
|
|
797
873
|
return answer.allowSession ? 'allow_session' : 'allow';
|
|
798
874
|
return answer.kind === 'deny' ? 'deny' : undefined;
|
|
799
875
|
}
|
|
876
|
+
export function allowNoteReadingOf(answer) {
|
|
877
|
+
const note = readOwnDataValue(answer, 'note');
|
|
878
|
+
if (note === undefined)
|
|
879
|
+
return { kind: 'absent' };
|
|
880
|
+
if (typeof note !== 'string')
|
|
881
|
+
return { kind: 'invalid' };
|
|
882
|
+
return note.trim() === '' ? { kind: 'blank' } : { kind: 'text', text: note };
|
|
883
|
+
}
|
|
800
884
|
function respondNoteGate(reason, capable) {
|
|
801
885
|
if (reason === undefined || reason.trim() === '')
|
|
802
886
|
return { dropped: 'blank' };
|
|
@@ -815,10 +899,14 @@ export function answerPayloadFor(answer, lane, caps) {
|
|
|
815
899
|
const reason = answer.kind === 'deny' && typeof answer.reason === 'string' ? answer.reason : undefined;
|
|
816
900
|
return Object.freeze({ decision: 'deny', reason: denyReasonForWire(reason, 'answer') ?? DEFAULT_DENY_REASON });
|
|
817
901
|
}
|
|
902
|
+
const fb = allowNoteReadingOf(answer);
|
|
903
|
+
if (fb.kind === 'invalid')
|
|
904
|
+
hostLog('error', `answerPayloadFor: the allow note must be a string (got ${typeof readOwnDataValue(answer, 'note')}) — not sent as feedback; the decision itself is sent`);
|
|
818
905
|
return Object.freeze({
|
|
819
906
|
decision: 'approve',
|
|
820
907
|
...(word === 'allow_session' ? { remember: 'session' } : {}),
|
|
821
908
|
...(answer.updatedInput !== undefined ? { updatedInput: answer.updatedInput } : {}),
|
|
909
|
+
...(fb.kind === 'text' ? { feedback: fb.text } : {}),
|
|
822
910
|
});
|
|
823
911
|
}
|
|
824
912
|
if (answer.kind === 'allow')
|
|
@@ -827,9 +915,25 @@ export function answerPayloadFor(answer, lane, caps) {
|
|
|
827
915
|
return Object.freeze({ decision: word, ...('note' in gate ? { note: gate.note } : {}) });
|
|
828
916
|
}
|
|
829
917
|
const ANSWER_CARRIAGE_NONE = Object.freeze({ allowNote: 'none' });
|
|
918
|
+
const ANSWER_CARRIAGE_MODEL = Object.freeze({ allowNote: 'model' });
|
|
830
919
|
export function answerCarriage(card) {
|
|
831
|
-
|
|
832
|
-
|
|
920
|
+
return cardIsOnParkRow(card) ? ANSWER_CARRIAGE_MODEL : ANSWER_CARRIAGE_NONE;
|
|
921
|
+
}
|
|
922
|
+
function cardIsOnParkRow(card) {
|
|
923
|
+
try {
|
|
924
|
+
if (typeof card !== 'object' || card === null)
|
|
925
|
+
return false;
|
|
926
|
+
const park = readOwnDataValue(card, 'parkRow');
|
|
927
|
+
if (typeof park !== 'object' || park === null || Array.isArray(park))
|
|
928
|
+
return false;
|
|
929
|
+
return readOwnDataNonEmptyString(park, 'taskId') !== undefined;
|
|
930
|
+
}
|
|
931
|
+
catch {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
export function allowNoteNotCarriedSentence(lang) {
|
|
936
|
+
return renderMessage(msg('approval.allowNote.notCarried'), lang);
|
|
833
937
|
}
|
|
834
938
|
export function observeApprovalAnswerCommitted(listener) {
|
|
835
939
|
answerCommittedListeners.add(listener);
|
|
@@ -846,12 +950,30 @@ function emitAnswerCommitted(callKey, lane, answer, payload) {
|
|
|
846
950
|
invokeHostCallbackIsolated('liveToolApprovalWire: answer-committed listener', () => l(event));
|
|
847
951
|
}
|
|
848
952
|
export function isToolApprovalRespondReplayed(ack) {
|
|
849
|
-
return typeof ack === 'object' && ack !== null && ack.idempotent === true;
|
|
953
|
+
return typeof ack === 'object' && ack !== null && ack.idempotent === true && ack.accepted !== 'parked_redeemed';
|
|
954
|
+
}
|
|
955
|
+
export function isToolApprovalRespondParkedRedeemed(ack) {
|
|
956
|
+
return typeof ack === 'object' && ack !== null && ack.accepted === 'parked_redeemed';
|
|
957
|
+
}
|
|
958
|
+
export function replayedFirstDecisionOf(ack) {
|
|
959
|
+
if (ack === undefined)
|
|
960
|
+
return undefined;
|
|
961
|
+
if (isToolApprovalRespondReplayed(ack))
|
|
962
|
+
return ack.decision;
|
|
963
|
+
if (isToolApprovalRespondParkedRedeemed(ack) && ack.idempotent === true)
|
|
964
|
+
return ack.decision === 'allow' || ack.decision === 'deny' ? ack.decision : undefined;
|
|
965
|
+
return undefined;
|
|
966
|
+
}
|
|
967
|
+
export function settlementAckOf(read) {
|
|
968
|
+
return read !== undefined && isToolApprovalRespondParkedRedeemed(read) && read.idempotent !== true ? undefined : read;
|
|
850
969
|
}
|
|
851
970
|
export function readToolApprovalRespondAck(v) {
|
|
852
971
|
if (v === null || typeof v !== 'object')
|
|
853
972
|
return undefined;
|
|
854
973
|
const o = v;
|
|
974
|
+
const accepted = slotReadOf(o, 'accepted');
|
|
975
|
+
if (accepted.presence === 'present' && accepted.value === 'parked_redeemed')
|
|
976
|
+
return readParkedRedeemedAck(o);
|
|
855
977
|
if (o.idempotent === true)
|
|
856
978
|
return o.delivery === undefined ? readReplayedAck(o) : undefined;
|
|
857
979
|
if (o.delivery !== 'applied')
|
|
@@ -969,6 +1091,44 @@ function readReplayedAck(o) {
|
|
|
969
1091
|
...(o.rememberApplied === false ? { rememberApplied: false } : {}),
|
|
970
1092
|
};
|
|
971
1093
|
}
|
|
1094
|
+
function readParkedRedeemedAck(o) {
|
|
1095
|
+
if (slotReadOf(o, 'delivery').presence !== 'absent')
|
|
1096
|
+
return undefined;
|
|
1097
|
+
const text = (key) => {
|
|
1098
|
+
const slot = slotReadOf(o, key);
|
|
1099
|
+
return slot.presence === 'present' && typeof slot.value === 'string' && slot.value !== '' ? slot.value : undefined;
|
|
1100
|
+
};
|
|
1101
|
+
const approvalId = text('approvalId');
|
|
1102
|
+
const askId = text('askId');
|
|
1103
|
+
if (approvalId === undefined || askId === undefined)
|
|
1104
|
+
return undefined;
|
|
1105
|
+
const idem = slotReadOf(o, 'idempotent');
|
|
1106
|
+
if (idem.presence === 'unreadable')
|
|
1107
|
+
return undefined;
|
|
1108
|
+
const replay = idem.presence === 'present' && idem.value === true;
|
|
1109
|
+
const dec = slotReadOf(o, 'decision');
|
|
1110
|
+
let decision;
|
|
1111
|
+
if (replay) {
|
|
1112
|
+
if (dec.presence !== 'present' || (dec.value !== 'allow' && dec.value !== 'deny'))
|
|
1113
|
+
return undefined;
|
|
1114
|
+
decision = dec.value === 'allow' ? 'allow' : 'deny';
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
decision = dec.presence === 'present' && typeof dec.value === 'string' && dec.value !== '' ? dec.value : undefined;
|
|
1118
|
+
}
|
|
1119
|
+
const remember = slotReadOf(o, 'rememberApplied');
|
|
1120
|
+
return {
|
|
1121
|
+
approvalId,
|
|
1122
|
+
askId,
|
|
1123
|
+
accepted: 'parked_redeemed',
|
|
1124
|
+
...(replay ? { idempotent: true } : {}),
|
|
1125
|
+
...(decision !== undefined ? { decision } : {}),
|
|
1126
|
+
...(remember.presence === 'present' && typeof remember.value === 'boolean' ? { rememberApplied: remember.value } : {}),
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function replayedDecisionOf(first, sent, ack) {
|
|
1130
|
+
return { kind: 'decided', lane: 'respond', decision: first, ...(ack !== undefined ? { ack } : {}), settledElsewhere: toolPermissionSettledElsewhereOf(first, sent) };
|
|
1131
|
+
}
|
|
972
1132
|
function isNonEmptyStringArray(v) {
|
|
973
1133
|
return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
|
|
974
1134
|
}
|
|
@@ -1074,19 +1234,44 @@ function readProbeReason(v) {
|
|
|
1074
1234
|
function usableProbeReason(raw) {
|
|
1075
1235
|
return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
|
|
1076
1236
|
}
|
|
1237
|
+
function probeFactsAt(at) {
|
|
1238
|
+
const prose = usableProbeReason(ownDataValue(at, 'probeReason'));
|
|
1239
|
+
let cause;
|
|
1240
|
+
try {
|
|
1241
|
+
const raw = ownDataValue(at, 'probeCause');
|
|
1242
|
+
if (isWireRecordCarrier(raw))
|
|
1243
|
+
cause = raw;
|
|
1244
|
+
}
|
|
1245
|
+
catch {
|
|
1246
|
+
cause = undefined;
|
|
1247
|
+
}
|
|
1248
|
+
return { prose, cause };
|
|
1249
|
+
}
|
|
1250
|
+
function probeCarrierOf(carrier) {
|
|
1251
|
+
let d;
|
|
1252
|
+
try {
|
|
1253
|
+
d = Object.getOwnPropertyDescriptor(carrier, 'riskDescriptor');
|
|
1254
|
+
}
|
|
1255
|
+
catch {
|
|
1256
|
+
return undefined;
|
|
1257
|
+
}
|
|
1258
|
+
if (d === undefined)
|
|
1259
|
+
return carrier;
|
|
1260
|
+
return Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
1261
|
+
}
|
|
1077
1262
|
const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
|
|
1078
1263
|
export function askProbeReasonDetail(card, lang) {
|
|
1079
|
-
const prose =
|
|
1264
|
+
const { prose, cause } = probeFactsAt(card);
|
|
1080
1265
|
const shown = prose === undefined ? '' : displayUntrusted(prose);
|
|
1081
|
-
const sentence = probeCauseSentence(
|
|
1266
|
+
const sentence = probeCauseSentence(cause, shown !== '', lang);
|
|
1082
1267
|
if (sentence === undefined)
|
|
1083
1268
|
return shown === '' ? undefined : shown;
|
|
1084
1269
|
return shown === '' ? sentence : renderMessage(msg('approval.probe.proseJoin', { sentence, prose: shown }), lang);
|
|
1085
1270
|
}
|
|
1086
1271
|
function probeCauseSentence(cause, withProse, lang) {
|
|
1272
|
+
if (cause === undefined)
|
|
1273
|
+
return undefined;
|
|
1087
1274
|
try {
|
|
1088
|
-
if (!isWireRecordCarrier(cause))
|
|
1089
|
-
return undefined;
|
|
1090
1275
|
const code = ownDataValue(cause, 'code');
|
|
1091
1276
|
const withCode = typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code);
|
|
1092
1277
|
const key = withProse
|
|
@@ -1679,7 +1864,7 @@ function v1Bit(stamp) {
|
|
|
1679
1864
|
const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
|
|
1680
1865
|
'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
|
|
1681
1866
|
'Edits are not accepted on this card: there is no original input to edit';
|
|
1682
|
-
function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
|
|
1867
|
+
function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
|
|
1683
1868
|
const c = carrier;
|
|
1684
1869
|
const full = leg !== 'generic';
|
|
1685
1870
|
const live = leg === 'frame' ? carrier : leg === 'v1' ? liveFacts : undefined;
|
|
@@ -1707,12 +1892,14 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
|
|
|
1707
1892
|
if (reading.from === 'recorded') {
|
|
1708
1893
|
if (blind)
|
|
1709
1894
|
return { args: reading.args, argsUnavailable: true };
|
|
1710
|
-
return reading.rewritten
|
|
1895
|
+
return reading.rewritten
|
|
1896
|
+
? { args: reading.args, argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE, ...inputSourceBitOf(reading) }
|
|
1897
|
+
: { args: reading.args, ...inputSourceBitOf(reading) };
|
|
1711
1898
|
}
|
|
1712
1899
|
if (reading.from === 'stream' && typeof reading.args === 'object' && reading.args !== null) {
|
|
1713
1900
|
if (blind)
|
|
1714
1901
|
return { args: reading.args, argsUnavailable: true };
|
|
1715
|
-
return { args: reading.args, wireNote: ARGS_FROM_MODEL_CALL_NOTE };
|
|
1902
|
+
return { args: reading.args, wireNote: ARGS_FROM_MODEL_CALL_NOTE, ...inputSourceBitOf(reading) };
|
|
1716
1903
|
}
|
|
1717
1904
|
const omitted = c.argsOmitted === true || reading.shape === 'size_marker';
|
|
1718
1905
|
const fallback = argsFallbackOf(c.message, omitted);
|
|
@@ -1743,6 +1930,8 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
|
|
|
1743
1930
|
out.argsUnavailable = true;
|
|
1744
1931
|
if (argsPart.argsRewritten === true)
|
|
1745
1932
|
out.argsRewritten = true;
|
|
1933
|
+
if (argsPart.inputSource !== undefined)
|
|
1934
|
+
out.inputSource = argsPart.inputSource;
|
|
1746
1935
|
if (full) {
|
|
1747
1936
|
v1Bit(() => {
|
|
1748
1937
|
if (c.governanceForced === true)
|
|
@@ -1778,6 +1967,11 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
|
|
|
1778
1967
|
if (r !== undefined)
|
|
1779
1968
|
out.probeReason = r;
|
|
1780
1969
|
});
|
|
1970
|
+
v1Bit(() => {
|
|
1971
|
+
const by = approvalRequestedByOf(live);
|
|
1972
|
+
if (by !== undefined)
|
|
1973
|
+
out.requestedBy = by;
|
|
1974
|
+
});
|
|
1781
1975
|
}
|
|
1782
1976
|
v1Bit(() => {
|
|
1783
1977
|
if (readApprovalRememberRefused(carrier))
|
|
@@ -1865,8 +2059,11 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
|
|
|
1865
2059
|
if (typeof msg === 'string' && msg.trim() !== '')
|
|
1866
2060
|
out.message = msg;
|
|
1867
2061
|
});
|
|
1868
|
-
if (leg === 'generic')
|
|
2062
|
+
if (leg === 'generic') {
|
|
1869
2063
|
out.cardPayloadUnreadable = true;
|
|
2064
|
+
if (payloadWhy !== undefined)
|
|
2065
|
+
out.cardPayloadUnreadableWhy = payloadWhy;
|
|
2066
|
+
}
|
|
1870
2067
|
if (sessionAllowCarried(leg, approvalId))
|
|
1871
2068
|
out.allowSessionCarried = true;
|
|
1872
2069
|
out.grantBinding = readApprovalGrantBinding(carrier);
|
|
@@ -1898,17 +2095,29 @@ export function approvalCardFactsOfFrame(frame, opts) {
|
|
|
1898
2095
|
return undefined;
|
|
1899
2096
|
return Object.freeze(cardFactsOf(frame, 'frame', opts));
|
|
1900
2097
|
}
|
|
1901
|
-
export function approvalCardFactsOfGeneric(card) {
|
|
2098
|
+
export function approvalCardFactsOfGeneric(card, reading) {
|
|
1902
2099
|
if (typeof card !== 'object' || card === null)
|
|
1903
2100
|
return undefined;
|
|
1904
|
-
return Object.freeze(cardFactsOf(card, 'generic'));
|
|
2101
|
+
return Object.freeze(cardFactsOf(card, 'generic', null, undefined, undefined, approvalFrameUnreadableWhyOf(reading)));
|
|
1905
2102
|
}
|
|
1906
2103
|
const MAX_RESPOND_NOTE_CHARS = 2048;
|
|
1907
2104
|
export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
|
|
2105
|
+
const approvalId = readOwnDataNonEmptyString(frame, 'approvalId');
|
|
2106
|
+
if (approvalId === undefined)
|
|
2107
|
+
return approvalIdUnreadableRefusal();
|
|
2108
|
+
return surfaceApprovalFrameWithId(frame, approvalId, respond, streamArgs, signal, lane, run);
|
|
2109
|
+
}
|
|
2110
|
+
function approvalIdUnreadableRefusal() {
|
|
2111
|
+
hostLog('debug', 'liveToolApprovalWire: refused (approvalId_unreadable)');
|
|
2112
|
+
return { kind: 'not_sent', lane: 'respond', cause: 'card_unavailable' };
|
|
2113
|
+
}
|
|
2114
|
+
export async function surfaceApprovalFrameWithId(frame, approvalId, respond, streamArgs, signal, lane, run) {
|
|
1908
2115
|
const receivedAtMs = Date.now();
|
|
1909
|
-
|
|
2116
|
+
if (typeof approvalId !== 'string' || approvalId === '')
|
|
2117
|
+
return approvalIdUnreadableRefusal();
|
|
1910
2118
|
const carriers = openingFrameCarriersOf(frame);
|
|
1911
|
-
const
|
|
2119
|
+
const liveRun = liveCardRunOf(frame, run);
|
|
2120
|
+
const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, carriers, liveRun), respond, streamArgs, signal, lane, receivedAtMs };
|
|
1912
2121
|
return enterLiveCard(entry);
|
|
1913
2122
|
}
|
|
1914
2123
|
function openingFrameCarriersOf(frame) {
|
|
@@ -1916,9 +2125,10 @@ function openingFrameCarriersOf(frame) {
|
|
|
1916
2125
|
return { body: frame, live: frame, leg: 'frame' };
|
|
1917
2126
|
const card = frame.card;
|
|
1918
2127
|
const pinned = Object.create(frame, { card: { value: card, enumerable: true } });
|
|
1919
|
-
|
|
2128
|
+
const reading = approvalRequestFrameReadingThrowing(pinned);
|
|
2129
|
+
if (reading.v1)
|
|
1920
2130
|
return { body: pinned.card, live: frame, leg: 'v1' };
|
|
1921
|
-
return { body: typeof card === 'object' && card !== null ? card : {}, live: undefined, leg: 'generic' };
|
|
2131
|
+
return { body: typeof card === 'object' && card !== null ? card : {}, live: undefined, leg: 'generic', why: reading.why };
|
|
1922
2132
|
}
|
|
1923
2133
|
function enterLiveCard(e) {
|
|
1924
2134
|
const { approvalId, signal } = e;
|
|
@@ -1933,7 +2143,7 @@ function enterLiveCard(e) {
|
|
|
1933
2143
|
return joinOtherLaneHolder(e, other);
|
|
1934
2144
|
return holdLiveCard(e.identity, signal, async (hold) => {
|
|
1935
2145
|
const slot = {};
|
|
1936
|
-
const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
|
|
2146
|
+
const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs, e.identity.taskId);
|
|
1937
2147
|
noteCallOutcome(slot, out);
|
|
1938
2148
|
return out;
|
|
1939
2149
|
});
|
|
@@ -1948,15 +2158,20 @@ async function joinOtherLaneHolder(e, other) {
|
|
|
1948
2158
|
return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
|
|
1949
2159
|
return enterLiveCard(e);
|
|
1950
2160
|
}
|
|
1951
|
-
function
|
|
2161
|
+
function liveCardRunOf(frame, run) {
|
|
2162
|
+
const source = frameBindingOf(frame) ?? run;
|
|
2163
|
+
return {
|
|
2164
|
+
taskId: guardedRead(() => readOwnDataNonEmptyString(source, 'taskId')),
|
|
2165
|
+
sessionId: guardedRead(() => readOwnDataNonEmptyString(source, 'sessionId')),
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
function liveCardIdentityOf(approvalId, carriers, liveRun) {
|
|
1952
2169
|
const body = carriers.body;
|
|
1953
2170
|
const toolCallId = typeof body.toolCallId === 'string' && body.toolCallId !== '' ? body.toolCallId : undefined;
|
|
1954
2171
|
const fromSubagent = isFromSubagent(carriers.body);
|
|
1955
|
-
const
|
|
1956
|
-
const source = bound ?? run;
|
|
1957
|
-
const ownRun = fromSubagent ? subagentSourceSessionOf(carriers.body) : source?.taskId;
|
|
2172
|
+
const ownRun = fromSubagent ? subagentSourceSessionOf(carriers.body) : liveRun.taskId;
|
|
1958
2173
|
const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
|
|
1959
|
-
const sessionId =
|
|
2174
|
+
const sessionId = fromSubagent ? undefined : liveRun.sessionId;
|
|
1960
2175
|
const askSig = carrierAskSig(carriers.body, carriers.live);
|
|
1961
2176
|
return {
|
|
1962
2177
|
approvalId,
|
|
@@ -2012,12 +2227,13 @@ function readApprovalCardClaimSignal(v) {
|
|
|
2012
2227
|
export function claimApprovalCard(identity, options) {
|
|
2013
2228
|
return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
|
|
2014
2229
|
}
|
|
2015
|
-
async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
|
|
2230
|
+
async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs, cardTaskId) {
|
|
2016
2231
|
const bodyToolCallId = carriers.body.toolCallId;
|
|
2017
2232
|
const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
|
|
2018
2233
|
const decidedByReading = decidedByReadingSnapshotOf(lane);
|
|
2019
|
-
const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId);
|
|
2234
|
+
const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId, carriers.why);
|
|
2020
2235
|
const argsNotRealInput = facts.argsUnavailable === true;
|
|
2236
|
+
const editAcceptance = editedApprovalAcceptanceOf(facts);
|
|
2021
2237
|
const ruleOffers = facts.ruleOffers;
|
|
2022
2238
|
if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
|
|
2023
2239
|
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)'}`);
|
|
@@ -2025,6 +2241,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2025
2241
|
const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
|
|
2026
2242
|
...facts,
|
|
2027
2243
|
callKey: liveFrameCallKey(approvalId),
|
|
2244
|
+
...(cardTaskId !== undefined ? { taskId: cardTaskId } : {}),
|
|
2028
2245
|
signal: hold.cardSignal,
|
|
2029
2246
|
...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs, ...(carriers.leg === 'generic' ? { unknownShape: true } : {}) }),
|
|
2030
2247
|
});
|
|
@@ -2035,11 +2252,13 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2035
2252
|
surfaceApprovalParking();
|
|
2036
2253
|
return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
|
|
2037
2254
|
}
|
|
2038
|
-
if (
|
|
2255
|
+
if (editAcceptance.state !== 'accepts' && card.kind === 'allow' && card.updatedInput !== undefined) {
|
|
2039
2256
|
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)`);
|
|
2040
2257
|
surfaceEditRefusedOnBlindAsk();
|
|
2041
2258
|
return { kind: 'not_sent', lane: 'respond', cause: 'edit_refused' };
|
|
2042
2259
|
}
|
|
2260
|
+
if (card.kind === WITHDRAWN_CARD_DECISION_KIND)
|
|
2261
|
+
return withdrawnCardResolution(card, { lane: 'respond' }, 'liveToolApprovalWire');
|
|
2043
2262
|
if (card.kind === RETRACTED_CARD_DECISION_KIND) {
|
|
2044
2263
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
|
|
2045
2264
|
return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
|
|
@@ -2073,8 +2292,10 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2073
2292
|
: card;
|
|
2074
2293
|
const basePayload = answerPayloadFor(answerSnapshot, 'respond', { approvalDecisionNoteCapable: noteCapable });
|
|
2075
2294
|
const decision = basePayload?.decision ?? 'deny';
|
|
2076
|
-
|
|
2077
|
-
|
|
2295
|
+
const allowNoteText = decision !== 'deny' ? allowNoteReadingOf(answerSnapshot) : { kind: 'absent' };
|
|
2296
|
+
const allowNoteDropped = allowNoteText.kind === 'text' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none';
|
|
2297
|
+
if (allowNoteDropped && allowNoteText.kind === 'text') {
|
|
2298
|
+
hostLog('debug', `liveToolApprovalWire: ${approvalId} card carried a note on Yes (len=${allowNoteText.text.length}) — this approval door has no field that brings it to the model, so it was not sent`);
|
|
2078
2299
|
}
|
|
2079
2300
|
const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
|
|
2080
2301
|
const denyPolicyCauseForWire = card.kind === 'deny' ? readPolicyDenyCause(card) : undefined;
|
|
@@ -2228,15 +2449,15 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2228
2449
|
'reporting the answer as NOT settled (never as decided, never as parked); not re-sent');
|
|
2229
2450
|
return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
|
|
2230
2451
|
}
|
|
2231
|
-
const parsed = readToolApprovalRespondAck(raw);
|
|
2452
|
+
const parsed = settlementAckOf(readToolApprovalRespondAck(raw));
|
|
2232
2453
|
const replayedForThisCard = parsed !== undefined && isToolApprovalRespondReplayed(parsed) && parsed.approvalId === approvalId;
|
|
2233
|
-
const claimsReplay = ownIdempotentTrue(raw) || (parsed !== undefined
|
|
2454
|
+
const claimsReplay = ownIdempotentTrue(raw) || replayedFirstDecisionOf(parsed) !== undefined;
|
|
2234
2455
|
if (claimsReplay && !replayedForThisCard) {
|
|
2235
2456
|
const claimed = replayClaimOf(raw);
|
|
2236
2457
|
if (claimed.approvalId === approvalId && claimed.decision !== undefined && !claimed.carriesDelivery) {
|
|
2237
2458
|
hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay (body not in the full replay shape) — the approval was already decided ` +
|
|
2238
2459
|
`(${claimed.decision}); this answer was not delivered, reporting the earlier decision`);
|
|
2239
|
-
return
|
|
2460
|
+
return replayedDecisionOf(claimed.decision, decision, parsed !== undefined && isToolApprovalRespondParkedRedeemed(parsed) && parsed.approvalId === approvalId ? parsed : undefined);
|
|
2240
2461
|
}
|
|
2241
2462
|
hostLog('error', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay of an earlier decision that cannot be read or names another approval — ` +
|
|
2242
2463
|
'reporting the answer as NOT settled (never as the decision this client sent); not re-sent');
|
|
@@ -2306,6 +2527,8 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2306
2527
|
}
|
|
2307
2528
|
if (replayed === undefined && sessionDowngraded)
|
|
2308
2529
|
surfaceRememberNotApplied();
|
|
2530
|
+
if (replayed === undefined && allowNoteDropped)
|
|
2531
|
+
surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
|
|
2309
2532
|
if (respondOpts.updatedInput !== undefined && applied?.updatedInputForwarded === false) {
|
|
2310
2533
|
logAfterDecided(`liveToolApprovalWire: ${approvalId} edited args were NOT forwarded (ack.updatedInputForwarded=false) — the tool runs on the ORIGINAL input`);
|
|
2311
2534
|
surfaceEditNotForwarded();
|
|
@@ -2317,7 +2540,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2317
2540
|
if (replayed !== undefined) {
|
|
2318
2541
|
hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay — the approval was already decided (${replayed.decision}); ` +
|
|
2319
2542
|
'this answer was not delivered, reporting the earlier decision');
|
|
2320
|
-
return
|
|
2543
|
+
return replayedDecisionOf(replayed.decision, decision, replayed);
|
|
2321
2544
|
}
|
|
2322
2545
|
const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire) : undefined;
|
|
2323
2546
|
return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };
|