@sema-agent/client-core 0.91.0 → 0.93.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.
Files changed (195) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/README.md +51 -34
  3. package/dist/adapt/arms.js +6 -12
  4. package/dist/adapt/panelTasks.d.ts +0 -2
  5. package/dist/adapt/panelTasks.js +6 -26
  6. package/dist/adapt/toolCards.js +6 -4
  7. package/dist/adapt.d.ts +1 -1
  8. package/dist/adapt.js +3 -3
  9. package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
  10. package/dist/adapter/activeRunSelfHeal.js +5 -27
  11. package/dist/adapter/downstream/terminalToSdkResult.js +16 -7
  12. package/dist/adapter/runStream.js +6 -2
  13. package/dist/agentsWireCaps.js +1 -1
  14. package/dist/approvalDecidedByCapability.d.ts +16 -0
  15. package/dist/approvalDecidedByCapability.js +47 -0
  16. package/dist/approvalsStreamLiveCapability.js +3 -3
  17. package/dist/askSurvivesStandingAnswer.d.ts +4 -1
  18. package/dist/askSurvivesStandingAnswer.js +13 -0
  19. package/dist/autoModeUnavailable.js +3 -2
  20. package/dist/centerWiringCapability.js +1 -1
  21. package/dist/cloudConfigWireCaps.js +10 -9
  22. package/dist/compensations.js +7 -7
  23. package/dist/controlRouter.d.ts +23 -23
  24. package/dist/controlRouter.js +114 -49
  25. package/dist/coreValuePorts.d.ts +4 -2
  26. package/dist/coreValuePorts.js +5 -0
  27. package/dist/decideFailureNote.js +4 -2
  28. package/dist/decideReceipt.d.ts +11 -0
  29. package/dist/decideReceipt.js +95 -9
  30. package/dist/detachWire.d.ts +2 -14
  31. package/dist/detachWire.js +1 -47
  32. package/dist/deviceExecutorManagementCapability.d.ts +6 -2
  33. package/dist/deviceExecutorManagementCapability.js +61 -15
  34. package/dist/displayUntrusted.js +2 -1
  35. package/dist/effectiveFacts.d.ts +14 -4
  36. package/dist/effectiveFacts.js +45 -5
  37. package/dist/engineAgentAbsence.js +2 -1
  38. package/dist/engineCapReader.js +16 -10
  39. package/dist/engineCapsCache.d.ts +2 -0
  40. package/dist/engineCapsCache.js +13 -2
  41. package/dist/engineConfigDrift.d.ts +20 -0
  42. package/dist/engineConfigDrift.js +54 -0
  43. package/dist/engineErrorCodes.d.ts +17 -0
  44. package/dist/engineErrorCodes.js +45 -0
  45. package/dist/engineInlineTaskStats.d.ts +1 -0
  46. package/dist/engineNoticeCodes.d.ts +16 -2
  47. package/dist/engineNoticeCodes.js +71 -0
  48. package/dist/engineWireFor.d.ts +22 -0
  49. package/dist/engineWireFor.js +154 -9
  50. package/dist/engineWireSdk.d.ts +0 -1
  51. package/dist/engineWireSdk.js +0 -3
  52. package/dist/executionLaneCapability.js +4 -2
  53. package/dist/fileHistoryCaptureCapability.d.ts +1 -1
  54. package/dist/fileHistoryCaptureCapability.js +1 -6
  55. package/dist/fileHistoryLegNotice.d.ts +1 -1
  56. package/dist/fileHistoryLegNotice.js +1 -6
  57. package/dist/gateOutcome.js +2 -17
  58. package/dist/gateVocabulary.d.ts +2 -8
  59. package/dist/gateVocabulary.js +6 -32
  60. package/dist/generated/engineFactTables.d.ts +17 -1
  61. package/dist/generated/engineFactTables.js +91 -0
  62. package/dist/generated/engineNoticeTables.d.ts +1 -1
  63. package/dist/generated/engineNoticeTables.js +4 -1
  64. package/dist/generated/settingsSchemaTables.d.ts +8 -0
  65. package/dist/generated/settingsSchemaTables.js +40 -0
  66. package/dist/goalStopHook.d.ts +1 -8
  67. package/dist/goalStopHook.js +2 -58
  68. package/dist/headlessPermissionModeWire.d.ts +3 -2
  69. package/dist/headlessPermissionModeWire.js +46 -5
  70. package/dist/hitl/allowRuleShadow.d.ts +21 -0
  71. package/dist/hitl/allowRuleShadow.js +167 -0
  72. package/dist/hitl/askClassPair.d.ts +14 -0
  73. package/dist/hitl/askClassPair.js +42 -0
  74. package/dist/hitl/askGateWire.js +1 -0
  75. package/dist/hitl/askParkRowRouting.js +2 -2
  76. package/dist/hitl/askSurvivesPosture.d.ts +5 -0
  77. package/dist/hitl/askSurvivesPosture.js +17 -0
  78. package/dist/hitl/automaticAnswerVerdict.d.ts +21 -0
  79. package/dist/hitl/automaticAnswerVerdict.js +143 -0
  80. package/dist/hitl/crashConverged.d.ts +1 -0
  81. package/dist/hitl/crashConverged.js +1 -1
  82. package/dist/hitl/frameRouter.d.ts +2 -0
  83. package/dist/hitl/frameRouter.js +73 -16
  84. package/dist/hitl/gateLedger.d.ts +7 -0
  85. package/dist/hitl/gateLedger.js +37 -5
  86. package/dist/hitl/hitlBridge.d.ts +1 -0
  87. package/dist/hitl/hitlBridge.js +6 -1
  88. package/dist/hitl/hitlHostSurface.d.ts +5 -2
  89. package/dist/hitl/hitlHostSurface.js +25 -3
  90. package/dist/hitl/livePendingAsk.js +24 -5
  91. package/dist/hitl/parkResolver.js +24 -4
  92. package/dist/hitl/persistedRulesWire.d.ts +4 -1
  93. package/dist/hitl/persistedRulesWire.js +38 -19
  94. package/dist/hitl/planReviewWire.d.ts +0 -2
  95. package/dist/hitl/planReviewWire.js +3 -20
  96. package/dist/hitl/replayedApproval.d.ts +21 -0
  97. package/dist/hitl/replayedApproval.js +89 -0
  98. package/dist/hitl/sessionPolicyDeliverable.js +20 -2
  99. package/dist/hitl/sessionPolicyWire.d.ts +10 -0
  100. package/dist/hitl/sessionPolicyWire.js +19 -6
  101. package/dist/hitl/toolApprovalWire.d.ts +27 -7
  102. package/dist/hitl/toolApprovalWire.js +153 -94
  103. package/dist/hookMatcherVerdict.js +2 -1
  104. package/dist/hooksWireCaps.d.ts +1 -1
  105. package/dist/hooksWireCaps.js +1 -1
  106. package/dist/humanTurn.js +3 -4
  107. package/dist/index.d.ts +21 -6
  108. package/dist/index.js +17 -4
  109. package/dist/inheritEnvWire.d.ts +1 -4
  110. package/dist/inheritEnvWire.js +3 -22
  111. package/dist/limitsWire.d.ts +0 -6
  112. package/dist/limitsWire.js +0 -38
  113. package/dist/mcpLiveness.js +2 -1
  114. package/dist/mcpProbeCapability.js +1 -1
  115. package/dist/mcpProbeWire.js +10 -12
  116. package/dist/mcpReconnect.d.ts +5 -0
  117. package/dist/mcpReconnect.js +28 -0
  118. package/dist/memoryComplianceCapability.js +2 -2
  119. package/dist/memoryOriginCapability.js +2 -2
  120. package/dist/memoryVerbsWire.d.ts +1 -1
  121. package/dist/memoryVerbsWire.js +17 -13
  122. package/dist/model/catalog.js +2 -1
  123. package/dist/model/providerPresets.d.ts +3 -1
  124. package/dist/model/tierVocabulary.d.ts +2 -1
  125. package/dist/model/tierVocabulary.js +3 -8
  126. package/dist/ownKey.d.ts +2 -0
  127. package/dist/ownKey.js +15 -0
  128. package/dist/peerFrames.js +2 -2
  129. package/dist/peerLaneCapability.js +2 -2
  130. package/dist/permissionModesCapability.d.ts +16 -0
  131. package/dist/permissionModesCapability.js +71 -0
  132. package/dist/permissionRuleIssue.js +10 -7
  133. package/dist/permissionRulesWriteCapability.js +2 -2
  134. package/dist/permissionWireCaps.d.ts +2 -0
  135. package/dist/permissionWireCaps.js +8 -1
  136. package/dist/permissionsAllowGrantCapability.js +7 -2
  137. package/dist/permissionsDeclaration.d.ts +7 -1
  138. package/dist/permissionsDeclaration.js +29 -20
  139. package/dist/pluginHooksWire.js +2 -11
  140. package/dist/postureKnob.js +1 -1
  141. package/dist/promptProfileWireCaps.d.ts +2 -1
  142. package/dist/protocolSegment.d.ts +11 -0
  143. package/dist/protocolSegment.js +31 -0
  144. package/dist/readFacePosture.js +1 -1
  145. package/dist/request/taskRequest.js +1 -15
  146. package/dist/resumeRefusalCopy.d.ts +1 -0
  147. package/dist/resumeRefusalCopy.js +26 -3
  148. package/dist/retryStatus.js +2 -1
  149. package/dist/rewindArchiveCapability.js +2 -2
  150. package/dist/ruleCompileVerdict.d.ts +2 -0
  151. package/dist/ruleCompileVerdict.js +13 -0
  152. package/dist/ruleRemovalConsequence.d.ts +2 -1
  153. package/dist/runCancelContext.d.ts +12 -0
  154. package/dist/runCancelContext.js +83 -1
  155. package/dist/runTerminal.d.ts +16 -0
  156. package/dist/runTerminal.js +32 -3
  157. package/dist/seam.d.ts +1 -0
  158. package/dist/seam.js +1 -1
  159. package/dist/seatContract.d.ts +2 -1
  160. package/dist/sessionMemoryStatus.d.ts +2 -4
  161. package/dist/sessionPolicyCapability.js +1 -1
  162. package/dist/sessionSyncFileHistoryCapability.d.ts +18 -0
  163. package/dist/sessionSyncFileHistoryCapability.js +50 -0
  164. package/dist/skillToolListsCapability.d.ts +18 -0
  165. package/dist/skillToolListsCapability.js +50 -0
  166. package/dist/sqlEngineCapability.js +2 -9
  167. package/dist/steerPriorityCapability.d.ts +18 -0
  168. package/dist/steerPriorityCapability.js +80 -0
  169. package/dist/steerReceipt.d.ts +9 -0
  170. package/dist/steerReceipt.js +25 -0
  171. package/dist/storePostureCapability.js +2 -2
  172. package/dist/subagent/engineSubagentSteer.js +2 -9
  173. package/dist/supportFloor.d.ts +1 -0
  174. package/dist/supportFloor.js +15 -5
  175. package/dist/taskRequestWords.d.ts +4 -2
  176. package/dist/taskRequestWords.js +1 -1
  177. package/dist/toolHistoryBadTurn.js +1 -2
  178. package/dist/toolResult.d.ts +10 -1
  179. package/dist/toolResult.js +80 -6
  180. package/dist/types/engineState.d.ts +2 -12
  181. package/dist/userInvokedSkillsCapability.d.ts +18 -0
  182. package/dist/userInvokedSkillsCapability.js +50 -0
  183. package/dist/webSearchBackendCapability.js +2 -2
  184. package/dist/webSearchWireCaps.d.ts +1 -1
  185. package/dist/webSearchWireCaps.js +40 -2
  186. package/dist/wireFailureShape.d.ts +2 -0
  187. package/dist/wireFailureShape.js +6 -2
  188. package/dist/wireRefusalCopy.d.ts +9 -3
  189. package/dist/wireRefusalCopy.js +41 -4
  190. package/dist/workflowsJournalCapability.d.ts +18 -0
  191. package/dist/workflowsJournalCapability.js +50 -0
  192. package/dist/writeProtectionCapability.d.ts +2 -4
  193. package/dist/writeProtectionCapability.js +1 -1
  194. package/docs/INTEGRATION-CLIENTS.md +1516 -18
  195. package/package.json +2 -2
