@sema-agent/client-core 0.95.0 → 0.97.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 +269 -0
- package/README.md +61 -31
- package/dist/adapt/arms.js +7 -1
- package/dist/adapt/toolCards.d.ts +1 -0
- package/dist/adapt/toolCards.js +16 -3
- package/dist/adapt/wireShapes.js +2 -16
- package/dist/adapter/activeRunSelfHeal.d.ts +4 -2
- package/dist/adapter/activeRunSelfHeal.js +32 -6
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
- package/dist/adapter/downstream/terminalToSdkResult.js +14 -11
- package/dist/adapter/runStream.js +30 -47
- package/dist/agentSession/backgroundView.js +2 -15
- package/dist/agentSession/engineAgentRegistryRead.js +5 -4
- package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
- package/dist/agentsWireCaps.d.ts +1 -1
- package/dist/agentsWireCaps.js +29 -12
- package/dist/approvalDecidedByCapability.d.ts +2 -0
- package/dist/approvalDecidedByCapability.js +0 -1
- package/dist/approvalsStreamLiveCapability.d.ts +2 -0
- package/dist/approvalsStreamLiveCapability.js +3 -2
- package/dist/attachmentSeatCapability.d.ts +2 -0
- package/dist/attachmentSeatCapability.js +0 -1
- package/dist/callerCwdCapability.d.ts +2 -4
- package/dist/callerCwdCapability.js +0 -7
- package/dist/centerWiringCapability.d.ts +2 -4
- package/dist/centerWiringCapability.js +4 -9
- package/dist/classifierVerdictWire.js +4 -16
- package/dist/clientSlice.js +1 -1
- package/dist/cloudConfigWireCaps.d.ts +4 -4
- package/dist/cloudConfigWireCaps.js +241 -85
- 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/decideFailureNote.js +3 -1
- package/dist/decideReceipt.js +7 -10
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/deviceExecutorManagementCapability.d.ts +2 -0
- package/dist/deviceExecutorManagementCapability.js +3 -2
- package/dist/displayUntrusted.d.ts +10 -3
- package/dist/displayUntrusted.js +371 -60
- package/dist/engineCapReader.d.ts +8 -2
- package/dist/engineCapReader.js +15 -7
- package/dist/engineCapsCache.d.ts +17 -3
- package/dist/engineCapsCache.js +268 -43
- package/dist/engineDirectory.d.ts +79 -0
- package/dist/engineDirectory.js +186 -0
- package/dist/engineErrorCodes.d.ts +17 -1
- package/dist/engineErrorCodes.js +46 -16
- package/dist/engineNoticeCodes.d.ts +17 -0
- package/dist/engineNoticeCodes.js +54 -1
- package/dist/executionLaneCapability.d.ts +2 -0
- package/dist/executionLaneCapability.js +3 -2
- package/dist/fileHistoryCaptureCapability.d.ts +16 -0
- package/dist/fileHistoryCaptureCapability.js +98 -2
- package/dist/fileHistoryLegNotice.js +4 -0
- package/dist/fleet/bgSubagentRow.d.ts +15 -0
- package/dist/fleet/bgSubagentRow.js +67 -0
- package/dist/fleet/fleetLedger.d.ts +1 -0
- package/dist/fleet/fleetLedger.js +164 -126
- package/dist/fleet/fleetProjection.d.ts +1 -0
- package/dist/fleet/fleetProjection.js +10 -3
- package/dist/fleetTaskDesc.js +4 -17
- package/dist/gateOutcome.d.ts +12 -1
- package/dist/gateOutcome.js +56 -1
- package/dist/gateVocabulary.d.ts +2 -2
- package/dist/gateVocabulary.js +4 -3
- 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 +7 -0
- package/dist/generated/settingsSchemaFns.d.ts +3 -0
- package/dist/generated/settingsSchemaFns.js +53 -0
- package/dist/generated/toolNameTables.d.ts +1 -1
- package/dist/generated/toolNameTables.js +1 -42
- package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
- package/dist/hitl/approvalEndingCause.d.ts +2 -0
- package/dist/hitl/approvalEndingCause.js +26 -21
- package/dist/hitl/approvalHolds.d.ts +16 -1
- package/dist/hitl/approvalHolds.js +52 -12
- package/dist/hitl/askGateWire.js +1 -0
- package/dist/hitl/askParkRowRouting.js +1 -1
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/automaticAnswerVerdict.js +13 -10
- package/dist/hitl/decisionConflict.js +4 -5
- package/dist/hitl/decisionRefusal.d.ts +8 -0
- package/dist/hitl/decisionRefusal.js +55 -14
- package/dist/hitl/frameRouter.d.ts +3 -1
- package/dist/hitl/frameRouter.js +58 -39
- package/dist/hitl/gateLedger.d.ts +0 -5
- package/dist/hitl/gateLedger.js +0 -21
- package/dist/hitl/hitlBridge.d.ts +6 -2
- package/dist/hitl/hitlBridge.js +46 -11
- 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/parkResolver.d.ts +1 -0
- package/dist/hitl/parkResolver.js +14 -56
- package/dist/hitl/parkRowBirthWait.d.ts +1 -0
- package/dist/hitl/parkRowBirthWait.js +2 -2
- package/dist/hitl/planReviewWire.d.ts +1 -0
- package/dist/hitl/planReviewWire.js +98 -20
- package/dist/hitl/replayedApproval.d.ts +3 -3
- package/dist/hitl/replayedApproval.js +21 -16
- package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
- package/dist/hitl/sessionPolicyDeliverable.js +17 -17
- package/dist/hitl/sessionPolicyWire.d.ts +2 -0
- package/dist/hitl/sessionPolicyWire.js +15 -3
- package/dist/hitl/standingRows.d.ts +19 -0
- package/dist/hitl/standingRows.js +148 -0
- package/dist/hitl/suspendedReopen.js +2 -1
- package/dist/hitl/toolApprovalWire.d.ts +22 -10
- package/dist/hitl/toolApprovalWire.js +286 -47
- package/dist/hookMatcherVerdict.d.ts +1 -9
- package/dist/hookMatcherVerdict.js +21 -61
- package/dist/hooksWireCaps.js +2 -2
- package/dist/hostCallbackGuard.d.ts +1 -0
- package/dist/hostCallbackGuard.js +6 -0
- package/dist/index.d.ts +26 -12
- package/dist/index.js +20 -10
- package/dist/liveModelCatalog.d.ts +13 -0
- package/dist/liveModelCatalog.js +56 -0
- package/dist/mcpProbeCapability.d.ts +2 -4
- package/dist/mcpProbeCapability.js +3 -8
- package/dist/mcpWireCaps.d.ts +1 -0
- package/dist/mcpWireCaps.js +4 -1
- package/dist/memoryComplianceCapability.d.ts +2 -4
- package/dist/memoryComplianceCapability.js +3 -8
- package/dist/memoryOriginCapability.d.ts +2 -4
- package/dist/memoryOriginCapability.js +3 -8
- package/dist/memorySpecWire.js +2 -2
- package/dist/modelIdentityResolvability.d.ts +2 -2
- package/dist/modelIdentityResolvability.js +14 -5
- package/dist/noticePresentation.d.ts +68 -0
- package/dist/noticePresentation.js +247 -0
- package/dist/outputStyleSeatCapability.d.ts +2 -1
- package/dist/outputStyleSeatCapability.js +0 -4
- package/dist/ownKey.d.ts +2 -0
- package/dist/ownKey.js +18 -0
- package/dist/peerFrames.js +13 -9
- package/dist/peerLaneCapability.d.ts +2 -0
- package/dist/peerLaneCapability.js +3 -2
- package/dist/permissionModesCapability.d.ts +2 -4
- package/dist/permissionModesCapability.js +0 -7
- package/dist/permissionRuleIssue.js +0 -3
- package/dist/permissionRulesWriteCapability.d.ts +2 -0
- package/dist/permissionRulesWriteCapability.js +3 -2
- package/dist/permissionsAllowGrantCapability.d.ts +2 -0
- package/dist/permissionsAllowGrantCapability.js +4 -2
- package/dist/permissionsDeclaration.d.ts +2 -0
- package/dist/permissionsDeclaration.js +72 -59
- 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 -9
- package/dist/promptBlockedCopy.d.ts +5 -0
- package/dist/promptBlockedCopy.js +35 -0
- 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/request/taskRequest.d.ts +1 -4
- package/dist/request/taskRequest.js +74 -19
- package/dist/resumeRefusalCopy.js +9 -25
- package/dist/retryStatus.d.ts +9 -1
- package/dist/retryStatus.js +9 -2
- package/dist/rewindArchiveCapability.d.ts +3 -13
- package/dist/rewindArchiveCapability.js +12 -58
- package/dist/rewindArchiveReading.d.ts +17 -0
- package/dist/rewindArchiveReading.js +55 -0
- package/dist/ruleCompileVerdict.d.ts +9 -1
- package/dist/ruleCompileVerdict.js +29 -4
- package/dist/runTerminal.d.ts +48 -5
- package/dist/runTerminal.js +215 -12
- package/dist/sandboxWire.js +13 -0
- package/dist/sdkWireTransit.d.ts +2 -1
- package/dist/sdkWireTransit.js +1 -0
- package/dist/sealedKeyCapability.d.ts +2 -4
- package/dist/sealedKeyCapability.js +0 -7
- package/dist/seam.js +4 -2
- package/dist/seatContract.d.ts +76 -11
- package/dist/seatContract.js +222 -27
- package/dist/sessionPolicyCapability.d.ts +2 -0
- package/dist/sessionPolicyCapability.js +3 -2
- package/dist/sessionReadRedactionCapability.d.ts +2 -4
- package/dist/sessionReadRedactionCapability.js +0 -7
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +66 -0
- package/dist/sessionSyncFileHistoryCapability.d.ts +2 -1
- package/dist/sessionSyncFileHistoryCapability.js +3 -5
- package/dist/skillToolListsCapability.d.ts +2 -1
- package/dist/skillToolListsCapability.js +3 -5
- package/dist/sqlEngineCapability.d.ts +2 -0
- package/dist/sqlEngineCapability.js +4 -3
- package/dist/steerPriorityCapability.d.ts +2 -1
- package/dist/steerPriorityCapability.js +3 -5
- package/dist/storePostureCapability.d.ts +2 -4
- package/dist/storePostureCapability.js +4 -9
- package/dist/subagent/engineSubagentSteer.js +7 -5
- package/dist/subagentContentStore.js +4 -15
- package/dist/supportFloor.d.ts +1 -0
- package/dist/supportFloor.js +13 -9
- package/dist/systemReminderTag.d.ts +1 -0
- package/dist/systemReminderTag.js +8 -0
- package/dist/toolHistoryBadTurn.js +3 -2
- package/dist/toolHistoryMismatch.d.ts +0 -3
- package/dist/toolHistoryMismatch.js +3 -7
- package/dist/toolResult.d.ts +12 -1
- package/dist/toolResult.js +55 -1
- package/dist/toolResultContent.d.ts +2 -0
- package/dist/toolResultContent.js +63 -0
- package/dist/usageAnalytics.js +40 -16
- package/dist/userInvokedSkillsCapability.d.ts +2 -1
- package/dist/userInvokedSkillsCapability.js +3 -5
- package/dist/webSearchBackendCapability.d.ts +2 -0
- package/dist/webSearchBackendCapability.js +3 -2
- package/dist/wireErrorTriage.d.ts +2 -0
- package/dist/wireErrorTriage.js +91 -38
- package/dist/wireFailureShape.d.ts +20 -3
- package/dist/wireFailureShape.js +128 -72
- package/dist/workflowsJournalCapability.d.ts +2 -0
- package/dist/workflowsJournalCapability.js +3 -2
- package/dist/writeProtectionCapability.d.ts +2 -0
- package/dist/writeProtectionCapability.js +3 -2
- package/docs/INTEGRATION-CLIENTS.md +1784 -20
- package/package.json +5 -5
- package/dist/engineRefusedToolName.d.ts +0 -1
- package/dist/engineRefusedToolName.js +0 -8
|
@@ -46,7 +46,7 @@ export interface PlanReviewOutcome {
|
|
|
46
46
|
export declare const PLAN_REVIEW_MODE_AFTER_WORDS: readonly ["default", "acceptEdits"];
|
|
47
47
|
export type PlanReviewModeAfter = (typeof PLAN_REVIEW_MODE_AFTER_WORDS)[number];
|
|
48
48
|
export declare function isPlanReviewModeAfter(v: string | undefined): v is PlanReviewModeAfter;
|
|
49
|
-
export declare const GATE_FAILURE_CODES: readonly ["no_pending", "binding_mismatch", "wrong_gate", "bad_plan_edit", "bad_plan_mode", "empty_answer"];
|
|
49
|
+
export declare const GATE_FAILURE_CODES: readonly ["no_pending", "binding_mismatch", "wrong_gate", "bad_plan_edit", "bad_plan_mode", "empty_answer", "binding_missing"];
|
|
50
50
|
export type HitlSafetyCode = (typeof GATE_FAILURE_CODES)[number];
|
|
51
51
|
export type HitlFailureStage = 'fetch' | 'input' | 'card' | 'decide' | 'orchestration';
|
|
52
52
|
export declare const NO_APPROVAL_CARD_PORT_REASON = "no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)";
|
|
@@ -76,6 +76,10 @@ export type WireErrorCodeReading = {
|
|
|
76
76
|
readonly state: 'unreadable';
|
|
77
77
|
};
|
|
78
78
|
export declare function readWireErrorCodeReading(e: unknown): WireErrorCodeReading;
|
|
79
|
+
export declare function readWireCodes(e: unknown): {
|
|
80
|
+
readonly errorCode?: string;
|
|
81
|
+
readonly blockedBy?: string;
|
|
82
|
+
};
|
|
79
83
|
export declare function __setDecideTimeoutRetryBudgetForTests(ms?: number): void;
|
|
80
84
|
export declare class DecideTransportRetryExhaustedError extends Error {
|
|
81
85
|
readonly attempts: number;
|
|
@@ -116,7 +120,7 @@ export type FindPendingOutcome = {
|
|
|
116
120
|
} | {
|
|
117
121
|
ok: false;
|
|
118
122
|
reason: string;
|
|
119
|
-
code?: 'no_pending';
|
|
123
|
+
code?: 'no_pending' | 'wrong_gate';
|
|
120
124
|
};
|
|
121
125
|
export declare function findPendingForTask(client: HitlClientLike, taskId: string, matches: (toolName: string | undefined) => boolean, opts?: {
|
|
122
126
|
signal?: AbortSignal;
|
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -3,9 +3,9 @@ import { eventSeq } from '../adapter/types.js';
|
|
|
3
3
|
import { hostLog } from '../host.js';
|
|
4
4
|
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
5
5
|
import { abortableSleep } from '../abortableSleep.js';
|
|
6
|
-
import { readOwnDataValue } from '../ownKey.js';
|
|
7
|
-
import { THROWN_SLOT_UNREADABLE, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
8
|
-
import {
|
|
6
|
+
import { ownDataField, readOwnDataValue } from '../ownKey.js';
|
|
7
|
+
import { THROWN_SLOT_UNREADABLE, readBlockedBy, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
8
|
+
import { RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
|
|
9
9
|
import { suspendedReopenOf } from './suspendedReopen.js';
|
|
10
10
|
export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
|
|
11
11
|
const MAX_DENY_REASON_CHARS = 4096;
|
|
@@ -29,14 +29,15 @@ export const PLAN_REVIEW_MODE_AFTER_WORDS = Object.freeze(['default', 'acceptEdi
|
|
|
29
29
|
export function isPlanReviewModeAfter(v) {
|
|
30
30
|
return v !== undefined && PLAN_REVIEW_MODE_AFTER_WORDS.includes(v);
|
|
31
31
|
}
|
|
32
|
-
export const GATE_FAILURE_CODES = [
|
|
32
|
+
export const GATE_FAILURE_CODES = Object.freeze([
|
|
33
33
|
'no_pending',
|
|
34
34
|
'binding_mismatch',
|
|
35
35
|
'wrong_gate',
|
|
36
36
|
'bad_plan_edit',
|
|
37
37
|
'bad_plan_mode',
|
|
38
38
|
'empty_answer',
|
|
39
|
-
|
|
39
|
+
'binding_missing',
|
|
40
|
+
]);
|
|
40
41
|
export const NO_APPROVAL_CARD_PORT_REASON = 'no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)';
|
|
41
42
|
export const APPROVAL_CARD_PORT_KEY_MISMATCH_REASON = 'no approval card port installed for the default session key, but a non-default sessionKey has one — likely a keyed-install/default-read mismatch (installApprovalCardPortFor vs the zero-arg default-key read), not a missing install';
|
|
42
43
|
export const NO_QUESTION_OVERLAY_REASON = 'no question overlay mounted (print/non-REPL mode)';
|
|
@@ -54,7 +55,13 @@ export class HitlSafetyError extends Error {
|
|
|
54
55
|
export function hitlSafetyCodeOf(e) {
|
|
55
56
|
if (!thrownIsInstanceOf(e, HitlSafetyError))
|
|
56
57
|
return undefined;
|
|
57
|
-
|
|
58
|
+
let raw;
|
|
59
|
+
try {
|
|
60
|
+
raw = ownDataField(e, 'code');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
58
65
|
return typeof raw === 'string' && GATE_FAILURE_CODES.includes(raw) ? raw : undefined;
|
|
59
66
|
}
|
|
60
67
|
const _gateCurrentPendingReadonlyPin = true;
|
|
@@ -87,11 +94,28 @@ export function readWireErrorCode(e) {
|
|
|
87
94
|
export function readWireErrorCodeReading(e) {
|
|
88
95
|
if (e === null || e === undefined)
|
|
89
96
|
return { state: 'absent' };
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
let raw;
|
|
98
|
+
try {
|
|
99
|
+
if (typeof e === 'object' || typeof e === 'function') {
|
|
100
|
+
raw = ownDataField(e, 'errorCode');
|
|
101
|
+
if (raw === undefined && !Object.hasOwn(e, 'errorCode'))
|
|
102
|
+
void e.errorCode;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
92
106
|
return { state: 'unreadable' };
|
|
107
|
+
}
|
|
93
108
|
return typeof raw === 'string' && raw.length > 0 ? { state: 'code', code: raw } : { state: 'absent' };
|
|
94
109
|
}
|
|
110
|
+
export function readWireCodes(e) {
|
|
111
|
+
const reading = readWireErrorCodeReading(e);
|
|
112
|
+
if (reading.state !== 'code')
|
|
113
|
+
return {};
|
|
114
|
+
if (reading.code !== RESUME_POLICY_FOLD_CODE)
|
|
115
|
+
return { errorCode: reading.code };
|
|
116
|
+
const folded = readBlockedBy(e);
|
|
117
|
+
return folded.kind === 'named' ? { errorCode: reading.code, blockedBy: folded.code } : { errorCode: reading.code };
|
|
118
|
+
}
|
|
95
119
|
const DECIDE_TRANSPORT_RETRY_BACKOFF_MS = 750;
|
|
96
120
|
const DECIDE_RETRY_BACKOFF_MAX_MS = 5_000;
|
|
97
121
|
const DECIDE_ATTEMPT_TIMEOUT_CAP_MS = 60_000;
|
|
@@ -224,10 +248,21 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
|
|
|
224
248
|
hostLog('debug', `hitlBridge: approvals.list failed: ${t.log}`);
|
|
225
249
|
return { ok: false, reason: `approvals.list failed: ${t.reason}` };
|
|
226
250
|
}
|
|
227
|
-
const
|
|
251
|
+
const readings = rows.map((r) => ({ r, foreign: askParkForeignGateKind(r) }));
|
|
252
|
+
const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
|
|
228
253
|
const identityRow = callId !== undefined
|
|
229
254
|
? decidable.find((r) => r.taskId === taskId && (r.boundCallId === callId || r.toolCallId === callId))
|
|
230
255
|
: undefined;
|
|
256
|
+
const identityForeign = callId !== undefined && identityRow === undefined
|
|
257
|
+
? readings.find((x) => x.r.taskId === taskId && (x.r.boundCallId === callId || x.r.toolCallId === callId) && x.foreign !== null)
|
|
258
|
+
: undefined;
|
|
259
|
+
if (identityForeign !== undefined) {
|
|
260
|
+
return {
|
|
261
|
+
ok: false,
|
|
262
|
+
reason: `the pending row this park names (call ${callId}) is parked on a '${String(identityForeign.foreign)}' gate — not an approval this leg decides; refusing to decide a different row`,
|
|
263
|
+
code: 'wrong_gate',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
231
266
|
const identityConflicts = identityRow !== undefined && !identityAccepts(identityRow);
|
|
232
267
|
const pool = identityConflicts ? decidable.filter((r) => identityAccepts(r)) : decidable;
|
|
233
268
|
const pending = (identityConflicts ? undefined : identityRow) ??
|
|
@@ -286,7 +321,7 @@ export class HitlBridge {
|
|
|
286
321
|
if (p.boundCallId === undefined || p.boundInputHash === undefined) {
|
|
287
322
|
throw new HitlSafetyError(`pending checkpoint ${p.id ?? '<no-id>'} is missing D-1 binding fields ` +
|
|
288
323
|
`(boundCallId=${String(p.boundCallId)}, boundInputHash=${String(p.boundInputHash)}) — ` +
|
|
289
|
-
'pre-D-1 workers are no longer supported (server 5.x requires the verbatim two-tuple echo)', '
|
|
324
|
+
'pre-D-1 workers are no longer supported (server 5.x requires the verbatim two-tuple echo)', 'binding_missing');
|
|
290
325
|
}
|
|
291
326
|
return { boundCallId: p.boundCallId, boundInputHash: p.boundInputHash };
|
|
292
327
|
}
|
|
@@ -500,7 +535,7 @@ function bindingMismatchReading(e) {
|
|
|
500
535
|
const name = readThrownSlot(e, 'name');
|
|
501
536
|
const family = (code.state === 'code' && code.code === 'approval_binding_mismatch') || name === 'ApprovalBindingMismatchError';
|
|
502
537
|
if (family)
|
|
503
|
-
return
|
|
538
|
+
return 'mismatch';
|
|
504
539
|
if (code.state === 'unreadable')
|
|
505
540
|
return 'unreadable';
|
|
506
541
|
return 'not';
|
|
@@ -6,6 +6,7 @@ export interface LocalAllowRuleDeps {
|
|
|
6
6
|
bashRuleContentHasDangerousBarePrefix(ruleContent: string): boolean;
|
|
7
7
|
isDangerousBashPermission(toolName: string, ruleContent: string): boolean;
|
|
8
8
|
isDangerousPowerShellPermission(toolName: string, ruleContent: string): boolean;
|
|
9
|
+
onFailure?(log: string): void;
|
|
9
10
|
}
|
|
10
11
|
export type LocalAllowRuleParse = {
|
|
11
12
|
ok: true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
|
|
2
2
|
const BASH_TOOL_NAME_FOR_RULES = 'Bash';
|
|
3
3
|
export function parseLocalAllowRule(rule, expectedToolName, deps) {
|
|
4
4
|
if (typeof rule !== 'string' || rule === '') {
|
|
@@ -9,7 +9,7 @@ export function parseLocalAllowRule(rule, expectedToolName, deps) {
|
|
|
9
9
|
value = deps.parseRule(rule);
|
|
10
10
|
}
|
|
11
11
|
catch (e) {
|
|
12
|
-
return { ok: false, error: `rule text did not parse (${
|
|
12
|
+
return { ok: false, error: `rule text did not parse (${reasonOfThrownWithSink(e, (log) => deps.onFailure?.(log))})` };
|
|
13
13
|
}
|
|
14
14
|
if (typeof value.toolName !== 'string' || value.toolName === '') {
|
|
15
15
|
return { ok: false, error: 'rule text carries no tool name' };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'budget' | 'other';
|
|
1
|
+
export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'engine_ask_rule' | 'budget' | 'other';
|
|
2
2
|
export interface RuleLadderNoAllowFacts {
|
|
3
3
|
readonly allowed?: boolean;
|
|
4
4
|
readonly denied?: boolean;
|
|
5
5
|
readonly askRuleMatched?: boolean;
|
|
6
6
|
readonly budgetExhausted?: boolean;
|
|
7
7
|
readonly noRuleMatched?: boolean;
|
|
8
|
+
readonly noAskRuleMatched?: boolean;
|
|
8
9
|
}
|
|
9
10
|
export declare function noAllowCauseOf(card: object | null | undefined, ladder: RuleLadderNoAllowFacts | null | undefined): NoAllowCause | undefined;
|
|
@@ -18,8 +18,9 @@ export function noAllowCauseOf(card, ladder) {
|
|
|
18
18
|
if (ladderSays(ladder, 'budgetExhausted'))
|
|
19
19
|
return 'budget';
|
|
20
20
|
const standing = automaticAnswerVerdict(card, 'standing_rule');
|
|
21
|
-
if (standing.kind === 'must_ask' && (standing.askClass === 'ask_rule' || standing.origin === 'ask_rule'))
|
|
22
|
-
return 'ask_rule';
|
|
21
|
+
if (standing.kind === 'must_ask' && (standing.askClass === 'ask_rule' || standing.origin === 'ask_rule')) {
|
|
22
|
+
return ladderSays(ladder, 'noAskRuleMatched') || ladderSays(ladder, 'noRuleMatched') ? 'engine_ask_rule' : 'ask_rule';
|
|
23
|
+
}
|
|
23
24
|
if (ladderSays(ladder, 'noRuleMatched') && standing.kind === 'may_answer')
|
|
24
25
|
return 'no_allow_rule';
|
|
25
26
|
return 'other';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { putOwnKey, readOwnDataValue } from '../ownKey.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { DecideTransportRetryExhaustedError, GATE_FAILURE_CODES, HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, hitlSafetyCodeOf, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireCodes, } from './hitlBridge.js';
|
|
3
|
+
import { DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, RESUME_REOPEN_CODES, RESUME_RETRY_LATER_CODES, isRefusedParkCode, refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
4
|
+
import { RESUME_REFUSAL_CODES } from '../resumeRefusalCopy.js';
|
|
5
|
+
import { readDecisionRefusalAsk } from './decisionRefusal.js';
|
|
6
6
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
7
7
|
import { hostLog } from '../host.js';
|
|
8
8
|
import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
|
|
@@ -35,67 +35,25 @@ function alreadyResolvedReasonToken(reason) {
|
|
|
35
35
|
}
|
|
36
36
|
export function isAlreadyResolvedFailure(failure) {
|
|
37
37
|
try {
|
|
38
|
-
|
|
38
|
+
const r = readDecisionRefusalAsk(failure);
|
|
39
|
+
return r.reading === 'settled' || (r.reading === 'not_settled' && (r.gateMoved || r.wordingHint));
|
|
39
40
|
}
|
|
40
41
|
catch {
|
|
41
42
|
return false;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
const ALREADY_RESOLVED_TYPED_ERROR_NAMES = Object.freeze([ApprovalStaleError.name, NotFoundError.name]);
|
|
45
|
-
const PARKED_RESUME_STARTUP_TYPED_ERROR_NAME = ParkedResumeStartupError.name;
|
|
46
|
-
function decideFailureFactsOf(failure) {
|
|
47
|
-
if (typeof failure !== 'object' || failure === null)
|
|
48
|
-
return undefined;
|
|
49
|
-
const o = failure;
|
|
50
|
-
const ec = o.errorCode;
|
|
51
|
-
const errorCode = typeof ec === 'string' && ec.length > 0 ? ec : undefined;
|
|
52
|
-
const retriable = refusalSaysStillPending(failure);
|
|
53
|
-
if (Object.hasOwn(failure, 'kind') && o.kind === 'failed') {
|
|
54
|
-
const safety = isGateFailureCode(o.safetyCode) ? o.safetyCode : undefined;
|
|
55
|
-
const code = isGateFailureCode(o.code) ? o.code : safety;
|
|
56
|
-
return { code, safetySignal: safety !== undefined, errorCode, retriable, typedErrorName: undefined, reason: o.reason };
|
|
57
|
-
}
|
|
58
|
-
const name = o.name;
|
|
59
|
-
return {
|
|
60
|
-
code: isGateFailureCode(o.code) ? o.code : undefined,
|
|
61
|
-
safetySignal: isHitlSafetyErrorLike(failure),
|
|
62
|
-
errorCode,
|
|
63
|
-
retriable,
|
|
64
|
-
typedErrorName: typeof name === 'string' ? name : undefined,
|
|
65
|
-
reason: String(failure),
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function alreadyResolvedFailureVerdict(failure) {
|
|
69
|
-
const f = decideFailureFactsOf(failure);
|
|
70
|
-
if (f === undefined)
|
|
71
|
-
return false;
|
|
72
|
-
if (f.code !== undefined)
|
|
73
|
-
return f.code === 'no_pending';
|
|
74
|
-
if (f.safetySignal)
|
|
75
|
-
return false;
|
|
76
|
-
if (isDecideAlreadyResolvedCode(f.errorCode))
|
|
77
|
-
return true;
|
|
78
|
-
if (f.typedErrorName !== undefined && ALREADY_RESOLVED_TYPED_ERROR_NAMES.includes(f.typedErrorName))
|
|
79
|
-
return true;
|
|
80
|
-
if (f.typedErrorName === PARKED_RESUME_STARTUP_TYPED_ERROR_NAME && !isDecideParkMovedCode(f.errorCode))
|
|
81
|
-
return false;
|
|
82
|
-
if (isCodeClassifiedGateFailure(f.errorCode, f.retriable))
|
|
83
|
-
return false;
|
|
84
|
-
if (isDecideParkMovedCode(f.errorCode))
|
|
85
|
-
return true;
|
|
86
|
-
if (f.errorCode === ASK_GONE)
|
|
87
|
-
return true;
|
|
88
|
-
return isAlreadyResolvedGateReason(f.reason);
|
|
89
|
-
}
|
|
90
45
|
export function isGateStandingErrorCode(code, retriable) {
|
|
91
46
|
return (code === RESUME_POLICY_FOLD_CODE ||
|
|
92
47
|
code === 'corrupt' ||
|
|
93
|
-
(code
|
|
48
|
+
(isRefusedParkCode(code) && retriable === true) ||
|
|
49
|
+
(typeof code === 'string' && RESUME_REOPEN_CODES.includes(code) && retriable === true) ||
|
|
94
50
|
code === DECIDE_WORKFLOW_PARKS_UNREADABLE ||
|
|
95
51
|
code === DECIDE_WORKFLOW_PARK_FORKED);
|
|
96
52
|
}
|
|
97
53
|
export function isCodeClassifiedGateFailure(code, retriable) {
|
|
98
|
-
return isGateStandingErrorCode(code, retriable) ||
|
|
54
|
+
return (isGateStandingErrorCode(code, retriable) ||
|
|
55
|
+
code === PARKED_RESUME_STARTUP_FAILED ||
|
|
56
|
+
(typeof code === 'string' && (RESUME_RETRY_LATER_CODES.includes(code) || RESUME_REFUSAL_CODES.includes(code))));
|
|
99
57
|
}
|
|
100
58
|
export function toAnsweredOutput(questions, answer) {
|
|
101
59
|
const answers = {};
|
|
@@ -193,7 +151,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
193
151
|
catch (e) {
|
|
194
152
|
const code = hitlSafetyCodeOf(e);
|
|
195
153
|
const currentPending = readDecideCurrentPending(e);
|
|
196
|
-
const
|
|
154
|
+
const wireCodes = readWireCodes(e);
|
|
197
155
|
const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
|
|
198
156
|
const failureTexts = wireFailureTextsOf(e);
|
|
199
157
|
hostLog('debug', `liveHitlAskWire: decide failed: ${failureTexts.log}`);
|
|
@@ -205,7 +163,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
205
163
|
...(code !== undefined ? { code } : {}),
|
|
206
164
|
...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
|
|
207
165
|
...(currentPending !== undefined ? { currentPending } : {}),
|
|
208
|
-
...
|
|
166
|
+
...wireCodes,
|
|
209
167
|
...(wireRetriable ? { retriable: true } : {}),
|
|
210
168
|
};
|
|
211
169
|
}
|
|
@@ -235,7 +193,7 @@ async function surfaceParkGate(park, ctx, gatedCallId, onPresented) {
|
|
|
235
193
|
...(onPresented ? { onPresented } : {}),
|
|
236
194
|
...(deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : {}),
|
|
237
195
|
...(decidedByReading !== undefined ? { approvalDecidedBy: decidedByReading } : {}),
|
|
238
|
-
}, taskId, argsByCallOf(led), ctx.signal, gatedCallId);
|
|
196
|
+
}, taskId, argsByCallOf(led), ctx.signal, gatedCallId, park.toolName);
|
|
239
197
|
const outcomeDenied = 'denied' in outcome ? outcome.denied : undefined;
|
|
240
198
|
if (outcome.kind === 'decided' && outcomeDenied === true && outcome.gatedCallId) {
|
|
241
199
|
const denySettledBy = 'denySettledBy' in outcome ? outcome.denySettledBy : undefined;
|
|
@@ -35,5 +35,6 @@ export interface ParkRowBirthWaitDeps<Row> {
|
|
|
35
35
|
now?: () => number;
|
|
36
36
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
37
37
|
signal?: AbortSignal;
|
|
38
|
+
onFailure?: (log: string) => void;
|
|
38
39
|
}
|
|
39
40
|
export declare function waitForParkRowBirth<Row>(deps: ParkRowBirthWaitDeps<Row>): Promise<ParkRowWaitVerdict<Row>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { abortableSleep } from '../abortableSleep.js';
|
|
2
|
-
import {
|
|
2
|
+
import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
|
|
3
3
|
function parkRowPollDelay(ms, signal) {
|
|
4
4
|
return abortableSleep(ms, signal ?? new AbortController().signal);
|
|
5
5
|
}
|
|
@@ -17,7 +17,7 @@ export async function waitForParkRowBirth(deps) {
|
|
|
17
17
|
return await deps.probe(attempt, signal);
|
|
18
18
|
}
|
|
19
19
|
catch (e) {
|
|
20
|
-
return { kind: 'unborn', reason: `probe threw: ${
|
|
20
|
+
return { kind: 'unborn', reason: `probe threw: ${reasonOfThrownWithSink(e, (log) => deps.onFailure?.(log))}` };
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
const probeWithinRemaining = async (attempt, remainingMs) => {
|
|
@@ -13,7 +13,7 @@ import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
|
|
|
13
13
|
import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
|
|
14
14
|
import { notePlanReviewAnswered, notePlanReviewAnsweredFor, planReviewArmedKeyFor, registerArmedGateFor, waitForGateArmedFor, wasGateArmedFor, } from './armedGateRegistry.js';
|
|
15
15
|
import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
16
|
-
import { PLAN_REVIEW_GATE_KIND } from '../adapter/activeRunSelfHeal.js';
|
|
16
|
+
import { PLAN_REVIEW_GATE_KIND, durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
|
|
17
17
|
import { readRunTerminal, runTerminalGateKind } from '../runTerminal.js';
|
|
18
18
|
const PLAN_REVIEW_RESUME_TIMEOUT_MS = 6 * 60 * 60_000;
|
|
19
19
|
export const PLAN_REVIEW_APPROVE_LABEL = 'Yes, approve and run the plan';
|
|
@@ -130,6 +130,18 @@ function pausedCheckpointIdOf(carrier) {
|
|
|
130
130
|
return undefined;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
function pausedGateKindOf(carrier) {
|
|
134
|
+
try {
|
|
135
|
+
const read = readRunTerminal(carrier);
|
|
136
|
+
if (read === null || read.kind !== 'paused')
|
|
137
|
+
return undefined;
|
|
138
|
+
const kind = runTerminalGateKind(read);
|
|
139
|
+
return typeof kind === 'string' && kind.length > 0 ? kind : undefined;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
133
145
|
const armedPlanReviewTaskIds = new Set();
|
|
134
146
|
const offerModeAfterByTask = new Map();
|
|
135
147
|
export function _resetArmedPlanReviewsForTest() {
|
|
@@ -470,7 +482,53 @@ export function __resetPlanReviewDecisionLatchForTests() {
|
|
|
470
482
|
planReviewDecisionOps.length = 0;
|
|
471
483
|
}
|
|
472
484
|
export async function decidePlanReview(taskId, decision, permissionModeAfter, opts) {
|
|
473
|
-
return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
|
|
485
|
+
return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, parkedGateIdentityOf(opts));
|
|
486
|
+
}
|
|
487
|
+
function parkedGateIdentityOf(opts) {
|
|
488
|
+
try {
|
|
489
|
+
return opts !== null && typeof opts === 'object' ? pausedCheckpointIdOf(opts.parkedResult) : undefined;
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function planReviewSettledVerdict(r) {
|
|
496
|
+
const status = r.postStatus ?? r.bodyStatus;
|
|
497
|
+
const gateMoved = r.gateBefore !== undefined && r.gateAfter !== undefined && r.gateAfter !== r.gateBefore;
|
|
498
|
+
if (r.reparked)
|
|
499
|
+
return status !== 'completed' && !gateMoved ? { effect: 'still_parked', proven: true } : { effect: 'unconfirmed', proven: false };
|
|
500
|
+
if (status === 'completed')
|
|
501
|
+
return { effect: 'took_effect', proven: false };
|
|
502
|
+
const newGateHasCard = () => {
|
|
503
|
+
const leg = durableDecideLegOf({ gateKind: r.gateKindAfter ?? null });
|
|
504
|
+
return leg === 'approval' || leg === 'plan_review';
|
|
505
|
+
};
|
|
506
|
+
if (status === 'suspended' && r.postStatus === 'suspended' && newGateHasCard())
|
|
507
|
+
return { effect: 'advanced', proven: false };
|
|
508
|
+
if (status === 'needs_review' && r.postStatus === 'needs_review' && r.gateBefore !== undefined && r.gateAfter !== undefined) {
|
|
509
|
+
if (r.gateAfter === r.gateBefore)
|
|
510
|
+
return { effect: 'still_parked', proven: true };
|
|
511
|
+
if (newGateHasCard())
|
|
512
|
+
return { effect: 'advanced', proven: false };
|
|
513
|
+
}
|
|
514
|
+
return { effect: 'unconfirmed', proven: false };
|
|
515
|
+
}
|
|
516
|
+
function bodyWordOf(body, key) {
|
|
517
|
+
try {
|
|
518
|
+
const v = body !== null && typeof body === 'object' ? body[key] : undefined;
|
|
519
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function bodySaysReparked(body) {
|
|
526
|
+
try {
|
|
527
|
+
return body !== null && typeof body === 'object' && body.retriable === true;
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
474
532
|
}
|
|
475
533
|
async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, gateBefore) {
|
|
476
534
|
if (typeof taskId === 'string' && planReviewDecisionOps.some((op) => op.taskId === taskId && op.phase === 'posting')) {
|
|
@@ -586,6 +644,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
586
644
|
hostLog('debug', `planReviewWire: ${decision} → ok (dispatch #${dispatchNo}) ${JSON.stringify(body).slice(0, 200)}`);
|
|
587
645
|
let postStatus;
|
|
588
646
|
let gateAfter;
|
|
647
|
+
let gateKindAfter;
|
|
589
648
|
try {
|
|
590
649
|
const probe = clients.repull();
|
|
591
650
|
if (probe) {
|
|
@@ -601,35 +660,54 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
601
660
|
else {
|
|
602
661
|
post = (await probe.runs.get(taskId));
|
|
603
662
|
}
|
|
604
|
-
postStatus =
|
|
663
|
+
postStatus = bodyWordOf(post, 'status');
|
|
605
664
|
gateAfter = pausedCheckpointIdOf(post?.result);
|
|
665
|
+
gateKindAfter = pausedGateKindOf(post?.result);
|
|
606
666
|
}
|
|
607
667
|
}
|
|
608
668
|
catch (e) {
|
|
609
669
|
hostLog('debug', `planReviewWire: post-decide status re-pull failed (fail-soft): ${String(e)}`);
|
|
610
670
|
}
|
|
611
|
-
const
|
|
671
|
+
const bodyStatus = bodyWordOf(body, 'status');
|
|
672
|
+
const effective = postStatus ?? bodyStatus;
|
|
612
673
|
statusSeen = effective;
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
else if (effective === 'needs_review') {
|
|
619
|
-
effect = 'still_parked';
|
|
620
|
-
stillParkedProven = postStatus === 'needs_review' && gateBefore !== undefined && gateAfter !== undefined && gateAfter === gateBefore;
|
|
674
|
+
const verdict = planReviewSettledVerdict({ postStatus, bodyStatus, reparked: bodySaysReparked(body), gateBefore, gateAfter, gateKindAfter });
|
|
675
|
+
effect = verdict.effect;
|
|
676
|
+
stillParkedProven = verdict.proven;
|
|
677
|
+
const code = bodyWordOf(body, 'errorCode');
|
|
678
|
+
if (effect === 'still_parked' && postStatus === 'needs_review' && !bodySaysReparked(body)) {
|
|
621
679
|
outcome =
|
|
622
680
|
`The plan_review ${decision} returned HTTP 200 but the session is STILL locked on the same review gate ` +
|
|
623
681
|
`(post-decide status: needs_review). The decision did NOT take effect (known engine issue, RB-471 family). ` +
|
|
624
682
|
`Tell the user plainly that the ${decision} did not go through; the reliable exits today are approving the plan or cancelling the task.`;
|
|
625
683
|
}
|
|
626
|
-
else if (
|
|
627
|
-
|
|
628
|
-
|
|
684
|
+
else if (effect === 'still_parked') {
|
|
685
|
+
outcome =
|
|
686
|
+
`The plan_review ${decision} was NOT applied: the engine could not continue the task and put the plan back to wait for a decision` +
|
|
687
|
+
`${code !== undefined ? ` (engine code: ${code})` : ''}. Tell the user plainly that the ${decision} did not go through.`;
|
|
688
|
+
}
|
|
689
|
+
else if (effect === 'advanced') {
|
|
690
|
+
outcome = planReviewAdvancedOutcome(decision, effective === 'needs_review' ? 'needs_review' : 'suspended');
|
|
691
|
+
}
|
|
692
|
+
else if (effect === 'took_effect') {
|
|
693
|
+
outcome =
|
|
694
|
+
`The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — final status: ${effective} ` +
|
|
695
|
+
`(${postStatus !== undefined ? 'post-decide re-checked: task left the review gate' : "from the decision's own reply; the post-decide re-check was unavailable"}).`;
|
|
696
|
+
}
|
|
697
|
+
else if (effective === 'needs_review' && (gateBefore === undefined || gateAfter === undefined || postStatus !== 'needs_review')) {
|
|
698
|
+
outcome =
|
|
699
|
+
`The plan_review ${decision} returned HTTP 200 and the task is waiting at a plan review gate again (post-decide status: needs_review), ` +
|
|
700
|
+
`but it could not be told whether this is the same review (the ${decision} did not take effect) or a new plan written after it (it did).`;
|
|
701
|
+
}
|
|
702
|
+
else if (effective === 'suspended' || effective === 'needs_review') {
|
|
703
|
+
outcome =
|
|
704
|
+
`The plan_review ${decision} was accepted by the engine (HTTP 200) and the task is paused again (post-decide status: ${effective}), ` +
|
|
705
|
+
`${gateKindAfter !== undefined ? `at a '${gateKindAfter}' pause that is not an approval card` : 'but the pause it stopped at was not reported'} — what the ${decision} led to could not be confirmed.`;
|
|
629
706
|
}
|
|
630
707
|
else if (effective !== undefined) {
|
|
631
|
-
|
|
632
|
-
|
|
708
|
+
outcome =
|
|
709
|
+
`The plan_review ${decision} was accepted by the engine (HTTP 200), but the task then reported status ${effective}` +
|
|
710
|
+
`${code !== undefined ? ` (engine code: ${code})` : ''} — not a completion; what the ${decision} led to could not be confirmed.`;
|
|
633
711
|
}
|
|
634
712
|
else {
|
|
635
713
|
outcome = `The plan_review ${decision} was accepted by the engine (HTTP 200) but its effect is unconfirmed — final status: unknown (post-decide verification unavailable). Do not treat this as ${decision === 'approve' ? 'an approval that resumed the task' : 'a discarded plan'}.`;
|
|
@@ -678,10 +756,10 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
678
756
|
outcome = `This ${decision} was not applied. ${corruptStoredRowContent(where)}`;
|
|
679
757
|
}
|
|
680
758
|
else if (typeof status === 'number') {
|
|
681
|
-
const
|
|
682
|
-
hostLog('debug', `planReviewWire: ${decision} → ${status} ${
|
|
759
|
+
const failureTexts = wireFailureTextsOf(e);
|
|
760
|
+
hostLog('debug', `planReviewWire: ${decision} → ${status} ${failureTexts.log.slice(0, 200)}`);
|
|
683
761
|
effect = (status >= 400 && status < 500) || (typeof refusedCode === 'string' && isUnavailableFaceCode(refusedCode)) ? 'not_applied' : 'unconfirmed';
|
|
684
|
-
outcome = `${`The plan_review decision failed: HTTP ${status} ${
|
|
762
|
+
outcome = `${`The plan_review decision failed: HTTP ${status} ${failureTexts.reason}`.trim()}${decideFailureNote(e)}`;
|
|
685
763
|
}
|
|
686
764
|
else if (noAnswer === 'time-limit' || noAnswer === 'deadline') {
|
|
687
765
|
hostLog('debug', `planReviewWire: ${decision} sent but no answer within the request time limit (task ${taskId})`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ApprovalSettledHow } from './
|
|
1
|
+
import { type ApprovalSettledHow } from './approvalHolds.js';
|
|
2
2
|
import { type ApprovalsListEnvelope, type CrashConvergedRow } from './crashConverged.js';
|
|
3
3
|
export type ReplayedApprovalDisposition = {
|
|
4
4
|
readonly kind: 'live';
|
|
@@ -20,10 +20,10 @@ export interface ReplayPendingBasis {
|
|
|
20
20
|
} | {
|
|
21
21
|
readonly read: 'failed';
|
|
22
22
|
};
|
|
23
|
-
readonly settled?: ReadonlyMap<string, ApprovalSettledHow>;
|
|
24
23
|
}
|
|
25
|
-
export declare function settledApprovalDisposition(frame: object
|
|
24
|
+
export declare function settledApprovalDisposition(frame: object): ReplayedApprovalDisposition | undefined;
|
|
26
25
|
export declare function replayedApprovalDisposition(frame: object, basis: ReplayPendingBasis): ReplayedApprovalDisposition;
|
|
26
|
+
export declare function replayedApprovalClosesCardFrame(disposition: ReplayedApprovalDisposition | null | undefined): boolean;
|
|
27
27
|
export declare function replayedApprovalSentence(disposition: ReplayedApprovalDisposition | undefined, opts?: {
|
|
28
28
|
readonly toolName?: string;
|
|
29
29
|
}): string | undefined;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hasLedgerPositionKey } from './gateLedger.js';
|
|
2
|
+
import { anyApprovalSettled, settledApprovalOf } from './approvalHolds.js';
|
|
2
3
|
import { readLivePendingRows } from './livePendingAsk.js';
|
|
3
4
|
import { projectCrashConverged } from './crashConverged.js';
|
|
4
5
|
import { quoteUntrustedForSentence } from '../displayUntrusted.js';
|
|
6
|
+
import { readOwnDataValue } from '../ownKey.js';
|
|
5
7
|
const live = (basis) => Object.freeze({ kind: 'live', basis });
|
|
6
8
|
const unknown = (why) => Object.freeze({ kind: 'unknown', why });
|
|
7
9
|
const decided = (row) => Object.freeze(row === undefined
|
|
@@ -22,15 +24,12 @@ function crashConvergedFactsOf(body, approvalId) {
|
|
|
22
24
|
}
|
|
23
25
|
return undefined;
|
|
24
26
|
}
|
|
25
|
-
export function settledApprovalDisposition(frame
|
|
27
|
+
export function settledApprovalDisposition(frame) {
|
|
26
28
|
try {
|
|
27
|
-
if (
|
|
29
|
+
if (!anyApprovalSettled())
|
|
28
30
|
return undefined;
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
return undefined;
|
|
32
|
-
const how = settled.get(id);
|
|
33
|
-
return typeof how === 'string' && APPROVAL_SETTLED_HOWS.includes(how) ? decidedSettled(how) : undefined;
|
|
31
|
+
const entry = settledApprovalOf(approvalIdOf(frame));
|
|
32
|
+
return entry === undefined ? undefined : decidedSettled(entry.how);
|
|
34
33
|
}
|
|
35
34
|
catch {
|
|
36
35
|
return undefined;
|
|
@@ -47,14 +46,7 @@ export function replayedApprovalDisposition(frame, basis) {
|
|
|
47
46
|
catch {
|
|
48
47
|
return unknown('unreadable_frame');
|
|
49
48
|
}
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
settledBasis = typeof basis === 'object' && basis !== null ? basis.settled : undefined;
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
settledBasis = undefined;
|
|
56
|
-
}
|
|
57
|
-
const early = settledApprovalDisposition(frame, settledBasis);
|
|
49
|
+
const early = settledApprovalDisposition(frame);
|
|
58
50
|
if (early !== undefined)
|
|
59
51
|
return early;
|
|
60
52
|
try {
|
|
@@ -162,6 +154,19 @@ const SETTLED_PHRASE = Object.freeze({
|
|
|
162
154
|
});
|
|
163
155
|
const settledRepeat = (what, how) => `${sentenceInitial(what)} was ${SETTLED_PHRASE[how]} earlier, so this repeated copy of it was not raised as a new question and no decision was sent for it.`;
|
|
164
156
|
const decidedPlain = (what) => `${sentenceInitial(what)} is no longer listed by the engine as waiting for a decision, so no decision was sent for it.`;
|
|
157
|
+
export function replayedApprovalClosesCardFrame(disposition) {
|
|
158
|
+
try {
|
|
159
|
+
if (typeof disposition !== 'object' || disposition === null)
|
|
160
|
+
return false;
|
|
161
|
+
if (readOwnDataValue(disposition, 'kind') !== 'decided')
|
|
162
|
+
return false;
|
|
163
|
+
const cause = readOwnDataValue(disposition, 'cause');
|
|
164
|
+
return typeof cause === 'string' && Object.hasOwn(STOPPED_BY_CAUSE, cause);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
165
170
|
const unconfirmed = (what) => `Whether ${what} is still waiting for a decision could not be confirmed, so no decision was sent for it.`;
|
|
166
171
|
function guardedGet(o, key) {
|
|
167
172
|
try {
|
|
@@ -15,7 +15,6 @@ export interface SessionPolicyDeliverability {
|
|
|
15
15
|
export interface SessionPolicyDeliverableOptions {
|
|
16
16
|
readonly roster?: ToolRosterView;
|
|
17
17
|
}
|
|
18
|
-
export { isEngineRefusedToolName } from '../engineRefusedToolName.js';
|
|
19
18
|
export declare function sessionPolicyDeliverable(behavior: 'deny' | 'ask', rules: readonly string[], opts?: SessionPolicyDeliverableOptions): SessionPolicyDeliverability;
|
|
20
19
|
export declare function sessionPolicyWithheldNotice(why: SessionPolicyWithheldWhy): string;
|
|
21
20
|
export declare function legacyToolNameFailureNoteOf(code: string | undefined): string | undefined;
|