@namzu/sdk 36.0.0 → 38.0.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 (412) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +68 -41
  3. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  4. package/dist/bridge/a2a/mapper.js +4 -0
  5. package/dist/bridge/a2a/mapper.js.map +1 -1
  6. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  7. package/dist/bridge/sse/mapper.js +16 -1
  8. package/dist/bridge/sse/mapper.js.map +1 -1
  9. package/dist/compaction/extractor.d.ts +1 -1
  10. package/dist/compaction/extractor.d.ts.map +1 -1
  11. package/dist/compaction/extractor.js +15 -7
  12. package/dist/compaction/extractor.js.map +1 -1
  13. package/dist/compaction/manager.d.ts +2 -0
  14. package/dist/compaction/manager.d.ts.map +1 -1
  15. package/dist/compaction/manager.js +12 -1
  16. package/dist/compaction/manager.js.map +1 -1
  17. package/dist/compaction/verifier.d.ts.map +1 -1
  18. package/dist/compaction/verifier.js +82 -15
  19. package/dist/compaction/verifier.js.map +1 -1
  20. package/dist/config/runtime.d.ts +16 -6
  21. package/dist/config/runtime.d.ts.map +1 -1
  22. package/dist/config/runtime.js +2 -0
  23. package/dist/config/runtime.js.map +1 -1
  24. package/dist/contracts/api.d.ts +1 -1
  25. package/dist/contracts/api.d.ts.map +1 -1
  26. package/dist/contracts/schemas.d.ts +10 -10
  27. package/dist/eval/harness-verification.d.ts +63 -0
  28. package/dist/eval/harness-verification.d.ts.map +1 -0
  29. package/dist/eval/harness-verification.js +165 -0
  30. package/dist/eval/harness-verification.js.map +1 -0
  31. package/dist/eval/index.d.ts +2 -0
  32. package/dist/eval/index.d.ts.map +1 -1
  33. package/dist/eval/index.js +1 -0
  34. package/dist/eval/index.js.map +1 -1
  35. package/dist/execution/code-runtime/json-value.d.ts +10 -0
  36. package/dist/execution/code-runtime/json-value.d.ts.map +1 -0
  37. package/dist/execution/code-runtime/json-value.js +89 -0
  38. package/dist/execution/code-runtime/json-value.js.map +1 -0
  39. package/dist/execution/code-runtime/worker-program.d.ts +8 -0
  40. package/dist/execution/code-runtime/worker-program.d.ts.map +1 -0
  41. package/dist/execution/code-runtime/worker-program.js +240 -0
  42. package/dist/execution/code-runtime/worker-program.js.map +1 -0
  43. package/dist/execution/code-runtime/worker.d.ts +22 -0
  44. package/dist/execution/code-runtime/worker.d.ts.map +1 -1
  45. package/dist/execution/code-runtime/worker.js +148 -111
  46. package/dist/execution/code-runtime/worker.js.map +1 -1
  47. package/dist/manager/agent/lifecycle.d.ts +12 -0
  48. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  49. package/dist/manager/agent/lifecycle.js +316 -46
  50. package/dist/manager/agent/lifecycle.js.map +1 -1
  51. package/dist/manager/run/persistence.d.ts +2 -0
  52. package/dist/manager/run/persistence.d.ts.map +1 -1
  53. package/dist/manager/run/persistence.js +6 -0
  54. package/dist/manager/run/persistence.js.map +1 -1
  55. package/dist/prompt/coding-agent-doctrine.d.ts +2 -2
  56. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  57. package/dist/prompt/coding-agent-doctrine.js +11 -5
  58. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  59. package/dist/provider/capabilities.d.ts +5 -1
  60. package/dist/provider/capabilities.d.ts.map +1 -1
  61. package/dist/provider/capabilities.js +24 -0
  62. package/dist/provider/capabilities.js.map +1 -1
  63. package/dist/provider/fallback.d.ts.map +1 -1
  64. package/dist/provider/fallback.js +6 -2
  65. package/dist/provider/fallback.js.map +1 -1
  66. package/dist/public-runtime.d.ts +4 -1
  67. package/dist/public-runtime.d.ts.map +1 -1
  68. package/dist/public-runtime.js +3 -1
  69. package/dist/public-runtime.js.map +1 -1
  70. package/dist/public-tools.d.ts +2 -1
  71. package/dist/public-tools.d.ts.map +1 -1
  72. package/dist/public-tools.js +2 -1
  73. package/dist/public-tools.js.map +1 -1
  74. package/dist/public-types.d.ts +2 -0
  75. package/dist/public-types.d.ts.map +1 -1
  76. package/dist/registry/tool/execute.d.ts +3 -0
  77. package/dist/registry/tool/execute.d.ts.map +1 -1
  78. package/dist/registry/tool/execute.js +11 -80
  79. package/dist/registry/tool/execute.js.map +1 -1
  80. package/dist/run/command-gate.d.ts.map +1 -1
  81. package/dist/run/command-gate.js +65 -12
  82. package/dist/run/command-gate.js.map +1 -1
  83. package/dist/run/index.d.ts +2 -0
  84. package/dist/run/index.d.ts.map +1 -1
  85. package/dist/run/index.js +1 -0
  86. package/dist/run/index.js.map +1 -1
  87. package/dist/run/memory-promoter.d.ts +3 -35
  88. package/dist/run/memory-promoter.d.ts.map +1 -1
  89. package/dist/run/memory-promoter.js +32 -40
  90. package/dist/run/memory-promoter.js.map +1 -1
  91. package/dist/run/memory-recall.d.ts +25 -0
  92. package/dist/run/memory-recall.d.ts.map +1 -0
  93. package/dist/run/memory-recall.js +228 -0
  94. package/dist/run/memory-recall.js.map +1 -0
  95. package/dist/run/reporter.d.ts.map +1 -1
  96. package/dist/run/reporter.js +2 -0
  97. package/dist/run/reporter.js.map +1 -1
  98. package/dist/run/token-budget.d.ts +12 -4
  99. package/dist/run/token-budget.d.ts.map +1 -1
  100. package/dist/run/token-budget.js +49 -14
  101. package/dist/run/token-budget.js.map +1 -1
  102. package/dist/run/workspace-fingerprint.d.ts +10 -3
  103. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  104. package/dist/run/workspace-fingerprint.js +17 -8
  105. package/dist/run/workspace-fingerprint.js.map +1 -1
  106. package/dist/runtime/query/checkpoint.d.ts +14 -0
  107. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  108. package/dist/runtime/query/checkpoint.js +82 -1
  109. package/dist/runtime/query/checkpoint.js.map +1 -1
  110. package/dist/runtime/query/executor.d.ts +8 -3
  111. package/dist/runtime/query/executor.d.ts.map +1 -1
  112. package/dist/runtime/query/executor.js +94 -30
  113. package/dist/runtime/query/executor.js.map +1 -1
  114. package/dist/runtime/query/index.d.ts +7 -8
  115. package/dist/runtime/query/index.d.ts.map +1 -1
  116. package/dist/runtime/query/index.js +55 -7
  117. package/dist/runtime/query/index.js.map +1 -1
  118. package/dist/runtime/query/iteration/index.d.ts +12 -0
  119. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  120. package/dist/runtime/query/iteration/index.js +348 -38
  121. package/dist/runtime/query/iteration/index.js.map +1 -1
  122. package/dist/runtime/query/iteration/native-output.d.ts +10 -0
  123. package/dist/runtime/query/iteration/native-output.d.ts.map +1 -0
  124. package/dist/runtime/query/iteration/native-output.js +32 -0
  125. package/dist/runtime/query/iteration/native-output.js.map +1 -0
  126. package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
  127. package/dist/runtime/query/iteration/phases/advisory.js +18 -13
  128. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  129. package/dist/runtime/query/iteration/phases/compaction.d.ts +2 -0
  130. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  131. package/dist/runtime/query/iteration/phases/compaction.js +32 -11
  132. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  133. package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
  134. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  135. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  137. package/dist/runtime/query/iteration/phases/tool-review.js +1 -1
  138. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  139. package/dist/runtime/query/iteration/phases/working-memory.d.ts +1 -1
  140. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
  141. package/dist/runtime/query/iteration/phases/working-memory.js +23 -6
  142. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
  143. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  144. package/dist/runtime/query/iteration/stream-turn.js +12 -0
  145. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  146. package/dist/runtime/query/observation-context.d.ts +10 -0
  147. package/dist/runtime/query/observation-context.d.ts.map +1 -0
  148. package/dist/runtime/query/observation-context.js +84 -0
  149. package/dist/runtime/query/observation-context.js.map +1 -0
  150. package/dist/runtime/query/request-context.d.ts +30 -0
  151. package/dist/runtime/query/request-context.d.ts.map +1 -0
  152. package/dist/runtime/query/request-context.js +76 -0
  153. package/dist/runtime/query/request-context.js.map +1 -0
  154. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  155. package/dist/runtime/query/request-rich-content.js +6 -3
  156. package/dist/runtime/query/request-rich-content.js.map +1 -1
  157. package/dist/runtime/query/review-policy.d.ts +3 -0
  158. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  159. package/dist/runtime/query/review-policy.js +4 -1
  160. package/dist/runtime/query/review-policy.js.map +1 -1
  161. package/dist/runtime/query/steering.d.ts +6 -2
  162. package/dist/runtime/query/steering.d.ts.map +1 -1
  163. package/dist/runtime/query/steering.js +17 -2
  164. package/dist/runtime/query/steering.js.map +1 -1
  165. package/dist/runtime/query/tool-call-budget.d.ts +19 -0
  166. package/dist/runtime/query/tool-call-budget.d.ts.map +1 -0
  167. package/dist/runtime/query/tool-call-budget.js +110 -0
  168. package/dist/runtime/query/tool-call-budget.js.map +1 -0
  169. package/dist/runtime/query/tooling.d.ts +3 -0
  170. package/dist/runtime/query/tooling.d.ts.map +1 -1
  171. package/dist/runtime/query/tooling.js +5 -0
  172. package/dist/runtime/query/tooling.js.map +1 -1
  173. package/dist/sandbox/file-walk-program.d.ts.map +1 -1
  174. package/dist/sandbox/file-walk-program.js +8 -0
  175. package/dist/sandbox/file-walk-program.js.map +1 -1
  176. package/dist/sandbox/file-walk.d.ts.map +1 -1
  177. package/dist/sandbox/file-walk.js +9 -0
  178. package/dist/sandbox/file-walk.js.map +1 -1
  179. package/dist/scheduler/completion-inbox.d.ts.map +1 -1
  180. package/dist/scheduler/completion-inbox.js +3 -0
  181. package/dist/scheduler/completion-inbox.js.map +1 -1
  182. package/dist/scheduler/local.d.ts.map +1 -1
  183. package/dist/scheduler/local.js +1 -0
  184. package/dist/scheduler/local.js.map +1 -1
  185. package/dist/session/handoff/capacity.d.ts +2 -2
  186. package/dist/session/handoff/capacity.d.ts.map +1 -1
  187. package/dist/session/handoff/capacity.js +3 -2
  188. package/dist/session/handoff/capacity.js.map +1 -1
  189. package/dist/store/memory/disk.d.ts +6 -2
  190. package/dist/store/memory/disk.d.ts.map +1 -1
  191. package/dist/store/memory/disk.js +42 -25
  192. package/dist/store/memory/disk.js.map +1 -1
  193. package/dist/store/memory/index.d.ts +4 -0
  194. package/dist/store/memory/index.d.ts.map +1 -1
  195. package/dist/store/memory/index.js +55 -19
  196. package/dist/store/memory/index.js.map +1 -1
  197. package/dist/store/memory/memory.d.ts +3 -2
  198. package/dist/store/memory/memory.d.ts.map +1 -1
  199. package/dist/store/memory/memory.js +11 -2
  200. package/dist/store/memory/memory.js.map +1 -1
  201. package/dist/store/memory/operation-lock.d.ts +11 -0
  202. package/dist/store/memory/operation-lock.d.ts.map +1 -0
  203. package/dist/store/memory/operation-lock.js +94 -0
  204. package/dist/store/memory/operation-lock.js.map +1 -0
  205. package/dist/store/run/token-budget-disk.js +3 -0
  206. package/dist/store/run/token-budget-disk.js.map +1 -1
  207. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  208. package/dist/tools/builtins/computer-use.js +60 -10
  209. package/dist/tools/builtins/computer-use.js.map +1 -1
  210. package/dist/tools/builtins/edit.d.ts.map +1 -1
  211. package/dist/tools/builtins/edit.js +12 -26
  212. package/dist/tools/builtins/edit.js.map +1 -1
  213. package/dist/tools/builtins/file-write-result.d.ts +4 -0
  214. package/dist/tools/builtins/file-write-result.d.ts.map +1 -0
  215. package/dist/tools/builtins/file-write-result.js +72 -0
  216. package/dist/tools/builtins/file-write-result.js.map +1 -0
  217. package/dist/tools/builtins/glob.d.ts.map +1 -1
  218. package/dist/tools/builtins/glob.js +8 -1
  219. package/dist/tools/builtins/glob.js.map +1 -1
  220. package/dist/tools/builtins/grep.d.ts.map +1 -1
  221. package/dist/tools/builtins/grep.js +10 -0
  222. package/dist/tools/builtins/grep.js.map +1 -1
  223. package/dist/tools/builtins/job.d.ts.map +1 -1
  224. package/dist/tools/builtins/job.js +6 -0
  225. package/dist/tools/builtins/job.js.map +1 -1
  226. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  227. package/dist/tools/builtins/read-file.js +10 -0
  228. package/dist/tools/builtins/read-file.js.map +1 -1
  229. package/dist/tools/builtins/run-code.d.ts +6 -0
  230. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  231. package/dist/tools/builtins/run-code.js +15 -4
  232. package/dist/tools/builtins/run-code.js.map +1 -1
  233. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  234. package/dist/tools/builtins/search-tools.js +14 -2
  235. package/dist/tools/builtins/search-tools.js.map +1 -1
  236. package/dist/tools/builtins/write-file.d.ts.map +1 -1
  237. package/dist/tools/builtins/write-file.js +30 -44
  238. package/dist/tools/builtins/write-file.js.map +1 -1
  239. package/dist/tools/defineTool.d.ts +2 -0
  240. package/dist/tools/defineTool.d.ts.map +1 -1
  241. package/dist/tools/defineTool.js +3 -0
  242. package/dist/tools/defineTool.js.map +1 -1
  243. package/dist/tools/file-read-tracker.d.ts +4 -0
  244. package/dist/tools/file-read-tracker.d.ts.map +1 -0
  245. package/dist/tools/file-read-tracker.js +16 -0
  246. package/dist/tools/file-read-tracker.js.map +1 -0
  247. package/dist/tools/memory/delete.d.ts +4 -0
  248. package/dist/tools/memory/delete.d.ts.map +1 -0
  249. package/dist/tools/memory/delete.js +26 -0
  250. package/dist/tools/memory/delete.js.map +1 -0
  251. package/dist/tools/memory/index.d.ts +2 -0
  252. package/dist/tools/memory/index.d.ts.map +1 -1
  253. package/dist/tools/memory/index.js +6 -0
  254. package/dist/tools/memory/index.js.map +1 -1
  255. package/dist/tools/memory/save.d.ts.map +1 -1
  256. package/dist/tools/memory/save.js +3 -2
  257. package/dist/tools/memory/save.js.map +1 -1
  258. package/dist/tools/memory/search.d.ts.map +1 -1
  259. package/dist/tools/memory/search.js +12 -5
  260. package/dist/tools/memory/search.js.map +1 -1
  261. package/dist/tools/memory/update.d.ts +4 -0
  262. package/dist/tools/memory/update.d.ts.map +1 -0
  263. package/dist/tools/memory/update.js +47 -0
  264. package/dist/tools/memory/update.js.map +1 -0
  265. package/dist/tools/task/list.js +2 -2
  266. package/dist/tools/task/list.js.map +1 -1
  267. package/dist/types/agent/scheduler.d.ts +5 -0
  268. package/dist/types/agent/scheduler.d.ts.map +1 -1
  269. package/dist/types/agent/task.d.ts +10 -0
  270. package/dist/types/agent/task.d.ts.map +1 -1
  271. package/dist/types/computer-use/index.d.ts +6 -0
  272. package/dist/types/computer-use/index.d.ts.map +1 -1
  273. package/dist/types/computer-use/index.js.map +1 -1
  274. package/dist/types/errors/catalog.js +1 -1
  275. package/dist/types/errors/catalog.js.map +1 -1
  276. package/dist/types/hitl/index.d.ts +11 -1
  277. package/dist/types/hitl/index.d.ts.map +1 -1
  278. package/dist/types/hitl/index.js.map +1 -1
  279. package/dist/types/memory/index.d.ts +14 -1
  280. package/dist/types/memory/index.d.ts.map +1 -1
  281. package/dist/types/plugin/index.d.ts +7 -0
  282. package/dist/types/plugin/index.d.ts.map +1 -1
  283. package/dist/types/plugin/index.js.map +1 -1
  284. package/dist/types/project/entity.d.ts +1 -0
  285. package/dist/types/project/entity.d.ts.map +1 -1
  286. package/dist/types/provider/chat.d.ts +4 -0
  287. package/dist/types/provider/chat.d.ts.map +1 -1
  288. package/dist/types/provider/config.d.ts +4 -0
  289. package/dist/types/provider/config.d.ts.map +1 -1
  290. package/dist/types/provider/model.d.ts +4 -0
  291. package/dist/types/provider/model.d.ts.map +1 -1
  292. package/dist/types/provider/stream.d.ts +6 -0
  293. package/dist/types/provider/stream.d.ts.map +1 -1
  294. package/dist/types/run/answer-review.d.ts +2 -0
  295. package/dist/types/run/answer-review.d.ts.map +1 -1
  296. package/dist/types/run/config.d.ts +2 -0
  297. package/dist/types/run/config.d.ts.map +1 -1
  298. package/dist/types/run/events.d.ts +19 -0
  299. package/dist/types/run/events.d.ts.map +1 -1
  300. package/dist/types/run/events.js.map +1 -1
  301. package/dist/types/run/prepare-step.d.ts +19 -1
  302. package/dist/types/run/prepare-step.d.ts.map +1 -1
  303. package/dist/types/sandbox/index.d.ts +1 -0
  304. package/dist/types/sandbox/index.d.ts.map +1 -1
  305. package/dist/types/sandbox/index.js.map +1 -1
  306. package/dist/types/structured-output/index.d.ts +8 -2
  307. package/dist/types/structured-output/index.d.ts.map +1 -1
  308. package/dist/types/tool/index.d.ts +15 -0
  309. package/dist/types/tool/index.d.ts.map +1 -1
  310. package/dist/types/tool/index.js.map +1 -1
  311. package/dist/types/tool/presentation.d.ts +4 -0
  312. package/dist/types/tool/presentation.d.ts.map +1 -1
  313. package/dist/utils/json-snapshot.d.ts +3 -0
  314. package/dist/utils/json-snapshot.d.ts.map +1 -0
  315. package/dist/utils/json-snapshot.js +79 -0
  316. package/dist/utils/json-snapshot.js.map +1 -0
  317. package/package.json +4 -2
  318. package/src/bridge/a2a/mapper.ts +4 -0
  319. package/src/bridge/sse/mapper.ts +16 -1
  320. package/src/compaction/extractor.ts +21 -8
  321. package/src/compaction/manager.ts +11 -1
  322. package/src/compaction/verifier.ts +80 -18
  323. package/src/config/runtime.ts +2 -0
  324. package/src/contracts/api.ts +1 -0
  325. package/src/eval/harness-verification.ts +270 -0
  326. package/src/eval/index.ts +10 -0
  327. package/src/execution/code-runtime/json-value.ts +82 -0
  328. package/src/execution/code-runtime/worker-program.ts +239 -0
  329. package/src/execution/code-runtime/worker.ts +164 -111
  330. package/src/manager/agent/lifecycle.ts +352 -54
  331. package/src/manager/run/persistence.ts +7 -0
  332. package/src/prompt/coding-agent-doctrine.ts +11 -5
  333. package/src/provider/capabilities.ts +41 -1
  334. package/src/provider/fallback.ts +9 -2
  335. package/src/public-runtime.ts +6 -0
  336. package/src/public-tools.ts +7 -1
  337. package/src/public-types.ts +7 -0
  338. package/src/registry/tool/execute.ts +17 -79
  339. package/src/run/command-gate.ts +61 -13
  340. package/src/run/index.ts +2 -0
  341. package/src/run/memory-promoter.ts +32 -40
  342. package/src/run/memory-recall.ts +254 -0
  343. package/src/run/reporter.ts +2 -0
  344. package/src/run/token-budget.ts +55 -14
  345. package/src/run/workspace-fingerprint.ts +17 -8
  346. package/src/runtime/query/checkpoint.ts +96 -2
  347. package/src/runtime/query/executor.ts +100 -29
  348. package/src/runtime/query/index.ts +76 -16
  349. package/src/runtime/query/iteration/index.ts +405 -46
  350. package/src/runtime/query/iteration/native-output.ts +35 -0
  351. package/src/runtime/query/iteration/phases/advisory.ts +21 -11
  352. package/src/runtime/query/iteration/phases/compaction.ts +36 -14
  353. package/src/runtime/query/iteration/phases/context.ts +9 -0
  354. package/src/runtime/query/iteration/phases/tool-review.ts +5 -1
  355. package/src/runtime/query/iteration/phases/working-memory.ts +23 -5
  356. package/src/runtime/query/iteration/stream-turn.ts +15 -0
  357. package/src/runtime/query/observation-context.ts +86 -0
  358. package/src/runtime/query/request-context.ts +103 -0
  359. package/src/runtime/query/request-rich-content.ts +8 -3
  360. package/src/runtime/query/review-policy.ts +7 -1
  361. package/src/runtime/query/steering.ts +19 -2
  362. package/src/runtime/query/tool-call-budget.ts +115 -0
  363. package/src/runtime/query/tooling.ts +8 -0
  364. package/src/sandbox/file-walk-program.ts +8 -0
  365. package/src/sandbox/file-walk.ts +9 -0
  366. package/src/scheduler/completion-inbox.ts +3 -0
  367. package/src/scheduler/local.ts +1 -0
  368. package/src/session/handoff/capacity.ts +7 -3
  369. package/src/store/memory/disk.ts +52 -30
  370. package/src/store/memory/index.ts +75 -27
  371. package/src/store/memory/memory.ts +18 -6
  372. package/src/store/memory/operation-lock.ts +99 -0
  373. package/src/store/run/token-budget-disk.ts +3 -0
  374. package/src/tools/builtins/computer-use.ts +61 -7
  375. package/src/tools/builtins/edit.ts +12 -27
  376. package/src/tools/builtins/file-write-result.ts +84 -0
  377. package/src/tools/builtins/glob.ts +7 -1
  378. package/src/tools/builtins/grep.ts +10 -0
  379. package/src/tools/builtins/job.ts +8 -0
  380. package/src/tools/builtins/read-file.ts +10 -0
  381. package/src/tools/builtins/run-code.ts +24 -5
  382. package/src/tools/builtins/search-tools.ts +18 -3
  383. package/src/tools/builtins/write-file.ts +39 -44
  384. package/src/tools/defineTool.ts +5 -0
  385. package/src/tools/file-read-tracker.ts +16 -0
  386. package/src/tools/memory/delete.ts +29 -0
  387. package/src/tools/memory/index.ts +6 -0
  388. package/src/tools/memory/save.ts +4 -2
  389. package/src/tools/memory/search.ts +12 -5
  390. package/src/tools/memory/update.ts +50 -0
  391. package/src/tools/task/list.ts +2 -2
  392. package/src/types/agent/scheduler.ts +6 -0
  393. package/src/types/agent/task.ts +13 -0
  394. package/src/types/computer-use/index.ts +6 -0
  395. package/src/types/errors/catalog.ts +1 -1
  396. package/src/types/hitl/index.ts +11 -1
  397. package/src/types/memory/index.ts +16 -1
  398. package/src/types/plugin/index.ts +10 -0
  399. package/src/types/project/entity.ts +1 -0
  400. package/src/types/provider/chat.ts +2 -0
  401. package/src/types/provider/config.ts +4 -0
  402. package/src/types/provider/model.ts +4 -0
  403. package/src/types/provider/stream.ts +7 -0
  404. package/src/types/run/answer-review.ts +2 -0
  405. package/src/types/run/config.ts +2 -0
  406. package/src/types/run/events.ts +20 -0
  407. package/src/types/run/prepare-step.ts +19 -1
  408. package/src/types/sandbox/index.ts +1 -0
  409. package/src/types/structured-output/index.ts +15 -2
  410. package/src/types/tool/index.ts +16 -0
  411. package/src/types/tool/presentation.ts +4 -0
  412. package/src/utils/json-snapshot.ts +76 -0
