@sema-agent/client-core 0.104.0 → 0.105.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 +99 -0
- package/README.md +13 -5
- package/dist/adapt/arms.js +14 -6
- package/dist/adapter/activeRunSelfHeal.d.ts +1 -0
- package/dist/adapter/activeRunSelfHeal.js +10 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -4
- package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
- package/dist/adapter/downstream/wiringManifestView.js +25 -0
- package/dist/adapter/runStream.js +5 -1
- package/dist/cloudConfigWireCaps.js +3 -0
- package/dist/compensations.js +6 -6
- package/dist/coreValuePorts.d.ts +3 -1
- package/dist/coreValuePorts.js +4 -0
- package/dist/displayUntrusted.d.ts +4 -2
- package/dist/displayUntrusted.js +45 -22
- 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/eventLeg.d.ts +1 -1
- package/dist/eventLeg.js +23 -3
- package/dist/generated/engineFactTables.d.ts +1 -0
- package/dist/generated/engineFactTables.js +1 -0
- package/dist/generated/sdkFactTables.d.ts +2 -0
- package/dist/generated/sdkFactTables.js +9 -0
- package/dist/headlessReconnectWire.js +31 -4
- package/dist/hitl/askGateWire.js +26 -2
- package/dist/hitl/frameRouter.d.ts +4 -0
- package/dist/hitl/frameRouter.js +14 -0
- package/dist/hitl/gateIdentity.d.ts +1 -0
- package/dist/hitl/gateIdentity.js +3 -0
- package/dist/hitl/livePendingAsk.d.ts +2 -1
- package/dist/hitl/livePendingAsk.js +7 -1
- package/dist/hitl/planReviewCard.d.ts +21 -0
- package/dist/hitl/planReviewCard.js +229 -0
- package/dist/hitl/planReviewWire.d.ts +38 -13
- package/dist/hitl/planReviewWire.js +355 -133
- package/dist/hitl/toolApprovalWire.d.ts +2 -2
- package/dist/hitl/toolApprovalWire.js +53 -32
- package/dist/hostSettingsSource.d.ts +1 -1
- package/dist/hostSettingsSource.js +8 -4
- package/dist/hostTurnSettings.d.ts +1 -1
- package/dist/hostTurnSettings.js +29 -10
- package/dist/i18n/messages.en.d.ts +50 -0
- package/dist/i18n/messages.en.js +50 -0
- package/dist/i18n/messages.zh.js +51 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +9 -1
- package/dist/mcpTransportKindsCapability.d.ts +18 -0
- package/dist/mcpTransportKindsCapability.js +75 -0
- package/dist/mcpWireCaps.d.ts +4 -1
- package/dist/mcpWireCaps.js +24 -13
- package/dist/notifications.d.ts +9 -0
- package/dist/notifications.js +11 -2
- package/dist/ownKey.d.ts +4 -0
- package/dist/ownKey.js +8 -0
- 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 +23 -1
- package/dist/permissionsDeclaration.js +60 -30
- package/dist/planReviewCardFacts.d.ts +116 -0
- package/dist/planReviewCardFacts.js +469 -0
- package/dist/reattachWindow.d.ts +8 -1
- package/dist/reattachWindow.js +44 -12
- 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 +13 -5
- package/dist/runReattachPolicy.js +58 -23
- package/dist/seam.d.ts +3 -0
- package/dist/seam.js +1 -1
- package/dist/seatContract.js +1 -0
- 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/sseIdleTriage.d.ts +3 -1
- package/dist/sseIdleTriage.js +19 -4
- package/dist/steering.d.ts +1 -0
- package/dist/steering.js +34 -26
- package/dist/subagent/engineSubagentTail.js +2 -0
- package/dist/subagentContentStore.d.ts +3 -0
- package/dist/subagentContentStore.js +10 -1
- package/dist/turnFailurePresentation.d.ts +5 -2
- package/dist/turnFailurePresentation.js +12 -4
- package/docs/INTEGRATION-CLIENTS.md +587 -10
- package/package.json +2 -2
|
@@ -3,7 +3,17 @@ import { hostEnv } from './hostEnv.js';
|
|
|
3
3
|
import { isSdkRunFrameSynthesizable } from './terminalStatuses.js';
|
|
4
4
|
import { createStreamDrainingLegs } from './streamDrainingLegs.js';
|
|
5
5
|
import { abortableSleep } from './abortableSleep.js';
|
|
6
|
+
import { mintRunNotFollowed, registerRunNotFollowed } from './runFollow.js';
|
|
7
|
+
import { classifyTurnWireError } from './wireErrorTriage.js';
|
|
6
8
|
export const HEADLESS_RECONNECT_MAX = 2;
|
|
9
|
+
function guardedTransport(e) {
|
|
10
|
+
try {
|
|
11
|
+
return classifyTurnWireError(e).kind === 'transport';
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
7
17
|
function debugLog(msg) {
|
|
8
18
|
if (hostEnv().SEMA_DEBUG) {
|
|
9
19
|
console.error(`[sema] headless reconnect: ${msg}`);
|
|
@@ -46,6 +56,12 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
46
56
|
let isRecoverableTear;
|
|
47
57
|
const drains = createStreamDrainingLegs();
|
|
48
58
|
let drainReopenAfterMs;
|
|
59
|
+
let lastBreak;
|
|
60
|
+
const giveUp = (fact) => {
|
|
61
|
+
if (typeof originalTearErr === 'object' && originalTearErr !== null)
|
|
62
|
+
registerRunNotFollowed(originalTearErr, fact);
|
|
63
|
+
throw originalTearErr;
|
|
64
|
+
};
|
|
49
65
|
const terminalFromRunRow = async (taskId) => {
|
|
50
66
|
const row = await deps.getRun(taskId).catch(() => null);
|
|
51
67
|
const terminal = terminalEventFromRunRow(row);
|
|
@@ -60,9 +76,18 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
60
76
|
isRecoverableTear = isSseIdleError(originalTearErr);
|
|
61
77
|
if (!isRecoverableTear)
|
|
62
78
|
throw originalTearErr;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
let handle;
|
|
80
|
+
try {
|
|
81
|
+
handle = deps.getTaskId();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return giveUp(mintRunNotFollowed('bad_input'));
|
|
85
|
+
}
|
|
86
|
+
if (handle === null || handle === undefined)
|
|
87
|
+
return giveUp(mintRunNotFollowed('no_run_id'));
|
|
88
|
+
if (typeof handle !== 'string' || handle === '')
|
|
89
|
+
return giveUp(mintRunNotFollowed('bad_input'));
|
|
90
|
+
const taskId = handle;
|
|
66
91
|
if (drainReopenAfterMs !== undefined) {
|
|
67
92
|
const waitMs = drainReopenAfterMs;
|
|
68
93
|
drainReopenAfterMs = undefined;
|
|
@@ -74,7 +99,7 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
74
99
|
else {
|
|
75
100
|
if (attempts >= HEADLESS_RECONNECT_MAX) {
|
|
76
101
|
debugLog(`budget exhausted (${attempts}/${HEADLESS_RECONNECT_MAX}) — falling to the honest death path`);
|
|
77
|
-
|
|
102
|
+
return giveUp(mintRunNotFollowed('reattach_exhausted', taskId, lastBreak));
|
|
78
103
|
}
|
|
79
104
|
attempts++;
|
|
80
105
|
if (!(await probe(deps.baseUrl)))
|
|
@@ -111,6 +136,7 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
111
136
|
if (deps.signal?.aborted)
|
|
112
137
|
return;
|
|
113
138
|
const drained = drains.legEnded('returned');
|
|
139
|
+
lastBreak = drained !== undefined ? 'stream_draining' : undefined;
|
|
114
140
|
if (drained !== undefined) {
|
|
115
141
|
if ('reconnectAfterMs' in drained) {
|
|
116
142
|
drainReopenAfterMs = drained.reconnectAfterMs;
|
|
@@ -128,6 +154,7 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
128
154
|
}
|
|
129
155
|
catch (contErr) {
|
|
130
156
|
drains.legEnded('threw');
|
|
157
|
+
lastBreak = guardedTransport(contErr) ? 'connection_lost' : undefined;
|
|
131
158
|
debugLog(`runs.events continuation failed: ${contErr instanceof Error ? contErr.message : String(contErr)}`);
|
|
132
159
|
if (deps.signal?.aborted)
|
|
133
160
|
throw originalTearErr;
|
package/dist/hitl/askGateWire.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createGateLedger } from './gateLedger.js';
|
|
2
|
-
import { flushHeldWithInterruptRewrite, isHostProgressFrame, routeFrame, } from './frameRouter.js';
|
|
2
|
+
import { bridgeRunHandleFallbackOf, flushHeldWithInterruptRewrite, isHostProgressFrame, routeFrame, } from './frameRouter.js';
|
|
3
3
|
import { resolvePark, nextHopBudget, stalledTerminal, MAX_GATE_HOPS } from './parkResolver.js';
|
|
4
4
|
import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
|
|
5
5
|
import { abortableSleep } from '../abortableSleep.js';
|
|
6
6
|
import { hostLog } from '../host.js';
|
|
7
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
7
8
|
import { decideAcceptedNotResolved, readDecideReceipt } from '../decideReceipt.js';
|
|
8
9
|
import { APPROVAL_EXPIRED } from '../engineErrorCodes.js';
|
|
9
10
|
import { isNotFoundFailure } from '../wireErrorTriage.js';
|
|
@@ -37,11 +38,14 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
37
38
|
let lastRoundDecided = false;
|
|
38
39
|
let unverifiableParks = 0;
|
|
39
40
|
const drains = createStreamDrainingLegs();
|
|
41
|
+
let lastDrainFrame;
|
|
40
42
|
while (true) {
|
|
41
43
|
let park = null;
|
|
42
44
|
for await (const ev of stream) {
|
|
43
|
-
if (drains.see(ev))
|
|
45
|
+
if (drains.see(ev)) {
|
|
46
|
+
lastDrainFrame = ev;
|
|
44
47
|
continue;
|
|
48
|
+
}
|
|
45
49
|
const answering = led.answeringLiveCards();
|
|
46
50
|
if (answering.length > 0)
|
|
47
51
|
await Promise.all(answering);
|
|
@@ -77,6 +81,11 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
77
81
|
const drained = drains.legEnded('returned');
|
|
78
82
|
if (drained !== undefined) {
|
|
79
83
|
const reopenAfterMs = 'reconnectAfterMs' in drained ? drained.reconnectAfterMs : undefined;
|
|
84
|
+
if (taskId.current === undefined) {
|
|
85
|
+
const bound = bridgeRunHandleFallbackOf(deps);
|
|
86
|
+
if (bound !== undefined && reopenAfterMs !== undefined)
|
|
87
|
+
taskId.current = bound;
|
|
88
|
+
}
|
|
80
89
|
if (reopenAfterMs !== undefined && taskId.current !== undefined && !opts?.signal?.aborted) {
|
|
81
90
|
hostLog('debug', `liveHitlAskWire: runs.events(${taskId.current}) ended on the drain end frame — the engine is going down; reopening in ${reopenAfterMs}ms`);
|
|
82
91
|
await abortableSleep(reopenAfterMs, opts?.signal ?? new AbortController().signal);
|
|
@@ -91,6 +100,13 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
hostLog('debug', `liveHitlAskWire: runs.events ended on the drain end frame and is not reopened (${'giveUp' in drained ? drained.giveUp : taskId.current === undefined ? 'no run handle' : 'aborted'}) — ending this leg like a dropped stream`);
|
|
103
|
+
if (!opts?.signal?.aborted) {
|
|
104
|
+
const runId = taskId.current;
|
|
105
|
+
const event = 'giveUp' in drained
|
|
106
|
+
? { cause: 'stream_draining', ladder: 'exhausted', ...(runId !== undefined ? { runId } : {}) }
|
|
107
|
+
: { cause: 'stream_draining', frame: lastDrainFrame ?? {}, ...(runId !== undefined ? { runId } : {}) };
|
|
108
|
+
notifyBreak(deps, Object.freeze(event), opts?.signal);
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
111
|
yield* flushHeldWithInterruptRewrite(led, { signal: opts?.signal });
|
|
96
112
|
return;
|
|
@@ -161,6 +177,14 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
161
177
|
});
|
|
162
178
|
}
|
|
163
179
|
}
|
|
180
|
+
function notifyBreak(deps, event, signal) {
|
|
181
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onBreak', () => {
|
|
182
|
+
const cb = deps.onBreak;
|
|
183
|
+
if (typeof cb !== 'function' || signal?.aborted === true)
|
|
184
|
+
return;
|
|
185
|
+
return cb.call(deps, event);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
164
188
|
export const LATE_DECIDE_OUTCOMES = Object.freeze(['accepted-not-resolved', 'resumed', 'expired-reaped', 'settled-elsewhere', 'unknown']);
|
|
165
189
|
const LATE_DECIDE_TASK_ID_MAX = 64;
|
|
166
190
|
export function lateDecideOutcomeOf(reply, opts) {
|
|
@@ -5,6 +5,7 @@ import type { ApprovalResolution } from './approvalResolution.js';
|
|
|
5
5
|
import type { GateLedger } from './gateLedger.js';
|
|
6
6
|
import type { NoAllowCause } from './noAllowCause.js';
|
|
7
7
|
import { type ReplayedApprovalDisposition } from './replayedApproval.js';
|
|
8
|
+
import type { ReattachDrainingEvent } from '../runReattachPolicy.js';
|
|
8
9
|
export declare const HITL_REJECT_MESSAGE = "The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.";
|
|
9
10
|
export declare const HITL_POLICY_DENY_MESSAGE: string;
|
|
10
11
|
export type PolicyDenyLane = 'no_approval_ui' | 'dont_ask';
|
|
@@ -31,11 +32,14 @@ export interface AskGateWireDeps {
|
|
|
31
32
|
}) => AsyncGenerator<AgentEvent>;
|
|
32
33
|
respondToolApproval?: RespondToolApprovalFn;
|
|
33
34
|
onParkReattach?: (e: ParkReattachNotice) => void;
|
|
35
|
+
onBreak?: (event: ReattachDrainingEvent) => void;
|
|
34
36
|
approvalLane?: ToolApprovalFrameLaneOpts;
|
|
35
37
|
onToolApprovalOutcome?: (frame: ToolApprovalFrame, resolution: ApprovalResolution) => void | Promise<void>;
|
|
36
38
|
onDecideOperation?: DecideOperationObserver;
|
|
37
39
|
onReplayedApproval?: (frame: ToolApprovalFrame, disposition: ReplayedApprovalDisposition) => void | Promise<void>;
|
|
38
40
|
}
|
|
41
|
+
export declare function setBridgeRunHandleFallback(deps: object, read: () => string | undefined): void;
|
|
42
|
+
export declare function bridgeRunHandleFallbackOf(deps: object): string | undefined;
|
|
39
43
|
type GateKind = 'ask' | 'fs';
|
|
40
44
|
export interface GatePark {
|
|
41
45
|
gate: GateKind;
|
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -83,6 +83,20 @@ function denyStampedEnd(ev, attribution) {
|
|
|
83
83
|
export const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = '[Request interrupted by user for tool use]';
|
|
84
84
|
const RUN_CANCELLED_ERROR_CODE = RUN_CANCELLED_CODE;
|
|
85
85
|
export const ENGINE_ABORT_TOOL_RESULT = 'Operation aborted';
|
|
86
|
+
const bridgeRunHandleFallbacks = new WeakMap();
|
|
87
|
+
export function setBridgeRunHandleFallback(deps, read) {
|
|
88
|
+
bridgeRunHandleFallbacks.set(deps, read);
|
|
89
|
+
}
|
|
90
|
+
export function bridgeRunHandleFallbackOf(deps) {
|
|
91
|
+
try {
|
|
92
|
+
const read = bridgeRunHandleFallbacks.get(deps);
|
|
93
|
+
const v = read?.();
|
|
94
|
+
return typeof v === 'string' && v !== '' ? v : undefined;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
86
100
|
function isAskToolLoose(name) {
|
|
87
101
|
return typeof name === 'string' && name.replace(/[\s_-]+/g, '').toLowerCase() === 'askuserquestion';
|
|
88
102
|
}
|
|
@@ -3,6 +3,7 @@ export declare const REOPEN_ID_TAIL = "#reopen-";
|
|
|
3
3
|
export declare function approvalCallKey(gatedCallId: string | undefined, taskId: string): string;
|
|
4
4
|
export declare function askGateQuestionId(callKey: string): string;
|
|
5
5
|
export declare function planReviewQuestionId(taskId: string): string;
|
|
6
|
+
export declare function freshPlanReviewQuestionId(taskId: string, suffix: string): string;
|
|
6
7
|
export declare function liveFrameCallKey(approvalId: string): string;
|
|
7
8
|
export declare function taskIdFromPlanReviewQuestionId(questionId: string): string | undefined;
|
|
8
9
|
export declare function armedKeyFromQuestionId(questionId: string): string;
|
|
@@ -11,6 +11,9 @@ export function askGateQuestionId(callKey) {
|
|
|
11
11
|
export function planReviewQuestionId(taskId) {
|
|
12
12
|
return `${PLAN_REVIEW_QUESTION_ID_PREFIX}${taskId}`;
|
|
13
13
|
}
|
|
14
|
+
export function freshPlanReviewQuestionId(taskId, suffix) {
|
|
15
|
+
return `${planReviewQuestionId(taskId)}${REOPEN_ID_TAIL}${suffix}`;
|
|
16
|
+
}
|
|
14
17
|
export function liveFrameCallKey(approvalId) {
|
|
15
18
|
return `${HITL_FRAME_CALL_KEY_PREFIX}${approvalId}`;
|
|
16
19
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ApprovalRequestFrame } from '@sema-agent/sdk';
|
|
1
2
|
import type { ApprovalResolution } from './approvalResolution.js';
|
|
2
3
|
import type { ApprovalsFeedEmission } from './approvalsFeed.js';
|
|
3
4
|
import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
|
|
@@ -11,7 +12,7 @@ export interface LivePendingAskView {
|
|
|
11
12
|
governanceForced?: true;
|
|
12
13
|
fromSubagent?: true;
|
|
13
14
|
originTaskId?: string;
|
|
14
|
-
frame?: ToolApprovalFrame;
|
|
15
|
+
frame?: ToolApprovalFrame | ApprovalRequestFrame;
|
|
15
16
|
}
|
|
16
17
|
export type LivePendingReading = {
|
|
17
18
|
kind: 'not_reported';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
|
|
1
2
|
import { appendOwnElement } from '../ownKey.js';
|
|
2
3
|
import { readRequiresRealApproval } from './approvalCardReading.js';
|
|
3
4
|
import { THROWN_SLOT_UNREADABLE, readThrownSlot } from '../wireFailureShape.js';
|
|
@@ -21,6 +22,11 @@ function snapshotMembers(raw) {
|
|
|
21
22
|
}
|
|
22
23
|
return raw.length === length ? out : undefined;
|
|
23
24
|
}
|
|
25
|
+
function openingFrameOf(f) {
|
|
26
|
+
if (isToolApprovalFrame(f))
|
|
27
|
+
return f.type === 'tool_approval' ? f : undefined;
|
|
28
|
+
return isApprovalRequestFrameV1(f) ? f : undefined;
|
|
29
|
+
}
|
|
24
30
|
export function readLivePendingRows(body) {
|
|
25
31
|
if (body === null || typeof body !== 'object' || Array.isArray(body))
|
|
26
32
|
return { kind: 'malformed' };
|
|
@@ -65,7 +71,7 @@ export function readLivePendingRows(body) {
|
|
|
65
71
|
...(o.governanceForced === true ? { governanceForced: true } : {}),
|
|
66
72
|
...(o.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
67
73
|
...(nonEmptyString(o.originTaskId) ? { originTaskId: o.originTaskId } : {}),
|
|
68
|
-
...(
|
|
74
|
+
...((f) => (f !== undefined && f.approvalId === o.approvalId ? { frame: f } : {}))(openingFrameOf(o.frame)),
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
if (members.length > 0 && rows.length === 0)
|
|
@@ -0,0 +1,21 @@
|
|
|
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 interface PlanReviewLedgerSource {
|
|
10
|
+
readonly runRow: (taskId: string, signal: AbortSignal) => Promise<object>;
|
|
11
|
+
readonly frames: (taskId: string, signal: AbortSignal) => AsyncIterable<object>;
|
|
12
|
+
}
|
|
13
|
+
export type PlanReviewFactsTaking = {
|
|
14
|
+
readonly kind: 'now';
|
|
15
|
+
readonly taken: PlanReviewFactsTaken;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'ledger';
|
|
18
|
+
readonly pending: Promise<PlanReviewFactsTaken | undefined>;
|
|
19
|
+
};
|
|
20
|
+
export declare function _resetPlanReviewFactsFlightsForTest(): void;
|
|
21
|
+
export declare function takePlanReviewFacts(sessionKey: string, taskId: string, checkpointId: string | undefined, source: () => PlanReviewLedgerSource | undefined, signal?: AbortSignal): PlanReviewFactsTaking;
|
|
@@ -0,0 +1,229 @@
|
|
|
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
|
+
const PLAN_REVIEW_LEDGER_BUDGET_MS = 3_000;
|
|
50
|
+
const planReviewFactsFlights = new Map();
|
|
51
|
+
export function _resetPlanReviewFactsFlightsForTest() {
|
|
52
|
+
planReviewFactsFlights.clear();
|
|
53
|
+
}
|
|
54
|
+
export function takePlanReviewFacts(sessionKey, taskId, checkpointId, source, signal) {
|
|
55
|
+
const now = (facts, generation) => ({ kind: 'now', taken: { sessionKey, taskId, facts, generation } });
|
|
56
|
+
try {
|
|
57
|
+
const g0 = planReviewRunGeneration(sessionKey, taskId);
|
|
58
|
+
const gate = currentPlanReviewGateOf(sessionKey, taskId);
|
|
59
|
+
if (checkpointId !== undefined && gate !== undefined && gate.checkpointId === checkpointId)
|
|
60
|
+
return now(gate.facts, gate.generation);
|
|
61
|
+
const flightKey = `${sessionKey}\u0000${taskId}\u0000${checkpointId ?? ''}`;
|
|
62
|
+
const inFlight = planReviewFactsFlights.get(flightKey);
|
|
63
|
+
if (inFlight !== undefined)
|
|
64
|
+
return { kind: 'ledger', pending: inFlight };
|
|
65
|
+
let src;
|
|
66
|
+
try {
|
|
67
|
+
src = source();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
src = undefined;
|
|
71
|
+
}
|
|
72
|
+
if (src === undefined)
|
|
73
|
+
return now(mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('not_observed')), g0);
|
|
74
|
+
const reader = src;
|
|
75
|
+
const started = Date.now();
|
|
76
|
+
const ledger = (budgetMs) => readPlanReviewFactsFromLedger(beginPlanReviewFactsRead(sessionKey, taskId), checkpointId, reader, signal, budgetMs);
|
|
77
|
+
const viaRow = checkpointId === undefined && gate !== undefined
|
|
78
|
+
? readPlanGateRowWithin(reader, taskId, signal, PLAN_REVIEW_LEDGER_BUDGET_MS).then((row) => {
|
|
79
|
+
const cur = currentPlanReviewGateOf(sessionKey, taskId);
|
|
80
|
+
if (row.ok && row.atPlanGate && row.checkpointId !== undefined && cur !== undefined && cur.checkpointId === row.checkpointId) {
|
|
81
|
+
return { sessionKey, taskId, facts: cur.facts, generation: cur.generation };
|
|
82
|
+
}
|
|
83
|
+
if (!row.ok)
|
|
84
|
+
return { sessionKey, taskId, facts: mintPlanReviewCardFacts(taskId, undefined, unknownBody('unreadable')), generation: g0 };
|
|
85
|
+
return ledger(Math.max(0, PLAN_REVIEW_LEDGER_BUDGET_MS - (Date.now() - started)));
|
|
86
|
+
})
|
|
87
|
+
: undefined;
|
|
88
|
+
const pending = (viaRow ?? ledger(PLAN_REVIEW_LEDGER_BUDGET_MS)).then((taken) => {
|
|
89
|
+
if (planReviewFactsFlights.get(flightKey) === pending)
|
|
90
|
+
planReviewFactsFlights.delete(flightKey);
|
|
91
|
+
return taken;
|
|
92
|
+
}, () => {
|
|
93
|
+
if (planReviewFactsFlights.get(flightKey) === pending)
|
|
94
|
+
planReviewFactsFlights.delete(flightKey);
|
|
95
|
+
return undefined;
|
|
96
|
+
});
|
|
97
|
+
planReviewFactsFlights.set(flightKey, pending);
|
|
98
|
+
return { kind: 'ledger', pending };
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return now(mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('unreadable')), Number.NaN);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const rowUnread = () => ({ ok: false, atPlanGate: false });
|
|
105
|
+
async function readPlanGateRow(source, taskId, signal) {
|
|
106
|
+
try {
|
|
107
|
+
return planGateRowReadOf(await source.runRow(taskId, signal));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return rowUnread();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function readPlanGateRowWithin(source, taskId, outer, budgetMs) {
|
|
114
|
+
if (outer?.aborted === true)
|
|
115
|
+
return rowUnread();
|
|
116
|
+
let onOuterAbort;
|
|
117
|
+
try {
|
|
118
|
+
const aborted = new Promise((resolve) => {
|
|
119
|
+
onOuterAbort = () => resolve(rowUnread());
|
|
120
|
+
outer?.addEventListener('abort', onOuterAbort, { once: true });
|
|
121
|
+
});
|
|
122
|
+
const read = settleWithinDeadline(budgetMs, (deadline) => {
|
|
123
|
+
const ac = new AbortController();
|
|
124
|
+
const stop = () => ac.abort();
|
|
125
|
+
deadline.addEventListener('abort', stop, { once: true });
|
|
126
|
+
outer?.addEventListener('abort', stop, { once: true });
|
|
127
|
+
return readPlanGateRow(source, taskId, ac.signal).finally(() => {
|
|
128
|
+
deadline.removeEventListener('abort', stop);
|
|
129
|
+
outer?.removeEventListener('abort', stop);
|
|
130
|
+
});
|
|
131
|
+
}).then((settled) => (settled.kind === 'value' ? settled.value : rowUnread()));
|
|
132
|
+
return await Promise.race([read, aborted]);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return rowUnread();
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
if (onOuterAbort !== undefined)
|
|
139
|
+
outer?.removeEventListener('abort', onOuterAbort);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function foldLedgerFrame(fold, ev) {
|
|
143
|
+
if (typeof ev !== 'object' || ev === null)
|
|
144
|
+
return fold;
|
|
145
|
+
const snap = snapshotSegmentIdentity(ev);
|
|
146
|
+
return stepPlanReviewLedgerFold(fold, ev, snap === null || snapshotIsSubFlow(snap));
|
|
147
|
+
}
|
|
148
|
+
async function readPlanReviewFactsFromLedger(ticket, given, source, signal, budgetMs) {
|
|
149
|
+
const { sessionKey, taskId } = ticket;
|
|
150
|
+
const reads = [];
|
|
151
|
+
let claim;
|
|
152
|
+
let highWater;
|
|
153
|
+
try {
|
|
154
|
+
const snap = await readRunLedgerSnapshot(taskId, undefined, {
|
|
155
|
+
isParked: async (id, s) => {
|
|
156
|
+
const r = await readPlanGateRow(source, id, s);
|
|
157
|
+
reads.push(r);
|
|
158
|
+
const first = reads[0];
|
|
159
|
+
if (!r.ok || !r.atPlanGate || r.checkpointId === undefined)
|
|
160
|
+
return false;
|
|
161
|
+
if (given !== undefined && r.checkpointId !== given)
|
|
162
|
+
return false;
|
|
163
|
+
return reads.length === 1 || (first !== undefined && first.ok && first.checkpointId === r.checkpointId);
|
|
164
|
+
},
|
|
165
|
+
frames: (id, s) => source.frames(id, s),
|
|
166
|
+
budgetMs,
|
|
167
|
+
log: (msg) => {
|
|
168
|
+
hostLog('debug', `planReviewCard: ${msg}`);
|
|
169
|
+
},
|
|
170
|
+
fold: {
|
|
171
|
+
init: openPlanReviewLedgerFold,
|
|
172
|
+
step: foldLedgerFrame,
|
|
173
|
+
strictOrder: true,
|
|
174
|
+
substantive: isPlanReviewClaimFrame,
|
|
175
|
+
agree: (a, b) => {
|
|
176
|
+
const x = planReviewLedgerClaimOf(a);
|
|
177
|
+
const y = planReviewLedgerClaimOf(b);
|
|
178
|
+
return x === undefined || y === undefined ? x === y : samePlanClaim(x, y);
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
}, signal);
|
|
182
|
+
if (snap !== null && snap.folded !== undefined) {
|
|
183
|
+
claim = planReviewLedgerClaimOf(snap.folded);
|
|
184
|
+
if (claim !== undefined)
|
|
185
|
+
highWater = snap.highWater;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
claim = undefined;
|
|
190
|
+
}
|
|
191
|
+
const first = reads[0];
|
|
192
|
+
const firstGate = first !== undefined && first.ok && first.atPlanGate ? first.checkpointId : undefined;
|
|
193
|
+
let facts;
|
|
194
|
+
if (claim !== undefined && firstGate !== undefined) {
|
|
195
|
+
facts = mintPlanReviewCardFacts(taskId, firstGate, claim.kind === 'text' ? planReviewBodyOf(claim.text) : unknownBody(claim.cause));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const missed = ledgerMissOf(reads, given);
|
|
199
|
+
claim = { kind: 'cause', cause: missed.cause };
|
|
200
|
+
highWater = undefined;
|
|
201
|
+
facts = mintPlanReviewCardFacts(taskId, missed.checkpointId, unknownBody(missed.cause));
|
|
202
|
+
}
|
|
203
|
+
const out = commitPlanReviewFacts(ticket, facts, claim, highWater);
|
|
204
|
+
if (out === undefined) {
|
|
205
|
+
hostLog('debug', `planReviewCard: ledger-read-superseded task=${taskId}`);
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
return { sessionKey, taskId, facts: out, generation: ticket.generation };
|
|
209
|
+
}
|
|
210
|
+
function ledgerMissOf(reads, given) {
|
|
211
|
+
const first = reads[0];
|
|
212
|
+
if (first === undefined || !first.ok)
|
|
213
|
+
return { cause: 'unreadable', ...(given !== undefined ? { checkpointId: given } : {}) };
|
|
214
|
+
if (!first.atPlanGate || first.checkpointId === undefined)
|
|
215
|
+
return { cause: 'gate_unidentified' };
|
|
216
|
+
const asked = given ?? first.checkpointId;
|
|
217
|
+
if (given !== undefined && first.checkpointId !== given)
|
|
218
|
+
return { cause: 'gate_moved', checkpointId: asked };
|
|
219
|
+
const second = reads[1];
|
|
220
|
+
if (second !== undefined) {
|
|
221
|
+
if (!second.ok)
|
|
222
|
+
return { cause: 'unreadable', checkpointId: asked };
|
|
223
|
+
if (!second.atPlanGate || second.checkpointId === undefined)
|
|
224
|
+
return { cause: 'gate_unidentified' };
|
|
225
|
+
if (second.checkpointId !== first.checkpointId)
|
|
226
|
+
return { cause: 'gate_moved', checkpointId: asked };
|
|
227
|
+
}
|
|
228
|
+
return { cause: 'unreadable', checkpointId: asked };
|
|
229
|
+
}
|
|
@@ -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;
|
|
@@ -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) => 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>;
|