@sema-agent/client-core 0.83.6 → 0.84.1

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/README.md +20 -13
  3. package/dist/abortableSleep.d.ts +10 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/wireShapes.js +3 -2
  6. package/dist/adapter/activeRunSelfHeal.js +7 -7
  7. package/dist/adapter/downstream/eventToSdkMessage.js +6 -2
  8. package/dist/adapter/downstream/terminalToSdkResult.d.ts +1 -0
  9. package/dist/adapter/downstream/terminalToSdkResult.js +10 -5
  10. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  11. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  12. package/dist/agentSession/engineAgentRegistryRead.d.ts +18 -0
  13. package/dist/agentSession/engineAgentRegistryRead.js +167 -0
  14. package/dist/agentsWireCaps.js +10 -1
  15. package/dist/attachmentsWireCaps.js +2 -2
  16. package/dist/controlRouter.js +1 -1
  17. package/dist/decideFailureNote.d.ts +2 -1
  18. package/dist/decideFailureNote.js +5 -3
  19. package/dist/detachWire.d.ts +1 -0
  20. package/dist/detachWire.js +13 -5
  21. package/dist/displayUntrusted.js +114 -18
  22. package/dist/engineAgentAbsence.d.ts +37 -0
  23. package/dist/engineAgentAbsence.js +142 -0
  24. package/dist/engineErrorCodes.d.ts +2 -0
  25. package/dist/engineErrorCodes.js +9 -5
  26. package/dist/engineNoticeCodes.d.ts +43 -0
  27. package/dist/engineNoticeCodes.js +118 -5
  28. package/dist/engineWireSdk.d.ts +2 -0
  29. package/dist/fleet/fleetProjection.js +3 -2
  30. package/dist/frozenSet.d.ts +1 -0
  31. package/dist/frozenSet.js +20 -0
  32. package/dist/handsSeam.d.ts +3 -0
  33. package/dist/handsSeam.js +19 -0
  34. package/dist/hitl/askGateWire.d.ts +1 -1
  35. package/dist/hitl/askGateWire.js +1 -1
  36. package/dist/hitl/hitlHostSurface.js +1 -1
  37. package/dist/hitl/parkResolver.d.ts +1 -0
  38. package/dist/hitl/parkResolver.js +9 -3
  39. package/dist/hitl/planReviewWire.d.ts +30 -3
  40. package/dist/hitl/planReviewWire.js +139 -32
  41. package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
  42. package/dist/hitl/sessionPolicyDeliverable.js +14 -3
  43. package/dist/hitl/sessionPolicyWire.d.ts +40 -0
  44. package/dist/hitl/sessionPolicyWire.js +210 -0
  45. package/dist/hitl/toolApprovalWire.js +2 -2
  46. package/dist/index.d.ts +24 -13
  47. package/dist/index.js +13 -13
  48. package/dist/liveInitToolFace.js +28 -10
  49. package/dist/model/catalogLoader.js +3 -3
  50. package/dist/model/tierVocabulary.js +1 -1
  51. package/dist/request/taskRequest.js +15 -5
  52. package/dist/resumeRefusalCopy.d.ts +11 -0
  53. package/dist/resumeRefusalCopy.js +39 -1
  54. package/dist/rewindWireCaps.d.ts +0 -1
  55. package/dist/seam.d.ts +2 -1
  56. package/dist/seatContract.js +2 -2
  57. package/dist/systemReminderTag.d.ts +5 -0
  58. package/dist/systemReminderTag.js +20 -9
  59. package/dist/toolResult.js +2 -6
  60. package/dist/toolRoster.d.ts +16 -0
  61. package/dist/toolRoster.js +46 -5
  62. package/dist/wireErrorTriage.js +1 -11
  63. package/dist/workflowClient.js +4 -3
  64. package/dist/workflowMonitor.d.ts +1 -1
  65. package/docs/INTEGRATION-CLIENTS.md +701 -37
  66. package/package.json +3 -3
@@ -74,8 +74,15 @@ export const ENGINE_NOTICE_CODES = Object.freeze([
74
74
  'tool_result.offload_put_failed',
75
75
  ]);
