@sema-agent/client-core 0.107.0 → 0.109.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 +239 -0
- package/README.md +54 -36
- package/dist/adapt/arms.js +23 -8
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +37 -16
- package/dist/adapter/activeRunSelfHeal.d.ts +2 -0
- package/dist/adapter/activeRunSelfHeal.js +21 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +2 -1
- package/dist/agentSession/engineAgentRegistryRead.js +2 -0
- package/dist/bashExitReading.d.ts +12 -0
- package/dist/bashExitReading.js +44 -0
- package/dist/checkpointRowVerdict.d.ts +6 -1
- package/dist/checkpointRowVerdict.js +30 -2
- package/dist/cloudConfigWireCaps.d.ts +10 -0
- package/dist/cloudConfigWireCaps.js +33 -0
- package/dist/compensations.js +1 -1
- package/dist/coreValuePorts.d.ts +17 -1
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideReceipt.js +2 -1
- package/dist/engineAgentAbsence.d.ts +2 -0
- package/dist/engineAgentAbsence.js +28 -0
- package/dist/engineErrorCodes.d.ts +9 -0
- package/dist/engineErrorCodes.js +9 -0
- package/dist/engineHealthFacts.d.ts +17 -0
- package/dist/engineHealthFacts.js +42 -3
- package/dist/engineNoticeCodes.d.ts +147 -1
- package/dist/engineNoticeCodes.js +694 -57
- package/dist/engineTargetSeat.js +1 -0
- package/dist/errorSupplementRoles.js +6 -1
- package/dist/eventLeg.js +1 -1
- package/dist/executionLaneWords.js +2 -1
- package/dist/fleetTaskDesc.js +3 -3
- package/dist/gateOutcome.d.ts +10 -1
- package/dist/gateOutcome.js +48 -1
- package/dist/generated/engineFactTables.d.ts +2 -0
- package/dist/generated/engineFactTables.js +11 -0
- package/dist/generated/engineNoticeTables.js +6 -0
- package/dist/generated/settingsSchemaFns.d.ts +4 -2
- package/dist/generated/settingsSchemaFns.js +9 -3
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
- package/dist/generated/settingsSchemaMcpRequestEntry.js +9 -18
- package/dist/generated/settingsSchemaReservedMemberName.d.ts +1 -0
- package/dist/generated/settingsSchemaReservedMemberName.js +7 -0
- package/dist/generated/settingsSchemaTables.d.ts +2 -0
- package/dist/generated/settingsSchemaTables.js +10 -0
- package/dist/generated/settingsSchemaWireName.d.ts +1 -0
- package/dist/generated/settingsSchemaWireName.js +8 -0
- package/dist/headlessReconnectWire.js +12 -7
- package/dist/hitl/approvalAskers.d.ts +13 -0
- package/dist/hitl/approvalAskers.js +79 -0
- package/dist/hitl/approvalCardReasons.js +22 -4
- package/dist/hitl/approvalRequestFrameReading.d.ts +14 -0
- package/dist/hitl/approvalRequestFrameReading.js +79 -0
- package/dist/hitl/approvalResolution.d.ts +3 -0
- package/dist/hitl/approvalResolution.js +22 -4
- package/dist/hitl/approvalsFeed.js +4 -13
- package/dist/hitl/askParkRowRouting.js +10 -3
- package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
- package/dist/hitl/automaticAnswerVerdict.js +16 -2
- package/dist/hitl/decisionConflict.d.ts +2 -1
- package/dist/hitl/decisionConflict.js +4 -1
- package/dist/hitl/decisionRefusal.d.ts +1 -0
- package/dist/hitl/decisionRefusal.js +5 -2
- package/dist/hitl/decisionSend.js +2 -6
- package/dist/hitl/frameRouter.d.ts +3 -2
- package/dist/hitl/frameRouter.js +37 -7
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +3 -1
- package/dist/hitl/hitlBridge.d.ts +4 -0
- package/dist/hitl/hitlBridge.js +11 -8
- package/dist/hitl/livePendingAsk.js +17 -16
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +1 -1
- package/dist/hitl/noAllowCause.js +4 -2
- package/dist/hitl/parkExpiryJudgements.js +26 -1
- package/dist/hitl/parkOwnership.d.ts +2 -0
- package/dist/hitl/parkOwnership.js +48 -8
- package/dist/hitl/parkResolver.js +39 -30
- package/dist/hitl/parkRowBirthWait.d.ts +2 -0
- package/dist/hitl/parkRowBirthWait.js +8 -1
- package/dist/hitl/parkedUnresumable.d.ts +13 -0
- package/dist/hitl/parkedUnresumable.js +80 -0
- package/dist/hitl/permissionRuleScope.d.ts +4 -0
- package/dist/hitl/permissionRuleScope.js +31 -0
- package/dist/hitl/persistedRulesWire.d.ts +7 -1
- package/dist/hitl/persistedRulesWire.js +25 -8
- package/dist/hitl/planReviewWire.js +12 -16
- package/dist/hitl/policyDenyCause.d.ts +3 -0
- package/dist/hitl/policyDenyCause.js +16 -0
- package/dist/hitl/sessionAllowRoute.js +5 -4
- package/dist/hitl/sessionPolicyDeliverable.js +1 -1
- package/dist/hitl/standingRows.d.ts +1 -5
- package/dist/hitl/standingRows.js +15 -14
- package/dist/hitl/toolApprovalWire.d.ts +16 -4
- package/dist/hitl/toolApprovalWire.js +57 -17
- package/dist/hooksReaderGenerations.d.ts +1 -0
- package/dist/hooksReaderGenerations.js +12 -0
- package/dist/hooksWireCaps.js +12 -7
- package/dist/hostSettingsSource.js +2 -0
- package/dist/i18n/messages.en.d.ts +206 -1
- package/dist/i18n/messages.en.js +209 -4
- package/dist/i18n/messages.zh.js +214 -4
- package/dist/index.d.ts +35 -8
- package/dist/index.js +20 -5
- package/dist/liveModelCatalog.d.ts +4 -2
- package/dist/liveModelCatalog.js +164 -26
- package/dist/localEngineJudge.d.ts +75 -0
- package/dist/localEngineJudge.js +242 -0
- package/dist/localEngineOccupancy.d.ts +88 -0
- package/dist/localEngineOccupancy.js +269 -0
- package/dist/localEngineSeatReading.d.ts +29 -0
- package/dist/localEngineSeatReading.js +294 -0
- package/dist/localEngineSpawn.d.ts +113 -0
- package/dist/localEngineSpawn.js +486 -0
- package/dist/localEngineStep.d.ts +286 -0
- package/dist/localEngineStep.js +1086 -0
- package/dist/localEngineStopLadder.d.ts +29 -0
- package/dist/localEngineStopLadder.js +139 -0
- package/dist/localEngineSwapCard.d.ts +43 -0
- package/dist/localEngineSwapCard.js +205 -0
- package/dist/localEngineTiming.d.ts +8 -0
- package/dist/localEngineTiming.js +8 -0
- package/dist/mcpReconnect.js +12 -5
- package/dist/mcpWireCaps.js +2 -2
- package/dist/memoryEntriesWire.d.ts +1 -1
- package/dist/memoryEntriesWire.js +5 -0
- package/dist/model/catalogDefault.d.ts +25 -0
- package/dist/model/catalogDefault.js +44 -0
- package/dist/noticePresentation.d.ts +2 -0
- package/dist/noticePresentation.js +79 -1
- package/dist/ownKey.d.ts +10 -0
- package/dist/ownKey.js +15 -1
- package/dist/permissionWireCaps.d.ts +1 -0
- package/dist/permissionWireCaps.js +11 -0
- package/dist/pluginHooksWire.d.ts +5 -3
- package/dist/pluginHooksWire.js +116 -7
- package/dist/queuedDelivery.d.ts +1 -0
- package/dist/queuedDelivery.js +12 -0
- package/dist/resultFrameKeys.js +4 -2
- package/dist/resumeRefusalCopy.js +15 -8
- package/dist/ruleReadings.js +1 -1
- package/dist/runPermissionModeSwitch.d.ts +2 -1
- package/dist/runPermissionModeSwitch.js +16 -6
- package/dist/seam.d.ts +13 -2
- package/dist/seam.js +8 -1
- package/dist/seatContract.d.ts +103 -12
- package/dist/seatContract.js +344 -14
- package/dist/sessionBackground.js +2 -0
- package/dist/sessionEngine.d.ts +2 -1
- package/dist/sessionEngine.js +5 -0
- package/dist/settingsHooksSalvage.d.ts +5 -0
- package/dist/settingsHooksSalvage.js +54 -0
- package/dist/subagentContentStore.d.ts +12 -0
- package/dist/subagentContentStore.js +73 -12
- package/dist/supportFloor.js +2 -0
- package/dist/taskNotificationAdmission.d.ts +19 -0
- package/dist/taskNotificationAdmission.js +84 -0
- package/dist/toolResult.js +20 -6
- package/dist/webSearchWireCaps.js +9 -4
- package/dist/wireFailureShape.d.ts +20 -0
- package/dist/wireFailureShape.js +37 -6
- package/dist/wireRefusalCopy.d.ts +23 -2
- package/dist/wireRefusalCopy.js +93 -4
- package/docs/INTEGRATION-CLIENTS.md +1959 -15
- package/package.json +4 -4
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
|
|
2
|
-
import { appendOwnElement } from '../ownKey.js';
|
|
2
|
+
import { appendOwnElement, readOwnDataNonEmptyString } from '../ownKey.js';
|
|
3
3
|
import { readRequiresRealApproval } from './approvalCardReading.js';
|
|
4
4
|
import { pendingRowIsOfSession } from './parkRowDelegation.js';
|
|
5
5
|
import { approvalLiveFaceOf, liveFaceCarried } from './approvalLiveFace.js';
|
|
6
6
|
import { THROWN_SLOT_UNREADABLE, readThrownSlot } from '../wireFailureShape.js';
|
|
7
|
-
import { isToolApprovalFrame,
|
|
7
|
+
import { isToolApprovalFrame, surfaceApprovalFrameWithId, } from './toolApprovalWire.js';
|
|
8
8
|
function nonEmptyString(v) {
|
|
9
9
|
return typeof v === 'string' && v !== '';
|
|
10
10
|
}
|
|
@@ -24,7 +24,9 @@ function snapshotMembers(raw) {
|
|
|
24
24
|
}
|
|
25
25
|
return raw.length === length ? out : undefined;
|
|
26
26
|
}
|
|
27
|
-
function openingFrameOf(f) {
|
|
27
|
+
function openingFrameOf(f, approvalId) {
|
|
28
|
+
if (typeof f !== 'object' || f === null || readOwnDataNonEmptyString(f, 'approvalId') !== approvalId)
|
|
29
|
+
return undefined;
|
|
28
30
|
if (isToolApprovalFrame(f))
|
|
29
31
|
return f.type === 'tool_approval' ? f : undefined;
|
|
30
32
|
return isApprovalRequestFrameV1(f) ? f : undefined;
|
|
@@ -54,19 +56,19 @@ export function readLivePendingRows(body) {
|
|
|
54
56
|
continue;
|
|
55
57
|
}
|
|
56
58
|
const o = item;
|
|
57
|
-
|
|
59
|
+
const approvalId = readOwnDataNonEmptyString(o, 'approvalId');
|
|
60
|
+
if (approvalId === undefined || !nonEmptyString(o.toolName) || !nonNegativeFinite(o.ts) || !nonNegativeFinite(o.expiresAtMs)) {
|
|
58
61
|
dropped++;
|
|
59
62
|
continue;
|
|
60
63
|
}
|
|
61
|
-
if (seen.has(
|
|
64
|
+
if (seen.has(approvalId)) {
|
|
62
65
|
dropped++;
|
|
63
66
|
continue;
|
|
64
67
|
}
|
|
65
|
-
seen.add(
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
approvalId: o.approvalId,
|
|
68
|
+
seen.add(approvalId);
|
|
69
|
+
const frame = openingFrameOf(o.frame, approvalId);
|
|
70
|
+
appendOwnElement(rows, {
|
|
71
|
+
approvalId,
|
|
70
72
|
toolName: o.toolName,
|
|
71
73
|
ts: o.ts,
|
|
72
74
|
expiresAtMs: o.expiresAtMs,
|
|
@@ -147,13 +149,12 @@ export function livePendingRowToApprovalFrame(row) {
|
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
export function surfaceSuspendedAskAndRespond(row, respond, signal, lane) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
const frame = row.frame;
|
|
153
|
+
const approvalId = row.approvalId;
|
|
154
|
+
if (frame !== undefined) {
|
|
155
|
+
return surfaceApprovalFrameWithId(frame, approvalId, respond, undefined, signal, lane, undefined);
|
|
152
156
|
}
|
|
153
|
-
return
|
|
154
|
-
...lane,
|
|
155
|
-
argsUnavailable: true,
|
|
156
|
-
});
|
|
157
|
+
return surfaceApprovalFrameWithId(livePendingRowToApprovalFrame(row), approvalId, respond, undefined, signal, { ...lane, argsUnavailable: true }, undefined);
|
|
157
158
|
}
|
|
158
159
|
const TRACKER_UNSEEN_CLAIMS_MAX = 512;
|
|
159
160
|
export function createSuspendedAskTracker(filter) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
|
|
2
|
+
import { isWholeToolRuleValue } from './permissionRuleScope.js';
|
|
2
3
|
const BASH_TOOL_NAME_FOR_RULES = 'Bash';
|
|
3
4
|
export function parseLocalAllowRule(rule, expectedToolName, deps) {
|
|
4
5
|
if (typeof rule !== 'string' || rule === '') {
|
|
@@ -14,7 +15,7 @@ export function parseLocalAllowRule(rule, expectedToolName, deps) {
|
|
|
14
15
|
if (typeof value.toolName !== 'string' || value.toolName === '') {
|
|
15
16
|
return { ok: false, error: 'rule text carries no tool name' };
|
|
16
17
|
}
|
|
17
|
-
if (value
|
|
18
|
+
if (isWholeToolRuleValue(value)) {
|
|
18
19
|
return {
|
|
19
20
|
ok: false,
|
|
20
21
|
error: 'candidate is a whole-tool allow rule (broader than this ask) — not offered locally',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'engine_ask_rule' | 'budget' | 'other';
|
|
1
|
+
export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'engine_ask_rule' | 'budget' | 'whole_tool_rule_only' | 'other';
|
|
2
2
|
export interface RuleLadderNoAllowFacts {
|
|
3
3
|
readonly allowed?: boolean;
|
|
4
4
|
readonly denied?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { askLevelVerdict } from './automaticAnswerVerdict.js';
|
|
2
2
|
function ladderSays(ladder, key) {
|
|
3
3
|
if (typeof ladder !== 'object' || ladder === null)
|
|
4
4
|
return false;
|
|
@@ -17,11 +17,13 @@ export function noAllowCauseOf(card, ladder) {
|
|
|
17
17
|
return 'ask_rule';
|
|
18
18
|
if (ladderSays(ladder, 'budgetExhausted'))
|
|
19
19
|
return 'budget';
|
|
20
|
-
const standing =
|
|
20
|
+
const standing = askLevelVerdict(card, 'standing_rule');
|
|
21
21
|
if (standing.kind === 'must_ask' && (standing.askClass === 'ask_rule' || standing.origin === 'ask_rule')) {
|
|
22
22
|
return ladderSays(ladder, 'noAskRuleMatched') || ladderSays(ladder, 'noRuleMatched') ? 'engine_ask_rule' : 'ask_rule';
|
|
23
23
|
}
|
|
24
24
|
if (ladderSays(ladder, 'noRuleMatched') && standing.kind === 'may_answer')
|
|
25
25
|
return 'no_allow_rule';
|
|
26
|
+
if (standing.kind === 'may_answer')
|
|
27
|
+
return 'whole_tool_rule_only';
|
|
26
28
|
return 'other';
|
|
27
29
|
}
|
|
@@ -1,11 +1,36 @@
|
|
|
1
|
+
import { isApprovalCompleteFrameV1 } from '@sema-agent/sdk';
|
|
1
2
|
import { APPROVAL_EXPIRED } from '../engineErrorCodes.js';
|
|
2
3
|
import { approvalCompleteParked } from './approvalCompleteParked.js';
|
|
3
4
|
export function classifyApprovalCompleteFrame(frame) {
|
|
4
5
|
if (typeof frame !== 'object' || frame === null)
|
|
5
6
|
return null;
|
|
6
7
|
const f = frame;
|
|
7
|
-
|
|
8
|
+
const type = f.type;
|
|
9
|
+
if (type === 'tool_approval_complete')
|
|
10
|
+
return dispositionOf(frame, f);
|
|
11
|
+
if (type === 'approval_complete')
|
|
12
|
+
return presentedFamilyDisposition(frame);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
function presentedFamilyDisposition(frame) {
|
|
16
|
+
try {
|
|
17
|
+
const snapshot = ownDataSnapshot(frame);
|
|
18
|
+
return isApprovalCompleteFrameV1(snapshot) ? dispositionOf(snapshot, snapshot) : null;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
8
21
|
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function ownDataSnapshot(frame) {
|
|
25
|
+
const snapshot = Object.create(null);
|
|
26
|
+
for (const key of Object.getOwnPropertyNames(frame)) {
|
|
27
|
+
const d = Object.getOwnPropertyDescriptor(frame, key);
|
|
28
|
+
if (d !== undefined && Object.hasOwn(d, 'value'))
|
|
29
|
+
snapshot[key] = d.value;
|
|
30
|
+
}
|
|
31
|
+
return snapshot;
|
|
32
|
+
}
|
|
33
|
+
function dispositionOf(frame, f) {
|
|
9
34
|
const outcome = f.outcome;
|
|
10
35
|
if (outcome === 'allowed' || outcome === 'denied')
|
|
11
36
|
return 'settled';
|
|
@@ -6,5 +6,7 @@ export interface ParkOwnershipDeps {
|
|
|
6
6
|
isOwnRun?: (taskIdTail: string) => boolean;
|
|
7
7
|
}
|
|
8
8
|
export declare function pendingRowIsOwnedByThisSession(row: PendingCheckpoint, deps?: ParkOwnershipDeps): boolean;
|
|
9
|
+
export declare function pendingRowOwnershipOf(row: object, deps?: ParkOwnershipDeps): 'owned' | 'foreign' | 'unknown';
|
|
10
|
+
export declare function usableAskQuestions(value: unknown): AskQuestion[] | null;
|
|
9
11
|
export declare function askQuestionsFromPending(pending: PendingCheckpoint): AskQuestion[] | null;
|
|
10
12
|
export declare function isHitlSafetyErrorLike(e: unknown): boolean;
|
|
@@ -3,31 +3,71 @@ import { rowIdTail } from '../workflow.js';
|
|
|
3
3
|
import { hostSessionFor } from '../host.js';
|
|
4
4
|
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
5
5
|
import { pendingRowIsOfSession } from './parkRowDelegation.js';
|
|
6
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
6
7
|
export function pendingRowIsOwnedByThisSession(row, deps) {
|
|
8
|
+
return ownershipVerdictOf(row, deps) === 'owned';
|
|
9
|
+
}
|
|
10
|
+
export function pendingRowOwnershipOf(row, deps) {
|
|
11
|
+
try {
|
|
12
|
+
return ownershipVerdictOf(row, deps);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return 'unknown';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function ownershipVerdictOf(row, deps) {
|
|
7
19
|
const sessionKey = deps?.sessionKey ?? DEFAULT_SESSION_KEY;
|
|
8
20
|
const rowTaskId = typeof row.taskId === 'string' ? row.taskId : '';
|
|
9
21
|
if (rowTaskId.length > 0) {
|
|
10
22
|
const tail = rowIdTail(rowTaskId);
|
|
11
23
|
if (deps?.isOwnRun !== undefined && deps.isOwnRun(tail))
|
|
12
|
-
return
|
|
24
|
+
return 'owned';
|
|
13
25
|
if (sessionKey === DEFAULT_SESSION_KEY && isOwnEngineRun(tail))
|
|
14
|
-
return
|
|
26
|
+
return 'owned';
|
|
15
27
|
}
|
|
16
28
|
const currentSessionId = deps?.currentSessionId ?? (() => hostSessionFor(sessionKey)?.currentSessionId());
|
|
17
29
|
const sessionId = currentSessionId();
|
|
18
30
|
if (typeof sessionId !== 'string' || sessionId.length === 0)
|
|
31
|
+
return 'unknown';
|
|
32
|
+
if (pendingRowIsOfSession(row, sessionId))
|
|
33
|
+
return 'owned';
|
|
34
|
+
return rowIsProvablyForeign(row, sessionId) ? 'foreign' : 'unknown';
|
|
35
|
+
}
|
|
36
|
+
function rowIsProvablyForeign(row, selfSessionId) {
|
|
37
|
+
const own = slotReadOf(row, 'sessionId');
|
|
38
|
+
if (own.presence !== 'present' || typeof own.value !== 'string' || own.value === '' || own.value === selfSessionId)
|
|
39
|
+
return false;
|
|
40
|
+
const agent = slotReadOf(row, 'agent');
|
|
41
|
+
if (agent.presence === 'absent')
|
|
42
|
+
return true;
|
|
43
|
+
if (agent.presence !== 'present' || typeof agent.value !== 'object' || agent.value === null)
|
|
19
44
|
return false;
|
|
20
|
-
|
|
45
|
+
for (const key of ['parentSessionId', 'rootSessionId']) {
|
|
46
|
+
const link = slotReadOf(agent.value, key);
|
|
47
|
+
if (link.presence === 'absent')
|
|
48
|
+
continue;
|
|
49
|
+
if (link.presence !== 'present' || typeof link.value !== 'string' || link.value === selfSessionId)
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
export function usableAskQuestions(value) {
|
|
55
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
56
|
+
return null;
|
|
57
|
+
for (let i = 0; i < value.length; i++) {
|
|
58
|
+
if (!Object.hasOwn(value, i))
|
|
59
|
+
return null;
|
|
60
|
+
const q = value[i];
|
|
61
|
+
if (typeof q !== 'object' || q === null || typeof q.question !== 'string')
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
21
65
|
}
|
|
22
66
|
export function askQuestionsFromPending(pending) {
|
|
23
67
|
const input = pending.input;
|
|
24
68
|
if (typeof input !== 'object' || input === null)
|
|
25
69
|
return null;
|
|
26
|
-
|
|
27
|
-
if (!Array.isArray(qs) || qs.length === 0)
|
|
28
|
-
return null;
|
|
29
|
-
const ok = qs.every(q => typeof q === 'object' && q !== null && typeof q.question === 'string');
|
|
30
|
-
return ok ? qs : null;
|
|
70
|
+
return usableAskQuestions(input.questions);
|
|
31
71
|
}
|
|
32
72
|
export function isHitlSafetyErrorLike(e) {
|
|
33
73
|
return typeof e === 'object' && e !== null && e.name === 'HitlSafetyError';
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { putOwnKey, readOwnDataValue } from '../ownKey.js';
|
|
2
2
|
import { HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, } from './hitlBridge.js';
|
|
3
|
-
import { approvalResolutionDetail, directSafetyStopOf, resolutionOfDecisionFailure } from './approvalResolution.js';
|
|
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
8
|
import { INTERRUPT_DENY_REASON, observeCancelByDeny } from './hitlHostSurface.js';
|
|
9
|
-
import { flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
|
|
9
|
+
import { denyAttributionOf, flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
|
|
10
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
10
11
|
import { inheritFrameBinding } from '../frameBinding.js';
|
|
11
12
|
import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
|
|
13
|
+
import { usableAskQuestions } from './parkOwnership.js';
|
|
12
14
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
13
15
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
14
16
|
import { wireFailureTextsOf } from '../wireFailureShape.js';
|
|
15
17
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
18
|
+
import { refusedParkFromError } from '../resumeRefusalCopy.js';
|
|
16
19
|
export const MAX_GATE_HOPS = 2;
|
|
17
20
|
export function nextHopBudget(prev, round) {
|
|
18
21
|
const total = (prev?.total ?? 0) + 1;
|
|
@@ -67,12 +70,11 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
67
70
|
return { kind: 'not_sent', lane: 'ask', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}), ...(found.rowVerdict !== undefined ? { rowVerdict: found.rowVerdict } : {}) };
|
|
68
71
|
const { pending, gatedCallId } = found;
|
|
69
72
|
const at = { lane: 'ask', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
if (!Array.isArray(questions) || questions.length === 0) {
|
|
73
|
+
const rowQuestions = usableAskQuestions(pending.input?.questions);
|
|
74
|
+
const streamQuestions = usableAskQuestions(gatedCallId ? askArgsByCall.get(gatedCallId)?.questions : undefined);
|
|
75
|
+
const questionsFromModelCall = rowQuestions === null && streamQuestions !== null;
|
|
76
|
+
const questions = rowQuestions ?? streamQuestions;
|
|
77
|
+
if (questions === null) {
|
|
76
78
|
const truncated = pending.input?.truncated === true;
|
|
77
79
|
return {
|
|
78
80
|
kind: 'not_sent',
|
|
@@ -105,7 +107,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
105
107
|
}
|
|
106
108
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
107
109
|
onPresented?.();
|
|
108
|
-
publishQuestionFrame({ type: 'question', questionId, questions });
|
|
110
|
+
publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}) });
|
|
109
111
|
});
|
|
110
112
|
const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
|
|
111
113
|
if (answer === null) {
|
|
@@ -160,6 +162,10 @@ function failureReasonOf(r) {
|
|
|
160
162
|
return '';
|
|
161
163
|
return r.detail ?? approvalResolutionDetail(r) ?? r.cause;
|
|
162
164
|
}
|
|
165
|
+
function failsoftReasonOf(r) {
|
|
166
|
+
const tag = approvalResolutionStatusTag(r);
|
|
167
|
+
return tag === '' ? failureReasonOf(r) : `${failureReasonOf(r)} ${tag}`;
|
|
168
|
+
}
|
|
163
169
|
function orchestrationEnd(park, gatedCallId, detail) {
|
|
164
170
|
return { kind: 'unsettled', lane: park.gate === 'fs' ? 'decide' : 'ask', cause: 'orchestration', ...(gatedCallId !== undefined ? { gatedCallId } : {}), detail };
|
|
165
171
|
}
|
|
@@ -180,10 +186,7 @@ async function surfaceParkGate(park, ctx, gatedCallId, onPresented) {
|
|
|
180
186
|
...(decidedByReading !== undefined ? { approvalDecidedBy: decidedByReading } : {}),
|
|
181
187
|
}, taskId, argsByCallOf(led), ctx.signal, gatedCallId, park.toolName);
|
|
182
188
|
if (outcome.kind === 'decided' && outcome.decision === 'deny' && outcome.gatedCallId) {
|
|
183
|
-
led.markDenied(outcome.gatedCallId,
|
|
184
|
-
...(outcome.deny?.settledBy !== undefined ? { settledBy: outcome.deny.settledBy } : {}),
|
|
185
|
-
...(outcome.deny?.reason !== undefined ? { reason: outcome.deny.reason } : {}),
|
|
186
|
-
});
|
|
189
|
+
led.markDenied(outcome.gatedCallId, denyAttributionOf(outcome.deny));
|
|
187
190
|
}
|
|
188
191
|
return outcome;
|
|
189
192
|
}
|
|
@@ -283,10 +286,9 @@ export async function resolvePark(park, ctx) {
|
|
|
283
286
|
? undefined
|
|
284
287
|
: outcome.kind === 'not_sent' && outcome.cause === 'retracted'
|
|
285
288
|
? 'the approval card was retracted by the host without a decision (nothing was sent; the run stays parked and remains decidable elsewhere)'
|
|
286
|
-
:
|
|
289
|
+
: failsoftReasonOf(outcome);
|
|
287
290
|
const noDecisionSurface = outcome.kind === 'not_sent' && outcome.cause === 'card_unavailable' && outcome.detail !== undefined && isNoDecisionSurfaceReason(outcome.detail);
|
|
288
|
-
|
|
289
|
-
return { kind: 'failsoft', events: failsoftEvents(park, ctx, interrupted, failedReason, undefined, noDecisionSurface, undecidableHere) };
|
|
291
|
+
return { kind: 'failsoft', events: failsoftEvents(park, ctx, interrupted, failedReason, undefined, noDecisionSurface, failsoftWayOutOf(outcome)) };
|
|
290
292
|
}
|
|
291
293
|
led.dropHeldForDecidedPark(outcome.gatedCallId, park.gatedCallId);
|
|
292
294
|
if (outcome.gatedCallId) {
|
|
@@ -309,17 +311,27 @@ export async function resolvePark(park, ctx) {
|
|
|
309
311
|
...(handoffTaskId !== undefined ? { handoffTaskId } : {}),
|
|
310
312
|
};
|
|
311
313
|
}
|
|
312
|
-
function
|
|
314
|
+
function failsoftWayOutOf(outcome) {
|
|
315
|
+
if (outcome.kind === 'not_sent' && (outcome.cause === 'row_unresumable' || (outcome.cause === 'safety_stop' && outcome.safetyCode === 'binding_missing')))
|
|
316
|
+
return 'reason';
|
|
317
|
+
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && refusedParkFromError(outcome.refusal) !== null)
|
|
318
|
+
return 'cancel';
|
|
319
|
+
return 'card';
|
|
320
|
+
}
|
|
321
|
+
function failsoftReasonSentence(park, failedReason, terminal, wayOut = 'card') {
|
|
313
322
|
if (terminal !== undefined)
|
|
314
323
|
return terminal.errorMessage;
|
|
315
324
|
if (failedReason === undefined)
|
|
316
325
|
return undefined;
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
const gate = park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion';
|
|
327
|
+
if (wayOut === 'reason')
|
|
328
|
+
return renderMessage(msg('approval.park.stillParked', { gate, reason: failedReason }));
|
|
329
|
+
if (wayOut === 'cancel')
|
|
330
|
+
return renderMessage(msg('approval.park.refusedNotResumable', { gate, reason: failedReason }));
|
|
319
331
|
return (`${park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion'} gate could not be answered: ${failedReason}` +
|
|
320
332
|
' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.');
|
|
321
333
|
}
|
|
322
|
-
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface = false,
|
|
334
|
+
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface = false, wayOut = 'card') {
|
|
323
335
|
const events = [
|
|
324
336
|
...flushHeldWithInterruptRewrite(ctx.led, {
|
|
325
337
|
terminal: park.pendingDone,
|
|
@@ -327,7 +339,7 @@ function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, no
|
|
|
327
339
|
gateAbortedByUser,
|
|
328
340
|
}),
|
|
329
341
|
];
|
|
330
|
-
const reason = failsoftReasonSentence(park, failedReason, terminal,
|
|
342
|
+
const reason = failsoftReasonSentence(park, failedReason, terminal, wayOut);
|
|
331
343
|
if (park.pendingDone) {
|
|
332
344
|
events.push(reason === undefined || noDecisionSurface ? park.pendingDone : inheritFrameBinding(park.pendingDone, { ...park.pendingDone, _sema_park_failed_reason: reason }));
|
|
333
345
|
}
|
|
@@ -356,15 +368,12 @@ export function stalledTerminal(park, ctx, stalledRounds, lastStallReason, decid
|
|
|
356
368
|
return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, reason) };
|
|
357
369
|
}
|
|
358
370
|
function notifyParkReattach(ctx, attempt, max, reason) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
cb({ attempt, max, reason });
|
|
364
|
-
}
|
|
365
|
-
catch (e) {
|
|
366
|
-
hostLog('debug', `liveHitlAskWire: onParkReattach threw (${String(e)}) — ignored`);
|
|
367
|
-
}
|
|
371
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onParkReattach', () => {
|
|
372
|
+
const cb = ctx.deps.onParkReattach;
|
|
373
|
+
if (typeof cb !== 'function' || ctx.signal?.aborted === true)
|
|
374
|
+
return;
|
|
375
|
+
return cb.call(ctx.deps, { attempt, max, reason });
|
|
376
|
+
});
|
|
368
377
|
}
|
|
369
378
|
function transportReasonToken(reason) {
|
|
370
379
|
const m = /(ECONNREFUSED|ECONNRESET|ETIMEDOUT|EPIPE|EAI_AGAIN|TimeoutError|AbortError|fetch failed|network)/i.exec(reason);
|
|
@@ -11,6 +11,7 @@ export type ParkRowProbe<Row> = {
|
|
|
11
11
|
rowVerdict?: Exclude<CheckpointRowVerdictReading, {
|
|
12
12
|
readonly kind: 'ok';
|
|
13
13
|
}>;
|
|
14
|
+
rowTaskId?: string;
|
|
14
15
|
};
|
|
15
16
|
export type ParkRowWaitVerdict<Row> = {
|
|
16
17
|
kind: 'row';
|
|
@@ -30,6 +31,7 @@ export type ParkRowWaitVerdict<Row> = {
|
|
|
30
31
|
rowVerdict?: Exclude<CheckpointRowVerdictReading, {
|
|
31
32
|
readonly kind: 'ok';
|
|
32
33
|
}>;
|
|
34
|
+
rowTaskId?: string;
|
|
33
35
|
} | {
|
|
34
36
|
kind: 'aborted';
|
|
35
37
|
waitedMs: number;
|
|
@@ -103,7 +103,14 @@ export async function waitForParkRowBirth(deps) {
|
|
|
103
103
|
if (probed.kind === 'row')
|
|
104
104
|
return { kind: 'row', row: probed.row, waitedMs: now() - startedAt, probes };
|
|
105
105
|
if (probed.kind === 'settled') {
|
|
106
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
kind: 'settled',
|
|
108
|
+
reason: probed.reason,
|
|
109
|
+
waitedMs: now() - startedAt,
|
|
110
|
+
probes,
|
|
111
|
+
...(probed.rowVerdict !== undefined ? { rowVerdict: probed.rowVerdict } : {}),
|
|
112
|
+
...(probed.rowTaskId !== undefined ? { rowTaskId: probed.rowTaskId } : {}),
|
|
113
|
+
};
|
|
107
114
|
}
|
|
108
115
|
lastReason = probed.reason;
|
|
109
116
|
const remainingMs = budgetMs - (now() - startedAt);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CheckpointRowBar } from '../checkpointRowVerdict.js';
|
|
2
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
3
|
+
import type { OwnedPendingListView } from './askParkRowRouting.js';
|
|
4
|
+
import { type ParkOwnershipDeps } from './parkOwnership.js';
|
|
5
|
+
export interface ParkedUnresumableRow {
|
|
6
|
+
readonly rowVerdict: CheckpointRowBar;
|
|
7
|
+
readonly rowKey?: string;
|
|
8
|
+
readonly taskId?: string;
|
|
9
|
+
readonly toolName?: string;
|
|
10
|
+
readonly rowDeadline?: 'armed' | 'none';
|
|
11
|
+
}
|
|
12
|
+
export declare function parkedUnresumableRowsOf(listed: OwnedPendingListView | null | undefined, deps?: ParkOwnershipDeps): readonly ParkedUnresumableRow[];
|
|
13
|
+
export declare function parkedUnresumableLine(row: ParkedUnresumableRow, lang?: UiLanguage | (string & {})): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { checkpointDeadlineStateOf, checkpointRowBarOf, checkpointRowBarSentence, rowBarWayOutContent, } from '../checkpointRowVerdict.js';
|
|
2
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
3
|
+
import { QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
4
|
+
import { approvalCallKey } from './gateIdentity.js';
|
|
5
|
+
import { checkpointQueueReadingOf } from './hitlBridge.js';
|
|
6
|
+
import { pendingRowIsOwnedByThisSession } from './parkOwnership.js';
|
|
7
|
+
function ownNonEmpty(o, key) {
|
|
8
|
+
const r = slotReadOf(o, key);
|
|
9
|
+
return r.presence === 'present' && typeof r.value === 'string' && r.value !== '' ? r.value : undefined;
|
|
10
|
+
}
|
|
11
|
+
function memberOf(row, rowVerdict) {
|
|
12
|
+
const taskId = ownNonEmpty(row, 'taskId');
|
|
13
|
+
const callId = ownNonEmpty(row, 'toolCallId') ?? ownNonEmpty(row, 'boundCallId');
|
|
14
|
+
const rowKey = callId !== undefined || taskId !== undefined ? approvalCallKey(callId, taskId ?? '') : undefined;
|
|
15
|
+
const toolName = ownNonEmpty(row, 'toolName');
|
|
16
|
+
const deadline = checkpointDeadlineStateOf(row);
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
rowVerdict,
|
|
19
|
+
...(rowKey !== undefined ? { rowKey } : {}),
|
|
20
|
+
...(taskId !== undefined ? { taskId } : {}),
|
|
21
|
+
...(toolName !== undefined ? { toolName } : {}),
|
|
22
|
+
...(deadline !== 'unknown' ? { rowDeadline: deadline } : {}),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function parkedUnresumableRowsOf(listed, deps) {
|
|
26
|
+
if (typeof listed !== 'object' || listed === null || Array.isArray(listed)) {
|
|
27
|
+
throw new Error('approvals.list answered without a readable body — the rows this engine cannot continue are unknown');
|
|
28
|
+
}
|
|
29
|
+
const pending = listed.pending;
|
|
30
|
+
if (!Array.isArray(pending)) {
|
|
31
|
+
throw new Error('approvals.list answered without a readable `pending` array — the rows this engine cannot continue are unknown');
|
|
32
|
+
}
|
|
33
|
+
const out = [];
|
|
34
|
+
for (const row of pending) {
|
|
35
|
+
const member = parkedMemberOf(row, deps);
|
|
36
|
+
if (member !== undefined)
|
|
37
|
+
out.push(member);
|
|
38
|
+
}
|
|
39
|
+
return Object.freeze(out);
|
|
40
|
+
}
|
|
41
|
+
function parkedMemberOf(row, deps) {
|
|
42
|
+
try {
|
|
43
|
+
if (typeof row !== 'object' || row === null)
|
|
44
|
+
return undefined;
|
|
45
|
+
const reading = checkpointQueueReadingOf(row);
|
|
46
|
+
if (reading === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (reading.foreign !== null && reading.foreign !== QUEUE_ROW_FAMILY_UNREADABLE_GATE)
|
|
49
|
+
return undefined;
|
|
50
|
+
const bar = checkpointRowBarOf(reading.r);
|
|
51
|
+
if (bar === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (!pendingRowIsOwnedByThisSession(reading.r, deps))
|
|
54
|
+
return undefined;
|
|
55
|
+
return memberOf(reading.r, bar);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const UNREADABLE_BAR = Object.freeze({ kind: 'unreadable' });
|
|
62
|
+
export function parkedUnresumableLine(row, lang) {
|
|
63
|
+
let bar = UNREADABLE_BAR;
|
|
64
|
+
let state = 'unknown';
|
|
65
|
+
try {
|
|
66
|
+
if (typeof row === 'object' && row !== null) {
|
|
67
|
+
const v = slotReadOf(row, 'rowVerdict');
|
|
68
|
+
if (v.presence === 'present')
|
|
69
|
+
bar = checkpointRowBarOf({ rowVerdict: v.value }) ?? UNREADABLE_BAR;
|
|
70
|
+
const d = slotReadOf(row, 'rowDeadline');
|
|
71
|
+
if (d.presence === 'present' && (d.value === 'armed' || d.value === 'none'))
|
|
72
|
+
state = d.value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
bar = UNREADABLE_BAR;
|
|
77
|
+
state = 'unknown';
|
|
78
|
+
}
|
|
79
|
+
return `${checkpointRowBarSentence(bar, lang)} · ${rowBarWayOutContent(bar, state, lang)}`;
|
|
80
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PermissionRuleValue } from '@sema-agent/agent-types';
|
|
2
|
+
export declare function isWholeToolRuleValue(value: PermissionRuleValue): boolean;
|
|
3
|
+
export type AllowRuleScope = 'whole_tool' | 'content' | 'unreadable';
|
|
4
|
+
export declare function allowRuleScopeOf(value: object | null | undefined): AllowRuleScope;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readOwnDataValue } from '../ownKey.js';
|
|
2
|
+
import { inSlotReadOf } from '../wireFailureShape.js';
|
|
3
|
+
export function isWholeToolRuleValue(value) {
|
|
4
|
+
return value.ruleContent === undefined || value.ruleContent === '';
|
|
5
|
+
}
|
|
6
|
+
export function allowRuleScopeOf(value) {
|
|
7
|
+
try {
|
|
8
|
+
if (typeof value !== 'object' || value === null)
|
|
9
|
+
return 'unreadable';
|
|
10
|
+
const toolName = readOwnDataValue(value, 'toolName');
|
|
11
|
+
if (typeof toolName !== 'string' || toolName === '')
|
|
12
|
+
return 'unreadable';
|
|
13
|
+
let ruleContent;
|
|
14
|
+
const slot = inSlotReadOf(value, 'ruleContent');
|
|
15
|
+
if (slot.state === 'present') {
|
|
16
|
+
const raw = slot.value;
|
|
17
|
+
if (raw !== undefined) {
|
|
18
|
+
if (typeof raw !== 'string' || raw === '')
|
|
19
|
+
return 'unreadable';
|
|
20
|
+
ruleContent = raw;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else if (slot.state !== 'absent')
|
|
24
|
+
return 'unreadable';
|
|
25
|
+
const snapshot = Object.freeze(Object.assign(Object.create(null), ruleContent !== undefined ? { toolName, ruleContent } : { toolName }));
|
|
26
|
+
return isWholeToolRuleValue(snapshot) ? 'whole_tool' : 'content';
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return 'unreadable';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -31,12 +31,18 @@ export interface CcImportRedeemRuleKey {
|
|
|
31
31
|
readonly rule: string;
|
|
32
32
|
readonly scope: string;
|
|
33
33
|
}
|
|
34
|
+
export interface CcImportRedeemRefused {
|
|
35
|
+
readonly key?: CcImportRedeemRuleKey;
|
|
36
|
+
readonly reason?: string;
|
|
37
|
+
readonly detail?: string;
|
|
38
|
+
}
|
|
34
39
|
export interface CcImportRedeemCounts {
|
|
35
40
|
persisted: number;
|
|
36
41
|
deduped: number;
|
|
37
42
|
indeterminate: readonly CcImportRedeemIndeterminate[];
|
|
43
|
+
refused?: readonly CcImportRedeemRefused[];
|
|
38
44
|
}
|
|
39
|
-
export type CcImportReceiptOutcome = 'persisted' | 'no-op' | 'indeterminate';
|
|
45
|
+
export type CcImportReceiptOutcome = 'persisted' | 'no-op' | 'refused' | 'indeterminate';
|
|
40
46
|
export declare function readCcImportRedeemCounts(body: unknown): CcImportRedeemCounts | null;
|
|
41
47
|
export type RulesFailure = {
|
|
42
48
|
kind: 'lane-unavailable';
|