@sema-agent/client-core 0.104.1 → 0.106.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 +310 -0
- package/README.md +36 -14
- package/dist/adapt/arms.js +23 -6
- package/dist/adapter/activeRunSelfHeal.d.ts +14 -2
- package/dist/adapter/activeRunSelfHeal.js +90 -12
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +24 -23
- package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
- package/dist/adapter/downstream/wiringManifestView.d.ts +7 -3
- package/dist/adapter/downstream/wiringManifestView.js +25 -1
- package/dist/adapter/runStream.js +8 -5
- package/dist/approvalReachWire.d.ts +1 -0
- package/dist/approvalReachWire.js +4 -1
- package/dist/approvalSeat.d.ts +21 -0
- package/dist/approvalSeat.js +73 -0
- package/dist/assistantInbox.d.ts +2 -0
- package/dist/assistantInbox.js +3 -0
- package/dist/checkpointRowVerdict.d.ts +23 -0
- package/dist/checkpointRowVerdict.js +89 -0
- package/dist/cloudConfigWireCaps.js +3 -0
- package/dist/compensations.js +6 -6
- package/dist/coreValuePorts.d.ts +14 -1
- package/dist/coreValuePorts.js +14 -0
- package/dist/decideFailureNote.js +3 -1
- package/dist/decideReceipt.d.ts +9 -0
- package/dist/decideReceipt.js +29 -1
- package/dist/declared/supportFloorDeclared.js +2 -2
- package/dist/deviceRebind.d.ts +7 -0
- package/dist/deviceRebind.js +39 -5
- package/dist/displayUntrusted.d.ts +4 -2
- package/dist/displayUntrusted.js +45 -22
- package/dist/engineAddress.d.ts +3 -0
- package/dist/engineAddress.js +25 -0
- package/dist/engineDirectory.js +9 -17
- package/dist/engineErrorCodes.d.ts +8 -0
- package/dist/engineErrorCodes.js +8 -0
- package/dist/engineGenerationPort.d.ts +11 -2
- package/dist/engineGenerationPort.js +63 -9
- package/dist/engineHealthFacts.d.ts +5 -0
- package/dist/engineHealthFacts.js +11 -0
- package/dist/engineNoticeCodes.d.ts +26 -3
- package/dist/engineNoticeCodes.js +52 -7
- package/dist/engineRowRef.js +1 -1
- package/dist/errorSupplementRoles.js +2 -0
- package/dist/eventLeg.d.ts +3 -3
- package/dist/eventLeg.js +25 -4
- package/dist/fleet/fleetProjection.js +26 -5
- package/dist/generated/engineFactTables.d.ts +3 -1
- package/dist/generated/engineFactTables.js +4 -1
- package/dist/generated/engineNoticeTables.js +19 -4
- package/dist/generated/sdkFactTables.d.ts +5 -0
- package/dist/generated/sdkFactTables.js +24 -0
- package/dist/generated/settingsSchemaFns.d.ts +11 -0
- package/dist/generated/settingsSchemaFns.js +2 -1
- package/dist/generated/settingsSchemaSafetyMergeSpec.js +1 -1
- package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -1
- package/dist/generated/settingsSchemaSettingsSources.js +22 -2
- package/dist/headlessReconnectWire.js +31 -4
- package/dist/hitl/approvalLiveFace.d.ts +17 -0
- package/dist/hitl/approvalLiveFace.js +35 -0
- package/dist/hitl/approvalQueueRow.d.ts +21 -0
- package/dist/hitl/approvalQueueRow.js +69 -0
- package/dist/hitl/approvalsFeed.d.ts +5 -4
- package/dist/hitl/approvalsFeed.js +38 -6
- package/dist/hitl/askGateWire.js +26 -2
- package/dist/hitl/askParkRowRouting.d.ts +4 -3
- package/dist/hitl/askParkRowRouting.js +15 -3
- package/dist/hitl/automaticAnswerVerdict.js +13 -10
- package/dist/hitl/crashConverged.d.ts +5 -1
- package/dist/hitl/decisionConflict.js +2 -1
- package/dist/hitl/decisionRefusal.js +1 -1
- package/dist/hitl/decisionSend.d.ts +1 -0
- package/dist/hitl/decisionSend.js +14 -2
- package/dist/hitl/frameRouter.d.ts +4 -0
- package/dist/hitl/frameRouter.js +18 -5
- package/dist/hitl/gateIdentity.d.ts +1 -0
- package/dist/hitl/gateIdentity.js +3 -0
- package/dist/hitl/hitlBridge.d.ts +1 -1
- package/dist/hitl/hitlBridge.js +26 -5
- package/dist/hitl/livePendingAsk.d.ts +5 -0
- package/dist/hitl/livePendingAsk.js +6 -1
- package/dist/hitl/planReviewCard.d.ts +23 -0
- package/dist/hitl/planReviewCard.js +250 -0
- package/dist/hitl/planReviewWire.d.ts +39 -14
- package/dist/hitl/planReviewWire.js +450 -163
- package/dist/hitl/sessionAllowRoute.d.ts +2 -0
- package/dist/hitl/sessionAllowRoute.js +7 -0
- package/dist/hitl/sessionPolicyWire.d.ts +1 -7
- package/dist/hitl/sessionPolicyWire.js +3 -43
- package/dist/hitl/subagentSource.d.ts +1 -0
- package/dist/hitl/subagentSource.js +13 -0
- package/dist/hitl/toolApprovalWire.d.ts +3 -2
- package/dist/hitl/toolApprovalWire.js +79 -37
- package/dist/hostSettingsSource.d.ts +5 -2
- package/dist/hostSettingsSource.js +19 -10
- package/dist/hostTurnSettings.d.ts +5 -6
- package/dist/hostTurnSettings.js +106 -26
- package/dist/i18n/messages.en.d.ts +85 -12
- package/dist/i18n/messages.en.js +85 -12
- package/dist/i18n/messages.zh.js +88 -12
- package/dist/index.d.ts +35 -4
- package/dist/index.js +19 -2
- package/dist/liveInitToolFace.js +0 -1
- package/dist/liveModelCatalog.d.ts +15 -0
- package/dist/liveModelCatalog.js +40 -0
- package/dist/mcpTransportKindsCapability.d.ts +18 -0
- package/dist/mcpTransportKindsCapability.js +75 -0
- package/dist/mcpWireCaps.d.ts +4 -1
- package/dist/mcpWireCaps.js +25 -14
- package/dist/modelCapabilityProbe.js +66 -33
- package/dist/notifications.d.ts +9 -0
- package/dist/notifications.js +11 -2
- package/dist/ownKey.d.ts +4 -1
- package/dist/ownKey.js +8 -2
- package/dist/permissionModeWriteCapability.d.ts +13 -0
- package/dist/permissionModeWriteCapability.js +35 -0
- package/dist/permissionsAllowGrantCapability.d.ts +3 -0
- package/dist/permissionsAllowGrantCapability.js +26 -3
- package/dist/permissionsDeclaration.d.ts +25 -3
- package/dist/permissionsDeclaration.js +69 -41
- package/dist/planReviewCardFacts.d.ts +116 -0
- package/dist/planReviewCardFacts.js +469 -0
- package/dist/projectSeed.d.ts +39 -0
- package/dist/projectSeed.js +80 -0
- package/dist/promptDelivery.js +1 -1
- package/dist/reattachWindow.d.ts +8 -1
- package/dist/reattachWindow.js +44 -12
- package/dist/replayBody.d.ts +7 -6
- package/dist/replayBody.js +62 -7
- package/dist/request/taskRequest.js +3 -4
- package/dist/ruleCompileVerdict.d.ts +0 -2
- package/dist/ruleCompileVerdict.js +0 -31
- package/dist/ruleRefusalSentence.js +0 -3
- package/dist/runFollow.d.ts +30 -0
- package/dist/runFollow.js +117 -0
- package/dist/runLedgerSnapshot.d.ts +20 -0
- package/dist/runLedgerSnapshot.js +272 -0
- package/dist/runPermissionModeSwitch.d.ts +57 -0
- package/dist/runPermissionModeSwitch.js +135 -0
- package/dist/runReattachPolicy.d.ts +16 -5
- package/dist/runReattachPolicy.js +72 -24
- package/dist/runTerminal.js +30 -8
- package/dist/sandboxWire.d.ts +0 -3
- package/dist/sandboxWire.js +0 -9
- package/dist/seam.d.ts +17 -7
- package/dist/seam.js +13 -1
- package/dist/seatContract.d.ts +57 -7
- package/dist/seatContract.js +133 -3
- package/dist/serverSupportRange.d.ts +18 -0
- package/dist/serverSupportRange.js +25 -0
- package/dist/sessionEngine.d.ts +156 -0
- package/dist/sessionEngine.js +464 -0
- package/dist/sessionMap.d.ts +11 -1
- package/dist/sessionMap.js +35 -9
- package/dist/sessionMemoryErase.d.ts +1 -1
- package/dist/sessionMemoryErase.js +5 -0
- package/dist/sessionMemoryStatus.d.ts +27 -1
- package/dist/sessionMemoryStatus.js +117 -0
- package/dist/sessionMemorySync.d.ts +31 -0
- package/dist/sessionMemorySync.js +95 -0
- package/dist/sessionRowTag.d.ts +4 -0
- package/dist/sessionRowTag.js +18 -0
- package/dist/settingsLayerAdmission.d.ts +1 -0
- package/dist/settingsLayerAdmission.js +4 -1
- package/dist/sseIdleTriage.d.ts +3 -1
- package/dist/sseIdleTriage.js +19 -4
- package/dist/subagent/engineSubagentTail.js +2 -0
- package/dist/subagentContentStore.d.ts +3 -0
- package/dist/subagentContentStore.js +10 -1
- package/dist/toolResult.js +1 -1
- package/dist/turnFailurePresentation.d.ts +5 -2
- package/dist/turnFailurePresentation.js +12 -4
- package/dist/wireRefusalCopy.js +2 -6
- package/docs/INTEGRATION-CLIENTS.md +1507 -18
- package/package.json +5 -5
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -10,6 +10,7 @@ import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_INVALID_OUTCOME, RESUME_POLICY_FO
|
|
|
10
10
|
import { suspendedReopenOf } from './suspendedReopen.js';
|
|
11
11
|
import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisionLedger.js';
|
|
12
12
|
import { askParkBlockGeneration, noteAskParkDecideSettled } from './askParkAutoReopenBlock.js';
|
|
13
|
+
import { checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
13
14
|
export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
|
|
14
15
|
export const MAX_DENY_REASON_CHARS = 4096;
|
|
15
16
|
export function denyReasonForWire(reason, tag) {
|
|
@@ -28,6 +29,15 @@ export function denyReasonForWire(reason, tag) {
|
|
|
28
29
|
`(server caps approval reason at ${MAX_DENY_REASON_CHARS}; over-cap is a 413 reason_too_large that would drop the DECISION, not just the reason)`);
|
|
29
30
|
return cut;
|
|
30
31
|
}
|
|
32
|
+
function resumeTakesRestatementBody(client) {
|
|
33
|
+
if (typeof Reflect.get(client.approvals, 'decidePeerInbound') === 'function')
|
|
34
|
+
return true;
|
|
35
|
+
return client.assistant.resume.length >= 3;
|
|
36
|
+
}
|
|
37
|
+
const _realClientFitsHitlSlice = true;
|
|
38
|
+
void _realClientFitsHitlSlice;
|
|
39
|
+
const _bindingKeysEchoed = true;
|
|
40
|
+
void _bindingKeysEchoed;
|
|
31
41
|
export const PLAN_REVIEW_MODE_AFTER_WORDS = Object.freeze(['default', 'acceptEdits']);
|
|
32
42
|
export function isPlanReviewModeAfter(v) {
|
|
33
43
|
return v !== undefined && PLAN_REVIEW_MODE_AFTER_WORDS.includes(v);
|
|
@@ -258,7 +268,13 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
|
|
|
258
268
|
hostLog('debug', `hitlBridge: approvals.list failed: ${t.log}`);
|
|
259
269
|
return { ok: false, reason: `approvals.list failed: ${t.reason}` };
|
|
260
270
|
}
|
|
261
|
-
const readings = rows.
|
|
271
|
+
const readings = rows.flatMap((r) => {
|
|
272
|
+
const sort = checkpointQueueRowSortOf(r);
|
|
273
|
+
if (sort === 'excluded')
|
|
274
|
+
return [];
|
|
275
|
+
const cp = r;
|
|
276
|
+
return [{ r: cp, foreign: sort === 'unreadable' ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(cp) }];
|
|
277
|
+
});
|
|
262
278
|
const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
|
|
263
279
|
const identityRow = callId !== undefined
|
|
264
280
|
? decidable.find((r) => r.taskId === taskId && (r.boundCallId === callId || r.toolCallId === callId))
|
|
@@ -328,12 +344,14 @@ export class HitlBridge {
|
|
|
328
344
|
throw new Error(found.reason);
|
|
329
345
|
}
|
|
330
346
|
bindingOf(p) {
|
|
331
|
-
|
|
347
|
+
const boundCallId = readOwnDataValue(p, 'boundCallId');
|
|
348
|
+
const boundInputHash = readOwnDataValue(p, 'boundInputHash');
|
|
349
|
+
if (typeof boundCallId !== 'string' || boundCallId.length === 0 || typeof boundInputHash !== 'string' || boundInputHash.length === 0) {
|
|
332
350
|
throw new HitlSafetyError(`pending checkpoint ${p.id ?? '<no-id>'} is missing D-1 binding fields ` +
|
|
333
|
-
`(boundCallId=${String(
|
|
351
|
+
`(boundCallId=${String(boundCallId)}, boundInputHash=${String(boundInputHash)}) — ` +
|
|
334
352
|
'pre-D-1 workers are no longer supported (server 5.x requires the verbatim two-tuple echo)', 'binding_missing');
|
|
335
353
|
}
|
|
336
|
-
return { boundCallId
|
|
354
|
+
return { boundCallId, boundInputHash };
|
|
337
355
|
}
|
|
338
356
|
async decideTool(outcome, toolUseID, opts, preResolvedPending) {
|
|
339
357
|
const pending = preResolvedPending ?? (await this.pendingForDecide(toolUseID, wireOptsOf(opts)));
|
|
@@ -406,7 +424,10 @@ export class HitlBridge {
|
|
|
406
424
|
if (kind && kind !== 'resource_limit') {
|
|
407
425
|
throw new HitlSafetyError(`cannot resume a "${kind}" gate — "continue" must never bypass a human approval; use decide`, 'wrong_gate');
|
|
408
426
|
}
|
|
409
|
-
|
|
427
|
+
const assistant = this.client.assistant;
|
|
428
|
+
if (resumeTakesRestatementBody(this.client))
|
|
429
|
+
return assistant.resume(this.taskId, undefined, opts);
|
|
430
|
+
return Reflect.apply(assistant.resume, assistant, [this.taskId, opts]);
|
|
410
431
|
}
|
|
411
432
|
async decideRaw(sessionId, decision, optsIn) {
|
|
412
433
|
const opts = wireOptsOf(optsIn);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ApprovalRequestFrame } from '@sema-agent/sdk';
|
|
2
2
|
import type { ApprovalResolution } from './approvalResolution.js';
|
|
3
3
|
import type { ApprovalsFeedEmission } from './approvalsFeed.js';
|
|
4
|
+
import type { ApprovalMandateWord } from '../gateVocabulary.js';
|
|
4
5
|
import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
|
|
5
6
|
export interface LivePendingAskView {
|
|
6
7
|
approvalId: string;
|
|
@@ -13,6 +14,10 @@ export interface LivePendingAskView {
|
|
|
13
14
|
fromSubagent?: true;
|
|
14
15
|
originTaskId?: string;
|
|
15
16
|
frame?: ToolApprovalFrame | ApprovalRequestFrame;
|
|
17
|
+
mandated?: true;
|
|
18
|
+
mandate?: ApprovalMandateWord;
|
|
19
|
+
origin?: string;
|
|
20
|
+
liveFactsUnknown?: true;
|
|
16
21
|
}
|
|
17
22
|
export type LivePendingReading = {
|
|
18
23
|
kind: 'not_reported';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
|
|
2
2
|
import { appendOwnElement } from '../ownKey.js';
|
|
3
3
|
import { readRequiresRealApproval } from './approvalCardReading.js';
|
|
4
|
+
import { approvalLiveFaceOf, liveFaceCarried } from './approvalLiveFace.js';
|
|
4
5
|
import { THROWN_SLOT_UNREADABLE, readThrownSlot } from '../wireFailureShape.js';
|
|
5
6
|
import { isToolApprovalFrame, surfaceToolApprovalFrameAndRespond, } from './toolApprovalWire.js';
|
|
6
7
|
function nonEmptyString(v) {
|
|
@@ -61,6 +62,8 @@ export function readLivePendingRows(body) {
|
|
|
61
62
|
continue;
|
|
62
63
|
}
|
|
63
64
|
seen.add(o.approvalId);
|
|
65
|
+
const opening = openingFrameOf(o.frame);
|
|
66
|
+
const frame = opening !== undefined && opening.approvalId === o.approvalId ? opening : undefined;
|
|
64
67
|
rows.push({
|
|
65
68
|
approvalId: o.approvalId,
|
|
66
69
|
toolName: o.toolName,
|
|
@@ -71,7 +74,8 @@ export function readLivePendingRows(body) {
|
|
|
71
74
|
...(o.governanceForced === true ? { governanceForced: true } : {}),
|
|
72
75
|
...(o.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
73
76
|
...(nonEmptyString(o.originTaskId) ? { originTaskId: o.originTaskId } : {}),
|
|
74
|
-
...(
|
|
77
|
+
...(frame !== undefined ? { frame } : {}),
|
|
78
|
+
...(frame === undefined ? liveFaceCarried(approvalLiveFaceOf(o)) : {}),
|
|
75
79
|
});
|
|
76
80
|
}
|
|
77
81
|
if (members.length > 0 && rows.length === 0)
|
|
@@ -137,6 +141,7 @@ export function livePendingRowToApprovalFrame(row) {
|
|
|
137
141
|
...(row.originTaskId !== undefined ? { sourceTaskId: row.originTaskId } : {}),
|
|
138
142
|
...(row.governanceForced === true ? { governanceForced: true } : {}),
|
|
139
143
|
...(readRequiresRealApproval(row) ? { requiresRealApproval: true } : {}),
|
|
144
|
+
...liveFaceCarried(approvalLiveFaceOf(row)),
|
|
140
145
|
};
|
|
141
146
|
}
|
|
142
147
|
export function surfaceSuspendedAskAndRespond(row, respond, signal, lane) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type PlanReviewCardFacts, type PlanReviewFactsTaken } from '../planReviewCardFacts.js';
|
|
2
|
+
import { type MessageRef } from '../i18n/translate.js';
|
|
3
|
+
import type { MessageKey } from '../i18n/messages.en.js';
|
|
4
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
5
|
+
export declare function planReviewCardNoteRef(facts: PlanReviewCardFacts): MessageRef | undefined;
|
|
6
|
+
export declare function planReviewUnreadableNoteRef(): MessageRef;
|
|
7
|
+
export declare function planReviewCardNote(facts: PlanReviewCardFacts, lang?: UiLanguage): string | undefined;
|
|
8
|
+
export declare function planReviewAskAgainKey(facts: PlanReviewCardFacts | undefined): MessageKey;
|
|
9
|
+
export type PlanReviewDecisionReason = 'card_missing' | 'card_withholds' | 'gate_moved' | 'gate_untrusted';
|
|
10
|
+
export declare function planReviewDecisionReasonRef(reason: PlanReviewDecisionReason, card?: PlanReviewCardFacts): MessageRef;
|
|
11
|
+
export interface PlanReviewLedgerSource {
|
|
12
|
+
readonly runRow: (taskId: string, signal: AbortSignal) => Promise<object>;
|
|
13
|
+
readonly frames: (taskId: string, signal: AbortSignal) => AsyncIterable<object>;
|
|
14
|
+
}
|
|
15
|
+
export type PlanReviewFactsTaking = {
|
|
16
|
+
readonly kind: 'now';
|
|
17
|
+
readonly taken: PlanReviewFactsTaken;
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: 'ledger';
|
|
20
|
+
readonly pending: Promise<PlanReviewFactsTaken | undefined>;
|
|
21
|
+
};
|
|
22
|
+
export declare function _resetPlanReviewFactsFlightsForTest(): void;
|
|
23
|
+
export declare function takePlanReviewFacts(sessionKey: string, taskId: string, checkpointId: string | undefined, source: () => PlanReviewLedgerSource | undefined, signal?: AbortSignal): PlanReviewFactsTaking;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { currentPlanReviewGateOf, isPlanReviewClaimFrame, mintPlanReviewCardFacts, openPlanReviewLedgerFold, planGateRowReadOf, planReviewBodyOf, planReviewCardFactsFrom, planReviewLedgerClaimOf, planReviewRunGeneration, beginPlanReviewFactsRead, commitPlanReviewFacts, samePlanClaim, stepPlanReviewLedgerFold, unknownBody, } from '../planReviewCardFacts.js';
|
|
2
|
+
import { msg, renderMessage } from '../i18n/translate.js';
|
|
3
|
+
import { readRunLedgerSnapshot } from '../runLedgerSnapshot.js';
|
|
4
|
+
import { snapshotIsSubFlow, snapshotSegmentIdentity } from '../adapter/types.js';
|
|
5
|
+
import { settleWithinDeadline } from '../abortableSleep.js';
|
|
6
|
+
import { hostLog } from '../host.js';
|
|
7
|
+
export function planReviewCardNoteRef(facts) {
|
|
8
|
+
const body = facts.body;
|
|
9
|
+
switch (body.kind) {
|
|
10
|
+
case 'shown':
|
|
11
|
+
return body.markdown.trim() === '' ? msg('approval.planReview.note.empty') : undefined;
|
|
12
|
+
case 'marked':
|
|
13
|
+
return msg('approval.planReview.note.marked');
|
|
14
|
+
case 'too_large':
|
|
15
|
+
return msg('approval.planReview.note.tooLarge', { length: body.length });
|
|
16
|
+
case 'unknown':
|
|
17
|
+
return msg(body.cause === 'not_carried' ? 'approval.planReview.note.notCarried' : 'approval.planReview.note.unknown');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function planReviewUnreadableNoteRef() {
|
|
21
|
+
return msg('approval.planReview.note.unknown');
|
|
22
|
+
}
|
|
23
|
+
export function planReviewCardNote(facts, lang) {
|
|
24
|
+
let ref;
|
|
25
|
+
try {
|
|
26
|
+
const read = planReviewCardFactsFrom(facts);
|
|
27
|
+
ref = read === undefined ? planReviewUnreadableNoteRef() : planReviewCardNoteRef(read);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
ref = planReviewUnreadableNoteRef();
|
|
31
|
+
}
|
|
32
|
+
return ref === undefined ? undefined : renderMessage(ref, lang);
|
|
33
|
+
}
|
|
34
|
+
export function planReviewAskAgainKey(facts) {
|
|
35
|
+
const body = facts?.body;
|
|
36
|
+
if (body === undefined)
|
|
37
|
+
return 'approval.planReview.askAgain.unknown';
|
|
38
|
+
switch (body.kind) {
|
|
39
|
+
case 'shown':
|
|
40
|
+
return 'approval.planReview.askAgain.shown';
|
|
41
|
+
case 'marked':
|
|
42
|
+
return 'approval.planReview.askAgain.marked';
|
|
43
|
+
case 'too_large':
|
|
44
|
+
return 'approval.planReview.askAgain.tooLarge';
|
|
45
|
+
case 'unknown':
|
|
46
|
+
return body.cause === 'not_carried' ? 'approval.planReview.askAgain.notCarried' : 'approval.planReview.askAgain.unknown';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function planReviewDecisionReasonRef(reason, card) {
|
|
50
|
+
switch (reason) {
|
|
51
|
+
case 'card_missing':
|
|
52
|
+
return msg('approval.planReview.decision.cardMissing');
|
|
53
|
+
case 'card_withholds': {
|
|
54
|
+
let ref;
|
|
55
|
+
try {
|
|
56
|
+
const read = card === undefined ? undefined : planReviewCardFactsFrom(card);
|
|
57
|
+
ref = read === undefined ? undefined : planReviewCardNoteRef(read);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
ref = undefined;
|
|
61
|
+
}
|
|
62
|
+
return ref ?? planReviewUnreadableNoteRef();
|
|
63
|
+
}
|
|
64
|
+
case 'gate_moved':
|
|
65
|
+
return msg('approval.planReview.decision.gateMoved');
|
|
66
|
+
case 'gate_untrusted':
|
|
67
|
+
return msg('approval.planReview.decision.gateUntrusted');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const PLAN_REVIEW_LEDGER_BUDGET_MS = 3_000;
|
|
71
|
+
const planReviewFactsFlights = new Map();
|
|
72
|
+
export function _resetPlanReviewFactsFlightsForTest() {
|
|
73
|
+
planReviewFactsFlights.clear();
|
|
74
|
+
}
|
|
75
|
+
export function takePlanReviewFacts(sessionKey, taskId, checkpointId, source, signal) {
|
|
76
|
+
const now = (facts, generation) => ({ kind: 'now', taken: { sessionKey, taskId, facts, generation } });
|
|
77
|
+
try {
|
|
78
|
+
const g0 = planReviewRunGeneration(sessionKey, taskId);
|
|
79
|
+
const gate = currentPlanReviewGateOf(sessionKey, taskId);
|
|
80
|
+
if (checkpointId !== undefined && gate !== undefined && gate.checkpointId === checkpointId)
|
|
81
|
+
return now(gate.facts, gate.generation);
|
|
82
|
+
const flightKey = `${sessionKey}\u0000${taskId}\u0000${checkpointId ?? ''}`;
|
|
83
|
+
const inFlight = planReviewFactsFlights.get(flightKey);
|
|
84
|
+
if (inFlight !== undefined)
|
|
85
|
+
return { kind: 'ledger', pending: inFlight };
|
|
86
|
+
let src;
|
|
87
|
+
try {
|
|
88
|
+
src = source();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
src = undefined;
|
|
92
|
+
}
|
|
93
|
+
if (src === undefined)
|
|
94
|
+
return now(mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('not_observed')), g0);
|
|
95
|
+
const reader = src;
|
|
96
|
+
const started = Date.now();
|
|
97
|
+
const ledger = (budgetMs) => readPlanReviewFactsFromLedger(beginPlanReviewFactsRead(sessionKey, taskId), checkpointId, reader, signal, budgetMs);
|
|
98
|
+
const viaRow = checkpointId === undefined && gate !== undefined
|
|
99
|
+
? readPlanGateRowWithin(reader, taskId, signal, PLAN_REVIEW_LEDGER_BUDGET_MS).then((row) => {
|
|
100
|
+
const cur = currentPlanReviewGateOf(sessionKey, taskId);
|
|
101
|
+
if (row.ok && row.atPlanGate && row.checkpointId !== undefined && cur !== undefined && cur.checkpointId === row.checkpointId) {
|
|
102
|
+
return { sessionKey, taskId, facts: cur.facts, generation: cur.generation };
|
|
103
|
+
}
|
|
104
|
+
if (!row.ok)
|
|
105
|
+
return { sessionKey, taskId, facts: mintPlanReviewCardFacts(taskId, undefined, unknownBody('unreadable')), generation: g0 };
|
|
106
|
+
return ledger(Math.max(0, PLAN_REVIEW_LEDGER_BUDGET_MS - (Date.now() - started)));
|
|
107
|
+
})
|
|
108
|
+
: undefined;
|
|
109
|
+
const pending = (viaRow ?? ledger(PLAN_REVIEW_LEDGER_BUDGET_MS)).then((taken) => {
|
|
110
|
+
if (planReviewFactsFlights.get(flightKey) === pending)
|
|
111
|
+
planReviewFactsFlights.delete(flightKey);
|
|
112
|
+
return taken;
|
|
113
|
+
}, () => {
|
|
114
|
+
if (planReviewFactsFlights.get(flightKey) === pending)
|
|
115
|
+
planReviewFactsFlights.delete(flightKey);
|
|
116
|
+
return undefined;
|
|
117
|
+
});
|
|
118
|
+
planReviewFactsFlights.set(flightKey, pending);
|
|
119
|
+
return { kind: 'ledger', pending };
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return now(mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('unreadable')), Number.NaN);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const rowUnread = () => ({ ok: false, atPlanGate: false });
|
|
126
|
+
async function readPlanGateRow(source, taskId, signal) {
|
|
127
|
+
try {
|
|
128
|
+
return planGateRowReadOf(await source.runRow(taskId, signal));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return rowUnread();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function readPlanGateRowWithin(source, taskId, outer, budgetMs) {
|
|
135
|
+
if (outer?.aborted === true)
|
|
136
|
+
return rowUnread();
|
|
137
|
+
let onOuterAbort;
|
|
138
|
+
try {
|
|
139
|
+
const aborted = new Promise((resolve) => {
|
|
140
|
+
onOuterAbort = () => resolve(rowUnread());
|
|
141
|
+
outer?.addEventListener('abort', onOuterAbort, { once: true });
|
|
142
|
+
});
|
|
143
|
+
const read = settleWithinDeadline(budgetMs, (deadline) => {
|
|
144
|
+
const ac = new AbortController();
|
|
145
|
+
const stop = () => ac.abort();
|
|
146
|
+
deadline.addEventListener('abort', stop, { once: true });
|
|
147
|
+
outer?.addEventListener('abort', stop, { once: true });
|
|
148
|
+
return readPlanGateRow(source, taskId, ac.signal).finally(() => {
|
|
149
|
+
deadline.removeEventListener('abort', stop);
|
|
150
|
+
outer?.removeEventListener('abort', stop);
|
|
151
|
+
});
|
|
152
|
+
}).then((settled) => (settled.kind === 'value' ? settled.value : rowUnread()));
|
|
153
|
+
return await Promise.race([read, aborted]);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return rowUnread();
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
if (onOuterAbort !== undefined)
|
|
160
|
+
outer?.removeEventListener('abort', onOuterAbort);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function foldLedgerFrame(fold, ev) {
|
|
164
|
+
if (typeof ev !== 'object' || ev === null)
|
|
165
|
+
return fold;
|
|
166
|
+
const snap = snapshotSegmentIdentity(ev);
|
|
167
|
+
return stepPlanReviewLedgerFold(fold, ev, snap === null || snapshotIsSubFlow(snap));
|
|
168
|
+
}
|
|
169
|
+
async function readPlanReviewFactsFromLedger(ticket, given, source, signal, budgetMs) {
|
|
170
|
+
const { sessionKey, taskId } = ticket;
|
|
171
|
+
const reads = [];
|
|
172
|
+
let claim;
|
|
173
|
+
let highWater;
|
|
174
|
+
try {
|
|
175
|
+
const snap = await readRunLedgerSnapshot(taskId, undefined, {
|
|
176
|
+
isParked: async (id, s) => {
|
|
177
|
+
const r = await readPlanGateRow(source, id, s);
|
|
178
|
+
reads.push(r);
|
|
179
|
+
const first = reads[0];
|
|
180
|
+
if (!r.ok || !r.atPlanGate || r.checkpointId === undefined)
|
|
181
|
+
return false;
|
|
182
|
+
if (given !== undefined && r.checkpointId !== given)
|
|
183
|
+
return false;
|
|
184
|
+
return reads.length === 1 || (first !== undefined && first.ok && first.checkpointId === r.checkpointId);
|
|
185
|
+
},
|
|
186
|
+
frames: (id, s) => source.frames(id, s),
|
|
187
|
+
budgetMs,
|
|
188
|
+
log: (msg) => {
|
|
189
|
+
hostLog('debug', `planReviewCard: ${msg}`);
|
|
190
|
+
},
|
|
191
|
+
fold: {
|
|
192
|
+
init: openPlanReviewLedgerFold,
|
|
193
|
+
step: foldLedgerFrame,
|
|
194
|
+
strictOrder: true,
|
|
195
|
+
substantive: isPlanReviewClaimFrame,
|
|
196
|
+
agree: (a, b) => {
|
|
197
|
+
const x = planReviewLedgerClaimOf(a);
|
|
198
|
+
const y = planReviewLedgerClaimOf(b);
|
|
199
|
+
return x === undefined || y === undefined ? x === y : samePlanClaim(x, y);
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
}, signal);
|
|
203
|
+
if (snap !== null && snap.folded !== undefined) {
|
|
204
|
+
claim = planReviewLedgerClaimOf(snap.folded);
|
|
205
|
+
if (claim !== undefined)
|
|
206
|
+
highWater = snap.highWater;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
claim = undefined;
|
|
211
|
+
}
|
|
212
|
+
const first = reads[0];
|
|
213
|
+
const firstGate = first !== undefined && first.ok && first.atPlanGate ? first.checkpointId : undefined;
|
|
214
|
+
let facts;
|
|
215
|
+
if (claim !== undefined && firstGate !== undefined) {
|
|
216
|
+
facts = mintPlanReviewCardFacts(taskId, firstGate, claim.kind === 'text' ? planReviewBodyOf(claim.text) : unknownBody(claim.cause));
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const missed = ledgerMissOf(reads, given);
|
|
220
|
+
claim = { kind: 'cause', cause: missed.cause };
|
|
221
|
+
highWater = undefined;
|
|
222
|
+
facts = mintPlanReviewCardFacts(taskId, missed.checkpointId, unknownBody(missed.cause));
|
|
223
|
+
}
|
|
224
|
+
const out = commitPlanReviewFacts(ticket, facts, claim, highWater);
|
|
225
|
+
if (out === undefined) {
|
|
226
|
+
hostLog('debug', `planReviewCard: ledger-read-superseded task=${taskId}`);
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
return { sessionKey, taskId, facts: out, generation: ticket.generation };
|
|
230
|
+
}
|
|
231
|
+
function ledgerMissOf(reads, given) {
|
|
232
|
+
const first = reads[0];
|
|
233
|
+
if (first === undefined || !first.ok)
|
|
234
|
+
return { cause: 'unreadable', ...(given !== undefined ? { checkpointId: given } : {}) };
|
|
235
|
+
if (!first.atPlanGate || first.checkpointId === undefined)
|
|
236
|
+
return { cause: 'gate_unidentified' };
|
|
237
|
+
const asked = given ?? first.checkpointId;
|
|
238
|
+
if (given !== undefined && first.checkpointId !== given)
|
|
239
|
+
return { cause: 'gate_moved', checkpointId: asked };
|
|
240
|
+
const second = reads[1];
|
|
241
|
+
if (second !== undefined) {
|
|
242
|
+
if (!second.ok)
|
|
243
|
+
return { cause: 'unreadable', checkpointId: asked };
|
|
244
|
+
if (!second.atPlanGate || second.checkpointId === undefined)
|
|
245
|
+
return { cause: 'gate_unidentified' };
|
|
246
|
+
if (second.checkpointId !== first.checkpointId)
|
|
247
|
+
return { cause: 'gate_moved', checkpointId: asked };
|
|
248
|
+
}
|
|
249
|
+
return { cause: 'unreadable', checkpointId: asked };
|
|
250
|
+
}
|
|
@@ -2,34 +2,52 @@ import { type QuestionAnswer } from '../liveQuestionStore.js';
|
|
|
2
2
|
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
3
3
|
import { type PlanReviewModeAfter } from './hitlBridge.js';
|
|
4
4
|
import type { AgentClient } from '@sema-agent/sdk';
|
|
5
|
+
import { isPlanReviewPark, type PlanReviewCardFacts } from '../planReviewCardFacts.js';
|
|
6
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
5
7
|
import type { ReopenCardVerdict } from '../adapter/activeRunSelfHeal.js';
|
|
6
8
|
export declare const PLAN_REVIEW_APPROVE_LABEL = "Yes, approve and run the plan";
|
|
7
9
|
export declare const PLAN_REVIEW_REJECT_LABEL = "No, reject it (keep planning)";
|
|
8
10
|
export declare const PLAN_REVIEW_APPROVE_AUTO_LABEL = "Yes, and auto-accept edits";
|
|
9
11
|
export declare const PLAN_REVIEW_APPROVE_MANUAL_LABEL = "Yes, and manually approve edits";
|
|
10
|
-
export
|
|
12
|
+
export type PlanReviewOptionChoice = 'approve' | 'approve_accept_edits' | 'approve_manual' | 'reject' | 'ask_again';
|
|
13
|
+
export interface PlanReviewOption {
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly description: string;
|
|
16
|
+
readonly choice: PlanReviewOptionChoice;
|
|
17
|
+
}
|
|
18
|
+
type PlanReviewNonDecision = 'dismissed' | 'inadmissible';
|
|
19
|
+
export declare function planReviewDecisionFromAnswer(answer: QuestionAnswer | null | undefined, shownLabels?: readonly string[]): 'approve' | 'reject' | 'ask_again' | PlanReviewNonDecision;
|
|
11
20
|
export type PlanReviewChoice = {
|
|
12
21
|
decision: 'approve';
|
|
13
22
|
permissionModeAfter?: PlanReviewModeAfter;
|
|
14
23
|
} | {
|
|
15
24
|
decision: 'reject';
|
|
16
25
|
reason?: string;
|
|
26
|
+
} | {
|
|
27
|
+
decision: 'ask_again';
|
|
28
|
+
reason?: string;
|
|
17
29
|
};
|
|
18
|
-
export declare function planReviewChoiceFromAnswer(answer: QuestionAnswer | null | undefined, shownLabels?: readonly string[]): PlanReviewChoice |
|
|
30
|
+
export declare function planReviewChoiceFromAnswer(answer: QuestionAnswer | null | undefined, shownLabels?: readonly string[]): PlanReviewChoice | PlanReviewNonDecision;
|
|
19
31
|
export declare function planReviewCardOptions(offerModeAfter: boolean): Array<{
|
|
20
32
|
label: string;
|
|
21
33
|
description: string;
|
|
22
34
|
}>;
|
|
35
|
+
export declare function planReviewOptionsOf(facts: PlanReviewCardFacts, opts?: {
|
|
36
|
+
readonly submittedInPlanMode?: boolean;
|
|
37
|
+
}): readonly PlanReviewOption[];
|
|
38
|
+
export interface PlanReviewCardView {
|
|
39
|
+
readonly facts?: PlanReviewCardFacts;
|
|
40
|
+
readonly options: readonly PlanReviewOption[];
|
|
41
|
+
readonly note?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function planReviewCardViewOf(frame: object | null | undefined, lang?: UiLanguage): PlanReviewCardView | undefined;
|
|
23
44
|
export declare function notePlanReviewAnsweredIfDecisive(questionId: unknown, answer: QuestionAnswer | null | undefined): void;
|
|
24
|
-
export
|
|
25
|
-
taskId: string;
|
|
26
|
-
sessionId?: string;
|
|
27
|
-
};
|
|
45
|
+
export { isPlanReviewPark };
|
|
28
46
|
export declare function _resetArmedPlanReviewsForTest(): void;
|
|
29
47
|
export type PlanReviewReopenTrigger = 'user' | 'automatic';
|
|
30
48
|
export type PlanReviewGateInstanceReader = (taskId: string) => Promise<ReadonlySet<string> | null>;
|
|
31
49
|
export declare function clearPlanReviewUserDismissals(sessionKey?: string): void;
|
|
32
|
-
export type PlanReviewOfferedMode = 'three_choice' | 'two_choice' | 'unknown';
|
|
50
|
+
export type PlanReviewOfferedMode = 'three_choice' | 'two_choice' | 'withheld' | 'unknown';
|
|
33
51
|
export interface PlanReviewCardLike {
|
|
34
52
|
readonly questionId?: string;
|
|
35
53
|
readonly questions?: ReadonlyArray<{
|
|
@@ -45,8 +63,8 @@ export interface PlanReviewQuestionIdReading {
|
|
|
45
63
|
export declare function planReviewTaskIdFromQuestionId(questionId: {} | null | undefined): PlanReviewQuestionIdReading | undefined;
|
|
46
64
|
export declare function planReviewOfferedModeOf(card: string | PlanReviewCardLike): PlanReviewOfferedMode;
|
|
47
65
|
export declare function planReviewShownLabelsOf(questionId: {} | null | undefined): readonly string[] | undefined;
|
|
48
|
-
export declare function planReviewAnswerChoiceOf(questionId: {} | null | undefined, answer: QuestionAnswer | null | undefined): PlanReviewChoice |
|
|
49
|
-
export declare function planReviewAnswerDecisionOf(questionId: {} | null | undefined, answer: QuestionAnswer | null | undefined): 'approve' | 'reject' | '
|
|
66
|
+
export declare function planReviewAnswerChoiceOf(questionId: {} | null | undefined, answer: QuestionAnswer | null | undefined): PlanReviewChoice | PlanReviewNonDecision;
|
|
67
|
+
export declare function planReviewAnswerDecisionOf(questionId: {} | null | undefined, answer: QuestionAnswer | null | undefined): 'approve' | 'reject' | 'ask_again' | PlanReviewNonDecision;
|
|
50
68
|
export interface ArmPlanReviewOptions {
|
|
51
69
|
submittedInPlanMode?: boolean;
|
|
52
70
|
readGateInstanceKeys?: PlanReviewGateInstanceReader;
|
|
@@ -58,14 +76,14 @@ export type PlanReviewDecisionEffect = 'took_effect' | 'advanced' | 'still_parke
|
|
|
58
76
|
export interface PlanReviewOutcomeMeta {
|
|
59
77
|
taskId: string;
|
|
60
78
|
dispatchNo: number;
|
|
61
|
-
decision: 'approve' | 'reject';
|
|
79
|
+
decision: 'approve' | 'reject' | 'ask_again';
|
|
62
80
|
effect: PlanReviewDecisionEffect;
|
|
63
81
|
cause?: 'stored_form_unsupported';
|
|
64
82
|
}
|
|
65
83
|
export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect, cause?: 'stored_form_unsupported'): string;
|
|
66
84
|
export type PlanReviewWireClient = {
|
|
67
85
|
assistant: Pick<AgentClient['assistant'], 'planReview'>;
|
|
68
|
-
runs: Pick<AgentClient['runs'], 'get'
|
|
86
|
+
runs: Pick<AgentClient['runs'], 'get'> & Partial<Pick<AgentClient['runs'], 'events'>>;
|
|
69
87
|
};
|
|
70
88
|
export interface PlanReviewWire extends EngineWireInjection<PlanReviewWireClient> {
|
|
71
89
|
client: PlanReviewWireClient;
|
|
@@ -82,7 +100,7 @@ export interface PlanReviewDeliveryOptions {
|
|
|
82
100
|
}
|
|
83
101
|
export interface DecidePlanReviewOptions extends PlanReviewDeliveryOptions {
|
|
84
102
|
reason?: string;
|
|
85
|
-
|
|
103
|
+
card?: PlanReviewCardFacts;
|
|
86
104
|
}
|
|
87
105
|
export interface PlanReviewDecisionHandle {
|
|
88
106
|
settle(): void;
|
|
@@ -95,17 +113,18 @@ export declare function notePlanReviewDecisionNotApplied(taskId: string): void;
|
|
|
95
113
|
export declare function notePlanReviewGateLeft(taskId: string): void;
|
|
96
114
|
export declare function latestPlanReviewDecisionMark(taskId: string): number | null;
|
|
97
115
|
export declare function __resetPlanReviewDecisionLatchForTests(): void;
|
|
98
|
-
export declare function decidePlanReview(taskId: string, decision: 'approve' | 'reject', permissionModeAfter?: string, opts?: DecidePlanReviewOptions): Promise<void>;
|
|
116
|
+
export declare function decidePlanReview(taskId: string, decision: 'approve' | 'reject' | 'ask_again', permissionModeAfter?: string, opts?: DecidePlanReviewOptions): Promise<void>;
|
|
99
117
|
export interface ReopenPlanReviewSharedOpts {
|
|
100
118
|
sessionKey?: string;
|
|
101
119
|
presentationReceiptMs: number;
|
|
102
120
|
trigger?: PlanReviewReopenTrigger;
|
|
103
121
|
readGateInstanceKeys?: PlanReviewGateInstanceReader;
|
|
104
122
|
pendingReadStartedAt?: number;
|
|
123
|
+
checkpointId?: string;
|
|
105
124
|
}
|
|
106
125
|
export interface ReopenPlanReviewFreshOpts extends ReopenPlanReviewSharedOpts {
|
|
107
126
|
mintFreshQuestionId?: true;
|
|
108
|
-
deliverDecision?: (taskId: string, decision:
|
|
127
|
+
deliverDecision?: (taskId: string, decision: Parameters<typeof decidePlanReview>[1], reason: string | undefined, card: PlanReviewCardFacts) => void | Promise<void>;
|
|
109
128
|
wire?: PlanReviewWire;
|
|
110
129
|
onOutcome?: PlanReviewDeliveryOptions['onOutcome'];
|
|
111
130
|
}
|
|
@@ -118,3 +137,9 @@ export interface ReopenPlanReviewReuseOpts extends ReopenPlanReviewSharedOpts {
|
|
|
118
137
|
export type ReopenPlanReviewOpts = ReopenPlanReviewFreshOpts | ReopenPlanReviewReuseOpts;
|
|
119
138
|
export declare function _resetActiveReopenRespondersForTest(): void;
|
|
120
139
|
export declare function reopenPlanReviewCard(taskId: string, opts: ReopenPlanReviewOpts): Promise<ReopenCardVerdict>;
|
|
140
|
+
export interface ReadPlanReviewCardFactsOptions {
|
|
141
|
+
readonly checkpointId?: string;
|
|
142
|
+
readonly wire?: PlanReviewWire;
|
|
143
|
+
readonly signal?: AbortSignal;
|
|
144
|
+
}
|
|
145
|
+
export declare function readPlanReviewCardFacts(taskId: string, opts?: ReadPlanReviewCardFactsOptions): Promise<PlanReviewCardFacts>;
|