76
76
  const CATALOG = new Set(ENGINE_NOTICE_CODES);
77
+ export const SERVER_NOTICE_AUDIENCE = Object.freeze({
78
+ 'instructions.source_changed': 'user',
79
+ 'memory.layer_locked_legacy': 'operator',
80
+ });
81
+ function audienceRow(table, code) {
82
+ return Object.hasOwn(table, code) ? table[code] : undefined;
83
+ }
77
84
  export function engineNoticeInCatalog(code) {
78
- return typeof code === 'string' && CATALOG.has(code);
85
+ return typeof code === 'string' && (CATALOG.has(code) || Object.hasOwn(SERVER_NOTICE_AUDIENCE, code));
79
86
  }
80
87
  export const ENGINE_NOTICE_AUDIENCE = Object.freeze({
81
88
  'config.autocompact_window_clamped': 'operator',
@@ -153,10 +160,9 @@ export const ENGINE_NOTICE_AUDIENCE = Object.freeze({
153
160
  'tool_result.offload_put_failed': 'operator',
154
161
  });
155
162
  export function noticeAudienceOf(code) {
156
- const row = typeof code === 'string' && Object.hasOwn(ENGINE_NOTICE_AUDIENCE, code)
157
- ? ENGINE_NOTICE_AUDIENCE[code]
158
- : undefined;
159
- return row ?? 'operator';
163
+ if (typeof code !== 'string')
164
+ return 'operator';
165
+ return audienceRow(ENGINE_NOTICE_AUDIENCE, code) ?? audienceRow(SERVER_NOTICE_AUDIENCE, code) ?? 'operator';
160
166
  }
161
167
  export const MCP_INJECTION_DROP_REASONS = Object.freeze([
162
168
  'malformed_entry',
@@ -303,3 +309,110 @@ export function readMcpServerRedialed(notice) {
303
309
  const reason = str(o.reason);
304
310
  return { server, outcome, added, removed, ...(reason !== undefined ? { reason } : {}), sessionId, runId };
305
311
  }
312
+ const READ_ROOT_GRANT_COVERS_EXACT = 'exact';
313
+ const _grantCoversPin = true;
314
+ void _grantCoversPin;
315
+ export function readReadRootGrantNotice(notice) {
316
+ try {
317
+ return readRootGrantNoticeOf(notice);
318
+ }
319
+ catch {
320
+ return undefined;
321
+ }
322
+ }
323
+ function readRootGrantNoticeOf(notice) {
324
+ if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
325
+ return undefined;
326
+ const n = notice;
327
+ if (n.code !== 'approval.read_root_granted' && n.code !== 'approval.read_root_grant_rejected')
328
+ return undefined;
329
+ const d = n.detail;
330
+ if (typeof d !== 'object' || d === null || Array.isArray(d))
331
+ return undefined;
332
+ const o = d;
333
+ const str = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
334
+ const toolCallId = str(o.toolCallId);
335
+ if (toolCallId === undefined)
336
+ return undefined;
337
+ const sessionId = str(o.sessionId);
338
+ if (n.code === 'approval.read_root_granted') {
339
+ const dir = str(o.dir);
340
+ const root = str(o.root);
341
+ if (dir === undefined || root === undefined)
342
+ return undefined;
343
+ if ('covers' in o && o.covers !== READ_ROOT_GRANT_COVERS_EXACT)
344
+ return undefined;
345
+ return {
346
+ outcome: 'granted',
347
+ toolCallId,
348
+ dir,
349
+ root,
350
+ ...('covers' in o ? { covers: READ_ROOT_GRANT_COVERS_EXACT } : {}),
351
+ ...(sessionId !== undefined ? { sessionId } : {}),
352
+ };
353
+ }
354
+ const reason = str(o.reason);
355
+ return { outcome: 'rejected', toolCallId, ...(reason !== undefined ? { reason } : {}), ...(sessionId !== undefined ? { sessionId } : {}) };
356
+ }
357
+ function ownPathOrNull(o, key) {
358
+ if (!Object.hasOwn(o, key))
359
+ return undefined;
360
+ const v = o[key];
361
+ if (v === null)
362
+ return null;
363
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
364
+ }
365
+ export function readInstructionsSourceChanged(notice) {
366
+ if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
367
+ return undefined;
368
+ try {
369
+ const n = notice;
370
+ if (n.code !== 'instructions.source_changed')
371
+ return undefined;
372
+ const d = n.detail;
373
+ if (typeof d !== 'object' || d === null || Array.isArray(d))
374
+ return undefined;
375
+ const o = d;
376
+ const previous = ownPathOrNull(o, 'previous');
377
+ const current = ownPathOrNull(o, 'current');
378
+ const sessionId = typeof o.sessionId === 'string' && o.sessionId.length > 0 ? o.sessionId : undefined;
379
+ if (previous === undefined || current === undefined || sessionId === undefined)
380
+ return undefined;
381
+ return { previous, current, sessionId };
382
+ }
383
+ catch {
384
+ return undefined;
385
+ }
386
+ }
387
+ export const ENGINE_NOTICE_FACT_READERS = Object.freeze({
388
+ 'mcp.injection_dropped': readMcpInjectionDrop,
389
+ 'delegation.ask_unresolvable': readAskUnresolvable,
390
+ 'config.durable_gate_unavailable': readDurableGateUnavailable,
391
+ 'skills.listing_truncated': readSkillsListingTruncated,
392
+ 'mcp.server_redialed': readMcpServerRedialed,
393
+ 'instructions.source_changed': readInstructionsSourceChanged,
394
+ 'approval.read_root_granted': readReadRootGrantNotice,
395
+ 'approval.read_root_grant_rejected': readReadRootGrantNotice,
396
+ });
397
+ const _factReaderTablePin = true;
398
+ void _factReaderTablePin;
399
+ export function readEngineNoticeFacts(notice) {
400
+ if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
401
+ return undefined;
402
+ let code;
403
+ let detail;
404
+ try {
405
+ code = notice.code;
406
+ if (typeof code !== 'string' || !Object.hasOwn(ENGINE_NOTICE_FACT_READERS, code))
407
+ return undefined;
408
+ detail = notice.detail;
409
+ }
410
+ catch {
411
+ return undefined;
412
+ }
413
+ const k = code;
414
+ const facts = ENGINE_NOTICE_FACT_READERS[k]({ code: k, detail });
415
+ if (facts === undefined)
416
+ return undefined;
417
+ return { code: k, audience: noticeAudienceOf(k), facts };
418
+ }
@@ -6,11 +6,13 @@ export type WireAuthTokenSource = string | (() => string | undefined);
6
6
  export declare function isLoopbackWireUrl(url: string): boolean;
7
7
  export declare function resolveWireAuth(baseUrl: string | undefined, token?: string): WireAuthToken;
8
8
  export declare function wireAuthTokenFor(baseUrl: string, token?: string): WireAuthToken;
9
+ export type EngineProbeClient = Pick<AgentClient, 'capabilities' | 'scenarioCapabilities'>;
9
10
  export type EngineProbeOpts = {
10
11
  fetchImpl?: typeof fetch;
11
12
  timeoutMs?: number;
12
13
  authToken?: WireAuthTokenSource | undefined;
13
14
  principal?: string | undefined;
15
+ client?: EngineProbeClient;
14
16
  };
15
17
  export type EngineWireClientConfig = {
16
18
  baseUrl: string;
@@ -1,6 +1,7 @@
1
1
  import { collapseLabel, escapeDisplayControlChars, taskDescFromName } from '../fleetTaskDesc.js';
2
2
  import { rowIdTail } from '../workflow.js';
3
- export const TERMINAL_FLEET_TASK_STATUSES = new Set([
3
+ import { frozenSet } from '../frozenSet.js';
4
+ export const TERMINAL_FLEET_TASK_STATUSES = frozenSet([
4
5
  'completed',
5
6
  'failed',
6
7
  'killed',
@@ -51,7 +52,7 @@ function deriveAgentLabel(agentType, agentName, name, id) {
51
52
  const tail = rowIdTail(id).slice(-8);
52
53
  return tail ? `agent ${tail}` : 'agent';
53
54
  }
54
- export const CONTROL_TOOL_VERBS = new Set([
55
+ export const CONTROL_TOOL_VERBS = frozenSet([
55
56
  'Agent',
56
57
  'Task',
57
58
  'Fork',
@@ -0,0 +1 @@
1
+ export declare function frozenSet<T>(members: Iterable<T>): ReadonlySet<T>;
@@ -0,0 +1,20 @@
1
+ class FrozenVerdictSet extends Set {
2
+ constructor(members) {
3
+ super();
4
+ for (const m of members)
5
+ super.add(m);
6
+ }
7
+ add(_value) {
8
+ throw new TypeError('This set is read-only: it is a verdict table shared by every caller; copy it into a new Set to change it.');
9
+ }
10
+ delete(_value) {
11
+ throw new TypeError('This set is read-only: it is a verdict table shared by every caller; copy it into a new Set to change it.');
12
+ }
13
+ clear() {
14
+ throw new TypeError('This set is read-only: it is a verdict table shared by every caller; copy it into a new Set to change it.');
15
+ }
16
+ }
17
+ Object.freeze(FrozenVerdictSet.prototype);
18
+ export function frozenSet(members) {
19
+ return Object.freeze(new FrozenVerdictSet(members));
20
+ }
@@ -0,0 +1,3 @@
1
+ import type { WiringManifestHandsView } from './toolRoster.js';
2
+ export declare function projectHandsSection(holder: object): WiringManifestHandsView | undefined;
3
+ export declare const HANDS_REASON_NO_EXECUTION_ENV = "no_execution_env";
@@ -0,0 +1,19 @@
1
+ export function projectHandsSection(holder) {
2
+ if (!Object.hasOwn(holder, 'hands'))
3
+ return undefined;
4
+ const raw = holder.hands;
5
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
6
+ return undefined;
7
+ if (!Object.hasOwn(raw, 'mounted'))
8
+ return undefined;
9
+ const mounted = raw.mounted;
10
+ if (typeof mounted !== 'boolean')
11
+ return undefined;
12
+ const reason = Object.hasOwn(raw, 'reason') ? raw.reason : undefined;
13
+ return { mounted, ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}) };
14
+ }
15
+ export const HANDS_REASON_NO_EXECUTION_ENV = 'no_execution_env';
16
+ const _handsReasonPin = true;
17
+ void _handsReasonPin;
18
+ const _handsViewPin = true;
19
+ void _handsViewPin;
@@ -8,7 +8,7 @@ export type { AskAnsweredOutput } from './gateLedger.js';
8
8
  export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
9
9
  export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
10
10
  export type { GateDenyAttribution } from './gateLedger.js';
11
- export { toAnsweredOutput, isAlreadyResolvedGateReason, isGateStandingErrorCode, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
11
+ export { toAnsweredOutput, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
12
12
  export type { HopBudget, HopRound } from './parkResolver.js';
13
13
  export type { GateOutcome, GateFailureCode } from './parkResolver.js';
14
14
  export declare function bridgeAskUserQuestionGates(source: AsyncGenerator<AgentEvent>, deps: AskGateWireDeps, opts?: {
@@ -6,7 +6,7 @@ export { HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE } from './frameRouter.js';
6
6
  export { HITL_POLICY_DENY_MESSAGE } from './frameRouter.js';
7
7
  export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
8
8
  export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
9
- export { toAnsweredOutput, isAlreadyResolvedGateReason, isGateStandingErrorCode, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
9
+ export { toAnsweredOutput, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure, GATE_FAILURE_CODES, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
10
10
  export async function* bridgeAskUserQuestionGates(source, deps, opts) {
11
11
  const led = createGateLedger();
12
12
  const taskId = { current: opts?.taskId };
@@ -113,7 +113,7 @@ export function observeCancelByDeny(settle, taskId) {
113
113
  hostLog('debug', `[hitl-cancel-deny] cancel-by-deny no_pending for run ${taskId} (benign — already resolved)`);
114
114
  return;
115
115
  }
116
- const rowNote = decideFailureNote(e);
116
+ const rowNote = decideFailureNote(e, 'deny');
117
117
  hostLog('error', `[hitl-cancel-deny] cancel-by-deny FAILED for run ${taskId}: ${String(e)}${rowNote}`);
118
118
  surfaceCancelDenyWarn(`${CANCEL_DENY_WARN_TEXT}${rowNote}`);
119
119
  })
@@ -34,6 +34,7 @@ export type GateOutcome = {
34
34
  };
35
35
  export declare function isAlreadyResolvedGateReason(reason: unknown): boolean;
36
36
  export declare function isGateStandingErrorCode(code: string | null | undefined): boolean;
37
+ export declare function isCodeClassifiedGateFailure(code: string | null | undefined): boolean;
37
38
  export declare function toAnsweredOutput(questions: unknown[], answer: QuestionAnswer): AskAnsweredOutput;
38
39
  export type ParkResolution = {
39
40
  kind: 'reattach';
@@ -1,6 +1,6 @@
1
1
  import { putOwnKey } from '../ownKey.js';
2
2
  import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
3
- import { RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
3
+ import { PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
4
4
  import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
5
5
  import { hostLog } from '../host.js';
6
6
  import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
@@ -44,13 +44,16 @@ function isAlreadyResolvedFailure(outcome) {
44
44
  const code = 'code' in outcome && isGateFailureCode(outcome.code) ? outcome.code : undefined;
45
45
  if (code !== undefined)
46
46
  return code === 'no_pending';
47
- if ('errorCode' in outcome && isGateStandingErrorCode(outcome.errorCode))
47
+ if ('errorCode' in outcome && isCodeClassifiedGateFailure(outcome.errorCode))
48
48
  return false;
49
49
  return isAlreadyResolvedGateReason(outcome.reason);
50
50
  }
51
51
  export function isGateStandingErrorCode(code) {
52
52
  return code === RESUME_POLICY_FOLD_CODE || code === 'corrupt';
53
53
  }
54
+ export function isCodeClassifiedGateFailure(code) {
55
+ return isGateStandingErrorCode(code) || code === PARKED_RESUME_STARTUP_FAILED;
56
+ }
54
57
  export function toAnsweredOutput(questions, answer) {
55
58
  const answers = {};
56
59
  const annotations = {};
@@ -122,10 +125,12 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
122
125
  return { kind: 'aborted', gatedCallId };
123
126
  }
124
127
  let declinedReceipt;
128
+ let attemptedDecision;
125
129
  try {
126
130
  const entries = answer.answers ?? [];
127
131
  const hasContent = entries.some(a => (a.selected?.length ?? 0) > 0 || (a.note?.length ?? 0) > 0);
128
132
  if (hasContent) {
133
+ attemptedDecision = 'approve';
129
134
  const raw = await bridge.answerQuestion(entries, gatedCallId, signal ? { signal } : undefined, pending);
130
135
  const receipt = readDecideReceipt(raw);
131
136
  return {
@@ -136,6 +141,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
136
141
  };
137
142
  }
138
143
  else {
144
+ attemptedDecision = 'deny';
139
145
  const raw = await bridge.decideTool({ decision: 'deny', reason: 'User declined to answer' }, gatedCallId, signal ? { signal } : undefined, pending);
140
146
  declinedReceipt = readDecideReceipt(raw);
141
147
  }
@@ -149,7 +155,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
149
155
  kind: 'failed',
150
156
  stage: 'decide',
151
157
  gatedCallId,
152
- reason: `decide failed: ${String(e)}${decideFailureNote(e)}`,
158
+ reason: `decide failed: ${String(e)}${decideFailureNote(e, attemptedDecision)}`,
153
159
  ...(code !== undefined ? { code } : {}),
154
160
  ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
155
161
  ...(currentPending !== undefined ? { currentPending } : {}),
@@ -1,5 +1,6 @@
1
1
  import { type QuestionAnswer } from '../liveQuestionStore.js';
2
2
  import { type PlanReviewModeAfter } from './hitlBridge.js';
3
+ import type { AgentClient } from '@sema-agent/sdk';
3
4
  import type { ReopenCardVerdict } from '../adapter/activeRunSelfHeal.js';
4
5
  export declare const PLAN_REVIEW_APPROVE_LABEL = "Yes, approve and run the plan";
5
6
  export declare const PLAN_REVIEW_REJECT_LABEL = "No, reject it (keep planning)";
@@ -28,10 +29,13 @@ export declare function _resetArmedPlanReviewsForTest(): void;
28
29
  export type PlanReviewReopenTrigger = 'user' | 'automatic';
29
30
  export type PlanReviewGateInstanceReader = (taskId: string) => Promise<ReadonlySet<string> | null>;
30
31
  export declare function clearPlanReviewUserDismissals(sessionKey?: string): void;
31
- export declare function armPlanReviewApproval(result: unknown, sessionKey?: string, opts?: {
32
+ export interface ArmPlanReviewOptions {
32
33
  submittedInPlanMode?: boolean;
33
34
  readGateInstanceKeys?: PlanReviewGateInstanceReader;
34
- }): boolean;
35
+ wire?: PlanReviewWire;
36
+ onOutcome?: PlanReviewDeliveryOptions['onOutcome'];
37
+ }
38
+ export declare function armPlanReviewApproval(result: unknown, sessionKey?: string, opts?: ArmPlanReviewOptions): boolean;
35
39
  export type PlanReviewDecisionEffect = 'took_effect' | 'advanced' | 'still_parked' | 'not_sent' | 'not_applied' | 'unconfirmed';
36
40
  export interface PlanReviewOutcomeMeta {
37
41
  taskId: string;
@@ -40,12 +44,35 @@ export interface PlanReviewOutcomeMeta {
40
44
  effect: PlanReviewDecisionEffect;
41
45
  }
42
46
  export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect): string;
47
+ export type PlanReviewWireClient = {
48
+ assistant: Pick<AgentClient['assistant'], 'planReview'>;
49
+ runs: Pick<AgentClient['runs'], 'get'>;
50
+ };
51
+ export interface PlanReviewWire {
52
+ client: PlanReviewWireClient;
53
+ capsBaseUrl?: string;
54
+ }
55
+ export interface PlanReviewDecisionOutcome extends PlanReviewOutcomeMeta {
56
+ detail: string;
57
+ tail: string;
58
+ prompt: string;
59
+ status?: string;
60
+ }
61
+ export interface PlanReviewDeliveryOptions {
62
+ wire?: PlanReviewWire;
63
+ onOutcome?: (outcome: PlanReviewDecisionOutcome) => void;
64
+ }
65
+ export interface DecidePlanReviewOptions extends PlanReviewDeliveryOptions {
66
+ reason?: string;
67
+ }
43
68
  export declare function __resetPlanReviewDecisionLatchForTests(): void;
44
- export declare function decidePlanReview(taskId: string, decision: 'approve' | 'reject', permissionModeAfter?: string): Promise<void>;
69
+ export declare function decidePlanReview(taskId: string, decision: 'approve' | 'reject', permissionModeAfter?: string, opts?: DecidePlanReviewOptions): Promise<void>;
45
70
  export interface ReopenPlanReviewOpts {
46
71
  mintFreshQuestionId?: boolean;
47
72
  deliverDecision?: (taskId: string, decision: 'approve' | 'reject') => void | Promise<void>;
48
73
  sessionKey?: string;
74
+ wire?: PlanReviewWire;
75
+ onOutcome?: PlanReviewDeliveryOptions['onOutcome'];
49
76
  presentationReceiptMs?: number;
50
77
  trigger?: PlanReviewReopenTrigger;
51
78
  readGateInstanceKeys?: PlanReviewGateInstanceReader;