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