@sema-agent/client-core 0.110.0 → 0.112.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 +244 -0
- package/README.md +59 -18
- package/dist/abortableSleep.d.ts +7 -0
- package/dist/abortableSleep.js +37 -0
- package/dist/adapt/arms.js +25 -4
- package/dist/adapt/toolCards.d.ts +3 -1
- package/dist/adapt/toolCards.js +4 -2
- package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
- package/dist/adapter/activeRunSelfHeal.js +171 -3
- package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
- package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
- package/dist/adapter/runStream.d.ts +5 -0
- package/dist/adapter/runStream.js +15 -3
- package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
- package/dist/attachmentRoute.d.ts +20 -0
- package/dist/attachmentRoute.js +39 -0
- package/dist/attachmentsWireCaps.js +1 -1
- package/dist/builtinOutputStyles.d.ts +12 -0
- package/dist/builtinOutputStyles.js +147 -0
- package/dist/checkpointRowVerdict.d.ts +3 -0
- package/dist/checkpointRowVerdict.js +14 -1
- package/dist/compactionStatus.d.ts +9 -0
- package/dist/compactionStatus.js +29 -5
- package/dist/consoleCaPin.d.ts +136 -0
- package/dist/consoleCaPin.js +827 -0
- package/dist/controlRouter.js +2 -1
- package/dist/coreValuePorts.d.ts +39 -1
- package/dist/coreValuePorts.js +10 -0
- package/dist/decideReceipt.d.ts +2 -1
- package/dist/deviceRebind.js +3 -3
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +6 -0
- package/dist/effectiveFacts.d.ts +9 -0
- package/dist/effectiveFacts.js +33 -0
- package/dist/engineAddress.d.ts +1 -0
- package/dist/engineAddress.js +4 -1
- package/dist/engineCredentialRelease.d.ts +54 -0
- package/dist/engineCredentialRelease.js +146 -0
- package/dist/engineErrorCodes.d.ts +1 -0
- package/dist/engineErrorCodes.js +1 -0
- package/dist/engineHealthProbeVerdict.d.ts +24 -0
- package/dist/engineHealthProbeVerdict.js +123 -0
- package/dist/engineNoticeCodes.js +5 -7
- package/dist/engineRepoint.d.ts +12 -5
- package/dist/engineRepoint.js +44 -19
- package/dist/engineRowRef.d.ts +3 -0
- package/dist/engineRowRef.js +11 -1
- package/dist/engineSwapCrossing.d.ts +57 -0
- package/dist/engineSwapCrossing.js +297 -0
- package/dist/engineUnavailable.d.ts +45 -0
- package/dist/engineUnavailable.js +496 -0
- package/dist/engineUrl.js +4 -2
- package/dist/eventLeg.js +3 -3
- package/dist/gateFailureCodes.js +2 -2
- package/dist/generated/sdkFactTables.d.ts +2 -1
- package/dist/generated/sdkFactTables.js +14 -0
- package/dist/generated/settingsSchemaFns.d.ts +1 -0
- package/dist/generated/settingsSchemaFns.js +3 -0
- package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
- package/dist/generated/settingsSchemaTables.d.ts +56 -0
- package/dist/generated/settingsSchemaTables.js +19 -0
- package/dist/generated/settingsSchemaTypes.d.ts +23 -13
- package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
- package/dist/generated/settingsSchemaUrlShape.js +17 -0
- package/dist/hitl/approvalDelivery.d.ts +1 -0
- package/dist/hitl/approvalDelivery.js +1 -1
- package/dist/hitl/approvalResolution.d.ts +1 -1
- package/dist/hitl/approvalResolution.js +2 -1
- package/dist/hitl/askGateWire.js +5 -6
- package/dist/hitl/decisionRefusal.d.ts +6 -0
- package/dist/hitl/decisionRefusal.js +19 -10
- package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
- package/dist/hitl/decisionRefusalCarrier.js +348 -0
- package/dist/hitl/decisionSend.d.ts +7 -0
- package/dist/hitl/decisionSend.js +34 -12
- package/dist/hitl/editedApprovalAcceptance.js +1 -1
- package/dist/hitl/elicitationRefusal.d.ts +4 -0
- package/dist/hitl/elicitationRefusal.js +23 -0
- package/dist/hitl/frameRouter.d.ts +2 -2
- package/dist/hitl/frameRouter.js +8 -15
- package/dist/hitl/gateIdentity.d.ts +5 -0
- package/dist/hitl/gateIdentity.js +23 -0
- package/dist/hitl/hitlBridge.d.ts +6 -0
- package/dist/hitl/hitlBridge.js +10 -4
- package/dist/hitl/hitlHostSurface.d.ts +2 -1
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/localAllowRedemption.d.ts +70 -0
- package/dist/hitl/localAllowRedemption.js +276 -0
- package/dist/hitl/parkOwnership.js +2 -1
- package/dist/hitl/parkResolver.js +14 -4
- package/dist/hitl/persistedRulesWire.d.ts +16 -0
- package/dist/hitl/persistedRulesWire.js +70 -0
- package/dist/hitl/planReviewWire.d.ts +3 -0
- package/dist/hitl/planReviewWire.js +23 -6
- package/dist/hitl/policyDenyCause.d.ts +1 -1
- package/dist/hitl/policyDenyCause.js +1 -1
- package/dist/hitl/resumeRunningCard.d.ts +1 -0
- package/dist/hitl/resumeRunningCard.js +1 -0
- package/dist/hitl/screenCardCarriers.d.ts +15 -0
- package/dist/hitl/screenCardCarriers.js +41 -0
- package/dist/hitl/screenDecisionCards.d.ts +98 -0
- package/dist/hitl/screenDecisionCards.js +363 -0
- package/dist/hitl/sessionPolicyWire.js +2 -2
- package/dist/hitl/standingRows.d.ts +6 -2
- package/dist/hitl/standingRows.js +27 -2
- package/dist/hitl/toolApprovalWire.d.ts +30 -8
- package/dist/hitl/toolApprovalWire.js +264 -121
- package/dist/hooksReaderGenerations.js +1 -1
- package/dist/hooksWireCaps.js +3 -3
- package/dist/hostSettingNoticeArm.d.ts +3 -0
- package/dist/hostSettingNoticeArm.js +12 -0
- package/dist/hostTurnSettings.d.ts +3 -0
- package/dist/hostTurnSettings.js +38 -5
- package/dist/humanTurnRead.js +11 -1
- package/dist/i18n/messages.en.d.ts +292 -0
- package/dist/i18n/messages.en.js +298 -7
- package/dist/i18n/messages.zh.js +316 -6
- package/dist/i18n/translate.d.ts +3 -0
- package/dist/i18n/translate.js +78 -10
- package/dist/i18n/zh.d.ts +1 -0
- package/dist/i18n/zh.js +1 -0
- package/dist/index.d.ts +71 -20
- package/dist/index.js +44 -13
- package/dist/interactiveHalt.js +2 -1
- package/dist/interactiveInterrupt.js +20 -2
- package/dist/liveModelCatalog.d.ts +4 -0
- package/dist/liveModelCatalog.js +12 -3
- package/dist/localConfig/documents.d.ts +61 -0
- package/dist/localConfig/documents.js +51 -0
- package/dist/localConfig/kernel.d.ts +149 -0
- package/dist/localConfig/kernel.js +597 -0
- package/dist/localConfig/mcp/edit.d.ts +161 -0
- package/dist/localConfig/mcp/edit.js +846 -0
- package/dist/localConfig/mcp/servers.d.ts +165 -0
- package/dist/localConfig/mcp/servers.js +776 -0
- package/dist/localConfig/mcp/views.d.ts +170 -0
- package/dist/localConfig/mcp/views.js +429 -0
- package/dist/localConfig/modelPool.d.ts +262 -0
- package/dist/localConfig/modelPool.js +1489 -0
- package/dist/localConfig/modelPoolWords.d.ts +13 -0
- package/dist/localConfig/modelPoolWords.js +20 -0
- package/dist/localConfig/receiptShape.d.ts +7 -0
- package/dist/localConfig/receiptShape.js +135 -0
- package/dist/localConfig/settingsWrite.d.ts +127 -0
- package/dist/localConfig/settingsWrite.js +237 -0
- package/dist/localConfig/skills.d.ts +124 -0
- package/dist/localConfig/skills.js +397 -0
- package/dist/localConfig/skillsWords.d.ts +10 -0
- package/dist/localConfig/skillsWords.js +5 -0
- package/dist/localEngineSeatReading.d.ts +2 -2
- package/dist/localEngineSeatReading.js +5 -3
- package/dist/localEngineSpawn.d.ts +0 -7
- package/dist/localEngineSpawn.js +0 -92
- package/dist/localEngineStep.d.ts +2 -2
- package/dist/localEngineStep.js +2 -0
- package/dist/mcpProbeWire.js +2 -1
- package/dist/mcpReconnect.js +3 -3
- package/dist/mcpWireCaps.js +1 -1
- package/dist/memoryVerbsWire.js +3 -2
- package/dist/model/engineModelWords.d.ts +3 -0
- package/dist/model/engineModelWords.js +13 -0
- package/dist/model/onboardJudgement.d.ts +14 -4
- package/dist/model/onboardJudgement.js +91 -139
- package/dist/model/subagentModelWord.d.ts +21 -2
- package/dist/model/subagentModelWord.js +100 -29
- package/dist/modelCapabilityProbe.js +3 -40
- package/dist/modelConfigGapNotice.d.ts +20 -0
- package/dist/modelConfigGapNotice.js +102 -0
- package/dist/modsKernelIdentity.js +2 -47
- package/dist/notifications.d.ts +19 -0
- package/dist/notifications.js +114 -12
- package/dist/ownKey.d.ts +7 -0
- package/dist/ownKey.js +71 -8
- package/dist/pendingUserRow.d.ts +4 -0
- package/dist/pendingUserRow.js +16 -0
- package/dist/permissionsDeclaration.d.ts +3 -1
- package/dist/permissionsDeclaration.js +33 -9
- package/dist/planReviewCardFacts.d.ts +3 -0
- package/dist/planReviewCardFacts.js +52 -2
- package/dist/pluginHooksWire.d.ts +9 -1
- package/dist/pluginHooksWire.js +40 -6
- package/dist/printToolResultFrame.js +4 -2
- package/dist/promptBlockedCopy.js +12 -7
- package/dist/promptSuggestionBatch.d.ts +7 -0
- package/dist/promptSuggestionBatch.js +9 -0
- package/dist/promptSuggestionsTail.d.ts +39 -0
- package/dist/promptSuggestionsTail.js +116 -0
- package/dist/request/taskRequest.js +10 -6
- package/dist/requestCoordinate.d.ts +1 -0
- package/dist/requestCoordinate.js +13 -0
- package/dist/resumeRefusalCopy.d.ts +3 -1
- package/dist/resumeRefusalCopy.js +12 -4
- package/dist/rewindArchiveReading.js +4 -4
- package/dist/runCancelContext.d.ts +2 -1
- package/dist/runCancelContext.js +1 -0
- package/dist/runLedgerSnapshot.js +3 -3
- package/dist/runPermissionModeSwitch.js +3 -4
- package/dist/scenarioRefusal.js +11 -13
- package/dist/scenarioSkills.js +11 -6
- package/dist/seam.d.ts +16 -2
- package/dist/seam.js +10 -0
- package/dist/seatContract.d.ts +259 -9
- package/dist/seatContract.js +444 -15
- package/dist/sessionCostReading.d.ts +70 -0
- package/dist/sessionCostReading.js +150 -0
- package/dist/sessionHooksRoster.d.ts +4 -0
- package/dist/sessionHooksRoster.js +158 -0
- package/dist/sessionIdSegment.d.ts +1 -0
- package/dist/sessionIdSegment.js +4 -1
- package/dist/sessionMemoryErase.js +2 -1
- package/dist/sessionMemoryStatus.js +2 -2
- package/dist/sessionReadRedaction.d.ts +7 -0
- package/dist/sessionReadRedaction.js +26 -0
- package/dist/sessionSyncFileHistory.js +8 -9
- package/dist/settingsHooksSalvage.js +2 -1
- package/dist/sha256Bytes.d.ts +1 -0
- package/dist/sha256Bytes.js +46 -0
- package/dist/skillOverrideChain.d.ts +83 -0
- package/dist/skillOverrideChain.js +378 -0
- package/dist/skillsWireCaps.d.ts +3 -0
- package/dist/skillsWireCaps.js +16 -1
- package/dist/sseIdleTriage.js +2 -1
- package/dist/storeDurabilityWord.d.ts +15 -0
- package/dist/storeDurabilityWord.js +18 -0
- package/dist/storePostureCapability.d.ts +7 -3
- package/dist/storePostureCapability.js +69 -39
- package/dist/subagent/engineSubagentResume.js +2 -2
- package/dist/subagent/engineSubagentTail.js +2 -1
- package/dist/subagentContentStore.d.ts +11 -0
- package/dist/subagentContentStore.js +28 -2
- package/dist/toolResult.d.ts +1 -0
- package/dist/toolResult.js +2 -0
- package/dist/turnFailurePresentation.js +4 -10
- package/dist/userAttachmentEnvelope.d.ts +2 -1
- package/dist/userAttachmentEnvelope.js +10 -0
- package/dist/wireErrorTriage.js +2 -1
- package/dist/wireFailureShape.d.ts +4 -0
- package/dist/wireFailureShape.js +141 -19
- package/dist/wireRefusalCopy.d.ts +6 -5
- package/dist/wireRefusalCopy.js +49 -92
- package/docs/INTEGRATION-CLIENTS.md +2718 -17
- package/package.json +7 -3
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -8,6 +8,7 @@ import { guardedRead, ownDataField, readOwnDataValue } from '../ownKey.js';
|
|
|
8
8
|
import { PROTOCOL_ERROR_NAME, isSlotCarrier, protocolErrorNameReading, readBlockedBy, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
9
9
|
import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_INVALID_OUTCOME, RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
|
|
10
10
|
import { suspendedReopenOf } from './suspendedReopen.js';
|
|
11
|
+
import { decisionConflictOf } from './decisionConflict.js';
|
|
11
12
|
import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisionLedger.js';
|
|
12
13
|
import { askParkBlockGeneration, noteAskParkDecideSettled } from './askParkAutoReopenBlock.js';
|
|
13
14
|
import { checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
@@ -259,6 +260,9 @@ function notifyDecideObserver(observer, ev) {
|
|
|
259
260
|
invokeHostCallbackIsolated(`hitlBridge: onDecideOperation(${ev.phase})`, () => observer(ev));
|
|
260
261
|
}
|
|
261
262
|
const ANY_TOOL_FAMILY = () => true;
|
|
263
|
+
export function pendingRowAnswersCall(row, taskId, callId) {
|
|
264
|
+
return row.taskId === taskId && (row.boundCallId === callId || row.toolCallId === callId);
|
|
265
|
+
}
|
|
262
266
|
export async function findPendingForTask(client, taskId, matches, opts, callId, identityAccepts = (r) => matches(typeof r.toolName === 'string' ? r.toolName : undefined)) {
|
|
263
267
|
let rows;
|
|
264
268
|
try {
|
|
@@ -275,10 +279,10 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
|
|
|
275
279
|
});
|
|
276
280
|
const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
|
|
277
281
|
const identityRow = callId !== undefined
|
|
278
|
-
? decidable.find((r) => r
|
|
282
|
+
? decidable.find((r) => pendingRowAnswersCall(r, taskId, callId))
|
|
279
283
|
: undefined;
|
|
280
284
|
const identityForeign = callId !== undefined && identityRow === undefined
|
|
281
|
-
? readings.find((x) => x.r
|
|
285
|
+
? readings.find((x) => pendingRowAnswersCall(x.r, taskId, callId) && x.foreign !== null)
|
|
282
286
|
: undefined;
|
|
283
287
|
if (identityForeign !== undefined) {
|
|
284
288
|
const identityBar = identityForeign.foreign === QUEUE_ROW_FAMILY_UNREADABLE_GATE ? checkpointRowBarOf(identityForeign.r) : undefined;
|
|
@@ -602,8 +606,10 @@ function bindingMismatchReading(e) {
|
|
|
602
606
|
const codeReclassified = code.state === 'code' && code.code === CHECKPOINT_INVALID_OUTCOME;
|
|
603
607
|
const family = (code.state === 'code' && code.code === APPROVAL_BINDING_MISMATCH) ||
|
|
604
608
|
(!codeReclassified && nameReading.state === 'named' && nameReading.name === PROTOCOL_ERROR_NAME.ApprovalBindingMismatchError);
|
|
605
|
-
if (family)
|
|
606
|
-
|
|
609
|
+
if (family) {
|
|
610
|
+
const captured = code.state === 'code' ? { state: 'read', wire: code.code } : code.state === 'absent' ? { state: 'read' } : { state: 'unreadable' };
|
|
611
|
+
return decisionConflictOf(e, captured, nameReading) === 'row_integrity' ? 'not' : 'mismatch';
|
|
612
|
+
}
|
|
607
613
|
if (code.state === 'unreadable')
|
|
608
614
|
return 'unreadable';
|
|
609
615
|
return 'not';
|
|
@@ -22,7 +22,8 @@ export declare function hitlHostSurfaceMissesFor(sessionKey: string): number;
|
|
|
22
22
|
export declare function hitlHostSurfaceFor(sessionKey: string): HitlHostSurface | null;
|
|
23
23
|
export declare function _resetHitlHostSurfaceForTest(): void;
|
|
24
24
|
export declare function surfaceForCurrentSession(): HitlHostSurface | null;
|
|
25
|
-
export declare const CANCEL_DENY_BUDGET_MS =
|
|
25
|
+
export declare const CANCEL_DENY_BUDGET_MS = 8000;
|
|
26
|
+
export declare function interruptDenyLifetime(): AbortSignal;
|
|
26
27
|
export declare const INTERRUPT_DENY_REASON = "Interrupted by user";
|
|
27
28
|
export declare const CANCEL_DENY_WARN_TEXT = "could not cancel the pending question \u2014 the session may stay locked; the run may need engine-side recovery";
|
|
28
29
|
export declare const REMEMBER_NOT_APPLIED_WARN_TEXT = "this session-wide allow was not remembered by the worker \u2014 you will be asked again for this tool";
|
|
@@ -4,6 +4,7 @@ import { unrefTimer } from '../unrefTimer.js';
|
|
|
4
4
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
5
5
|
import { editedApprovalRefusedLine } from './editedApprovalAcceptance.js';
|
|
6
6
|
import { decisionRefusalSaysAskGone } from './decisionRefusal.js';
|
|
7
|
+
import { deliveryTiming, RETRY_BUDGET_MS } from './approvalDelivery.js';
|
|
7
8
|
const hostSurfaceByKey = createSessionSlot();
|
|
8
9
|
const hostSurfaceMissesByKey = new Map();
|
|
9
10
|
export function installHitlHostSurface(surface) {
|
|
@@ -39,7 +40,10 @@ function surfaceForSession(sessionKey) {
|
|
|
39
40
|
}
|
|
40
41
|
return hostSurface;
|
|
41
42
|
}
|
|
42
|
-
export const CANCEL_DENY_BUDGET_MS =
|
|
43
|
+
export const CANCEL_DENY_BUDGET_MS = RETRY_BUDGET_MS;
|
|
44
|
+
export function interruptDenyLifetime() {
|
|
45
|
+
return AbortSignal.timeout(Math.max(1, deliveryTiming().budgetMs));
|
|
46
|
+
}
|
|
43
47
|
export const INTERRUPT_DENY_REASON = 'Interrupted by user';
|
|
44
48
|
export const CANCEL_DENY_WARN_TEXT = 'could not cancel the pending question — the session may stay locked; the run may need engine-side recovery';
|
|
45
49
|
const CANCEL_DENY_WARN_KEY = 'hitl-cancel-deny-warn';
|
|
@@ -140,8 +144,9 @@ export function surfaceRuleArmRejected(why) {
|
|
|
140
144
|
}
|
|
141
145
|
export function observeCancelByDeny(settle, taskId) {
|
|
142
146
|
let timer;
|
|
147
|
+
const budgetMs = deliveryTiming().budgetMs;
|
|
143
148
|
const budget = new Promise((_, reject) => {
|
|
144
|
-
timer = setTimeout(() => reject(new Error(`cancel-by-deny did not settle within ${
|
|
149
|
+
timer = setTimeout(() => reject(new Error(`cancel-by-deny did not settle within ${budgetMs}ms`)), budgetMs);
|
|
145
150
|
unrefTimer(timer);
|
|
146
151
|
});
|
|
147
152
|
void Promise.race([settle, budget])
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { EngineCapState } from '../engineCapsCache.js';
|
|
2
|
+
import type { HostSettingsLayer, WorkspaceTrustState } from '../hostSettingsSource.js';
|
|
3
|
+
import { type LocalAllowRuleDeps } from './localAllowRule.js';
|
|
4
|
+
import { type ListAllPersistedRulesOutcome, type RulesFacade } from './persistedRulesWire.js';
|
|
5
|
+
export declare const LOCAL_ALLOW_NOT_REDEEMABLE_REASONS: readonly ["managed_only", "tighten_only", "no_scope_root", "no_consent", "road_closed"];
|
|
6
|
+
export type LocalAllowNotRedeemableReason = (typeof LOCAL_ALLOW_NOT_REDEEMABLE_REASONS)[number];
|
|
7
|
+
export declare const LOCAL_ALLOW_UNKNOWN_CAUSES: readonly ["input_unreadable", "policy_unreadable", "consent_unreadable", "road_unknown"];
|
|
8
|
+
export type LocalAllowUnknownCause = (typeof LOCAL_ALLOW_UNKNOWN_CAUSES)[number];
|
|
9
|
+
export type LocalAllowRedeemability = {
|
|
10
|
+
readonly kind: 'redeemable';
|
|
11
|
+
readonly via: 'layer' | 'consent';
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'not_redeemable';
|
|
14
|
+
readonly reason: LocalAllowNotRedeemableReason;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'unknown';
|
|
17
|
+
readonly cause: LocalAllowUnknownCause;
|
|
18
|
+
};
|
|
19
|
+
export interface LocalAllowOfferedFacts {
|
|
20
|
+
readonly managedRulesOnly: boolean | 'unreadable';
|
|
21
|
+
readonly scopeRoot: string | undefined;
|
|
22
|
+
readonly road: EngineCapState;
|
|
23
|
+
}
|
|
24
|
+
export interface LocalAllowDeclaredFacts {
|
|
25
|
+
readonly source: HostSettingsLayer;
|
|
26
|
+
readonly workspace: WorkspaceTrustState | 'unreadable';
|
|
27
|
+
readonly managedRulesOnly: boolean | 'unreadable';
|
|
28
|
+
readonly scopeRoot: string | undefined;
|
|
29
|
+
readonly store: ListAllPersistedRulesOutcome;
|
|
30
|
+
}
|
|
31
|
+
export type LocalAllowRedeemabilityInput = ({
|
|
32
|
+
readonly subject: 'offered';
|
|
33
|
+
} & LocalAllowOfferedFacts) | ({
|
|
34
|
+
readonly subject: 'declared';
|
|
35
|
+
readonly rule: string;
|
|
36
|
+
} & LocalAllowDeclaredFacts);
|
|
37
|
+
export declare function localAllowRedeemability(input: LocalAllowRedeemabilityInput): LocalAllowRedeemability;
|
|
38
|
+
export declare function localAllowLandingRedeemability(rule: {} | null | undefined, landing: {} | null | undefined): LocalAllowRedeemability;
|
|
39
|
+
export declare const LOCAL_ALLOW_REDEMPTION_REFUSALS: readonly ["managed_only", "no_scope_root", "rule_shape", "request_refused", "preview_mismatch", "engine_refused", "ticket_dead"];
|
|
40
|
+
export type LocalAllowRedemptionRefusal = (typeof LOCAL_ALLOW_REDEMPTION_REFUSALS)[number];
|
|
41
|
+
export declare const LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES: readonly ["road_closed", "road_unknown", "policy_unreadable", "input_unreadable", "prepare_failed"];
|
|
42
|
+
export type LocalAllowRedemptionUnavailableCause = (typeof LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES)[number];
|
|
43
|
+
export declare const LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES: readonly ["redeem_unsettled", "receipt_unreadable"];
|
|
44
|
+
export type LocalAllowRedemptionUnknownCause = (typeof LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES)[number];
|
|
45
|
+
export type LocalAllowRedemptionReceipt = {
|
|
46
|
+
readonly kind: 'applied';
|
|
47
|
+
readonly outcome: 'persisted' | 'already_live';
|
|
48
|
+
readonly rule: string;
|
|
49
|
+
readonly scope: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'refused';
|
|
52
|
+
readonly reason: LocalAllowRedemptionRefusal;
|
|
53
|
+
readonly detail?: string;
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: 'unavailable';
|
|
56
|
+
readonly cause: LocalAllowRedemptionUnavailableCause;
|
|
57
|
+
} | {
|
|
58
|
+
readonly kind: 'unknown';
|
|
59
|
+
readonly cause: LocalAllowRedemptionUnknownCause;
|
|
60
|
+
};
|
|
61
|
+
export type LocalAllowRedemptionFacade = Pick<RulesFacade, 'ccImportPrepare' | 'ccImportRedeem'>;
|
|
62
|
+
export interface ReadOnlyRuleOfferRedemption extends LocalAllowOfferedFacts {
|
|
63
|
+
readonly rule: string;
|
|
64
|
+
readonly expectedToolName?: string;
|
|
65
|
+
readonly settingsPath: string;
|
|
66
|
+
readonly deps: LocalAllowRuleDeps;
|
|
67
|
+
}
|
|
68
|
+
export declare function redeemReadOnlyRuleOffer(facade: LocalAllowRedemptionFacade, input: ReadOnlyRuleOfferRedemption, opts?: {
|
|
69
|
+
readonly signal?: AbortSignal;
|
|
70
|
+
}): Promise<LocalAllowRedemptionReceipt>;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { displayUntrusted } from '../displayUntrusted.js';
|
|
2
|
+
import { readOwnDataValue } from '../ownKey.js';
|
|
3
|
+
import { layerAdmissionWord } from '../settingsLayerAdmission.js';
|
|
4
|
+
import { parseLocalAllowRule } from './localAllowRule.js';
|
|
5
|
+
import { classifyRulesFailure, classifySkippedReason, readCcImportRedeemCounts, } from './persistedRulesWire.js';
|
|
6
|
+
export const LOCAL_ALLOW_NOT_REDEEMABLE_REASONS = Object.freeze(['managed_only', 'tighten_only', 'no_scope_root', 'no_consent', 'road_closed']);
|
|
7
|
+
export const LOCAL_ALLOW_UNKNOWN_CAUSES = Object.freeze(['input_unreadable', 'policy_unreadable', 'consent_unreadable', 'road_unknown']);
|
|
8
|
+
function verdict(v) {
|
|
9
|
+
return Object.freeze({ ...v });
|
|
10
|
+
}
|
|
11
|
+
const notRedeemable = (reason) => verdict({ kind: 'not_redeemable', reason });
|
|
12
|
+
const unknownBecause = (cause) => verdict({ kind: 'unknown', cause });
|
|
13
|
+
const PROJECT_SCOPE_PREFIX = 'project:';
|
|
14
|
+
const ALLOW_BEHAVIOR = 'allow';
|
|
15
|
+
const MANAGED_LAYER = 'policySettings';
|
|
16
|
+
const DETAIL_SHOWN_MAX = 400;
|
|
17
|
+
function policyStep(v) {
|
|
18
|
+
if (v === true)
|
|
19
|
+
return notRedeemable('managed_only');
|
|
20
|
+
if (v === false)
|
|
21
|
+
return undefined;
|
|
22
|
+
return unknownBecause('policy_unreadable');
|
|
23
|
+
}
|
|
24
|
+
function scopeRootOf(v) {
|
|
25
|
+
return typeof v === 'string' && v !== '' ? v : undefined;
|
|
26
|
+
}
|
|
27
|
+
function storeRowConsents(row, rule, scopeRoot) {
|
|
28
|
+
if (typeof row !== 'object' || row === null)
|
|
29
|
+
return false;
|
|
30
|
+
const behavior = readOwnDataValue(row, 'behavior');
|
|
31
|
+
const text = readOwnDataValue(row, 'rule');
|
|
32
|
+
const scope = readOwnDataValue(row, 'scope');
|
|
33
|
+
return behavior === ALLOW_BEHAVIOR && text === rule && (scope === 'global' || scope === PROJECT_SCOPE_PREFIX + scopeRoot);
|
|
34
|
+
}
|
|
35
|
+
function consentRecorded(store, rule, scopeRoot) {
|
|
36
|
+
if (typeof store !== 'object' || store === null)
|
|
37
|
+
return undefined;
|
|
38
|
+
const ok = readOwnDataValue(store, 'ok');
|
|
39
|
+
if (ok !== true)
|
|
40
|
+
return undefined;
|
|
41
|
+
const rows = readOwnDataValue(store, 'rules');
|
|
42
|
+
if (!Array.isArray(rows))
|
|
43
|
+
return undefined;
|
|
44
|
+
for (let i = 0; i < rows.length; i++) {
|
|
45
|
+
if (storeRowConsents(readOwnDataValue(rows, String(i)), rule, scopeRoot))
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
function roadStep(road) {
|
|
51
|
+
if (road === 'true')
|
|
52
|
+
return verdict({ kind: 'redeemable', via: 'consent' });
|
|
53
|
+
if (road === 'false' || road === 'absent')
|
|
54
|
+
return notRedeemable('road_closed');
|
|
55
|
+
return unknownBecause('road_unknown');
|
|
56
|
+
}
|
|
57
|
+
export function localAllowRedeemability(input) {
|
|
58
|
+
try {
|
|
59
|
+
if (typeof input !== 'object' || input === null)
|
|
60
|
+
return unknownBecause('input_unreadable');
|
|
61
|
+
const subject = readOwnDataValue(input, 'subject');
|
|
62
|
+
if (subject !== 'offered' && subject !== 'declared')
|
|
63
|
+
return unknownBecause('input_unreadable');
|
|
64
|
+
const source = readOwnDataValue(input, 'source');
|
|
65
|
+
if (!(subject === 'declared' && source === MANAGED_LAYER)) {
|
|
66
|
+
const policy = policyStep(readOwnDataValue(input, 'managedRulesOnly'));
|
|
67
|
+
if (policy !== undefined)
|
|
68
|
+
return policy;
|
|
69
|
+
}
|
|
70
|
+
const scopeRoot = scopeRootOf(readOwnDataValue(input, 'scopeRoot'));
|
|
71
|
+
if (subject === 'offered') {
|
|
72
|
+
if (scopeRoot === undefined)
|
|
73
|
+
return notRedeemable('no_scope_root');
|
|
74
|
+
return roadStep(readOwnDataValue(input, 'road'));
|
|
75
|
+
}
|
|
76
|
+
const workspace = readOwnDataValue(input, 'workspace');
|
|
77
|
+
let word;
|
|
78
|
+
try {
|
|
79
|
+
word = layerAdmissionWord(source, workspace, 'permissions', ALLOW_BEHAVIOR);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return unknownBecause('input_unreadable');
|
|
83
|
+
}
|
|
84
|
+
if (word === 'admitted')
|
|
85
|
+
return verdict({ kind: 'redeemable', via: 'layer' });
|
|
86
|
+
if (word === 'tighten_only')
|
|
87
|
+
return notRedeemable('tighten_only');
|
|
88
|
+
if (scopeRoot === undefined)
|
|
89
|
+
return notRedeemable('no_scope_root');
|
|
90
|
+
const rule = readOwnDataValue(input, 'rule');
|
|
91
|
+
if (typeof rule !== 'string' || rule === '')
|
|
92
|
+
return unknownBecause('input_unreadable');
|
|
93
|
+
const consent = consentRecorded(readOwnDataValue(input, 'store'), rule, scopeRoot);
|
|
94
|
+
if (consent === undefined)
|
|
95
|
+
return unknownBecause('consent_unreadable');
|
|
96
|
+
return consent ? verdict({ kind: 'redeemable', via: 'consent' }) : notRedeemable('no_consent');
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return unknownBecause('input_unreadable');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function localAllowLandingRedeemability(rule, landing) {
|
|
103
|
+
try {
|
|
104
|
+
if (typeof rule !== 'string' || rule.trim() === '')
|
|
105
|
+
return unknownBecause('input_unreadable');
|
|
106
|
+
if (typeof landing !== 'object' || landing === null)
|
|
107
|
+
return unknownBecause('input_unreadable');
|
|
108
|
+
return localAllowRedeemability({
|
|
109
|
+
subject: 'declared',
|
|
110
|
+
rule,
|
|
111
|
+
source: readOwnDataValue(landing, 'source'),
|
|
112
|
+
workspace: readOwnDataValue(landing, 'workspace'),
|
|
113
|
+
managedRulesOnly: readOwnDataValue(landing, 'managedRulesOnly'),
|
|
114
|
+
scopeRoot: readOwnDataValue(landing, 'scopeRoot'),
|
|
115
|
+
store: readOwnDataValue(landing, 'store'),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return unknownBecause('input_unreadable');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export const LOCAL_ALLOW_REDEMPTION_REFUSALS = Object.freeze([
|
|
123
|
+
'managed_only',
|
|
124
|
+
'no_scope_root',
|
|
125
|
+
'rule_shape',
|
|
126
|
+
'request_refused',
|
|
127
|
+
'preview_mismatch',
|
|
128
|
+
'engine_refused',
|
|
129
|
+
'ticket_dead',
|
|
130
|
+
]);
|
|
131
|
+
export const LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES = Object.freeze(['road_closed', 'road_unknown', 'policy_unreadable', 'input_unreadable', 'prepare_failed']);
|
|
132
|
+
export const LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES = Object.freeze(['redeem_unsettled', 'receipt_unreadable']);
|
|
133
|
+
function singleRuleLayerContent(canonical) {
|
|
134
|
+
return JSON.stringify({ permissions: { allow: [canonical] } });
|
|
135
|
+
}
|
|
136
|
+
function receipt(r) {
|
|
137
|
+
return Object.freeze({ ...r });
|
|
138
|
+
}
|
|
139
|
+
function detailOf(text) {
|
|
140
|
+
if (typeof text !== 'string')
|
|
141
|
+
return {};
|
|
142
|
+
const shown = displayUntrusted(text, { max: DETAIL_SHOWN_MAX });
|
|
143
|
+
return shown.trim() !== '' ? { detail: shown } : {};
|
|
144
|
+
}
|
|
145
|
+
const JUDGED_RECEIPT = {
|
|
146
|
+
managed_only: { kind: 'refused', reason: 'managed_only' },
|
|
147
|
+
no_scope_root: { kind: 'refused', reason: 'no_scope_root' },
|
|
148
|
+
road_closed: { kind: 'unavailable', cause: 'road_closed' },
|
|
149
|
+
road_unknown: { kind: 'unavailable', cause: 'road_unknown' },
|
|
150
|
+
policy_unreadable: { kind: 'unavailable', cause: 'policy_unreadable' },
|
|
151
|
+
input_unreadable: { kind: 'unavailable', cause: 'input_unreadable' },
|
|
152
|
+
tighten_only: { kind: 'unavailable', cause: 'input_unreadable' },
|
|
153
|
+
no_consent: { kind: 'unavailable', cause: 'input_unreadable' },
|
|
154
|
+
consent_unreadable: { kind: 'unavailable', cause: 'input_unreadable' },
|
|
155
|
+
};
|
|
156
|
+
function previewSingle(prep, scopeRoot) {
|
|
157
|
+
const preview = typeof prep === 'object' && prep !== null ? readOwnDataValue(prep, 'preview') : undefined;
|
|
158
|
+
if (typeof preview !== 'object' || preview === null)
|
|
159
|
+
return { mismatch: true };
|
|
160
|
+
const skipped = readOwnDataValue(preview, 'skipped');
|
|
161
|
+
if (Array.isArray(skipped) && skipped.length > 0) {
|
|
162
|
+
const first = readOwnDataValue(skipped, '0');
|
|
163
|
+
const reason = typeof first === 'object' && first !== null ? readOwnDataValue(first, 'reason') : undefined;
|
|
164
|
+
return { mismatch: true, skipped: typeof reason === 'string' ? classifySkippedReason(reason).text : '' };
|
|
165
|
+
}
|
|
166
|
+
if (!Array.isArray(skipped))
|
|
167
|
+
return { mismatch: true };
|
|
168
|
+
const candidates = readOwnDataValue(preview, 'candidates');
|
|
169
|
+
if (!Array.isArray(candidates) || candidates.length !== 1)
|
|
170
|
+
return { mismatch: true };
|
|
171
|
+
const c = readOwnDataValue(candidates, '0');
|
|
172
|
+
if (typeof c !== 'object' || c === null)
|
|
173
|
+
return { mismatch: true };
|
|
174
|
+
const behavior = readOwnDataValue(c, 'behavior');
|
|
175
|
+
const rule = readOwnDataValue(c, 'rule');
|
|
176
|
+
const scope = readOwnDataValue(c, 'scope');
|
|
177
|
+
const kind = typeof scope === 'object' && scope !== null ? readOwnDataValue(scope, 'kind') : undefined;
|
|
178
|
+
const root = typeof scope === 'object' && scope !== null ? readOwnDataValue(scope, 'root') : undefined;
|
|
179
|
+
if (behavior !== ALLOW_BEHAVIOR || typeof rule !== 'string' || kind !== 'project' || root !== scopeRoot)
|
|
180
|
+
return { mismatch: true };
|
|
181
|
+
return { rule };
|
|
182
|
+
}
|
|
183
|
+
export async function redeemReadOnlyRuleOffer(facade, input, opts) {
|
|
184
|
+
let canonical;
|
|
185
|
+
let offered;
|
|
186
|
+
let scopeRoot;
|
|
187
|
+
let settingsPath;
|
|
188
|
+
try {
|
|
189
|
+
if (typeof input !== 'object' || input === null)
|
|
190
|
+
return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
|
|
191
|
+
const judged = localAllowRedeemability({
|
|
192
|
+
subject: 'offered',
|
|
193
|
+
managedRulesOnly: readOwnDataValue(input, 'managedRulesOnly'),
|
|
194
|
+
scopeRoot: readOwnDataValue(input, 'scopeRoot'),
|
|
195
|
+
road: readOwnDataValue(input, 'road'),
|
|
196
|
+
});
|
|
197
|
+
if (judged.kind !== 'redeemable')
|
|
198
|
+
return receipt(JUDGED_RECEIPT[judged.kind === 'not_redeemable' ? judged.reason : judged.cause]);
|
|
199
|
+
scopeRoot = readOwnDataValue(input, 'scopeRoot');
|
|
200
|
+
const path = readOwnDataValue(input, 'settingsPath');
|
|
201
|
+
if (typeof path !== 'string' || path === '')
|
|
202
|
+
return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
|
|
203
|
+
settingsPath = path;
|
|
204
|
+
const rule = readOwnDataValue(input, 'rule');
|
|
205
|
+
const expected = readOwnDataValue(input, 'expectedToolName');
|
|
206
|
+
const deps = readOwnDataValue(input, 'deps');
|
|
207
|
+
if (typeof rule !== 'string' || deps === undefined || (expected !== undefined && typeof expected !== 'string'))
|
|
208
|
+
return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
|
|
209
|
+
const parsed = parseLocalAllowRule(rule, expected, deps);
|
|
210
|
+
if (!parsed.ok)
|
|
211
|
+
return receipt({ kind: 'refused', reason: 'rule_shape', ...detailOf(parsed.error) });
|
|
212
|
+
canonical = parsed.canonical;
|
|
213
|
+
offered = rule;
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
|
|
217
|
+
}
|
|
218
|
+
const signal = opts?.signal;
|
|
219
|
+
let prep;
|
|
220
|
+
try {
|
|
221
|
+
prep = await facade.ccImportPrepare([{ layer: 'localSettings', path: settingsPath, root: scopeRoot, content: singleRuleLayerContent(canonical) }], signal !== undefined ? { signal } : undefined);
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
const f = classifyRulesFailure(e);
|
|
225
|
+
if (f.kind === 'lane-unavailable' || f.kind === 'route-missing')
|
|
226
|
+
return receipt({ kind: 'unavailable', cause: 'road_closed' });
|
|
227
|
+
if (f.kind === 'body-shape' || f.kind === 'field-refused' || f.kind === 'too-many-candidates' || f.kind === 'forbidden' || f.kind === 'operators-unconfigured') {
|
|
228
|
+
return receipt({ kind: 'refused', reason: 'request_refused' });
|
|
229
|
+
}
|
|
230
|
+
return receipt({ kind: 'unavailable', cause: 'prepare_failed' });
|
|
231
|
+
}
|
|
232
|
+
let ticket;
|
|
233
|
+
let storedRule;
|
|
234
|
+
try {
|
|
235
|
+
const single = previewSingle(prep, scopeRoot);
|
|
236
|
+
if ('mismatch' in single) {
|
|
237
|
+
return receipt(single.skipped !== undefined ? { kind: 'refused', reason: 'engine_refused', ...detailOf(single.skipped) } : { kind: 'refused', reason: 'preview_mismatch' });
|
|
238
|
+
}
|
|
239
|
+
if (single.rule !== canonical && single.rule !== offered)
|
|
240
|
+
return receipt({ kind: 'refused', reason: 'preview_mismatch' });
|
|
241
|
+
storedRule = single.rule;
|
|
242
|
+
const t = readOwnDataValue(prep, 'ticket');
|
|
243
|
+
if (typeof t !== 'string' || t === '')
|
|
244
|
+
return receipt({ kind: 'refused', reason: 'preview_mismatch' });
|
|
245
|
+
ticket = t;
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return receipt({ kind: 'refused', reason: 'preview_mismatch' });
|
|
249
|
+
}
|
|
250
|
+
let body;
|
|
251
|
+
try {
|
|
252
|
+
body = await facade.ccImportRedeem(ticket, signal !== undefined ? { signal } : undefined);
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
const f = classifyRulesFailure(e);
|
|
256
|
+
if (f.kind === 'ticket-dead')
|
|
257
|
+
return receipt({ kind: 'refused', reason: 'ticket_dead' });
|
|
258
|
+
if (f.kind === 'lane-unavailable')
|
|
259
|
+
return receipt({ kind: 'unavailable', cause: 'road_closed' });
|
|
260
|
+
return receipt({ kind: 'unknown', cause: 'redeem_unsettled' });
|
|
261
|
+
}
|
|
262
|
+
const counts = readCcImportRedeemCounts(body);
|
|
263
|
+
if (counts === null)
|
|
264
|
+
return receipt({ kind: 'unknown', cause: 'receipt_unreadable' });
|
|
265
|
+
const refusedRow = counts.refused?.[0];
|
|
266
|
+
if (refusedRow !== undefined)
|
|
267
|
+
return receipt({ kind: 'refused', reason: 'engine_refused', ...detailOf(refusedRow.detail) });
|
|
268
|
+
if (counts.indeterminate.length > 0)
|
|
269
|
+
return receipt({ kind: 'unknown', cause: 'redeem_unsettled' });
|
|
270
|
+
const scope = PROJECT_SCOPE_PREFIX + scopeRoot;
|
|
271
|
+
if (counts.persisted === 1 && counts.deduped === 0)
|
|
272
|
+
return receipt({ kind: 'applied', outcome: 'persisted', rule: storedRule, scope });
|
|
273
|
+
if (counts.deduped === 1 && counts.persisted === 0)
|
|
274
|
+
return receipt({ kind: 'applied', outcome: 'already_live', rule: storedRule, scope });
|
|
275
|
+
return receipt({ kind: 'unknown', cause: 'receipt_unreadable' });
|
|
276
|
+
}
|
|
@@ -4,6 +4,7 @@ import { hostSessionFor } from '../host.js';
|
|
|
4
4
|
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
5
5
|
import { pendingRowIsOfSession } from './parkRowDelegation.js';
|
|
6
6
|
import { slotReadOf } from '../wireFailureShape.js';
|
|
7
|
+
import { safetyStopNamed } from './hitlBridge.js';
|
|
7
8
|
export function pendingRowIsOwnedByThisSession(row, deps) {
|
|
8
9
|
return ownershipVerdictOf(row, deps) === 'owned';
|
|
9
10
|
}
|
|
@@ -70,5 +71,5 @@ export function askQuestionsFromPending(pending) {
|
|
|
70
71
|
return usableAskQuestions(input.questions);
|
|
71
72
|
}
|
|
72
73
|
export function isHitlSafetyErrorLike(e) {
|
|
73
|
-
return typeof e === 'object' && e !== null && e
|
|
74
|
+
return typeof e === 'object' && e !== null && safetyStopNamed(e);
|
|
74
75
|
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { putOwnKey, readOwnDataValue } from '../ownKey.js';
|
|
1
|
+
import { guardedRead, putOwnKey, readOwnDataValue } from '../ownKey.js';
|
|
2
2
|
import { HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, } from './hitlBridge.js';
|
|
3
3
|
import { approvalResolutionDetail, approvalResolutionStatusTag, directSafetyStopOf, resolutionOfDecisionFailure } from './approvalResolution.js';
|
|
4
4
|
import { readDecisionRefusalAsk } from './decisionRefusal.js';
|
|
5
5
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
6
6
|
import { hostLog } from '../host.js';
|
|
7
7
|
import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
|
|
8
|
-
import { INTERRUPT_DENY_REASON, observeCancelByDeny } from './hitlHostSurface.js';
|
|
8
|
+
import { INTERRUPT_DENY_REASON, interruptDenyLifetime, observeCancelByDeny } from './hitlHostSurface.js';
|
|
9
9
|
import { denyAttributionOf, flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
|
|
10
10
|
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
11
11
|
import { inheritFrameBinding } from '../frameBinding.js';
|
|
12
12
|
import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
|
|
13
|
+
import { parkQuestionFrameBits, sourceGenerationNow } from './screenCardCarriers.js';
|
|
13
14
|
import { usableAskQuestions } from './parkOwnership.js';
|
|
14
15
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
15
16
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
16
17
|
import { wireFailureTextsOf } from '../wireFailureShape.js';
|
|
17
18
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
18
19
|
import { refusedParkFromError } from '../resumeRefusalCopy.js';
|
|
20
|
+
import { decisionRefusalWayOutContent } from '../checkpointRowVerdict.js';
|
|
19
21
|
export const MAX_GATE_HOPS = 2;
|
|
20
22
|
export function nextHopBudget(prev, round) {
|
|
21
23
|
const total = (prev?.total ?? 0) + 1;
|
|
@@ -65,6 +67,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
65
67
|
if (!hasQuestionOverlay()) {
|
|
66
68
|
return { kind: 'not_sent', lane: 'ask', cause: 'card_unavailable', detail: NO_QUESTION_OVERLAY_REASON };
|
|
67
69
|
}
|
|
70
|
+
const sourceGeneration = sourceGenerationNow(deps);
|
|
68
71
|
const found = await findPendingForTask(deps.client, taskId, isAskTool, signal ? { signal } : undefined, parkGatedCallId, (row) => isAskTool(typeof row.toolName === 'string' ? row.toolName : undefined));
|
|
69
72
|
if (!found.ok)
|
|
70
73
|
return { kind: 'not_sent', lane: 'ask', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}), ...(found.rowVerdict !== undefined ? { rowVerdict: found.rowVerdict } : {}) };
|
|
@@ -113,7 +116,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
113
116
|
}
|
|
114
117
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
115
118
|
onPresented?.();
|
|
116
|
-
publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}) });
|
|
119
|
+
publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}), ...parkQuestionFrameBits(taskId, gatedCallId, sourceGeneration) });
|
|
117
120
|
});
|
|
118
121
|
if (withdrawn !== undefined) {
|
|
119
122
|
hostLog('debug', `liveHitlAskWire: question-withdrawn-by-host card=${questionId} verdict=${withdrawn.rowVerdict.kind}`);
|
|
@@ -121,7 +124,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
121
124
|
}
|
|
122
125
|
const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
|
|
123
126
|
if (answer === null) {
|
|
124
|
-
observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId,
|
|
127
|
+
observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, { signal: interruptDenyLifetime() }, pending), taskId);
|
|
125
128
|
return { kind: 'unsettled', ...at, cause: 'interrupted' };
|
|
126
129
|
}
|
|
127
130
|
let declinedReceipt;
|
|
@@ -194,6 +197,7 @@ async function surfaceParkGate(park, ctx, gatedCallId, onPresented) {
|
|
|
194
197
|
...(onPresented ? { onPresented } : {}),
|
|
195
198
|
...(deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : {}),
|
|
196
199
|
...(decidedByReading !== undefined ? { approvalDecidedBy: decidedByReading } : {}),
|
|
200
|
+
...((read) => (typeof read === 'function' ? { engineGeneration: () => read.call(deps) } : {}))(guardedRead(() => deps.engineGeneration)),
|
|
197
201
|
}, taskId, argsByCallOf(led), ctx.signal, gatedCallId, park.toolName);
|
|
198
202
|
if (outcome.kind === 'decided' && outcome.decision === 'deny' && outcome.gatedCallId) {
|
|
199
203
|
led.markDenied(outcome.gatedCallId, denyAttributionOf(outcome.deny));
|
|
@@ -329,6 +333,10 @@ function failsoftWayOutOf(outcome) {
|
|
|
329
333
|
return 'speak';
|
|
330
334
|
if (outcome.kind === 'not_sent' && (outcome.cause === 'row_unresumable' || (outcome.cause === 'safety_stop' && outcome.safetyCode === 'binding_missing')))
|
|
331
335
|
return 'reason';
|
|
336
|
+
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && outcome.refusal?.wayOut === 'newer_worker')
|
|
337
|
+
return 'newer_worker';
|
|
338
|
+
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && outcome.refusal?.wayOut === 'cancel_run')
|
|
339
|
+
return 'cancel';
|
|
332
340
|
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && (refusedParkFromError(outcome.refusal) !== null || outcome.refusal?.conflict === 'row_integrity'))
|
|
333
341
|
return 'cancel';
|
|
334
342
|
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused')
|
|
@@ -345,6 +353,8 @@ function failsoftReasonSentence(park, failedReason, terminal, wayOut) {
|
|
|
345
353
|
return renderMessage(msg('approval.park.stillParked', { gate, reason: failedReason }));
|
|
346
354
|
if (wayOut === 'cancel')
|
|
347
355
|
return renderMessage(msg('approval.park.refusedNotResumable', { gate, reason: failedReason }));
|
|
356
|
+
if (wayOut === 'newer_worker')
|
|
357
|
+
return renderMessage(msg('approval.park.newerWorker', { gate, reason: failedReason, wayOut: decisionRefusalWayOutContent('newer_worker') ?? '' }));
|
|
348
358
|
return renderMessage(msg(wayOut === 'speak' ? 'approval.park.resume.speak' : 'approval.park.resume.card', { gate, reason: failedReason }));
|
|
349
359
|
}
|
|
350
360
|
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface, wayOut) {
|
|
@@ -44,6 +44,11 @@ export interface CcImportRedeemCounts {
|
|
|
44
44
|
}
|
|
45
45
|
export type CcImportReceiptOutcome = 'persisted' | 'no-op' | 'refused' | 'indeterminate';
|
|
46
46
|
export declare function readCcImportRedeemCounts(body: unknown): CcImportRedeemCounts | null;
|
|
47
|
+
export interface CcImportRefusedReading {
|
|
48
|
+
readonly count: number;
|
|
49
|
+
readonly rows: readonly CcImportRedeemRefused[];
|
|
50
|
+
}
|
|
51
|
+
export declare function refusedRowOf(counts: CcImportRedeemCounts | null | undefined): CcImportRefusedReading | null;
|
|
47
52
|
export type RulesFailure = {
|
|
48
53
|
kind: 'lane-unavailable';
|
|
49
54
|
message: string;
|
|
@@ -189,6 +194,17 @@ export declare function classifySkippedReason(reason: string): {
|
|
|
189
194
|
code?: string;
|
|
190
195
|
text: string;
|
|
191
196
|
};
|
|
197
|
+
export interface CcImportSkippedGroup {
|
|
198
|
+
readonly entries: readonly string[];
|
|
199
|
+
readonly reason: string;
|
|
200
|
+
readonly code?: string;
|
|
201
|
+
}
|
|
202
|
+
export interface CcImportSkippedReading {
|
|
203
|
+
readonly shape: 'absent' | 'malformed' | 'read';
|
|
204
|
+
readonly groups: readonly CcImportSkippedGroup[];
|
|
205
|
+
readonly unreadable: number;
|
|
206
|
+
}
|
|
207
|
+
export declare function ccImportSkippedRowsOf(skipped: {} | null | undefined): CcImportSkippedReading;
|
|
192
208
|
export type RulePersistOutcome = {
|
|
193
209
|
state: 'unknown';
|
|
194
210
|
} | {
|
|
@@ -133,6 +133,35 @@ export function readCcImportRedeemCounts(body) {
|
|
|
133
133
|
return null;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
const isCount = (r) => r.presence === 'present' && typeof r.value === 'number' && Number.isSafeInteger(r.value) && r.value >= 0;
|
|
137
|
+
const NO_REFUSED_ROWS = Object.freeze([]);
|
|
138
|
+
const NO_REFUSED = Object.freeze({ count: 0, rows: NO_REFUSED_ROWS });
|
|
139
|
+
export function refusedRowOf(counts) {
|
|
140
|
+
try {
|
|
141
|
+
if (typeof counts !== 'object' || counts === null)
|
|
142
|
+
return null;
|
|
143
|
+
if (!isCount(slotReadOf(counts, 'persisted')) || !isCount(slotReadOf(counts, 'deduped')))
|
|
144
|
+
return null;
|
|
145
|
+
const slot = slotReadOf(counts, 'refused');
|
|
146
|
+
if (slot.presence === 'absent')
|
|
147
|
+
return NO_REFUSED;
|
|
148
|
+
if (slot.presence !== 'present')
|
|
149
|
+
return null;
|
|
150
|
+
const rows = ownArrayItems(slot.value);
|
|
151
|
+
if (rows === undefined)
|
|
152
|
+
return null;
|
|
153
|
+
const kept = [];
|
|
154
|
+
for (const row of rows) {
|
|
155
|
+
if (typeof row !== 'object' || row === null)
|
|
156
|
+
return null;
|
|
157
|
+
kept.push(row);
|
|
158
|
+
}
|
|
159
|
+
return kept.length === 0 ? NO_REFUSED : Object.freeze({ count: kept.length, rows: Object.freeze(kept) });
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
136
165
|
export function classifyRulesFailure(e, context) {
|
|
137
166
|
const { status, errorCode, message, retryAfterSec, committed } = wireFailureShapeOf(e);
|
|
138
167
|
if (errorCode === 'capability.rule_store_required') {
|
|
@@ -635,6 +664,47 @@ export function classifySkippedReason(reason) {
|
|
|
635
664
|
return { text };
|
|
636
665
|
return { code: head, text };
|
|
637
666
|
}
|
|
667
|
+
const CC_IMPORT_SKIPPED_READ_BUDGET = 20_000;
|
|
668
|
+
const CC_IMPORT_SKIPPED_SHOWN_MAX = 2000;
|
|
669
|
+
function ccImportSkippedReading(shape, groups, unreadable) {
|
|
670
|
+
return Object.freeze({ shape, groups: Object.freeze([...groups]), unreadable });
|
|
671
|
+
}
|
|
672
|
+
export function ccImportSkippedRowsOf(skipped) {
|
|
673
|
+
if (skipped === undefined)
|
|
674
|
+
return ccImportSkippedReading('absent', [], 0);
|
|
675
|
+
const grouped = new Map();
|
|
676
|
+
let unreadable = 0;
|
|
677
|
+
try {
|
|
678
|
+
if (!Array.isArray(skipped))
|
|
679
|
+
return ccImportSkippedReading('malformed', [], 0);
|
|
680
|
+
const length = readOwnDataValue(skipped, 'length');
|
|
681
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0 || length > CC_IMPORT_SKIPPED_READ_BUDGET)
|
|
682
|
+
return ccImportSkippedReading('malformed', [], 0);
|
|
683
|
+
for (let i = 0; i < length; i++) {
|
|
684
|
+
const row = guardedRead(() => readOwnDataValue(skipped, String(i)));
|
|
685
|
+
const rule = typeof row === 'object' && row !== null ? guardedRead(() => readOwnDataValue(row, 'rule')) : undefined;
|
|
686
|
+
const reason = typeof row === 'object' && row !== null ? guardedRead(() => readOwnDataValue(row, 'reason')) : undefined;
|
|
687
|
+
if (typeof rule !== 'string' || typeof reason !== 'string') {
|
|
688
|
+
unreadable += 1;
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
const entries = grouped.get(reason);
|
|
692
|
+
if (entries === undefined)
|
|
693
|
+
grouped.set(reason, [displayUntrusted(rule, { max: CC_IMPORT_SKIPPED_SHOWN_MAX })]);
|
|
694
|
+
else
|
|
695
|
+
entries.push(displayUntrusted(rule, { max: CC_IMPORT_SKIPPED_SHOWN_MAX }));
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch {
|
|
699
|
+
return ccImportSkippedReading('malformed', [], 0);
|
|
700
|
+
}
|
|
701
|
+
const groups = [];
|
|
702
|
+
for (const [raw, entries] of grouped) {
|
|
703
|
+
const code = classifySkippedReason(raw).code;
|
|
704
|
+
groups.push(Object.freeze({ entries: Object.freeze(entries), reason: displayUntrusted(raw, { max: CC_IMPORT_SKIPPED_SHOWN_MAX }), ...(code !== undefined ? { code } : {}) }));
|
|
705
|
+
}
|
|
706
|
+
return ccImportSkippedReading('read', groups, unreadable);
|
|
707
|
+
}
|
|
638
708
|
export function readRulePersistOutcome(ack) {
|
|
639
709
|
const parsed = readToolApprovalRespondAck(ack);
|
|
640
710
|
if (parsed === undefined || isToolApprovalRespondReplayed(parsed) || isToolApprovalRespondParkedRedeemed(parsed))
|