@namzu/sdk 62.6.1 → 62.8.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 (344) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +5 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
  6. package/dist/agents/examples/SupervisorAgent.js +1 -0
  7. package/dist/agents/examples/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/explore.d.ts.map +1 -1
  9. package/dist/agents/explore.js +2 -1
  10. package/dist/agents/explore.js.map +1 -1
  11. package/dist/compaction/index.d.ts +1 -1
  12. package/dist/compaction/index.d.ts.map +1 -1
  13. package/dist/compaction/index.js +1 -1
  14. package/dist/compaction/index.js.map +1 -1
  15. package/dist/compaction/model-summary.js +2 -2
  16. package/dist/compaction/model-summary.js.map +1 -1
  17. package/dist/compaction/summary.d.ts +16 -1
  18. package/dist/compaction/summary.d.ts.map +1 -1
  19. package/dist/compaction/summary.js +25 -2
  20. package/dist/compaction/summary.js.map +1 -1
  21. package/dist/config/runtime.d.ts +8 -0
  22. package/dist/config/runtime.d.ts.map +1 -1
  23. package/dist/config/runtime.js +15 -0
  24. package/dist/config/runtime.js.map +1 -1
  25. package/dist/execution/workflow/runtime.d.ts.map +1 -1
  26. package/dist/execution/workflow/runtime.js +3 -0
  27. package/dist/execution/workflow/runtime.js.map +1 -1
  28. package/dist/execution/workflow/types.d.ts +8 -0
  29. package/dist/execution/workflow/types.d.ts.map +1 -1
  30. package/dist/manager/agent/child-session.d.ts +12 -0
  31. package/dist/manager/agent/child-session.d.ts.map +1 -1
  32. package/dist/manager/agent/child-session.js +29 -0
  33. package/dist/manager/agent/child-session.js.map +1 -1
  34. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  35. package/dist/manager/agent/lifecycle.js +8 -0
  36. package/dist/manager/agent/lifecycle.js.map +1 -1
  37. package/dist/manager/session/turn-recorder.d.ts +13 -2
  38. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  39. package/dist/manager/session/turn-recorder.js +107 -29
  40. package/dist/manager/session/turn-recorder.js.map +1 -1
  41. package/dist/prompt/coding-agent-doctrine.d.ts +3 -3
  42. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  43. package/dist/prompt/coding-agent-doctrine.js +5 -3
  44. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  45. package/dist/provider/fallback.d.ts.map +1 -1
  46. package/dist/provider/fallback.js +5 -1
  47. package/dist/provider/fallback.js.map +1 -1
  48. package/dist/provider/retry.d.ts +25 -0
  49. package/dist/provider/retry.d.ts.map +1 -1
  50. package/dist/provider/retry.js +28 -0
  51. package/dist/provider/retry.js.map +1 -1
  52. package/dist/public-runtime.d.ts +3 -3
  53. package/dist/public-runtime.d.ts.map +1 -1
  54. package/dist/public-runtime.js +3 -3
  55. package/dist/public-runtime.js.map +1 -1
  56. package/dist/public-tools.d.ts +4 -2
  57. package/dist/public-tools.d.ts.map +1 -1
  58. package/dist/public-tools.js +5 -2
  59. package/dist/public-tools.js.map +1 -1
  60. package/dist/runtime/query/auto-review/classifier.d.ts +7 -1
  61. package/dist/runtime/query/auto-review/classifier.d.ts.map +1 -1
  62. package/dist/runtime/query/auto-review/classifier.js +8 -4
  63. package/dist/runtime/query/auto-review/classifier.js.map +1 -1
  64. package/dist/runtime/query/auto-review/prompt.d.ts.map +1 -1
  65. package/dist/runtime/query/auto-review/prompt.js +2 -0
  66. package/dist/runtime/query/auto-review/prompt.js.map +1 -1
  67. package/dist/runtime/query/auto-review/script-evidence.d.ts +93 -0
  68. package/dist/runtime/query/auto-review/script-evidence.d.ts.map +1 -0
  69. package/dist/runtime/query/auto-review/script-evidence.js +566 -0
  70. package/dist/runtime/query/auto-review/script-evidence.js.map +1 -0
  71. package/dist/runtime/query/checkpoint.d.ts +13 -0
  72. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  73. package/dist/runtime/query/checkpoint.js +37 -0
  74. package/dist/runtime/query/checkpoint.js.map +1 -1
  75. package/dist/runtime/query/context.d.ts.map +1 -1
  76. package/dist/runtime/query/context.js +28 -2
  77. package/dist/runtime/query/context.js.map +1 -1
  78. package/dist/runtime/query/executor.d.ts +38 -1
  79. package/dist/runtime/query/executor.d.ts.map +1 -1
  80. package/dist/runtime/query/executor.js +154 -13
  81. package/dist/runtime/query/executor.js.map +1 -1
  82. package/dist/runtime/query/finalize-turn.d.ts +7 -1
  83. package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
  84. package/dist/runtime/query/finalize-turn.js +2 -0
  85. package/dist/runtime/query/finalize-turn.js.map +1 -1
  86. package/dist/runtime/query/index.d.ts +8 -0
  87. package/dist/runtime/query/index.d.ts.map +1 -1
  88. package/dist/runtime/query/index.js +7 -0
  89. package/dist/runtime/query/index.js.map +1 -1
  90. package/dist/runtime/query/iteration/index.d.ts +32 -1
  91. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  92. package/dist/runtime/query/iteration/index.js +158 -10
  93. package/dist/runtime/query/iteration/index.js.map +1 -1
  94. package/dist/runtime/query/iteration/phases/compaction.d.ts +12 -0
  95. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  96. package/dist/runtime/query/iteration/phases/compaction.js +48 -1
  97. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  98. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  99. package/dist/runtime/query/iteration/phases/context.js +5 -0
  100. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  101. package/dist/runtime/query/iteration/stream-turn.js +3 -3
  102. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  103. package/dist/runtime/query/prelude-lease.d.ts.map +1 -1
  104. package/dist/runtime/query/prelude-lease.js +22 -0
  105. package/dist/runtime/query/prelude-lease.js.map +1 -1
  106. package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
  107. package/dist/runtime/query/prepare-turn.js +30 -12
  108. package/dist/runtime/query/prepare-turn.js.map +1 -1
  109. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  110. package/dist/runtime/query/review-policy.js +29 -0
  111. package/dist/runtime/query/review-policy.js.map +1 -1
  112. package/dist/runtime/query/session-storage.d.ts.map +1 -1
  113. package/dist/runtime/query/session-storage.js +44 -3
  114. package/dist/runtime/query/session-storage.js.map +1 -1
  115. package/dist/runtime/query/tooling.d.ts +2 -0
  116. package/dist/runtime/query/tooling.d.ts.map +1 -1
  117. package/dist/runtime/query/tooling.js +1 -0
  118. package/dist/runtime/query/tooling.js.map +1 -1
  119. package/dist/runtime/query/turn/completion-audit.d.ts +27 -1
  120. package/dist/runtime/query/turn/completion-audit.d.ts.map +1 -1
  121. package/dist/runtime/query/turn/completion-audit.js +71 -6
  122. package/dist/runtime/query/turn/completion-audit.js.map +1 -1
  123. package/dist/runtime/query/turn/outcome-ledger.d.ts +113 -0
  124. package/dist/runtime/query/turn/outcome-ledger.d.ts.map +1 -0
  125. package/dist/runtime/query/turn/outcome-ledger.js +578 -0
  126. package/dist/runtime/query/turn/outcome-ledger.js.map +1 -0
  127. package/dist/sandbox/file-walk.d.ts +7 -1
  128. package/dist/sandbox/file-walk.d.ts.map +1 -1
  129. package/dist/sandbox/file-walk.js +30 -4
  130. package/dist/sandbox/file-walk.js.map +1 -1
  131. package/dist/sandbox/glob-segment.d.ts +8 -1
  132. package/dist/sandbox/glob-segment.d.ts.map +1 -1
  133. package/dist/sandbox/glob-segment.js +28 -9
  134. package/dist/sandbox/glob-segment.js.map +1 -1
  135. package/dist/store/checkpoint/contract.d.ts +12 -0
  136. package/dist/store/checkpoint/contract.d.ts.map +1 -1
  137. package/dist/store/checkpoint/contract.js.map +1 -1
  138. package/dist/store/checkpoint/disk.d.ts.map +1 -1
  139. package/dist/store/checkpoint/disk.js +34 -2
  140. package/dist/store/checkpoint/disk.js.map +1 -1
  141. package/dist/store/checkpoint/prune.d.ts.map +1 -1
  142. package/dist/store/checkpoint/prune.js +6 -0
  143. package/dist/store/checkpoint/prune.js.map +1 -1
  144. package/dist/store/evidence/disk.js +40 -3
  145. package/dist/store/evidence/disk.js.map +1 -1
  146. package/dist/store/session-log/incremental.d.ts +23 -0
  147. package/dist/store/session-log/incremental.d.ts.map +1 -0
  148. package/dist/store/session-log/incremental.js +117 -0
  149. package/dist/store/session-log/incremental.js.map +1 -0
  150. package/dist/store/session-log/index.d.ts +1 -0
  151. package/dist/store/session-log/index.d.ts.map +1 -1
  152. package/dist/store/session-log/index.js +1 -0
  153. package/dist/store/session-log/index.js.map +1 -1
  154. package/dist/store/session-log/turn-outcome.d.ts +37 -0
  155. package/dist/store/session-log/turn-outcome.d.ts.map +1 -0
  156. package/dist/store/session-log/turn-outcome.js +77 -0
  157. package/dist/store/session-log/turn-outcome.js.map +1 -0
  158. package/dist/tools/admit-facts.d.ts.map +1 -1
  159. package/dist/tools/admit-facts.js +1 -0
  160. package/dist/tools/admit-facts.js.map +1 -1
  161. package/dist/tools/builtins/bash.d.ts.map +1 -1
  162. package/dist/tools/builtins/bash.js +11 -2
  163. package/dist/tools/builtins/bash.js.map +1 -1
  164. package/dist/tools/builtins/bounded-file-read.d.ts +19 -1
  165. package/dist/tools/builtins/bounded-file-read.d.ts.map +1 -1
  166. package/dist/tools/builtins/bounded-file-read.js +56 -3
  167. package/dist/tools/builtins/bounded-file-read.js.map +1 -1
  168. package/dist/tools/builtins/edit-apply.d.ts.map +1 -1
  169. package/dist/tools/builtins/edit-apply.js +24 -3
  170. package/dist/tools/builtins/edit-apply.js.map +1 -1
  171. package/dist/tools/builtins/edit.d.ts +31 -17
  172. package/dist/tools/builtins/edit.d.ts.map +1 -1
  173. package/dist/tools/builtins/edit.js +92 -27
  174. package/dist/tools/builtins/edit.js.map +1 -1
  175. package/dist/tools/builtins/glob.js +1 -1
  176. package/dist/tools/builtins/glob.js.map +1 -1
  177. package/dist/tools/builtins/grep.d.ts +8 -0
  178. package/dist/tools/builtins/grep.d.ts.map +1 -1
  179. package/dist/tools/builtins/grep.js +148 -21
  180. package/dist/tools/builtins/grep.js.map +1 -1
  181. package/dist/tools/builtins/notebook-guard.d.ts +19 -0
  182. package/dist/tools/builtins/notebook-guard.d.ts.map +1 -0
  183. package/dist/tools/builtins/notebook-guard.js +75 -0
  184. package/dist/tools/builtins/notebook-guard.js.map +1 -0
  185. package/dist/tools/builtins/read-render.d.ts.map +1 -1
  186. package/dist/tools/builtins/read-render.js +8 -2
  187. package/dist/tools/builtins/read-render.js.map +1 -1
  188. package/dist/tools/builtins/search-support.d.ts +8 -0
  189. package/dist/tools/builtins/search-support.d.ts.map +1 -1
  190. package/dist/tools/builtins/search-support.js +66 -0
  191. package/dist/tools/builtins/search-support.js.map +1 -1
  192. package/dist/tools/builtins/write-file.d.ts.map +1 -1
  193. package/dist/tools/builtins/write-file.js +14 -1
  194. package/dist/tools/builtins/write-file.js.map +1 -1
  195. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  196. package/dist/tools/coordinator/agent.js +25 -6
  197. package/dist/tools/coordinator/agent.js.map +1 -1
  198. package/dist/tools/coordinator/index.d.ts.map +1 -1
  199. package/dist/tools/coordinator/index.js +6 -1
  200. package/dist/tools/coordinator/index.js.map +1 -1
  201. package/dist/tools/coordinator/outcome-record.d.ts +22 -0
  202. package/dist/tools/coordinator/outcome-record.d.ts.map +1 -0
  203. package/dist/tools/coordinator/outcome-record.js +91 -0
  204. package/dist/tools/coordinator/outcome-record.js.map +1 -0
  205. package/dist/tools/defineTool.d.ts +2 -0
  206. package/dist/tools/defineTool.d.ts.map +1 -1
  207. package/dist/tools/defineTool.js +1 -0
  208. package/dist/tools/defineTool.js.map +1 -1
  209. package/dist/tools/file-read-tracker.d.ts +23 -2
  210. package/dist/tools/file-read-tracker.d.ts.map +1 -1
  211. package/dist/tools/file-read-tracker.js +37 -2
  212. package/dist/tools/file-read-tracker.js.map +1 -1
  213. package/dist/tools/untrusted-envelope.d.ts +6 -0
  214. package/dist/tools/untrusted-envelope.d.ts.map +1 -1
  215. package/dist/tools/untrusted-envelope.js +22 -1
  216. package/dist/tools/untrusted-envelope.js.map +1 -1
  217. package/dist/toolsets/manager.d.ts.map +1 -1
  218. package/dist/toolsets/manager.js +3 -4
  219. package/dist/toolsets/manager.js.map +1 -1
  220. package/dist/toolsets/validation-issues.d.ts +27 -0
  221. package/dist/toolsets/validation-issues.d.ts.map +1 -0
  222. package/dist/toolsets/validation-issues.js +83 -0
  223. package/dist/toolsets/validation-issues.js.map +1 -0
  224. package/dist/turn/memory-recall.d.ts +13 -0
  225. package/dist/turn/memory-recall.d.ts.map +1 -1
  226. package/dist/turn/memory-recall.js +18 -0
  227. package/dist/turn/memory-recall.js.map +1 -1
  228. package/dist/types/agent/base.d.ts +19 -0
  229. package/dist/types/agent/base.d.ts.map +1 -1
  230. package/dist/types/agent/query.d.ts +2 -0
  231. package/dist/types/agent/query.d.ts.map +1 -1
  232. package/dist/types/common/index.d.ts +17 -0
  233. package/dist/types/common/index.d.ts.map +1 -1
  234. package/dist/types/common/index.js +8 -1
  235. package/dist/types/common/index.js.map +1 -1
  236. package/dist/types/hitl/index.d.ts +22 -0
  237. package/dist/types/hitl/index.d.ts.map +1 -1
  238. package/dist/types/hitl/index.js.map +1 -1
  239. package/dist/types/session/config.d.ts +20 -0
  240. package/dist/types/session/config.d.ts.map +1 -1
  241. package/dist/types/session/events.d.ts +3 -3
  242. package/dist/types/session/events.d.ts.map +1 -1
  243. package/dist/types/session/index.d.ts +1 -0
  244. package/dist/types/session/index.d.ts.map +1 -1
  245. package/dist/types/session/index.js +1 -0
  246. package/dist/types/session/index.js.map +1 -1
  247. package/dist/types/session/outcome.d.ts +95 -0
  248. package/dist/types/session/outcome.d.ts.map +1 -0
  249. package/dist/types/session/outcome.js +45 -0
  250. package/dist/types/session/outcome.js.map +1 -0
  251. package/dist/types/session/prepare-step.d.ts +2 -0
  252. package/dist/types/session/prepare-step.d.ts.map +1 -1
  253. package/dist/types/session/records.d.ts +167 -2
  254. package/dist/types/session/records.d.ts.map +1 -1
  255. package/dist/types/session/records.js +42 -0
  256. package/dist/types/session/records.js.map +1 -1
  257. package/dist/types/session/turn.d.ts +18 -0
  258. package/dist/types/session/turn.d.ts.map +1 -1
  259. package/dist/types/session/turn.js.map +1 -1
  260. package/dist/types/tool/index.d.ts +30 -0
  261. package/dist/types/tool/index.d.ts.map +1 -1
  262. package/dist/types/tool/index.js.map +1 -1
  263. package/dist/types/tool/presentation.d.ts +6 -0
  264. package/dist/types/tool/presentation.d.ts.map +1 -1
  265. package/package.json +1 -1
  266. package/src/agents/QueryAgent.ts +5 -0
  267. package/src/agents/examples/SupervisorAgent.ts +1 -0
  268. package/src/agents/explore.ts +2 -1
  269. package/src/compaction/index.ts +1 -0
  270. package/src/compaction/model-summary.ts +2 -2
  271. package/src/compaction/summary.ts +30 -2
  272. package/src/config/runtime.ts +15 -0
  273. package/src/execution/workflow/runtime.ts +4 -0
  274. package/src/execution/workflow/types.ts +9 -0
  275. package/src/manager/agent/child-session.ts +36 -0
  276. package/src/manager/agent/lifecycle.ts +9 -0
  277. package/src/manager/session/turn-recorder.ts +115 -30
  278. package/src/pricing/rates.source.json +1 -1
  279. package/src/prompt/coding-agent-doctrine.ts +5 -3
  280. package/src/provider/fallback.ts +6 -1
  281. package/src/provider/retry.ts +51 -0
  282. package/src/public-runtime.ts +10 -1
  283. package/src/public-tools.ts +6 -1
  284. package/src/runtime/query/auto-review/classifier.ts +21 -5
  285. package/src/runtime/query/auto-review/prompt.ts +2 -0
  286. package/src/runtime/query/auto-review/script-evidence.ts +618 -0
  287. package/src/runtime/query/checkpoint.ts +53 -1
  288. package/src/runtime/query/context.ts +33 -6
  289. package/src/runtime/query/executor.ts +183 -21
  290. package/src/runtime/query/finalize-turn.ts +8 -1
  291. package/src/runtime/query/index.ts +19 -0
  292. package/src/runtime/query/iteration/index.ts +167 -14
  293. package/src/runtime/query/iteration/phases/compaction.ts +53 -3
  294. package/src/runtime/query/iteration/phases/context.ts +6 -0
  295. package/src/runtime/query/iteration/stream-turn.ts +3 -3
  296. package/src/runtime/query/prelude-lease.ts +19 -0
  297. package/src/runtime/query/prepare-turn.ts +29 -12
  298. package/src/runtime/query/review-policy.ts +33 -0
  299. package/src/runtime/query/session-storage.ts +42 -3
  300. package/src/runtime/query/tooling.ts +3 -0
  301. package/src/runtime/query/turn/completion-audit.ts +76 -5
  302. package/src/runtime/query/turn/outcome-ledger.ts +633 -0
  303. package/src/sandbox/file-walk.ts +41 -4
  304. package/src/sandbox/glob-segment.ts +48 -24
  305. package/src/store/checkpoint/contract.ts +15 -0
  306. package/src/store/checkpoint/disk.ts +34 -2
  307. package/src/store/checkpoint/prune.ts +13 -0
  308. package/src/store/evidence/disk.ts +57 -2
  309. package/src/store/session-log/incremental.ts +147 -0
  310. package/src/store/session-log/index.ts +7 -0
  311. package/src/store/session-log/turn-outcome.ts +84 -0
  312. package/src/tools/admit-facts.ts +1 -0
  313. package/src/tools/builtins/bash.ts +12 -2
  314. package/src/tools/builtins/bounded-file-read.ts +59 -2
  315. package/src/tools/builtins/edit-apply.ts +26 -4
  316. package/src/tools/builtins/edit.ts +106 -41
  317. package/src/tools/builtins/glob.ts +1 -1
  318. package/src/tools/builtins/grep.ts +173 -27
  319. package/src/tools/builtins/notebook-guard.ts +76 -0
  320. package/src/tools/builtins/read-render.ts +12 -2
  321. package/src/tools/builtins/search-support.ts +62 -0
  322. package/src/tools/builtins/write-file.ts +12 -1
  323. package/src/tools/coordinator/agent.ts +31 -7
  324. package/src/tools/coordinator/index.ts +6 -1
  325. package/src/tools/coordinator/outcome-record.ts +93 -0
  326. package/src/tools/defineTool.ts +3 -0
  327. package/src/tools/file-read-tracker.ts +51 -2
  328. package/src/tools/untrusted-envelope.ts +23 -1
  329. package/src/toolsets/manager.ts +6 -10
  330. package/src/toolsets/validation-issues.ts +97 -0
  331. package/src/turn/memory-recall.ts +28 -0
  332. package/src/types/agent/base.ts +20 -0
  333. package/src/types/agent/query.ts +2 -0
  334. package/src/types/common/index.ts +25 -1
  335. package/src/types/hitl/index.ts +27 -0
  336. package/src/types/session/config.ts +20 -0
  337. package/src/types/session/events.ts +3 -3
  338. package/src/types/session/index.ts +1 -0
  339. package/src/types/session/outcome.ts +139 -0
  340. package/src/types/session/prepare-step.ts +2 -0
  341. package/src/types/session/records.ts +67 -1
  342. package/src/types/session/turn.ts +19 -0
  343. package/src/types/tool/index.ts +30 -0
  344. package/src/types/tool/presentation.ts +6 -0