@@ -0,0 +1,115 @@
1
+ import type { RunId } from '../../types/ids/index.js'
2
+ import type { RunEvent } from '../../types/run/index.js'
3
+
4
+ export function assertMaxToolCalls(limit: number | undefined): void {
5
+ if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 0)) {
6
+ throw new RangeError('maxToolCalls must be a nonnegative safe integer.')
7
+ }
8
+ }
9
+
10
+ /** A run-owned admission ledger; durability belongs to the run's event store. */
11
+ export class ToolCallBudget {
12
+ private used = 0
13
+ private initialized?: Promise<void>
14
+ private chain: Promise<void> = Promise.resolve()
15
+ private failed: unknown
16
+
17
+ constructor(
18
+ private readonly limit: number,
19
+ private readonly runId: RunId,
20
+ private readonly emit: (event: RunEvent) => Promise<void>,
21
+ private readonly read?: () => Promise<readonly RunEvent[]>,
22
+ ) {
23
+ assertMaxToolCalls(limit)
24
+ }
25
+
26
+ private async initialize(): Promise<void> {
27
+ const events = (await this.read?.()) ?? []
28
+ if (events.length > 100_000) throw new Error('Tool-call budget recovery exceeds 100000 events.')
29
+ let initialized = false
30
+ let expectedSeq = 0
31
+ for (const event of events) {
32
+ // A replayed transcript must be complete and belong to this run.
33
+ if (event.runId !== this.runId || event.seq !== ++expectedSeq)
34
+ throw new Error('Tool-call budget recovery requires a complete run event log.')
35
+ if (event.type === 'tool_calls_admitted') {
36
+ if (
37
+ !Number.isSafeInteger(event.count) ||
38
+ event.count < 0 ||
39
+ !Number.isSafeInteger(event.used) ||
40
+ event.used !== this.used + event.count ||
41
+ !Number.isSafeInteger(event.limit) ||
42
+ event.limit < event.used ||
43
+ !['initialize', 'batch', 'nested', 'retry'].includes(event.kind) ||
44
+ (event.kind === 'initialize'
45
+ ? initialized || event.count !== 0
46
+ : !initialized || event.count === 0)
47
+ )
48
+ throw new Error('Invalid tool-call admission ledger.')
49
+ initialized = true
50
+ this.used = event.used
51
+ } else if (
52
+ !initialized &&
53
+ (event.type === 'tool_executing' || event.type === 'tool_completed')
54
+ ) {
55
+ throw new Error('Cannot establish tool-call usage for an earlier unbudgeted execution.')
56
+ }
57
+ }
58
+ if (!initialized) {
59
+ await this.emit({
60
+ type: 'tool_calls_admitted',
61
+ runId: this.runId,
62
+ kind: 'initialize',
63
+ count: 0,
64
+ used: 0,
65
+ limit: this.limit,
66
+ })
67
+ }
68
+ }
69
+
70
+ /** Reserve before work, never refund. A refused group consumes no new slots. */
71
+ async admit(
72
+ count: number,
73
+ kind: 'batch' | 'nested' | 'retry',
74
+ signal: AbortSignal,
75
+ ): Promise<string | undefined> {
76
+ signal.throwIfAborted()
77
+ const previous = this.chain
78
+ let release!: () => void
79
+ this.chain = new Promise<void>((resolve) => {
80
+ release = resolve
81
+ })
82
+ try {
83
+ await previous
84
+ signal.throwIfAborted()
85
+ if (this.failed !== undefined) throw this.failed
86
+ this.initialized ??= this.initialize()
87
+ await this.initialized
88
+ signal.throwIfAborted()
89
+ if (count > Math.max(0, this.limit - this.used)) {
90
+ return `Tool-call budget exhausted: this ${kind} needs ${count} slots; ${Math.max(0, this.limit - this.used)} remain of maxToolCalls=${this.limit}. No calls in this admission were executed.`
91
+ }
92
+ if (count === 0) return undefined
93
+ const used = this.used + count
94
+ try {
95
+ await this.emit({
96
+ type: 'tool_calls_admitted',
97
+ runId: this.runId,
98
+ kind,
99
+ count,
100
+ used,
101
+ limit: this.limit,
102
+ })
103
+ } catch (error) {
104
+ // An uncertain write must not be retried as a fresh allowance.
105
+ this.failed = error ?? new Error('Tool-call admission persistence failed.')
106
+ throw this.failed
107
+ }
108
+ this.used = used
109
+ signal.throwIfAborted()
110
+ return undefined
111
+ } finally {
112
+ release()
113
+ }
114
+ }
115
+ }
@@ -20,6 +20,7 @@ import { ToolExecutor } from './executor.js'
20
20
  export type EmitEvent = (event: RunEvent) => Promise<void>
