@namzu/sdk 54.0.0 → 55.1.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 (325) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +1 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/compaction/extractor.d.ts.map +1 -1
  6. package/dist/compaction/extractor.js +5 -1
  7. package/dist/compaction/extractor.js.map +1 -1
  8. package/dist/compaction/managers/structured.d.ts.map +1 -1
  9. package/dist/compaction/managers/structured.js +5 -0
  10. package/dist/compaction/managers/structured.js.map +1 -1
  11. package/dist/compaction/manual.d.ts.map +1 -1
  12. package/dist/compaction/manual.js +6 -0
  13. package/dist/compaction/manual.js.map +1 -1
  14. package/dist/compaction/serializer.d.ts +6 -0
  15. package/dist/compaction/serializer.d.ts.map +1 -1
  16. package/dist/compaction/serializer.js +21 -3
  17. package/dist/compaction/serializer.js.map +1 -1
  18. package/dist/compaction/verifier.d.ts.map +1 -1
  19. package/dist/compaction/verifier.js +27 -6
  20. package/dist/compaction/verifier.js.map +1 -1
  21. package/dist/constants/compaction/index.d.ts +1 -0
  22. package/dist/constants/compaction/index.d.ts.map +1 -1
  23. package/dist/constants/compaction/index.js +1 -0
  24. package/dist/constants/compaction/index.js.map +1 -1
  25. package/dist/{manager/resident/learning-cycle.d.ts → learning/cycle.d.ts} +45 -36
  26. package/dist/learning/cycle.d.ts.map +1 -0
  27. package/dist/{manager/resident/learning-cycle.js → learning/cycle.js} +52 -20
  28. package/dist/learning/cycle.js.map +1 -0
  29. package/dist/learning/deprecated.d.ts +86 -0
  30. package/dist/learning/deprecated.d.ts.map +1 -0
  31. package/dist/learning/deprecated.js +22 -0
  32. package/dist/learning/deprecated.js.map +1 -0
  33. package/dist/learning/experience-store.d.ts +35 -0
  34. package/dist/learning/experience-store.d.ts.map +1 -0
  35. package/dist/learning/experience-store.js +143 -0
  36. package/dist/learning/experience-store.js.map +1 -0
  37. package/dist/learning/experience.d.ts +150 -0
  38. package/dist/learning/experience.d.ts.map +1 -0
  39. package/dist/learning/experience.js +146 -0
  40. package/dist/learning/experience.js.map +1 -0
  41. package/dist/learning/generator.d.ts +118 -0
  42. package/dist/learning/generator.d.ts.map +1 -0
  43. package/dist/learning/generator.js +275 -0
  44. package/dist/learning/generator.js.map +1 -0
  45. package/dist/learning/leak-tripwire.d.ts +66 -0
  46. package/dist/learning/leak-tripwire.d.ts.map +1 -0
  47. package/dist/learning/leak-tripwire.js +208 -0
  48. package/dist/learning/leak-tripwire.js.map +1 -0
  49. package/dist/{manager/resident/learning-observation.d.ts → learning/observation.d.ts} +4 -4
  50. package/dist/learning/observation.d.ts.map +1 -0
  51. package/dist/{manager/resident/learning-observation.js → learning/observation.js} +3 -3
  52. package/dist/learning/observation.js.map +1 -0
  53. package/dist/{manager/resident/learning.d.ts → learning/skill.d.ts} +29 -29
  54. package/dist/learning/skill.d.ts.map +1 -0
  55. package/dist/{manager/resident/learning.js → learning/skill.js} +27 -27
  56. package/dist/learning/skill.js.map +1 -0
  57. package/dist/{manager/resident/learning-store.d.ts → learning/store.d.ts} +38 -31
  58. package/dist/learning/store.d.ts.map +1 -0
  59. package/dist/{manager/resident/learning-store.js → learning/store.js} +52 -20
  60. package/dist/learning/store.js.map +1 -0
  61. package/dist/manager/resident/activity.d.ts +30 -6
  62. package/dist/manager/resident/activity.d.ts.map +1 -1
  63. package/dist/manager/resident/activity.js.map +1 -1
  64. package/dist/manager/resident/agenda.d.ts +42 -12
  65. package/dist/manager/resident/agenda.d.ts.map +1 -1
  66. package/dist/manager/resident/agenda.js +9 -7
  67. package/dist/manager/resident/agenda.js.map +1 -1
  68. package/dist/manager/resident/consumption.d.ts +27 -5
  69. package/dist/manager/resident/consumption.d.ts.map +1 -1
  70. package/dist/manager/resident/consumption.js +2 -0
  71. package/dist/manager/resident/consumption.js.map +1 -1
  72. package/dist/manager/resident/delivery-window.d.ts +7 -1
  73. package/dist/manager/resident/delivery-window.d.ts.map +1 -1
  74. package/dist/manager/resident/delivery-window.js +2 -0
  75. package/dist/manager/resident/delivery-window.js.map +1 -1
  76. package/dist/manager/resident/evidence-recall.d.ts +7 -1
  77. package/dist/manager/resident/evidence-recall.d.ts.map +1 -1
  78. package/dist/manager/resident/evidence-recall.js +2 -0
  79. package/dist/manager/resident/evidence-recall.js.map +1 -1
  80. package/dist/manager/resident/history.d.ts +45 -9
  81. package/dist/manager/resident/history.d.ts.map +1 -1
  82. package/dist/manager/resident/history.js.map +1 -1
  83. package/dist/manager/resident/host.d.ts +44 -10
  84. package/dist/manager/resident/host.d.ts.map +1 -1
  85. package/dist/manager/resident/host.js +4 -2
  86. package/dist/manager/resident/host.js.map +1 -1
  87. package/dist/manager/resident/initiative.d.ts +32 -6
  88. package/dist/manager/resident/initiative.d.ts.map +1 -1
  89. package/dist/manager/resident/initiative.js +2 -0
  90. package/dist/manager/resident/initiative.js.map +1 -1
  91. package/dist/manager/resident/loop.d.ts +22 -4
  92. package/dist/manager/resident/loop.d.ts.map +1 -1
  93. package/dist/manager/resident/loop.js +7 -1
  94. package/dist/manager/resident/loop.js.map +1 -1
  95. package/dist/manager/resident/outbox.d.ts +42 -8
  96. package/dist/manager/resident/outbox.d.ts.map +1 -1
  97. package/dist/manager/resident/outbox.js +2 -0
  98. package/dist/manager/resident/outbox.js.map +1 -1
  99. package/dist/manager/resident/proposal.d.ts +19 -5
  100. package/dist/manager/resident/proposal.d.ts.map +1 -1
  101. package/dist/manager/resident/proposal.js +2 -0
  102. package/dist/manager/resident/proposal.js.map +1 -1
  103. package/dist/manager/resident/store.d.ts +31 -9
  104. package/dist/manager/resident/store.d.ts.map +1 -1
  105. package/dist/manager/resident/store.js +7 -1
  106. package/dist/manager/resident/store.js.map +1 -1
  107. package/dist/manager/resident/tool-evidence.d.ts +45 -9
  108. package/dist/manager/resident/tool-evidence.d.ts.map +1 -1
  109. package/dist/manager/resident/tool-evidence.js +5 -1
  110. package/dist/manager/resident/tool-evidence.js.map +1 -1
  111. package/dist/prompt/coding-agent-doctrine.d.ts +3 -3
  112. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  113. package/dist/prompt/coding-agent-doctrine.js +18 -20
  114. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  115. package/dist/prompt/resident-learning.d.ts +12 -2
  116. package/dist/prompt/resident-learning.d.ts.map +1 -1
  117. package/dist/prompt/resident-learning.js +6 -5
  118. package/dist/prompt/resident-learning.js.map +1 -1
  119. package/dist/prompt/resident-step.d.ts +10 -4
  120. package/dist/prompt/resident-step.d.ts.map +1 -1
  121. package/dist/prompt/resident-step.js +6 -4
  122. package/dist/prompt/resident-step.js.map +1 -1
  123. package/dist/public-runtime.d.ts +10 -4
  124. package/dist/public-runtime.d.ts.map +1 -1
  125. package/dist/public-runtime.js +11 -4
  126. package/dist/public-runtime.js.map +1 -1
  127. package/dist/public-types.d.ts +9 -4
  128. package/dist/public-types.d.ts.map +1 -1
  129. package/dist/runtime/query/checkpoint.d.ts +12 -2
  130. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  131. package/dist/runtime/query/checkpoint.js +42 -8
  132. package/dist/runtime/query/checkpoint.js.map +1 -1
  133. package/dist/runtime/query/executor.d.ts +39 -1
  134. package/dist/runtime/query/executor.d.ts.map +1 -1
  135. package/dist/runtime/query/executor.js +115 -7
  136. package/dist/runtime/query/executor.js.map +1 -1
  137. package/dist/runtime/query/index.d.ts +18 -0
  138. package/dist/runtime/query/index.d.ts.map +1 -1
  139. package/dist/runtime/query/index.js +20 -1
  140. package/dist/runtime/query/index.js.map +1 -1
  141. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  142. package/dist/runtime/query/iteration/index.js +17 -8
  143. package/dist/runtime/query/iteration/index.js.map +1 -1
  144. package/dist/runtime/query/iteration/phases/context.d.ts +24 -2
  145. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  146. package/dist/runtime/query/iteration/phases/context.js +22 -22
  147. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  148. package/dist/runtime/query/iteration/phases/per-call-review.d.ts +87 -0
  149. package/dist/runtime/query/iteration/phases/per-call-review.d.ts.map +1 -0
  150. package/dist/runtime/query/iteration/phases/per-call-review.js +513 -0
  151. package/dist/runtime/query/iteration/phases/per-call-review.js.map +1 -0
  152. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  153. package/dist/runtime/query/iteration/phases/tool-review.js +129 -62
  154. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  155. package/dist/runtime/query/iteration/step-context.d.ts +14 -0
  156. package/dist/runtime/query/iteration/step-context.d.ts.map +1 -1
  157. package/dist/runtime/query/iteration/step-context.js +29 -0
  158. package/dist/runtime/query/iteration/step-context.js.map +1 -1
  159. package/dist/runtime/query/iteration/step-shaping.d.ts.map +1 -1
  160. package/dist/runtime/query/iteration/step-shaping.js +6 -2
  161. package/dist/runtime/query/iteration/step-shaping.js.map +1 -1
  162. package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
  163. package/dist/runtime/query/prepare-turn.js +16 -3
  164. package/dist/runtime/query/prepare-turn.js.map +1 -1
  165. package/dist/runtime/query/project-instructions.d.ts +24 -5
  166. package/dist/runtime/query/project-instructions.d.ts.map +1 -1
  167. package/dist/runtime/query/project-instructions.js +107 -35
  168. package/dist/runtime/query/project-instructions.js.map +1 -1
  169. package/dist/runtime/query/prompt.d.ts +6 -0
  170. package/dist/runtime/query/prompt.d.ts.map +1 -1
  171. package/dist/runtime/query/prompt.js +10 -49
  172. package/dist/runtime/query/prompt.js.map +1 -1
  173. package/dist/runtime/query/resume-pending.d.ts +8 -0
  174. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  175. package/dist/runtime/query/resume-pending.js +101 -0
  176. package/dist/runtime/query/resume-pending.js.map +1 -1
  177. package/dist/runtime/query/review-policy.d.ts +18 -0
  178. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  179. package/dist/runtime/query/review-policy.js +10 -0
  180. package/dist/runtime/query/review-policy.js.map +1 -1
  181. package/dist/runtime/query/steering.d.ts +55 -56
  182. package/dist/runtime/query/steering.d.ts.map +1 -1
  183. package/dist/runtime/query/steering.js +78 -72
  184. package/dist/runtime/query/steering.js.map +1 -1
  185. package/dist/tools/builtins/bash.d.ts +8 -0
  186. package/dist/tools/builtins/bash.d.ts.map +1 -1
  187. package/dist/tools/builtins/bash.js +13 -18
  188. package/dist/tools/builtins/bash.js.map +1 -1
  189. package/dist/tools/builtins/browser.d.ts.map +1 -1
  190. package/dist/tools/builtins/browser.js +38 -12
  191. package/dist/tools/builtins/browser.js.map +1 -1
  192. package/dist/tools/builtins/edit.js +4 -4
  193. package/dist/tools/builtins/edit.js.map +1 -1
  194. package/dist/tools/builtins/job.js +1 -1
  195. package/dist/tools/builtins/job.js.map +1 -1
  196. package/dist/tools/builtins/skill.js +1 -1
  197. package/dist/tools/builtins/skill.js.map +1 -1
  198. package/dist/tools/builtins/structuredOutput.js +1 -1
  199. package/dist/tools/builtins/structuredOutput.js.map +1 -1
  200. package/dist/tools/builtins/verify-outputs.js +1 -1
  201. package/dist/tools/builtins/verify-outputs.js.map +1 -1
  202. package/dist/tools/builtins/write-file.js +4 -4
  203. package/dist/tools/builtins/write-file.js.map +1 -1
  204. package/dist/tools/coordinator/ask-user-question.d.ts +6 -0
  205. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  206. package/dist/tools/coordinator/ask-user-question.js +6 -1
  207. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  208. package/dist/tools/coordinator/index.d.ts.map +1 -1
  209. package/dist/tools/coordinator/index.js +10 -3
  210. package/dist/tools/coordinator/index.js.map +1 -1
  211. package/dist/tools/resident-history.d.ts +2 -0
  212. package/dist/tools/resident-history.d.ts.map +1 -1
  213. package/dist/tools/resident-history.js +2 -0
  214. package/dist/tools/resident-history.js.map +1 -1
  215. package/dist/tools/resident-tool-evidence.d.ts +5 -1
  216. package/dist/tools/resident-tool-evidence.d.ts.map +1 -1
  217. package/dist/tools/resident-tool-evidence.js +5 -1
  218. package/dist/tools/resident-tool-evidence.js.map +1 -1
  219. package/dist/tools/schedules/loop-tool.d.ts.map +1 -1
  220. package/dist/tools/schedules/loop-tool.js +4 -2
  221. package/dist/tools/schedules/loop-tool.js.map +1 -1
  222. package/dist/tools/schedules/schedule-tool.js +2 -2
  223. package/dist/tools/schedules/schedule-tool.js.map +1 -1
  224. package/dist/tools/task/create.js +1 -1
  225. package/dist/tools/task/create.js.map +1 -1
  226. package/dist/tools/untrusted-envelope.js +1 -1
  227. package/dist/tools/untrusted-envelope.js.map +1 -1
  228. package/dist/turn/evidence-query.d.ts.map +1 -1
  229. package/dist/turn/evidence-query.js +16 -1
  230. package/dist/turn/evidence-query.js.map +1 -1
  231. package/dist/turn/evidence-recall.js +1 -1
  232. package/dist/turn/evidence-recall.js.map +1 -1
  233. package/dist/types/agent/base.d.ts +7 -0
  234. package/dist/types/agent/base.d.ts.map +1 -1
  235. package/dist/types/hitl/index.d.ts +66 -0
  236. package/dist/types/hitl/index.d.ts.map +1 -1
  237. package/dist/types/hitl/index.js.map +1 -1
  238. package/dist/types/message/index.d.ts +8 -0
  239. package/dist/types/message/index.d.ts.map +1 -1
  240. package/dist/types/message/index.js +12 -2
  241. package/dist/types/message/index.js.map +1 -1
  242. package/dist/types/message/notice-frame.d.ts +41 -0
  243. package/dist/types/message/notice-frame.d.ts.map +1 -0
  244. package/dist/types/message/notice-frame.js +65 -0
  245. package/dist/types/message/notice-frame.js.map +1 -0
  246. package/package.json +1 -1
  247. package/src/agents/QueryAgent.ts +1 -0
  248. package/src/compaction/extractor.ts +8 -1
  249. package/src/compaction/managers/structured.ts +3 -0
  250. package/src/compaction/manual.ts +5 -0
  251. package/src/compaction/serializer.ts +28 -3
  252. package/src/compaction/verifier.ts +32 -6
  253. package/src/constants/compaction/index.ts +1 -0
  254. package/src/{manager/resident/learning-cycle.ts → learning/cycle.ts} +130 -78
  255. package/src/learning/deprecated.ts +124 -0
  256. package/src/learning/experience-store.ts +202 -0
  257. package/src/learning/experience.ts +238 -0
  258. package/src/learning/generator.ts +388 -0
  259. package/src/learning/leak-tripwire.ts +280 -0
  260. package/src/{manager/resident/learning-observation.ts → learning/observation.ts} +5 -5
  261. package/src/{manager/resident/learning.ts → learning/skill.ts} +64 -67
  262. package/src/{manager/resident/learning-store.ts → learning/store.ts} +97 -67
  263. package/src/manager/resident/activity.ts +30 -6
  264. package/src/manager/resident/agenda.ts +63 -33
  265. package/src/manager/resident/consumption.ts +27 -5
  266. package/src/manager/resident/delivery-window.ts +7 -1
  267. package/src/manager/resident/evidence-recall.ts +7 -1
  268. package/src/manager/resident/history.ts +45 -9
  269. package/src/manager/resident/host.ts +45 -11
  270. package/src/manager/resident/initiative.ts +32 -6
  271. package/src/manager/resident/loop.ts +22 -4
  272. package/src/manager/resident/outbox.ts +42 -8
  273. package/src/manager/resident/proposal.ts +17 -3
  274. package/src/manager/resident/store.ts +27 -5
  275. package/src/manager/resident/tool-evidence.ts +45 -9
  276. package/src/prompt/coding-agent-doctrine.ts +18 -20
  277. package/src/prompt/resident-learning.ts +16 -7
  278. package/src/prompt/resident-step.ts +19 -13
  279. package/src/public-runtime.ts +36 -5
  280. package/src/public-types.ts +96 -34
  281. package/src/runtime/query/checkpoint.ts +49 -8
  282. package/src/runtime/query/executor.ts +165 -15
  283. package/src/runtime/query/index.ts +45 -1
  284. package/src/runtime/query/iteration/index.ts +19 -6
  285. package/src/runtime/query/iteration/phases/context.ts +37 -11
  286. package/src/runtime/query/iteration/phases/per-call-review.ts +617 -0
  287. package/src/runtime/query/iteration/phases/tool-review.ts +168 -83
  288. package/src/runtime/query/iteration/step-context.ts +29 -0
  289. package/src/runtime/query/iteration/step-shaping.ts +6 -2
  290. package/src/runtime/query/prepare-turn.ts +18 -1
  291. package/src/runtime/query/project-instructions.ts +119 -38
  292. package/src/runtime/query/prompt.ts +11 -54
  293. package/src/runtime/query/resume-pending.ts +131 -0
  294. package/src/runtime/query/review-policy.ts +28 -0
  295. package/src/runtime/query/steering.ts +100 -96
  296. package/src/tools/builtins/bash.ts +13 -19
  297. package/src/tools/builtins/browser.ts +38 -12
  298. package/src/tools/builtins/edit.ts +4 -4
  299. package/src/tools/builtins/job.ts +1 -1
  300. package/src/tools/builtins/skill.ts +1 -1
  301. package/src/tools/builtins/structuredOutput.ts +1 -1
  302. package/src/tools/builtins/verify-outputs.ts +1 -1
  303. package/src/tools/builtins/write-file.ts +4 -4
  304. package/src/tools/coordinator/ask-user-question.ts +12 -1
  305. package/src/tools/coordinator/index.ts +10 -3
  306. package/src/tools/resident-history.ts +2 -0
  307. package/src/tools/resident-tool-evidence.ts +5 -1
  308. package/src/tools/schedules/loop-tool.ts +4 -2
  309. package/src/tools/schedules/schedule-tool.ts +2 -2
  310. package/src/tools/task/create.ts +1 -1
  311. package/src/tools/untrusted-envelope.ts +1 -1
  312. package/src/turn/evidence-query.ts +19 -1
  313. package/src/turn/evidence-recall.ts +1 -1
  314. package/src/types/agent/base.ts +8 -0
  315. package/src/types/hitl/index.ts +67 -0
  316. package/src/types/message/index.ts +20 -2
  317. package/src/types/message/notice-frame.ts +88 -0
  318. package/dist/manager/resident/learning-cycle.d.ts.map +0 -1
  319. package/dist/manager/resident/learning-cycle.js.map +0 -1
  320. package/dist/manager/resident/learning-observation.d.ts.map +0 -1
  321. package/dist/manager/resident/learning-observation.js.map +0 -1
  322. package/dist/manager/resident/learning-store.d.ts.map +0 -1
  323. package/dist/manager/resident/learning-store.js.map +0 -1
  324. package/dist/manager/resident/learning.d.ts.map +0 -1
  325. package/dist/manager/resident/learning.js.map +0 -1