@@ -0,0 +1,93 @@
1
+ import type { TurnOutcome, TurnOutcomeCheck } from '../../types/session/outcome.js'
2
+ import { KERNEL_RECORD_PREFIX } from '../untrusted-envelope.js'
3
+
4
+ /**
5
+ * The kernel's own sentence about what a delegated run did, for the parent
6
+ * that delegated it.
7
+ *
8
+ * It states the record, not the child's account of itself, and it sits outside
9
+ * the marked block that holds the child's words: the parent reads it as a fact
10
+ * of the kernel's, the way it reads the task id. What it can say is exactly
11
+ * what the record can say. `checked` means a recognised check passed and the
12
+ * workspace did not change after it; it does not mean the check covers the
13
+ * change.
14
+ *
15
+ * A run that changed nothing and failed no check gets no line.
16
+ *
17
+ * The command is the child's own text, so it is shown as plain data: one line
18
+ * of printable ASCII with no quotes or backticks, cut short. A command that
19
+ * does not survive that is named as "a command" instead.
20
+ */
21
+ export function describeOutcomeRecord(outcome: TurnOutcome): string | undefined {
22
+ const body = describe(outcome)
23
+ return body === undefined ? undefined : `${KERNEL_RECORD_PREFIX}${body}`
24
+ }
25
+
26
+ const SHOWN_COMMAND_CHARS = 60
27
+
28
+ /** A command as data inside backticks, or `undefined` when it cannot be shown without being rewritten. */
29
+ export function showCommand(command: string): string | undefined {
30
+ const trimmed = command.trim()
31
+ if (trimmed === '' || trimmed.length > SHOWN_COMMAND_CHARS) return undefined
32
+ // Printable ASCII, and none of the characters that open a quotation or a
33
+ // substitution in whatever reads this next.
34
+ if (!/^[\x20-\x7e]+$/.test(trimmed) || /[`'"$\\#]/.test(trimmed)) return undefined
35
+ return `\`${trimmed}\``
36
+ }
37
+
38
+ function named(check: TurnOutcomeCheck): string {
39
+ return showCommand(check.command) ?? 'a command'
40
+ }
41
+
42
+ function changedPhrase(outcome: TurnOutcome): string | undefined {
43
+ const { total, outsideKnownTools } = outcome.changes
44
+ if (total > 0) return total === 1 ? '1 file changed' : `${total} files changed`
45
+ if (outsideKnownTools) return 'files changed, by something other than the edit tools'
46
+ return undefined
47
+ }
48
+
49
+ function describe(outcome: TurnOutcome): string | undefined {
50
+ const changed = changedPhrase(outcome)
51
+ const failed = outcome.checks.filter((check) => check.result === 'failed')
52
+ const lead = changed === undefined ? '' : `${changed}; `
53
+
54
+ switch (outcome.verdict) {
55
+ case 'unchanged':
56
+ // Nothing to say, and a line on every delegation that only read would
57
+ // be noise the parent learns to skip. The record is still in `data`.
58
+ return undefined
59
+ case 'unknown':
60
+ return 'a command ran that may have changed files, and nothing here could tell whether it did.'
61
+ case 'unchecked':
62
+ return `${lead}nothing was run to check ${total(outcome) === 1 ? 'it' : 'them'}.`
63
+ case 'failing': {
64
+ const first = failed[0]
65
+ if (first === undefined) return undefined
66
+ const more = failed.length > 1 ? ` (and ${failed.length - 1} more)` : ''
67
+ return `${lead}${named(first)} failed${more}.`
68
+ }
69
+ case 'checked': {
70
+ const passed = [...outcome.checks].reverse().find((c) => c.result === 'passed' && c.fresh)
71
+ return `${lead}${passed ? named(passed) : 'a check'} passed and nothing changed after it.`
72
+ }
73
+ case 'stale': {
74
+ const passed = [...outcome.checks].reverse().find((c) => c.result === 'passed')
75
+ return `${lead}${passed ? named(passed) : 'a check'} passed, then files changed again.`
76
+ }
77
+ case 'inconclusive': {
78
+ const masked = outcome.checks.find((c) => c.result === 'masked')
79
+ if (masked) {
80
+ const how = /(?<!\|)\|(?!\|)/.test(masked.command) ? 'the pipe' : 'the command line'
81
+ return `${lead}${named(masked)} ran, but ${how} hides whether it passed.`
82
+ }
83
+ const unsettled = outcome.checks.find((c) => c.result === 'unsettled')
84
+ if (unsettled) return `${lead}${named(unsettled)} ran, but its result was not seen.`
85
+ const passed = outcome.checks.find((c) => c.result === 'passed')
86
+ return `${lead}${passed ? named(passed) : 'a check'} passed, but it cannot be told whether files changed after it.`
87
+ }
88
+ }
89
+ }
90
+
91
+ function total(outcome: TurnOutcome): number {
92
+ return outcome.changes.total
93
+ }
@@ -41,6 +41,8 @@ export interface DefineToolOptions<S extends z.ZodType> {
41
41
  executionBarrier?: boolean
42
42
  /** The tool bounds its own output; see {@link ToolDefinition.selfLimitingOutput}. */
43
43
  selfLimitingOutput?: boolean
44
+ /** A tighter per-result cap; see {@link ToolDefinition.maxOutputChars}. */
45
+ maxOutputChars?: number
44
46
  tier?: string
45
47
  /**
46
48
  * How this tool's call and result should be shown; see
@@ -163,6 +165,7 @@ export function defineTool<S extends z.ZodType>(
163
165
  ...(options.selfLimitingOutput !== undefined
164
166
  ? { selfLimitingOutput: options.selfLimitingOutput }
165
167
  : {}),
168
+ ...(options.maxOutputChars !== undefined ? { maxOutputChars: options.maxOutputChars } : {}),
166
169
  category: options.category,
167
170
  permissions: options.permissions,
168
171
  isReadOnly:
@@ -4,9 +4,48 @@ import { fingerprintContent } from './builtins/content-fingerprint.js'
4
4
  /** A body larger than this is not kept; the file's change is still seen by fingerprint. */
5
5
  const MAX_BODY_CHARS = 512 * 1024
6
6
  const MAX_BODIES = 128
7
+ const MAX_AUTHORED = 1024
7
8
 
8
- /** Host-owned observation ledger. Share only across turns of the same conversation and filesystem. */
9
- export function createFileReadTracker(): FileReadTracker {
9
+ /**
10
+ * What a family of agents (a parent and every descendant) wrote through the
11
+ * write and edit tools: path to the fingerprint of the content last put there,
12
+ * and when the ledger began. Shared by the whole family, because the approval
13
+ * classifier asks "did one of us write this script?". It carries no read
14
+ * state, so sharing it can never stand in for one agent's read of a file.
15
+ */
16
+ export interface AuthoredLedger {
17
+ readonly startedAt: number
18
+ record(key: string, content: string): void
19
+ fingerprint(key: string): string | undefined
20
+ }
21
+
22
+ export function createAuthoredLedger(): AuthoredLedger {
23
+ const authored = new Map<string, string>()
24
+ return {
25
+ startedAt: Date.now(),
26
+ record(key, content) {
27
+ authored.delete(key)
28
+ authored.set(key, fingerprintContent(content))
29
+ while (authored.size > MAX_AUTHORED) {
30
+ const oldest = authored.keys().next().value
31
+ if (oldest === undefined) break
32
+ authored.delete(oldest)
33
+ }
34
+ },
35
+ fingerprint: (key) => authored.get(key),
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Host-owned observation ledger. Share only across turns of the same
41
+ * conversation and filesystem, never between agents: what a ledger holds is
42
+ * what ITS reader has read, and the stale-write refusal compares the disk to
43
+ * that. A child gets `tracker.forChild()`: its own reader record, the family's
44
+ * authored ledger.
45
+ */
46
+ export function createFileReadTracker(options?: {
47
+ readonly authored?: AuthoredLedger
48
+ }): FileReadTracker {
10
49
  const paths = new Set<string>()
11
50
  const fingerprints = new Map<string, string>()
12
51
  const writes = new Map<string, string>()
@@ -36,6 +75,11 @@ export function createFileReadTracker(): FileReadTracker {
36
75
  // than the cap keeps no body (its change is still detectable by
37
76
  // fingerprint), and the oldest bodies go first.
38
77
  const bodies = new Map<string, string>()
78
+ // What the agent family wrote, by fingerprint. Not a body: a classifier
79
+ // reads the file as it is, and uses this only to say whether it is still
80
+ // what an agent wrote. Shared with children; everything else here is not.
81
+ const authored = options?.authored ?? createAuthoredLedger()
82
+ const keepAuthored = (key: string, content: string): void => authored.record(key, content)
39
83
  function keepBody(key: string, content: string | undefined): void {
40
84
  bodies.delete(key)
41
85
  if (content === undefined || content.length > MAX_BODY_CHARS) return
@@ -63,6 +107,7 @@ export function createFileReadTracker(): FileReadTracker {
63
107
  // A write supplies the whole body; any other observation without a
64
108
  // witness may be a window of the file, which proves nothing about the rest.
65
109
  keepBody(key, fullWriteCallId ? content : undefined)
110
+ if (content !== undefined && fullWriteCallId) keepAuthored(key, content)
66
111
  if (next !== undefined && fullWriteCallId) {
67
112
  writes.set(key, fullWriteCallId)
68
113
  // A full body arrived whole in one call, so the chain is empty
@@ -76,6 +121,9 @@ export function createFileReadTracker(): FileReadTracker {
76
121
  const MAX_WINDOWS_PER_FILE = 6
77
122
  const MAX_WINDOWED_FILES = 512
78
123
  return {
124
+ startedAt: authored.startedAt,
125
+ authoredFingerprint: (key) => authored.fingerprint(key),
126
+ forChild: () => createFileReadTracker({ authored }),
79
127
  recordReadWindow(key, window, entry) {
80
128
  let forFile = windows.get(key)
81
129
  if (forFile) windows.delete(key)
@@ -127,6 +175,7 @@ export function createFileReadTracker(): FileReadTracker {
127
175
  reads.delete(key)
128
176
  fingerprints.set(key, fingerprintContent(content))
129
177
  keepBody(key, content)
178
+ keepAuthored(key, content)
130
179
  if (!chained) {
131
180
  writes.delete(key)
132
181
  chains.delete(key)
@@ -205,6 +205,28 @@ export function wrapUntrusted(
205
205
  ].join('\n')
206
206
  }
207
207
 
208
+ /**
209
+ * The opening words of the one line the kernel writes beside a delegated
210
+ * agent's answer, outside its block, to say what the run did according to its
211
+ * own record (`describeOutcomeRecord`).
212
+ */
213
+ export const KERNEL_RECORD_PREFIX = 'Record of this run: '
214
+
215
+ /**
216
+ * `text` without a kernel record line that follows its last block.
217
+ *
218
+ * A reader of the block must still find it when the kernel has said one more
219
+ * thing after it. The line is the kernel's: everything a child produced is
220
+ * inside the block, where this module has already made its delimiter
221
+ * impossible to forge, so a line out here asks the reader to trust nothing
222
+ * that it did not already trust.
223
+ */
224
+ function withoutKernelRecord(trimmed: string): string {
225
+ const at = trimmed.lastIndexOf(`\n\n${KERNEL_RECORD_PREFIX}`)
226
+ if (at < 0 || trimmed.indexOf('\n', at + 2) >= 0) return trimmed
227
+ return trimmed.slice(0, at)
228
+ }
229
+
208
230
  /**
209
231
  * The body of a single wrapped block, when `text` is one.
210
232
  *
@@ -246,7 +268,7 @@ export function wrapUntrusted(
246
268
  * it, which is the one failure this function must not have.
247
269
  */
248
270
  export function untrustedEnvelopeBody(text: string): string | undefined {
249
- const trimmed = text.trim()
271
+ const trimmed = withoutKernelRecord(text.trim())
250
272
  const opening = OPENING_TAG_PATTERN.exec(trimmed)
251
273
  if (!opening) return undefined
252
274
  const nonce = opening[1]
@@ -35,6 +35,7 @@ import { type Logger, resolveLogger } from '../utils/logger.js'
35
35
  import { ToolsetConflictError } from './combine.js'
36
36
  import type { ToolSourceRef, Toolset, ToolsetAvailability } from './types.js'
37
37
  import { toToolSourceRef, toolsetIsReady } from './types.js'
38
+ import { type ValidationIssueLike, describeValidationIssues } from './validation-issues.js'
38
39
 
39
40
  export type { ToolExecutionResult }
40
41
 
@@ -984,16 +985,9 @@ Executable tool names, descriptions, and JSON input schemas are attached through
984
985
  private validationFailure(
985
986
  tool: ToolDefinition,
986
987
  rawInput: unknown,
987
- error: {
988
- readonly issues: readonly {
989
- readonly path: readonly PropertyKey[]
990
- readonly message: string
991
- }[]
992
- },
988
+ error: { readonly issues: readonly ValidationIssueLike[] },
993
989
  ): ToolExecutionResult {
994
- const errorMessage = error.issues
995
- .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
996
- .join('; ')
990
+ const errorMessage = describeValidationIssues(error.issues)
997
991
  const isEmptyInput =
998
992
  rawInput === null ||
999
993
  rawInput === undefined ||
@@ -1006,7 +1000,9 @@ Executable tool names, descriptions, and JSON input schemas are attached through
1006
1000
  : ''
1007
1001
  const enrichedMessage = isEmptyInput
1008
1002
  ? `Tool "${tool.name}" was called with no arguments. ${requiredHint}${recoveryHint} Retry the call with the required parameters populated.`
1009
- : `Validation failed for "${tool.name}": ${errorMessage}. ${requiredHint}${recoveryHint}`
1003
+ : `Validation failed for "${tool.name}": ${errorMessage}${
1004
+ errorMessage.includes('\n') ? '\n' : '. '
1005
+ }${requiredHint}${recoveryHint}`
1010
1006
  this.log.error('Tool input validation failed', {
1011
1007
  'namzu.tool.name': tool.name,
1012
1008
  'namzu.registry.errors': errorMessage,
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Tool-input validation issues as sentences a model can act on.
3
+ *
4
+ * A zod message is written for a developer reading a stack trace:
5
+ * `edits: Too small: expected array to have >=1 items` names neither what to
6
+ * send nor why. A small model that reads it tends to repeat the same call. Each
7
+ * kind of issue a flat tool schema commonly produces is rewritten here to name
8
+ * the field and the fix; anything not recognised keeps its own message, so
9
+ * nothing is lost by this being a short list.
10
+ */
11
+
12
+ export interface ValidationIssueLike {
13
+ readonly path: readonly PropertyKey[]
14
+ readonly message: string
15
+ readonly code?: string
16
+ readonly origin?: string
17
+ readonly minimum?: number | bigint
18
+ readonly maximum?: number | bigint
19
+ readonly expected?: string
20
+ readonly keys?: readonly string[]
21
+ readonly values?: readonly unknown[]
22
+ }
23
+
24
+ /** `edits.0.old_string` as `edits[0].old_string`, the way a model wrote it. */
25
+ export function renderIssuePath(path: readonly PropertyKey[]): string {
26
+ let out = ''
27
+ for (const part of path) {
28
+ if (typeof part === 'number') out += `[${part}]`
29
+ else out += out === '' ? String(part) : `.${String(part)}`
30
+ }
31
+ return out
32
+ }
33
+
34
+ function plural(n: number | bigint, noun: string): string {
35
+ return `${n} ${noun}${n === 1 || n === 1n ? '' : 's'}`
36
+ }
37
+
38
+ function received(message: string): string | undefined {
39
+ return /received (\w+)$/.exec(message)?.[1]
40
+ }
41
+
42
+ /** One issue as one sentence, without a trailing period. */
43
+ export function describeValidationIssue(issue: ValidationIssueLike): string {
44
+ const field = renderIssuePath(issue.path)
45
+ const name = field === '' ? 'The input' : `"${field}"`
46
+ const min = issue.minimum
47
+ switch (issue.code) {
48
+ case 'too_small':
49
+ if (issue.origin === 'string' && min !== undefined && Number(min) <= 1) {
50
+ return `${name} must not be empty; send the text itself`
51
+ }
52
+ if (issue.origin === 'string' && min !== undefined) {
53
+ return `${name} is too short; it needs at least ${plural(min, 'character')}`
54
+ }
55
+ if (issue.origin === 'array' && min !== undefined) {
56
+ return `${name} is a list with too few entries; it needs at least ${plural(min, 'entry').replace('entrys', 'entries')}, or leave the field out`
57
+ }
58
+ if (min !== undefined) return `${name} is too small; the smallest accepted is ${min}`
59
+ break
60
+ case 'too_big':
61
+ if (issue.maximum !== undefined) {
62
+ return `${name} is too large; the largest accepted is ${issue.maximum}`
63
+ }
64
+ break
65
+ case 'invalid_type': {
66
+ const got = received(issue.message)
67
+ if (issue.expected !== undefined && got === 'undefined') {
68
+ return `${name} is missing; send it as ${withArticle(issue.expected)}`
69
+ }
70
+ if (issue.expected !== undefined && got !== undefined) {
71
+ return `${name} must be ${withArticle(issue.expected)}, not ${got === 'null' ? 'null' : `a ${got}`}`
72
+ }
73
+ break
74
+ }
75
+ case 'unrecognized_keys':
76
+ if (issue.keys !== undefined && issue.keys.length > 0) {
77
+ const list = issue.keys.map((key) => `"${key}"`).join(', ')
78
+ return `${field === '' ? '' : `${name} has `}${issue.keys.length === 1 ? 'a field' : 'fields'} this tool does not take: ${list}. Remove ${issue.keys.length === 1 ? 'it' : 'them'}`
79
+ }
80
+ break
81
+ case 'invalid_value':
82
+ if (issue.values !== undefined && issue.values.length > 0) {
83
+ return `${name} must be one of ${issue.values.map((v) => JSON.stringify(v)).join(', ')}`
84
+ }
85
+ break
86
+ }
87
+ const message = issue.message.replace(/\.+$/, '')
88
+ return field === '' ? message : `${field}: ${message}`
89
+ }
90
+
91
+ function withArticle(type: string): string {
92
+ return /^[aeiou]/.test(type) ? `an ${type}` : `a ${type}`
93
+ }
94
+
95
+ export function describeValidationIssues(issues: readonly ValidationIssueLike[]): string {
96
+ return issues.map(describeValidationIssue).join('; ')
97
+ }
@@ -28,6 +28,19 @@ export interface MemoryRecallOptions {
28
28
  * aged memory names before relying on it. Default one day.
29
29
  */
30
30
  readonly ageNoticeAfterMs?: number
31
+ /**
32
+ * Asked once for each record about to be shown (at most `maxMemories` a
33
+ * request), with its id and metadata, never its body. A returned string is
34
+ * added to the record's line as `caution` and to the event's entry, so the
35
+ * model reads that the record may no longer hold; the record is still
36
+ * shown. A host uses it to check what a record names (a file, say) against
37
+ * the project as it is now. It runs inside the recall deadline, so it must
38
+ * be cheap; a throw, or a deadline passing, means no caution.
39
+ */
40
+ readonly caution?: (record: {
41
+ readonly id: string
42
+ readonly metadata?: Readonly<Record<string, unknown>>
43
+ }) => string | undefined | Promise<string | undefined>
31
44
  /** Clock for ages; defaults to `Date.now`. */
32
45
  readonly now?: () => number
33
46
  /**
@@ -593,6 +606,19 @@ export function createMemoryRecallStep(options: MemoryRecallOptions): PrepareSte
593
606
  )
594
607
  )
595
608
  continue
609
+ let caution: string | undefined
610
+ if (options.caution) {
611
+ try {
612
+ caution = await options.caution({
613
+ id: entry.id,
614
+ ...(full.metadata ? { metadata: full.metadata } : {}),
615
+ })
616
+ } catch {
617
+ // A check that failed says nothing about the record.
618
+ }
619
+ if (expired) break
620
+ if (caution !== undefined) caution = clipped(caution, 200)
621
+ }
596
622
  const aged = now() - entry.updatedAt > ageNoticeAfterMs
597
623
  // The first aged record pays for the notice it brings with it, so
598
624
  // a block of fresh records spends nothing on it.
@@ -614,6 +640,7 @@ export function createMemoryRecallStep(options: MemoryRecallOptions): PrepareSte
614
640
  ),
615
641
  updatedAt: entry.updatedAt,
616
642
  ...(aged ? { age: describeMemoryAge(entry.updatedAt, now()) } : {}),
643
+ ...(caution ? { caution } : {}),
617
644
  sourceTurn:
618
645
  typeof full.metadata?.turnId === 'string'
619
646
  ? clipped(full.metadata.turnId, 80)
@@ -632,6 +659,7 @@ export function createMemoryRecallStep(options: MemoryRecallOptions): PrepareSte
632
659
  title: entry.title,
633
660
  origin: memoryOrigin(full.metadata),
634
661
  updatedAt: entry.updatedAt,
662
+ ...(caution ? { caution } : {}),
635
663
  // Without a selector the words chose it and there is nothing to add;
636
664
  // with one, say so when it did not.
637
665
  ...(reasons.has(entry.id) || options.select
@@ -160,6 +160,20 @@ export interface BaseAgentConfig {
160
160
  */
161
161
  toolResultGuardrails?: readonly import('../guardrail/index.js').ToolResultGuardrailSpec[]
162
162
 
163
+ /**
164
+ * This agent's own file observation ledger.
165
+ *
166
+ * A delegated child is a fresh turn with its own executor. It starts with
167
+ * its own read record (`FileReadTracker.forChild`), so a write by its
168
+ * parent or a sibling still makes what it read stale, but it shares the
169
+ * delegating turn's record of what the agents wrote: a script its parent
170
+ * wrote, run by the child, is the session's own, so the auto-mode
171
+ * classifier reads it. Stamped by `AgentManager` from
172
+ * `configOverrides.fileReadTracker`, which the delegation tools fill from
173
+ * the turn's own `ToolContext`.
174
+ */
175
+ fileReadTracker?: import('../tool/index.js').FileReadTracker
176
+
163
177
  /**
164
178
  * Tools this turn may NOT use, subtracted from whatever it would
165
179
  * otherwise have.
@@ -232,6 +246,10 @@ export interface BaseAgentConfig {
232
246
  reasoning?: import('../session/config.js').TurnConfig['reasoning']
233
247
  /** Ask once for a check of edits before a turn ends; see {@link import('../session/config.js').TurnConfig.completionAudit}. */
234
248
  completionAudit?: boolean
249
+ /** Consecutive end-of-turn hook refusals honoured; see {@link import('../session/config.js').TurnConfig.maxHookRefusals}. */
250
+ maxHookRefusals?: number
251
+ /** The outcome record's hooks; see {@link import('../session/config.js').TurnConfig.outcome}. */
252
+ outcome?: import('../session/config.js').TurnConfig['outcome']
235
253
 
236
254
  /**
237
255
  * Deduplicate a retried invocation instead of running it twice.
@@ -411,6 +429,8 @@ export interface BaseAgentResult {
411
429
  durationMs: number
412
430
  messages: Message[]
413
431
  result?: string
432
+ /** What the turn changed and what checked it; see {@link import('../session/turn.js').Turn.outcome}. */
433
+ outcome?: import('../session/outcome.js').TurnOutcome
414
434
  /**
415
435
  * The schema-validated answer, when the turn was configured to produce one.
416
436
  *
@@ -67,6 +67,8 @@ export interface QueryAgentConfig extends BaseAgentConfig {
67
67
  sandboxTeardownTimeoutMs?: number
68
68
  /** See {@link QueryParams.outsideRootAccess}. Default `'refuse'`. */
69
69
  outsideRootAccess?: 'refuse' | 'review'
70
+ /** See {@link QueryParams.outsideRootRefusal}. */
71
+ outsideRootRefusal?: string
70
72
  /** See {@link QueryParams.sandboxEscape}. Default `'refuse'`. */
71
73
  sandboxEscape?: 'refuse' | 'review'
72
74
 
@@ -12,6 +12,22 @@ export interface TokenUsage {
12
12
  cachedTokens: number
13
13
  cacheWriteTokens: number
14
14
 
15
+ /**
16
+ * The whole prompt this call processed, cache reads and cache writes
17
+ * included: the size of the context the model was given.
18
+ *
19
+ * Drivers disagree on what `promptTokens` counts. Some include cached
20
+ * tokens, others report only the uncached remainder,
21
+ * which on a warm cache is a handful of tokens against a context of tens
22
+ * of thousands. Context size, the compaction trigger and the context meter
23
+ * need the whole prompt, so a driver that excludes cache from
24
+ * `promptTokens` reports it here. Absent means `promptTokens` already is
25
+ * the whole prompt; read it through {@link promptSizeOf}.
26
+ *
27
+ * A per-call fact like {@link TokenUsage.cacheTtlMs}: never summed.
28
+ */
29
+ fullPromptTokens?: number
30
+
15
31
  /**
16
32
  * Output tokens the model spent on internal reasoning.
17
33
  *
@@ -88,6 +104,11 @@ export interface CacheMiss {
88
104
  missedTokens?: number
89
105
  }
90
106
 
107
+ /** The whole prompt one call processed: {@link TokenUsage.fullPromptTokens} when the driver reports it, else `promptTokens`. */
108
+ export function promptSizeOf(usage: TokenUsage): number {
109
+ return usage.fullPromptTokens ?? usage.promptTokens
110
+ }
111
+
91
112
  export function accumulateTokenUsage(current: TokenUsage, addition: TokenUsage): TokenUsage {
92
113
  return {
93
114
  promptTokens: current.promptTokens + addition.promptTokens,
@@ -105,7 +126,7 @@ export function accumulateTokenUsage(current: TokenUsage, addition: TokenUsage):
105
126
  ...(current.webSearchRequests !== undefined || addition.webSearchRequests !== undefined
106
127
  ? { webSearchRequests: (current.webSearchRequests ?? 0) + (addition.webSearchRequests ?? 0) }
107
128
  : {}),
108
- // `cacheTtlMs`, `cacheWriteTtlTokens`, `speed`, `serviceTier`,
129
+ // `fullPromptTokens`, `cacheTtlMs`, `cacheWriteTtlTokens`, `speed`, `serviceTier`,
109
130
  // `inferenceGeo` and `cacheMiss` are per-call facts and are deliberately
110
131
  // not carried into a sum.
111
132
  }
@@ -139,6 +160,9 @@ export function mergeTokenUsage(current: TokenUsage, next: TokenUsage): TokenUsa
139
160
  totalTokens: Math.max(current.totalTokens, next.totalTokens, promptTokens + completionTokens),
140
161
  cachedTokens: Math.max(current.cachedTokens, next.cachedTokens),
141
162
  cacheWriteTokens: Math.max(current.cacheWriteTokens, next.cacheWriteTokens),
163
+ ...(current.fullPromptTokens !== undefined || next.fullPromptTokens !== undefined
164
+ ? { fullPromptTokens: Math.max(current.fullPromptTokens ?? 0, next.fullPromptTokens ?? 0) }
165
+ : {}),
142
166
  ...(current.reasoningTokens !== undefined || next.reasoningTokens !== undefined
143
167
  ? { reasoningTokens: Math.max(current.reasoningTokens ?? 0, next.reasoningTokens ?? 0) }
144
168
  : {}),
@@ -1,6 +1,7 @@
1
1
  import type { CostInfo, TokenUsage } from '../common/index.js'
2
2
  import type { CheckpointId, PlanId, SessionId, TurnId } from '../ids/index.js'
3
3
  import type { Message } from '../message/index.js'
4
+ import type { ShellDialect } from '../tool/index.js'
4
5
 
5
6
  export type { CheckpointId }
6
7
 
@@ -179,6 +180,32 @@ export interface ResumeContext {
179
180
  readonly transcript: () => readonly Message[]
180
181
  /** The turn's cancellation signal, when it has one. */
181
182
  readonly signal?: AbortSignal
183
+ /**
184
+ * The script files a shell line launches that the agent wrote, or that came
185
+ * into being during the session, read now. Absent when the host keeps no
186
+ * file ledger. `callId` names the call under review, so a change to a file
187
+ * between this read and the run can be noticed when it runs.
188
+ */
189
+ readonly launchedScripts?: (
190
+ callId: string,
191
+ line: string,
192
+ dialect: ShellDialect,
193
+ ) => Promise<readonly LaunchedScript[]>
194
+ }
195
+
196
+ /** A script file a reviewed shell line launches, as read for its reviewer. */
197
+ export interface LaunchedScript {
198
+ readonly path: string
199
+ /** `authored`: written through the write or edit tools. `unknown`: it appeared during the session some other way. */
200
+ readonly origin: 'authored' | 'unknown'
201
+ /** `ok`: shown as read. `changed`: no longer what the agent wrote. `unreadable`: the file could not be read. */
202
+ readonly state: 'ok' | 'changed' | 'unreadable'
203
+ /** One plain sentence saying what the reviewer is looking at. */
204
+ readonly note: string
205
+ /** The text shown: the whole file, or its head and tail with the cut marked. */
206
+ readonly content?: string
207
+ /** Characters of the file left out of `content`. */
208
+ readonly cut?: number
182
209
  }
183
210
 
184
211
  /**
@@ -106,6 +106,26 @@ export interface TurnConfig {
106
106
  * Off when absent, so an embedding host sees no change.
107
107
  */
108
108
  completionAudit?: boolean
109
+ /**
110
+ * How many times in a row `turn_end` / `subagent_stop` hooks may refuse to
111
+ * let the turn end (answer `skip`) before the turn stops honouring them. At
112
+ * the limit the turn ends, one notice naming the hook is recorded, and the
113
+ * remaining refusals are ignored. A tool call between refusals resets the
114
+ * count. Off when absent or 0, so an embedding host sees no change; a
115
+ * positive integer sets the limit.
116
+ */
117
+ maxHookRefusals?: number
118
+ /**
119
+ * The turn's outcome record: what changed, what was run to check it, and
120
+ * whether the last word of each check was said about the workspace as the
121
+ * turn left it (`Turn.outcome`).
122
+ *
123
+ * The record is kept whether or not this is set, from the kernel's own write
124
+ * tools alone. A host that names `detectChanges` gets the workspace itself
125
+ * compared at the start, after each check and at the end, so a change made
126
+ * by a shell command is seen. It never blocks or prolongs a turn.
127
+ */
128
+ outcome?: import('./outcome.js').TurnOutcomeConfig
109
129
  /** Provider-hosted search for this turn. Explicit opt-in; no local network permission. */
110
130
  webSearch?: import('../provider/index.js').ChatCompletionParams['webSearch']
111
131
  /** Cumulative parent-and-descendant tokens; 0 is unlimited with usage accounting. */
@@ -24,7 +24,7 @@ import type { FencingToken } from './durable.js'
24
24
  import type { Lineage } from './lineage.js'
25
25
  import type { MessageStopReason, StopReason } from './stop-reason.js'
26
26
  import type { SubSessionKind } from './sub-session.js'
27
- import type { Origin, TurnBudgetBinding, TurnConfigSnapshot, TurnSettlement } from './turn.js'
27
+ import type { LiveTurnSettlement, Origin, TurnBudgetBinding, TurnConfigSnapshot } from './turn.js'
28
28
 
29
29
  /**
30
30
  * Live events of a session: the stream a host listens to while a turn runs.
@@ -809,7 +809,7 @@ type CoreSessionEvent =
809
809
  * value where an honest gap belongs.
810
810
  */
811
811
  cancelCause?: CancelCause
812
- settlement: TurnSettlement
812
+ settlement: LiveTurnSettlement
813
813
  }
814
814
  /**
815
815
  * The turn failed. Terminal, like `turn_completed`.
@@ -853,7 +853,7 @@ type CoreSessionEvent =
853
853
  * it sends the reader somewhere specific and wrong.
854
854
  */
855
855
  explanation?: { id: string; message: string; hint: string }
856
- settlement: TurnSettlement
856
+ settlement: LiveTurnSettlement
857
857
  }
858
858
  // Additive 2026-07 (provider capability negotiation): emitted once per
859
859
  // turn when the request asks for something the provider DRIVER declared
@@ -42,6 +42,7 @@ export type {
42
42
  } from './sub-session.js'
43
43
 
44
44
  export * from './turn.js'
45
+ export * from './outcome.js'
45
46
  export * from './config.js'
46
47
  export * from './events.js'
47
48
  export * from './records.js'