@sema-agent/client-core 0.94.0 → 0.95.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 +341 -0
- package/README.md +70 -47
- package/dist/adapt/arms.js +5 -6
- package/dist/adapt/panelTasks.js +4 -10
- package/dist/adapt/toolCards.js +2 -2
- package/dist/adapt/wireShapes.js +3 -8
- package/dist/adapter/activeRunSelfHeal.d.ts +8 -3
- package/dist/adapter/activeRunSelfHeal.js +20 -12
- package/dist/adapter/downstream/eventToSdkMessage.js +24 -17
- package/dist/adapter/downstream/terminalToSdkResult.js +11 -3
- package/dist/adapter/runStream.js +2 -1
- package/dist/adapter/types.d.ts +7 -0
- package/dist/adapter/types.js +15 -0
- package/dist/agentSession/backgroundView.js +5 -1
- package/dist/agentSession/engineAgentRegistryRead.js +2 -1
- package/dist/agentSession/engineSessionBackgroundStop.js +3 -2
- package/dist/agentsWireCaps.d.ts +2 -0
- package/dist/approvalDecidedByCapability.d.ts +0 -1
- package/dist/approvalDecidedByCapability.js +2 -7
- package/dist/approvalsStreamLiveCapability.d.ts +0 -1
- package/dist/approvalsStreamLiveCapability.js +2 -6
- package/dist/atMostOnce.d.ts +8 -0
- package/dist/atMostOnce.js +36 -0
- package/dist/attachmentSeatCapability.d.ts +0 -1
- package/dist/attachmentSeatCapability.js +0 -3
- package/dist/attachmentsWireCaps.js +11 -6
- package/dist/bashExitReading.d.ts +10 -0
- package/dist/bashExitReading.js +12 -0
- package/dist/callerCwdCapability.d.ts +0 -1
- package/dist/callerCwdCapability.js +2 -7
- package/dist/centerWiringCapability.d.ts +1 -3
- package/dist/centerWiringCapability.js +18 -21
- package/dist/classifierStatus.js +4 -4
- package/dist/cloudConfigWireCaps.js +43 -21
- package/dist/compensations.js +1 -1
- package/dist/cycleOrder.d.ts +15 -0
- package/dist/cycleOrder.js +45 -0
- package/dist/decideFailureNote.js +4 -1
- package/dist/decideReceipt.d.ts +2 -1
- package/dist/decideReceipt.js +41 -13
- package/dist/declared/supportFloorDeclared.d.ts +1 -0
- package/dist/declared/supportFloorDeclared.js +5 -0
- package/dist/deviceExecutorManagementCapability.d.ts +0 -1
- package/dist/deviceExecutorManagementCapability.js +7 -16
- package/dist/deviceRebind.d.ts +88 -0
- package/dist/deviceRebind.js +242 -0
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +313 -11
- package/dist/engineAgentAbsence.d.ts +2 -2
- package/dist/engineAgentPanelStore.d.ts +8 -18
- package/dist/engineAgentPanelStore.js +19 -537
- package/dist/engineCapReader.d.ts +6 -6
- package/dist/engineCapReader.js +30 -43
- package/dist/engineCapsCache.d.ts +7 -2
- package/dist/engineCapsCache.js +64 -26
- package/dist/engineCredentialRequirement.d.ts +9 -0
- package/dist/engineCredentialRequirement.js +23 -0
- package/dist/engineEnvSourcing.d.ts +53 -0
- package/dist/engineEnvSourcing.js +81 -0
- package/dist/engineErrorCodes.d.ts +14 -3
- package/dist/engineErrorCodes.js +31 -10
- package/dist/engineIdentity.js +2 -0
- package/dist/engineWireSdk.js +2 -2
- package/dist/executionLaneCapability.d.ts +2 -3
- package/dist/executionLaneCapability.js +18 -23
- package/dist/fileHistoryCaptureCapability.d.ts +0 -1
- package/dist/fileHistoryCaptureCapability.js +2 -6
- package/dist/fileHistoryLegNotice.d.ts +3 -2
- package/dist/fileHistoryLegNotice.js +51 -2
- package/dist/fleet/fleetLedger.d.ts +2 -0
- package/dist/fleet/fleetLedger.js +69 -52
- package/dist/fleet/fleetProjection.d.ts +0 -3
- package/dist/fleet/fleetProjection.js +9 -16
- package/dist/fleetAgentPanelProjection.d.ts +4 -17
- package/dist/fleetAgentPanelProjection.js +7 -197
- package/dist/fleetAgentRowLike.d.ts +16 -0
- package/dist/fleetAgentRowLike.js +1 -0
- package/dist/gateOutcome.js +30 -18
- package/dist/generated/sdkFactTables.d.ts +2 -0
- package/dist/generated/sdkFactTables.js +17 -0
- package/dist/generated/settingsSchemaFns.d.ts +10 -0
- package/dist/generated/settingsSchemaFns.js +22 -0
- package/dist/headlessPermissionModeWire.d.ts +1 -1
- package/dist/headlessPermissionModeWire.js +1 -1
- package/dist/headlessReconnectWire.js +49 -20
- package/dist/hitl/allowRuleShadow.d.ts +1 -1
- package/dist/hitl/allowRuleShadow.js +49 -8
- package/dist/hitl/approvalCompleteParked.d.ts +1 -0
- package/dist/hitl/approvalCompleteParked.js +14 -0
- package/dist/hitl/approvalEndingCause.d.ts +5 -0
- package/dist/hitl/approvalEndingCause.js +61 -0
- package/dist/hitl/approvalHolds.d.ts +12 -2
- package/dist/hitl/approvalHolds.js +53 -2
- package/dist/hitl/approvalsFeed.js +23 -11
- package/dist/hitl/askGateWire.d.ts +2 -1
- package/dist/hitl/askGateWire.js +26 -1
- package/dist/hitl/automaticAnswerVerdict.js +2 -2
- package/dist/hitl/crashConverged.js +2 -0
- package/dist/hitl/decisionConflict.d.ts +3 -0
- package/dist/hitl/decisionConflict.js +43 -0
- package/dist/hitl/decisionRefusal.d.ts +4 -0
- package/dist/hitl/decisionRefusal.js +26 -1
- package/dist/hitl/frameRouter.js +26 -43
- package/dist/hitl/gateLedger.d.ts +5 -0
- package/dist/hitl/gateLedger.js +21 -0
- package/dist/hitl/hitlBridge.d.ts +12 -1
- package/dist/hitl/hitlBridge.js +68 -53
- package/dist/hitl/hitlHostSurface.js +3 -11
- package/dist/hitl/livePendingAsk.d.ts +16 -6
- package/dist/hitl/livePendingAsk.js +36 -4
- package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
- package/dist/hitl/parkExpiryJudgements.js +46 -0
- package/dist/hitl/parkResolver.d.ts +3 -7
- package/dist/hitl/parkResolver.js +50 -26
- package/dist/hitl/persistedRulesWire.js +5 -1
- package/dist/hitl/planReviewWire.d.ts +2 -2
- package/dist/hitl/planReviewWire.js +56 -30
- package/dist/hitl/replayedApproval.d.ts +8 -0
- package/dist/hitl/replayedApproval.js +125 -10
- package/dist/hitl/resumeRunningCard.d.ts +2 -0
- package/dist/hitl/resumeRunningCard.js +15 -4
- package/dist/hitl/toolApprovalWire.d.ts +24 -4
- package/dist/hitl/toolApprovalWire.js +115 -38
- package/dist/hooksWireCaps.js +38 -6
- package/dist/host.d.ts +5 -0
- package/dist/host.js +3 -1
- package/dist/hostCallbackGuard.d.ts +1 -0
- package/dist/hostCallbackGuard.js +25 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +20 -8
- package/dist/interactiveInterrupt.d.ts +8 -0
- package/dist/interactiveInterrupt.js +54 -0
- package/dist/limitsWire.d.ts +5 -0
- package/dist/limitsWire.js +49 -3
- package/dist/liveModelCatalog.d.ts +95 -0
- package/dist/liveModelCatalog.js +297 -32
- package/dist/mcpEngineLeg.d.ts +25 -0
- package/dist/mcpEngineLeg.js +71 -15
- package/dist/mcpPanel.js +2 -1
- package/dist/mcpProbeCapability.d.ts +0 -1
- package/dist/mcpProbeCapability.js +2 -6
- package/dist/mcpReconnect.d.ts +1 -3
- package/dist/mcpReconnect.js +10 -5
- package/dist/memoryComplianceCapability.d.ts +0 -1
- package/dist/memoryComplianceCapability.js +2 -6
- package/dist/memoryOriginCapability.d.ts +0 -1
- package/dist/memoryOriginCapability.js +2 -6
- package/dist/memoryVerbsWire.d.ts +1 -1
- package/dist/memoryVerbsWire.js +4 -0
- package/dist/model/liveCatalogShape.d.ts +14 -0
- package/dist/model/liveCatalogShape.js +71 -0
- package/dist/model/modelSupplyRules.d.ts +1 -0
- package/dist/model/modelSupplyRules.js +30 -9
- package/dist/model/modelsPolicy.d.ts +21 -0
- package/dist/model/modelsPolicy.js +90 -0
- package/dist/modelIdentityResolvability.d.ts +1 -1
- package/dist/modelIdentityResolvability.js +5 -12
- package/dist/modelSwitchFallback.d.ts +69 -0
- package/dist/modelSwitchFallback.js +206 -0
- package/dist/notifications.js +12 -11
- package/dist/outputStyleSeatCapability.d.ts +0 -1
- package/dist/outputStyleSeatCapability.js +2 -7
- package/dist/ownKey.d.ts +6 -0
- package/dist/ownKey.js +48 -0
- package/dist/panelReduce.d.ts +114 -0
- package/dist/panelReduce.js +1135 -0
- package/dist/panelStateHolder.d.ts +17 -0
- package/dist/panelStateHolder.js +201 -0
- package/dist/peerLaneCapability.d.ts +0 -1
- package/dist/peerLaneCapability.js +2 -6
- package/dist/permissionModesCapability.d.ts +0 -1
- package/dist/permissionModesCapability.js +5 -10
- package/dist/permissionRulesWriteCapability.d.ts +0 -1
- package/dist/permissionRulesWriteCapability.js +2 -6
- package/dist/permissionsAllowGrantCapability.d.ts +0 -1
- package/dist/permissionsAllowGrantCapability.js +3 -9
- package/dist/permissionsDeclaration.d.ts +6 -0
- package/dist/permissionsDeclaration.js +45 -0
- package/dist/printToolResultFrame.js +2 -10
- package/dist/promptDelivery.js +0 -8
- package/dist/resumeRefusalCopy.d.ts +18 -1
- package/dist/resumeRefusalCopy.js +126 -15
- package/dist/rewindArchiveCapability.d.ts +2 -1
- package/dist/rewindArchiveCapability.js +15 -7
- package/dist/rewindEngineAction.d.ts +21 -0
- package/dist/rewindEngineAction.js +36 -0
- package/dist/rewindWireCaps.d.ts +2 -1
- package/dist/rewindWireCaps.js +1 -0
- package/dist/runTerminal.d.ts +0 -11
- package/dist/runTerminal.js +4 -27
- package/dist/sdkRegistryTransit.d.ts +2 -1
- package/dist/sdkRegistryTransit.js +1 -0
- package/dist/sealedKeyCapability.d.ts +0 -1
- package/dist/sealedKeyCapability.js +3 -9
- package/dist/seatContract.d.ts +54 -25
- package/dist/seatContract.js +394 -171
- package/dist/sessionIdSegment.d.ts +1 -0
- package/dist/sessionIdSegment.js +11 -0
- package/dist/sessionMemoryErase.d.ts +1 -1
- package/dist/sessionMemoryErase.js +6 -1
- package/dist/sessionMemoryStatus.js +3 -2
- package/dist/sessionPolicyCapability.d.ts +0 -1
- package/dist/sessionPolicyCapability.js +2 -6
- package/dist/sessionReadRedactionCapability.d.ts +0 -1
- package/dist/sessionReadRedactionCapability.js +2 -7
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
- package/dist/sessionSyncFileHistoryCapability.js +2 -6
- package/dist/skillToolListsCapability.d.ts +0 -1
- package/dist/skillToolListsCapability.js +2 -6
- package/dist/skillsWireCaps.d.ts +4 -0
- package/dist/skillsWireCaps.js +66 -1
- package/dist/sqlEngineCapability.d.ts +0 -1
- package/dist/sqlEngineCapability.js +9 -14
- package/dist/steerPriorityCapability.d.ts +0 -1
- package/dist/steerPriorityCapability.js +3 -9
- package/dist/storePostureCapability.d.ts +1 -1
- package/dist/storePostureCapability.js +16 -16
- package/dist/streamDrainingLegs.d.ts +6 -0
- package/dist/streamDrainingLegs.js +23 -0
- package/dist/subagent/engineCompactWire.js +13 -38
- package/dist/subagent/engineDelegatedPrompt.js +10 -6
- package/dist/subagent/engineSubagentOutput.d.ts +0 -1
- package/dist/subagent/engineSubagentOutput.js +5 -20
- package/dist/subagent/engineSubagentTail.d.ts +0 -1
- package/dist/subagent/engineSubagentTail.js +7 -32
- package/dist/subagentContentStore.js +3 -3
- package/dist/supportFloor.js +2 -5
- package/dist/terminalStatuses.d.ts +30 -0
- package/dist/terminalStatuses.js +67 -0
- package/dist/toolResult.d.ts +5 -0
- package/dist/toolResult.js +88 -36
- package/dist/types/engineState.d.ts +2 -1
- package/dist/usageAnalytics.d.ts +146 -0
- package/dist/usageAnalytics.js +311 -0
- package/dist/userInvokedSkillsCapability.d.ts +0 -1
- package/dist/userInvokedSkillsCapability.js +2 -6
- package/dist/webSearchBackendCapability.d.ts +0 -1
- package/dist/webSearchBackendCapability.js +3 -7
- package/dist/webSearchWireCaps.js +3 -11
- package/dist/wireFailureShape.d.ts +4 -0
- package/dist/wireFailureShape.js +42 -2
- package/dist/wireRefusalCopy.js +2 -18
- package/dist/workflowClient.js +7 -11
- package/dist/workflowsJournalCapability.d.ts +0 -1
- package/dist/workflowsJournalCapability.js +2 -6
- package/dist/writeProtectionCapability.d.ts +0 -1
- package/dist/writeProtectionCapability.js +5 -10
- package/docs/INTEGRATION-CLIENTS.md +1033 -26
- package/package.json +5 -5
- package/dist/engineCapsGenerationGuard.d.ts +0 -4
- package/dist/engineCapsGenerationGuard.js +0 -19
- package/dist/panelRunningHistory.d.ts +0 -22
- package/dist/panelRunningHistory.js +0 -82
- package/dist/registryConflict.d.ts +0 -3
- package/dist/registryConflict.js +0 -44
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function ownData(o, key) {
|
|
2
|
+
const d = Object.getOwnPropertyDescriptor(o, key);
|
|
3
|
+
return d !== undefined && 'value' in d ? d.value : undefined;
|
|
4
|
+
}
|
|
5
|
+
export function approvalCompleteParked(frame) {
|
|
6
|
+
try {
|
|
7
|
+
if (frame === null || typeof frame !== 'object')
|
|
8
|
+
return false;
|
|
9
|
+
return ownData(frame, 'outcome') === 'expired' && ownData(frame, 'parked') === true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ApprovalSettleCause, AskRowEndingCause } from '@sema-agent/sdk';
|
|
2
|
+
export declare const ASK_ROW_ENDING_CAUSE_WORDS: readonly ["routing_failure_fail_closed", "run_cancelled", "run_exited", "batch_aborted", "batch_sibling_withdrawn", "batch_bound_elsewhere", "adhoc_leg_no_durable_domain", "orphan_ttl_exceeded", "crashed_before_park", "shutdown_before_park"];
|
|
3
|
+
export declare function askRowEndingCauseFor(state: 'VOID' | 'DENIED' | undefined, word: unknown): AskRowEndingCause | undefined;
|
|
4
|
+
export declare function approvalCompleteCauseOf(frame: unknown): ApprovalSettleCause | undefined;
|
|
5
|
+
export declare function notPendingCauseSaysEnded(err: object): boolean;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ASK_GONE_DENIED_WORD } from '../engineErrorCodes.js';
|
|
2
|
+
import { SDK_ASK_ROW_ENDING_CAUSES } from '../generated/sdkFactTables.js';
|
|
3
|
+
export const ASK_ROW_ENDING_CAUSE_WORDS = SDK_ASK_ROW_ENDING_CAUSES;
|
|
4
|
+
const _askRowCausePin = true;
|
|
5
|
+
void _askRowCausePin;
|
|
6
|
+
const DENIED_GROUP_WORD = ASK_GONE_DENIED_WORD;
|
|
7
|
+
const WAIT_CLASS_SETTLE_CAUSE_WORDS = Object.freeze({
|
|
8
|
+
ttl_expired: true,
|
|
9
|
+
broadcast_lost: true,
|
|
10
|
+
stream_disconnected: true,
|
|
11
|
+
undeliverable: true,
|
|
12
|
+
});
|
|
13
|
+
function isAskRowEndingCause(v) {
|
|
14
|
+
return typeof v === 'string' && ASK_ROW_ENDING_CAUSE_WORDS.includes(v);
|
|
15
|
+
}
|
|
16
|
+
function isApprovalSettleCause(v) {
|
|
17
|
+
return isAskRowEndingCause(v) || (typeof v === 'string' && Object.hasOwn(WAIT_CLASS_SETTLE_CAUSE_WORDS, v));
|
|
18
|
+
}
|
|
19
|
+
export function askRowEndingCauseFor(state, word) {
|
|
20
|
+
if (!isAskRowEndingCause(word))
|
|
21
|
+
return undefined;
|
|
22
|
+
if (state === 'DENIED')
|
|
23
|
+
return word === DENIED_GROUP_WORD ? word : undefined;
|
|
24
|
+
if (state === 'VOID')
|
|
25
|
+
return word !== DENIED_GROUP_WORD ? word : undefined;
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
export function approvalCompleteCauseOf(frame) {
|
|
29
|
+
try {
|
|
30
|
+
if (typeof frame !== 'object' || frame === null)
|
|
31
|
+
return undefined;
|
|
32
|
+
const typeSlot = Object.getOwnPropertyDescriptor(frame, 'type');
|
|
33
|
+
if (typeSlot === undefined || !Object.hasOwn(typeSlot, 'value') || typeSlot.value !== 'tool_approval_complete')
|
|
34
|
+
return undefined;
|
|
35
|
+
const causeSlot = Object.getOwnPropertyDescriptor(frame, 'cause');
|
|
36
|
+
if (causeSlot === undefined || !Object.hasOwn(causeSlot, 'value'))
|
|
37
|
+
return undefined;
|
|
38
|
+
const word = causeSlot.value;
|
|
39
|
+
return isApprovalSettleCause(word) ? word : undefined;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const LEGACY_NOT_PENDING_ENDED_WORDS = new Set(['voided', 'denied']);
|
|
46
|
+
export function notPendingCauseSaysEnded(err) {
|
|
47
|
+
try {
|
|
48
|
+
const extraSlot = Object.getOwnPropertyDescriptor(err, 'extra');
|
|
49
|
+
const extra = extraSlot !== undefined && Object.hasOwn(extraSlot, 'value') ? extraSlot.value : undefined;
|
|
50
|
+
if (typeof extra !== 'object' || extra === null || Array.isArray(extra))
|
|
51
|
+
return false;
|
|
52
|
+
const causeSlot = Object.getOwnPropertyDescriptor(extra, 'cause');
|
|
53
|
+
if (causeSlot === undefined || !Object.hasOwn(causeSlot, 'value'))
|
|
54
|
+
return false;
|
|
55
|
+
const word = causeSlot.value;
|
|
56
|
+
return isAskRowEndingCause(word) || (typeof word === 'string' && LEGACY_NOT_PENDING_ENDED_WORDS.has(word));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FsApprovalOutcome, ToolApprovalFrameOutcome } from './toolApprovalWire.js';
|
|
1
|
+
import type { ApprovalHolderIdentity, FsApprovalOutcome, ToolApprovalFrameOutcome } from './toolApprovalWire.js';
|
|
2
2
|
export interface DurableDecisionHold {
|
|
3
3
|
readonly lane: 'durable';
|
|
4
4
|
readonly outcome: Promise<FsApprovalOutcome>;
|
|
@@ -9,6 +9,7 @@ export interface DurableDecisionHold {
|
|
|
9
9
|
}
|
|
10
10
|
export interface LiveCardHold {
|
|
11
11
|
readonly lane: 'live';
|
|
12
|
+
readonly identity: LiveCardIdentity;
|
|
12
13
|
readonly outcome: Promise<ToolApprovalFrameOutcome>;
|
|
13
14
|
phase: 'card' | 'respond';
|
|
14
15
|
superseded?: {
|
|
@@ -19,6 +20,15 @@ export interface LiveCardHold {
|
|
|
19
20
|
trailing?: Promise<void>;
|
|
20
21
|
}
|
|
21
22
|
export type ApprovalHold = DurableDecisionHold | LiveCardHold;
|
|
23
|
+
export interface LiveCardIdentity {
|
|
24
|
+
readonly approvalId: string;
|
|
25
|
+
readonly toolCallId?: string;
|
|
26
|
+
readonly taskId?: string;
|
|
27
|
+
readonly sessionId?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function liveCardHoldFor(who: ApprovalHolderIdentity): LiveCardHold | undefined;
|
|
30
|
+
export declare function liveCardAnsweredHow(o: ToolApprovalFrameOutcome): 'answered' | 'interrupted' | undefined;
|
|
31
|
+
export declare function awaitLiveCardHolder(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ToolApprovalFrameOutcome | undefined>;
|
|
22
32
|
export interface ApprovalDecisionHoldTable {
|
|
23
33
|
readonly inFlight: Map<string, ApprovalHold>;
|
|
24
34
|
readonly settled: Map<string, DurableSettledHold>;
|
|
@@ -31,6 +41,6 @@ export interface DurableSettledHold {
|
|
|
31
41
|
}
|
|
32
42
|
export declare const approvalDecisionHolds: ApprovalDecisionHoldTable;
|
|
33
43
|
export declare function liveCardHoldOf(key: string): LiveCardHold | undefined;
|
|
34
|
-
export declare function holdLiveCard(
|
|
44
|
+
export declare function holdLiveCard(identity: LiveCardIdentity, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<ToolApprovalFrameOutcome>): Promise<ToolApprovalFrameOutcome>;
|
|
35
45
|
export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ToolApprovalFrameOutcome>;
|
|
36
46
|
export declare function supersedeLiveCard(key: string, parked: boolean): boolean;
|
|
@@ -1,10 +1,61 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
|
+
import { liveFrameCallKey } from './gateIdentity.js';
|
|
3
|
+
import { approvalResolutionOf } from './approvalResolution.js';
|
|
4
|
+
const nonEmpty = (v) => typeof v === 'string' && v !== '';
|
|
5
|
+
export function liveCardHoldFor(who) {
|
|
6
|
+
if (nonEmpty(who.approvalId))
|
|
7
|
+
return liveCardHoldOf(liveFrameCallKey(who.approvalId));
|
|
8
|
+
if (!nonEmpty(who.taskId) || !nonEmpty(who.toolCallId))
|
|
9
|
+
return undefined;
|
|
10
|
+
for (const h of approvalDecisionHolds.inFlight.values()) {
|
|
11
|
+
if (h.lane !== 'live' || h.phase !== 'card')
|
|
12
|
+
continue;
|
|
13
|
+
const id = h.identity;
|
|
14
|
+
if (id.taskId !== who.taskId || id.toolCallId !== who.toolCallId)
|
|
15
|
+
continue;
|
|
16
|
+
if (nonEmpty(who.sessionId) && nonEmpty(id.sessionId) && id.sessionId !== who.sessionId)
|
|
17
|
+
continue;
|
|
18
|
+
return h;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
function liveCardSettledQuestion(hold, o) {
|
|
23
|
+
const r = approvalResolutionOf(o);
|
|
24
|
+
if (answeredHowOf(r) !== undefined)
|
|
25
|
+
return true;
|
|
26
|
+
if (r.kind === 'unsettled')
|
|
27
|
+
return false;
|
|
28
|
+
return hold.superseded !== undefined && !hold.superseded.parked;
|
|
29
|
+
}
|
|
30
|
+
export function liveCardAnsweredHow(o) {
|
|
31
|
+
return answeredHowOf(approvalResolutionOf(o));
|
|
32
|
+
}
|
|
33
|
+
function answeredHowOf(r) {
|
|
34
|
+
if (r.kind === 'decided')
|
|
35
|
+
return 'answered';
|
|
36
|
+
return r.kind === 'unsettled' && r.cause === 'interrupted' ? 'interrupted' : undefined;
|
|
37
|
+
}
|
|
38
|
+
export async function awaitLiveCardHolder(hold, signal) {
|
|
39
|
+
const unsubscribe = followCallerSignal(signal, hold.interrupt);
|
|
40
|
+
let o;
|
|
41
|
+
try {
|
|
42
|
+
o = await hold.outcome;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
unsubscribe();
|
|
49
|
+
}
|
|
50
|
+
return liveCardSettledQuestion(hold, o) ? { ...o, joinedInFlight: true } : undefined;
|
|
51
|
+
}
|
|
2
52
|
export const approvalDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), clock: 0 };
|
|
3
53
|
export function liveCardHoldOf(key) {
|
|
4
54
|
const h = approvalDecisionHolds.inFlight.get(key);
|
|
5
55
|
return h !== undefined && h.lane === 'live' ? h : undefined;
|
|
6
56
|
}
|
|
7
|
-
export function holdLiveCard(
|
|
57
|
+
export function holdLiveCard(identity, callerSignal, present) {
|
|
58
|
+
const key = liveFrameCallKey(identity.approvalId);
|
|
8
59
|
const h = approvalDecisionHolds;
|
|
9
60
|
const cut = new AbortController();
|
|
10
61
|
let settled = false;
|
|
@@ -19,7 +70,7 @@ export function holdLiveCard(key, callerSignal, present) {
|
|
|
19
70
|
resolveOutcome = resolve;
|
|
20
71
|
rejectOutcome = reject;
|
|
21
72
|
});
|
|
22
|
-
const hold = { lane: 'live', outcome, phase: 'card', cardSignal: cut.signal, interrupt };
|
|
73
|
+
const hold = { lane: 'live', identity: Object.freeze({ ...identity }), outcome, phase: 'card', cardSignal: cut.signal, interrupt };
|
|
23
74
|
h.inFlight.set(key, hold);
|
|
24
75
|
const release = () => {
|
|
25
76
|
if (h.inFlight.get(key) === hold)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
2
|
import { unrefTimer } from '../unrefTimer.js';
|
|
3
3
|
import { readLivePendingRows } from './livePendingAsk.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
|
|
5
5
|
const MAX_CONSECUTIVE_POLL_ESCAPES = 5;
|
|
6
6
|
function readDurablePendingRows(raw) {
|
|
7
7
|
if (!Array.isArray(raw))
|
|
@@ -365,7 +365,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
365
365
|
.then(() => scheduleReconcile());
|
|
366
366
|
}, reconcileDelayMs));
|
|
367
367
|
}
|
|
368
|
-
function scheduleStreamRetry() {
|
|
368
|
+
function scheduleStreamRetry(afterMs = streamRetryMs) {
|
|
369
369
|
if (stopped || ac.signal.aborted || streamRetryMs <= 0)
|
|
370
370
|
return;
|
|
371
371
|
if (!client.approvals.stream)
|
|
@@ -378,9 +378,9 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
378
378
|
return;
|
|
379
379
|
hostLog('debug', 'approvalsFeed: retrying push leg (降级是暂时的,服务端升级后不该还要重启客户端)');
|
|
380
380
|
void runStream();
|
|
381
|
-
},
|
|
381
|
+
}, afterMs));
|
|
382
382
|
}
|
|
383
|
-
function degradeToPoll(why, real = true) {
|
|
383
|
+
function degradeToPoll(why, real = true, retryAfterMs) {
|
|
384
384
|
if (stopped)
|
|
385
385
|
return;
|
|
386
386
|
if (real)
|
|
@@ -390,8 +390,9 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
390
390
|
}
|
|
391
391
|
mode = 'poll';
|
|
392
392
|
schedulePoll();
|
|
393
|
-
scheduleStreamRetry();
|
|
393
|
+
scheduleStreamRetry(retryAfterMs);
|
|
394
394
|
}
|
|
395
|
+
const drains = createStreamDrainingLegs();
|
|
395
396
|
async function runStream() {
|
|
396
397
|
const stream = client.approvals.stream;
|
|
397
398
|
if (!stream) {
|
|
@@ -402,6 +403,10 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
402
403
|
for await (const ev of stream.call(client.approvals, { signal: ac.signal })) {
|
|
403
404
|
if (stopped || ac.signal.aborted)
|
|
404
405
|
return;
|
|
406
|
+
if (drains.see(ev)) {
|
|
407
|
+
hostLog('debug', 'approvalsFeed: push leg received the drain end frame — the engine is going down; this leg ends with it and reopens on the draining ladder');
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
405
410
|
if (mode !== 'push') {
|
|
406
411
|
mode = 'push';
|
|
407
412
|
if (pollTimer !== undefined) {
|
|
@@ -416,17 +421,24 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
416
421
|
stats.heartbeats++;
|
|
417
422
|
continue;
|
|
418
423
|
}
|
|
419
|
-
if (isInStreamDrainingEnd(ev)) {
|
|
420
|
-
hostLog('debug', 'approvalsFeed: push leg received the drain end frame — the engine is going down; the stream reconnects and re-syncs on its own');
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
424
|
stats.pushEvents++;
|
|
424
425
|
await take('push');
|
|
425
426
|
}
|
|
426
|
-
if (
|
|
427
|
-
|
|
427
|
+
if (stopped || ac.signal.aborted)
|
|
428
|
+
return;
|
|
429
|
+
const drained = drains.legEnded('returned');
|
|
430
|
+
if (drained !== undefined) {
|
|
431
|
+
const decision = drained;
|
|
432
|
+
if ('reconnectAfterMs' in decision)
|
|
433
|
+
degradeToPoll(`engine draining — push leg closed; reopening in ${decision.reconnectAfterMs}ms`, false, decision.reconnectAfterMs);
|
|
434
|
+
else
|
|
435
|
+
degradeToPoll(`engine draining — reconnect attempts exhausted (${decision.giveUp})`);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
degradeToPoll('stream ended without progress');
|
|
428
439
|
}
|
|
429
440
|
catch (e) {
|
|
441
|
+
drains.legEnded('threw');
|
|
430
442
|
if (stopped || ac.signal.aborted)
|
|
431
443
|
return;
|
|
432
444
|
degradeToPoll(String(e));
|
|
@@ -8,9 +8,10 @@ export type { AskAnsweredOutput } from './gateLedger.js';
|
|
|
8
8
|
export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
|
|
9
9
|
export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
|
|
10
10
|
export type { GateDenyAttribution } from './gateLedger.js';
|
|
11
|
-
export { toAnsweredOutput, isAlreadyResolvedFailure, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure,
|
|
11
|
+
export { toAnsweredOutput, isAlreadyResolvedFailure, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
|
|
12
12
|
export type { HopBudget, HopRound } from './parkResolver.js';
|
|
13
13
|
export type { GateOutcome, GateFailureCode } from './parkResolver.js';
|
|
14
|
+
export { GATE_FAILURE_CODES } from './hitlBridge.js';
|
|
14
15
|
export declare function bridgeAskUserQuestionGates(source: AsyncGenerator<AgentEvent>, deps: AskGateWireDeps, opts?: {
|
|
15
16
|
signal?: AbortSignal;
|
|
16
17
|
taskId?: string;
|
package/dist/hitl/askGateWire.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createGateLedger } from './gateLedger.js';
|
|
2
2
|
import { flushHeldWithInterruptRewrite, isHostProgressFrame, routeFrame, } from './frameRouter.js';
|
|
3
3
|
import { resolvePark, nextHopBudget, stalledTerminal, MAX_GATE_HOPS } from './parkResolver.js';
|
|
4
|
+
import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
|
|
5
|
+
import { abortableSleep } from '../abortableSleep.js';
|
|
6
|
+
import { hostLog } from '../host.js';
|
|
4
7
|
export { HITL_REJECT_MESSAGE, ENGINE_ABORT_TOOL_RESULT } from './frameRouter.js';
|
|
5
8
|
export { HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE } from './frameRouter.js';
|
|
6
9
|
export { HITL_POLICY_DENY_MESSAGE } from './frameRouter.js';
|
|
7
10
|
export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
|
|
8
11
|
export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
|
|
9
|
-
export { toAnsweredOutput, isAlreadyResolvedFailure, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure,
|
|
12
|
+
export { toAnsweredOutput, isAlreadyResolvedFailure, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
|
|
13
|
+
export { GATE_FAILURE_CODES } from './hitlBridge.js';
|
|
10
14
|
export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
11
15
|
const led = createGateLedger();
|
|
12
16
|
const taskId = { current: opts?.taskId };
|
|
@@ -24,9 +28,12 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
24
28
|
let lastResolvedGatedCallId;
|
|
25
29
|
let lastRoundDecided = false;
|
|
26
30
|
let unverifiableParks = 0;
|
|
31
|
+
const drains = createStreamDrainingLegs();
|
|
27
32
|
while (true) {
|
|
28
33
|
let park = null;
|
|
29
34
|
for await (const ev of stream) {
|
|
35
|
+
if (drains.see(ev))
|
|
36
|
+
continue;
|
|
30
37
|
const answering = led.answeringLiveCards();
|
|
31
38
|
if (answering.length > 0)
|
|
32
39
|
await Promise.all(answering);
|
|
@@ -57,6 +64,24 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
57
64
|
break;
|
|
58
65
|
}
|
|
59
66
|
if (!park) {
|
|
67
|
+
const drained = drains.legEnded('returned');
|
|
68
|
+
if (drained !== undefined) {
|
|
69
|
+
const reopenAfterMs = 'reconnectAfterMs' in drained ? drained.reconnectAfterMs : undefined;
|
|
70
|
+
if (reopenAfterMs !== undefined && taskId.current !== undefined && !opts?.signal?.aborted) {
|
|
71
|
+
hostLog('debug', `liveHitlAskWire: runs.events(${taskId.current}) ended on the drain end frame — the engine is going down; reopening in ${reopenAfterMs}ms`);
|
|
72
|
+
await abortableSleep(reopenAfterMs, opts?.signal ?? new AbortController().signal);
|
|
73
|
+
if (!opts?.signal?.aborted) {
|
|
74
|
+
const seq = led.lastSeq();
|
|
75
|
+
led.beginStreamSegment();
|
|
76
|
+
stream = deps.runsEvents(taskId.current, {
|
|
77
|
+
...(seq !== undefined ? { lastEventId: seq } : {}),
|
|
78
|
+
...(opts?.signal ? { signal: opts.signal } : {}),
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
hostLog('debug', `liveHitlAskWire: runs.events ended on the drain end frame and is not reopened (${'giveUp' in drained ? drained.giveUp : taskId.current === undefined ? 'no run handle' : 'aborted'}) — ending this leg like a dropped stream`);
|
|
84
|
+
}
|
|
60
85
|
yield* flushHeldWithInterruptRewrite(led, { signal: opts?.signal });
|
|
61
86
|
return;
|
|
62
87
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CORE_MODE_ARBITRATION, CORE_MODE_RELEASE_REASONS } from '../generated/engineFactTables.js';
|
|
2
2
|
import { CORE_RELEASABLE_ASK_CLASSES_BY_FACE, CORE_SHELL_FACE_TOOL_NAMES } from '../generated/engineAskFaceTables.js';
|
|
3
3
|
import { mandatedApprovalDetail } from '../gateVocabulary.js';
|
|
4
|
-
import { readOwnNonEmptyString } from '../ownKey.js';
|
|
4
|
+
import { readAskOrigin, readOwnNonEmptyString } from '../ownKey.js';
|
|
5
5
|
import { readAskClassPairState } from './askClassPair.js';
|
|
6
6
|
import { approvalAllowRuleShadowed } from './allowRuleShadow.js';
|
|
7
7
|
import { approvalIsMandated, readApprovalMandate } from './toolApprovalWire.js';
|
|
@@ -102,7 +102,7 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
102
102
|
if (typeof card !== 'object' || card === null)
|
|
103
103
|
return UNKNOWN.card_unreadable;
|
|
104
104
|
const face = lane === 'mode_posture' ? askFaceOfCard(card) : undefined;
|
|
105
|
-
const origin =
|
|
105
|
+
const origin = readAskOrigin(evidenceView(card, 'origin'));
|
|
106
106
|
if (origin !== undefined && Object.hasOwn(ORIGIN_IMPLIES_CLASS, origin)) {
|
|
107
107
|
const cls = ORIGIN_IMPLIES_CLASS[origin];
|
|
108
108
|
if (classBlocks(lane, cls, face))
|
|
@@ -3,6 +3,8 @@ const CRASH_CONVERGED_CAUSE_WORDS = Object.freeze({
|
|
|
3
3
|
shutdown_before_park: true,
|
|
4
4
|
});
|
|
5
5
|
export const isCrashConvergedCause = (v) => typeof v === 'string' && Object.hasOwn(CRASH_CONVERGED_CAUSE_WORDS, v);
|
|
6
|
+
const _crashConvergedCausePin = true;
|
|
7
|
+
void _crashConvergedCausePin;
|
|
6
8
|
const isString = (v) => typeof v === 'string';
|
|
7
9
|
const isNumber = (v) => typeof v === 'number';
|
|
8
10
|
const nonEmptyString = (v) => typeof v === 'string' && v.length > 0;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type DecisionRefusalConflict = 'binding_mismatch' | 'row_integrity' | 'format_version' | 'answer_invalid';
|
|
2
|
+
export declare function decisionConflictOf(err: unknown): DecisionRefusalConflict | undefined;
|
|
3
|
+
export declare const DECISION_ANSWER_INVALID_CONTENT = "The engine rejected the answer itself because it does not fit the question that was asked \u00B7 Nothing was decided and the question is still waiting \u00B7 Correct the answer and send it again";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_UNSUPPORTED_VERSION, RESUME_OUTCOME_INVALID } from '../engineErrorCodes.js';
|
|
2
|
+
import { readRefusalField } from '../wireFailureShape.js';
|
|
3
|
+
const BINDING_SEAT_FIELDS = new Set(['boundCallId', 'boundInputHash']);
|
|
4
|
+
const ANSWER_FIELD = 'answer';
|
|
5
|
+
const BINDING_MISMATCH_ERROR_NAME = 'ApprovalBindingMismatchError';
|
|
6
|
+
export function decisionConflictOf(err) {
|
|
7
|
+
if (typeof err !== 'object' || err === null)
|
|
8
|
+
return undefined;
|
|
9
|
+
let code;
|
|
10
|
+
let name;
|
|
11
|
+
try {
|
|
12
|
+
code = err.errorCode;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
code = undefined;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
name = err.name;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
name = undefined;
|
|
22
|
+
}
|
|
23
|
+
if (code === CHECKPOINT_UNSUPPORTED_VERSION)
|
|
24
|
+
return 'format_version';
|
|
25
|
+
const bindingFamily = code === APPROVAL_BINDING_MISMATCH || (code === undefined && name === BINDING_MISMATCH_ERROR_NAME);
|
|
26
|
+
if (!bindingFamily && code !== RESUME_OUTCOME_INVALID)
|
|
27
|
+
return undefined;
|
|
28
|
+
let field;
|
|
29
|
+
try {
|
|
30
|
+
field = readRefusalField(err);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
field = undefined;
|
|
34
|
+
}
|
|
35
|
+
if (code === RESUME_OUTCOME_INVALID)
|
|
36
|
+
return field === ANSWER_FIELD ? 'answer_invalid' : undefined;
|
|
37
|
+
if (field === ANSWER_FIELD)
|
|
38
|
+
return 'answer_invalid';
|
|
39
|
+
if (field === undefined)
|
|
40
|
+
return 'row_integrity';
|
|
41
|
+
return BINDING_SEAT_FIELDS.has(field) ? 'binding_mismatch' : undefined;
|
|
42
|
+
}
|
|
43
|
+
export const DECISION_ANSWER_INVALID_CONTENT = 'The engine rejected the answer itself because it does not fit the question that was asked · Nothing was decided and the question is still waiting · Correct the answer and send it again';
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { type DecisionRefusalConflict } from './decisionConflict.js';
|
|
1
2
|
export type DecisionRefusalStanding = 'settled-elsewhere' | 'ambiguous' | 'refused-pending';
|
|
3
|
+
export type { DecisionRefusalConflict } from './decisionConflict.js';
|
|
4
|
+
export declare function decisionRefusalSaysAskGone(err: unknown): boolean;
|
|
2
5
|
export interface DecisionRefusalClassified {
|
|
3
6
|
readonly standing: DecisionRefusalStanding;
|
|
4
7
|
readonly status: number;
|
|
5
8
|
readonly errorCode?: string;
|
|
6
9
|
readonly transient?: true;
|
|
10
|
+
readonly conflict?: DecisionRefusalConflict;
|
|
7
11
|
}
|
|
8
12
|
export declare function classifyDecisionRefusal(err: unknown): DecisionRefusalClassified | undefined;
|
|
9
13
|
export type LiveAskPendingReading = 'pending' | 'not_pending' | 'unknown';
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import { refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
2
|
+
import { notPendingCauseSaysEnded } from './approvalEndingCause.js';
|
|
3
|
+
import { decisionConflictOf } from './decisionConflict.js';
|
|
2
4
|
import { readLivePendingRows } from './livePendingAsk.js';
|
|
3
5
|
import { readToolApprovalRespondRefusal } from './toolApprovalWire.js';
|
|
4
6
|
const ASK_DECIDED_CODE = 'conflict.ask_decided';
|
|
5
7
|
const TRANSIENT_STATUSES = new Set([408, 425, 429, 502, 503, 504]);
|
|
6
8
|
const DISPLAYABLE_CODE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
9
|
+
export function decisionRefusalSaysAskGone(err) {
|
|
10
|
+
try {
|
|
11
|
+
if (typeof err === 'object' && err !== null && 'code' in err) {
|
|
12
|
+
const code = err.code;
|
|
13
|
+
if (code === 'no_pending')
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const c = classifyDecisionRefusal(err);
|
|
17
|
+
if (c?.standing !== 'settled-elsewhere')
|
|
18
|
+
return false;
|
|
19
|
+
return c.status !== 404 || (typeof err === 'object' && err !== null && notPendingCauseSaysEnded(err));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
7
25
|
export function classifyDecisionRefusal(err) {
|
|
8
26
|
const r = readToolApprovalRespondRefusal(err);
|
|
9
27
|
const status = r?.status;
|
|
@@ -17,7 +35,14 @@ export function classifyDecisionRefusal(err) {
|
|
|
17
35
|
? 'ambiguous'
|
|
18
36
|
: 'refused-pending';
|
|
19
37
|
const transient = TRANSIENT_STATUSES.has(status) || (typeof err === 'object' && err !== null && refusalSaysStillPending(err));
|
|
20
|
-
|
|
38
|
+
const conflict = status === 409 ? decisionConflictOf(err) : undefined;
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
standing,
|
|
41
|
+
status,
|
|
42
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
43
|
+
...(transient ? { transient: true } : {}),
|
|
44
|
+
...(conflict !== undefined ? { conflict } : {}),
|
|
45
|
+
});
|
|
21
46
|
}
|
|
22
47
|
export function liveAskPendingReading(envelope, approvalId) {
|
|
23
48
|
if (typeof approvalId !== 'string' || approvalId === '')
|
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
2
3
|
import { isToolApprovalGate, toolNameIsFsWrite, toolNameIsShellExec, isToolApprovalFrame, isFromSubagent, surfaceToolApprovalFrameAndRespond, } from './toolApprovalWire.js';
|
|
3
4
|
import { classifierDenyFromToolEnd } from '../classifierVerdictWire.js';
|
|
4
5
|
import { GATE_PARKED_ERROR_CODE } from '../engineErrorCodes.js';
|
|
5
6
|
import { readRunTerminal, runTerminalCode, runTerminalGateKind, runTerminalGateToolName } from '../runTerminal.js';
|
|
6
7
|
import { surfaceForCurrentSession, surfaceReplayedApprovalListUnreadable } from './hitlHostSurface.js';
|
|
7
|
-
import { liveCardHoldOf, supersedeLiveCard } from './approvalHolds.js';
|
|
8
|
+
import { liveCardAnsweredHow, liveCardHoldOf, supersedeLiveCard } from './approvalHolds.js';
|
|
9
|
+
import { approvalCompleteParked } from './approvalCompleteParked.js';
|
|
8
10
|
import { liveFrameCallKey } from './gateIdentity.js';
|
|
9
11
|
import { SEMA_DENIAL_KIND_KEY, hasLedgerPositionKey } from './gateLedger.js';
|
|
10
|
-
import { replayedApprovalDisposition } from './replayedApproval.js';
|
|
12
|
+
import { replayedApprovalDisposition, settledApprovalDisposition } from './replayedApproval.js';
|
|
11
13
|
import { settleWithinDeadline } from '../abortableSleep.js';
|
|
12
14
|
import { ccToolDenialKindForSettledBy } from '../gateVocabulary.js';
|
|
13
15
|
import { REJECT_MESSAGE } from '../adapt/wireShapes.js';
|
|
@@ -266,48 +268,34 @@ const TOOL_END_ARMS = [
|
|
|
266
268
|
'改顺序必须同批改 TOOL_END_ARM_ORDER 并说明为什么新顺序仍然对。');
|
|
267
269
|
}
|
|
268
270
|
}
|
|
269
|
-
function completeFrameParked(ev) {
|
|
270
|
-
try {
|
|
271
|
-
const d = Object.getOwnPropertyDescriptor(ev, 'parked');
|
|
272
|
-
return d !== undefined && 'value' in d && d.value === true;
|
|
273
|
-
}
|
|
274
|
-
catch {
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
271
|
async function routeToolApprovalFrame(ev, ctx) {
|
|
279
272
|
const { led, deps } = ctx;
|
|
280
273
|
if (ev.type === 'tool_approval_complete') {
|
|
281
274
|
const id = ev.approvalId;
|
|
282
|
-
if (typeof id === 'string' && id !== '' && supersedeLiveCard(liveFrameCallKey(id),
|
|
275
|
+
if (typeof id === 'string' && id !== '' && supersedeLiveCard(liveFrameCallKey(id), approvalCompleteParked(ev))) {
|
|
283
276
|
hostLog('debug', `liveHitlAskWire: tool_approval_complete for ${id} arrived while its card was waiting — card withdrawn (nothing will be sent from it)`);
|
|
284
277
|
}
|
|
278
|
+
if (typeof id === 'string' && id !== '')
|
|
279
|
+
led.noteApprovalSettled(id, 'closed_by_engine');
|
|
285
280
|
return { kind: 'skip' };
|
|
286
281
|
}
|
|
287
282
|
if (!deps.respondToolApproval) {
|
|
288
283
|
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} but no respond wire — engine self-settles (TTL deny)`);
|
|
289
284
|
return { kind: 'skip' };
|
|
290
285
|
}
|
|
291
|
-
|
|
292
|
-
|
|
286
|
+
const settledHere = settledApprovalDisposition(ev, led.settledApprovals());
|
|
287
|
+
if (settledHere !== undefined || (hasLedgerPositionKey(ev) && !led.preambleApprovals().has(ev.approvalId))) {
|
|
288
|
+
const disposition = settledHere ?? (await judgeLedgerApprovalFrame(ev, ctx));
|
|
293
289
|
if (disposition.kind !== 'live') {
|
|
294
|
-
hostLog('debug',
|
|
295
|
-
|
|
296
|
-
|
|
290
|
+
hostLog('debug', disposition.kind === 'decided' && disposition.settled !== undefined
|
|
291
|
+
? `liveHitlAskWire: tool_approval frame ${ev.approvalId} repeats a question already settled on this bridge (${disposition.settled}) — not raised again, no decision sent`
|
|
292
|
+
: `liveHitlAskWire: tool_approval frame ${ev.approvalId} read back from the run event log is ${disposition.kind}` +
|
|
293
|
+
`${disposition.kind === 'unknown' ? ` (${disposition.why})` : disposition.cause !== undefined ? ` (${disposition.cause})` : ''}` +
|
|
294
|
+
' — not raised as a question, no decision sent');
|
|
297
295
|
if (disposition.kind === 'unknown' && ctx.signal?.aborted !== true)
|
|
298
296
|
surfaceReplayedApprovalListUnreadable();
|
|
299
297
|
if (deps.onReplayedApproval !== undefined) {
|
|
300
|
-
|
|
301
|
-
const out = deps.onReplayedApproval(ev, disposition);
|
|
302
|
-
if (out && typeof out.then === 'function') {
|
|
303
|
-
void Promise.resolve(out).catch((e) => {
|
|
304
|
-
hostLog('debug', `liveHitlAskWire: onReplayedApproval rejected: ${String(e).slice(0, 160)}`);
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
catch (e) {
|
|
309
|
-
hostLog('debug', `liveHitlAskWire: onReplayedApproval threw: ${String(e).slice(0, 160)}`);
|
|
310
|
-
}
|
|
298
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onReplayedApproval', () => deps.onReplayedApproval?.(ev, disposition));
|
|
311
299
|
}
|
|
312
300
|
return { kind: 'skip' };
|
|
313
301
|
}
|
|
@@ -317,29 +305,24 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
317
305
|
const frameCallId = !fromSubagent && typeof ev.toolCallId === 'string' && ev.toolCallId !== '' ? ev.toolCallId : undefined;
|
|
318
306
|
const gatedCallId = fromSubagent ? undefined : frameCallId ?? led.lastPendingFsCall();
|
|
319
307
|
const argsCallId = frameCallId === undefined ? gatedCallId : typeof ev.toolName === 'string' && toolNameIsFsWrite(ev.toolName) ? frameCallId : undefined;
|
|
320
|
-
const
|
|
308
|
+
const runTaskId = ctx.taskId.current;
|
|
309
|
+
const approvalIdAtLaunch = ev.approvalId;
|
|
310
|
+
const launched = surfaceToolApprovalFrameAndRespond(ev, deps.respondToolApproval, argsOfGatedStart(led, argsCallId), ctx.signal, deps.approvalLane, typeof runTaskId === 'string' && runTaskId !== '' ? { taskId: runTaskId } : undefined);
|
|
321
311
|
const hold = liveCardHoldOf(liveFrameCallKey(ev.approvalId));
|
|
322
|
-
const handled = launched.then((outcome) => settleLiveCardOutcome(ev, outcome, ctx, fromSubagent, gatedCallId), (e) => {
|
|
312
|
+
const handled = launched.then((outcome) => settleLiveCardOutcome(ev, approvalIdAtLaunch, outcome, ctx, fromSubagent, gatedCallId), (e) => {
|
|
323
313
|
hostLog('debug', `liveHitlAskWire: tool_approval card for ${ev.approvalId} failed: ${String(e).slice(0, 160)}`);
|
|
324
314
|
});
|
|
325
315
|
led.trackLiveCard(() => hold?.phase ?? 'card', handled);
|
|
326
316
|
return { kind: 'skip' };
|
|
327
317
|
}
|
|
328
|
-
function settleLiveCardOutcome(ev, outcome, ctx, fromSubagent, gatedCallId) {
|
|
318
|
+
function settleLiveCardOutcome(ev, approvalId, outcome, ctx, fromSubagent, gatedCallId) {
|
|
329
319
|
const { led, deps } = ctx;
|
|
330
320
|
const { decision } = outcome;
|
|
321
|
+
const settledHow = liveCardAnsweredHow(outcome);
|
|
322
|
+
if (settledHow !== undefined)
|
|
323
|
+
led.noteApprovalSettled(approvalId, settledHow);
|
|
331
324
|
if (deps.onToolApprovalOutcome !== undefined) {
|
|
332
|
-
|
|
333
|
-
const out = deps.onToolApprovalOutcome(ev, outcome);
|
|
334
|
-
if (out && typeof out.then === 'function') {
|
|
335
|
-
void Promise.resolve(out).catch((e) => {
|
|
336
|
-
hostLog('debug', `liveHitlAskWire: onToolApprovalOutcome rejected: ${String(e).slice(0, 160)}`);
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
catch (e) {
|
|
341
|
-
hostLog('debug', `liveHitlAskWire: onToolApprovalOutcome threw: ${String(e).slice(0, 160)}`);
|
|
342
|
-
}
|
|
325
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onToolApprovalOutcome', () => deps.onToolApprovalOutcome?.(ev, outcome));
|
|
343
326
|
}
|
|
344
327
|
if (decision === 'deny' && !fromSubagent) {
|
|
345
328
|
const attribution = {
|
|
@@ -374,7 +357,7 @@ async function judgeLedgerApprovalFrame(ev, ctx) {
|
|
|
374
357
|
.finally(release);
|
|
375
358
|
});
|
|
376
359
|
const approvals = got.kind === 'value' ? { read: 'ok', body: got.value } : { read: 'failed' };
|
|
377
|
-
return replayedApprovalDisposition(ev, { preamble: ctx.led.preambleApprovals(), approvals });
|
|
360
|
+
return replayedApprovalDisposition(ev, { preamble: ctx.led.preambleApprovals(), approvals, settled: ctx.led.settledApprovals() });
|
|
378
361
|
}
|
|
379
362
|
function routeToolStart(ev, callId, led) {
|
|
380
363
|
led.noteStart(callId, ev);
|
|
@@ -16,6 +16,9 @@ export interface AskAnsweredOutput {
|
|
|
16
16
|
export declare function durableSeqOf(ev: object): string | undefined;
|
|
17
17
|
export declare function hasLedgerPositionKey(ev: object): boolean;
|
|
18
18
|
export declare const PREAMBLE_APPROVALS_MAX_ENTRIES = 4096;
|
|
19
|
+
export declare const APPROVAL_SETTLED_HOWS: readonly ["answered", "interrupted", "closed_by_engine"];
|
|
20
|
+
export type ApprovalSettledHow = (typeof APPROVAL_SETTLED_HOWS)[number];
|
|
21
|
+
export declare const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
|
|
19
22
|
export interface GateLedger {
|
|
20
23
|
noteSeq(ev: AgentEvent): void;
|
|
21
24
|
lastSeq(): string | undefined;
|
|
@@ -23,6 +26,8 @@ export interface GateLedger {
|
|
|
23
26
|
notePreambleApproval(approvalId: string): void;
|
|
24
27
|
preambleApprovals(): ReadonlySet<string>;
|
|
25
28
|
beginStreamSegment(): void;
|
|
29
|
+
noteApprovalSettled(approvalId: string, how: ApprovalSettledHow): void;
|
|
30
|
+
settledApprovals(): ReadonlyMap<string, ApprovalSettledHow>;
|
|
26
31
|
trackLiveCard(phaseOf: () => 'card' | 'respond', handled: Promise<void>): void;
|
|
27
32
|
answeringLiveCards(): Promise<void>[];
|
|
28
33
|
liveCardsSettled(): Promise<void>;
|