@@ -19,11 +19,12 @@ import {
19
19
  type UserMessage,
20
20
  createRuntimeContextMessage,
21
21
  } from '../../types/message/index.js'
22
+ import { escapeNoticeFrame } from '../../types/message/notice-frame.js'
22
23
  import { CHECKPOINT_DOCUMENT_VERSION, type Checkpoint } from '../../types/session/checkpoint.js'
23
24
  import type { CheckpointListEntry } from '../../types/session/fork.js'
24
25
  import type { SessionRecord } from '../../types/session/records.js'
25
26
  import { asGoalId, generateCheckpointId } from '../../utils/id.js'
26
- import { formatSteeringNote, readSteeringNote } from './steering.js'
27
+ import { readSteeringNote } from './steering.js'
27
28
 
28
29
  /** Keep intent text/provenance without copying attachment payloads into a second slot. */
29
30
  function snapshotUserIntent(value: unknown): UserMessage {
@@ -460,8 +461,8 @@ export class CheckpointManager {
460
461
 
461
462
  /**
462
463
  * The id of the record that carries a message's text: its own record,
463
- * or — for operator guidance delivered attached to a tool result — the
464
- * record of the tool result that carries it.
464
+ * or, for the operator's message sent while a tool ran, the record of the
465
+ * framed message that carries it.
465
466
  */
466
467
  private messageIdOf(recorder: TurnRecorder, message: Message): MessageId | undefined {
467
468
  if (message === this.restoredUserMessage) return this.restoredUserMessageId
@@ -483,14 +484,17 @@ export class CheckpointManager {
483
484
  ) {
484
485
  return undefined
485
486
  }
486
- const note = formatSteeringNote(message.content)
487
+ // The operator's words were sent as a framed message of their own whose
488
+ // marker is random, so it is found by what its frame holds.
489
+ const wanted = escapeNoticeFrame(message.content)
487
490
  const messages = recorder.messages
488
491
  for (let i = messages.length - 1; i >= 0; i--) {
489
492
  const candidate = messages[i] as Message
490
493
  if (
491
- candidate.role === 'tool' &&
494
+ candidate.role === 'user' &&
492
495
  typeof candidate.content === 'string' &&
493
- candidate.content.endsWith(note)
496
+ (candidate as UserMessage).source?.type === 'runtime-context' &&
497
+ readSteeringNote(candidate.content) === wanted
494
498
  ) {
495
499
  const id = recorder.recordedIdOf(candidate)
496
500
  if (id) this.steeringCarriers.set(message, id)
@@ -578,6 +582,42 @@ export class CheckpointManager {
578
582
  return park
579
583
  }
580
584
 
585
+ /**
586
+ * End the parks that were the other questions of a batch whose decision has
587
+ * just been applied (the per-call review parks one question per call).
588
+ *
589
+ * The batch is answered now, so a question about one of its calls is moot;
590
+ * left open, an approval queue would keep serving it and a host resuming it
591
+ * would rewind the turn to a batch that has long since been carried out.
592
+ * Recorded as superseded, not as an answer: nobody gave one.
593
+ */
594
+ async supersedeBatchParks(
595
+ callIds: ReadonlySet<string>,
596
+ except: CheckpointId,
597
+ superseding: HITLResumeDecision,
598
+ ): Promise<number> {
599
+ await this.recorder.flush()
600
+ const parks = await readParks(this.recorder.log, {
601
+ turnId: this.scope.turnId,
602
+ })
603
+ let ended = 0
604
+ for (const park of parks) {
605
+ const request = park.pending.request
606
+ if (
607
+ park.checkpointId === except ||
608
+ park.pending.resolvedAt !== undefined ||
609
+ request.type !== 'tool_review' ||
610
+ request.perCall !== true ||
611
+ !request.toolCalls.every((call) => callIds.has(call.id))
612
+ ) {
613
+ continue
614
+ }
615
+ await this.unpark(park.checkpointId, superseding)
616
+ ended += 1
617
+ }
618
+ return ended
619
+ }
620
+
581
621
  private async openPark(checkpointId: CheckpointId): Promise<RecordedPark | null> {
582
622
  await this.recorder.flush()
583
623
  const parks = await readParks(this.recorder.log, { turnId: this.scope.turnId })
@@ -693,9 +733,10 @@ export async function restoreCheckpointContext(
693
733
  `Checkpoint latestUserMessage names message ${intentId}, which its session log does not hold`,
694
734
  )
695
735
  }
696
- // Guidance delivered attached to a tool result: the intent is the note.
736
+ // Guidance the operator sent while a tool ran: the intent is the note, whether
737
+ // it is a message of its own or, in an older session, the end of a tool result.
697
738
  const guidance =
698
- intent?.role === 'tool' && typeof intent.content === 'string'
739
+ (intent?.role === 'tool' || intent?.role === 'user') && typeof intent.content === 'string'
699
740
  ? readSteeringNote(intent.content)
700
741
  : undefined
701
742
  const operatorIntent =
@@ -567,6 +567,43 @@ export interface ToolCallOutcome {
567
567
  revealedTools?: readonly ToolRevealReceipt[]
568
568
  }
569
569
 
570
+ /** What the per-call review decided about one call that reached the front of the walk. */
571
+ export type CallAdmission =
572
+ | { readonly run: true }
573
+ | {
574
+ readonly run: false
575
+ readonly reason: string
576
+ readonly declined?: ToolCallDeclined
577
+ }
578
+
579
+ /**
580
+ * The per-call review, as the executor sees it: a batch executed while its
581
+ * calls are still being asked about.
582
+ *
583
+ * The executor walks the calls in the model's order. A call that is safe to run
584
+ * beside others starts as soon as every call already in flight is also safe;
585
+ * any other call starts only when nothing is in flight, so it never overlaps an
586
+ * earlier call, and nothing after it starts before it does. Starting a call
587
+ * means asking `admit` and, on a yes, running it right away.
588
+ */
589
+ export interface ReviewedExecution {
590
+ admit(call: ToolCall, index: number): Promise<CallAdmission>
591
+ /**
592
+ * Aborts when a refusal ends the turn. Calls that are running are cancelled
593
+ * through it, and a call that has not started is refused without `admit`
594
+ * being consulted for a yes.
595
+ */
596
+ readonly ended: AbortSignal
597
+ }
598
+
599
+ interface ReviewUnit {
600
+ readonly index: number
601
+ readonly toolCall: ToolCall
602
+ readonly preparedCall?: PreparedDirectCall
603
+ readonly safe: boolean
604
+ readonly run: () => Promise<void>
605
+ }
606
+
570
607
  export interface ToolExecutionBatch {
571
608
  messages: Message[]
572
609
  results: ToolCallOutcome[]
@@ -1268,6 +1305,7 @@ export class ToolExecutor {
1268
1305
  prior?: PriorToolResults,
1269
1306
  preparedBatch?: PreparedToolBatch,
1270
1307
  declines?: ToolCallDeclines,
1308
+ review?: ReviewedExecution,
1271
1309
  ): Promise<ToolExecutionBatch> {
1272
1310
  const toolCalls = response.message.toolCalls
1273
1311
  if (!toolCalls) {
@@ -1294,9 +1332,9 @@ export class ToolExecutor {
1294
1332
  if (refusal) {
1295
1333
  const refused = new Map(denials)
1296
1334
  for (const call of toolCalls) if (!refused.has(call.id)) refused.set(call.id, refusal)
1297
- return await this.runBatch(toolCalls, refused, prior, owned, declines)
1335
+ return await this.runBatch(toolCalls, refused, prior, owned, declines, review)
1298
1336
  }
1299
- return await this.runBatch(toolCalls, denials, prior, owned, declines)
1337
+ return await this.runBatch(toolCalls, denials, prior, owned, declines, review)
1300
1338
  } finally {
1301
1339
  // Cleared so a later single execution outside a batch resolves
1302
1340
  // live rather than inheriting the last batch's sample.
@@ -1310,6 +1348,7 @@ export class ToolExecutor {
1310
1348
  prior?: PriorToolResults,
1311
1349
  preparedBatch?: OwnedPreparedToolBatch,
1312
1350
  declines?: ToolCallDeclines,
1351
+ review?: ReviewedExecution,
1313
1352
  ): Promise<ToolExecutionBatch> {
1314
1353
  this.log.debug('Executing tool batch', {
1315
1354
  [NAMZU.TURN_ID]: this.config.turnId,
@@ -1370,6 +1409,9 @@ export class ToolExecutor {
1370
1409
  // gate keeps that at a working-set size while preserving completion
1371
1410
  // order independence.
1372
1411
  const gate = new Semaphore(this.config.maxToolConcurrency ?? DEFAULT_TOOL_CONCURRENCY)
1412
+ // Under a per-call review the calls to run are collected here and walked
1413
+ // afterwards; refusals that were settled before the review need no walk.
1414
+ const reviewUnits: ReviewUnit[] = []
1373
1415
  toolCalls.forEach((toolCall, i) => {
1374
1416
  const preparedCall = preparedBatch?.calls.get(toolCall.id)
1375
1417
  const tool = this.config.tools.get(preparedCall?.toolName ?? toolCall.function.name)
@@ -1391,7 +1433,7 @@ export class ToolExecutor {
1391
1433
  ? { structuredResultJson: recovered.structuredResultJson }
1392
1434
  : {}),
1393
1435
  }
1394
- if (isBarrier) schedule(async () => {}, true, true)
1436
+ if (isBarrier && !review) schedule(async () => {}, true, true)
1395
1437
  return
1396
1438
  }
1397
1439
 
@@ -1400,18 +1442,16 @@ export class ToolExecutor {
1400
1442
  // Denied calls never touch the tool; they still get a result
1401
1443
  // message so the assistant turn stays fully answered. They can
1402
1444
  // overlap other calls within a segment, respecting its barriers.
1403
- schedule(
1404
- async () => {
1405
- results[i] = await this.recordDenial(
1406
- toolCall,
1407
- denialReason,
1408
- preparedCall,
1409
- declines?.get(toolCall.id),
1410
- )
1411
- },
1412
- true,
1413
- isBarrier,
1414
- )
1445
+ const recordDenied = async () => {
1446
+ results[i] = await this.recordDenial(
1447
+ toolCall,
1448
+ denialReason,
1449
+ preparedCall,
1450
+ declines?.get(toolCall.id),
1451
+ )
1452
+ }
1453
+ if (review) parallel.push(recordDenied())
1454
+ else schedule(recordDenied, true, isBarrier)
1415
1455
  return
1416
1456
  }
1417
1457
  // Per-call, because the event has to name which call it is about:
@@ -1436,6 +1476,13 @@ export class ToolExecutor {
1436
1476
  ...(escalation?.outsidePaths ? { approvedPaths: escalation.outsidePaths } : {}),
1437
1477
  ...(escalation?.sandboxEscape ? { sandboxEscapeApproved: true } : {}),
1438
1478
  source: { kind: 'direct' },
1479
+ // A refusal that ends the turn cancels what is running, through the
1480
+ // same signal a tool already honours for a cancelled turn.
1481
+ ...(review
1482
+ ? {
1483
+ abortSignal: AbortSignal.any([baseContext.abortSignal, review.ended]),
1484
+ }
1485
+ : {}),
1439
1486
  // Overridden per call, so a nested dispatch can name the call
1440
1487
  // that made it. The base context has no `toolUseId`, and a
1441
1488
  // closure built there would report every nested call as
@@ -1477,8 +1524,19 @@ export class ToolExecutor {
1477
1524
  // non-JSON args → treat as unsafe (serialize), the conservative path
1478
1525
  }
1479
1526
  const safe = tool?.isConcurrencySafe?.(input) === true
1527
+ if (review) {
1528
+ reviewUnits.push({
1529
+ index: i,
1530
+ toolCall,
1531
+ ...(preparedCall ? { preparedCall } : {}),
1532
+ safe: safe && !isBarrier,
1533
+ run: safe ? gated : run,
1534
+ })
1535
+ return
1536
+ }
1480
1537
  schedule(safe ? gated : run, safe, isBarrier)
1481
1538
  })
1539
+ if (review) await this.walkReviewed(reviewUnits, results, review)
1482
1540
  await Promise.all([...parallel, serial])
1483
1541
 
1484
1542
  // Whatever failed above left a hole in `results`; fill every one, so
@@ -1518,6 +1576,98 @@ export class ToolExecutor {
1518
1576
  return { messages, results, observations }
1519
1577
  }
1520
1578
 
1579
+ /**
1580
+ * Walk the calls of a batch in the model's order, asking about each as it
1581
+ * reaches the front and running it right after a yes.
1582
+ *
1583
+ * A call may start when nothing is in flight, or when it and every call in
1584
+ * flight are safe to run side by side. The walk stops at the first call that
1585
+ * may not start yet, so an unsafe call is never overtaken by one that came
1586
+ * after it. Once a refusal has ended the turn, nothing waits: every call that
1587
+ * has not started is refused at once and the ones in flight are cancelled.
1588
+ */
1589
+ private async walkReviewed(
1590
+ units: readonly ReviewUnit[],
1591
+ results: ToolCallOutcome[],
1592
+ review: ReviewedExecution,
1593
+ ): Promise<void> {
1594
+ const inFlight = new Set<ReviewUnit>()
1595
+ let cursor = 0
1596
+ let failure: { readonly error: unknown } | undefined
1597
+ // Once the turn has ended, a refusal is written only after every call
1598
+ // before it has settled, so the transcript lists the calls in the order the
1599
+ // model wrote them: the declined call before the ones refused unasked.
1600
+ const settledUnits = new Map<number, Promise<void>>()
1601
+ const refusedAfterEnd =
1602
+ 'An earlier call in this batch was refused and the turn was stopped, so this call was not run.'
1603
+ await new Promise<void>((resolve) => {
1604
+ const start = (unit: ReviewUnit): void => {
1605
+ inFlight.add(unit)
1606
+ let settledUnit!: () => void
1607
+ settledUnits.set(
1608
+ unit.index,
1609
+ new Promise<void>((done) => {
1610
+ settledUnit = done
1611
+ }),
1612
+ )
1613
+ void (async () => {
1614
+ try {
1615
+ let admission: CallAdmission
1616
+ try {
1617
+ admission = await review.admit(unit.toolCall, unit.index)
1618
+ } catch {
1619
+ // An answer that could not be obtained is not a yes.
1620
+ admission = {
1621
+ run: false,
1622
+ reason: 'The approval for this call could not be completed, so it was not run.',
1623
+ }
1624
+ }
1625
+ if (admission.run && !review.ended.aborted) {
1626
+ await unit.run()
1627
+ } else {
1628
+ const refusal = admission.run
1629
+ ? { reason: refusedAfterEnd }
1630
+ : { reason: admission.reason, declined: admission.declined }
1631
+ if (review.ended.aborted) {
1632
+ await Promise.all(
1633
+ [...settledUnits].filter(([index]) => index < unit.index).map(([, done]) => done),
1634
+ )
1635
+ }
1636
+ results[unit.index] = await this.recordDenial(
1637
+ unit.toolCall,
1638
+ refusal.reason,
1639
+ unit.preparedCall,
1640
+ 'declined' in refusal ? refusal.declined : undefined,
1641
+ )
1642
+ }
1643
+ } catch (error) {
1644
+ failure ??= { error }
1645
+ } finally {
1646
+ settledUnit()
1647
+ inFlight.delete(unit)
1648
+ pump()
1649
+ }
1650
+ })()
1651
+ }
1652
+ const pump = (): void => {
1653
+ while (cursor < units.length) {
1654
+ const unit = units[cursor] as ReviewUnit
1655
+ if (!review.ended.aborted) {
1656
+ const mayStart =
1657
+ inFlight.size === 0 || (unit.safe && [...inFlight].every((other) => other.safe))
1658
+ if (!mayStart) break
1659
+ }
1660
+ cursor++
1661
+ start(unit)
1662
+ }
1663
+ if (cursor >= units.length && inFlight.size === 0) resolve()
1664
+ }
1665
+ review.ended.addEventListener('abort', pump, { once: true })
1666
+ pump()
1667
+ })
1668
+ if (failure) throw failure.error
1669
+ }
1670
+
1521
1671
  /**
1522
1672
  * Hold the batch's combined result text under the per-message cap.
1523
1673
  *
@@ -786,6 +786,15 @@ export interface QueryParams {
786
786
  * See {@link ReminderSource}.
787
787
  */
788
788
  reminders?: import('./reminders.js').ReminderSource
789
+ /**
790
+ * Whether the kernel tells the model, once, that the approval policy
791
+ * changed ("Approval policy changed from X to Y ..."), as request-only
792
+ * context after the history. Default `true`. A host that already tells the
793
+ * model about a mode change itself (through `reminders`) sets `false` so
794
+ * the model hears it once; the `approval_policy_changed` event is
795
+ * recorded either way.
796
+ */
797
+ announceApprovalPolicyChange?: boolean
789
798
 
790
799
  /**
791
800
  * Wake a background-task hold when operator input is available, without draining it.
@@ -840,6 +849,16 @@ export interface QueryParams {
840
849
  */
841
850
  reviewAllowedCalls?: () => boolean
842
851
 
852
+ /**
853
+ * Ask about one call at a time while the batch runs, instead of reviewing the
854
+ * whole batch as a unit. Opt-in; absent, nothing about review changes. See
855
+ * {@link import('../../types/hitl/index.js').PerCallReviewConfig}.
856
+ *
857
+ * Only this turn is affected: a delegated child reviews its own batches as
858
+ * it did before.
859
+ */
860
+ perCallReview?: import('../../types/hitl/index.js').PerCallReviewConfig
861
+
843
862
  /**
844
863
  * A name for the policy `resumeHandler` implements.
845
864
  *
@@ -1770,7 +1789,11 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
1770
1789
  // Read through the box on every call, so a swap lands on the next
1771
1790
  // question rather than the next turn.
1772
1791
  resumeHandler: (request, context) => approvalPolicy.current.handler(request, context),
1773
- takeApprovalPolicyChange: () => approvalPolicy.takeUnannouncedChange(),
1792
+ takeApprovalPolicyChange: () => {
1793
+ // Taken either way, so the change is not left pending for a later turn.
1794
+ const change = approvalPolicy.takeUnannouncedChange()
1795
+ return params.announceApprovalPolicyChange === false ? undefined : change
1796
+ },
1774
1797
  promptContributions,
1775
1798
  ...(params.steering ? { steering: params.steering } : {}),
1776
1799
  ...(jobNotices ? { jobNotices } : {}),
@@ -1788,6 +1811,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
1788
1811
  toolGrants: new ToolGrantSet(),
1789
1812
  skillGrants,
1790
1813
  ...(params.reviewAllowedCalls ? { reviewAllowedCalls: params.reviewAllowedCalls } : {}),
1814
+ ...(params.perCallReview ? { perCallReview: params.perCallReview } : {}),
1791
1815
  // Turn-scoped for the same reason. A repeat count carried into a later
1792
1816
  // run is a claim about work nobody repeated, and a module-level map
1793
1817
  // would leak exactly that way.
@@ -2690,6 +2714,26 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
2690
2714
  })
2691
2715
  return null
2692
2716
  })
2717
+ // A decision about one call of a batch answered the batch: the
2718
+ // batch's other open questions are moot.
2719
+ if (pendingResume?.partial && params.pendingDecision) {
2720
+ const batchIds = new Set(
2721
+ pendingResume.response.message.toolCalls?.map((call) => call.id) ?? [],
2722
+ )
2723
+ await checkpointMgr
2724
+ .supersedeBatchParks(
2725
+ batchIds,
2726
+ resolvedCheckpointId,
2727
+ supersededByRecovery(params.pendingDecision),
2728
+ )
2729
+ .catch((err: unknown) => {
2730
+ ctx.log.error('Applied a pending decision but failed to clear its sibling parks', {
2731
+ [NAMZU.TURN_ID]: ctx.turnId,
2732
+ 'exception.message': err instanceof Error ? err.message : String(err),
2733
+ })
2734
+ return 0
2735
+ })
2736
+ }
2693
2737
  }
2694
2738
 
2695
2739
  yield* iterationOrchestrator.runLoop()
@@ -102,6 +102,7 @@ import {
102
102
  streamWithProviderRejectedImageRecovery,
103
103
  } from './provider-rejected-image.js'
104
104
  import { type MediaRemoval, RequestRepair } from './request-repair.js'
105
+ import { placeBeforeLatestOperatorMessage } from './step-context.js'
105
106
  import {
106
107
  type StepShaping,
107
108
  appendWorkContext,
@@ -853,8 +854,8 @@ export class IterationOrchestrator {
853
854
  // there is no length check here — a second guard for the same
854
855
  // case is one more thing to keep in agreement with the first.
855
856
  const stepSkills = step.skills ? renderSkillsSection([...step.skills]) : null
856
- // A supervision change rides the same ephemeral slot, and for
857
- // the same reason: it applies to what happens next, not to the
857
+ // A supervision change is request-only context, and for the
858
+ // same reason: it applies to what happens next, not to the
858
859
  // run's history. The model plans around how closely it is being
859
860
  // watched — a turn that silently stops asking a human leaves it
860
861
  // batching destructive calls it expects to be reviewed, and one
@@ -879,7 +880,7 @@ export class IterationOrchestrator {
879
880
 
880
881
  // Request-only context: observations that need no system
881
882
  // authority and change from request to request. They ride
882
- // runtime-context messages of kind `step-context` after the
883
+ // runtime-context messages of kind `step-context`, most of them after the
883
884
  // history, which every driver keeps there and a caching driver
884
885
  // ends its breakpoint before — so a changed pin or a new turn
885
886
  // snapshot costs its own tokens, not a re-read of the history.
@@ -887,15 +888,27 @@ export class IterationOrchestrator {
887
888
  // (compaction preserves it there) and leaves the request's
888
889
  // system run here.
889
890
  const workingMemory = splitWorkingMemoryForRequest(baseMessages)
890
- const stepPreamble = [step.system, stepSkills, policyNotice, ...turnSections]
891
+ const stepPreamble = [step.system, stepSkills, ...turnSections]
891
892
  .filter(Boolean)
892
893
  .join('\n\n')
893
894
  const requestHistory = stepPreamble
894
895
  ? [...workingMemory.history, createSystemMessage(stepPreamble)]
895
896
  : [...workingMemory.history]
896
897
  if (workingMemory.context) requestHistory.push(workingMemory.context)
897
- if (contextText) requestHistory.push(stepContextMessage(contextText))
898
- if (step.context) requestHistory.push(stepContextMessage(step.context))
898
+ // The blocks of data that belong to this turn (recalled memory and
899
+ // evidence, the context contributions) sit just before the
900
+ // operator's latest message, so the request is the last thing the
901
+ // model reads. The short reminders and notices stay after it.
902
+ placeBeforeLatestOperatorMessage(
903
+ requestHistory,
904
+ [contextText, step.context]
905
+ .filter((text): text is string => Boolean(text))
906
+ .map(stepContextMessage),
907
+ )
908
+ // After the history, never ahead of it: a system message in front
909
+ // of the conversation moves the prefix every cached request shares,
910
+ // and a mode change would re-key the whole conversation.
911
+ if (policyNotice) requestHistory.push(stepContextMessage(policyNotice))
899
912
  const messages = projectRequestRichContent(
900
913
  this.projectObservations(requestHistory),
901
914
  this.ctx.turnConfig.maxRequestRichContentBytes ??
@@ -201,6 +201,8 @@ export interface IterationContext {
201
201
  readonly skillGrants?: SkillGrantSet
202
202
  /** See `QueryParams.reviewAllowedCalls`. Absent: allowed and granted batches skip review. */
203
203
  readonly reviewAllowedCalls?: () => boolean
204
+ /** See `QueryParams.perCallReview`. Absent: a batch is reviewed as a unit. */
205
+ readonly perCallReview?: import('../../../../types/hitl/index.js').PerCallReviewConfig
204
206
  /**
205
207
  * Absent when the host opted out with `repeatCallAdvisory: false`. The
206
208
  * opt-out is the ABSENCE, not a flag read at every call site, so a code
@@ -316,9 +318,10 @@ export async function awaitDecisionDurably(
316
318
  ctx: IterationContext,
317
319
  checkpoint: { readonly id: CheckpointId },
318
320
  request: Parameters<ResumeHandler>[0],
321
+ scope?: AskScope,
319
322
  ): Promise<HITLResumeDecision> {
320
323
  const delay = ctx.parkRecordDelayMs ?? PARK_RECORD_DELAY_MS
321
- const decisionPromise = awaitDecisionOrAbort(ctx, request)
324
+ const decisionPromise = awaitDecisionOrAbort(ctx, request, scope)
322
325
 
323
326
  let settled = false
324
327
  let recorded = false
@@ -423,9 +426,26 @@ export async function awaitDecisionDurably(
423
426
  * turns into `setStopReason('cancelled') + markCancelled + stop`. Fails closed:
424
427
  * a resume-handler rejection also resolves to `abort` rather than hanging.
425
428
  */
429
+ /**
430
+ * One question among several that are open at once (the per-call review).
431
+ *
432
+ * `signal` closes just this question; when it aborts and the turn itself was not
433
+ * cancelled, `closedAs` says what the question's answer is, so the park is
434
+ * recorded as what really ended it rather than as a cancelled turn.
435
+ */
436
+ export interface AskScope {
437
+ readonly signal: AbortSignal
438
+ readonly closedAs: () => HITLResumeDecision
439
+ }
440
+
426
441
  /** What a resume handler may read besides the request; see {@link ResumeContext}. */
427
- function resumeContextOf(ctx: IterationContext): ResumeContext {
428
- const signal = ctx.abortController?.signal
442
+ function resumeContextOf(ctx: IterationContext, scope?: AskScope): ResumeContext {
443
+ const turnSignal = ctx.abortController?.signal
444
+ const signal = scope
445
+ ? turnSignal
446
+ ? AbortSignal.any([turnSignal, scope.signal])
447
+ : scope.signal
448
+ : turnSignal
429
449
  return {
430
450
  transcript: () => ctx.recorder.messages,
431
451
  ...(signal ? { signal } : {}),
@@ -435,24 +455,30 @@ function resumeContextOf(ctx: IterationContext): ResumeContext {
435
455
  export async function awaitDecisionOrAbort(
436
456
  ctx: IterationContext,
437
457
  request: Parameters<ResumeHandler>[0],
458
+ scope?: AskScope,
438
459
  ): Promise<HITLResumeDecision> {
439
- const signal = ctx.abortController?.signal
460
+ const turnSignal = ctx.abortController?.signal
461
+ const signal = scope
462
+ ? turnSignal
463
+ ? AbortSignal.any([turnSignal, scope.signal])
464
+ : scope.signal
465
+ : turnSignal
440
466
  // No abort signal wired (e.g. a minimal test harness) → behave exactly as a
441
467
  // direct resumeHandler await, no race. In production TurnContextFactory always
442
468
  // provides the controller, so the race below is live.
443
- const resumeContext = resumeContextOf(ctx)
469
+ const resumeContext = resumeContextOf(ctx, scope)
444
470
  if (!signal) return ctx.resumeHandler(request, resumeContext)
445
- const abortDecision: HITLResumeDecision = {
446
- action: 'abort',
447
- reason: 'run aborted while parked for HITL',
448
- }
449
- if (signal.aborted) return abortDecision
471
+ const abortedDecision = (): HITLResumeDecision =>
472
+ scope && !turnSignal?.aborted
473
+ ? scope.closedAs()
474
+ : { action: 'abort', reason: 'run aborted while parked for HITL' }
475
+ if (signal.aborted) return abortedDecision()
450
476
  return new Promise<HITLResumeDecision>((resolve) => {
451
477
  let settled = false
452
478
  const onAbort = (): void => {
453
479
  if (settled) return
454
480
  settled = true
455
- resolve(abortDecision)
481
+ resolve(abortedDecision())
456
482
  }
457
483
  signal.addEventListener('abort', onAbort, { once: true })
458
484
  Promise.resolve(ctx.resumeHandler(request, resumeContext)).then(