@sema-agent/client-core 0.93.0 → 0.94.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 +261 -0
- package/README.md +31 -18
- package/dist/adapt/arms.js +8 -5
- package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
- package/dist/agentSession/engineSessionBackgroundStop.js +2 -1
- package/dist/agentsWireCaps.d.ts +15 -0
- package/dist/agentsWireCaps.js +111 -1
- package/dist/approvalReachWire.d.ts +18 -0
- package/dist/approvalReachWire.js +58 -0
- package/dist/approvalsStreamLiveCapability.js +16 -11
- package/dist/callerCwdCapability.d.ts +16 -0
- package/dist/callerCwdCapability.js +47 -0
- package/dist/controlRouter.js +3 -10
- package/dist/coreValuePorts.d.ts +64 -1
- package/dist/coreValuePorts.js +30 -0
- package/dist/detachWire.d.ts +1 -4
- package/dist/detachWire.js +1 -1
- package/dist/deviceExecutorManagementCapability.js +39 -34
- package/dist/deviceFlowPolling.d.ts +5 -0
- package/dist/deviceFlowPolling.js +28 -0
- package/dist/displayUntrusted.js +652 -21
- package/dist/engineErrorCodes.d.ts +5 -1
- package/dist/engineErrorCodes.js +9 -12
- package/dist/engineNoticeCodes.d.ts +22 -2
- package/dist/engineNoticeCodes.js +73 -6
- package/dist/engineRefusedToolName.d.ts +1 -0
- package/dist/engineRefusedToolName.js +8 -0
- package/dist/executionLaneCapability.js +14 -9
- package/dist/fileHistoryCaptureCapability.js +14 -9
- package/dist/fileHistoryLegNotice.d.ts +11 -0
- package/dist/fileHistoryLegNotice.js +50 -13
- package/dist/fleet/fleetLedger.js +5 -0
- package/dist/gateOutcome.d.ts +2 -1
- package/dist/gateOutcome.js +3 -0
- package/dist/gateVocabulary.js +1 -1
- package/dist/generated/engineAskFaceTables.d.ts +3 -0
- package/dist/generated/engineAskFaceTables.js +12 -0
- package/dist/generated/engineFactTables.d.ts +4 -2
- package/dist/generated/engineFactTables.js +13 -0
- package/dist/generated/engineNoticeTables.d.ts +5 -0
- package/dist/generated/engineNoticeTables.js +7 -0
- package/dist/generated/sdkFactTables.d.ts +3 -0
- package/dist/generated/sdkFactTables.js +17 -0
- package/dist/generated/settingsSchemaTables.d.ts +3 -0
- package/dist/generated/settingsSchemaTables.js +3 -0
- package/dist/headlessPermissionModeWire.d.ts +2 -2
- package/dist/headlessPermissionModeWire.js +11 -21
- package/dist/hitl/approvalHolds.d.ts +36 -0
- package/dist/hitl/approvalHolds.js +89 -0
- package/dist/hitl/approvalOutcomeNote.js +2 -1
- package/dist/hitl/approvalResolution.d.ts +3 -2
- package/dist/hitl/approvalResolution.js +21 -0
- package/dist/hitl/approvalsFeed.js +5 -0
- package/dist/hitl/askGateWire.js +3 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +8 -2
- package/dist/hitl/automaticAnswerVerdict.js +69 -21
- package/dist/hitl/decisionRefusal.d.ts +19 -0
- package/dist/hitl/decisionRefusal.js +63 -0
- package/dist/hitl/frameRouter.d.ts +10 -0
- package/dist/hitl/frameRouter.js +59 -3
- package/dist/hitl/gateLedger.d.ts +3 -0
- package/dist/hitl/gateLedger.js +17 -0
- package/dist/hitl/hitlBridge.d.ts +2 -2
- package/dist/hitl/hitlBridge.js +8 -10
- package/dist/hitl/hitlHostSurface.d.ts +6 -0
- package/dist/hitl/hitlHostSurface.js +25 -4
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +9 -0
- package/dist/hitl/noAllowCause.js +26 -0
- package/dist/hitl/parkResolver.js +6 -3
- package/dist/hitl/parkRowBirthWait.js +2 -1
- package/dist/hitl/persistedRulesWire.d.ts +6 -1
- package/dist/hitl/persistedRulesWire.js +12 -4
- package/dist/hitl/planReviewWire.d.ts +0 -1
- package/dist/hitl/planReviewWire.js +30 -9
- package/dist/hitl/sessionPolicyDeliverable.d.ts +1 -1
- package/dist/hitl/sessionPolicyDeliverable.js +3 -8
- package/dist/hitl/sessionPolicyWire.js +1 -1
- package/dist/hitl/toolApprovalWire.d.ts +37 -2
- package/dist/hitl/toolApprovalWire.js +392 -13
- package/dist/humanTurnRead.d.ts +15 -1
- package/dist/humanTurnRead.js +28 -2
- package/dist/index.d.ts +18 -6
- package/dist/index.js +14 -5
- package/dist/limitsWire.d.ts +0 -1
- package/dist/limitsWire.js +2 -10
- package/dist/liveModelCatalog.d.ts +14 -0
- package/dist/liveModelCatalog.js +61 -0
- package/dist/mcpProbeCapability.js +14 -9
- package/dist/mcpReconnect.js +13 -8
- package/dist/memoryComplianceCapability.js +15 -10
- package/dist/memoryOriginCapability.js +15 -10
- package/dist/modelIdentityResolvability.d.ts +4 -2
- package/dist/modelIdentityResolvability.js +31 -4
- package/dist/outputStyleSeatCapability.d.ts +16 -0
- package/dist/outputStyleSeatCapability.js +47 -0
- package/dist/ownKey.d.ts +1 -0
- package/dist/ownKey.js +10 -0
- package/dist/peerLaneCapability.js +16 -11
- package/dist/permissionRuleIssue.js +1 -1
- package/dist/permissionRulesWriteCapability.js +16 -11
- package/dist/permissionsAllowGrantCapability.d.ts +2 -0
- package/dist/permissionsAllowGrantCapability.js +38 -16
- package/dist/permissionsDeclaration.d.ts +10 -1
- package/dist/permissionsDeclaration.js +97 -15
- package/dist/protocolSegment.d.ts +1 -0
- package/dist/protocolSegment.js +13 -1
- package/dist/request/taskRequest.d.ts +11 -3
- package/dist/request/taskRequest.js +13 -1
- package/dist/resultFrameKeys.d.ts +4 -0
- package/dist/resultFrameKeys.js +13 -0
- package/dist/resumeRefusalCopy.js +6 -16
- package/dist/retryStatus.js +6 -4
- package/dist/rewindArchiveCapability.js +13 -8
- package/dist/{hitl/rosterMountedNames.d.ts → rosterMountedNames.d.ts} +1 -1
- package/dist/runCancelContext.js +2 -1
- package/dist/sealedKeyCapability.js +19 -14
- package/dist/seatContract.d.ts +33 -5
- package/dist/seatContract.js +143 -2
- package/dist/sessionPolicyCapability.js +15 -10
- package/dist/sessionReadRedactionCapability.d.ts +16 -0
- package/dist/sessionReadRedactionCapability.js +47 -0
- package/dist/sessionSyncFileHistoryCapability.js +14 -9
- package/dist/skillToolListsCapability.js +14 -9
- package/dist/sqlEngineCapability.js +18 -13
- package/dist/steerPriorityCapability.js +21 -16
- package/dist/subagent/agentStateDetail.d.ts +1 -0
- package/dist/subagent/agentStateDetail.js +41 -0
- package/dist/subagent/engineCompactWire.d.ts +15 -0
- package/dist/subagent/engineCompactWire.js +75 -13
- package/dist/supportFloor.js +1 -1
- package/dist/taskRequestWords.d.ts +2 -4
- package/dist/taskRequestWords.js +3 -2
- package/dist/types/engineState.d.ts +1 -1
- package/dist/userAttachmentEnvelope.d.ts +22 -1
- package/dist/userAttachmentEnvelope.js +90 -2
- package/dist/userInvokedSkillsCapability.js +14 -9
- package/dist/webSearchBackendCapability.js +21 -16
- package/dist/wireErrorTriage.d.ts +11 -0
- package/dist/wireErrorTriage.js +35 -0
- package/dist/wireFailureShape.d.ts +6 -0
- package/dist/wireFailureShape.js +30 -3
- package/dist/wireRefusalCopy.d.ts +6 -1
- package/dist/wireRefusalCopy.js +22 -8
- package/dist/wiringInstructionsAttribution.d.ts +10 -0
- package/dist/wiringInstructionsAttribution.js +67 -0
- package/dist/workflowClient.js +22 -1
- package/dist/workflowsJournalCapability.js +14 -9
- package/dist/writeProtectionCapability.js +28 -23
- package/docs/INTEGRATION-CLIENTS.md +817 -45
- package/package.json +5 -5
- package/dist/askSurvivesStandingAnswer.d.ts +0 -13
- package/dist/askSurvivesStandingAnswer.js +0 -53
- package/dist/hitl/askSurvivesPosture.d.ts +0 -20
- package/dist/hitl/askSurvivesPosture.js +0 -48
- /package/dist/{hitl/rosterMountedNames.js → rosterMountedNames.js} +0 -0
|
@@ -28,6 +28,7 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
28
28
|
"permission.ask_released_by_mode",
|
|
29
29
|
"permission.read_deny_unapplied",
|
|
30
30
|
"permission.rule_store_unavailable",
|
|
31
|
+
"permission.rule_row_dropped",
|
|
31
32
|
"permission.allowed_tool_not_granted",
|
|
32
33
|
"peer.inbound_disposition",
|
|
33
34
|
"peer.held_settled",
|
|
@@ -116,6 +117,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
|
|
|
116
117
|
"permission.ask_released_by_mode": "user",
|
|
117
118
|
"permission.read_deny_unapplied": "user",
|
|
118
119
|
"permission.rule_store_unavailable": "user",
|
|
120
|
+
"permission.rule_row_dropped": "user",
|
|
119
121
|
"permission.allowed_tool_not_granted": "user",
|
|
120
122
|
"peer.inbound_disposition": "user",
|
|
121
123
|
"peer.held_settled": "user",
|
|
@@ -210,6 +212,7 @@ export const CORE_NOTICE_MINTER = Object.freeze({
|
|
|
210
212
|
"permission.ask_released_by_mode": "engine",
|
|
211
213
|
"permission.read_deny_unapplied": "engine",
|
|
212
214
|
"permission.rule_store_unavailable": "engine",
|
|
215
|
+
"permission.rule_row_dropped": "engine",
|
|
213
216
|
"permission.allowed_tool_not_granted": "engine",
|
|
214
217
|
"peer.inbound_disposition": "engine",
|
|
215
218
|
"peer.held_settled": "engine",
|
|
@@ -278,3 +281,7 @@ export const CORE_TOOL_TURN_INCOMPLETE_CAUSES = Object.freeze([
|
|
|
278
281
|
"bad_frame",
|
|
279
282
|
"missing_result_synthesized",
|
|
280
283
|
]);
|
|
284
|
+
export const CORE_USER_INPUT_UNDRAINED_FAMILIES = Object.freeze({
|
|
285
|
+
"steer": Object.freeze({ code: "task.user_steer_undrained", countKey: "steer" }),
|
|
286
|
+
"followUp": Object.freeze({ code: "task.user_followup_undrained", countKey: "followUp" }),
|
|
287
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const SDK_ABORT_CAUSES: readonly ["client_disconnected", "ask_park_grace_exhausted", "cancel_verb"];
|
|
2
|
+
export declare const SDK_STATUS_DERIVED_COARSE_CODES: readonly ["request.rejected", "auth.unauthorized", "auth.forbidden", "not_found.resource", "request.payload_too_large", "request.unprocessable", "limit.rate_exceeded", "capability.unavailable", "internal.error"];
|
|
3
|
+
export declare const SDK_DENY_REASON_ROUTING_FAILURE: "routing_failure_fail_closed";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const SDK_ABORT_CAUSES = Object.freeze([
|
|
2
|
+
"client_disconnected",
|
|
3
|
+
"ask_park_grace_exhausted",
|
|
4
|
+
"cancel_verb",
|
|
5
|
+
]);
|
|
6
|
+
export const SDK_STATUS_DERIVED_COARSE_CODES = Object.freeze([
|
|
7
|
+
"request.rejected",
|
|
8
|
+
"auth.unauthorized",
|
|
9
|
+
"auth.forbidden",
|
|
10
|
+
"not_found.resource",
|
|
11
|
+
"request.payload_too_large",
|
|
12
|
+
"request.unprocessable",
|
|
13
|
+
"limit.rate_exceeded",
|
|
14
|
+
"capability.unavailable",
|
|
15
|
+
"internal.error",
|
|
16
|
+
]);
|
|
17
|
+
export const SDK_DENY_REASON_ROUTING_FAILURE = "routing_failure_fail_closed";
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export declare const SETTINGS_ENGINE_OWNED_HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreCompact", "PostCompact", "Stop", "StopFailure", "UserPromptSubmit"];
|
|
2
2
|
export declare const SETTINGS_SECRET_LITERAL_RE: RegExp;
|
|
3
|
+
export declare const SETTINGS_PRIVATE_KEY_PEM_HEADER: RegExp;
|
|
4
|
+
export declare const SETTINGS_DEVICE_CODE_TTL_SECONDS = 900;
|
|
5
|
+
export declare const SETTINGS_DEVICE_POLL_INTERVAL_SECONDS = 5;
|
|
3
6
|
export declare const SETTINGS_TIER_GROUP_NAME_RE: RegExp;
|
|
4
7
|
export declare const SETTINGS_BUDGET_NUMBER_KEYS: readonly ["maxBudgetUsd", "tpmLimit", "rpmLimit", "maxParallelRequests", "maxRunCostUsd", "maxIterations"];
|
|
5
8
|
export declare const SETTINGS_BUDGET_STRING_KEYS: readonly ["mode", "budgetDuration", "enforcement"];
|
|
@@ -10,6 +10,9 @@ export const SETTINGS_ENGINE_OWNED_HOOK_EVENTS = Object.freeze([
|
|
|
10
10
|
"UserPromptSubmit",
|
|
11
11
|
]);
|
|
12
12
|
export const SETTINGS_SECRET_LITERAL_RE = Object.freeze(/^(sk-|gh[opus]_|github_pat_|xox[baprse]-|xapp-|bearer\s|basic\s|eyJ[A-Za-z0-9_-]{10,}\.|AKIA[0-9A-Z]{12,}|AIza[0-9A-Za-z_-]{20,}|-----BEGIN[A-Z0-9 ]*PRIVATE KEY)/i);
|
|
13
|
+
export const SETTINGS_PRIVATE_KEY_PEM_HEADER = Object.freeze(/-----BEGIN[A-Z0-9 ]*PRIVATE KEY/i);
|
|
14
|
+
export const SETTINGS_DEVICE_CODE_TTL_SECONDS = 900;
|
|
15
|
+
export const SETTINGS_DEVICE_POLL_INTERVAL_SECONDS = 5;
|
|
13
16
|
export const SETTINGS_TIER_GROUP_NAME_RE = Object.freeze(/^[a-z0-9][a-z0-9._-]*$/i);
|
|
14
17
|
export const SETTINGS_BUDGET_NUMBER_KEYS = Object.freeze([
|
|
15
18
|
"maxBudgetUsd",
|
|
@@ -3,10 +3,10 @@ import { type PermissionModesReading } from './permissionModesCapability.js';
|
|
|
3
3
|
import { type EnvLike } from './hostEnv.js';
|
|
4
4
|
export declare const HEADLESS_PERMISSION_MODE_ENV = "SEMA_HEADLESS_PERMISSION_MODE";
|
|
5
5
|
export declare const HEADLESS_PERMISSION_WIRE_MODES: readonly PermissionMode[];
|
|
6
|
-
export declare function normalizeShellPermissionModeForWire(mode: string | undefined,
|
|
6
|
+
export declare function normalizeShellPermissionModeForWire(mode: string | undefined, _permissionModes?: PermissionModesReading): string | undefined;
|
|
7
7
|
export type HeadlessPermissionResolution = {
|
|
8
8
|
fields: PermissionModeField;
|
|
9
|
-
source: 'flag' | 'env' | 'default'
|
|
9
|
+
source: 'flag' | 'env' | 'default';
|
|
10
10
|
warning?: string;
|
|
11
11
|
};
|
|
12
12
|
export declare function permissionModeFromArgv(argv: readonly string[]): string | undefined;
|
|
@@ -6,15 +6,10 @@ import { PERMISSION_MODE_WORDS } from './taskRequestWords.js';
|
|
|
6
6
|
import { readOwnDataValue } from './ownKey.js';
|
|
7
7
|
export const HEADLESS_PERMISSION_MODE_ENV = 'SEMA_HEADLESS_PERMISSION_MODE';
|
|
8
8
|
export const HEADLESS_PERMISSION_WIRE_MODES = [...PERMISSION_MODE_WORDS];
|
|
9
|
-
const SHELL_INTERNAL_MODE_TO_WIRE = new Map([
|
|
10
|
-
|
|
11
|
-
['dontAsk', 'default'],
|
|
12
|
-
]);
|
|
13
|
-
export function normalizeShellPermissionModeForWire(mode, permissionModes) {
|
|
9
|
+
const SHELL_INTERNAL_MODE_TO_WIRE = new Map([['manual', 'default']]);
|
|
10
|
+
export function normalizeShellPermissionModeForWire(mode, _permissionModes) {
|
|
14
11
|
if (mode === undefined)
|
|
15
12
|
return undefined;
|
|
16
|
-
if (mode === 'dontAsk' && permissionModeAdmitted(permissionModes, mode))
|
|
17
|
-
return mode;
|
|
18
13
|
return SHELL_INTERNAL_MODE_TO_WIRE.get(mode) ?? mode;
|
|
19
14
|
}
|
|
20
15
|
export function permissionModeFromArgv(argv) {
|
|
@@ -35,8 +30,9 @@ export function resolveHeadlessPermissionMode(argv, env = hostEnv(), permissionM
|
|
|
35
30
|
const flagMode = permissionModeFromArgv(argv);
|
|
36
31
|
if (flagMode !== undefined) {
|
|
37
32
|
const snap = flagMode === 'dontAsk' ? dontAskSnapshot(permissionModes) : undefined;
|
|
38
|
-
const normalized = normalizeShellPermissionModeForWire(flagMode
|
|
39
|
-
|
|
33
|
+
const normalized = normalizeShellPermissionModeForWire(flagMode);
|
|
34
|
+
const projected = permissionModeField(normalized, { stampDefault: true, permissionModes: snap });
|
|
35
|
+
return { fields: projected.permissionMode !== undefined ? projected : permissionModeField('default', { stampDefault: true }), source: 'flag' };
|
|
40
36
|
}
|
|
41
37
|
const raw = env[HEADLESS_PERMISSION_MODE_ENV]?.trim();
|
|
42
38
|
if (raw === undefined || raw === '') {
|
|
@@ -44,21 +40,15 @@ export function resolveHeadlessPermissionMode(argv, env = hostEnv(), permissionM
|
|
|
44
40
|
}
|
|
45
41
|
if (HEADLESS_PERMISSION_WIRE_MODES.includes(raw)) {
|
|
46
42
|
const snap = raw === 'dontAsk' ? dontAskSnapshot(permissionModes) : undefined;
|
|
47
|
-
|
|
48
|
-
const downgraded = {
|
|
49
|
-
fields: permissionModeField('default', { stampDefault: true }),
|
|
50
|
-
source: 'env',
|
|
51
|
-
};
|
|
52
|
-
if (!warnedInvalidEnv) {
|
|
53
|
-
warnedInvalidEnv = true;
|
|
54
|
-
downgraded.warning = `sema: ${HEADLESS_PERMISSION_MODE_ENV}="dontAsk" is only sent once this client knows the engine accepts that mode, and ${dontAskWithheldClause(permissionModes)} — this run uses "default"`;
|
|
55
|
-
}
|
|
56
|
-
return downgraded;
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
43
|
+
const resolution = {
|
|
59
44
|
fields: permissionModeField(raw, { stampDefault: true, permissionModes: snap }),
|
|
60
45
|
source: 'env',
|
|
61
46
|
};
|
|
47
|
+
if (raw === 'dontAsk' && resolution.fields.permissionMode !== 'dontAsk' && !warnedInvalidEnv) {
|
|
48
|
+
warnedInvalidEnv = true;
|
|
49
|
+
resolution.warning = `sema: ${HEADLESS_PERMISSION_MODE_ENV}="dontAsk" is only sent once this client knows the engine accepts that mode, and ${dontAskWithheldClause(permissionModes)} — this run uses "default"`;
|
|
50
|
+
}
|
|
51
|
+
return resolution;
|
|
62
52
|
}
|
|
63
53
|
const resolution = {
|
|
64
54
|
fields: permissionModeField('default', { stampDefault: true }),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FsApprovalOutcome, ToolApprovalFrameOutcome } from './toolApprovalWire.js';
|
|
2
|
+
export interface DurableDecisionHold {
|
|
3
|
+
readonly lane: 'durable';
|
|
4
|
+
readonly outcome: Promise<FsApprovalOutcome>;
|
|
5
|
+
presented: boolean;
|
|
6
|
+
readonly interrupt: () => void;
|
|
7
|
+
readonly cardSignal: AbortSignal;
|
|
8
|
+
trailing?: Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface LiveCardHold {
|
|
11
|
+
readonly lane: 'live';
|
|
12
|
+
readonly outcome: Promise<ToolApprovalFrameOutcome>;
|
|
13
|
+
phase: 'card' | 'respond';
|
|
14
|
+
superseded?: {
|
|
15
|
+
readonly parked: boolean;
|
|
16
|
+
};
|
|
17
|
+
readonly cardSignal: AbortSignal;
|
|
18
|
+
readonly interrupt: () => void;
|
|
19
|
+
trailing?: Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export type ApprovalHold = DurableDecisionHold | LiveCardHold;
|
|
22
|
+
export interface ApprovalDecisionHoldTable {
|
|
23
|
+
readonly inFlight: Map<string, ApprovalHold>;
|
|
24
|
+
readonly settled: Map<string, DurableSettledHold>;
|
|
25
|
+
readonly openReads: Set<number>;
|
|
26
|
+
clock: number;
|
|
27
|
+
}
|
|
28
|
+
export interface DurableSettledHold {
|
|
29
|
+
readonly at: number;
|
|
30
|
+
readonly hold: DurableDecisionHold;
|
|
31
|
+
}
|
|
32
|
+
export declare const approvalDecisionHolds: ApprovalDecisionHoldTable;
|
|
33
|
+
export declare function liveCardHoldOf(key: string): LiveCardHold | undefined;
|
|
34
|
+
export declare function holdLiveCard(key: string, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<ToolApprovalFrameOutcome>): Promise<ToolApprovalFrameOutcome>;
|
|
35
|
+
export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ToolApprovalFrameOutcome>;
|
|
36
|
+
export declare function supersedeLiveCard(key: string, parked: boolean): boolean;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { hostLog } from '../host.js';
|
|
2
|
+
export const approvalDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), clock: 0 };
|
|
3
|
+
export function liveCardHoldOf(key) {
|
|
4
|
+
const h = approvalDecisionHolds.inFlight.get(key);
|
|
5
|
+
return h !== undefined && h.lane === 'live' ? h : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function holdLiveCard(key, callerSignal, present) {
|
|
8
|
+
const h = approvalDecisionHolds;
|
|
9
|
+
const cut = new AbortController();
|
|
10
|
+
let settled = false;
|
|
11
|
+
const interrupt = () => {
|
|
12
|
+
if (!settled && !cut.signal.aborted)
|
|
13
|
+
cut.abort();
|
|
14
|
+
};
|
|
15
|
+
const unsubscribe = followCallerSignal(callerSignal, interrupt);
|
|
16
|
+
let resolveOutcome = () => { };
|
|
17
|
+
let rejectOutcome = () => { };
|
|
18
|
+
const outcome = new Promise((resolve, reject) => {
|
|
19
|
+
resolveOutcome = resolve;
|
|
20
|
+
rejectOutcome = reject;
|
|
21
|
+
});
|
|
22
|
+
const hold = { lane: 'live', outcome, phase: 'card', cardSignal: cut.signal, interrupt };
|
|
23
|
+
h.inFlight.set(key, hold);
|
|
24
|
+
const release = () => {
|
|
25
|
+
if (h.inFlight.get(key) === hold)
|
|
26
|
+
h.inFlight.delete(key);
|
|
27
|
+
};
|
|
28
|
+
const settle = (give) => {
|
|
29
|
+
settled = true;
|
|
30
|
+
unsubscribe();
|
|
31
|
+
const trailing = hold.trailing;
|
|
32
|
+
if (trailing === undefined) {
|
|
33
|
+
release();
|
|
34
|
+
give();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
give();
|
|
38
|
+
trailing.then(release, release);
|
|
39
|
+
};
|
|
40
|
+
let run;
|
|
41
|
+
try {
|
|
42
|
+
run = present(hold);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
run = Promise.reject(e);
|
|
46
|
+
}
|
|
47
|
+
run.then((o) => settle(() => resolveOutcome(o)), (e) => settle(() => rejectOutcome(e)));
|
|
48
|
+
return outcome;
|
|
49
|
+
}
|
|
50
|
+
export async function joinLiveCard(hold, signal) {
|
|
51
|
+
const unsubscribe = followCallerSignal(signal, hold.interrupt);
|
|
52
|
+
try {
|
|
53
|
+
const o = await hold.outcome;
|
|
54
|
+
return { ...o, joinedInFlight: true };
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
unsubscribe();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function followCallerSignal(signal, onAbort) {
|
|
61
|
+
if (signal === undefined || signal === null)
|
|
62
|
+
return () => { };
|
|
63
|
+
try {
|
|
64
|
+
if (signal.aborted === true) {
|
|
65
|
+
onAbort();
|
|
66
|
+
return () => { };
|
|
67
|
+
}
|
|
68
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return () => { };
|
|
72
|
+
}
|
|
73
|
+
return () => {
|
|
74
|
+
try {
|
|
75
|
+
signal.removeEventListener('abort', onAbort);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
hostLog('debug', `approvalHolds: could not detach from the caller's abort signal (${String(e).slice(0, 120)}) — the listener is one-shot and inert after settle`);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function supersedeLiveCard(key, parked) {
|
|
83
|
+
const hold = liveCardHoldOf(key);
|
|
84
|
+
if (hold === undefined || hold.phase !== 'card' || hold.superseded !== undefined)
|
|
85
|
+
return false;
|
|
86
|
+
hold.superseded = Object.freeze({ parked });
|
|
87
|
+
hold.interrupt();
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
@@ -17,7 +17,8 @@ function detailOf(r) {
|
|
|
17
17
|
}
|
|
18
18
|
export function approvalOutcomeNoteOf(frame, outcome) {
|
|
19
19
|
const r = approvalResolutionOf(outcome);
|
|
20
|
-
const
|
|
20
|
+
const cause = approvalNotSentCause(r);
|
|
21
|
+
const retracted = cause === 'retracted';
|
|
21
22
|
const detail = detailOf(r);
|
|
22
23
|
return {
|
|
23
24
|
approvalId: frame.approvalId,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { DecideReceiptView } from '../decideReceipt.js';
|
|
2
2
|
import type { HitlSafetyCode } from './hitlBridge.js';
|
|
3
|
-
import type { FsApprovalOutcome, ToolApprovalFrameOutcome, ToolApprovalRespondAckView, ToolApprovalRespondDecision } from './toolApprovalWire.js';
|
|
3
|
+
import type { FsApprovalOutcome, ToolApprovalFrameOutcome, ToolApprovalRespondAckView, ToolApprovalRespondDecision, ToolApprovalRespondParkingView } from './toolApprovalWire.js';
|
|
4
4
|
export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "fetch_failed", "no_input", "card_unavailable"];
|
|
5
5
|
export type ApprovalNotSentCause = (typeof APPROVAL_NOT_SENT_CAUSES)[number];
|
|
6
|
-
export declare const APPROVAL_UNSETTLED_CAUSES: readonly ["engine_refused", "decide_failed", "transport_exhausted", "safety_stop", "respond_failed", "interrupted", "orchestration", "unreadable"];
|
|
6
|
+
export declare const APPROVAL_UNSETTLED_CAUSES: readonly ["engine_refused", "decide_failed", "transport_exhausted", "safety_stop", "respond_failed", "parking_in_progress", "interrupted", "orchestration", "unreadable"];
|
|
7
7
|
export type ApprovalUnsettledCause = (typeof APPROVAL_UNSETTLED_CAUSES)[number];
|
|
8
8
|
export interface ApprovalResolutionDecided {
|
|
9
9
|
kind: 'decided';
|
|
@@ -26,6 +26,7 @@ export interface ApprovalResolutionUnsettled {
|
|
|
26
26
|
errorCode?: string;
|
|
27
27
|
status?: number;
|
|
28
28
|
detail?: string;
|
|
29
|
+
parking?: ToolApprovalRespondParkingView;
|
|
29
30
|
}
|
|
30
31
|
export type ApprovalResolution = ApprovalResolutionDecided | ApprovalResolutionNotSent | ApprovalResolutionUnsettled;
|
|
31
32
|
export type ApprovalLegOutcome = FsApprovalOutcome | ToolApprovalFrameOutcome;
|
|
@@ -12,6 +12,7 @@ export const APPROVAL_UNSETTLED_CAUSES = Object.freeze([
|
|
|
12
12
|
'transport_exhausted',
|
|
13
13
|
'safety_stop',
|
|
14
14
|
'respond_failed',
|
|
15
|
+
'parking_in_progress',
|
|
15
16
|
'interrupted',
|
|
16
17
|
'orchestration',
|
|
17
18
|
'unreadable',
|
|
@@ -98,11 +99,31 @@ function fsLegResolution(outcome, kind) {
|
|
|
98
99
|
return { kind: 'unsettled', cause: 'orchestration', ...call, ...detail };
|
|
99
100
|
return { kind: 'unsettled', cause: 'unreadable', ...call, ...detail };
|
|
100
101
|
}
|
|
102
|
+
function parkingViewOf(v) {
|
|
103
|
+
const approvalId = str(pick(v, 'approvalId'));
|
|
104
|
+
if (approvalId === undefined)
|
|
105
|
+
return undefined;
|
|
106
|
+
const hint = pick(v, 'retryAfterMs');
|
|
107
|
+
const retryAfterMs = typeof hint === 'number' && Number.isFinite(hint) && hint >= 0 ? hint : undefined;
|
|
108
|
+
const decidePath = str(pick(v, 'decidePath'));
|
|
109
|
+
const message = str(pick(v, 'message'));
|
|
110
|
+
return {
|
|
111
|
+
approvalId,
|
|
112
|
+
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
113
|
+
...(decidePath !== undefined ? { decidePath } : {}),
|
|
114
|
+
...(message !== undefined ? { message } : {}),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
101
117
|
function frameLegResolution(outcome) {
|
|
102
118
|
if (pick(outcome, 'retracted') === true)
|
|
103
119
|
return { kind: 'not_sent', cause: 'retracted' };
|
|
104
120
|
if (pick(outcome, 'editRefused') === true)
|
|
105
121
|
return { kind: 'not_sent', cause: 'edit_refused' };
|
|
122
|
+
if (pick(outcome, 'aborted') === true)
|
|
123
|
+
return { kind: 'unsettled', cause: 'interrupted' };
|
|
124
|
+
const parking = parkingViewOf(pick(outcome, 'parking'));
|
|
125
|
+
if (parking !== undefined)
|
|
126
|
+
return { kind: 'unsettled', cause: 'parking_in_progress', parking };
|
|
106
127
|
if (pick(outcome, 'decision') === 'unresolved') {
|
|
107
128
|
const refusal = pick(outcome, 'respondRefusal');
|
|
108
129
|
return {
|
|
@@ -1,6 +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 { isInStreamDrainingEnd } from '../wireErrorTriage.js';
|
|
4
5
|
const MAX_CONSECUTIVE_POLL_ESCAPES = 5;
|
|
5
6
|
function readDurablePendingRows(raw) {
|
|
6
7
|
if (!Array.isArray(raw))
|
|
@@ -415,6 +416,10 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
415
416
|
stats.heartbeats++;
|
|
416
417
|
continue;
|
|
417
418
|
}
|
|
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
|
+
}
|
|
418
423
|
stats.pushEvents++;
|
|
419
424
|
await take('push');
|
|
420
425
|
}
|
package/dist/hitl/askGateWire.js
CHANGED
|
@@ -27,6 +27,9 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
27
27
|
while (true) {
|
|
28
28
|
let park = null;
|
|
29
29
|
for await (const ev of stream) {
|
|
30
|
+
const answering = led.answeringLiveCards();
|
|
31
|
+
if (answering.length > 0)
|
|
32
|
+
await Promise.all(answering);
|
|
30
33
|
led.noteSeq(ev);
|
|
31
34
|
if (isHostProgressFrame(ev))
|
|
32
35
|
progressedSinceReattach = true;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type CoreModeArbitrationRow } from '../generated/engineFactTables.js';
|
|
2
|
-
|
|
2
|
+
import { CORE_ASK_FACES } from '../generated/engineAskFaceTables.js';
|
|
3
|
+
export type AutomaticAnswerLane = 'mode_posture' | 'standing_rule' | 'permission_request_hook';
|
|
4
|
+
export type AutomaticAnswerAskFace = (typeof CORE_ASK_FACES)[number];
|
|
3
5
|
export interface AutomaticAnswerFacts {
|
|
4
6
|
readonly settingsAskRuleMatched?: boolean;
|
|
5
7
|
}
|
|
@@ -17,5 +19,9 @@ export type AutomaticAnswerVerdict = {
|
|
|
17
19
|
readonly kind: 'unknown';
|
|
18
20
|
readonly why: AutomaticAnswerUnknownWhy;
|
|
19
21
|
};
|
|
20
|
-
export declare
|
|
22
|
+
export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "rule_unreadable" | "write_protection_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "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
|
+
export declare function foldModeArbitrationRow(classes: Iterable<string>, askFace?: AutomaticAnswerAskFace): CoreModeArbitrationRow;
|
|
21
24
|
export declare function automaticAnswerVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
|
|
25
|
+
export declare function mandatedApprovalDetailOf(card: object | null | undefined): string;
|
|
26
|
+
export type PermissionRequestHookScope = 'allow_or_deny' | 'deny_only' | 'skip';
|
|
27
|
+
export declare function permissionRequestHookScope(verdict: AutomaticAnswerVerdict | null | undefined): PermissionRequestHookScope;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { CORE_MODE_ARBITRATION, CORE_MODE_RELEASE_REASONS } from '../generated/engineFactTables.js';
|
|
2
|
+
import { CORE_RELEASABLE_ASK_CLASSES_BY_FACE, CORE_SHELL_FACE_TOOL_NAMES } from '../generated/engineAskFaceTables.js';
|
|
3
|
+
import { mandatedApprovalDetail } from '../gateVocabulary.js';
|
|
2
4
|
import { readOwnNonEmptyString } from '../ownKey.js';
|
|
3
5
|
import { readAskClassPairState } from './askClassPair.js';
|
|
4
6
|
import { approvalAllowRuleShadowed } from './allowRuleShadow.js';
|
|
5
7
|
import { approvalIsMandated, readApprovalMandate } from './toolApprovalWire.js';
|
|
8
|
+
export const MODE_ARBITRATION = CORE_MODE_ARBITRATION;
|
|
6
9
|
const TABLE = CORE_MODE_ARBITRATION;
|
|
7
10
|
const ORIGIN_IMPLIES_CLASS = Object.freeze({
|
|
8
11
|
content_question: 'content_question',
|
|
@@ -12,12 +15,26 @@ const ORIGIN_IMPLIES_CLASS = Object.freeze({
|
|
|
12
15
|
ask_rule: 'ask_rule',
|
|
13
16
|
denial_limit_fallback: 'denial_limit_fallback',
|
|
14
17
|
});
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
function mandateWordClass(word) {
|
|
19
|
+
if (word === 'operator_always')
|
|
20
|
+
return 'shell_floor';
|
|
21
|
+
return Object.hasOwn(TABLE, word) ? word : undefined;
|
|
22
|
+
}
|
|
19
23
|
const ORDINARY_ROW = CORE_MODE_ARBITRATION.ordinary;
|
|
20
|
-
|
|
24
|
+
function bypassImmuneOn(c, face) {
|
|
25
|
+
const on = face !== undefined && Object.hasOwn(CORE_RELEASABLE_ASK_CLASSES_BY_FACE, face) ? face : 'other';
|
|
26
|
+
return !CORE_RELEASABLE_ASK_CLASSES_BY_FACE[on].includes(c);
|
|
27
|
+
}
|
|
28
|
+
function askFaceOfCard(card) {
|
|
29
|
+
try {
|
|
30
|
+
const d = Object.getOwnPropertyDescriptor(card, 'toolName');
|
|
31
|
+
return d !== undefined && 'value' in d && typeof d.value === 'string' && CORE_SHELL_FACE_TOOL_NAMES.includes(d.value) ? 'shell' : 'other';
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return 'other';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function foldModeArbitrationRow(classes, askFace) {
|
|
21
38
|
let any = false;
|
|
22
39
|
let bypassImmune = false;
|
|
23
40
|
let classifierEligible = true;
|
|
@@ -29,7 +46,7 @@ export function foldModeArbitrationRow(classes) {
|
|
|
29
46
|
continue;
|
|
30
47
|
const row = TABLE[c];
|
|
31
48
|
any = true;
|
|
32
|
-
bypassImmune ||=
|
|
49
|
+
bypassImmune ||= bypassImmuneOn(c, askFace);
|
|
33
50
|
classifierEligible &&= row.classifierEligible;
|
|
34
51
|
sandboxAdmission &&= row.sandboxAdmission;
|
|
35
52
|
ruleMayClear &&= row.ruleMayClear;
|
|
@@ -39,8 +56,12 @@ export function foldModeArbitrationRow(classes) {
|
|
|
39
56
|
return ORDINARY_ROW;
|
|
40
57
|
return Object.freeze({ bypassImmune, classifierEligible, sandboxAdmission, ruleMayClear, hookAskFloor });
|
|
41
58
|
}
|
|
42
|
-
function
|
|
43
|
-
return lane === 'mode_posture' ? row.bypassImmune : !row.ruleMayClear;
|
|
59
|
+
function rowBlocks(lane, row) {
|
|
60
|
+
return lane === 'mode_posture' ? row.bypassImmune : lane === 'standing_rule' ? !row.ruleMayClear : row.hookAskFloor;
|
|
61
|
+
}
|
|
62
|
+
function classBlocks(lane, c, face) {
|
|
63
|
+
const row = TABLE[c];
|
|
64
|
+
return lane === 'mode_posture' ? bypassImmuneOn(c, face) : lane === 'standing_rule' ? !row.ruleMayClear : row.hookAskFloor;
|
|
44
65
|
}
|
|
45
66
|
function mustAsk(road, extra) {
|
|
46
67
|
return Object.freeze({ kind: 'must_ask', road, ...(extra?.askClass !== undefined ? { askClass: extra.askClass } : {}), ...(extra?.origin !== undefined ? { origin: extra.origin } : {}) });
|
|
@@ -75,34 +96,33 @@ function readHostAskRuleMatched(facts) {
|
|
|
75
96
|
}
|
|
76
97
|
}
|
|
77
98
|
export function automaticAnswerVerdict(card, lane, facts) {
|
|
78
|
-
if (lane !== 'mode_posture' && lane !== 'standing_rule')
|
|
99
|
+
if (lane !== 'mode_posture' && lane !== 'standing_rule' && lane !== 'permission_request_hook')
|
|
79
100
|
return UNKNOWN.lane_not_recognised;
|
|
80
101
|
try {
|
|
81
102
|
if (typeof card !== 'object' || card === null)
|
|
82
103
|
return UNKNOWN.card_unreadable;
|
|
104
|
+
const face = lane === 'mode_posture' ? askFaceOfCard(card) : undefined;
|
|
83
105
|
const origin = readOwnNonEmptyString(evidenceView(card, 'origin'), 'origin');
|
|
84
106
|
if (origin !== undefined && Object.hasOwn(ORIGIN_IMPLIES_CLASS, origin)) {
|
|
85
107
|
const cls = ORIGIN_IMPLIES_CLASS[origin];
|
|
86
|
-
if (
|
|
108
|
+
if (classBlocks(lane, cls, face))
|
|
87
109
|
return mustAsk('origin', { origin, askClass: cls });
|
|
88
110
|
}
|
|
89
|
-
if (lane
|
|
111
|
+
if (lane !== 'mode_posture' && evidenceView(card, 'requiresRealApproval').requiresRealApproval === true)
|
|
90
112
|
return mustAsk('requires_real_approval');
|
|
91
113
|
if (evidenceView(card, 'governanceForced').governanceForced === true)
|
|
92
114
|
return mustAsk('governance');
|
|
93
115
|
if (lane === 'mode_posture') {
|
|
94
116
|
const word = readApprovalMandate(evidenceView(card, 'mandate'));
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return mustAsk('mandate_word', { askClass: cls });
|
|
99
|
-
}
|
|
117
|
+
const cls = word !== undefined ? mandateWordClass(word) : undefined;
|
|
118
|
+
if (cls !== undefined && classBlocks(lane, cls, face))
|
|
119
|
+
return mustAsk('mandate_word', { askClass: cls });
|
|
100
120
|
}
|
|
101
121
|
else {
|
|
102
122
|
const mandateView = evidenceView(card, 'mandated', 'ruleOffersAbsence', 'mandate');
|
|
103
123
|
if (approvalIsMandated(mandateView))
|
|
104
124
|
return mustAsk('mandated');
|
|
105
|
-
if (approvalAllowRuleShadowed(card) || readOwnNonEmptyString(mandateView, 'ruleOffersAbsence') === 'shadowed')
|
|
125
|
+
if (lane === 'standing_rule' && (approvalAllowRuleShadowed(card) || readOwnNonEmptyString(mandateView, 'ruleOffersAbsence') === 'shadowed'))
|
|
106
126
|
return mustAsk('allow_rule_shadow');
|
|
107
127
|
}
|
|
108
128
|
const pair = readAskClassPairState(card);
|
|
@@ -110,7 +130,7 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
110
130
|
return UNKNOWN.classes_malformed;
|
|
111
131
|
if (pair.state === 'absent')
|
|
112
132
|
return UNKNOWN.classes_absent;
|
|
113
|
-
const folded = foldModeArbitrationRow(pair.classes);
|
|
133
|
+
const folded = foldModeArbitrationRow(pair.classes, face);
|
|
114
134
|
let anyKnown = false;
|
|
115
135
|
let firstBlocking;
|
|
116
136
|
let anyUnknown = false;
|
|
@@ -120,14 +140,14 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
120
140
|
continue;
|
|
121
141
|
}
|
|
122
142
|
anyKnown = true;
|
|
123
|
-
if (firstBlocking === undefined &&
|
|
143
|
+
if (firstBlocking === undefined && classBlocks(lane, c, face))
|
|
124
144
|
firstBlocking = c;
|
|
125
145
|
}
|
|
126
|
-
if (anyKnown &&
|
|
146
|
+
if (anyKnown && rowBlocks(lane, folded))
|
|
127
147
|
return mustAsk('ask_class', firstBlocking !== undefined ? { askClass: firstBlocking } : undefined);
|
|
128
148
|
if (anyUnknown)
|
|
129
149
|
return UNKNOWN.class_not_in_table;
|
|
130
|
-
if (lane
|
|
150
|
+
if (lane !== 'mode_posture')
|
|
131
151
|
return MAY_ANSWER;
|
|
132
152
|
const matched = readHostAskRuleMatched(facts);
|
|
133
153
|
if (matched === true)
|
|
@@ -141,3 +161,31 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
141
161
|
return UNKNOWN.card_unreadable;
|
|
142
162
|
}
|
|
143
163
|
}
|
|
164
|
+
export function mandatedApprovalDetailOf(card) {
|
|
165
|
+
try {
|
|
166
|
+
if (typeof card !== 'object' || card === null)
|
|
167
|
+
return mandatedApprovalDetail();
|
|
168
|
+
const pair = readAskClassPairState(card);
|
|
169
|
+
if (pair.state !== 'pair')
|
|
170
|
+
return mandatedApprovalDetail();
|
|
171
|
+
if (pair.classes.some((c) => !Object.hasOwn(TABLE, c)))
|
|
172
|
+
return mandatedApprovalDetail();
|
|
173
|
+
const row = foldModeArbitrationRow(pair.classes);
|
|
174
|
+
if (row.classifierEligible || row.sandboxAdmission)
|
|
175
|
+
return mandatedApprovalDetail();
|
|
176
|
+
return MANDATED_NO_AUTOMATIC_CHECK_DETAIL;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return mandatedApprovalDetail();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const MANDATED_NO_AUTOMATIC_CHECK_DETAIL = 'no saved allow rule and no remembered answer can retire this question, and no automatic check answers it; each call is settled on its own, by you here, and an answer covers only that call';
|
|
183
|
+
export function permissionRequestHookScope(verdict) {
|
|
184
|
+
try {
|
|
185
|
+
const kind = typeof verdict === 'object' && verdict !== null ? verdict.kind : undefined;
|
|
186
|
+
return kind === 'may_answer' ? 'allow_or_deny' : kind === 'must_ask' ? 'skip' : 'deny_only';
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return 'deny_only';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type DecisionRefusalStanding = 'settled-elsewhere' | 'ambiguous' | 'refused-pending';
|
|
2
|
+
export interface DecisionRefusalClassified {
|
|
3
|
+
readonly standing: DecisionRefusalStanding;
|
|
4
|
+
readonly status: number;
|
|
5
|
+
readonly errorCode?: string;
|
|
6
|
+
readonly transient?: true;
|
|
7
|
+
}
|
|
8
|
+
export declare function classifyDecisionRefusal(err: unknown): DecisionRefusalClassified | undefined;
|
|
9
|
+
export type LiveAskPendingReading = 'pending' | 'not_pending' | 'unknown';
|
|
10
|
+
export declare function liveAskPendingReading(envelope: unknown, approvalId: string): LiveAskPendingReading;
|
|
11
|
+
export declare function decisionRefusalClosesOut(refusal: DecisionRefusalClassified | undefined, pending: LiveAskPendingReading): boolean;
|
|
12
|
+
export type UndeliverableDecisionEnd = 'run-stopped' | 'run-unconfirmed' | 'turn-stopped';
|
|
13
|
+
export interface UndeliverableDecisionFacts {
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly errorCode?: string;
|
|
16
|
+
readonly end: UndeliverableDecisionEnd;
|
|
17
|
+
readonly runId?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function undeliverableDecisionContent(facts: UndeliverableDecisionFacts): string;
|