@@ -2,12 +2,12 @@ import { CHROME_ARMS } from './seam.js';
2
2
  export const MIGRATED_COMPENSATIONS = [
3
3
  {
4
4
  id: 'T32',
5
- what: 'bindTaskCard —— 把引擎子代行绑到它的委派工具卡上(显式父 / FIFO 猜父 / 含糊即 unbound)',
5
+ what: 'bindTaskCard —— 子代行按 typed 父键绑到委派工具卡上(点名的卡不合格 / 父键缺席即 unbound)',
6
6
  disposition: 'in-library',
7
7
  libraryHalf: 'adapt.ts bindTaskCard()',
8
8
  chromeArm: null,
9
9
  hostHalf: null,
10
- retireOn: 'RL-31',
10
+ retireOn: null,
11
11
  },
12
12
  {
13
13
  id: 'T34',
@@ -25,7 +25,7 @@ export const MIGRATED_COMPENSATIONS = [
25
25
  libraryHalf: 'adapt.ts fireSubagentStart()(类型恒记 + 一生一次去重 + if-started 门)',
26
26
  chromeArm: 'subagent_lifecycle',
27
27
  hostHalf: '真执行用户 hook 进程(浏览器宿主根本不能执行 ⇒ 这一半必须留端)',
28
- retireOn: 'RL-32',
28
+ retireOn: null,
29
29
  },
30
30
  {
31
31
  id: 'T36',
@@ -34,7 +34,7 @@ export const MIGRATED_COMPENSATIONS = [
34
34
  libraryHalf: 'adapt.ts settlePanelTasks 的四个调用点(tool_end 关卡 / result / turn 末 / abort)',
35
35
  chromeArm: 'inline_task_stats',
36
36
  hostHalf: null,
37
- retireOn: 'RL-33',
37
+ retireOn: null,
38
38
  },
39
39
  {
40
40
  id: 'T44',
@@ -47,12 +47,12 @@ export const MIGRATED_COMPENSATIONS = [
47
47
  },
48
48
  {
49
49
  id: 'T47',
50
- what: 'workflow lane 三级判据门 —— workflow 子代的 tick 绝不进前台 subagent lane',
50
+ what: 'workflow lane 判据门(帧带 workflowRunId)—— workflow 子代的 tick 绝不进前台 subagent lane',
51
51
  disposition: 'in-library',
52
- libraryHalf: 'adapt.ts task_progress 臂的 workflowRunId / wa-id / workflow-card-parent 三级门',
52
+ libraryHalf: 'adapt.ts task_progress 臂的 workflowRunId 门',
53
53
  chromeArm: null,
54
54
  hostHalf: null,
55
- retireOn: 'RL-34',
55
+ retireOn: null,
56
56
  },
57
57
  {
58
58
  id: 'T48',
@@ -1,13 +1,11 @@
1
- import type { TaskRequest, RunReceipt, CancelAck, Scenario } from '@sema-agent/sdk';
1
+ import type { TaskRequest, RunReceipt, CancelAck, Scenario, SteerPriority } from '@sema-agent/sdk';
2
+ import { type SteerPriorityReading } from './steerPriorityCapability.js';
2
3
  export interface RunsResourceLike {
3
4
  create(req: TaskRequest, opts?: {
4
5
  idempotencyKey?: string | null;
5
6
  signal?: AbortSignal;
6
7
  }): Promise<RunReceipt>;
7
- steer(taskId: string, steer: {
8
- text: string;
9
- mode?: 'all' | 'one-at-a-time';
10
- }, opts?: {
8
+ steer(taskId: string, steer: SteerBodyInput, opts?: {
11
9
  signal?: AbortSignal;
12
10
  }): Promise<unknown>;
13
11
  cancel(taskId: string, opts?: {
@@ -17,7 +15,7 @@ export interface RunsResourceLike {
17
15
  export interface ControlClientLike {
18
16
  runs: RunsResourceLike;
19
17
  }
20
- export type ControlSafetyCode = 'not_running' | 'invalid_content' | 'queue_full' | 'duplicate_input_id' | 'ambiguous_target' | 'still_running' | 'blocked_by_hook' | 'ledger_unavailable' | 'steering_other' | 'cancel_lost_race' | 'cancel_run_not_running' | 'not_found';
18
+ export type ControlSafetyCode = 'not_running' | 'invalid_content' | 'queue_full' | 'duplicate_input_id' | 'ambiguous_target' | 'still_running' | 'blocked_by_hook' | 'ledger_unavailable' | 'steering_other' | 'cancel_lost_race' | 'cancel_run_not_running' | 'not_found' | 'priority_not_applied' | 'steer_input_invalid';
21
19
  export declare class ControlSafetyError extends Error {
22
20
  readonly code: ControlSafetyCode;
23
21
  readonly cause?: unknown | undefined;
@@ -28,11 +26,10 @@ export declare class ControlSafetyError extends Error {
28
26
  readonly status?: number;
29
27
  });
30
28
  }
31
- export type QueuePriority = 'now' | 'next' | 'later';
32
- export interface QueuedCommand {
33
- id: string;
34
- text: string;
35
- priority: QueuePriority;
29
+ export interface ControlRouterSteerOptions {
30
+ signal?: AbortSignal;
31
+ priority?: SteerPriority;
32
+ steerPriority?: SteerPriorityReading;
36
33
  }
37
34
  export interface ControlRouterOptions {
38
35
  sessionId?: string;
@@ -47,8 +44,6 @@ export declare class ControlRouter {
47
44
  private readonly jobId;
48
45
  private readonly scenario;
49
46
  private readonly systemPrompt;
50
- private readonly queue;
51
- private seq;
52
47
  constructor(client: ControlClientLike, opts?: ControlRouterOptions);
53
48
  activeTaskId(): string | null;
54
49
  activeSessionId(): string | undefined;
@@ -56,19 +51,10 @@ export declare class ControlRouter {
56
51
  signal?: AbortSignal;
57
52
  }): Promise<RunReceipt>;
58
53
  private buildTaskRequest;
59
- steer(text: string, mode?: 'all' | 'one-at-a-time', opts?: {
60
- signal?: AbortSignal;
61
- }): Promise<unknown>;
54
+ steer(text: string, mode?: 'all' | 'one-at-a-time', opts?: ControlRouterSteerOptions): Promise<unknown>;
62
55
  cancel(opts?: {
63
56
  signal?: AbortSignal;
64
57
  }): Promise<CancelAck>;
65
- enqueue(text: string, priority?: QueuePriority): QueuedCommand;
66
- dequeue(id: string): boolean;
67
- pending(): readonly QueuedCommand[];
68
- drainNext(opts?: {
69
- signal?: AbortSignal;
70
- }): Promise<RunReceipt | null>;
71
- private nextId;
72
58
  }
73
59
  export interface ErrCodes {
74
60
  code?: string | undefined;
@@ -80,7 +66,21 @@ export declare function steeringStatusIs4xx(e: object): boolean;
80
66
  export interface SteerRefusalAdvice {
81
67
  safety: ControlSafetyCode;
82
68
  advice: string;
69
+ happened: string;
70
+ nextStep: string;
83
71
  }
84
72
  export type SteeringRefusedBeforeEngine = SteerRefusalAdvice;
85
73
  export declare function steeringRefusedBeforeEngine(e: object): SteeringRefusedBeforeEngine | undefined;
86
74
  export declare function steerRefusalAdviceOf(e: object): SteerRefusalAdvice | undefined;
75
+ export interface SteerBody {
76
+ readonly text: string;
77
+ readonly mode?: 'all' | 'one-at-a-time';
78
+ readonly priority?: 'now' | 'later';
79
+ }
80
+ export interface SteerBodyInput {
81
+ text: string;
82
+ mode?: 'all' | 'one-at-a-time';
83
+ priority?: SteerPriority;
84
+ }
85
+ export declare const UNOBSERVED_STEER_PRIORITY: SteerPriorityReading;
86
+ export declare function steerBodyOf(input: SteerBodyInput, reading: SteerPriorityReading): SteerBody;
@@ -1,5 +1,5 @@
1
1
  import { cancelConflictContent, cancelConflictFromError } from './wireRefusalCopy.js';
2
- import { uuid } from './adapter/types.js';
2
+ import { steerPriorityMode } from './steerPriorityCapability.js';
3
3
  export class ControlSafetyError extends Error {
4
4
  code;
5
5
  cause;
@@ -16,7 +16,6 @@ export class ControlSafetyError extends Error {
16
16
  this.status = status;
17
17
  }
18
18
  }
19
- const BAND_RANK = { now: 0, next: 1, later: 2 };
20
19
  export class ControlRouter {
21
20
  client;
22
21
  taskId = null;
@@ -24,8 +23,6 @@ export class ControlRouter {
24
23
  jobId;
25
24
  scenario;
26
25
  systemPrompt;
27
- queue = [];
28
- seq = 0;
29
26
  constructor(client, opts = {}) {
30
27
  this.client = client;
31
28
  this.sessionId = opts.sessionId;
@@ -63,7 +60,16 @@ export class ControlRouter {
63
60
  if (this.taskId === null) {
64
61
  throw controlSafetyRefusal('not_running', 'no run in flight to steer', 'submit a turn first');
65
62
  }
66
- const body = mode !== undefined ? { text, mode } : { text };
63
+ let priority;
64
+ let reading;
65
+ try {
66
+ priority = opts?.priority;
67
+ reading = opts?.steerPriority;
68
+ }
69
+ catch (e) {
70
+ throw steerLocalRefusal('steer_input_invalid', 'unknown', e);
71
+ }
72
+ const body = steerBodyOf({ text, ...(mode !== undefined ? { mode } : {}), ...(priority !== undefined ? { priority: priority } : {}) }, (reading ?? UNOBSERVED_STEER_PRIORITY));
67
73
  try {
68
74
  return await this.client.runs.steer(this.taskId, body, opts?.signal !== undefined ? { signal: opts.signal } : undefined);
69
75
  }
@@ -92,38 +98,11 @@ export class ControlRouter {
92
98
  'check what that decision did to the run before retrying the cancel', e);
93
99
  }
94
100
  if (isNotFound(e)) {
95
- throw controlSafetyRefusal('not_found', 'runs.cancel 404 — non-owner / unknown run (no existence leak)', undefined, e);
101
+ throw controlSafetyRefusal('not_found', 'runs.cancel 404', `${CANCEL_NOT_FOUND_COPY.happened} — ${CANCEL_NOT_FOUND_COPY.nextStep}`, e);
96
102
  }
97
103
  throw e;
98
104
  }
99
105
  }
100
- enqueue(text, priority = 'next') {
101
- const cmd = { id: this.nextId(), text, priority };
102
- this.queue.push(cmd);
103
- return cmd;
104
- }
105
- dequeue(id) {
106
- const i = this.queue.findIndex((c) => c.id === id);
107
- if (i < 0)
108
- return false;
109
- this.queue.splice(i, 1);
110
- return true;
111
- }
112
- pending() {
113
- return [...this.queue].sort((a, b) => BAND_RANK[a.priority] - BAND_RANK[b.priority]);
114
- }
115
- async drainNext(opts) {
116
- const ordered = this.pending();
117
- const head = ordered[0];
118
- if (!head)
119
- return null;
120
- this.dequeue(head.id);
121
- return this.submit(head.text, opts);
122
- }
123
- nextId() {
124
- this.seq += 1;
125
- return `q-${this.seq}-${uuid()}`;
126
- }
127
106
  }
128
107
  export function errCodes(e) {
129
108
  if (!e || typeof e !== 'object')
@@ -152,20 +131,72 @@ const STEERING_NAME_TO_SAFETY = new Map([
152
131
  ['SteeringAmbiguousTargetError', 'ambiguous_target'],
153
132
  ['SteeringStillRunningError', 'still_running'],
154
133
  ['SteeringLedgerUnavailableError', 'ledger_unavailable'],
134
+ ['SteeringQueueFullError', 'queue_full'],
135
+ ['SteeringDuplicateInputIdError', 'duplicate_input_id'],
155
136
  ['SteeringError', 'steering_other'],
156
137
  ]);
157
- const STEERING_ADVICE = {
158
- not_running: 'the run is suspended/terminal/on another replica — resolve its gate via decide, do not steer',
159
- invalid_content: 'the steer text carried a control-plane escape; surface it, do not strip-and-retry',
160
- queue_full: 'the parked steering queue on this checkpoint is full (core fails loud rather than evicting an accepted instruction) — wait for it to drain, do not resend',
161
- duplicate_input_id: 'this Idempotency-Key is already parked with DIFFERENT steering content — reissue with a fresh key, resending verbatim will never succeed',
162
- ambiguous_target: 'more than one live target matches — address it uniquely (sub-agent: parentToolCallId; workflow: a more specific label); retrying verbatim yields the same result',
163
- still_running: 'the target is STILL RUNNING — steer it instead of resuming, or wait for it to settle (this is the exact opposite of not_running: never collapse the two)',
164
- blocked_by_hook: "this deployment's userPromptSubmit gate refused the input (block, timeout and crash all report this one code, fail-closed) — the input was NOT accepted: no human_input frame, the inputId is not on the ledger, so editing the text and sending again is a normal retry, not a duplicate",
165
- ledger_unavailable: 'the server could not record this steer in its user-input ledger and refused it before anything reached the engine — the input was NOT delivered; resend the same text later (the same Idempotency-Key is fine)',
166
- steering_other: 'an unrecognized steering.* refusal (open set) — the steer did NOT land; log the errorCode verbatim, do not guess its meaning',
138
+ const STEERING_COPY = {
139
+ not_running: {
140
+ happened: 'that run is not accepting messages right now — it has just finished, is running on another server, or is not the active run of its session',
141
+ nextStep: 'check its current state and send the message to the run that is active',
142
+ },
143
+ invalid_content: {
144
+ happened: 'the message was refused because its text contains control markers the engine does not accept, so nothing was delivered',
145
+ nextStep: 'rewrite the message and send it again',
146
+ },
147
+ queue_full: {
148
+ happened: 'that run is paused and already holds as many waiting messages as it can, so this one was not accepted',
149
+ nextStep: 'send it again once the waiting messages have been delivered (when the run resumes or the session is woken)',
150
+ },
151
+ duplicate_input_id: {
152
+ happened: 'a different instruction was already accepted under this idempotency key, and that earlier instruction stands',
153
+ nextStep: 'if you changed the text, send the new text under a new key; otherwise do not resend it — a new key would deliver the same instruction twice',
154
+ },
155
+ ambiguous_target: {
156
+ happened: 'more than one running agent matches that target, so the message was not delivered',
157
+ nextStep: 'address a sub-agent by its tool-call id; a workflow agent cannot be singled out while another running agent shares its label, so send it again once only one does',
158
+ },
159
+ still_running: {
160
+ happened: 'that agent is still running, so it cannot be resumed',
161
+ nextStep: 'send it a message instead, or wait for it to finish',
162
+ },
163
+ blocked_by_hook: {
164
+ happened: "the message was not accepted: a prompt check on this deployment blocked it, so nothing was delivered",
165
+ nextStep: 'edit the message and send it again — that is not a duplicate submission',
166
+ },
167
+ ledger_unavailable: {
168
+ happened: 'the server could not record the message and refused it before anything reached the engine, so nothing was delivered',
169
+ nextStep: 'send the same text again later',
170
+ },
171
+ steering_other: {
172
+ happened: 'the message was refused for a reason this version does not recognise, so it did not land',
173
+ nextStep: 'report its error code if this keeps happening; do not guess what it means',
174
+ },
175
+ };
176
+ const adviceOfCopy = (c) => `${c.happened} — ${c.nextStep}`;
177
+ const STEERING_ADVICE = Object.freeze(Object.fromEntries(Object.entries(STEERING_COPY).map(([k, c]) => [k, adviceOfCopy(c)])));
178
+ const STEERING_OTHER_UNKNOWN_COPY = {
179
+ happened: 'the message was refused for a reason this version does not recognise, and whether it was delivered is not known',
180
+ nextStep: 'do not resend it as if it had certainly been dropped — check the run first, and report its error code if this keeps happening',
181
+ };
182
+ const CANCEL_NOT_FOUND_COPY = {
183
+ happened: 'this server has no run with that id for you — it does not exist, or it belongs to someone else',
184
+ nextStep: 'nothing was cancelled; refresh the run list',
185
+ };
186
+ const PRIORITY_NOT_APPLIED_COPY = {
187
+ advisory: {
188
+ happened: "this server accepts a steer priority but does not apply it — a 'now' message would not interrupt the running turn and a 'later' message would not wait",
189
+ nextStep: 'nothing was sent; to interrupt the turn now, send the text as an interrupt, or send it without a priority to have it delivered at the next turn boundary',
190
+ },
191
+ unknown: {
192
+ happened: 'whether this server applies a steer priority is not known — there is no usable capability reading for it',
193
+ nextStep: 'nothing was sent; wait for the capability reading and try again, or send it without a priority to have it delivered at the next turn boundary',
194
+ },
195
+ };
196
+ const STEER_INPUT_INVALID_COPY = {
197
+ happened: "the steer request could not be built — its priority must be 'now', 'next' or 'later', and its fields must be readable",
198
+ nextStep: 'nothing was sent; fix the request and send it again',
167
199
  };
168
- const STEERING_OTHER_UNKNOWN_ADVICE = 'an unrecognized steering.* refusal (open set) — treat delivery as unknown (do not resend as if it had certainly been dropped); log the errorCode verbatim, do not guess its meaning';
169
200
  export function steeringStatusIs4xx(e) {
170
201
  let status;
171
202
  try {
@@ -176,10 +207,10 @@ export function steeringStatusIs4xx(e) {
176
207
  }
177
208
  return status !== undefined && status >= 400 && status < 500;
178
209
  }
179
- function steeringAdviceOf(e, safety) {
210
+ function steeringCopyOf(e, safety) {
180
211
  if (safety === 'steering_other' && !steeringStatusIs4xx(e))
181
- return STEERING_OTHER_UNKNOWN_ADVICE;
182
- return STEERING_ADVICE[safety];
212
+ return STEERING_OTHER_UNKNOWN_COPY;
213
+ return STEERING_COPY[safety];
183
214
  }
184
215
  const STEERING_REFUSED_BEFORE_ENGINE = new Map([['ledger_unavailable', 503]]);
185
216
  export function steeringRefusedBeforeEngine(e) {
@@ -190,7 +221,8 @@ export function steeringRefusedBeforeEngine(e) {
190
221
  const safety = steeringVerdict(e);
191
222
  if (safety === undefined || STEERING_REFUSED_BEFORE_ENGINE.get(safety) !== status)
192
223
  return undefined;
193
- return { safety, advice: STEERING_ADVICE[safety] };
224
+ const copy = STEERING_COPY[safety];
225
+ return { safety, advice: STEERING_ADVICE[safety], happened: copy.happened, nextStep: copy.nextStep };
194
226
  }
195
227
  catch {
196
228
  return undefined;
@@ -217,7 +249,8 @@ export function steerRefusalAdviceOf(e) {
217
249
  if (verdict === undefined)
218
250
  return undefined;
219
251
  const safety = steeringSafetyOf(e, verdict);
220
- return { safety, advice: steeringAdviceOf(e, safety) };
252
+ const copy = steeringCopyOf(e, safety);
253
+ return { safety, advice: adviceOfCopy(copy), happened: copy.happened, nextStep: copy.nextStep };
221
254
  }
222
255
  catch {
223
256
  return undefined;
@@ -245,6 +278,38 @@ function controlSafetyRefusal(code, head, advice, cause) {
245
278
  const detail = { ...(advice !== undefined ? { advice } : {}), ...(status !== undefined ? { status } : {}) };
246
279
  return new ControlSafetyError(advice === undefined ? head : `${head} — ${advice}`, code, cause, detail);
247
280
  }
281
+ export const UNOBSERVED_STEER_PRIORITY = Object.freeze({ kind: 'unobserved' });
282
+ function steerLocalRefusal(code, mode, cause) {
283
+ const copy = code === 'steer_input_invalid' ? STEER_INPUT_INVALID_COPY : PRIORITY_NOT_APPLIED_COPY[mode === 'advisory' ? 'advisory' : 'unknown'];
284
+ return controlSafetyRefusal(code, 'steer not sent', adviceOfCopy(copy), cause);
285
+ }
286
+ function ownInputField(input, key) {
287
+ return Object.hasOwn(input, key) ? input[key] : undefined;
288
+ }
289
+ export function steerBodyOf(input, reading) {
290
+ let text;
291
+ let mode;
292
+ let priority;
293
+ try {
294
+ if (input === null || typeof input !== 'object' || Array.isArray(input))
295
+ throw new TypeError('steer input is not an object');
296
+ text = ownInputField(input, 'text');
297
+ mode = ownInputField(input, 'mode');
298
+ priority = ownInputField(input, 'priority');
299
+ }
300
+ catch (e) {
301
+ throw steerLocalRefusal('steer_input_invalid', 'unknown', e);
302
+ }
303
+ const base = mode !== undefined ? { text, mode } : { text };
304
+ if (priority === undefined || priority === 'next')
305
+ return Object.freeze(base);
306
+ const tierMode = steerPriorityMode(reading);
307
+ if (priority !== 'now' && priority !== 'later')
308
+ throw steerLocalRefusal('steer_input_invalid', tierMode);
309
+ if (tierMode !== 'engine')
310
+ throw steerLocalRefusal('priority_not_applied', tierMode);
311
+ return Object.freeze({ ...base, priority });
312
+ }
248
313
  function isCancelSuspendedConflict(e) {
249
314
  const { name, status } = errCodes(e);
250
315
  return name === 'ConflictError' || status === 409;
@@ -32,7 +32,7 @@ export interface CoreProtocolNamespaceView {
32
32
  }
33
33
  export interface PermissionRuleView {
34
34
  rule: string;
35
- behavior: 'deny' | 'ask' | 'allow';
35
+ behavior: 'deny' | 'ask';
36
36
  source?: string;
37
37
  }
38
38
  export interface PermissionRuleIssueView {
@@ -59,8 +59,9 @@ export interface CoreValuePorts {
59
59
  ruleToolGrammarOf: (toolName: string) => 'command' | 'path' | undefined;
60
60
  validatePermissionRules: (rules: PermissionRuleView[], opts?: ValidatePermissionRulesOptionsView) => PermissionRuleIssueView[];
61
61
  DISCUSSION_WORKFLOW_NAME: string;
62
+ mintNamespacePrefix(ns: CoreProtocolNamespaceView, peer: string): string;
62
63
  }
63
- export declare const CORE_VALUE_PORT_NAMES: readonly ["resolveAutonomousLoopPrompt", "loosenReasons", "combinePolicies", "createAllowDenyPolicy", "MCP_NAMESPACE", "RETIRED_TOOL_NAMES", "protocolOf", "ruleToolGrammarOf", "validatePermissionRules", "DISCUSSION_WORKFLOW_NAME"];
64
+ export declare const CORE_VALUE_PORT_NAMES: readonly ["resolveAutonomousLoopPrompt", "loosenReasons", "combinePolicies", "createAllowDenyPolicy", "MCP_NAMESPACE", "RETIRED_TOOL_NAMES", "protocolOf", "ruleToolGrammarOf", "validatePermissionRules", "DISCUSSION_WORKFLOW_NAME", "mintNamespacePrefix"];
64
65
  export type CoreValuePortName = (typeof CORE_VALUE_PORT_NAMES)[number];
65
66
  export declare function installCoreValuePorts(ports: Partial<CoreValuePorts> | null): () => void;
66
67
  export declare function hasCoreValuePorts(): boolean;
@@ -76,3 +77,4 @@ export declare function protocolOf(name: string): CoreProtocolNamespaceView | un
76
77
  export declare function ruleToolGrammarOf(toolName: string): 'command' | 'path' | undefined;
77
78
  export declare function validatePermissionRules(rules: PermissionRuleView[], opts?: ValidatePermissionRulesOptionsView): PermissionRuleIssueView[] | undefined;
78
79
  export declare function discussionWorkflowName(): string | undefined;
80
+ export declare function mintNamespacePrefix(ns: CoreProtocolNamespaceView, peer: string): string | undefined;
@@ -9,6 +9,7 @@ export const CORE_VALUE_PORT_NAMES = Object.freeze([
9
9
  'ruleToolGrammarOf',
10
10
  'validatePermissionRules',
11
11
  'DISCUSSION_WORKFLOW_NAME',
12
+ 'mintNamespacePrefix',
12
13
  ]);
13
14
  let installed = null;
14
15
  export function installCoreValuePorts(ports) {
@@ -67,3 +68,7 @@ export function validatePermissionRules(rules, opts) {
67
68
  export function discussionWorkflowName() {
68
69
  return port('DISCUSSION_WORKFLOW_NAME');
69
70
  }
71
+ export function mintNamespacePrefix(ns, peer) {
72
+ const fn = port('mintNamespacePrefix');
73
+ return fn === undefined ? undefined : fn(ns, peer);
74
+ }
@@ -1,6 +1,6 @@
1
1
  import { corruptStoredRowContent, corruptStoredRowWhere } from './wireErrorTriage.js';
2
2
  import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError, parkedResumeStartupContent, parkedResumeStartupFromError, resumePolicyBlockContent, resumePolicyBlockFromError, } from './resumeRefusalCopy.js';
3
- import { decideParkMovedContent, decideParkMovedFromError } from './decideReceipt.js';
3
+ import { askGoneContent, askGoneFromError, decideParkMovedContent, decideParkMovedFromError } from './decideReceipt.js';
4
4
  import { settingsPermissionBucketRefusalContent, settingsPermissionBucketRefusalFromError } from './wireRefusalCopy.js';
5
5
  export function decideFailureNote(err, decision) {
6
6
  const where = corruptStoredRowWhere(err);
@@ -9,10 +9,12 @@ export function decideFailureNote(err, decision) {
9
9
  const moved = decideParkMovedFromError(err);
10
10
  const bucket = settingsPermissionBucketRefusalFromError(err);
11
11
  const unsupported = checkpointUnsupportedVersionFromError(err);
12
+ const gone = askGoneFromError(err);
12
13
  return ((where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`) +
13
14
  (fold === null ? '' : ` — ${resumePolicyBlockContent(fold)}`) +
14
15
  (bucket === null ? '' : ` — ${settingsPermissionBucketRefusalContent(bucket)}`) +
15
16
  (startup === null ? '' : ` — ${parkedResumeStartupContent(startup)}`) +
16
17
  (moved === null ? '' : ` — ${decideParkMovedContent(moved)}`) +
17
- (unsupported === null ? '' : ` — ${checkpointUnsupportedVersionContent(unsupported)}`));
18
+ (unsupported === null ? '' : ` — ${checkpointUnsupportedVersionContent(unsupported)}`) +
19
+ (gone === null ? '' : ` — ${askGoneContent(gone)}`));
18
20
  }
@@ -1,5 +1,6 @@
1
1
  import { type GateOutcomeView } from './gateOutcome.js';
2
2
  import { type ResumeReopenDetail } from './hitl/suspendedReopen.js';
3
+ import { type CrashConvergedRow } from './hitl/crashConverged.js';
3
4
  export interface DecideReceiptView {
4
5
  status?: string;
5
6
  taskId?: string;
@@ -17,6 +18,8 @@ export declare function decideAcceptedNotResolved(receipt: DecideReceiptView | u
17
18
  export interface DecideRefusalDetail {
18
19
  code: string;
19
20
  runId?: string;
21
+ runIds?: readonly string[];
22
+ readableHolderRunId?: string;
20
23
  detail: string;
21
24
  resendable: boolean;
22
25
  }
@@ -27,3 +30,11 @@ export interface DecideParkMovedDetail {
27
30
  }
28
31
  export declare function decideParkMovedFromError(e: unknown): DecideParkMovedDetail | null;
29
32
  export declare function decideParkMovedContent(detail: Pick<DecideParkMovedDetail, 'code'>): string;
33
+ export interface AskGoneDetail {
34
+ code: 'gone.ask';
35
+ state?: 'VOID' | 'DENIED';
36
+ deniedReason?: string;
37
+ holderGone?: CrashConvergedRow['cause'];
38
+ }
39
+ export declare function askGoneFromError(e: unknown): AskGoneDetail | null;
40
+ export declare function askGoneContent(detail: AskGoneDetail): string;
@@ -1,8 +1,9 @@
1
1
  import { gateOutcomeOf } from './gateOutcome.js';
2
2
  import { resumeReopenFromError } from './hitl/suspendedReopen.js';
3
- import { DECIDE_CLAIM_LOST, DECIDE_GATE_MOVED, DECIDE_NOT_PARKED, DECIDE_WORKFLOW_HOST_UNKNOWN, DECIDE_WORKFLOW_LANE_CODES, DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, isDecideParkMovedCode, } from './engineErrorCodes.js';
3
+ import { ASK_GONE, ASK_GONE_DENIED_WORD, DECIDE_CLAIM_LOST, DECIDE_GATE_MOVED, DECIDE_NOT_PARKED, DECIDE_WORKFLOW_HOST_UNKNOWN, DECIDE_WORKFLOW_LANE_CODES, DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, isDecideParkMovedCode, } from './engineErrorCodes.js';
4
4
  import { effectiveWireErrorCode } from './wireErrorTriage.js';
5
- import { readErrorString } from './wireFailureShape.js';
5
+ import { readErrorString, readErrorStrictStringList } from './wireFailureShape.js';
6
+ import { isCrashConvergedCause } from './hitl/crashConverged.js';
6
7
  const STATUS_RESUMING = 'resuming';
7
8
  function str(v) {
8
9
  return typeof v === 'string' && v.length > 0 ? v : undefined;
@@ -56,25 +57,50 @@ export function decideAcceptedNotResolved(receipt) {
56
57
  const DECIDE_REFUSAL_SENTENCES = Object.freeze({
57
58
  [DECIDE_WORKFLOW_HOST_UNKNOWN]: 'no session is attached to this run to receive the decision, so re-sending the same decision cannot help; the approval is still pending and can be decided again once the run is reachable',
58
59
  [DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED]: 'this run cannot remember the decision for the session; re-send the same decision without the remember option — nothing was consumed by this refusal',
60
+ [DECIDE_WORKFLOW_PARKS_UNREADABLE]: "this approval cannot be matched to the run that is waiting on it, because that run's record was written by an earlier engine version or is damaged; nothing was delivered and the approval is still pending — try resuming that run, and if this approval is still refused afterwards, the record has to be repaired by whoever operates this deployment before it can be decided",
61
+ [DECIDE_WORKFLOW_PARK_FORKED]: 'the same run was resumed more than once and each resumed copy is waiting on this same approval, so there is no single run to hand the decision to; nothing was delivered and the approval is still pending — resume one of those runs, and if it reaches this approval again, decide here',
59
62
  });
60
63
  const DECIDE_RESENDABLE = Object.freeze({
61
64
  [DECIDE_WORKFLOW_HOST_UNKNOWN]: false,
62
65
  [DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED]: true,
66
+ [DECIDE_WORKFLOW_PARKS_UNREADABLE]: false,
67
+ [DECIDE_WORKFLOW_PARK_FORKED]: false,
63
68
  });
69
+ function readTopOwn(e, key) {
70
+ try {
71
+ return e !== null && e !== undefined ? e[key] : undefined;
72
+ }
73
+ catch {
74
+ return undefined;
75
+ }
76
+ }
77
+ function readForkedRunIds(e) {
78
+ const list = readErrorStrictStringList(e, 'runIds');
79
+ if (list === undefined || list.length === 0)
80
+ return undefined;
81
+ for (const id of list)
82
+ if (id.length === 0)
83
+ return undefined;
84
+ return Object.freeze(list.slice());
85
+ }
64
86
  export function decideRefusalFromError(e) {
65
- const code = typeof e?.errorCode === 'string'
66
- ? (e.errorCode)
67
- : undefined;
87
+ const rawCode = readTopOwn(e, 'errorCode');
88
+ const code = typeof rawCode === 'string' ? rawCode : undefined;
68
89
  if (code === undefined || !DECIDE_WORKFLOW_LANE_CODES.includes(code))
69
90
  return null;
70
- const rawRunId = e.runId;
91
+ const rawRunId = code === DECIDE_WORKFLOW_PARK_FORKED ? undefined : readTopOwn(e, 'runId');
71
92
  const runId = typeof rawRunId === 'string' && rawRunId.length > 0 ? rawRunId : undefined;
93
+ const runIds = code === DECIDE_WORKFLOW_PARK_FORKED ? readForkedRunIds(e) : undefined;
94
+ const holderRaw = code === DECIDE_WORKFLOW_PARKS_UNREADABLE ? readErrorString(e, 'readableHolderRunId') : undefined;
95
+ const readableHolderRunId = holderRaw !== undefined && holderRaw.length > 0 ? holderRaw : undefined;
72
96
  const detail = Object.hasOwn(DECIDE_REFUSAL_SENTENCES, code)
73
97
  ? DECIDE_REFUSAL_SENTENCES[code]
74
98
  : undefined;
75
99
  return {
76
100
  code,
77
101
  ...(runId !== undefined ? { runId } : {}),
102
+ ...(runIds !== undefined ? { runIds } : {}),
103
+ ...(readableHolderRunId !== undefined ? { readableHolderRunId } : {}),
78
104
  detail: detail ?? 'the engine refused this decision; the approval is still pending and can be decided again',
79
105
  resendable: Object.hasOwn(DECIDE_RESENDABLE, code) ? DECIDE_RESENDABLE[code] === true : false,
80
106
  };
@@ -87,9 +113,9 @@ export function decideParkMovedFromError(e) {
87
113
  return { code, ...(taskId !== undefined && taskId.length > 0 ? { taskId } : {}) };
88
114
  }
89
115
  const DECIDE_PARK_MOVED_WHAT = Object.freeze({
90
- [DECIDE_NOT_PARKED]: 'This approval was no longer waiting when the decision arrived (the agent was stopped, the approval was decided elsewhere, or the session it belongs to was deleted)',
91
- [DECIDE_GATE_MOVED]: 'This approval moved while the decision was on its way (it was parked again, or settled elsewhere)',
92
- [DECIDE_CLAIM_LOST]: 'Another decision on this approval got there first, or an earlier attempt is still holding it until the engine clears that hold on its own',
116
+ [DECIDE_NOT_PARKED]: 'This approval was no longer waiting when the decision arrived (the agent was stopped, the approval was decided elsewhere, or the session it belongs to was deleted; this client cannot tell which)',
117
+ [DECIDE_GATE_MOVED]: 'This approval moved while the decision was on its way (it was parked again, or settled elsewhere; this client cannot tell which)',
118
+ [DECIDE_CLAIM_LOST]: 'Another decision on this approval got there first, or an earlier attempt is still holding it until the engine clears that hold on its own; this client cannot tell which',
93
119
  });
94
120
  export function decideParkMovedContent(detail) {
95
121
  let code;
@@ -106,3 +132,63 @@ export function decideParkMovedContent(detail) {
106
132
  const extra = code === DECIDE_CLAIM_LOST ? '; if this approval is still listed, wait a little before deciding it again' : '';
107
133
  return `${what} · This decision was not applied — fetch the pending approvals again and decide from what is pending now${extra}`;
108
134
  }
135
+ export function askGoneFromError(e) {
136
+ if (typeof e !== 'object' || e === null)
137
+ return null;
138
+ if (effectiveWireErrorCode(e) !== ASK_GONE)
139
+ return null;
140
+ const rawState = readErrorString(e, 'state');
141
+ const state = rawState === 'VOID' || rawState === 'DENIED' ? rawState : undefined;
142
+ const rawReason = readErrorString(e, 'deniedReason');
143
+ const deniedReason = rawReason === undefined || rawReason.length === 0
144
+ ? undefined
145
+ : state === 'DENIED'
146
+ ? rawReason === ASK_GONE_DENIED_WORD ? rawReason : undefined
147
+ : state === 'VOID' ? rawReason : undefined;
148
+ const holderGone = state === 'VOID' && isCrashConvergedCause(deniedReason) ? deniedReason : undefined;
149
+ return {
150
+ code: ASK_GONE,
151
+ ...(state !== undefined ? { state } : {}),
152
+ ...(deniedReason !== undefined ? { deniedReason } : {}),
153
+ ...(holderGone !== undefined ? { holderGone } : {}),
154
+ };
155
+ }
156
+ export function askGoneContent(detail) {
157
+ let state;
158
+ let deniedReason;
159
+ let holderGone;
160
+ try {
161
+ state = detail.state;
162
+ deniedReason = detail.deniedReason;
163
+ holderGone = detail.holderGone;
164
+ }
165
+ catch {
166
+ state = undefined;
167
+ deniedReason = undefined;
168
+ holderGone = undefined;
169
+ }
170
+ const st = state === 'VOID' || state === 'DENIED' ? state : undefined;
171
+ const safety = 'Your answer was not applied and nothing was run on it';
172
+ if (st === 'VOID' && (isCrashConvergedCause(holderGone) || isCrashConvergedCause(deniedReason))) {
173
+ return [
174
+ 'The engine run that was waiting for this answer ended before the answer arrived',
175
+ safety,
176
+ 'That run will not continue: submit the work again as a new task, and it will ask again',
177
+ ].join(' · ');
178
+ }
179
+ if (st === 'DENIED') {
180
+ return ['This approval was closed without a human decision and the request was denied', safety, 'The denial stands; this approval cannot be reopened'].join(' · ');
181
+ }
182
+ if (st === 'VOID') {
183
+ return [
184
+ 'This approval was withdrawn without a human decision',
185
+ safety,
186
+ 'Nothing is waiting for this answer any more; if the work still needs this step, wait for it to be asked again or submit the work again',
187
+ ].join(' · ');
188
+ }
189
+ return [
190
+ 'This approval was closed without a human decision',
191
+ safety,
192
+ 'It can no longer be decided; fetch the pending approvals again and decide from what is pending now',
193
+ ].join(' · ');
194
+ }
@@ -2,27 +2,15 @@ import { type EnvLike } from './hostEnv.js';
2
2
  import { type WireAuthTokenSource } from './engineWireSdk.js';
3
3
  export declare const DETACH_HEADER = "x-detach-on-disconnect";
4
4
  export declare const HEADLESS_DETACH_ENV = "SEMA_HEADLESS_DETACH";
5
- export declare const DETACH_WIRE_MIN_ENGINE: readonly [number, number, number];
6
- export declare const versionSupportsDetach: (v: string | undefined) => boolean;
7
5
  export declare function headlessDetachDisabledByEnv(env?: EnvLike): boolean;
8
- export type EngineDetachSupport = 'supported' | 'unsupported' | 'unknown';
9
- export declare function probeEngineDetachSupport(baseUrl: string, opts?: {
10
- fetchImpl?: typeof fetch;
11
- timeoutMs?: number;
12
- }): Promise<EngineDetachSupport>;
13
- export declare function detachSupportDisclosure(support: EngineDetachSupport): string | undefined;
14
- export declare function engineSupportsDetach(baseUrl: string, opts?: {
15
- fetchImpl?: typeof fetch;
16
- timeoutMs?: number;
17
- }): Promise<boolean>;
18
6
  export type DetachResolution = {
19
7
  on: true;
20
8
  reason: 'default-on';
21
9
  } | {
22
10
  on: false;
23
- reason: 'env-off' | 'old-engine';
11
+ reason: 'env-off';
24
12
  };
25
- export declare function resolveHeadlessDetach(baseUrl: string, env?: EnvLike, opts?: {
13
+ export declare function resolveHeadlessDetach(_baseUrl: string, env?: EnvLike, _opts?: {
26
14
  fetchImpl?: typeof fetch;
27
15
  timeoutMs?: number;
28
16
  }): Promise<DetachResolution>;