@sema-agent/client-core 0.94.0 → 0.96.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 +521 -0
- package/README.md +104 -57
- package/dist/adapt/arms.js +12 -7
- package/dist/adapt/panelTasks.js +4 -10
- package/dist/adapt/toolCards.d.ts +1 -0
- package/dist/adapt/toolCards.js +18 -5
- package/dist/adapt/wireShapes.js +5 -24
- package/dist/adapter/activeRunSelfHeal.d.ts +12 -3
- package/dist/adapter/activeRunSelfHeal.js +50 -16
- package/dist/adapter/downstream/eventToSdkMessage.js +33 -18
- package/dist/adapter/downstream/terminalToSdkResult.js +19 -12
- package/dist/adapter/runStream.js +30 -46
- 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 +7 -5
- package/dist/agentSession/engineSessionBackgroundStop.js +25 -17
- package/dist/agentsWireCaps.d.ts +2 -0
- package/dist/agentsWireCaps.js +13 -2
- 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 -5
- package/dist/callerCwdCapability.js +2 -13
- package/dist/centerWiringCapability.d.ts +1 -7
- package/dist/centerWiringCapability.js +18 -27
- package/dist/classifierStatus.js +4 -4
- package/dist/classifierVerdictWire.js +4 -16
- package/dist/clientSlice.js +1 -1
- package/dist/cloudConfigWireCaps.d.ts +3 -3
- package/dist/cloudConfigWireCaps.js +85 -43
- package/dist/compensations.js +1 -1
- package/dist/coreValuePorts.d.ts +11 -7
- package/dist/coreValuePorts.js +5 -8
- package/dist/credentialProvenance.d.ts +28 -0
- package/dist/credentialProvenance.js +76 -0
- package/dist/cycleOrder.d.ts +15 -0
- package/dist/cycleOrder.js +45 -0
- package/dist/decideFailureNote.js +7 -2
- package/dist/decideReceipt.d.ts +2 -1
- package/dist/decideReceipt.js +39 -14
- 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 +8 -3
- package/dist/displayUntrusted.js +661 -63
- 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 +13 -2
- package/dist/engineCapsCache.js +276 -44
- package/dist/engineCredentialRequirement.d.ts +9 -0
- package/dist/engineCredentialRequirement.js +23 -0
- package/dist/engineDirectory.d.ts +79 -0
- package/dist/engineDirectory.js +186 -0
- package/dist/engineEnvSourcing.d.ts +53 -0
- package/dist/engineEnvSourcing.js +81 -0
- package/dist/engineErrorCodes.d.ts +28 -4
- package/dist/engineErrorCodes.js +68 -25
- package/dist/engineIdentity.js +2 -0
- package/dist/engineNoticeCodes.d.ts +17 -0
- package/dist/engineNoticeCodes.js +57 -3
- package/dist/engineWireSdk.js +2 -2
- package/dist/engineWordRenames.d.ts +8 -0
- package/dist/engineWordRenames.js +13 -0
- package/dist/executionLaneCapability.d.ts +2 -3
- package/dist/executionLaneCapability.js +18 -23
- package/dist/fileHistoryCaptureCapability.d.ts +14 -1
- package/dist/fileHistoryCaptureCapability.js +72 -6
- package/dist/fileHistoryLegNotice.d.ts +3 -2
- package/dist/fileHistoryLegNotice.js +51 -2
- package/dist/fleet/bgSubagentRow.d.ts +15 -0
- package/dist/fleet/bgSubagentRow.js +67 -0
- package/dist/fleet/fleetLedger.d.ts +3 -0
- package/dist/fleet/fleetLedger.js +153 -122
- package/dist/fleet/fleetProjection.d.ts +1 -3
- package/dist/fleet/fleetProjection.js +19 -19
- 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/fleetTaskDesc.js +4 -17
- package/dist/gateOutcome.d.ts +12 -1
- package/dist/gateOutcome.js +87 -18
- package/dist/gateVocabulary.d.ts +7 -2
- package/dist/gateVocabulary.js +5 -1
- package/dist/generated/engineAskFaceTables.js +1 -1
- package/dist/generated/engineFactTables.d.ts +5 -3
- package/dist/generated/engineFactTables.js +17 -4
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/sdkFactTables.d.ts +2 -0
- package/dist/generated/sdkFactTables.js +17 -0
- package/dist/generated/settingsSchemaFns.d.ts +13 -0
- package/dist/generated/settingsSchemaFns.js +75 -0
- package/dist/generated/toolNameTables.d.ts +1 -0
- package/dist/generated/toolNameTables.js +1 -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 +7 -0
- package/dist/hitl/approvalEndingCause.js +71 -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/askParkRowRouting.js +1 -1
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/automaticAnswerVerdict.js +15 -10
- package/dist/hitl/crashConverged.js +2 -0
- package/dist/hitl/decisionConflict.d.ts +3 -0
- package/dist/hitl/decisionConflict.js +41 -0
- package/dist/hitl/decisionRefusal.d.ts +12 -0
- package/dist/hitl/decisionRefusal.js +63 -4
- package/dist/hitl/frameRouter.d.ts +0 -1
- package/dist/hitl/frameRouter.js +32 -57
- package/dist/hitl/gateLedger.d.ts +5 -0
- package/dist/hitl/gateLedger.js +21 -0
- package/dist/hitl/hitlBridge.d.ts +17 -2
- package/dist/hitl/hitlBridge.js +91 -55
- 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/localAllowRule.d.ts +1 -0
- package/dist/hitl/localAllowRule.js +2 -2
- package/dist/hitl/noAllowCause.d.ts +2 -1
- package/dist/hitl/noAllowCause.js +3 -2
- package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
- package/dist/hitl/parkExpiryJudgements.js +46 -0
- package/dist/hitl/parkResolver.d.ts +4 -7
- package/dist/hitl/parkResolver.js +12 -34
- package/dist/hitl/parkRowBirthWait.d.ts +1 -0
- package/dist/hitl/parkRowBirthWait.js +2 -2
- package/dist/hitl/persistedRulesWire.js +5 -1
- package/dist/hitl/planReviewWire.d.ts +3 -2
- package/dist/hitl/planReviewWire.js +154 -50
- package/dist/hitl/replayedApproval.d.ts +9 -0
- package/dist/hitl/replayedApproval.js +139 -10
- package/dist/hitl/resumeRunningCard.d.ts +2 -0
- package/dist/hitl/resumeRunningCard.js +15 -4
- package/dist/hitl/sessionPolicyDeliverable.js +8 -6
- package/dist/hitl/standingRows.d.ts +18 -0
- package/dist/hitl/standingRows.js +127 -0
- package/dist/hitl/toolApprovalWire.d.ts +39 -7
- package/dist/hitl/toolApprovalWire.js +351 -61
- package/dist/hookMatcherVerdict.d.ts +1 -9
- package/dist/hookMatcherVerdict.js +21 -61
- package/dist/hooksWireCaps.js +40 -8
- package/dist/host.d.ts +5 -0
- package/dist/host.js +3 -1
- package/dist/hostCallbackGuard.d.ts +2 -0
- package/dist/hostCallbackGuard.js +31 -0
- package/dist/index.d.ts +48 -17
- package/dist/index.js +36 -15
- 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 -5
- package/dist/mcpProbeCapability.js +2 -12
- package/dist/mcpReconnect.d.ts +1 -3
- package/dist/mcpReconnect.js +10 -5
- package/dist/memoryComplianceCapability.d.ts +0 -5
- package/dist/memoryComplianceCapability.js +2 -12
- package/dist/memoryOriginCapability.d.ts +0 -5
- package/dist/memoryOriginCapability.js +2 -12
- package/dist/memorySpecWire.js +2 -2
- 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 +2 -2
- package/dist/modelIdentityResolvability.js +14 -12
- package/dist/modelSwitchFallback.d.ts +69 -0
- package/dist/modelSwitchFallback.js +206 -0
- package/dist/noticePresentation.d.ts +68 -0
- package/dist/noticePresentation.js +247 -0
- package/dist/notifications.js +12 -11
- package/dist/outputStyleSeatCapability.d.ts +0 -2
- package/dist/outputStyleSeatCapability.js +2 -10
- package/dist/ownKey.d.ts +8 -0
- package/dist/ownKey.js +66 -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/peerFrames.js +13 -9
- package/dist/peerLaneCapability.d.ts +0 -1
- package/dist/peerLaneCapability.js +2 -6
- package/dist/permissionModesCapability.d.ts +0 -5
- package/dist/permissionModesCapability.js +5 -16
- package/dist/permissionRuleIssue.js +0 -3
- 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 +8 -0
- package/dist/permissionsDeclaration.js +101 -41
- package/dist/printPermissionOutcome.d.ts +12 -0
- package/dist/printPermissionOutcome.js +50 -0
- package/dist/printToolResultFrame.d.ts +1 -0
- package/dist/printToolResultFrame.js +5 -17
- package/dist/promptBlockedCopy.d.ts +5 -0
- package/dist/promptBlockedCopy.js +35 -0
- package/dist/promptDelivery.js +0 -8
- package/dist/protocolSegment.d.ts +9 -6
- package/dist/protocolSegment.js +31 -21
- package/dist/providerRejection.d.ts +4 -0
- package/dist/providerRejection.js +75 -14
- package/dist/replayBody.d.ts +12 -0
- package/dist/replayBody.js +25 -0
- package/dist/resumeRefusalCopy.d.ts +18 -1
- package/dist/resumeRefusalCopy.js +127 -32
- package/dist/retryStatus.d.ts +9 -1
- package/dist/retryStatus.js +9 -2
- package/dist/rewindArchiveCapability.d.ts +5 -14
- package/dist/rewindArchiveCapability.js +18 -61
- package/dist/rewindArchiveReading.d.ts +15 -0
- package/dist/rewindArchiveReading.js +56 -0
- 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/ruleCompileVerdict.d.ts +9 -1
- package/dist/ruleCompileVerdict.js +29 -4
- package/dist/runTerminal.d.ts +15 -12
- package/dist/runTerminal.js +60 -35
- package/dist/sandboxWire.js +13 -0
- package/dist/sdkRegistryTransit.d.ts +2 -1
- package/dist/sdkRegistryTransit.js +1 -0
- package/dist/sealedKeyCapability.d.ts +0 -5
- package/dist/sealedKeyCapability.js +3 -15
- package/dist/seam.js +4 -2
- package/dist/seatContract.d.ts +125 -34
- package/dist/seatContract.js +583 -179
- 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 -5
- package/dist/sessionReadRedactionCapability.js +2 -13
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +66 -0
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -2
- package/dist/sessionSyncFileHistoryCapability.js +2 -9
- package/dist/skillToolListsCapability.d.ts +0 -2
- package/dist/skillToolListsCapability.js +2 -9
- 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 -2
- package/dist/steerPriorityCapability.js +3 -12
- package/dist/storePostureCapability.d.ts +1 -5
- package/dist/storePostureCapability.js +16 -22
- 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/engineSubagentSteer.js +7 -5
- package/dist/subagent/engineSubagentTail.d.ts +0 -1
- package/dist/subagent/engineSubagentTail.js +7 -32
- package/dist/subagentContentStore.js +7 -18
- package/dist/supportFloor.d.ts +1 -0
- package/dist/supportFloor.js +15 -14
- package/dist/systemReminderTag.d.ts +1 -0
- package/dist/systemReminderTag.js +8 -0
- package/dist/terminalStatuses.d.ts +30 -0
- package/dist/terminalStatuses.js +67 -0
- package/dist/toolHistoryMismatch.d.ts +0 -3
- package/dist/toolHistoryMismatch.js +3 -7
- package/dist/toolResult.d.ts +6 -1
- package/dist/toolResult.js +90 -37
- package/dist/toolResultContent.d.ts +2 -0
- package/dist/toolResultContent.js +63 -0
- package/dist/types/engineState.d.ts +2 -1
- package/dist/usageAnalytics.d.ts +146 -0
- package/dist/usageAnalytics.js +335 -0
- package/dist/userInvokedSkillsCapability.d.ts +0 -2
- package/dist/userInvokedSkillsCapability.js +2 -9
- package/dist/webSearchBackendCapability.d.ts +0 -1
- package/dist/webSearchBackendCapability.js +3 -7
- package/dist/webSearchWireCaps.js +3 -11
- package/dist/wireErrorTriage.d.ts +2 -0
- package/dist/wireErrorTriage.js +34 -5
- package/dist/wireFailureShape.d.ts +23 -2
- package/dist/wireFailureShape.js +88 -43
- 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 +2279 -27
- 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
|
@@ -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
|
}
|
|
@@ -50,7 +50,7 @@ export function askParkRowArm(row) {
|
|
|
50
50
|
const rowHasToolName = typeof row.toolName === 'string' && row.toolName.trim().length > 0;
|
|
51
51
|
return askQuestionsFromPending(row) !== null || !rowHasToolName ? 'question' : 'tool-gate';
|
|
52
52
|
}
|
|
53
|
-
const GENERIC_DECISION_GATE_KINDS = ['human'
|
|
53
|
+
const GENERIC_DECISION_GATE_KINDS = ['human'];
|
|
54
54
|
export function askParkRowStillPending(rows, identity) {
|
|
55
55
|
return rows.some(r => identity.gatedCallId !== undefined
|
|
56
56
|
? (r.toolCallId ?? r.boundCallId ?? undefined) === identity.gatedCallId
|
|
@@ -19,7 +19,7 @@ export type AutomaticAnswerVerdict = {
|
|
|
19
19
|
readonly kind: 'unknown';
|
|
20
20
|
readonly why: AutomaticAnswerUnknownWhy;
|
|
21
21
|
};
|
|
22
|
-
export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "rule_unreadable" | "
|
|
22
|
+
export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "rule_unreadable" | "target_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "org_rule" | "governance_unavailable" | "ask_rule" | "denial_limit_fallback" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", CoreModeArbitrationRow>>;
|
|
23
23
|
export declare function foldModeArbitrationRow(classes: Iterable<string>, askFace?: AutomaticAnswerAskFace): CoreModeArbitrationRow;
|
|
24
24
|
export declare function automaticAnswerVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
|
|
25
25
|
export declare function mandatedApprovalDetailOf(card: object | null | undefined): string;
|
|
@@ -1,8 +1,9 @@
|
|
|
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
|
+
import { currentEngineWord } from '../engineWordRenames.js';
|
|
6
7
|
import { approvalAllowRuleShadowed } from './allowRuleShadow.js';
|
|
7
8
|
import { approvalIsMandated, readApprovalMandate } from './toolApprovalWire.js';
|
|
8
9
|
export const MODE_ARBITRATION = CORE_MODE_ARBITRATION;
|
|
@@ -15,9 +16,10 @@ const ORIGIN_IMPLIES_CLASS = Object.freeze({
|
|
|
15
16
|
ask_rule: 'ask_rule',
|
|
16
17
|
denial_limit_fallback: 'denial_limit_fallback',
|
|
17
18
|
});
|
|
18
|
-
function mandateWordClass(
|
|
19
|
-
if (
|
|
19
|
+
function mandateWordClass(raw) {
|
|
20
|
+
if (raw === 'operator_always')
|
|
20
21
|
return 'shell_floor';
|
|
22
|
+
const word = currentEngineWord(raw);
|
|
21
23
|
return Object.hasOwn(TABLE, word) ? word : undefined;
|
|
22
24
|
}
|
|
23
25
|
const ORDINARY_ROW = CORE_MODE_ARBITRATION.ordinary;
|
|
@@ -41,7 +43,8 @@ export function foldModeArbitrationRow(classes, askFace) {
|
|
|
41
43
|
let sandboxAdmission = true;
|
|
42
44
|
let ruleMayClear = true;
|
|
43
45
|
let hookAskFloor = false;
|
|
44
|
-
for (const
|
|
46
|
+
for (const raw of classes) {
|
|
47
|
+
const c = typeof raw === 'string' ? currentEngineWord(raw) : raw;
|
|
45
48
|
if (!Object.hasOwn(TABLE, c))
|
|
46
49
|
continue;
|
|
47
50
|
const row = TABLE[c];
|
|
@@ -102,7 +105,7 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
102
105
|
if (typeof card !== 'object' || card === null)
|
|
103
106
|
return UNKNOWN.card_unreadable;
|
|
104
107
|
const face = lane === 'mode_posture' ? askFaceOfCard(card) : undefined;
|
|
105
|
-
const origin =
|
|
108
|
+
const origin = readAskOrigin(evidenceView(card, 'origin'));
|
|
106
109
|
if (origin !== undefined && Object.hasOwn(ORIGIN_IMPLIES_CLASS, origin)) {
|
|
107
110
|
const cls = ORIGIN_IMPLIES_CLASS[origin];
|
|
108
111
|
if (classBlocks(lane, cls, face))
|
|
@@ -130,11 +133,12 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
130
133
|
return UNKNOWN.classes_malformed;
|
|
131
134
|
if (pair.state === 'absent')
|
|
132
135
|
return UNKNOWN.classes_absent;
|
|
133
|
-
const
|
|
136
|
+
const classes = pair.classes.map(currentEngineWord);
|
|
137
|
+
const folded = foldModeArbitrationRow(classes, face);
|
|
134
138
|
let anyKnown = false;
|
|
135
139
|
let firstBlocking;
|
|
136
140
|
let anyUnknown = false;
|
|
137
|
-
for (const c of
|
|
141
|
+
for (const c of classes) {
|
|
138
142
|
if (!Object.hasOwn(TABLE, c)) {
|
|
139
143
|
anyUnknown = true;
|
|
140
144
|
continue;
|
|
@@ -154,7 +158,7 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
154
158
|
return mustAsk('settings_ask_rule');
|
|
155
159
|
if (matched !== false)
|
|
156
160
|
return UNKNOWN.host_not_reported;
|
|
157
|
-
const releaseReason = CORE_MODE_RELEASE_REASONS.find((r) =>
|
|
161
|
+
const releaseReason = CORE_MODE_RELEASE_REASONS.find((r) => classes.includes(r));
|
|
158
162
|
return releaseReason !== undefined ? Object.freeze({ kind: 'may_answer', releaseReason }) : MAY_ANSWER;
|
|
159
163
|
}
|
|
160
164
|
catch {
|
|
@@ -168,9 +172,10 @@ export function mandatedApprovalDetailOf(card) {
|
|
|
168
172
|
const pair = readAskClassPairState(card);
|
|
169
173
|
if (pair.state !== 'pair')
|
|
170
174
|
return mandatedApprovalDetail();
|
|
171
|
-
|
|
175
|
+
const classes = pair.classes.map(currentEngineWord);
|
|
176
|
+
if (classes.some((c) => !Object.hasOwn(TABLE, c)))
|
|
172
177
|
return mandatedApprovalDetail();
|
|
173
|
-
const row = foldModeArbitrationRow(
|
|
178
|
+
const row = foldModeArbitrationRow(classes);
|
|
174
179
|
if (row.classifierEligible || row.sandboxAdmission)
|
|
175
180
|
return mandatedApprovalDetail();
|
|
176
181
|
return MANDATED_NO_AUTOMATIC_CHECK_DETAIL;
|
|
@@ -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,41 @@
|
|
|
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 === undefined)
|
|
38
|
+
return 'row_integrity';
|
|
39
|
+
return BINDING_SEAT_FIELDS.has(field) ? 'binding_mismatch' : undefined;
|
|
40
|
+
}
|
|
41
|
+
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,21 @@
|
|
|
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;
|
|
5
|
+
export type DecisionRefusalAskReading = 'settled' | 'not_settled' | 'unreadable';
|
|
6
|
+
interface DecisionRefusalAskFacts {
|
|
7
|
+
readonly reading: DecisionRefusalAskReading;
|
|
8
|
+
readonly gateMoved: boolean;
|
|
9
|
+
readonly wordingHint: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function readDecisionRefusalAsk(failure: unknown): DecisionRefusalAskFacts;
|
|
12
|
+
export declare function decisionRefusalAskReading(failure: unknown): DecisionRefusalAskReading;
|
|
2
13
|
export interface DecisionRefusalClassified {
|
|
3
14
|
readonly standing: DecisionRefusalStanding;
|
|
4
15
|
readonly status: number;
|
|
5
16
|
readonly errorCode?: string;
|
|
6
17
|
readonly transient?: true;
|
|
18
|
+
readonly conflict?: DecisionRefusalConflict;
|
|
7
19
|
}
|
|
8
20
|
export declare function classifyDecisionRefusal(err: unknown): DecisionRefusalClassified | undefined;
|
|
9
21
|
export type LiveAskPendingReading = 'pending' | 'not_pending' | 'unknown';
|
|
@@ -1,9 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ApprovalStaleError, AskDecidedError, ParkedResumeStartupError } from '@sema-agent/sdk';
|
|
2
|
+
import { ASK_GONE, ASK_GONE_PARKED, isDecideAlreadyResolvedCode, isDecideParkMovedCode, refusalRetriableReading, refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
3
|
+
import { notPendingCauseReading } from './approvalEndingCause.js';
|
|
4
|
+
import { GATE_FAILURE_CODES } from './hitlBridge.js';
|
|
5
|
+
import { isAlreadyResolvedGateReason, isCodeClassifiedGateFailure } from './parkResolver.js';
|
|
6
|
+
import { decisionConflictOf } from './decisionConflict.js';
|
|
2
7
|
import { readLivePendingRows } from './livePendingAsk.js';
|
|
3
8
|
import { readToolApprovalRespondRefusal } from './toolApprovalWire.js';
|
|
4
9
|
const ASK_DECIDED_CODE = 'conflict.ask_decided';
|
|
5
10
|
const TRANSIENT_STATUSES = new Set([408, 425, 429, 502, 503, 504]);
|
|
6
11
|
const DISPLAYABLE_CODE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
12
|
+
export function decisionRefusalSaysAskGone(err) {
|
|
13
|
+
return readDecisionRefusalAsk(err).reading === 'settled';
|
|
14
|
+
}
|
|
15
|
+
const facts = (reading, gateMoved = false, wordingHint = false) => ({ reading, gateMoved, wordingHint });
|
|
16
|
+
export function readDecisionRefusalAsk(failure) {
|
|
17
|
+
if (typeof failure !== 'object' || failure === null)
|
|
18
|
+
return facts('not_settled');
|
|
19
|
+
let flattened, localCode, name, errorCode, status, retriable, text;
|
|
20
|
+
try {
|
|
21
|
+
const o = failure;
|
|
22
|
+
if (Object.hasOwn(failure, 'unreadable') && failure.unreadable === true)
|
|
23
|
+
return facts('unreadable');
|
|
24
|
+
flattened = Object.hasOwn(failure, 'kind') && o.kind === 'failed';
|
|
25
|
+
const closed = (v) => (typeof v === 'string' && GATE_FAILURE_CODES.includes(v) ? v : undefined);
|
|
26
|
+
localCode = flattened ? (closed(o.code) ?? closed(o.safetyCode)) : closed(o.code);
|
|
27
|
+
name = flattened ? undefined : o.name;
|
|
28
|
+
errorCode = o.errorCode;
|
|
29
|
+
status = o.status;
|
|
30
|
+
retriable = refusalRetriableReading(failure);
|
|
31
|
+
text = flattened ? o.reason : String(failure);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return facts('unreadable');
|
|
35
|
+
}
|
|
36
|
+
if (retriable === undefined)
|
|
37
|
+
return facts('unreadable');
|
|
38
|
+
if (localCode !== undefined)
|
|
39
|
+
return facts(localCode === 'no_pending' ? 'settled' : 'not_settled');
|
|
40
|
+
if (name === 'HitlSafetyError')
|
|
41
|
+
return facts('not_settled');
|
|
42
|
+
const code = typeof errorCode === 'string' && errorCode.length > 0 ? errorCode : undefined;
|
|
43
|
+
const st = typeof status === 'number' && Number.isInteger(status) && status >= 400 && status <= 599 ? status : undefined;
|
|
44
|
+
if (code === ASK_DECIDED_CODE || code === ASK_GONE || (st === 410 && code !== ASK_GONE_PARKED))
|
|
45
|
+
return facts('settled');
|
|
46
|
+
if (st === 404 && notPendingCauseReading(failure) === 'ended')
|
|
47
|
+
return facts('settled');
|
|
48
|
+
if (code === undefined && name === AskDecidedError.name)
|
|
49
|
+
return facts('settled');
|
|
50
|
+
const gateMoved = isDecideAlreadyResolvedCode(code) || isDecideParkMovedCode(code) || code === ASK_GONE_PARKED || (code === undefined && name === ApprovalStaleError.name);
|
|
51
|
+
if (gateMoved || st !== undefined || name === ParkedResumeStartupError.name || isCodeClassifiedGateFailure(code, retriable))
|
|
52
|
+
return facts('not_settled', gateMoved);
|
|
53
|
+
return facts('not_settled', false, isAlreadyResolvedGateReason(text));
|
|
54
|
+
}
|
|
55
|
+
export function decisionRefusalAskReading(failure) {
|
|
56
|
+
return readDecisionRefusalAsk(failure).reading;
|
|
57
|
+
}
|
|
7
58
|
export function classifyDecisionRefusal(err) {
|
|
8
59
|
const r = readToolApprovalRespondRefusal(err);
|
|
9
60
|
const status = r?.status;
|
|
@@ -11,13 +62,21 @@ export function classifyDecisionRefusal(err) {
|
|
|
11
62
|
return undefined;
|
|
12
63
|
const code = r?.errorCode;
|
|
13
64
|
const errorCode = code !== undefined && DISPLAYABLE_CODE.test(code) ? code : undefined;
|
|
14
|
-
const standing =
|
|
65
|
+
const standing = readDecisionRefusalAsk(err).reading === 'settled'
|
|
15
66
|
? 'settled-elsewhere'
|
|
16
67
|
: status >= 500 && status !== 501
|
|
17
68
|
? 'ambiguous'
|
|
18
69
|
: 'refused-pending';
|
|
19
|
-
const transient = TRANSIENT_STATUSES.has(status) ||
|
|
20
|
-
|
|
70
|
+
const transient = TRANSIENT_STATUSES.has(status) ||
|
|
71
|
+
(typeof err === 'object' && err !== null && (refusalSaysStillPending(err) || (status === 404 && notPendingCauseReading(err) === 'other_replica')));
|
|
72
|
+
const conflict = status === 409 ? decisionConflictOf(err) : undefined;
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
standing,
|
|
75
|
+
status,
|
|
76
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
77
|
+
...(transient ? { transient: true } : {}),
|
|
78
|
+
...(conflict !== undefined ? { conflict } : {}),
|
|
79
|
+
});
|
|
21
80
|
}
|
|
22
81
|
export function liveAskPendingReading(envelope, approvalId) {
|
|
23
82
|
if (typeof approvalId !== 'string' || approvalId === '')
|
|
@@ -17,7 +17,6 @@ export interface PolicyDenyReasonFacts {
|
|
|
17
17
|
export declare function policyDenyReason(facts: PolicyDenyReasonFacts): string;
|
|
18
18
|
export declare const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool use]";
|
|
19
19
|
export declare const ENGINE_ABORT_TOOL_RESULT = "Operation aborted";
|
|
20
|
-
export declare function toolEndOutputText(output: unknown): string | undefined;
|
|
21
20
|
export interface ParkReattachNotice {
|
|
22
21
|
attempt: number;
|
|
23
22
|
max: number;
|
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
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';
|
|
5
|
+
import { toolEndOutputText } from '../toolResultContent.js';
|
|
4
6
|
import { GATE_PARKED_ERROR_CODE } from '../engineErrorCodes.js';
|
|
5
7
|
import { readRunTerminal, runTerminalCode, runTerminalGateKind, runTerminalGateToolName } from '../runTerminal.js';
|
|
6
8
|
import { surfaceForCurrentSession, surfaceReplayedApprovalListUnreadable } from './hitlHostSurface.js';
|
|
7
|
-
import { liveCardHoldOf, supersedeLiveCard } from './approvalHolds.js';
|
|
9
|
+
import { liveCardAnsweredHow, liveCardHoldOf, supersedeLiveCard } from './approvalHolds.js';
|
|
10
|
+
import { approvalCompleteParked } from './approvalCompleteParked.js';
|
|
8
11
|
import { liveFrameCallKey } from './gateIdentity.js';
|
|
9
12
|
import { SEMA_DENIAL_KIND_KEY, hasLedgerPositionKey } from './gateLedger.js';
|
|
10
|
-
import { replayedApprovalDisposition } from './replayedApproval.js';
|
|
13
|
+
import { replayedApprovalDisposition, settledApprovalDisposition } from './replayedApproval.js';
|
|
11
14
|
import { settleWithinDeadline } from '../abortableSleep.js';
|
|
12
15
|
import { ccToolDenialKindForSettledBy } from '../gateVocabulary.js';
|
|
13
16
|
import { REJECT_MESSAGE } from '../adapt/wireShapes.js';
|
|
@@ -21,7 +24,7 @@ export function policyDenyReason(facts) {
|
|
|
21
24
|
const one = typeof facts.toolName === 'string' && SENTENCE_TOOL_NAME.test(facts.toolName) ? `this one "${facts.toolName}" call` : 'this one call';
|
|
22
25
|
const flag = typeof facts.allowRuleFlag === 'string' && SENTENCE_TOOL_NAME.test(facts.allowRuleFlag.replace(/^--?/, '')) ? ` (or ${facts.allowRuleFlag})` : '';
|
|
23
26
|
const budgetKnob = typeof facts.budgetSetting === 'string' && SENTENCE_TOOL_NAME.test(facts.budgetSetting) ? facts.budgetSetting : undefined;
|
|
24
|
-
const cause = facts.cause === 'no_allow_rule' || facts.cause === 'ask_rule' || facts.cause === 'budget' ? facts.cause : 'other';
|
|
27
|
+
const cause = facts.cause === 'no_allow_rule' || facts.cause === 'ask_rule' || facts.cause === 'engine_ask_rule' || facts.cause === 'budget' ? facts.cause : 'other';
|
|
25
28
|
if (facts.lane === 'dont_ask') {
|
|
26
29
|
const mode = 'denied by the dontAsk permission mode';
|
|
27
30
|
switch (cause) {
|
|
@@ -29,6 +32,8 @@ export function policyDenyReason(facts) {
|
|
|
29
32
|
return `${mode}: ${one} needed approval, no permission rule allowed it, and this mode refuses anything that would ask — calls that need no approval still run; an allow rule in settings${flag} can approve calls like this ahead of time`;
|
|
30
33
|
case 'ask_rule':
|
|
31
34
|
return `${mode}: an ask rule matched ${one}, and this mode refuses anything that would ask (an ask rule outranks allow rules) — narrow or remove that ask rule, or use another permission mode to answer it`;
|
|
35
|
+
case 'engine_ask_rule':
|
|
36
|
+
return `${mode}: a rule on the engine side, not one of the user's permission rules, asks about ${one}, and this mode refuses anything that would ask — the user's settings cannot approve it; use another permission mode to answer it`;
|
|
32
37
|
case 'budget':
|
|
33
38
|
return `${mode}: ${one} needed approval, and this session's automatic-approval budget is used up, so saved rules no longer approve calls${budgetKnob !== undefined ? ` — raise ${budgetKnob} before launch to change the cap` : ''}`;
|
|
34
39
|
default:
|
|
@@ -42,6 +47,8 @@ export function policyDenyReason(facts) {
|
|
|
42
47
|
return `${lane}: ${one} needed a person's approval, no permission rule answered it, and ${tail}; a matching allow rule in settings${flag} can approve calls like this ahead of time`;
|
|
43
48
|
case 'ask_rule':
|
|
44
49
|
return `${lane}: an ask rule matched ${one}, so it needed a person's answer, and ${tail}; an ask rule outranks allow rules, so narrow or remove that ask rule, or run the task interactively to answer it`;
|
|
50
|
+
case 'engine_ask_rule':
|
|
51
|
+
return `${lane}: a rule on the engine side, not one of the user's permission rules, asks about ${one}, so it needed a person's answer, and ${tail}; the user's settings cannot approve it - run the task interactively to answer it`;
|
|
45
52
|
case 'budget':
|
|
46
53
|
return `${lane}: ${one} needed a person's approval, and this session's automatic-approval budget is used up, so saved rules no longer approve calls; ${tail}${budgetKnob !== undefined ? `; raise ${budgetKnob} before launch to change the cap` : ''}`;
|
|
47
54
|
default:
|
|
@@ -71,19 +78,6 @@ function denyStampedEnd(ev, attribution) {
|
|
|
71
78
|
export const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = '[Request interrupted by user for tool use]';
|
|
72
79
|
const RUN_CANCELLED_ERROR_CODE = RUN_CANCELLED_CODE;
|
|
73
80
|
export const ENGINE_ABORT_TOOL_RESULT = 'Operation aborted';
|
|
74
|
-
export function toolEndOutputText(output) {
|
|
75
|
-
if (typeof output === 'string')
|
|
76
|
-
return output;
|
|
77
|
-
if (!Array.isArray(output))
|
|
78
|
-
return undefined;
|
|
79
|
-
const parts = [];
|
|
80
|
-
for (const b of output) {
|
|
81
|
-
const bb = b;
|
|
82
|
-
if (bb?.type === 'text' && typeof bb.text === 'string')
|
|
83
|
-
parts.push(bb.text);
|
|
84
|
-
}
|
|
85
|
-
return parts.length > 0 ? parts.join('') : undefined;
|
|
86
|
-
}
|
|
87
81
|
function isAskToolLoose(name) {
|
|
88
82
|
return typeof name === 'string' && name.replace(/[\s_-]+/g, '').toLowerCase() === 'askuserquestion';
|
|
89
83
|
}
|
|
@@ -266,48 +260,34 @@ const TOOL_END_ARMS = [
|
|
|
266
260
|
'改顺序必须同批改 TOOL_END_ARM_ORDER 并说明为什么新顺序仍然对。');
|
|
267
261
|
}
|
|
268
262
|
}
|
|
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
263
|
async function routeToolApprovalFrame(ev, ctx) {
|
|
279
264
|
const { led, deps } = ctx;
|
|
280
265
|
if (ev.type === 'tool_approval_complete') {
|
|
281
266
|
const id = ev.approvalId;
|
|
282
|
-
if (typeof id === 'string' && id !== '' && supersedeLiveCard(liveFrameCallKey(id),
|
|
267
|
+
if (typeof id === 'string' && id !== '' && supersedeLiveCard(liveFrameCallKey(id), approvalCompleteParked(ev))) {
|
|
283
268
|
hostLog('debug', `liveHitlAskWire: tool_approval_complete for ${id} arrived while its card was waiting — card withdrawn (nothing will be sent from it)`);
|
|
284
269
|
}
|
|
270
|
+
if (typeof id === 'string' && id !== '')
|
|
271
|
+
led.noteApprovalSettled(id, 'closed_by_engine');
|
|
285
272
|
return { kind: 'skip' };
|
|
286
273
|
}
|
|
287
274
|
if (!deps.respondToolApproval) {
|
|
288
275
|
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} but no respond wire — engine self-settles (TTL deny)`);
|
|
289
276
|
return { kind: 'skip' };
|
|
290
277
|
}
|
|
291
|
-
|
|
292
|
-
|
|
278
|
+
const settledHere = settledApprovalDisposition(ev, led.settledApprovals());
|
|
279
|
+
if (settledHere !== undefined || (hasLedgerPositionKey(ev) && !led.preambleApprovals().has(ev.approvalId))) {
|
|
280
|
+
const disposition = settledHere ?? (await judgeLedgerApprovalFrame(ev, ctx));
|
|
293
281
|
if (disposition.kind !== 'live') {
|
|
294
|
-
hostLog('debug',
|
|
295
|
-
|
|
296
|
-
|
|
282
|
+
hostLog('debug', disposition.kind === 'decided' && disposition.settled !== undefined
|
|
283
|
+
? `liveHitlAskWire: tool_approval frame ${ev.approvalId} repeats a question already settled on this bridge (${disposition.settled}) — not raised again, no decision sent`
|
|
284
|
+
: `liveHitlAskWire: tool_approval frame ${ev.approvalId} read back from the run event log is ${disposition.kind}` +
|
|
285
|
+
`${disposition.kind === 'unknown' ? ` (${disposition.why})` : disposition.cause !== undefined ? ` (${disposition.cause})` : ''}` +
|
|
286
|
+
' — not raised as a question, no decision sent');
|
|
297
287
|
if (disposition.kind === 'unknown' && ctx.signal?.aborted !== true)
|
|
298
288
|
surfaceReplayedApprovalListUnreadable();
|
|
299
289
|
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
|
-
}
|
|
290
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onReplayedApproval', () => deps.onReplayedApproval?.(ev, disposition));
|
|
311
291
|
}
|
|
312
292
|
return { kind: 'skip' };
|
|
313
293
|
}
|
|
@@ -317,29 +297,24 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
317
297
|
const frameCallId = !fromSubagent && typeof ev.toolCallId === 'string' && ev.toolCallId !== '' ? ev.toolCallId : undefined;
|
|
318
298
|
const gatedCallId = fromSubagent ? undefined : frameCallId ?? led.lastPendingFsCall();
|
|
319
299
|
const argsCallId = frameCallId === undefined ? gatedCallId : typeof ev.toolName === 'string' && toolNameIsFsWrite(ev.toolName) ? frameCallId : undefined;
|
|
320
|
-
const
|
|
300
|
+
const runTaskId = ctx.taskId.current;
|
|
301
|
+
const approvalIdAtLaunch = ev.approvalId;
|
|
302
|
+
const launched = surfaceToolApprovalFrameAndRespond(ev, deps.respondToolApproval, argsOfGatedStart(led, argsCallId), ctx.signal, deps.approvalLane, typeof runTaskId === 'string' && runTaskId !== '' ? { taskId: runTaskId } : undefined);
|
|
321
303
|
const hold = liveCardHoldOf(liveFrameCallKey(ev.approvalId));
|
|
322
|
-
const handled = launched.then((outcome) => settleLiveCardOutcome(ev, outcome, ctx, fromSubagent, gatedCallId), (e) => {
|
|
304
|
+
const handled = launched.then((outcome) => settleLiveCardOutcome(ev, approvalIdAtLaunch, outcome, ctx, fromSubagent, gatedCallId), (e) => {
|
|
323
305
|
hostLog('debug', `liveHitlAskWire: tool_approval card for ${ev.approvalId} failed: ${String(e).slice(0, 160)}`);
|
|
324
306
|
});
|
|
325
307
|
led.trackLiveCard(() => hold?.phase ?? 'card', handled);
|
|
326
308
|
return { kind: 'skip' };
|
|
327
309
|
}
|
|
328
|
-
function settleLiveCardOutcome(ev, outcome, ctx, fromSubagent, gatedCallId) {
|
|
310
|
+
function settleLiveCardOutcome(ev, approvalId, outcome, ctx, fromSubagent, gatedCallId) {
|
|
329
311
|
const { led, deps } = ctx;
|
|
330
312
|
const { decision } = outcome;
|
|
313
|
+
const settledHow = liveCardAnsweredHow(outcome);
|
|
314
|
+
if (settledHow !== undefined)
|
|
315
|
+
led.noteApprovalSettled(approvalId, settledHow);
|
|
331
316
|
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
|
-
}
|
|
317
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onToolApprovalOutcome', () => deps.onToolApprovalOutcome?.(ev, outcome));
|
|
343
318
|
}
|
|
344
319
|
if (decision === 'deny' && !fromSubagent) {
|
|
345
320
|
const attribution = {
|
|
@@ -374,7 +349,7 @@ async function judgeLedgerApprovalFrame(ev, ctx) {
|
|
|
374
349
|
.finally(release);
|
|
375
350
|
});
|
|
376
351
|
const approvals = got.kind === 'value' ? { read: 'ok', body: got.value } : { read: 'failed' };
|
|
377
|
-
return replayedApprovalDisposition(ev, { preamble: ctx.led.preambleApprovals(), approvals });
|
|
352
|
+
return replayedApprovalDisposition(ev, { preamble: ctx.led.preambleApprovals(), approvals, settled: ctx.led.settledApprovals() });
|
|
378
353
|
}
|
|
379
354
|
function routeToolStart(ev, callId, led) {
|
|
380
355
|
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>;
|