21
21
 
22
22
  export interface ToolingBootstrapConfig {
23
+ fileReadTracker?: import('../../types/tool/index.js').FileReadTracker
23
24
  tools: ToolRegistryContract
24
25
  runId: RunId
25
26
  workingDirectory: string
@@ -43,6 +44,8 @@ export interface ToolingBootstrapConfig {
43
44
  web?: import('../../types/tool/index.js').ToolContext['web']
44
45
  toolRetryBackoff?: Partial<BackoffPolicy>
45
46
  maxToolConcurrency?: number
47
+ maxToolCalls?: number
48
+ readToolCallBudgetEvents?: () => Promise<readonly RunEvent[]>
46
49
  maxToolOutputChars?: number
47
50
  maxToolContentBytes?: number
48
51
  toolOutputDir?: string
@@ -65,6 +68,7 @@ export class ToolingBootstrap {
65
68
  return new ToolExecutor(
66
69
  {
67
70
  tools: config.tools,
71
+ ...(config.fileReadTracker ? { fileReadTracker: config.fileReadTracker } : {}),
68
72
  runId: config.runId,
69
73
  workingDirectory: config.workingDirectory,
70
74
  ...(config.additionalDirectories?.length
@@ -84,6 +88,10 @@ export class ToolingBootstrap {
84
88
  ...(config.toolRetryBackoff !== undefined
85
89
  ? { toolRetryBackoff: config.toolRetryBackoff }
86
90
  : {}),
91
+ ...(config.maxToolCalls !== undefined ? { maxToolCalls: config.maxToolCalls } : {}),
92
+ ...(config.readToolCallBudgetEvents
93
+ ? { readToolCallBudgetEvents: config.readToolCallBudgetEvents }
94
+ : {}),
87
95
  ...(config.maxToolConcurrency !== undefined
88
96
  ? { maxToolConcurrency: config.maxToolConcurrency }
89
97
  : {}),
@@ -51,6 +51,14 @@ async function run() {
51
51
  try { canonicalRoot = await fs.realpath(plan.root); }
52
52
  catch (error) { if (missing(error)) return; throw error; }
53
53
  if (canonicalRoot !== path.resolve(plan.root)) throw new Error('File walk root follows a symbolic link; use its authorized real directory');
54
+ const rootInfo = await fs.lstat(plan.root);
55
+ if (rootInfo.isFile()) {
56
+ visit(++visited);
57
+ if (matchers.some(matcher => matcher.test(path.basename(plan.root)))) {
58
+ await write({type:'entry', path:plan.root, size:rootInfo.size});
59
+ }
60
+ return;
61
+ }
54
62
  let start = plan.root;
55
63
  for (const part of plan.prefix) {
56
64
  start = path.join(start, part);
@@ -227,6 +227,15 @@ async function* executeWalk(
227
227
  }
228
228
  if (canonicalRoot !== path.resolve(plan.root))
229
229
  throw new Error('File walk root follows a symbolic link; use its authorized real directory')
230
+ // A named regular file is a one-entry search, not an empty directory.
231
+ const rootInfo = await wait(fs.lstat(plan.root))
232
+ check()
233
+ if (rootInfo.isFile()) {
234
+ noteVisit()
235
+ if (matchers.some((matcher) => matcher.test(path.basename(plan.root))))
236
+ yield { path: plan.root, size: rootInfo.size }
237
+ return
238
+ }
230
239
  let start = plan.root
231
240
  // Never follow a static-prefix symlink to speed up a pattern: traversal
232
241
  // and its optimized starting point must have the same link policy.
@@ -464,6 +464,9 @@ export function formatCompletionNotification(handles: readonly TaskHandle[]): st
464
464
  `task_id: ${handle.taskId}`,
465
465
  `agent: ${handle.agentId}`,
466
466
  `state: ${handle.state}`,
467
+ ...(handle.state === 'completed' && run?.stopReason && run.stopReason !== 'end_turn'
468
+ ? ['outcome: incomplete — execution stopped; this does not establish task completion.']
469
+ : []),
467
470
  ...(handle.result?.stopReason ? [`stop_reason: ${handle.result.stopReason}`] : []),
468
471
  ...(durationMs !== undefined ? [`duration_ms: ${durationMs}`] : []),
469
472
  '',
@@ -111,6 +111,7 @@ export class LocalTaskScheduler implements TaskScheduler {
111
111
  const task = await this.agentManager.sendMessage(
112
112
  {
113
113
  agentId: options.agentId,
114
+ beforeStart: options.beforeStart,
114
115
  input: {
115
116
  messages: [createUserMessage(options.prompt)],
116
117
  workingDirectory: options.workingDirectory,
@@ -62,7 +62,7 @@ export interface CapacityValidator {
62
62
  ): Promise<void>
63
63
 
64
64
  /**
65
- * Asserts `existingDirectChildren + pendingNewChildren ≤ projectMaxWidth`
65
+ * Asserts `liveDirectChildren + pendingNewChildren ≤ projectMaxWidth`
66
66
  * under `parentSessionId`. The width cap applies to a single spawn call —
67
67
  * a broadcast of N recipients passes `pendingNewChildren = N`.
68
68
  */
@@ -77,7 +77,7 @@ export interface CapacityValidator {
77
77
  /**
78
78
  * Default validator backed by {@link SessionStore}. Uses `getAncestry` for
79
79
  * depth (root-to-self chain length) and `getChildren` for width (count of
80
- * existing direct sub-sessions).
80
+ * pending or active direct sub-sessions). Historical edges stay queryable.
81
81
  */
82
82
  export class DefaultCapacityValidator implements CapacityValidator {
83
83
  constructor(private readonly store: SessionStore) {}
@@ -108,7 +108,11 @@ export class DefaultCapacityValidator implements CapacityValidator {
108
108
  tenantId: TenantId,
109
109
  ): Promise<void> {
110
110
  const existing = await this.store.getChildren(parentSessionId, tenantId)
111
- const total = existing.length + pendingNewChildren
111
+ const live = existing.filter(
112
+ (child) =>
113
+ child.status !== 'idle' && child.status !== 'failed' && child.status !== 'archived',
114
+ )
115
+ const total = live.length + pendingNewChildren
112
116
  if (total > projectMaxWidth) {
113
117
  throw new DelegationCapacityExceeded({
114
118
  dimension: 'width',
@@ -6,16 +6,24 @@ import type {
6
6
  CreateMemoryParams,
7
7
  MemoryContent,
8
8
  MemoryIndexEntry,
9
+ MemoryRecord,
9
10
  MemorySearchParams,
10
11
  MemorySearchResult,
11
12
  MemoryStore,
13
+ UpdateMemoryParams,
12
14
  } from '../../types/memory/index.js'
15
+ import { assertMemoryStatus } from '../../types/memory/index.js'
13
16
  import { generateMemoryId, isEntityId } from '../../utils/id.js'
14
17
  import { SCOPE_ATTRIBUTE } from '../../utils/log/types.js'
15
18
  import { type Logger, resolveLogger } from '../../utils/logger.js'
16
19
  import { DiskRecordStore } from '../kv/record-store.js'
17
20
  import { defineSchema } from '../schema.js'
18
- import { InMemoryMemoryIndex } from './index.js'
21
+ import { InMemoryMemoryIndex, searchMemoryEntries } from './index.js'
22
+ import {
23
+ DEFAULT_MEMORY_LOCK_TIMEOUT_MS,
24
+ acquireMemoryOperationLock,
25
+ validateMemoryLockTimeout,
26
+ } from './operation-lock.js'
19
27
 
20
28
  /**
21
29
  * This store's on-disk format, versioned as a unit — which is how a
@@ -32,27 +40,6 @@ interface StorageLocation {
32
40
  readonly contentDir: string
33
41
  }
34
42
 
35
- const operationTails = new Map<string, Promise<void>>()
36
-
37
- /** Serialize every operation over one canonical disk projection in this process. */
38
- async function withMemoryOperationLock<T>(key: string, operation: () => Promise<T>): Promise<T> {
39
- const previous = operationTails.get(key) ?? Promise.resolve()
40
- let release = () => {}
41
- const current = new Promise<void>((resolveCurrent) => {
42
- release = resolveCurrent
43
- })
44
- const tail = previous.then(() => current)
45
- operationTails.set(key, tail)
46
-
47
- await previous
48
- try {
49
- return await operation()
50
- } finally {
51
- release()
52
- if (operationTails.get(key) === tail) operationTails.delete(key)
53
- }
54
- }
55
-
56
43
  function invalidIndex(
57
44
  reason: string,
58
45
  details: { entryIndex?: number; field?: string } = {},
@@ -163,11 +150,14 @@ function assertMemoryContent(value: unknown, expectedId: MemoryId): asserts valu
163
150
  export interface DiskMemoryStoreConfig {
164
151
  baseDir: string
165
152
  logger?: Logger
153
+ /** Maximum wait for another process's operation; default 10 seconds. Never breaks stale locks. */
154
+ lockTimeoutMs?: number
166
155
  }
167
156
 
168
157
  export class DiskMemoryStore implements MemoryStore {
169
158
  private readonly baseDir: string
170
159
  private readonly log: Logger
160
+ private readonly lockTimeoutMs: number
171
161
  private readonly index = new InMemoryMemoryIndex()
172
162
  private location?: StorageLocation
173
163
  // Two instances of one primitive rather than two copies of its body.
@@ -180,6 +170,8 @@ export class DiskMemoryStore implements MemoryStore {
180
170
  constructor(config: DiskMemoryStoreConfig) {
181
171
  this.baseDir = join(config.baseDir, 'memory')
182
172
  this.log = resolveLogger(config.logger).child({ [SCOPE_ATTRIBUTE]: 'store/memory/disk' })
173
+ this.lockTimeoutMs = config.lockTimeoutMs ?? DEFAULT_MEMORY_LOCK_TIMEOUT_MS
174
+ validateMemoryLockTimeout(this.lockTimeoutMs)
183
175
  }
184
176
 
185
177
  private get contentDir(): string {
@@ -235,10 +227,16 @@ export class DiskMemoryStore implements MemoryStore {
235
227
  operation: (location: StorageLocation) => Promise<T>,
236
228
  ): Promise<T> {
237
229
  const location = await this.storageLocation()
238
- return withMemoryOperationLock(location.indexPath, async () => {
230
+ const release = await acquireMemoryOperationLock(
231
+ join(dirname(location.indexPath), 'operation.lock'),
232
+ this.lockTimeoutMs,
233
+ )
234
+ try {
239
235
  await this.reloadIndex(location)
240
- return operation(location)
241
- })
236
+ return await operation(location)
237
+ } finally {
238
+ await release()
239
+ }
242
240
  }
243
241
 
244
242
  private async readContent(location: StorageLocation, id: MemoryId): Promise<MemoryContent> {
@@ -307,10 +305,17 @@ export class DiskMemoryStore implements MemoryStore {
307
305
  })
308
306
  }
309
307
 
310
- async update(
311
- id: MemoryId,
312
- updates: Partial<CreateMemoryParams>,
313
- ): Promise<MemoryIndexEntry | undefined> {
308
+ async getRecord(id: MemoryId): Promise<MemoryRecord | undefined> {
309
+ return this.withAuthoritativeIndex(async (location) => {
310
+ assertStorageMemoryId(id, (reason) => invalidContent(id, reason, { field: 'id' }))
311
+ const entry = this.index.getEntry(id)
312
+ if (!entry) return undefined
313
+ return structuredClone({ entry, content: await this.readContent(location, id) })
314
+ })
315
+ }
316
+
317
+ async update(id: MemoryId, updates: UpdateMemoryParams): Promise<MemoryIndexEntry | undefined> {
318
+ if (updates.status !== undefined) assertMemoryStatus(updates.status)
314
319
  return this.withAuthoritativeIndex(async (location) => {
315
320
  assertStorageMemoryId(id, (reason) => invalidContent(id, reason, { field: 'id' }))
316
321
  const existing = this.index.getEntry(id)
@@ -321,6 +326,7 @@ export class DiskMemoryStore implements MemoryStore {
321
326
  title: updates.title ?? existing.title,
322
327
  summary: updates.summary ?? existing.summary,
323
328
  tags: updates.tags ? [...updates.tags] : existing.tags,
329
+ status: updates.status ?? existing.status,
324
330
  updatedAt: Date.now(),
325
331
  }
326
332
  const updatesContent =
@@ -367,7 +373,23 @@ export class DiskMemoryStore implements MemoryStore {
367
373
  }
368
374
 
369
375
  async list(params?: MemorySearchParams): Promise<MemorySearchResult> {
370
- return this.withAuthoritativeIndex(async () => this.index.search(params ?? {}))
376
+ return this.withAuthoritativeIndex(async (location) => {
377
+ if (!params?.query?.trim() && !params?.requiredIdentifiers?.length)
378
+ return this.index.search(params ?? {})
379
+ // Search the current bodies inside the same operation as the index
380
+ // snapshot. No retained cache can hide another process's update.
381
+ const candidates = this.index.search({
382
+ ...params,
383
+ query: undefined,
384
+ limit: undefined,
385
+ requiredIdentifiers: undefined,
386
+ })
387
+ const contents = new Map<MemoryId, string>()
388
+ for (const entry of candidates.entries) {
389
+ contents.set(entry.id, (await this.readContent(location, entry.id)).content)
390
+ }
391
+ return searchMemoryEntries(candidates.entries, params, (id) => contents.get(id) ?? '')
392
+ })
371
393
  }
372
394
 
373
395
  getIndex(): InMemoryMemoryIndex {
@@ -6,37 +6,85 @@ import type {
6
6
  MemorySearchResult,
7
7
  } from '../../types/memory/index.js'
8
8
 
9
- export class InMemoryMemoryIndex implements MemoryIndex {
10
- private entries = new Map<string, MemoryIndexEntry>()
11
-
12
- search(params: MemorySearchParams): MemorySearchResult {
13
- let results = Array.from(this.entries.values())
14
-
15
- if (params.query) {
16
- const q = params.query.toLowerCase()
17
- results = results.filter(
18
- (e) => e.title.toLowerCase().includes(q) || e.summary.toLowerCase().includes(q),
19
- )
20
- }
21
-
22
- if (params.tags && params.tags.length > 0) {
23
- const requiredTags = params.tags
24
- results = results.filter((e) => requiredTags.every((tag) => e.tags.includes(tag)))
25
- }
9
+ function terms(text: string): Set<string> {
10
+ return new Set(
11
+ text
12
+ .normalize('NFKC')
13
+ .toLowerCase()
14
+ .match(/[\p{L}\p{N}]+/gu) ?? [],
15
+ )
16
+ }
26
17
 
27
- if (params.status) {
28
- results = results.filter((e) => e.status === params.status)
29
- }
18
+ /** Exact normalized word membership; no substring, stemming or alias inference. */
19
+ export function matchesMemoryIdentifier(text: string, identifiers: ReadonlySet<string>): boolean {
20
+ for (const match of text
21
+ .normalize('NFKC')
22
+ .toLowerCase()
23
+ .matchAll(/[\p{L}\p{N}_]+/gu)) {
24
+ if (identifiers.has(match[0])) return true
25
+ }
26
+ return false
27
+ }
30
28
 
31
- results.sort((a, b) => b.updatedAt - a.updatedAt)
29
+ /** Shared lexical ranking; a caller-owned index can omit body content. */
30
+ export function searchMemoryEntries(
31
+ entries: readonly MemoryIndexEntry[],
32
+ params: MemorySearchParams,
33
+ contentOf: (id: MemoryId) => string = () => '',
34
+ ): MemorySearchResult {
35
+ const query = terms(params.query ?? '')
36
+ const identifiers = new Set(
37
+ params.requiredIdentifiers?.map((id) => id.normalize('NFKC').toLowerCase()),
38
+ )
39
+ if (params.query?.trim() && query.size === 0) return { entries: [], totalCount: 0 }
40
+ const ranked = entries
41
+ .filter(
42
+ (entry) =>
43
+ (!params.status || entry.status === params.status) &&
44
+ (!params.tags?.length || params.tags.every((tag) => entry.tags.includes(tag))),
45
+ )
46
+ .filter(
47
+ (entry) =>
48
+ !identifiers.size ||
49
+ [entry.id, entry.title, entry.summary, contentOf(entry.id)].some((text) =>
50
+ matchesMemoryIdentifier(text, identifiers),
51
+ ),
52
+ )
53
+ .map((entry) => {
54
+ let coverage = 0
55
+ let score = 0
56
+ if (query.size > 0) {
57
+ const title = terms(entry.title)
58
+ const summary = terms(entry.summary)
59
+ const body = terms(contentOf(entry.id))
60
+ for (const term of query) {
61
+ const weight =
62
+ (title.has(term) ? 8 : 0) + (summary.has(term) ? 4 : 0) + (body.has(term) ? 1 : 0)
63
+ if (weight > 0) coverage++
64
+ score += weight
65
+ }
66
+ }
67
+ return { entry, coverage, score }
68
+ })
69
+ .filter(({ coverage }) => query.size === 0 || coverage > 0)
70
+ .sort(
71
+ (a, b) =>
72
+ b.coverage - a.coverage ||
73
+ b.score - a.score ||
74
+ b.entry.updatedAt - a.entry.updatedAt ||
75
+ (a.entry.id < b.entry.id ? -1 : a.entry.id > b.entry.id ? 1 : 0),
76
+ )
77
+ return {
78
+ entries: ranked.slice(0, params.limit ?? ranked.length).map(({ entry }) => entry),
79
+ totalCount: ranked.length,
80
+ }
81
+ }
32
82
 
33
- const totalCount = results.length
34
- const limit = params.limit ?? totalCount
83
+ export class InMemoryMemoryIndex implements MemoryIndex {
84
+ private entries = new Map<string, MemoryIndexEntry>()
35
85
 
36
- return {
37
- entries: results.slice(0, limit),
38
- totalCount,
39
- }
86
+ search(params: MemorySearchParams): MemorySearchResult {
87
+ return searchMemoryEntries([...this.entries.values()], params)
40
88
  }
41
89
 
42
90
  getEntry(id: MemoryId): MemoryIndexEntry | undefined {
@@ -3,12 +3,15 @@ import type {
3
3
  CreateMemoryParams,
4
4
  MemoryContent,
5
5
  MemoryIndexEntry,
6
+ MemoryRecord,
6
7
  MemorySearchParams,
7
8
  MemorySearchResult,
8
9
  MemoryStore,
10
+ UpdateMemoryParams,
9
11
  } from '../../types/memory/index.js'
12
+ import { assertMemoryStatus } from '../../types/memory/index.js'
10
13
  import { generateMemoryId } from '../../utils/id.js'
11
- import { InMemoryMemoryIndex } from './index.js'
14
+ import { InMemoryMemoryIndex, searchMemoryEntries } from './index.js'
12
15
 
13
16
  export class InMemoryMemoryStore implements MemoryStore {
14
17
  private content = new Map<string, MemoryContent>()
@@ -47,10 +50,14 @@ export class InMemoryMemoryStore implements MemoryStore {
47
50
  return this.content.get(id)
48
51
  }
49
52
 
50
- async update(
51
- id: MemoryId,
52
- updates: Partial<CreateMemoryParams>,
53
- ): Promise<MemoryIndexEntry | undefined> {
53
+ async getRecord(id: MemoryId): Promise<MemoryRecord | undefined> {
54
+ const entry = this.index.getEntry(id)
55
+ const content = this.content.get(id)
56
+ return entry && content ? structuredClone({ entry, content }) : undefined
57
+ }
58
+
59
+ async update(id: MemoryId, updates: UpdateMemoryParams): Promise<MemoryIndexEntry | undefined> {
60
+ if (updates.status !== undefined) assertMemoryStatus(updates.status)
54
61
  const existing = this.index.getEntry(id)
55
62
  if (!existing) return undefined
56
63
 
@@ -61,6 +68,7 @@ export class InMemoryMemoryStore implements MemoryStore {
61
68
  title: updates.title ?? existing.title,
62
69
  summary: updates.summary ?? existing.summary,
63
70
  tags: updates.tags ? [...updates.tags] : existing.tags,
71
+ status: updates.status ?? existing.status,
64
72
  updatedAt: now,
65
73
  }
66
74
 
@@ -94,7 +102,11 @@ export class InMemoryMemoryStore implements MemoryStore {
94
102
  }
95
103
 
96
104
  async list(params?: MemorySearchParams): Promise<MemorySearchResult> {
97
- return this.index.search(params ?? {})
105
+ return searchMemoryEntries(
106
+ this.index.allEntries(),
107
+ params ?? {},
108
+ (id) => this.content.get(id)?.content ?? '',
109
+ )
98
110
  }
99
111
 
100
112
  getIndex(): InMemoryMemoryIndex {
@@ -0,0 +1,99 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { type FileHandle, lstat, open, readFile, unlink } from 'node:fs/promises'
3
+ import { setTimeout as delay } from 'node:timers/promises'
4
+
5
+ import { NamzuError } from '../../types/errors/index.js'
6
+
7
+ /** Wait bound, not a lease: a slow or crashed owner is never silently replaced. */
8
+ export const DEFAULT_MEMORY_LOCK_TIMEOUT_MS = 10_000
9
+
10
+ export function validateMemoryLockTimeout(timeoutMs: number): void {
11
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
12
+ throw new NamzuError({
13
+ code: 'invalid_config',
14
+ message: 'DiskMemoryStore lockTimeoutMs must be a positive safe integer.',
15
+ retryable: false,
16
+ })
17
+ }
18
+ }
19
+
20
+ function lockError(path: string, reason: string, timeoutMs: number): NamzuError {
21
+ return new NamzuError({
22
+ code: 'storage_error',
23
+ message: `Memory operation lock ${path}: ${reason}. Stop all processes using this memory store and inspect the lock owner before removing a stale lock; it is never broken automatically.`,
24
+ details: { lockPath: path, timeoutMs },
25
+ retryable: false,
26
+ })
27
+ }
28
+
29
+ /**
30
+ * Local-filesystem exclusion for cooperating processes. Atomic rename of the
31
+ * index alone cannot serialize the index/content read-modify-write transaction.
32
+ * An abandoned lock fails closed; guessing a lease expired would permit two
33
+ * live writers to overwrite each other again.
34
+ */
35
+ export async function acquireMemoryOperationLock(
36
+ path: string,
37
+ timeoutMs: number,
38
+ ): Promise<() => Promise<void>> {
39
+ const deadline = performance.now() + timeoutMs
40
+ let attempted = false
41
+ for (;;) {
42
+ if (attempted && performance.now() >= deadline) {
43
+ throw lockError(path, `acquisition timed out after ${timeoutMs} ms`, timeoutMs)
44
+ }
45
+ attempted = true
46
+ let handle: FileHandle
47
+ try {
48
+ handle = await open(path, 'wx', 0o600)
49
+ } catch (error) {
50
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
51
+ const existing = await lstat(path).catch((statError: NodeJS.ErrnoException) => {
52
+ if (statError.code === 'ENOENT') return undefined
53
+ throw statError
54
+ })
55
+ if (!existing) continue
56
+ if (!existing.isFile() || existing.isSymbolicLink()) {
57
+ throw lockError(path, 'the occupied lock is not a regular file', timeoutMs)
58
+ }
59
+ if (performance.now() >= deadline) {
60
+ throw lockError(path, `acquisition timed out after ${timeoutMs} ms`, timeoutMs)
61
+ }
62
+ await delay(Math.min(25, Math.max(1, deadline - performance.now())))
63
+ continue
64
+ }
65
+
66
+ const owner = `${JSON.stringify({ pid: process.pid, token: randomUUID(), acquiredAt: Date.now() })}\n`
67
+ const identity = await handle.stat().catch(async (error: unknown) => {
68
+ await handle.close()
69
+ // Exclusive create happened, but ownership could not be established.
70
+ // Leave the path for explicit recovery rather than unlink an unknown inode.
71
+ throw error
72
+ })
73
+ try {
74
+ await handle.writeFile(owner, 'utf8')
75
+ await handle.sync()
76
+ } catch (error) {
77
+ await handle.close()
78
+ const current = await lstat(path).catch(() => undefined)
79
+ if (current?.dev === identity.dev && current.ino === identity.ino) {
80
+ await unlink(path).catch(() => undefined)
81
+ }
82
+ throw error
83
+ }
84
+ await handle.close()
85
+ return async () => {
86
+ const current = await lstat(path)
87
+ if (
88
+ !current.isFile() ||
89
+ current.isSymbolicLink() ||
90
+ current.dev !== identity.dev ||
91
+ current.ino !== identity.ino ||
92
+ (await readFile(path, 'utf8')) !== owner
93
+ ) {
94
+ throw lockError(path, 'the lock owner changed before release', timeoutMs)
95
+ }
96
+ await unlink(path)
97
+ }
98
+ }
99
+ }
@@ -227,6 +227,9 @@ function assertSameRoot(before: TokenBudgetSnapshot, after: TokenBudgetSnapshot)
227
227
  !current ||
228
228
  current.accountId !== previous.accountId ||
229
229
  current.runId !== previous.runId ||
230
+ (previous.unresolved === true &&
231
+ current.unresolved !== true &&
232
+ (active.has(previous.id) || current.usage === undefined)) ||
230
233
  (reconciled && current.usage === undefined) ||
231
234
  !usageAtLeast(current.usage, previous.usage)
232
235
  ) {