@sema-ai/core 1.275.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (962) hide show
  1. package/CHANGELOG.md +5433 -0
  2. package/LICENSE +27 -0
  3. package/NOTICE.md +30 -0
  4. package/README.md +1040 -0
  5. package/dist/agents/agent-definition.d.ts +10 -0
  6. package/dist/agents/agent-definition.d.ts.map +1 -0
  7. package/dist/agents/agent-definition.js +18 -0
  8. package/dist/agents/agent-definition.js.map +1 -0
  9. package/dist/agents/builtin-agents.d.ts +69 -0
  10. package/dist/agents/builtin-agents.d.ts.map +1 -0
  11. package/dist/agents/builtin-agents.js +177 -0
  12. package/dist/agents/builtin-agents.js.map +1 -0
  13. package/dist/agents/cascade.d.ts +101 -0
  14. package/dist/agents/cascade.d.ts.map +1 -0
  15. package/dist/agents/cascade.js +189 -0
  16. package/dist/agents/cascade.js.map +1 -0
  17. package/dist/agents/observer.d.ts +209 -0
  18. package/dist/agents/observer.d.ts.map +1 -0
  19. package/dist/agents/observer.js +495 -0
  20. package/dist/agents/observer.js.map +1 -0
  21. package/dist/agents/repair-loop.d.ts +236 -0
  22. package/dist/agents/repair-loop.d.ts.map +1 -0
  23. package/dist/agents/repair-loop.js +293 -0
  24. package/dist/agents/repair-loop.js.map +1 -0
  25. package/dist/agents/session-util.d.ts +8 -0
  26. package/dist/agents/session-util.d.ts.map +1 -0
  27. package/dist/agents/session-util.js +9 -0
  28. package/dist/agents/session-util.js.map +1 -0
  29. package/dist/agents/subagent-steps.d.ts +93 -0
  30. package/dist/agents/subagent-steps.d.ts.map +1 -0
  31. package/dist/agents/subagent-steps.js +206 -0
  32. package/dist/agents/subagent-steps.js.map +1 -0
  33. package/dist/agents/subagent.d.ts +441 -0
  34. package/dist/agents/subagent.d.ts.map +1 -0
  35. package/dist/agents/subagent.js +2492 -0
  36. package/dist/agents/subagent.js.map +1 -0
  37. package/dist/agents/suspend-guard.d.ts +35 -0
  38. package/dist/agents/suspend-guard.d.ts.map +1 -0
  39. package/dist/agents/suspend-guard.js +48 -0
  40. package/dist/agents/suspend-guard.js.map +1 -0
  41. package/dist/agents/teacher.d.ts +137 -0
  42. package/dist/agents/teacher.d.ts.map +1 -0
  43. package/dist/agents/teacher.js +575 -0
  44. package/dist/agents/teacher.js.map +1 -0
  45. package/dist/agents/team.d.ts +211 -0
  46. package/dist/agents/team.d.ts.map +1 -0
  47. package/dist/agents/team.js +293 -0
  48. package/dist/agents/team.js.map +1 -0
  49. package/dist/agents/tool-filter.d.ts +29 -0
  50. package/dist/agents/tool-filter.d.ts.map +1 -0
  51. package/dist/agents/tool-filter.js +50 -0
  52. package/dist/agents/tool-filter.js.map +1 -0
  53. package/dist/agents/verify.d.ts +177 -0
  54. package/dist/agents/verify.d.ts.map +1 -0
  55. package/dist/agents/verify.js +305 -0
  56. package/dist/agents/verify.js.map +1 -0
  57. package/dist/bench/metrics.d.ts +714 -0
  58. package/dist/bench/metrics.d.ts.map +1 -0
  59. package/dist/bench/metrics.js +618 -0
  60. package/dist/bench/metrics.js.map +1 -0
  61. package/dist/bin/sema-tb.d.ts +3 -0
  62. package/dist/bin/sema-tb.d.ts.map +1 -0
  63. package/dist/bin/sema-tb.js +516 -0
  64. package/dist/bin/sema-tb.js.map +1 -0
  65. package/dist/bin/tb-env.d.ts +13 -0
  66. package/dist/bin/tb-env.d.ts.map +1 -0
  67. package/dist/bin/tb-env.js +26 -0
  68. package/dist/bin/tb-env.js.map +1 -0
  69. package/dist/brain/anthropic.d.ts +46 -0
  70. package/dist/brain/anthropic.d.ts.map +1 -0
  71. package/dist/brain/anthropic.js +830 -0
  72. package/dist/brain/anthropic.js.map +1 -0
  73. package/dist/brain/circuit-breaker.d.ts +46 -0
  74. package/dist/brain/circuit-breaker.d.ts.map +1 -0
  75. package/dist/brain/circuit-breaker.js +173 -0
  76. package/dist/brain/circuit-breaker.js.map +1 -0
  77. package/dist/brain/degrading.d.ts +98 -0
  78. package/dist/brain/degrading.d.ts.map +1 -0
  79. package/dist/brain/degrading.js +249 -0
  80. package/dist/brain/degrading.js.map +1 -0
  81. package/dist/brain/errors.d.ts +31 -0
  82. package/dist/brain/errors.d.ts.map +1 -0
  83. package/dist/brain/errors.js +58 -0
  84. package/dist/brain/errors.js.map +1 -0
  85. package/dist/brain/failover.d.ts +18 -0
  86. package/dist/brain/failover.d.ts.map +1 -0
  87. package/dist/brain/failover.js +118 -0
  88. package/dist/brain/failover.js.map +1 -0
  89. package/dist/brain/model-presets.d.ts +36 -0
  90. package/dist/brain/model-presets.d.ts.map +1 -0
  91. package/dist/brain/model-presets.js +54 -0
  92. package/dist/brain/model-presets.js.map +1 -0
  93. package/dist/brain/openai.d.ts +43 -0
  94. package/dist/brain/openai.d.ts.map +1 -0
  95. package/dist/brain/openai.js +784 -0
  96. package/dist/brain/openai.js.map +1 -0
  97. package/dist/brain/reasoning.d.ts +138 -0
  98. package/dist/brain/reasoning.d.ts.map +1 -0
  99. package/dist/brain/reasoning.js +146 -0
  100. package/dist/brain/reasoning.js.map +1 -0
  101. package/dist/brain/repetition.d.ts +60 -0
  102. package/dist/brain/repetition.d.ts.map +1 -0
  103. package/dist/brain/repetition.js +121 -0
  104. package/dist/brain/repetition.js.map +1 -0
  105. package/dist/brain/request-params.d.ts +29 -0
  106. package/dist/brain/request-params.d.ts.map +1 -0
  107. package/dist/brain/request-params.js +78 -0
  108. package/dist/brain/request-params.js.map +1 -0
  109. package/dist/brain/retry.d.ts +18 -0
  110. package/dist/brain/retry.d.ts.map +1 -0
  111. package/dist/brain/retry.js +42 -0
  112. package/dist/brain/retry.js.map +1 -0
  113. package/dist/brain/routing.d.ts +19 -0
  114. package/dist/brain/routing.d.ts.map +1 -0
  115. package/dist/brain/routing.js +44 -0
  116. package/dist/brain/routing.js.map +1 -0
  117. package/dist/brain/status-sink.d.ts +46 -0
  118. package/dist/brain/status-sink.d.ts.map +1 -0
  119. package/dist/brain/status-sink.js +36 -0
  120. package/dist/brain/status-sink.js.map +1 -0
  121. package/dist/brain/stream-engine.d.ts +85 -0
  122. package/dist/brain/stream-engine.d.ts.map +1 -0
  123. package/dist/brain/stream-engine.js +391 -0
  124. package/dist/brain/stream-engine.js.map +1 -0
  125. package/dist/brain/timeout.d.ts +44 -0
  126. package/dist/brain/timeout.d.ts.map +1 -0
  127. package/dist/brain/timeout.js +41 -0
  128. package/dist/brain/timeout.js.map +1 -0
  129. package/dist/brain/tool-call-repair.d.ts +15 -0
  130. package/dist/brain/tool-call-repair.d.ts.map +1 -0
  131. package/dist/brain/tool-call-repair.js +67 -0
  132. package/dist/brain/tool-call-repair.js.map +1 -0
  133. package/dist/brain/walltime.d.ts +14 -0
  134. package/dist/brain/walltime.d.ts.map +1 -0
  135. package/dist/brain/walltime.js +14 -0
  136. package/dist/brain/walltime.js.map +1 -0
  137. package/dist/core/arg-summary.d.ts +19 -0
  138. package/dist/core/arg-summary.d.ts.map +1 -0
  139. package/dist/core/arg-summary.js +96 -0
  140. package/dist/core/arg-summary.js.map +1 -0
  141. package/dist/core/ask-question.d.ts +113 -0
  142. package/dist/core/ask-question.d.ts.map +1 -0
  143. package/dist/core/ask-question.js +246 -0
  144. package/dist/core/ask-question.js.map +1 -0
  145. package/dist/core/auto-compaction.d.ts +341 -0
  146. package/dist/core/auto-compaction.d.ts.map +1 -0
  147. package/dist/core/auto-compaction.js +495 -0
  148. package/dist/core/auto-compaction.js.map +1 -0
  149. package/dist/core/auto-promote.d.ts +126 -0
  150. package/dist/core/auto-promote.d.ts.map +1 -0
  151. package/dist/core/auto-promote.js +171 -0
  152. package/dist/core/auto-promote.js.map +1 -0
  153. package/dist/core/background-shell.d.ts +128 -0
  154. package/dist/core/background-shell.d.ts.map +1 -0
  155. package/dist/core/background-shell.js +42 -0
  156. package/dist/core/background-shell.js.map +1 -0
  157. package/dist/core/cache-break-detector.d.ts +63 -0
  158. package/dist/core/cache-break-detector.d.ts.map +1 -0
  159. package/dist/core/cache-break-detector.js +0 -0
  160. package/dist/core/cache-break-detector.js.map +1 -0
  161. package/dist/core/canonical-json.d.ts +45 -0
  162. package/dist/core/canonical-json.d.ts.map +1 -0
  163. package/dist/core/canonical-json.js +132 -0
  164. package/dist/core/canonical-json.js.map +1 -0
  165. package/dist/core/checkpoint-store.d.ts +1101 -0
  166. package/dist/core/checkpoint-store.d.ts.map +1 -0
  167. package/dist/core/checkpoint-store.js +514 -0
  168. package/dist/core/checkpoint-store.js.map +1 -0
  169. package/dist/core/consolidate-scope.d.ts +94 -0
  170. package/dist/core/consolidate-scope.d.ts.map +1 -0
  171. package/dist/core/consolidate-scope.js +158 -0
  172. package/dist/core/consolidate-scope.js.map +1 -0
  173. package/dist/core/context-edit.d.ts +82 -0
  174. package/dist/core/context-edit.d.ts.map +1 -0
  175. package/dist/core/context-edit.js +149 -0
  176. package/dist/core/context-edit.js.map +1 -0
  177. package/dist/core/context-guard.d.ts +43 -0
  178. package/dist/core/context-guard.d.ts.map +1 -0
  179. package/dist/core/context-guard.js +171 -0
  180. package/dist/core/context-guard.js.map +1 -0
  181. package/dist/core/dynamic-recall.d.ts +100 -0
  182. package/dist/core/dynamic-recall.d.ts.map +1 -0
  183. package/dist/core/dynamic-recall.js +108 -0
  184. package/dist/core/dynamic-recall.js.map +1 -0
  185. package/dist/core/exec-gate.d.ts +74 -0
  186. package/dist/core/exec-gate.d.ts.map +1 -0
  187. package/dist/core/exec-gate.js +143 -0
  188. package/dist/core/exec-gate.js.map +1 -0
  189. package/dist/core/exec-output-tail.d.ts +37 -0
  190. package/dist/core/exec-output-tail.d.ts.map +1 -0
  191. package/dist/core/exec-output-tail.js +63 -0
  192. package/dist/core/exec-output-tail.js.map +1 -0
  193. package/dist/core/file-snapshot-store.d.ts +137 -0
  194. package/dist/core/file-snapshot-store.d.ts.map +1 -0
  195. package/dist/core/file-snapshot-store.js +319 -0
  196. package/dist/core/file-snapshot-store.js.map +1 -0
  197. package/dist/core/git-worktree-env.d.ts +41 -0
  198. package/dist/core/git-worktree-env.d.ts.map +1 -0
  199. package/dist/core/git-worktree-env.js +64 -0
  200. package/dist/core/git-worktree-env.js.map +1 -0
  201. package/dist/core/hooks.d.ts +372 -0
  202. package/dist/core/hooks.d.ts.map +1 -0
  203. package/dist/core/hooks.js +269 -0
  204. package/dist/core/hooks.js.map +1 -0
  205. package/dist/core/lsp-diagnostics.d.ts +74 -0
  206. package/dist/core/lsp-diagnostics.d.ts.map +1 -0
  207. package/dist/core/lsp-diagnostics.js +125 -0
  208. package/dist/core/lsp-diagnostics.js.map +1 -0
  209. package/dist/core/lsp-protocol.d.ts +28 -0
  210. package/dist/core/lsp-protocol.d.ts.map +1 -0
  211. package/dist/core/lsp-protocol.js +179 -0
  212. package/dist/core/lsp-protocol.js.map +1 -0
  213. package/dist/core/lsp-session.d.ts +56 -0
  214. package/dist/core/lsp-session.d.ts.map +1 -0
  215. package/dist/core/lsp-session.js +185 -0
  216. package/dist/core/lsp-session.js.map +1 -0
  217. package/dist/core/lsp.d.ts +132 -0
  218. package/dist/core/lsp.d.ts.map +1 -0
  219. package/dist/core/lsp.js +147 -0
  220. package/dist/core/lsp.js.map +1 -0
  221. package/dist/core/mcp.d.ts +294 -0
  222. package/dist/core/mcp.d.ts.map +1 -0
  223. package/dist/core/mcp.js +992 -0
  224. package/dist/core/mcp.js.map +1 -0
  225. package/dist/core/media-byte-cap.d.ts +34 -0
  226. package/dist/core/media-byte-cap.d.ts.map +1 -0
  227. package/dist/core/media-byte-cap.js +76 -0
  228. package/dist/core/media-byte-cap.js.map +1 -0
  229. package/dist/core/memory-engine/data-plane.d.ts +103 -0
  230. package/dist/core/memory-engine/data-plane.d.ts.map +1 -0
  231. package/dist/core/memory-engine/data-plane.js +272 -0
  232. package/dist/core/memory-engine/data-plane.js.map +1 -0
  233. package/dist/core/memory-engine/dual-root.d.ts +40 -0
  234. package/dist/core/memory-engine/dual-root.d.ts.map +1 -0
  235. package/dist/core/memory-engine/dual-root.js +95 -0
  236. package/dist/core/memory-engine/dual-root.js.map +1 -0
  237. package/dist/core/memory-engine/engine.d.ts +161 -0
  238. package/dist/core/memory-engine/engine.d.ts.map +1 -0
  239. package/dist/core/memory-engine/engine.js +1110 -0
  240. package/dist/core/memory-engine/engine.js.map +1 -0
  241. package/dist/core/memory-engine/file-backend.d.ts +162 -0
  242. package/dist/core/memory-engine/file-backend.d.ts.map +1 -0
  243. package/dist/core/memory-engine/file-backend.js +948 -0
  244. package/dist/core/memory-engine/file-backend.js.map +1 -0
  245. package/dist/core/memory-engine/frontmatter.d.ts +24 -0
  246. package/dist/core/memory-engine/frontmatter.d.ts.map +1 -0
  247. package/dist/core/memory-engine/frontmatter.js +135 -0
  248. package/dist/core/memory-engine/frontmatter.js.map +1 -0
  249. package/dist/core/memory-engine/index.d.ts +13 -0
  250. package/dist/core/memory-engine/index.d.ts.map +1 -0
  251. package/dist/core/memory-engine/index.js +28 -0
  252. package/dist/core/memory-engine/index.js.map +1 -0
  253. package/dist/core/memory-engine/layout.d.ts +123 -0
  254. package/dist/core/memory-engine/layout.d.ts.map +1 -0
  255. package/dist/core/memory-engine/layout.js +643 -0
  256. package/dist/core/memory-engine/layout.js.map +1 -0
  257. package/dist/core/memory-engine/memory-backend-contract.d.ts +22 -0
  258. package/dist/core/memory-engine/memory-backend-contract.d.ts.map +1 -0
  259. package/dist/core/memory-engine/memory-backend-contract.js +271 -0
  260. package/dist/core/memory-engine/memory-backend-contract.js.map +1 -0
  261. package/dist/core/memory-engine/migrate.d.ts +17 -0
  262. package/dist/core/memory-engine/migrate.d.ts.map +1 -0
  263. package/dist/core/memory-engine/migrate.js +35 -0
  264. package/dist/core/memory-engine/migrate.js.map +1 -0
  265. package/dist/core/memory-engine/scan.d.ts +20 -0
  266. package/dist/core/memory-engine/scan.d.ts.map +1 -0
  267. package/dist/core/memory-engine/scan.js +84 -0
  268. package/dist/core/memory-engine/scan.js.map +1 -0
  269. package/dist/core/memory-engine/scope-contract.d.ts +104 -0
  270. package/dist/core/memory-engine/scope-contract.d.ts.map +1 -0
  271. package/dist/core/memory-engine/scope-contract.js +288 -0
  272. package/dist/core/memory-engine/scope-contract.js.map +1 -0
  273. package/dist/core/memory-engine/sync-client.d.ts +100 -0
  274. package/dist/core/memory-engine/sync-client.d.ts.map +1 -0
  275. package/dist/core/memory-engine/sync-client.js +328 -0
  276. package/dist/core/memory-engine/sync-client.js.map +1 -0
  277. package/dist/core/memory-engine/sync.d.ts +97 -0
  278. package/dist/core/memory-engine/sync.d.ts.map +1 -0
  279. package/dist/core/memory-engine/sync.js +91 -0
  280. package/dist/core/memory-engine/sync.js.map +1 -0
  281. package/dist/core/memory-engine/types.d.ts +279 -0
  282. package/dist/core/memory-engine/types.d.ts.map +1 -0
  283. package/dist/core/memory-engine/types.js +9 -0
  284. package/dist/core/memory-engine/types.js.map +1 -0
  285. package/dist/core/memory-recall.d.ts +261 -0
  286. package/dist/core/memory-recall.d.ts.map +1 -0
  287. package/dist/core/memory-recall.js +600 -0
  288. package/dist/core/memory-recall.js.map +1 -0
  289. package/dist/core/memory-vector.d.ts +24 -0
  290. package/dist/core/memory-vector.d.ts.map +1 -0
  291. package/dist/core/memory-vector.js +51 -0
  292. package/dist/core/memory-vector.js.map +1 -0
  293. package/dist/core/memory.d.ts +502 -0
  294. package/dist/core/memory.d.ts.map +1 -0
  295. package/dist/core/memory.js +881 -0
  296. package/dist/core/memory.js.map +1 -0
  297. package/dist/core/message-utils.d.ts +7 -0
  298. package/dist/core/message-utils.d.ts.map +1 -0
  299. package/dist/core/message-utils.js +10 -0
  300. package/dist/core/message-utils.js.map +1 -0
  301. package/dist/core/oracle-isolation.d.ts +85 -0
  302. package/dist/core/oracle-isolation.d.ts.map +1 -0
  303. package/dist/core/oracle-isolation.js +121 -0
  304. package/dist/core/oracle-isolation.js.map +1 -0
  305. package/dist/core/permission-rules.d.ts +102 -0
  306. package/dist/core/permission-rules.d.ts.map +1 -0
  307. package/dist/core/permission-rules.js +357 -0
  308. package/dist/core/permission-rules.js.map +1 -0
  309. package/dist/core/present-plan-tool.d.ts +28 -0
  310. package/dist/core/present-plan-tool.d.ts.map +1 -0
  311. package/dist/core/present-plan-tool.js +119 -0
  312. package/dist/core/present-plan-tool.js.map +1 -0
  313. package/dist/core/pricing.d.ts +48 -0
  314. package/dist/core/pricing.d.ts.map +1 -0
  315. package/dist/core/pricing.js +46 -0
  316. package/dist/core/pricing.js.map +1 -0
  317. package/dist/core/property-harness.d.ts +126 -0
  318. package/dist/core/property-harness.d.ts.map +1 -0
  319. package/dist/core/property-harness.js +192 -0
  320. package/dist/core/property-harness.js.map +1 -0
  321. package/dist/core/push-queue.d.ts +10 -0
  322. package/dist/core/push-queue.d.ts.map +1 -0
  323. package/dist/core/push-queue.js +42 -0
  324. package/dist/core/push-queue.js.map +1 -0
  325. package/dist/core/remote-env.d.ts +432 -0
  326. package/dist/core/remote-env.d.ts.map +1 -0
  327. package/dist/core/remote-env.js +77 -0
  328. package/dist/core/remote-env.js.map +1 -0
  329. package/dist/core/roles.d.ts +74 -0
  330. package/dist/core/roles.d.ts.map +1 -0
  331. package/dist/core/roles.js +272 -0
  332. package/dist/core/roles.js.map +1 -0
  333. package/dist/core/runner/active-skill-scope.d.ts +64 -0
  334. package/dist/core/runner/active-skill-scope.d.ts.map +1 -0
  335. package/dist/core/runner/active-skill-scope.js +197 -0
  336. package/dist/core/runner/active-skill-scope.js.map +1 -0
  337. package/dist/core/runner/assemble-result.d.ts +191 -0
  338. package/dist/core/runner/assemble-result.d.ts.map +1 -0
  339. package/dist/core/runner/assemble-result.js +250 -0
  340. package/dist/core/runner/assemble-result.js.map +1 -0
  341. package/dist/core/runner/call-cap.d.ts +130 -0
  342. package/dist/core/runner/call-cap.d.ts.map +1 -0
  343. package/dist/core/runner/call-cap.js +151 -0
  344. package/dist/core/runner/call-cap.js.map +1 -0
  345. package/dist/core/runner/grounding-signal.d.ts +12 -0
  346. package/dist/core/runner/grounding-signal.d.ts.map +1 -0
  347. package/dist/core/runner/grounding-signal.js +94 -0
  348. package/dist/core/runner/grounding-signal.js.map +1 -0
  349. package/dist/core/runner/image.d.ts +23 -0
  350. package/dist/core/runner/image.d.ts.map +1 -0
  351. package/dist/core/runner/image.js +151 -0
  352. package/dist/core/runner/image.js.map +1 -0
  353. package/dist/core/runner/memory-consolidation.d.ts +112 -0
  354. package/dist/core/runner/memory-consolidation.d.ts.map +1 -0
  355. package/dist/core/runner/memory-consolidation.js +372 -0
  356. package/dist/core/runner/memory-consolidation.js.map +1 -0
  357. package/dist/core/runner/prepare-task.d.ts +583 -0
  358. package/dist/core/runner/prepare-task.d.ts.map +1 -0
  359. package/dist/core/runner/prepare-task.js +3612 -0
  360. package/dist/core/runner/prepare-task.js.map +1 -0
  361. package/dist/core/runner/prompt-suggestions.d.ts +33 -0
  362. package/dist/core/runner/prompt-suggestions.d.ts.map +1 -0
  363. package/dist/core/runner/prompt-suggestions.js +116 -0
  364. package/dist/core/runner/prompt-suggestions.js.map +1 -0
  365. package/dist/core/runner/runtask.d.ts +274 -0
  366. package/dist/core/runner/runtask.d.ts.map +1 -0
  367. package/dist/core/runner/runtask.js +4606 -0
  368. package/dist/core/runner/runtask.js.map +1 -0
  369. package/dist/core/runner/session-rule-policy.d.ts +28 -0
  370. package/dist/core/runner/session-rule-policy.d.ts.map +1 -0
  371. package/dist/core/runner/session-rule-policy.js +110 -0
  372. package/dist/core/runner/session-rule-policy.js.map +1 -0
  373. package/dist/core/runner/strict-output-schema.d.ts +36 -0
  374. package/dist/core/runner/strict-output-schema.d.ts.map +1 -0
  375. package/dist/core/runner/strict-output-schema.js +365 -0
  376. package/dist/core/runner/strict-output-schema.js.map +1 -0
  377. package/dist/core/runner/synthetic-tools.d.ts +72 -0
  378. package/dist/core/runner/synthetic-tools.d.ts.map +1 -0
  379. package/dist/core/runner/synthetic-tools.js +203 -0
  380. package/dist/core/runner/synthetic-tools.js.map +1 -0
  381. package/dist/core/runner/tool-disclosure.d.ts +117 -0
  382. package/dist/core/runner/tool-disclosure.d.ts.map +1 -0
  383. package/dist/core/runner/tool-disclosure.js +0 -0
  384. package/dist/core/runner/tool-disclosure.js.map +1 -0
  385. package/dist/core/runner/turn-attachments.d.ts +381 -0
  386. package/dist/core/runner/turn-attachments.d.ts.map +1 -0
  387. package/dist/core/runner/turn-attachments.js +596 -0
  388. package/dist/core/runner/turn-attachments.js.map +1 -0
  389. package/dist/core/runner/usage-accounting.d.ts +35 -0
  390. package/dist/core/runner/usage-accounting.d.ts.map +1 -0
  391. package/dist/core/runner/usage-accounting.js +45 -0
  392. package/dist/core/runner/usage-accounting.js.map +1 -0
  393. package/dist/core/runtime.d.ts +9 -0
  394. package/dist/core/runtime.d.ts.map +1 -0
  395. package/dist/core/runtime.js +17 -0
  396. package/dist/core/runtime.js.map +1 -0
  397. package/dist/core/safety-axis-vocab.d.ts +33 -0
  398. package/dist/core/safety-axis-vocab.d.ts.map +1 -0
  399. package/dist/core/safety-axis-vocab.js +35 -0
  400. package/dist/core/safety-axis-vocab.js.map +1 -0
  401. package/dist/core/safety-merge-corpus.d.ts +48 -0
  402. package/dist/core/safety-merge-corpus.d.ts.map +1 -0
  403. package/dist/core/safety-merge-corpus.js +203 -0
  404. package/dist/core/safety-merge-corpus.js.map +1 -0
  405. package/dist/core/scheduler.d.ts +126 -0
  406. package/dist/core/scheduler.d.ts.map +1 -0
  407. package/dist/core/scheduler.js +54 -0
  408. package/dist/core/scheduler.js.map +1 -0
  409. package/dist/core/secret-env.d.ts +23 -0
  410. package/dist/core/secret-env.d.ts.map +1 -0
  411. package/dist/core/secret-env.js +35 -0
  412. package/dist/core/secret-env.js.map +1 -0
  413. package/dist/core/select-model.d.ts +26 -0
  414. package/dist/core/select-model.d.ts.map +1 -0
  415. package/dist/core/select-model.js +58 -0
  416. package/dist/core/select-model.js.map +1 -0
  417. package/dist/core/sensitive-path-policy.d.ts +24 -0
  418. package/dist/core/sensitive-path-policy.d.ts.map +1 -0
  419. package/dist/core/sensitive-path-policy.js +96 -0
  420. package/dist/core/sensitive-path-policy.js.map +1 -0
  421. package/dist/core/session-policy-store.d.ts +102 -0
  422. package/dist/core/session-policy-store.d.ts.map +1 -0
  423. package/dist/core/session-policy-store.js +177 -0
  424. package/dist/core/session-policy-store.js.map +1 -0
  425. package/dist/core/session-reconcile.d.ts +63 -0
  426. package/dist/core/session-reconcile.d.ts.map +1 -0
  427. package/dist/core/session-reconcile.js +71 -0
  428. package/dist/core/session-reconcile.js.map +1 -0
  429. package/dist/core/session-store.d.ts +99 -0
  430. package/dist/core/session-store.d.ts.map +1 -0
  431. package/dist/core/session-store.js +298 -0
  432. package/dist/core/session-store.js.map +1 -0
  433. package/dist/core/session.d.ts +144 -0
  434. package/dist/core/session.d.ts.map +1 -0
  435. package/dist/core/session.js +56 -0
  436. package/dist/core/session.js.map +1 -0
  437. package/dist/core/spec-contract.d.ts +147 -0
  438. package/dist/core/spec-contract.d.ts.map +1 -0
  439. package/dist/core/spec-contract.js +340 -0
  440. package/dist/core/spec-contract.js.map +1 -0
  441. package/dist/core/strategy-store.d.ts +50 -0
  442. package/dist/core/strategy-store.d.ts.map +1 -0
  443. package/dist/core/strategy-store.js +100 -0
  444. package/dist/core/strategy-store.js.map +1 -0
  445. package/dist/core/stub-env.d.ts +35 -0
  446. package/dist/core/stub-env.d.ts.map +1 -0
  447. package/dist/core/stub-env.js +69 -0
  448. package/dist/core/stub-env.js.map +1 -0
  449. package/dist/core/task-notification.d.ts +115 -0
  450. package/dist/core/task-notification.d.ts.map +1 -0
  451. package/dist/core/task-notification.js +247 -0
  452. package/dist/core/task-notification.js.map +1 -0
  453. package/dist/core/task-outcome.d.ts +80 -0
  454. package/dist/core/task-outcome.d.ts.map +1 -0
  455. package/dist/core/task-outcome.js +40 -0
  456. package/dist/core/task-outcome.js.map +1 -0
  457. package/dist/core/task-registry.d.ts +349 -0
  458. package/dist/core/task-registry.d.ts.map +1 -0
  459. package/dist/core/task-registry.js +1751 -0
  460. package/dist/core/task-registry.js.map +1 -0
  461. package/dist/core/tighten-task-spec.d.ts +27 -0
  462. package/dist/core/tighten-task-spec.d.ts.map +1 -0
  463. package/dist/core/tighten-task-spec.js +95 -0
  464. package/dist/core/tighten-task-spec.js.map +1 -0
  465. package/dist/core/tool-detach.d.ts +31 -0
  466. package/dist/core/tool-detach.d.ts.map +1 -0
  467. package/dist/core/tool-detach.js +62 -0
  468. package/dist/core/tool-detach.js.map +1 -0
  469. package/dist/core/tool-errors.d.ts +72 -0
  470. package/dist/core/tool-errors.d.ts.map +1 -0
  471. package/dist/core/tool-errors.js +194 -0
  472. package/dist/core/tool-errors.js.map +1 -0
  473. package/dist/core/tool-name-aliases.d.ts +5 -0
  474. package/dist/core/tool-name-aliases.d.ts.map +1 -0
  475. package/dist/core/tool-name-aliases.js +130 -0
  476. package/dist/core/tool-name-aliases.js.map +1 -0
  477. package/dist/core/tool-policy.d.ts +295 -0
  478. package/dist/core/tool-policy.d.ts.map +1 -0
  479. package/dist/core/tool-policy.js +567 -0
  480. package/dist/core/tool-policy.js.map +1 -0
  481. package/dist/core/tool-result-budget.d.ts +44 -0
  482. package/dist/core/tool-result-budget.d.ts.map +1 -0
  483. package/dist/core/tool-result-budget.js +140 -0
  484. package/dist/core/tool-result-budget.js.map +1 -0
  485. package/dist/core/tool-result-store.d.ts +136 -0
  486. package/dist/core/tool-result-store.d.ts.map +1 -0
  487. package/dist/core/tool-result-store.js +219 -0
  488. package/dist/core/tool-result-store.js.map +1 -0
  489. package/dist/core/tools.d.ts +14 -0
  490. package/dist/core/tools.d.ts.map +1 -0
  491. package/dist/core/tools.js +98 -0
  492. package/dist/core/tools.js.map +1 -0
  493. package/dist/core/trace.d.ts +340 -0
  494. package/dist/core/trace.d.ts.map +1 -0
  495. package/dist/core/trace.js +12 -0
  496. package/dist/core/trace.js.map +1 -0
  497. package/dist/core/types.d.ts +2828 -0
  498. package/dist/core/types.d.ts.map +1 -0
  499. package/dist/core/types.js +2 -0
  500. package/dist/core/types.js.map +1 -0
  501. package/dist/core/untrusted-egress.d.ts +32 -0
  502. package/dist/core/untrusted-egress.d.ts.map +1 -0
  503. package/dist/core/untrusted-egress.js +49 -0
  504. package/dist/core/untrusted-egress.js.map +1 -0
  505. package/dist/core/untrusted-text.d.ts +64 -0
  506. package/dist/core/untrusted-text.d.ts.map +1 -0
  507. package/dist/core/untrusted-text.js +119 -0
  508. package/dist/core/untrusted-text.js.map +1 -0
  509. package/dist/core/warm-resume.d.ts +23 -0
  510. package/dist/core/warm-resume.d.ts.map +1 -0
  511. package/dist/core/warm-resume.js +28 -0
  512. package/dist/core/warm-resume.js.map +1 -0
  513. package/dist/core/with-retry.d.ts +43 -0
  514. package/dist/core/with-retry.d.ts.map +1 -0
  515. package/dist/core/with-retry.js +59 -0
  516. package/dist/core/with-retry.js.map +1 -0
  517. package/dist/core/workflow-journal-store.d.ts +44 -0
  518. package/dist/core/workflow-journal-store.d.ts.map +1 -0
  519. package/dist/core/workflow-journal-store.js +47 -0
  520. package/dist/core/workflow-journal-store.js.map +1 -0
  521. package/dist/core/workflow-run-store-contract.d.ts +31 -0
  522. package/dist/core/workflow-run-store-contract.d.ts.map +1 -0
  523. package/dist/core/workflow-run-store-contract.js +191 -0
  524. package/dist/core/workflow-run-store-contract.js.map +1 -0
  525. package/dist/core/workflow-run-store.d.ts +149 -0
  526. package/dist/core/workflow-run-store.d.ts.map +1 -0
  527. package/dist/core/workflow-run-store.js +110 -0
  528. package/dist/core/workflow-run-store.js.map +1 -0
  529. package/dist/engine/compaction/compaction.d.ts +139 -0
  530. package/dist/engine/compaction/compaction.d.ts.map +1 -0
  531. package/dist/engine/compaction/compaction.js +1030 -0
  532. package/dist/engine/compaction/compaction.js.map +1 -0
  533. package/dist/engine/compaction/utils.d.ts +45 -0
  534. package/dist/engine/compaction/utils.d.ts.map +1 -0
  535. package/dist/engine/compaction/utils.js +203 -0
  536. package/dist/engine/compaction/utils.js.map +1 -0
  537. package/dist/engine/execution-env/kill-tree.d.ts +20 -0
  538. package/dist/engine/execution-env/kill-tree.d.ts.map +1 -0
  539. package/dist/engine/execution-env/kill-tree.js +183 -0
  540. package/dist/engine/execution-env/kill-tree.js.map +1 -0
  541. package/dist/engine/execution-env/node-execution-env.d.ts +133 -0
  542. package/dist/engine/execution-env/node-execution-env.d.ts.map +1 -0
  543. package/dist/engine/execution-env/node-execution-env.js +1031 -0
  544. package/dist/engine/execution-env/node-execution-env.js.map +1 -0
  545. package/dist/engine/harness/agent-harness.d.ts +174 -0
  546. package/dist/engine/harness/agent-harness.d.ts.map +1 -0
  547. package/dist/engine/harness/agent-harness.js +1053 -0
  548. package/dist/engine/harness/agent-harness.js.map +1 -0
  549. package/dist/engine/harness/messages.d.ts +17 -0
  550. package/dist/engine/harness/messages.d.ts.map +1 -0
  551. package/dist/engine/harness/messages.js +122 -0
  552. package/dist/engine/harness/messages.js.map +1 -0
  553. package/dist/engine/harness/types.d.ts +713 -0
  554. package/dist/engine/harness/types.d.ts.map +1 -0
  555. package/dist/engine/harness/types.js +100 -0
  556. package/dist/engine/harness/types.js.map +1 -0
  557. package/dist/engine/llm/diagnostics.d.ts +23 -0
  558. package/dist/engine/llm/diagnostics.d.ts.map +1 -0
  559. package/dist/engine/llm/diagnostics.js +32 -0
  560. package/dist/engine/llm/diagnostics.js.map +1 -0
  561. package/dist/engine/llm/event-stream.d.ts +25 -0
  562. package/dist/engine/llm/event-stream.d.ts.map +1 -0
  563. package/dist/engine/llm/event-stream.js +103 -0
  564. package/dist/engine/llm/event-stream.js.map +1 -0
  565. package/dist/engine/llm/index.d.ts +12 -0
  566. package/dist/engine/llm/index.d.ts.map +1 -0
  567. package/dist/engine/llm/index.js +12 -0
  568. package/dist/engine/llm/index.js.map +1 -0
  569. package/dist/engine/llm/types.d.ts +753 -0
  570. package/dist/engine/llm/types.d.ts.map +1 -0
  571. package/dist/engine/llm/types.js +2 -0
  572. package/dist/engine/llm/types.js.map +1 -0
  573. package/dist/engine/llm/validation.d.ts +8 -0
  574. package/dist/engine/llm/validation.d.ts.map +1 -0
  575. package/dist/engine/llm/validation.js +318 -0
  576. package/dist/engine/llm/validation.js.map +1 -0
  577. package/dist/engine/loop/agent-loop.d.ts +74 -0
  578. package/dist/engine/loop/agent-loop.d.ts.map +1 -0
  579. package/dist/engine/loop/agent-loop.js +1490 -0
  580. package/dist/engine/loop/agent-loop.js.map +1 -0
  581. package/dist/engine/loop/runtime-deps.d.ts +17 -0
  582. package/dist/engine/loop/runtime-deps.d.ts.map +1 -0
  583. package/dist/engine/loop/runtime-deps.js +21 -0
  584. package/dist/engine/loop/runtime-deps.js.map +1 -0
  585. package/dist/engine/loop/types.d.ts +609 -0
  586. package/dist/engine/loop/types.d.ts.map +1 -0
  587. package/dist/engine/loop/types.js +2 -0
  588. package/dist/engine/loop/types.js.map +1 -0
  589. package/dist/engine/lsp/frame-decoder.d.ts +16 -0
  590. package/dist/engine/lsp/frame-decoder.d.ts.map +1 -0
  591. package/dist/engine/lsp/frame-decoder.js +53 -0
  592. package/dist/engine/lsp/frame-decoder.js.map +1 -0
  593. package/dist/engine/lsp/node-lsp-manager.d.ts +105 -0
  594. package/dist/engine/lsp/node-lsp-manager.d.ts.map +1 -0
  595. package/dist/engine/lsp/node-lsp-manager.js +343 -0
  596. package/dist/engine/lsp/node-lsp-manager.js.map +1 -0
  597. package/dist/engine/lsp/stdio-lsp-transport.d.ts +37 -0
  598. package/dist/engine/lsp/stdio-lsp-transport.d.ts.map +1 -0
  599. package/dist/engine/lsp/stdio-lsp-transport.js +185 -0
  600. package/dist/engine/lsp/stdio-lsp-transport.js.map +1 -0
  601. package/dist/engine/session/import-validate.d.ts +44 -0
  602. package/dist/engine/session/import-validate.d.ts.map +1 -0
  603. package/dist/engine/session/import-validate.js +92 -0
  604. package/dist/engine/session/import-validate.js.map +1 -0
  605. package/dist/engine/session/memory-repo.d.ts +25 -0
  606. package/dist/engine/session/memory-repo.d.ts.map +1 -0
  607. package/dist/engine/session/memory-repo.js +59 -0
  608. package/dist/engine/session/memory-repo.js.map +1 -0
  609. package/dist/engine/session/memory-storage.d.ts +14 -0
  610. package/dist/engine/session/memory-storage.d.ts.map +1 -0
  611. package/dist/engine/session/memory-storage.js +17 -0
  612. package/dist/engine/session/memory-storage.js.map +1 -0
  613. package/dist/engine/session/repo-utils.d.ts +11 -0
  614. package/dist/engine/session/repo-utils.d.ts.map +1 -0
  615. package/dist/engine/session/repo-utils.js +40 -0
  616. package/dist/engine/session/repo-utils.js.map +1 -0
  617. package/dist/engine/session/session.d.ts +34 -0
  618. package/dist/engine/session/session.d.ts.map +1 -0
  619. package/dist/engine/session/session.js +227 -0
  620. package/dist/engine/session/session.js.map +1 -0
  621. package/dist/engine/session/storage-base.d.ts +38 -0
  622. package/dist/engine/session/storage-base.d.ts.map +1 -0
  623. package/dist/engine/session/storage-base.js +157 -0
  624. package/dist/engine/session/storage-base.js.map +1 -0
  625. package/dist/engine/session/timestamps.d.ts +3 -0
  626. package/dist/engine/session/timestamps.d.ts.map +1 -0
  627. package/dist/engine/session/timestamps.js +15 -0
  628. package/dist/engine/session/timestamps.js.map +1 -0
  629. package/dist/engine/session/uuid.d.ts +2 -0
  630. package/dist/engine/session/uuid.d.ts.map +1 -0
  631. package/dist/engine/session/uuid.js +50 -0
  632. package/dist/engine/session/uuid.js.map +1 -0
  633. package/dist/index.d.ts +160 -0
  634. package/dist/index.d.ts.map +1 -0
  635. package/dist/index.js +374 -0
  636. package/dist/index.js.map +1 -0
  637. package/dist/internal/harness.d.ts +26 -0
  638. package/dist/internal/harness.d.ts.map +1 -0
  639. package/dist/internal/harness.js +24 -0
  640. package/dist/internal/harness.js.map +1 -0
  641. package/dist/internal/llm.d.ts +9 -0
  642. package/dist/internal/llm.d.ts.map +1 -0
  643. package/dist/internal/llm.js +8 -0
  644. package/dist/internal/llm.js.map +1 -0
  645. package/dist/orchestration/builtin-workflows.d.ts +44 -0
  646. package/dist/orchestration/builtin-workflows.d.ts.map +1 -0
  647. package/dist/orchestration/builtin-workflows.js +165 -0
  648. package/dist/orchestration/builtin-workflows.js.map +1 -0
  649. package/dist/orchestration/dev-vm-script-runner.d.ts +24 -0
  650. package/dist/orchestration/dev-vm-script-runner.d.ts.map +1 -0
  651. package/dist/orchestration/dev-vm-script-runner.js +106 -0
  652. package/dist/orchestration/dev-vm-script-runner.js.map +1 -0
  653. package/dist/orchestration/goal.d.ts +97 -0
  654. package/dist/orchestration/goal.d.ts.map +1 -0
  655. package/dist/orchestration/goal.js +248 -0
  656. package/dist/orchestration/goal.js.map +1 -0
  657. package/dist/orchestration/run-spec.d.ts +63 -0
  658. package/dist/orchestration/run-spec.d.ts.map +1 -0
  659. package/dist/orchestration/run-spec.js +274 -0
  660. package/dist/orchestration/run-spec.js.map +1 -0
  661. package/dist/orchestration/run-workflow-tool.d.ts +187 -0
  662. package/dist/orchestration/run-workflow-tool.d.ts.map +1 -0
  663. package/dist/orchestration/run-workflow-tool.js +612 -0
  664. package/dist/orchestration/run-workflow-tool.js.map +1 -0
  665. package/dist/orchestration/workflow-governance.d.ts +70 -0
  666. package/dist/orchestration/workflow-governance.d.ts.map +1 -0
  667. package/dist/orchestration/workflow-governance.js +176 -0
  668. package/dist/orchestration/workflow-governance.js.map +1 -0
  669. package/dist/orchestration/workflow-meta.d.ts +41 -0
  670. package/dist/orchestration/workflow-meta.d.ts.map +1 -0
  671. package/dist/orchestration/workflow-meta.js +426 -0
  672. package/dist/orchestration/workflow-meta.js.map +1 -0
  673. package/dist/orchestration/workflow-observe.d.ts +77 -0
  674. package/dist/orchestration/workflow-observe.d.ts.map +1 -0
  675. package/dist/orchestration/workflow-observe.js +177 -0
  676. package/dist/orchestration/workflow-observe.js.map +1 -0
  677. package/dist/orchestration/workflow-primitives.d.ts +31 -0
  678. package/dist/orchestration/workflow-primitives.d.ts.map +1 -0
  679. package/dist/orchestration/workflow-primitives.js +104 -0
  680. package/dist/orchestration/workflow-primitives.js.map +1 -0
  681. package/dist/orchestration/workflow-sandbox-conformance.d.ts +34 -0
  682. package/dist/orchestration/workflow-sandbox-conformance.d.ts.map +1 -0
  683. package/dist/orchestration/workflow-sandbox-conformance.js +301 -0
  684. package/dist/orchestration/workflow-sandbox-conformance.js.map +1 -0
  685. package/dist/orchestration/workflow-script-runner.d.ts +127 -0
  686. package/dist/orchestration/workflow-script-runner.d.ts.map +1 -0
  687. package/dist/orchestration/workflow-script-runner.js +48 -0
  688. package/dist/orchestration/workflow-script-runner.js.map +1 -0
  689. package/dist/orchestration/workflow-script-store.d.ts +99 -0
  690. package/dist/orchestration/workflow-script-store.d.ts.map +1 -0
  691. package/dist/orchestration/workflow-script-store.js +133 -0
  692. package/dist/orchestration/workflow-script-store.js.map +1 -0
  693. package/dist/orchestration/workflow-size-guideline.d.ts +50 -0
  694. package/dist/orchestration/workflow-size-guideline.d.ts.map +1 -0
  695. package/dist/orchestration/workflow-size-guideline.js +78 -0
  696. package/dist/orchestration/workflow-size-guideline.js.map +1 -0
  697. package/dist/orchestration/workflow.d.ts +624 -0
  698. package/dist/orchestration/workflow.d.ts.map +1 -0
  699. package/dist/orchestration/workflow.js +1596 -0
  700. package/dist/orchestration/workflow.js.map +1 -0
  701. package/dist/prompts/default.d.ts +474 -0
  702. package/dist/prompts/default.d.ts.map +1 -0
  703. package/dist/prompts/default.js +676 -0
  704. package/dist/prompts/default.js.map +1 -0
  705. package/dist/prompts/supervisor.d.ts +60 -0
  706. package/dist/prompts/supervisor.d.ts.map +1 -0
  707. package/dist/prompts/supervisor.js +162 -0
  708. package/dist/prompts/supervisor.js.map +1 -0
  709. package/dist/scenarios/env.d.ts +41 -0
  710. package/dist/scenarios/env.d.ts.map +1 -0
  711. package/dist/scenarios/env.js +65 -0
  712. package/dist/scenarios/env.js.map +1 -0
  713. package/dist/scenarios/full-body.d.ts +47 -0
  714. package/dist/scenarios/full-body.d.ts.map +1 -0
  715. package/dist/scenarios/full-body.js +36 -0
  716. package/dist/scenarios/full-body.js.map +1 -0
  717. package/dist/scenarios/scenario-registry.d.ts +107 -0
  718. package/dist/scenarios/scenario-registry.d.ts.map +1 -0
  719. package/dist/scenarios/scenario-registry.js +192 -0
  720. package/dist/scenarios/scenario-registry.js.map +1 -0
  721. package/dist/scenarios/teacher-quickstart.d.ts +41 -0
  722. package/dist/scenarios/teacher-quickstart.d.ts.map +1 -0
  723. package/dist/scenarios/teacher-quickstart.js +32 -0
  724. package/dist/scenarios/teacher-quickstart.js.map +1 -0
  725. package/dist/server/http.d.ts +36 -0
  726. package/dist/server/http.d.ts.map +1 -0
  727. package/dist/server/http.js +144 -0
  728. package/dist/server/http.js.map +1 -0
  729. package/dist/stores/file/checkpoint-store.d.ts +62 -0
  730. package/dist/stores/file/checkpoint-store.d.ts.map +1 -0
  731. package/dist/stores/file/checkpoint-store.js +250 -0
  732. package/dist/stores/file/checkpoint-store.js.map +1 -0
  733. package/dist/stores/file/file-snapshot-store.d.ts +43 -0
  734. package/dist/stores/file/file-snapshot-store.d.ts.map +1 -0
  735. package/dist/stores/file/file-snapshot-store.js +374 -0
  736. package/dist/stores/file/file-snapshot-store.js.map +1 -0
  737. package/dist/stores/file/fs-atomic.d.ts +95 -0
  738. package/dist/stores/file/fs-atomic.d.ts.map +1 -0
  739. package/dist/stores/file/fs-atomic.js +415 -0
  740. package/dist/stores/file/fs-atomic.js.map +1 -0
  741. package/dist/stores/file/index.d.ts +98 -0
  742. package/dist/stores/file/index.d.ts.map +1 -0
  743. package/dist/stores/file/index.js +109 -0
  744. package/dist/stores/file/index.js.map +1 -0
  745. package/dist/stores/file/memory-store.d.ts +95 -0
  746. package/dist/stores/file/memory-store.d.ts.map +1 -0
  747. package/dist/stores/file/memory-store.js +393 -0
  748. package/dist/stores/file/memory-store.js.map +1 -0
  749. package/dist/stores/file/session-policy-store.d.ts +24 -0
  750. package/dist/stores/file/session-policy-store.d.ts.map +1 -0
  751. package/dist/stores/file/session-policy-store.js +123 -0
  752. package/dist/stores/file/session-policy-store.js.map +1 -0
  753. package/dist/stores/file/session-store.d.ts +34 -0
  754. package/dist/stores/file/session-store.d.ts.map +1 -0
  755. package/dist/stores/file/session-store.js +175 -0
  756. package/dist/stores/file/session-store.js.map +1 -0
  757. package/dist/stores/file/tool-result-store.d.ts +23 -0
  758. package/dist/stores/file/tool-result-store.d.ts.map +1 -0
  759. package/dist/stores/file/tool-result-store.js +61 -0
  760. package/dist/stores/file/tool-result-store.js.map +1 -0
  761. package/dist/stores/file/workflow-journal-store.d.ts +36 -0
  762. package/dist/stores/file/workflow-journal-store.d.ts.map +1 -0
  763. package/dist/stores/file/workflow-journal-store.js +157 -0
  764. package/dist/stores/file/workflow-journal-store.js.map +1 -0
  765. package/dist/stores/file/workflow-run-store.d.ts +58 -0
  766. package/dist/stores/file/workflow-run-store.d.ts.map +1 -0
  767. package/dist/stores/file/workflow-run-store.js +178 -0
  768. package/dist/stores/file/workflow-run-store.js.map +1 -0
  769. package/dist/stores/pg.d.ts +210 -0
  770. package/dist/stores/pg.d.ts.map +1 -0
  771. package/dist/stores/pg.js +652 -0
  772. package/dist/stores/pg.js.map +1 -0
  773. package/dist/tools/fs/encoding.d.ts +51 -0
  774. package/dist/tools/fs/encoding.d.ts.map +1 -0
  775. package/dist/tools/fs/encoding.js +74 -0
  776. package/dist/tools/fs/encoding.js.map +1 -0
  777. package/dist/tools/fs/gh-rate-limit.d.ts +19 -0
  778. package/dist/tools/fs/gh-rate-limit.d.ts.map +1 -0
  779. package/dist/tools/fs/gh-rate-limit.js +35 -0
  780. package/dist/tools/fs/gh-rate-limit.js.map +1 -0
  781. package/dist/tools/fs/index.d.ts +246 -0
  782. package/dist/tools/fs/index.d.ts.map +1 -0
  783. package/dist/tools/fs/index.js +2130 -0
  784. package/dist/tools/fs/index.js.map +1 -0
  785. package/dist/tools/fs/pdf.d.ts +106 -0
  786. package/dist/tools/fs/pdf.d.ts.map +1 -0
  787. package/dist/tools/fs/pdf.js +178 -0
  788. package/dist/tools/fs/pdf.js.map +1 -0
  789. package/dist/tools/fs/repo-map.d.ts +15 -0
  790. package/dist/tools/fs/repo-map.d.ts.map +1 -0
  791. package/dist/tools/fs/repo-map.js +251 -0
  792. package/dist/tools/fs/repo-map.js.map +1 -0
  793. package/dist/tools/fs/safety.d.ts +168 -0
  794. package/dist/tools/fs/safety.d.ts.map +1 -0
  795. package/dist/tools/fs/safety.js +479 -0
  796. package/dist/tools/fs/safety.js.map +1 -0
  797. package/dist/tools/fs/search.d.ts +118 -0
  798. package/dist/tools/fs/search.d.ts.map +1 -0
  799. package/dist/tools/fs/search.js +650 -0
  800. package/dist/tools/fs/search.js.map +1 -0
  801. package/dist/tools/gitea-issue.d.ts +39 -0
  802. package/dist/tools/gitea-issue.d.ts.map +1 -0
  803. package/dist/tools/gitea-issue.js +91 -0
  804. package/dist/tools/gitea-issue.js.map +1 -0
  805. package/dist/tools/monitor.d.ts +64 -0
  806. package/dist/tools/monitor.d.ts.map +1 -0
  807. package/dist/tools/monitor.js +135 -0
  808. package/dist/tools/monitor.js.map +1 -0
  809. package/dist/tools/scheduler-tools.d.ts +48 -0
  810. package/dist/tools/scheduler-tools.d.ts.map +1 -0
  811. package/dist/tools/scheduler-tools.js +449 -0
  812. package/dist/tools/scheduler-tools.js.map +1 -0
  813. package/dist/tools/sql-adapters.d.ts +32 -0
  814. package/dist/tools/sql-adapters.d.ts.map +1 -0
  815. package/dist/tools/sql-adapters.js +31 -0
  816. package/dist/tools/sql-adapters.js.map +1 -0
  817. package/dist/tools/sql.d.ts +36 -0
  818. package/dist/tools/sql.d.ts.map +1 -0
  819. package/dist/tools/sql.js +131 -0
  820. package/dist/tools/sql.js.map +1 -0
  821. package/dist/tools/task-list.d.ts +26 -0
  822. package/dist/tools/task-list.d.ts.map +1 -0
  823. package/dist/tools/task-list.js +352 -0
  824. package/dist/tools/task-list.js.map +1 -0
  825. package/dist/tools/todo.d.ts +11 -0
  826. package/dist/tools/todo.d.ts.map +1 -0
  827. package/dist/tools/todo.js +126 -0
  828. package/dist/tools/todo.js.map +1 -0
  829. package/dist/tools/web.d.ts +59 -0
  830. package/dist/tools/web.d.ts.map +1 -0
  831. package/dist/tools/web.js +607 -0
  832. package/dist/tools/web.js.map +1 -0
  833. package/dist/tools/worktree.d.ts +88 -0
  834. package/dist/tools/worktree.d.ts.map +1 -0
  835. package/dist/tools/worktree.js +434 -0
  836. package/dist/tools/worktree.js.map +1 -0
  837. package/docs/A1-ATTRIBUTION-ROUND1-2026-07-09.md +181 -0
  838. package/docs/AB-ORACLE-GATE-VERDICT-2026-07-10.md +100 -0
  839. package/docs/ANCHOR-GAP-INVENTORY-2026-07-09.md +143 -0
  840. package/docs/ARCHITECTURE.md +125 -0
  841. package/docs/BUGHUNT-2026-07-05-NIGHT.md +98 -0
  842. package/docs/BUILD.md +63 -0
  843. package/docs/CANCEL-ASYNC-ATTRIBUTION-2026-07-09.md +98 -0
  844. package/docs/CC-198-VS-201-DRIFT-2026-07-09.md +70 -0
  845. package/docs/CC-BASELINE-FAKE-ZERO-AUDIT-2026-07-09.md +51 -0
  846. package/docs/CC-DRIFT-198-206-AGENT-TYPES-2026-07-10.md +82 -0
  847. package/docs/CC-DRIFT-198-206-CHANGELOG-2026-07-10.md +82 -0
  848. package/docs/CC-DRIFT-198-206-REMINDERS-2026-07-10.md +96 -0
  849. package/docs/CC-DRIFT-198-206-ROADMAP-2026-07-10.md +110 -0
  850. package/docs/CC-DRIFT-198-206-TOOL-INVENTORY-2026-07-10.md +53 -0
  851. package/docs/CC-DRIFT-198-206-USAGE-CONSTANTS-2026-07-10.md +64 -0
  852. package/docs/CC-DRIFT-198-206-WORKFLOW-2026-07-10.md +83 -0
  853. package/docs/CC-PARITY-CHECKLIST.md +220 -0
  854. package/docs/CC-PARITY-DEEP-SWEEP-2026-07-07.md +149 -0
  855. package/docs/CC-SUBAGENT-PROMPT-STUDY-2026-07-09.md +219 -0
  856. package/docs/CC-TASK-REMINDER-CADENCE-STUDY-2026-07-09.md +396 -0
  857. package/docs/CC-TOOL-PARITY-SWEEP-2026-07.md +56 -0
  858. package/docs/CC-TOOL-PROMPT-DEEP-DIFF-2026-07-08.md +274 -0
  859. package/docs/CC-TOOLSURFACE-DEEP-DIVE-2026-07-07.md +197 -0
  860. package/docs/CC206-OBSERVER-ANCHORS-2026-07-11.md +463 -0
  861. package/docs/CC207-CORE-SWEEP-BACKLOG-2026-07-12.md +155 -0
  862. package/docs/CENTER-CONTROL-PLANE-SEAM.md +86 -0
  863. package/docs/CLEANUP-CONTRACT-ANCHOR-2026-07-09.md +83 -0
  864. package/docs/COLD-START-RSI-2026-07-09.md +72 -0
  865. package/docs/COMPACTION-LIVE-AUDIT-2026-07-09.md +114 -0
  866. package/docs/CONTEXT-lifecycle-extension-points-and-stop-attribution.md +71 -0
  867. package/docs/COREWARS-POLYGLOT-TRIAGE-2026-07-09.md +85 -0
  868. package/docs/DEADTARGET-OVERFULL-HBOX-2026-07-10.md +92 -0
  869. package/docs/DEADTARGET-PATH-TRACING-REVERSE-2026-07-10.md +87 -0
  870. package/docs/DEADTARGET-WINNING-AVG-COREWARS-2026-07-10.md +96 -0
  871. package/docs/DEEPSEEK-API-LIMITS-PROBE-2026-07-09.md +118 -0
  872. package/docs/DESIGN-140-MOCK-VERIFY-2026-07-11.md +79 -0
  873. package/docs/DESIGN-140-VERIFICATION-2026-07-11.md +43 -0
  874. package/docs/DESIGN-DRAFT-workflow-collab-2026-07-11.md +56 -0
  875. package/docs/DESIGN-ORACLE-GROUNDING-GATE-2026-07-10.md +167 -0
  876. package/docs/DESIGN-TFRAG-RECONCILE-SCAFFOLD-2026-07-09.md +102 -0
  877. package/docs/EXP-PA2-PE-DESIGN-2026-07-11.md +220 -0
  878. package/docs/EXT-DOGFOOD-2026-07-12.md +50 -0
  879. package/docs/FINAL-VERIFY-SURFACE-AUDIT-2026-07-09.md +44 -0
  880. package/docs/FRAG-RECON-INVESTIGATION-2026-07-09.md +183 -0
  881. package/docs/HANDOFF.md +121 -0
  882. package/docs/KNOWN-ISSUES.md +96 -0
  883. package/docs/LEGW-WINDOW-TAX-2026-07-10.md +82 -0
  884. package/docs/LIVE-PROBE-BATCH-2026-07-11.md +207 -0
  885. package/docs/M2-CERT-ATTRIBUTION-2026-07-10.md +61 -0
  886. package/docs/M25-LEG-VARIANCE-ATTRIBUTION-2026-07-10.md +81 -0
  887. package/docs/MAILMAN-TIMEOUT-ATTRIBUTION-2026-07-10.md +88 -0
  888. package/docs/MEMORY-BACKEND-DIALECT-NOTES.md +69 -0
  889. package/docs/MTEB-ESCAPE-HATCH-ATTRIBUTION-2026-07-09.md +57 -0
  890. package/docs/MTEB-LEADERBOARD-ATTRIBUTION-2026-07-10.md +134 -0
  891. package/docs/ORACLE-GATE-NEGATIVE-SAMPLE-2026-07-11.md +28 -0
  892. package/docs/PARITY-CONTEXT-TRANSACTIONS-2026-07-10.md +67 -0
  893. package/docs/PARITY-PROGRESS-MATRIX-2026-07-10.md +37 -0
  894. package/docs/PARITY-SPOT-AGENT-TYPES-2026-07-10.md +70 -0
  895. package/docs/PARITY-SPOT-REMINDERS-2026-07-10.md +144 -0
  896. package/docs/PARITY-SPOT-TOOL-INVENTORY-2026-07-10.md +61 -0
  897. package/docs/PARITY-SPOT-USAGE-CONSTANTS-2026-07-10.md +95 -0
  898. package/docs/PARITY-SPOT-WORKFLOW-2026-07-10.md +93 -0
  899. package/docs/PASSWORD-RECOVERY-PANEL-VERDICT-2026-07-10.md +62 -0
  900. package/docs/R8-R9-FINALIZE-COORDINATION-2026-07-09.md +124 -0
  901. package/docs/REPL-CODE-MODE-EVALUATION-2026-07-10.md +187 -0
  902. package/docs/REPORT-1249-TARGETED.md +48 -0
  903. package/docs/RESEARCH-3P-MEMORY-SWAP-2026-07-12.md +273 -0
  904. package/docs/ROADMAP.md +111 -0
  905. package/docs/RSI-CLOUD-DEPLOY-2026-07-06.md +289 -0
  906. package/docs/RSI-PROCESS-AI-COLDSTART-2026-07-11.md +68 -0
  907. package/docs/S18-THOROUGHNESS-AUDIT-2026-07-09.md +113 -0
  908. package/docs/SERVICE-INTEGRATION-GUIDE.md +298 -0
  909. package/docs/SUBAGENT-STREAM-RESILIENCE-PARITY-2026-07-10.md +118 -0
  910. package/docs/SWEBENCH-PRO-RECON-2026-07-11.md +31 -0
  911. package/docs/TB-EFFICIENCY-TELEMETRY-2026-07-08.md +114 -0
  912. package/docs/TB-FAILURE-AUTOPSY-2026-07-05-RERUN.md +67 -0
  913. package/docs/TB-FAIRNESS-AUDIT-2026-07-08.md +141 -0
  914. package/docs/TB-FULL-1257-2026-07-08.md +77 -0
  915. package/docs/TB-LEG-GAP-ATTRIBUTION-1261-1263-2026-07-11.md +154 -0
  916. package/docs/TB-REGRESSION-DAEMON-KILL-2026-07-08.md +58 -0
  917. package/docs/TB-RSI-CROSSCHECK-2026-07-06.md +68 -0
  918. package/docs/TB-RSI-LEDGER.md +30 -0
  919. package/docs/TB-STABLE-SET-2026-07-08.md +127 -0
  920. package/docs/TB21-RECON-2026-07-11.md +28 -0
  921. package/docs/TEST-DESIGN-BATCH-1-2-3.md +97 -0
  922. package/docs/THIRD-PARTY-INTEGRATION.md +45 -0
  923. package/docs/TOKEN-ESTIMATE-CALIBRATION-LIVE-2026-07-09.md +102 -0
  924. package/docs/audit-2026-06-10-/347/254/254/344/272/214/350/275/256-/347/251/272/347/231/275/345/214/272/344/270/216/345/277/230/346/216/245/347/272/277.md +147 -0
  925. package/docs/audit-2026-06-10-/350/256/241/347/256/227bug/346/211/253/346/217/217.md +163 -0
  926. package/docs/audit-2026-06-11-/344/277/256/345/244/215/345/244/215/345/256/241/344/270/216/345/233/236/345/275/222/344/270/223/346/211/253.md +95 -0
  927. package/docs/audit-2026-06-12-design71-P1/346/275/234/344/274/217/351/235/242/350/243/201/345/206/263/344/270/216/345/220/236/351/224/231/345/256/241/350/256/241.md +45 -0
  928. package/docs/audit-2026-06-12-/345/205/254/345/274/200/346/227/213/351/222/256/346/264/273/346/200/247/344/270/216vendored/346/255/273/350/267/257.md +63 -0
  929. package/docs/audit-2026-06-15/ADDENDUM-second-pass.md +88 -0
  930. package/docs/audit-2026-06-15/ARCHITECTURE-AND-ROADMAP.md +187 -0
  931. package/docs/audit-2026-06-15/DOC-CORRECTIONS.md +105 -0
  932. package/docs/audit-2026-06-15/INDEX.md +49 -0
  933. package/docs/audit-2026-06-15/ORCHESTRATION-STATUS.md +98 -0
  934. package/docs/audit-2026-06-15/PRODUCTION-ISSUES.md +189 -0
  935. package/docs/audit-2026-06-15/REFERENCES-literature.md +139 -0
  936. package/docs/audit-2026-06-15/VENDOR-REFACTOR-PLAN.md +96 -0
  937. package/docs/cc-probe-198/README.md +72 -0
  938. package/docs/cc-probe-198/STABILITY-AUDIT.md +118 -0
  939. package/docs/cc-probe-198/live-schema-monitor-worktree-2026-07-09.json +56 -0
  940. package/docs/cc-probe-198/mount-variants.json +536 -0
  941. package/docs/cc-probe-198/plan-mode-run.json +1 -0
  942. package/docs/cc-probe-198/probe-bash-bg.json +224 -0
  943. package/docs/cc-probe-198/probe-edit-mismatch.json +120 -0
  944. package/docs/cc-probe-198/probe-read-missing.json +62 -0
  945. package/docs/cc-probe-198/probe-search-empty.json +127 -0
  946. package/docs/cc-probe-198/probe-task-family.json +227 -0
  947. package/docs/cc-probe-198/probes.status +6 -0
  948. package/docs/cc-probe-198/raw-capture-interactive.json +948 -0
  949. package/docs/cc-probe-198/raw-capture-print.json +787 -0
  950. package/docs/cc-probe-198/raw-capture2-interactive.json +948 -0
  951. package/docs/cc-probe-198/raw-capture2-print.json +787 -0
  952. package/docs/cc-probe-198/system-prompt.txt +135 -0
  953. package/docs/cc-probe-198/tools-array.json +962 -0
  954. package/docs/durability-boundary.md +63 -0
  955. package/docs/fix-2026-06-10-/350/256/241/347/256/227bug/344/277/256/345/244/215-search/346/211/271.md +75 -0
  956. package/docs/fix-2026-06-11-/347/254/254/344/272/214/350/275/256-/344/277/256/345/244/215-search/346/211/271.md +62 -0
  957. package/docs/parity-sweep/REPORT-DYNAMIC.md +97 -0
  958. package/docs/parity-sweep/REPORT-STATIC-GGB.md +127 -0
  959. package/docs/parity-sweep/REPORT-STATIC-RWE.md +133 -0
  960. package/docs/vendor-history.md +385 -0
  961. package/docs/vendored-upstream-diff-2026-07.md +83 -0
  962. package/package.json +70 -0
@@ -0,0 +1,1751 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { Type } from "typebox";
3
+ import { defineTool } from "./tools.js";
4
+ import { hasBackgroundShell } from "./background-shell.js";
5
+ import { summarizeWorkflowRun } from "./workflow-run-store.js";
6
+ import { delimitUntrusted } from "./untrusted-text.js";
7
+ import { boundedRedactedSummary } from "./untrusted-egress.js";
8
+ const defaultMonitorTimers = {
9
+ setInterval: (fn, ms) => {
10
+ const h = setInterval(() => void fn(), ms);
11
+ h.unref?.(); // a watcher never keeps the process alive (same as the bash watcher)
12
+ return h;
13
+ },
14
+ clearInterval: (h) => clearInterval(h),
15
+ now: () => Date.now(),
16
+ };
17
+ const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
18
+ const BLOCK_MAX_TIMEOUT_MS = 600_000; // CC 187 cap (timeout: max(600000))
19
+ const BLOCK_POLL_INTERVAL_MS = 400;
20
+ function blockDeadline(timeoutMs, now) {
21
+ const t = Math.min(BLOCK_MAX_TIMEOUT_MS, Math.max(1, Math.floor(timeoutMs ?? BLOCK_DEFAULT_TIMEOUT_MS)));
22
+ return now + t;
23
+ }
24
+ /** T2A-7 (2026-07-05 soak): one blocked-poll step, CLAMPED to the caller's deadline — a small timeoutMs
25
+ * (e.g. 10ms) must not sleep the full 400ms step (~40× overshoot, and the overshoot window even served a
26
+ * late terminal as success). `undefined` deadline keeps the plain step (callers without a deadline). */
27
+ const sleepPollStep = (deadline, signal) => sleepMs(deadline === undefined ? BLOCK_POLL_INTERVAL_MS : Math.max(1, Math.min(BLOCK_POLL_INTERVAL_MS, deadline - Date.now())), signal);
28
+ const sleepMs = (ms, signal) => new Promise((resolve) => {
29
+ const t = setTimeout(done, ms);
30
+ function done() {
31
+ clearTimeout(t);
32
+ signal?.removeEventListener("abort", done);
33
+ resolve();
34
+ }
35
+ signal?.addEventListener("abort", done, { once: true });
36
+ });
37
+ const TASK_PREFIX = {
38
+ background_bash: "b",
39
+ workflow: "w",
40
+ background_agent: "a",
41
+ monitor: "m",
42
+ };
43
+ /** design/135 G2 Monitor defaults (CC schema: 200ms batch window; timeout default 5min / max 60min;
44
+ * "too many events" auto-stop — threshold is a sema choice, disclosed in the stop notification). */
45
+ export const MONITOR_BATCH_WINDOW_MS = 200;
46
+ export const MONITOR_DEFAULT_TIMEOUT_MS = 300_000;
47
+ export const MONITOR_MAX_TIMEOUT_MS = 3_600_000;
48
+ export const MONITOR_MAX_BATCHES_PER_MINUTE = 50;
49
+ /** Per-event display bound for one batch's joined lines (the notification's `result`). */
50
+ const MONITOR_EVENT_RESULT_CAP = 2_000;
51
+ /**
52
+ * CC 198 TaskOutput truncation (PARITY-SPOT-USAGE-CONSTANTS 2026-07-10 F3): limit = env
53
+ * `TASK_MAX_OUTPUT_LENGTH` (`oPm`, pretty.js:458009-458010; `vde` :229475 — unset/invalid → default
54
+ * `dFo=32000`, above `uFo=160000` → capped). Read per call (CC same), so a deployment can steer it
55
+ * without a restart.
56
+ */
57
+ const TASK_OUTPUT_DEFAULT_CHARS = 32_000;
58
+ const TASK_OUTPUT_MAX_CHARS = 160_000;
59
+ // design/136 §2.3: "WorkflowStatus" joined as a live alias when the standalone poll tool was retired —
60
+ // old model-facing calls run verbatim through TaskOutput (the legacy `runId` argument was already accepted).
61
+ const TASK_OUTPUT_ALIASES = ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
62
+ const TASK_STOP_ALIASES = ["KillShell", "KillBash"];
63
+ function taskMaxOutputChars() {
64
+ const raw = process.env.TASK_MAX_OUTPUT_LENGTH;
65
+ if (!raw)
66
+ return TASK_OUTPUT_DEFAULT_CHARS;
67
+ const n = parseInt(raw, 10);
68
+ if (!Number.isFinite(n) || n <= 0)
69
+ return TASK_OUTPUT_DEFAULT_CHARS;
70
+ return Math.min(n, TASK_OUTPUT_MAX_CHARS);
71
+ }
72
+ /**
73
+ * CC `FHl` shape (pretty.js:458012-458016) when the task has an on-disk output file: TAIL-keep (a
74
+ * background command's latest output is the valuable end) behind a `[Truncated. Full output: <path>]`
75
+ * pointer so the model can Read the full spool. Without a file (workflow/agent results), keep the
76
+ * legacy head+tail middle-omission — there is no path to point at, and the head often carries status.
77
+ */
78
+ export function clipTaskOutput(s, fullOutputPath) {
79
+ const limit = taskMaxOutputChars();
80
+ if (s.length <= limit)
81
+ return s;
82
+ if (fullOutputPath !== undefined) {
83
+ const header = `[Truncated. Full output: ${fullOutputPath}]\n\n`;
84
+ return header + s.slice(-(limit - header.length));
85
+ }
86
+ const half = Math.floor(limit / 2);
87
+ const omitted = s.length - 2 * half;
88
+ return `${s.slice(0, half)}\n…[${s.length} chars total, ${omitted} omitted from the middle]…\n${s.slice(s.length - half)}`;
89
+ }
90
+ function firstString(...values) {
91
+ for (const value of values) {
92
+ if (typeof value === "string" && value.trim() !== "")
93
+ return value.trim();
94
+ }
95
+ return undefined;
96
+ }
97
+ function noTask(taskId) {
98
+ return {
99
+ content: JSON.stringify({ error: "task not found" }),
100
+ details: { task_id: taskId, retrieval_status: "not_ready", error: "not_found" },
101
+ };
102
+ }
103
+ /** CC206-B 欠账 — TaskStop name addressing, CC `XN` verbatim (206:574955-574962): NFKC-normalize, strip
104
+ * control/format chars (whitespace-class ones survive), trim, lowercase, whitespace runs → hyphens. Both
105
+ * the query and the candidate label go through this, so "Fix Tests" addresses "fix tests". */
106
+ function normalizeAgentName(s) {
107
+ return s
108
+ .normalize("NFKC")
109
+ .replace(/[\p{Cc}\p{Cf}]/gu, (t) => (/\s/.test(t) ? t : ""))
110
+ .trim()
111
+ .toLowerCase()
112
+ .replace(/\s+/g, "-");
113
+ }
114
+ /** CC `gtr` (fable 复审 F1 — 亲核 @16169850: NOT plain Levenshtein but OSA / restricted
115
+ * Damerau-Levenshtein, WITH the adjacent-transposition term `o[i][s]=min(o[i][s], o[i-2][s-2]+1)`
116
+ * when `e[i-1]===t[s-2] && e[i-2]===t[s-1]`). The transposition matters: a typo like "tset"→"test"
117
+ * is distance 1 (CC has a Did-you-mean), plain Levenshtein would call it 2. */
118
+ function editDistance(a, b) {
119
+ if (a === b)
120
+ return 0;
121
+ const m = a.length;
122
+ const n = b.length;
123
+ const d = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)));
124
+ for (let i = 1; i <= m; i++) {
125
+ for (let j = 1; j <= n; j++) {
126
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
127
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
128
+ // CC's adjacent-transposition arm (OSA): `ab`↔`ba` costs 1, not 2.
129
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
130
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
131
+ }
132
+ }
133
+ }
134
+ return d[m][n];
135
+ }
136
+ /** CC `htr` shape (206:519814-519827): candidates within Levenshtein ≤2 (length-diff ≤2 pre-filter),
137
+ * best-first; the single best is the "Did you mean" suggestion (`htr(e, keys, 1)[0]`, 206:575196). */
138
+ function closestName(query, candidates) {
139
+ let best;
140
+ let bestDist = 3;
141
+ const seen = new Set();
142
+ for (const c of candidates) {
143
+ if (seen.has(c))
144
+ continue;
145
+ seen.add(c);
146
+ if (Math.abs(c.length - query.length) > 2)
147
+ continue;
148
+ const dist = editDistance(query, c);
149
+ if (dist <= 2 && dist < bestDist) {
150
+ bestDist = dist;
151
+ best = c;
152
+ }
153
+ }
154
+ return best;
155
+ }
156
+ /** TaskStop not-found — CC 2.1.198 verbatim base (live probe 2026-07-08: `No task found with ID: <id>`),
157
+ * CC206-B enrichment (CC `Kws` 206:575251-575258 + `ugo` 206:575227-575243): `. Did you mean: <name>?`
158
+ * when a close name exists, then `. Running background agents: <id (description)>, …` over the CALLER's
159
+ * running agent rows (sema's access guard already scopes the list; CC filters observers/self there).
160
+ * sema has no teammate registry on this face, so CC's `Running teammates:` leg is honestly absent.
161
+ * Only the STOP verb has CC probe evidence; TaskOutput keeps the JSON shape (unprobed face). */
162
+ function noTaskForStop(taskId, enrich) {
163
+ let content = `No task found with ID: ${taskId}`;
164
+ if (enrich?.suggestion !== undefined)
165
+ content += `. Did you mean: ${enrich.suggestion}?`;
166
+ if (enrich?.runningAgents !== undefined && enrich.runningAgents.length > 0)
167
+ content += `. Running background agents: ${enrich.runningAgents.join(", ")}`;
168
+ return {
169
+ content,
170
+ details: { task_id: taskId, retrieval_status: "not_ready", error: "not_found" },
171
+ };
172
+ }
173
+ function statusFromBackground(status, exitCode) {
174
+ if (status === "running")
175
+ return "running";
176
+ if (status === "killed")
177
+ return "killed";
178
+ if (status === "failed")
179
+ return "failed";
180
+ if (status === "exited")
181
+ return exitCode === 0 ? "completed" : "failed";
182
+ return "failed";
183
+ }
184
+ /** 飞轮 [511]③ (CC parity): is this monitor a TIMEOUT-anchored watch still inside its window?
185
+ * Such a handle must survive every run-scoped teardown sweep (settleKilledForOwner /
186
+ * clearBackgroundForOwner / markStopSourceForOwner / the env dispose except-list): CC's
187
+ * Monitor(persistent:false) lives to timeout_ms — its ONLY exits are timeout, process exit and
188
+ * TaskStop; the parent run ending is not one of them (the between-turns watch is the main use case).
189
+ * Reads the handle's INJECTED clock (design/87) so tests drive the window deterministically. */
190
+ function monitorInTimeoutWindow(handle) {
191
+ return handle.status === "running" && handle.deadlineAt !== undefined && handle.timers.now() < handle.deadlineAt;
192
+ }
193
+ function canAccess(handle, access) {
194
+ if (handle.scope !== undefined && access.scope !== handle.scope)
195
+ return false;
196
+ if (handle.owner === undefined)
197
+ return true;
198
+ if (access.owner === handle.owner)
199
+ return true;
200
+ // design/129 (codex F1): a session-scoped handle is keyed by the SESSION — a later turn's fresh
201
+ // per-turn owner still reaches it through the caller's sessionId. Explicit flag only; a task-scoped
202
+ // handle never matches on session (no widening of the cross-caller access surface).
203
+ if (handle.sessionScoped && access.sessionId !== undefined && access.sessionId === handle.owner)
204
+ return true;
205
+ return false;
206
+ }
207
+ /** design/115 review B1: register-side fail-closed — a handle with NEITHER owner NOR scope would be
208
+ * world-readable/killable through {@link canAccess} (both guards skip on undefined). The production
209
+ * chain always sets both (hostTaskId + principal-or-"default"); a direct registry caller must too. */
210
+ function assertOwnership(input, site) {
211
+ if (input.owner === undefined && input.scope === undefined) {
212
+ throw new Error(`TaskRegistry.${site}: refusing to register a task with neither owner nor scope (would be accessible to every caller)`);
213
+ }
214
+ }
215
+ function canAccessWorkflowRun(run, access) {
216
+ if (access.scope === undefined || run.scope !== access.scope)
217
+ return false;
218
+ // Fail-closed owner check (design/115 review S-1): when the caller has a known owner, a run WITHOUT a
219
+ // sourceTaskId is NOT assumed to be theirs — legacy/direct-started runs (pre-design/114 persistence, or
220
+ // startWorkflow called outside this tool) are denied rather than silently readable by any same-scope task.
221
+ // design/136 §2.3 (盲点③, accepted): the scope-only WorkflowStatus escape hatch was retired with the tool —
222
+ // scope+owner fail-closed is now the ONLY poll access posture (S-1 定谳); a same-scope different-task poll
223
+ // reads not_found, identically to an unknown id.
224
+ if (access.owner !== undefined && run.sourceTaskId !== access.owner)
225
+ return false;
226
+ return true;
227
+ }
228
+ function formatWorkflowRun(run) {
229
+ const summary = summarizeWorkflowRun(run);
230
+ const done = run.agents.filter((a) => a.status === "completed" || a.status === "failed").length;
231
+ const failed = run.agents.filter((a) => a.status === "failed").length;
232
+ return {
233
+ content: JSON.stringify({
234
+ task_id: run.id,
235
+ runId: run.id,
236
+ type: "workflow",
237
+ status: run.status,
238
+ ...(summary.name ? { name: summary.name } : {}),
239
+ ...(summary.currentPhase ? { currentPhase: summary.currentPhase } : {}),
240
+ agents: { done, total: run.agents.length, failed },
241
+ startedAt: run.startedAt,
242
+ ...(run.endedAt !== undefined ? { endedAt: run.endedAt, elapsedMs: run.endedAt - run.startedAt } : {}),
243
+ // 复审 r2 MAJOR: error/result are MODEL-INFLUENCEABLE (agent output / thrown message) — fence them
244
+ // like every other model-facing TaskOutput body (delimitUntrusted, cf. lines 606/670/779) so a
245
+ // crafted `</system-reminder>` in the workflow's return value can't break framing. Bounded+redacted
246
+ // upstream; the fence is the untrust boundary this file's own convention requires.
247
+ ...(run.status === "failed" && run.error ? { error: delimitUntrusted("workflow error", boundedRedactedSummary(run.error, 500)) } : {}),
248
+ // 黑板 [409]: a terminal poll must HAND OVER the result — "result via TaskOutput" pointed here,
249
+ // and this reply previously carried only agent counts (模型按指引取结果→取不到→连环刷).
250
+ // 黑板 [413] (NH-1): prefer resultFull — when the display bound truncated run.result, inlining the full
251
+ // text here lets the generic large-result offload persist it under a real read_tool_result ref (the
252
+ // truncated `…[+N chars]` tail previously had no retrieval path).
253
+ ...(run.status === "completed" && run.result !== undefined
254
+ ? { result: delimitUntrusted("workflow result", run.resultFull ?? run.result) }
255
+ : {}),
256
+ note: run.status === "running"
257
+ ? "still running — poll again shortly."
258
+ : run.status === "completed"
259
+ ? run.result !== undefined
260
+ ? "completed — `result` above is the workflow's return value."
261
+ : "completed — this run predates result persistence; its result was delivered on the completion notification."
262
+ : "failed — see error.",
263
+ }),
264
+ details: {
265
+ task_id: run.id,
266
+ type: "workflow",
267
+ status: run.status,
268
+ retrieval_status: "success",
269
+ details: summary,
270
+ },
271
+ };
272
+ }
273
+ export class TaskRegistry {
274
+ handles = new Map();
275
+ /** codex 终审 1.255 F1: observers of the design/129 session terminal anchor — `reapSessionBackground`
276
+ * fires each hook (swallow-guarded) AFTER reaping, so session-anchored side state (the session-scoped
277
+ * subagent retain ledger) releases on the SAME deployment call. Registered idempotently per consumer. */
278
+ sessionReapHooks = new Set();
279
+ legacyToTaskId = new Map();
280
+ mintTaskId(type) {
281
+ const prefix = TASK_PREFIX[type];
282
+ for (let i = 0; i < 32; i++) {
283
+ const id = `${prefix}${randomBytes(8).toString("hex")}`;
284
+ if (!this.handles.has(id) && !this.legacyToTaskId.has(id))
285
+ return id;
286
+ }
287
+ throw new Error(`TaskRegistry: exhausted task id mint retries for ${type}`);
288
+ }
289
+ registerBackgroundBash(input) {
290
+ assertOwnership(input, "registerBackgroundBash");
291
+ const id = input.id ?? this.mintTaskId("background_bash");
292
+ if (this.handles.has(id))
293
+ throw new Error(`TaskRegistry: task id already registered: ${id}`);
294
+ const now = input.now ?? Date.now();
295
+ this.handles.set(id, {
296
+ id,
297
+ type: "background_bash",
298
+ status: "running",
299
+ ...(input.sessionScoped ? { sessionScoped: true } : {}), // [636]①: explicit-flag lifetime (design/129)
300
+ ...(input.description !== undefined ? { description: input.description } : {}),
301
+ ...(input.owner !== undefined ? { owner: input.owner } : {}),
302
+ ...(input.scope !== undefined ? { scope: input.scope } : {}),
303
+ ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
304
+ createdAt: now,
305
+ updatedAt: now,
306
+ env: input.env,
307
+ shellId: input.shellId,
308
+ ...(input.onTerminal !== undefined ? { onTerminal: input.onTerminal } : {}), // [506]③: teardown-receipt access
309
+ });
310
+ this.legacyToTaskId.set(String(input.shellId), id);
311
+ if (input.onTerminal)
312
+ this.startBashWatcher(id, input.onTerminal);
313
+ return id;
314
+ }
315
+ /** design/115 P3: register a background sub-agent run. The CALLER owns driving the child promise and
316
+ * calling {@link settleBackgroundAgent} at the end; the registry provides the unified task_id, the
317
+ * owner/scope guard, poll/stop dispatch, and terminal GC — exactly like the other two kinds. */
318
+ registerBackgroundAgent(input) {
319
+ assertOwnership(input, "registerBackgroundAgent");
320
+ const id = this.mintTaskId("background_agent");
321
+ const now = input.now ?? Date.now();
322
+ this.handles.set(id, {
323
+ id,
324
+ type: "background_agent",
325
+ status: "running",
326
+ ...(input.sessionScoped ? { sessionScoped: true } : {}),
327
+ ...(input.description !== undefined ? { description: input.description } : {}),
328
+ ...(input.owner !== undefined ? { owner: input.owner } : {}),
329
+ ...(input.scope !== undefined ? { scope: input.scope } : {}),
330
+ ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
331
+ createdAt: now,
332
+ updatedAt: now,
333
+ abort: input.abort,
334
+ });
335
+ return id;
336
+ }
337
+ /** design/134 §3.3 (R3 single-source): record WHO is about to kill `id`, at the initiation site,
338
+ * BEFORE the abort()/status flip — the ordering is load-bearing: the guard below refuses markers on a
339
+ * non-running handle, so a caller that flips first loses its claim and attribution falls back to
340
+ * "system". First-marker-wins: an earlier marker (e.g. a service-wire "user") is never overwritten.
341
+ * Applies to background_agent AND background_bash (same class); workflow cancellation is out of scope. */
342
+ markStopSource(id, source) {
343
+ const handle = this.handles.get(id);
344
+ if (!handle || (handle.type !== "background_agent" && handle.type !== "background_bash" && handle.type !== "monitor"))
345
+ return;
346
+ if (handle.status !== "running" || handle.stopSource !== undefined)
347
+ return;
348
+ handle.stopSource = source;
349
+ }
350
+ /** design/134 §3.3: narrow terminal read for consumers (subagent sinkEmit/notify) — only a KILLED row
351
+ * has an attribution; "system" is the defensive floor for a row that went killed without a landed
352
+ * stoppedBy (e.g. legacy flips). Returns undefined for running/completed/failed/evicted rows. */
353
+ getStopAttribution(id) {
354
+ const handle = this.handles.get(id);
355
+ if (!handle || (handle.type !== "background_agent" && handle.type !== "background_bash" && handle.type !== "monitor"))
356
+ return undefined;
357
+ if (handle.status !== "killed")
358
+ return undefined;
359
+ return handle.stoppedBy ?? handle.stopSource ?? "system";
360
+ }
361
+ /** Terminal update for a background agent (the spawn-side then/catch calls this exactly once).
362
+ * design/129-B: returns the WINNING terminal status — the earlier writer's when this settle is refused
363
+ * (first-writer-wins), `undefined` when the handle is gone (evicted). Callers report THIS in their
364
+ * notification/terminal event so the push never contradicts the registry row (a completed-notify over a
365
+ * killed row — the TaskStop-then-late-resolve race — was an observable incoherence). */
366
+ settleBackgroundAgent(id, outcome) {
367
+ const handle = this.handles.get(id);
368
+ if (!handle || handle.type !== "background_agent")
369
+ return undefined;
370
+ // codex 1.237 复审 F3/F4 (修类): settle is FIRST-WRITER-WINS — a TaskStop already marked it killed, or a
371
+ // completed settle already landed; a later settle (the child promise's catch after an abort, or a
372
+ // rethrown notify-sink error routing the .then into .catch) must not rewrite the terminal state.
373
+ // design/134: this same guard protects `stoppedBy` — a refused settle never touches attribution either.
374
+ if (handle.status !== "running")
375
+ return handle.status === "completed" || handle.status === "failed" || handle.status === "killed" ? handle.status : undefined;
376
+ handle.status = outcome.status;
377
+ // design/134 §3.3 (R3): settle is only the FALLBACK filler — the initiation-site marker (stopSource)
378
+ // is the single source; "system" is the no-one-claimed-it floor (timeout / process teardown).
379
+ if (outcome.status === "killed" && handle.stoppedBy === undefined) {
380
+ handle.stoppedBy = outcome.stoppedBy ?? handle.stopSource ?? "system";
381
+ }
382
+ if (outcome.result !== undefined)
383
+ handle.result = outcome.result;
384
+ if (outcome.error !== undefined)
385
+ handle.error = outcome.error;
386
+ handle.updatedAt = Date.now();
387
+ return outcome.status;
388
+ }
389
+ /** Abort every background agent belonging to `access` (parent-task teardown — a finished parent must
390
+ * not leave orphan child runs burning tokens; mirrors clearBackgroundForOwner for bash). */
391
+ abortBackgroundAgentsForOwner(access, opts) {
392
+ let aborted = 0;
393
+ for (const handle of this.handles.values()) {
394
+ if (handle.type !== "background_agent" || handle.status !== "running" || !canAccess(handle, access))
395
+ continue;
396
+ // design/129 (codex F2): lifetime is filtered by the EXPLICIT flag, both directions — the parent
397
+ // teardown skips session-scoped children; the session terminal reaps ONLY them (a task-scoped entry
398
+ // under the same key value must not be collaterally killed).
399
+ if (opts?.skipSessionScoped && handle.sessionScoped)
400
+ continue;
401
+ if (opts?.sessionScopedOnly && !handle.sessionScoped)
402
+ continue;
403
+ // design/134 §3.3: parent-teardown reap = the PARENT stopped it (CC semantics) — mark before abort.
404
+ this.markStopSource(handle.id, "parent");
405
+ handle.abort.abort();
406
+ aborted++;
407
+ }
408
+ return aborted;
409
+ }
410
+ /** service 黑板 [418]①②: bulk stop-source attribution for one owner — the registry-side primitive that
411
+ * replaces the service's `list()+markStopSource` per-row loop, which had TWO holes: ① `list()` didn't
412
+ * expose `sessionScoped`, so on the resume leg (canonical taskId==sessionId, rebuilt taskConfig without
413
+ * spec.taskId) task-scoped and session-scoped children share the same owner KEY VALUE and can't be told
414
+ * apart; ② `list()`'s 500-row display cap silently skipped rows on >500-children runs.
415
+ * Same owner-filter semantics as {@link abortBackgroundAgentsForOwner}: {@link canAccess} + the EXPLICIT
416
+ * `sessionScoped` flag (design/129 — never inferred from key values), over a DIRECT handle-map walk
417
+ * (no list() cap in the path). Covers every markable kind ({@link markStopSource}'s own set:
418
+ * background_agent / background_bash / monitor); each row is delegated to `markStopSource`, so the
419
+ * running-only + first-marker-wins guards hold unchanged — an existing claim is never overwritten.
420
+ * Returns how many rows NEWLY took the marker (already-marked / non-running rows don't count). */
421
+ markStopSourceForOwner(access, source, opts) {
422
+ let marked = 0;
423
+ for (const handle of this.handles.values()) {
424
+ if (handle.type !== "background_agent" && handle.type !== "background_bash" && handle.type !== "monitor")
425
+ continue;
426
+ if (handle.status !== "running" || !canAccess(handle, access))
427
+ continue;
428
+ // design/129 (codex F2, same discipline as abortBackgroundAgentsForOwner): lifetime is filtered by
429
+ // the EXPLICIT flag — the [418]① resume-leg key collision is exactly why value inference is banned.
430
+ if (opts?.skipSessionScoped && handle.sessionScoped)
431
+ continue;
432
+ // 飞轮 [511]③: an in-window non-persistent monitor is not being killed by this run-level stop —
433
+ // it survives the teardown (see settleKilledForOwner). Marking it here would poison its LATER
434
+ // legitimate exit's attribution (first-marker-wins: a stray "user" marker would relabel the
435
+ // eventual timeout terminal as stoppedBy:"user").
436
+ if (handle.type === "monitor" && monitorInTimeoutWindow(handle))
437
+ continue;
438
+ const before = handle.stopSource;
439
+ this.markStopSource(handle.id, source);
440
+ if (before === undefined && handle.stopSource !== undefined)
441
+ marked++;
442
+ }
443
+ return marked;
444
+ }
445
+ /** 飞轮 [506]③ — the run-teardown KILLED-receipt producer for the SHELL lanes (background_bash + per-run
446
+ * monitor). The runner teardown disposes the processes (`disposeBackgroundShells`) and evicts the rows
447
+ * (`clearBackgroundForOwner`) — after which NO producer can ever mint the killed notification: the bash
448
+ * watcher's next poll sees a vanished shell ("nothing to notify about") or its evicted handle and just
449
+ * stops. So a mid-turn abort (or a normal turn end) silently swallowed children the model had been
450
+ * promised notifications for ("You will be notified when it completes — do not poll"). The AGENT lane
451
+ * already has its producer (the child promise's settle → notify, [496]④); this is the same receipt for
452
+ * the shell lanes — call it BEFORE dispose/evict so the sink chain (onTerminal/onEvent → the run's
453
+ * notification wrapper, whose lane is already down at teardown) PARKS the receipt on the per-session
454
+ * pending store and the session's next run drains it (the [492]② lane, one shape for all producers).
455
+ *
456
+ * Attribution (design/134 §3.3): `markStopSource(source)` first — first-marker-wins, so a deployment's
457
+ * earlier explicit mark (e.g. a service-wire "user" on abort) is never overwritten; `stoppedBy` then
458
+ * lands from the marker. Rows without a sink still settle (registry truth), they just have no receipt
459
+ * to send. Session-scoped rows are skipped under `skipSessionScoped` (they outlive the turn; their
460
+ * terminal anchor is `reapSessionBackground`). KNOWN small race, recorded: a child that exited within
461
+ * the last watcher-poll interval (~1s) but whose exit the watcher hasn't observed yet still reads
462
+ * "running" here and is settled `killed` — the receipt is at worst one status coarser, never silent.
463
+ *
464
+ * codex 1.257.3 review (HIGH): the settle EXECUTES the kill itself (await `killBackground`, Result-typed)
465
+ * before minting the receipt — previously the receipt claimed "killed" while the actual kill was left to
466
+ * the later `disposeBackgroundShells`, whose failure was swallowed (process alive + registry cleared =
467
+ * the receipt lied). A failed kill now mints an HONEST receipt naming the failure (the env dispose is
468
+ * still the idempotent backstop). Returns how many rows were settled. */
469
+ async settleKilledForOwner(access, opts) {
470
+ const source = opts?.source ?? "parent";
471
+ const clause = (by) => by === "user" ? "stopped by user" : by === "parent" ? "its parent run ended" : `stopped by ${by}`;
472
+ let settled = 0;
473
+ for (const handle of this.handles.values()) {
474
+ if (handle.type !== "background_bash" && handle.type !== "monitor")
475
+ continue;
476
+ if (handle.status !== "running" || !canAccess(handle, access))
477
+ continue;
478
+ // design/129 (codex F2 discipline): lifetime filtered by the EXPLICIT flag, never inferred from keys.
479
+ // [636]① envDying exception (same rationale as the opus 1.258 monitor arm below): a session-resident
480
+ // BASH shell on a per-task ISOLATED env dies with the env destroy right after this sweep — exempting
481
+ // it would leave a running row whose process is already gone (the watcher polls a vanished shell and
482
+ // goes silent: no receipt, ever). Settle it NOW with the accurate killed receipt instead.
483
+ if (opts?.skipSessionScoped && handle.sessionScoped && !(opts?.envDying === true && handle.type === "background_bash"))
484
+ continue;
485
+ // design/128 T1-1 (TB 2026-07-08 翻红回归,3 题对抗复核确认:configure-git-webserver / hf-model-inference /
486
+ // kv-store-grpc): a retain-declared env's background children OUTLIVE the run — the deliver-a-living-service
487
+ // contract. This settle is an AUTO sweep (run teardown), and its `killBackground` is the explicit-kill path
488
+ // that BYPASSES the dispose-side retain gate, so 1.257.3's kill-before-receipt silently killed every TB
489
+ // long-lived daemon 0.2s before the verifier probed it (1.249 left them orphaned → PASS; CC live evidence:
490
+ // the stable set's CC runs pass these daemon tasks, so CC's bg shells survive the agent run too). Skip
491
+ // ENTIRELY: no kill, no killed-receipt (nothing died — a receipt would lie), row left for the teardown's
492
+ // clearBackgroundForOwner evict (which clears the watcher and leaves the process alive, the 1.249 shape).
493
+ // Explicit TaskStop and the per-shell timeout hard-wall are untouched (host-alive semantics, T1-1).
494
+ // codex 1.258.1 review (MED): NOT when the owned env is about to be destroyed — retain on a dying env
495
+ // is illusory (destroy reaps the container and the daemon with it, receipt-less). Same rationale as the
496
+ // opus envDying monitor arm below: settle NOW with the accurate killed receipt instead.
497
+ if (handle.env.backgroundCapabilities.retainBackgroundProcesses === true && !opts?.envDying)
498
+ continue;
499
+ // 飞轮 [511]③ (CC parity): a non-persistent monitor is TIMEOUT-anchored, not run-anchored — the
500
+ // run teardown is NOT one of its exits (timeout / process exit / TaskStop are). An in-window watch
501
+ // is left running: its process rides the teardown dispose's except-list, its watcher keeps ticking
502
+ // on the registry-scoped timers, and its between-turns events/terminal park on the [492]② pending
503
+ // lane. Only a past-deadline straggler (watcher backlog) still settles here; the bash arm below is
504
+ // untouched — bash children remain run-scoped and settle killed-with-receipt exactly as before.
505
+ //
506
+ // opus 1.258 review (MED): the exemption only holds when the ENV survives the run. On a per-task
507
+ // owned env the teardown destroys the env right after this sweep (`teardownOwnedEnv`) — the watch
508
+ // process dies with it and the watcher's next tick would mint a MISLEADING "failed: lost its
509
+ // process" terminal. `envDying:true` (caller-derived from the teardownOwnedEnv gate) opts the
510
+ // in-window monitor back INTO the settle: it gets the accurate killed/parent receipt instead.
511
+ if (handle.type === "monitor" && monitorInTimeoutWindow(handle) && !opts?.envDying)
512
+ continue;
513
+ this.markStopSource(handle.id, source);
514
+ if (handle.type === "background_bash") {
515
+ if (handle.watcher !== undefined) {
516
+ clearInterval(handle.watcher);
517
+ handle.watcher = undefined;
518
+ }
519
+ // Kill FIRST (Result-typed, never throws); `not_found` = already dead (exited inside the watcher
520
+ // window) — that still reads killed-at-teardown here, one status coarser as documented above.
521
+ const kill = await handle.env.killBackground(handle.shellId);
522
+ const killFailed = !kill.ok && kill.error.code !== "not_found";
523
+ handle.status = "killed";
524
+ handle.stoppedBy = handle.stopSource ?? source;
525
+ handle.updatedAt = Date.now();
526
+ const tail = handle.spool !== undefined ? handle.spool.stdout.slice(-2_000) : "";
527
+ try {
528
+ handle.onTerminal?.({
529
+ task_id: handle.id,
530
+ task_type: "background_bash",
531
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
532
+ status: "killed",
533
+ stoppedBy: handle.stoppedBy,
534
+ summary: `${(handle.description ?? "background command").slice(0, 200)} — killed before completion (${clause(handle.stoppedBy)})${killFailed ? ` — kill attempt reported ${kill.ok ? "" : kill.error.code}: the process may still be terminating; the environment teardown retries` : ""}`,
535
+ ...(tail.length > 0 ? { result: tail } : {}),
536
+ });
537
+ }
538
+ catch {
539
+ /* a notification sink must never fault the teardown */
540
+ }
541
+ }
542
+ else {
543
+ if (handle.watcher !== undefined) {
544
+ handle.timers.clearInterval(handle.watcher);
545
+ handle.watcher = undefined;
546
+ }
547
+ // Same kill-before-receipt discipline as the bash arm (codex HIGH).
548
+ const kill = await handle.env.killBackground(handle.shellId);
549
+ const killFailed = !kill.ok && kill.error.code !== "not_found";
550
+ handle.status = "killed";
551
+ handle.stoppedBy = handle.stopSource ?? source;
552
+ handle.updatedAt = Date.now();
553
+ // Terminal flush parity with the watcher: a pending newline-less line rides the receipt.
554
+ const lastLines = handle.lineBuf !== "" ? [handle.lineBuf] : [];
555
+ handle.lineBuf = "";
556
+ try {
557
+ handle.onEvent?.({
558
+ task_id: handle.id,
559
+ task_type: "monitor",
560
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
561
+ status: "killed",
562
+ stoppedBy: handle.stoppedBy,
563
+ summary: `${(handle.description ?? "monitor").slice(0, 200)} — killed before completion (${clause(handle.stoppedBy)}); watch ended.${killFailed ? ` Kill attempt reported ${kill.ok ? "" : kill.error.code}: the process may still be terminating; the environment teardown retries.` : ""}`,
564
+ ...(lastLines.length > 0 ? { lines: lastLines, result: lastLines.join("\n") } : {}),
565
+ });
566
+ }
567
+ catch {
568
+ /* same: sink failures never fault the teardown */
569
+ }
570
+ }
571
+ settled++;
572
+ }
573
+ return settled;
574
+ }
575
+ /** design/129: the session-scoped children's TERMINAL anchor — call from your session release/sweep.
576
+ * Aborts every running session-scoped background child registered under `sessionId` and settles it
577
+ * `killed` (the child's own catch/then would also classify the abort as killed; this makes the registry
578
+ * state immediate rather than waiting on the child's promise). Idempotent. */
579
+ reapSessionBackground(sessionId, scope) {
580
+ const access = { owner: sessionId, ...(scope !== undefined ? { scope } : {}) };
581
+ let reaped = 0;
582
+ for (const handle of this.handles.values()) {
583
+ // design/135 G2: a session-scoped (persistent) MONITOR is reaped at the same terminal anchor —
584
+ // tear down the watcher first (no post-reap terminal notification), then best-effort kill.
585
+ if (handle.type === "monitor" && handle.status === "running" && handle.sessionScoped && canAccess(handle, access)) {
586
+ this.markStopSource(handle.id, "system");
587
+ if (handle.watcher !== undefined) {
588
+ handle.timers.clearInterval(handle.watcher);
589
+ handle.watcher = undefined;
590
+ }
591
+ // design/128 T1-1 (same gate as settleKilledForOwner): session release is an AUTO sweep too — on a
592
+ // retain-declared env the watched process is left alive (only the WATCH ends here); the handle still
593
+ // settles because the session, its watcher and its notification lane are gone either way.
594
+ if (handle.env.backgroundCapabilities.retainBackgroundProcesses !== true) {
595
+ void handle.env.killBackground(handle.shellId).catch(() => undefined);
596
+ }
597
+ handle.status = "killed";
598
+ handle.stoppedBy = handle.stopSource ?? "system";
599
+ handle.updatedAt = Date.now();
600
+ reaped++;
601
+ continue;
602
+ }
603
+ // 黑板 [636]① — the session-resident BASH shell's terminal anchor (mirrors the monitor arm above):
604
+ // stop the watcher first (no post-reap terminal notification — the session and its lanes are gone),
605
+ // then best-effort kill behind the same design/128 T1-1 retain gate (a retain-declared env's
606
+ // delivered daemons stay alive; only the registry row settles).
607
+ if (handle.type === "background_bash" && handle.status === "running" && handle.sessionScoped && canAccess(handle, access)) {
608
+ this.markStopSource(handle.id, "system");
609
+ if (handle.watcher !== undefined) {
610
+ clearInterval(handle.watcher);
611
+ handle.watcher = undefined;
612
+ }
613
+ if (handle.env.backgroundCapabilities.retainBackgroundProcesses !== true) {
614
+ void handle.env.killBackground(handle.shellId).catch(() => undefined);
615
+ }
616
+ handle.status = "killed";
617
+ handle.stoppedBy = handle.stopSource ?? "system";
618
+ handle.updatedAt = Date.now();
619
+ reaped++;
620
+ continue;
621
+ }
622
+ if (handle.type !== "background_agent" || handle.status !== "running" || !handle.sessionScoped)
623
+ continue;
624
+ if (!canAccess(handle, access))
625
+ continue;
626
+ // design/134 §3.3: session release is nobody's explicit stop — "system" (marked BEFORE the abort).
627
+ this.markStopSource(handle.id, "system");
628
+ handle.abort.abort();
629
+ this.settleBackgroundAgent(handle.id, { status: "killed", error: "session released" });
630
+ reaped++;
631
+ }
632
+ // codex 终审 1.255 F1: fire the session-reap hooks (swallow-guarded) — the session-scoped subagent
633
+ // retain ledger's anchor ②. Fired even when nothing was running: idle-but-retained rows must release
634
+ // on session terminal too (a settled child holds a pinned session until this call or its TTL).
635
+ for (const hook of this.sessionReapHooks) {
636
+ try {
637
+ hook(sessionId, scope);
638
+ }
639
+ catch {
640
+ /* an observer bug must not fault the reap */
641
+ }
642
+ }
643
+ return reaped;
644
+ }
645
+ /** 黑板 [651] (reap 责任裁定 B) — the FULL-registry reap for the ENGINE's own exit path. TOC form:
646
+ * the engine process IS the session terminal (the shell has no line to call — [650] live evidence:
647
+ * detached bg children reparent to PID 1 and outlive the engine), so the engine's SIGHUP/SIGTERM/
648
+ * normal-exit terminus calls THIS. Enumerates every running session-scoped row's (owner, scope)
649
+ * pair (owner = the sessionId by design/129 discipline) and runs the per-session reap on each —
650
+ * retain-declared envs keep their processes (rows settle only), exactly like the per-session anchor.
651
+ * Timing discipline ([651]①): call at the hardShutdown TERMINUS, never at drain start — an
652
+ * in-flight turn may still be TaskOutput-following a bg process. Returns total rows reaped. */
653
+ reapAllSessionBackground() {
654
+ const pairs = new Map();
655
+ for (const handle of this.handles.values()) {
656
+ if (handle.status !== "running")
657
+ continue;
658
+ if (!handle.sessionScoped || handle.owner === undefined)
659
+ continue;
660
+ const key = JSON.stringify([handle.owner, handle.scope ?? null]);
661
+ if (!pairs.has(key))
662
+ pairs.set(key, { owner: handle.owner, ...(handle.scope !== undefined ? { scope: handle.scope } : {}) });
663
+ }
664
+ let reaped = 0;
665
+ for (const p of pairs.values())
666
+ reaped += this.reapSessionBackground(p.owner, p.scope);
667
+ return reaped;
668
+ }
669
+ /** codex 终审 1.255 F1 — subscribe to `reapSessionBackground` (the design/129 session terminal anchor).
670
+ * Returns an unsubscribe. Hooks are best-effort observers; a throwing hook is swallowed. */
671
+ onSessionReap(hook) {
672
+ this.sessionReapHooks.add(hook);
673
+ return () => void this.sessionReapHooks.delete(hook);
674
+ }
675
+ /** G2b watcher (design/116 §7): 1s poll loop that spools increments (rolling bound) and fires ONE
676
+ * completion notification at the terminal state. `unref()`d — a watcher never keeps the process alive.
677
+ * Stops itself on terminal, on a vanished shell (env disposed), or when the handle is evicted. */
678
+ startBashWatcher(id, onTerminal) {
679
+ const handle = this.handles.get(id);
680
+ if (!handle || handle.type !== "background_bash")
681
+ return;
682
+ handle.spool = { stdout: "", stderr: "", rolledChars: 0 };
683
+ const ROLL_CAP = 2 * TASK_OUTPUT_MAX_CHARS; // 2× the env-cap (not the default): a later poll may raise TASK_MAX_OUTPUT_LENGTH, and rolled-off chars are unrecoverable
684
+ const roll = (s) => {
685
+ if (s.length <= ROLL_CAP)
686
+ return s;
687
+ const half = Math.floor(ROLL_CAP / 2);
688
+ handle.spool.rolledChars += s.length - 2 * half;
689
+ return s.slice(0, half) + s.slice(s.length - half);
690
+ };
691
+ let ticking = false; // re-entrancy guard: a slow env poll must not stack ticks
692
+ const stop = () => {
693
+ if (handle.watcher !== undefined) {
694
+ clearInterval(handle.watcher);
695
+ handle.watcher = undefined;
696
+ }
697
+ };
698
+ const tick = async () => {
699
+ if (ticking)
700
+ return;
701
+ ticking = true;
702
+ try {
703
+ if (this.handles.get(id) !== handle)
704
+ return stop(); // evicted (gc / clearBackgroundForOwner)
705
+ if (handle.status !== "running")
706
+ return stop(); // already terminal (TaskStop landed) — never resurrect
707
+ const r = await handle.env.pollBackground(handle.shellId);
708
+ // codex 1.254 F1 (same class as the monitor watcher): the poll await is a suspension point —
709
+ // stopBackgroundBash may have landed "killed" meanwhile; a stale in-flight tick must not overwrite
710
+ // it back to "running" (the interval is already cleared, so the resurrected status would stick forever).
711
+ if (this.handles.get(id) !== handle || handle.status !== "running")
712
+ return stop();
713
+ if (!r.ok)
714
+ return stop(); // vanished (env disposed / killed elsewhere) — nothing to notify about
715
+ const spool = handle.spool;
716
+ spool.stdout = roll(spool.stdout + r.value.stdout);
717
+ spool.stderr = roll(spool.stderr + r.value.stderr);
718
+ handle.status = statusFromBackground(r.value.status, r.value.exitCode);
719
+ // design/134: the env reported killed (TaskStop marked "parent" before killBackground; anything
720
+ // else — killed out-of-band — has no claimant and lands "system").
721
+ if (handle.status === "killed" && handle.stoppedBy === undefined)
722
+ handle.stoppedBy = handle.stopSource ?? "system";
723
+ handle.updatedAt = Date.now();
724
+ if (r.value.status !== "running") {
725
+ spool.exitCode = r.value.exitCode;
726
+ stop();
727
+ const tail = spool.stdout.slice(-2_000);
728
+ onTerminal({
729
+ task_id: id,
730
+ task_type: "background_bash",
731
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
732
+ status: handle.status === "completed" ? "completed" : handle.status === "killed" ? "killed" : "failed",
733
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
734
+ // CC 2.1.198 live verbatim on the happy path (reminders live-capture §6:
735
+ // `Background command "<desc>" completed (exit code N)`); non-exit statuses keep the
736
+ // sema wording (no CC sample for those faces).
737
+ summary: r.value.status === "exited"
738
+ ? `Background command "${(handle.description ?? "background command").slice(0, 200)}" completed (exit code ${r.value.exitCode})`
739
+ : `${(handle.description ?? "background command").slice(0, 200)} — ${r.value.status}`,
740
+ ...(tail.length > 0 ? { result: tail } : {}),
741
+ });
742
+ }
743
+ }
744
+ catch {
745
+ stop(); // a throwing env adapter must not leave a zombie interval
746
+ }
747
+ finally {
748
+ ticking = false;
749
+ }
750
+ };
751
+ handle.watcher = setInterval(() => void tick(), 1_000);
752
+ handle.watcher.unref?.();
753
+ }
754
+ /** design/135 G2: register a spawned background process as a MONITOR — the watcher polls at the batch
755
+ * window (200ms), turns each completed stdout line into an event, batches lines within one tick into
756
+ * ONE notification, enforces the non-persistent timeout, auto-stops an event storm, and fires exactly
757
+ * one terminal notification (exit code / timeout / auto-stop / env-side kill). */
758
+ /** 飞轮 [492]② — the shells a run teardown must LEAVE ALIVE: running SESSION-scoped monitors of this
759
+ * session on THIS env (their whole point is outliving the turn; the env-wide dispose would otherwise
760
+ * orphan the watch — handle alive, process dead, watcher reporting a bogus env-death terminal). Env
761
+ * identity is part of the match so a shared registry never exempts another env's ids. */
762
+ sessionResidentShellIds(sessionId, env) {
763
+ const out = [];
764
+ for (const handle of this.handles.values()) {
765
+ // [636]①: session-resident BASH shells ride the same dispose except-list as persistent monitors.
766
+ if ((handle.type === "monitor" || handle.type === "background_bash") && handle.status === "running" && handle.sessionScoped === true && handle.owner === sessionId && handle.env === env) {
767
+ out.push(handle.shellId);
768
+ }
769
+ }
770
+ return out;
771
+ }
772
+ /** 飞轮 [511]③ — the OTHER shells a run teardown must leave alive: NON-persistent monitors still inside
773
+ * their timeout window, on THIS env. CC semantics: Monitor(persistent:false) lives to timeout_ms — the
774
+ * parent run ending is not an exit (timeout / process exit / TaskStop are). Deliberately NO owner
775
+ * filter: on a shared env an earlier run's (or another session's) in-window watch must survive this
776
+ * run's env-wide dispose too. Env identity stays part of the match, same as
777
+ * {@link sessionResidentShellIds} (a shared registry never exempts another env's ids). */
778
+ timeoutResidentShellIds(env) {
779
+ const out = [];
780
+ for (const handle of this.handles.values()) {
781
+ if (handle.type === "monitor" && handle.env === env && monitorInTimeoutWindow(handle)) {
782
+ out.push(handle.shellId);
783
+ }
784
+ }
785
+ return out;
786
+ }
787
+ registerMonitor(input) {
788
+ assertOwnership(input, "registerMonitor");
789
+ const id = this.mintTaskId("monitor");
790
+ const now = input.now ?? Date.now();
791
+ const timers = input.timers ?? defaultMonitorTimers;
792
+ const persistent = input.persistent === true;
793
+ const timeoutMs = persistent
794
+ ? undefined
795
+ : Math.min(MONITOR_MAX_TIMEOUT_MS, Math.max(1, Math.floor(input.timeoutMs ?? MONITOR_DEFAULT_TIMEOUT_MS)));
796
+ const handle = {
797
+ id,
798
+ type: "monitor",
799
+ status: "running",
800
+ ...(input.description !== undefined ? { description: input.description } : {}),
801
+ ...(input.owner !== undefined ? { owner: input.owner } : {}),
802
+ ...(input.scope !== undefined ? { scope: input.scope } : {}),
803
+ ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
804
+ ...(persistent ? { persistent: true } : {}),
805
+ ...(input.sessionScoped ? { sessionScoped: true } : {}),
806
+ createdAt: now,
807
+ updatedAt: now,
808
+ env: input.env,
809
+ shellId: input.shellId,
810
+ spool: { stdout: "", stderr: "", rolledChars: 0 },
811
+ lineBuf: "",
812
+ seq: 0,
813
+ batchTimes: [],
814
+ batchWindowMs: Math.max(1, Math.floor(input.batchWindowMs ?? MONITOR_BATCH_WINDOW_MS)),
815
+ maxBatchesPerMinute: Math.max(1, Math.floor(input.maxBatchesPerMinute ?? MONITOR_MAX_BATCHES_PER_MINUTE)),
816
+ ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt: timers.now() + timeoutMs } : {}),
817
+ timers,
818
+ ...(input.onEvent !== undefined ? { onEvent: input.onEvent } : {}),
819
+ };
820
+ this.handles.set(id, handle);
821
+ this.startMonitorWatcher(id);
822
+ return id;
823
+ }
824
+ /** The monitor watcher tick loop. Same zombie-proofing discipline as {@link startBashWatcher}
825
+ * (re-entrancy guard / stop on eviction / stop on vanished shell / stop on a throwing adapter),
826
+ * plus the three monitor-only exits: watcher-enforced timeout, event-storm auto-stop, and the
827
+ * line→batched-event emission path. All time reads go through the INJECTED clock (design/87). */
828
+ startMonitorWatcher(id) {
829
+ const handle = this.handles.get(id);
830
+ if (!handle || handle.type !== "monitor")
831
+ return;
832
+ const timers = handle.timers;
833
+ const ROLL_CAP = 2 * TASK_OUTPUT_MAX_CHARS; // 2× the env-cap (not the default): a later poll may raise TASK_MAX_OUTPUT_LENGTH, and rolled-off chars are unrecoverable
834
+ const roll = (s) => {
835
+ if (s.length <= ROLL_CAP)
836
+ return s;
837
+ const half = Math.floor(ROLL_CAP / 2);
838
+ handle.spool.rolledChars += s.length - 2 * half;
839
+ return s.slice(0, half) + s.slice(s.length - half);
840
+ };
841
+ let ticking = false;
842
+ const stop = () => {
843
+ if (handle.watcher !== undefined) {
844
+ timers.clearInterval(handle.watcher);
845
+ handle.watcher = undefined;
846
+ }
847
+ };
848
+ const emit = (n) => {
849
+ try {
850
+ handle.onEvent?.(n);
851
+ }
852
+ catch {
853
+ /* a notification sink must never break the watcher */
854
+ }
855
+ };
856
+ const clipEvent = (s) => s.length <= MONITOR_EVENT_RESULT_CAP ? s : `${s.slice(0, MONITOR_EVENT_RESULT_CAP)}\n…[+${s.length - MONITOR_EVENT_RESULT_CAP} chars truncated]`;
857
+ /** Drain lineBuf's remainder into the final batch (a terminal flush must not drop a last
858
+ * newline-less line — CC reports it with the exit). */
859
+ const pendingFinalLines = (lines) => {
860
+ const out = handle.lineBuf !== "" ? [...lines, handle.lineBuf] : lines;
861
+ handle.lineBuf = "";
862
+ return out;
863
+ };
864
+ const terminal = (status, summary, finalLines) => {
865
+ handle.updatedAt = Date.now();
866
+ stop();
867
+ emit({
868
+ task_id: id,
869
+ task_type: "monitor",
870
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
871
+ status,
872
+ ...(status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
873
+ summary: `${(handle.description ?? "monitor").slice(0, 200)} — ${summary}`,
874
+ ...(finalLines.length > 0 ? { lines: finalLines, result: clipEvent(finalLines.join("\n")) } : {}),
875
+ });
876
+ };
877
+ const tick = async () => {
878
+ if (ticking)
879
+ return;
880
+ ticking = true;
881
+ try {
882
+ if (this.handles.get(id) !== handle)
883
+ return stop(); // evicted (gc / clearBackgroundForOwner)
884
+ if (handle.status !== "running")
885
+ return stop(); // already terminal (TaskStop/reap landed) — never resurrect
886
+ const r = await handle.env.pollBackground(handle.shellId);
887
+ // codex 1.254 F1: the await above is a suspension point — TaskStop/stopMonitor/reapSessionBackground
888
+ // may have landed a terminal status meanwhile (they tear the interval down, but THIS tick is already
889
+ // in flight). First-writer-wins, same gate as settleBackgroundAgent: a stale tick must not resurrect
890
+ // a stopped handle (status/updatedAt writes) nor emit events/terminal over the landed state.
891
+ if (this.handles.get(id) !== handle || handle.status !== "running")
892
+ return stop();
893
+ if (!r.ok) {
894
+ // codex 1.254 F2: an env-side vanish (env disposed / process killed out of band) previously just
895
+ // stopped the watcher SILENTLY, leaving the handle "running" forever (and its subscriber waiting
896
+ // for a terminal notification that would never come). Land failed + exactly one terminal notify
897
+ // (statusFromBackground maps an unknown/vanished state to failed — same family as the bash lane).
898
+ handle.status = "failed";
899
+ return terminal("failed", `monitor lost its process (${r.error.message}); watch ended.`, pendingFinalLines([]));
900
+ }
901
+ const spool = handle.spool;
902
+ spool.stdout = roll(spool.stdout + r.value.stdout);
903
+ spool.stderr = roll(spool.stderr + r.value.stderr); // ⑤ stderr: spooled (TaskOutput-readable), never an event
904
+ // Complete lines only: the partial tail waits in lineBuf for its newline (or the terminal flush).
905
+ handle.lineBuf += r.value.stdout;
906
+ const parts = handle.lineBuf.split(/\r?\n/);
907
+ handle.lineBuf = parts.pop() ?? "";
908
+ const lines = parts.filter((l) => l !== "");
909
+ handle.status = statusFromBackground(r.value.status, r.value.exitCode);
910
+ if (r.value.status !== "running") {
911
+ // ③/env-side terminal: exit (code reported), env hard-wall kill, or crash.
912
+ spool.exitCode = r.value.exitCode;
913
+ if (handle.status === "killed" && handle.stoppedBy === undefined)
914
+ handle.stoppedBy = handle.stopSource ?? "system";
915
+ const label = r.value.status === "exited"
916
+ ? `exited(code ${r.value.exitCode}); watch ended.`
917
+ : `${r.value.status}; watch ended.`;
918
+ return terminal(handle.status === "completed" ? "completed" : handle.status === "killed" ? "killed" : "failed", label, pendingFinalLines(lines));
919
+ }
920
+ const now = timers.now();
921
+ // ④ watcher-enforced timeout (non-persistent only; the env hard wall is a backstop behind this).
922
+ if (handle.deadlineAt !== undefined && now >= handle.deadlineAt) {
923
+ this.markStopSource(id, "timeout"); // open-enum StopSource (design/134): a timeout is nobody's explicit stop
924
+ const k = await handle.env.killBackground(handle.shellId); // Result-typed, never throws
925
+ if (this.handles.get(id) !== handle || handle.status !== "running")
926
+ return stop(); // F1 gate (kill await = suspension point)
927
+ // codex 1.254 F3: an ignored kill failure landed an HONESTY-BREAKING "killed" over a process that
928
+ // may still be running. Only a successful kill (or not_found = already gone) earns "killed";
929
+ // otherwise land failed with the honest caveat.
930
+ if (!k.ok && k.error.code !== "not_found") {
931
+ handle.status = "failed";
932
+ return terminal("failed", `timed out after ${handle.timeoutMs}ms, but the kill failed (${k.error.message}); the process may still be running. Watch ended.`, pendingFinalLines(lines));
933
+ }
934
+ handle.status = "killed";
935
+ handle.stoppedBy = handle.stopSource ?? "timeout";
936
+ return terminal("killed", `timed out after ${handle.timeoutMs}ms; the process was killed and the watch ended.`, pendingFinalLines(lines));
937
+ }
938
+ if (lines.length === 0) {
939
+ if (r.value.stdout.length > 0 || r.value.stderr.length > 0)
940
+ handle.updatedAt = Date.now();
941
+ return;
942
+ }
943
+ // ⑥ event-storm auto-stop: a monitor spamming batches would drown the model's turn boundaries.
944
+ const cutoff = now - 60_000;
945
+ handle.batchTimes = handle.batchTimes.filter((t) => t > cutoff);
946
+ handle.batchTimes.push(now);
947
+ if (handle.batchTimes.length > handle.maxBatchesPerMinute) {
948
+ this.markStopSource(id, "system");
949
+ const k = await handle.env.killBackground(handle.shellId);
950
+ if (this.handles.get(id) !== handle || handle.status !== "running")
951
+ return stop(); // F1 gate (kill await = suspension point)
952
+ if (!k.ok && k.error.code !== "not_found") {
953
+ // codex 1.254 F3 (same class as the timeout branch): no honest kill ⇒ no "killed" claim.
954
+ handle.status = "failed";
955
+ return terminal("failed", `auto-stop triggered (more than ${handle.maxBatchesPerMinute} notification batches within a minute), but the kill failed (${k.error.message}); the process may still be running. Watch ended.`, pendingFinalLines(lines));
956
+ }
957
+ handle.status = "killed";
958
+ handle.stoppedBy = handle.stopSource ?? "system";
959
+ return terminal("killed", `auto-stopped: it produced too many events (more than ${handle.maxBatchesPerMinute} notification batches within a minute). ` +
960
+ `The process was killed; its full output remains readable via TaskOutput. Restart it with a quieter command (filter its output) if you still need the watch.`, pendingFinalLines(lines));
961
+ }
962
+ // ①/② the event path: every stdout line is an event; lines within one 200ms tick batch into ONE notification.
963
+ handle.seq += 1;
964
+ handle.updatedAt = Date.now();
965
+ emit({
966
+ task_id: id,
967
+ task_type: "monitor",
968
+ ...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
969
+ status: "event",
970
+ seq: handle.seq,
971
+ lines,
972
+ summary: `${(handle.description ?? "monitor").slice(0, 200)} — ${lines.length} new output line(s)`,
973
+ result: clipEvent(lines.join("\n")),
974
+ });
975
+ }
976
+ catch {
977
+ stop(); // a throwing env adapter must not leave a zombie interval
978
+ }
979
+ finally {
980
+ ticking = false;
981
+ }
982
+ };
983
+ handle.watcher = timers.setInterval(tick, handle.batchWindowMs);
984
+ }
985
+ registerWorkflow(input) {
986
+ assertOwnership(input, "registerWorkflow");
987
+ const id = input.runId;
988
+ if (this.handles.has(id))
989
+ throw new Error(`TaskRegistry: task id already registered: ${id}`);
990
+ const now = input.now ?? Date.now();
991
+ const handle = {
992
+ id,
993
+ type: "workflow",
994
+ status: "running",
995
+ ...(input.description !== undefined ? { description: input.description } : {}),
996
+ ...(input.owner !== undefined ? { owner: input.owner } : {}),
997
+ ...(input.scope !== undefined ? { scope: input.scope } : {}),
998
+ ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
999
+ createdAt: now,
1000
+ updatedAt: now,
1001
+ runId: id,
1002
+ handle: input.handle,
1003
+ ...(input.store !== undefined ? { store: input.store } : {}),
1004
+ ...(input.onServedTerminal !== undefined ? { onServedTerminal: input.onServedTerminal } : {}),
1005
+ };
1006
+ this.handles.set(id, handle);
1007
+ void input.handle.done.then((res) => {
1008
+ handle.status = "completed";
1009
+ // 复审 r2 MAJOR: capture the bounded return value so a store-less terminal poll carries it (ackServed
1010
+ // then correctly acks a poll that DID hand over the result).
1011
+ // 黑板 [413] (NH-1): prefer the FULL text when the display bound truncated it — the poll reply inlines
1012
+ // it and the generic large-result offload mints a real read_tool_result ref (the truncated tail
1013
+ // previously dead-ended with no retrieval path).
1014
+ const run = res?.run;
1015
+ const r = run?.resultFull ?? run?.result;
1016
+ if (typeof r === "string")
1017
+ handle.result = r;
1018
+ handle.updatedAt = Date.now();
1019
+ }, (err) => {
1020
+ if (handle.status !== "cancelled")
1021
+ handle.status = "failed";
1022
+ // T2A-8 (2026-07-05 soak): keep the failure reason — a store-less terminal poll previously reported
1023
+ // only "failed" with the rejection dropped here, leaving the model no self-correction signal.
1024
+ handle.error = boundedRedactedSummary(err instanceof Error ? err.message : String(err), 500);
1025
+ handle.updatedAt = Date.now();
1026
+ });
1027
+ return id;
1028
+ }
1029
+ async pollTask(id, access, opts = {}) {
1030
+ const taskId = id.trim();
1031
+ const deadline = opts.block ? blockDeadline(opts.timeoutMs, Date.now()) : undefined;
1032
+ const handle = this.resolveInternal(taskId, access);
1033
+ if (handle) {
1034
+ if (handle.type === "background_bash")
1035
+ return this.pollBackgroundBash(handle, taskId, opts.filter, deadline, opts.signal);
1036
+ if (handle.type === "background_agent")
1037
+ return this.pollBackgroundAgent(handle, deadline, opts.signal);
1038
+ if (handle.type === "monitor")
1039
+ return this.pollMonitor(handle, opts.filter, deadline, opts.signal);
1040
+ return this.pollWorkflow(handle, access, deadline, opts.signal);
1041
+ }
1042
+ if (opts.workflowStore) {
1043
+ // Blocked poll over the durable store: re-read until terminal or deadline (no cursor to lose here).
1044
+ for (;;) {
1045
+ const run = await opts.workflowStore.get(taskId);
1046
+ if (!run || !canAccessWorkflowRun(run, access))
1047
+ break;
1048
+ if (run.status !== "running" || deadline === undefined || Date.now() >= deadline || opts.signal?.aborted) {
1049
+ const out = formatWorkflowRun(run);
1050
+ if (run.status === "running" && deadline !== undefined && Date.now() >= deadline)
1051
+ out.details.retrieval_status = "timeout"; // S5
1052
+ return out;
1053
+ }
1054
+ await sleepPollStep(deadline, opts.signal);
1055
+ }
1056
+ }
1057
+ return noTask(taskId);
1058
+ }
1059
+ async stopTask(id, access, opts = {}) {
1060
+ const taskId = id.trim();
1061
+ const handle = this.resolveInternal(taskId, access);
1062
+ if (handle) {
1063
+ if (handle.type === "background_bash")
1064
+ return this.stopBackgroundBash(handle);
1065
+ if (handle.type === "background_agent")
1066
+ return this.stopBackgroundAgent(handle);
1067
+ if (handle.type === "monitor")
1068
+ return this.stopMonitor(handle);
1069
+ return this.stopWorkflow(handle);
1070
+ }
1071
+ // CC206-B 欠账 (CC `y1e` 206:575269-575279 → `cgo` 206:575142-575178): id miss ⇒ NAME leg — a background
1072
+ // agent spawned with a name (sema: the Agent call's description label) is stoppable by that name. Unique
1073
+ // match stops; multiple matches list candidates (CC ambiguous posture, `USd` 206:575178+ adapted to the
1074
+ // bg-agent face — sema has no teammate registry here); zero matches fall through to the enriched
1075
+ // not-found below. Owner semantics stay FAIL-CLOSED: CC's `not_owner` ("Task X is owned by Y") reveals a
1076
+ // foreign task's existence — sema's canAccess guard already filtered those rows out of the name pool, so
1077
+ // a non-owner reads not_found (design/115 info-hiding posture, deliberate divergence, recorded here).
1078
+ // fable 复审 F2 (CC ladder invariant): EVERY exact-id resolution precedes the fuzzy NAME leg — the
1079
+ // second exact-id namespace (a durable workflow runId) must resolve BEFORE name matching, or a bg
1080
+ // agent whose description normalizes to that runId would shadow an exact TaskStop(runId) address.
1081
+ if (opts.workflowStore) {
1082
+ const run = await opts.workflowStore.get(taskId);
1083
+ if (run && canAccessWorkflowRun(run, access)) {
1084
+ return {
1085
+ content: JSON.stringify({
1086
+ task_id: taskId,
1087
+ runId: taskId,
1088
+ status: run.status,
1089
+ stopped: false,
1090
+ error: "workflow task is not attached to this process; cannot stop it here",
1091
+ }),
1092
+ details: {
1093
+ task_id: taskId,
1094
+ type: "workflow",
1095
+ status: run.status,
1096
+ retrieval_status: "success",
1097
+ error: "not_local",
1098
+ },
1099
+ };
1100
+ }
1101
+ }
1102
+ const byName = this.resolveBackgroundAgentByName(taskId, access);
1103
+ if (byName.status === "found")
1104
+ return this.stopBackgroundAgent(byName.handle);
1105
+ if (byName.status === "ambiguous") {
1106
+ return {
1107
+ content: byName.message,
1108
+ details: { task_id: taskId, retrieval_status: "not_ready", error: "ambiguous" },
1109
+ };
1110
+ }
1111
+ // CC `Kws` enrichment (206:575251-575258): fuzzy suggestion + the caller's running agents, so the model
1112
+ // can self-correct without a TaskList round-trip.
1113
+ return noTaskForStop(taskId, {
1114
+ ...(byName.suggestion !== undefined ? { suggestion: byName.suggestion } : {}),
1115
+ runningAgents: this.runningBackgroundAgentLabels(access),
1116
+ });
1117
+ }
1118
+ /** CC206-B name resolution over the caller-visible background_agent rows (CC `cgo`/`gvy`, 206:575142-575178
1119
+ * and 206:575199-575210 — sema's single-lane subset: no teammate/name-registry branches). Matching key =
1120
+ * normalized description (the spawn label that IS the agent's name on this face). Running rows are
1121
+ * preferred over terminal ones (CC `gvy`: `o.length > 0 ? o : n`), so a re-used label addresses the live
1122
+ * agent, while a lone terminal match still resolves (stop is then a no-op receipt with its real status). */
1123
+ resolveBackgroundAgentByName(name, access) {
1124
+ const key = normalizeAgentName(name);
1125
+ if (key === "")
1126
+ return { status: "not_found" };
1127
+ // fable 复审 F3 (CC `cgo` exact-name layer, 206:570911 `JTd`): try a VERBATIM description match
1128
+ // first — a unique exact hit resolves even when normalization would collide (labels "build" and
1129
+ // "Build" both present, query "build": CC finds the exact "build", not ambiguous).
1130
+ const exact = [];
1131
+ for (const h of this.handles.values()) {
1132
+ if (h.type !== "background_agent" || !canAccess(h, access))
1133
+ continue;
1134
+ if (h.description === name)
1135
+ exact.push(h);
1136
+ }
1137
+ const exactRunning = exact.filter((h) => h.status === "running");
1138
+ const exactPool = exactRunning.length > 0 ? exactRunning : exact;
1139
+ if (exactPool.length === 1)
1140
+ return { status: "found", handle: exactPool[0] };
1141
+ // 三轮复查 (CC `JTd` 206:570911-570930 亲核): exact-layer ambiguity SHORT-CIRCUITS — CC returns
1142
+ // ambiguous over the exact pool without consulting the normalized layer (`if (s.length > 1) return
1143
+ // { status: "ambiguous", candidates }`). Falling through instead could MIS-RESOLVE: with two
1144
+ // terminal rows labeled "build" and one running row labeled "Build", query "build" would skip the
1145
+ // ≥2 verbatim matches and stop the running "Build" — CC answers ambiguous there.
1146
+ if (exactPool.length > 1) {
1147
+ return {
1148
+ status: "ambiguous",
1149
+ message: `Multiple background agents match "${name}": ${exactPool.map((h) => (h.description ? `${h.id} (${h.description})` : h.id)).join(", ")}. Use the task ID.`,
1150
+ };
1151
+ }
1152
+ const matches = [];
1153
+ for (const h of this.handles.values()) {
1154
+ if (h.type !== "background_agent" || !canAccess(h, access))
1155
+ continue;
1156
+ if (h.description !== undefined && normalizeAgentName(h.description) === key)
1157
+ matches.push(h);
1158
+ }
1159
+ const running = matches.filter((h) => h.status === "running");
1160
+ const pool = running.length > 0 ? running : matches;
1161
+ if (pool.length === 1)
1162
+ return { status: "found", handle: pool[0] };
1163
+ if (pool.length > 1) {
1164
+ // CC ambiguous posture (`USd` 206:575178-575180 adapted): list the colliding rows, point at the
1165
+ // unambiguous address (the task ID — sema bg agents have no name@team form).
1166
+ return {
1167
+ status: "ambiguous",
1168
+ message: `Multiple background agents match "${name}": ${pool.map((h) => (h.description ? `${h.id} (${h.description})` : h.id)).join(", ")}. Use the task ID.`,
1169
+ };
1170
+ }
1171
+ // CC `hvy` (206:575184-575198): the suggestion pool is RUNNING rows' names; distance runs over the
1172
+ // NORMALIZED keys, but the suggested string is the map VALUE — an ADDRESSABLE handle (CC: agentId /
1173
+ // registered name; sema: the raw spawn label, which the name leg re-normalizes on retry).
1174
+ const byKey = new Map();
1175
+ for (const h of this.handles.values()) {
1176
+ if (h.type !== "background_agent" || h.status !== "running" || !canAccess(h, access) || h.description === undefined)
1177
+ continue;
1178
+ const k = normalizeAgentName(h.description);
1179
+ if (k !== "" && !byKey.has(k))
1180
+ byKey.set(k, h.description);
1181
+ }
1182
+ const closest = closestName(key, byKey.keys());
1183
+ return { status: "not_found", ...(closest !== undefined ? { suggestion: byKey.get(closest) } : {}) };
1184
+ }
1185
+ /** CC `ugo` (206:575227-575243): `id (description)` rows for the not-found footer — running bg agents the
1186
+ * CALLER can address (canAccess scopes; CC's self/observer/named exclusions have no counterpart rows here). */
1187
+ runningBackgroundAgentLabels(access) {
1188
+ const out = [];
1189
+ for (const h of this.handles.values()) {
1190
+ if (h.type !== "background_agent" || h.status !== "running" || !canAccess(h, access))
1191
+ continue;
1192
+ out.push(h.description ? `${h.id} (${h.description})` : h.id);
1193
+ }
1194
+ return out;
1195
+ }
1196
+ lastGcAt = 0;
1197
+ /**
1198
+ * 预清(飞轮 /tasks + footer pill 的数据源正道): list the caller's OWN tasks as a bounded DISPLAY
1199
+ * projection — never the internal handles (no shellId/env/abort). Same owner/scope guard as poll/stop;
1200
+ * a caller only ever sees its own tasks. Sorted newest-first, capped at `limit` (default 50).
1201
+ * CC parity note: CC's /tasks is a UI/SDK surface, NOT an LLM tool — expose this via SDK/service,
1202
+ * do not mount it as a model tool by default.
1203
+ */
1204
+ list(access, opts = {}) {
1205
+ const limit = Math.min(500, Math.max(1, Math.floor(opts.limit ?? 50)));
1206
+ const out = [];
1207
+ for (const h of this.handles.values()) {
1208
+ if (!canAccess(h, access))
1209
+ continue;
1210
+ out.push({
1211
+ task_id: h.id,
1212
+ type: h.type,
1213
+ status: h.status,
1214
+ sessionScoped: h.sessionScoped === true,
1215
+ ...(h.description !== undefined ? { description: h.description } : {}),
1216
+ ...(h.toolUseId !== undefined ? { toolUseId: h.toolUseId } : {}),
1217
+ createdAt: h.createdAt,
1218
+ updatedAt: h.updatedAt,
1219
+ });
1220
+ }
1221
+ out.sort((a, b) => b.createdAt - a.createdAt);
1222
+ return out.slice(0, limit);
1223
+ }
1224
+ /** Throttled {@link gc} for hot-path callers (design/115 review B5): prepare-task runs once per task,
1225
+ * and a full-map sweep per prepare is pure overhead — sweep at most once per minute. */
1226
+ maybeGc(now = Date.now()) {
1227
+ if (now - this.lastGcAt < 60_000)
1228
+ return 0;
1229
+ this.lastGcAt = now;
1230
+ return this.gc(now);
1231
+ }
1232
+ gc(now = Date.now(), terminalTtlMs = 60 * 60 * 1000) {
1233
+ let deleted = 0;
1234
+ for (const [id, handle] of this.handles) {
1235
+ if (handle.status === "running" || handle.status === "pending")
1236
+ continue;
1237
+ if (now - handle.updatedAt < terminalTtlMs)
1238
+ continue;
1239
+ this.handles.delete(id);
1240
+ if (handle.type === "background_bash") {
1241
+ this.legacyToTaskId.delete(String(handle.shellId));
1242
+ if (handle.watcher !== undefined)
1243
+ clearInterval(handle.watcher);
1244
+ }
1245
+ if (handle.type === "monitor" && handle.watcher !== undefined) {
1246
+ handle.timers.clearInterval(handle.watcher);
1247
+ handle.watcher = undefined;
1248
+ }
1249
+ deleted++;
1250
+ }
1251
+ return deleted;
1252
+ }
1253
+ clearBackgroundForOwner(access) {
1254
+ let deleted = 0;
1255
+ for (const [id, handle] of this.handles) {
1256
+ // design/135 G2: per-run teardown also evicts this run's MONITOR handles (the underlying process
1257
+ // dies with disposeBackgroundShells anyway) — EXCEPT session-scoped ones, which outlive the turn
1258
+ // and are reaped by reapSessionBackground (design/129 lifetime discipline, mirrors the agent reap).
1259
+ if (handle.type === "monitor" && !handle.sessionScoped && canAccess(handle, access)) {
1260
+ // 飞轮 [511]③ (CC parity): an in-window non-persistent monitor survives the per-run evict too —
1261
+ // evicting it would orphan the watch (the watcher stops on eviction; nobody would be left to
1262
+ // enforce the timeout, park between-turns events, or serve the spool). The row is evicted once
1263
+ // TERMINAL (its own timeout/exit/TaskStop landed) or past its window; until then the terminal
1264
+ // spool stays TaskOutput-readable across turns, which is exactly the CC posture.
1265
+ if (monitorInTimeoutWindow(handle))
1266
+ continue;
1267
+ this.handles.delete(id);
1268
+ if (handle.watcher !== undefined) {
1269
+ handle.timers.clearInterval(handle.watcher);
1270
+ handle.watcher = undefined;
1271
+ }
1272
+ deleted++;
1273
+ continue;
1274
+ }
1275
+ if (handle.type !== "background_bash" || !canAccess(handle, access))
1276
+ continue;
1277
+ // 黑板 [636]① (mirrors the monitor arm above): a session-scoped bash row outlives the per-run evict —
1278
+ // its watcher keeps ticking on real timers (between-turns terminal parks on the [492]② pending lane)
1279
+ // and its spool stays TaskOutput-readable across turns; reapSessionBackground is its terminal anchor.
1280
+ if (handle.sessionScoped)
1281
+ continue;
1282
+ this.handles.delete(id);
1283
+ this.legacyToTaskId.delete(String(handle.shellId));
1284
+ if (handle.watcher !== undefined)
1285
+ clearInterval(handle.watcher);
1286
+ deleted++;
1287
+ }
1288
+ return deleted;
1289
+ }
1290
+ resolveInternal(id, access) {
1291
+ const taskId = this.handles.has(id) ? id : this.legacyToTaskId.get(id);
1292
+ if (!taskId)
1293
+ return undefined;
1294
+ const handle = this.handles.get(taskId);
1295
+ if (!handle || !canAccess(handle, access))
1296
+ return undefined;
1297
+ return handle;
1298
+ }
1299
+ async pollBackgroundBash(handle, requestedId, filter, deadline, signal) {
1300
+ let rx;
1301
+ if (filter !== undefined) {
1302
+ try {
1303
+ rx = new RegExp(filter);
1304
+ }
1305
+ catch {
1306
+ return {
1307
+ content: `Error (TaskOutput): invalid filter regex: ${filter}`,
1308
+ details: { task_id: handle.id, type: "background_bash", status: handle.status, retrieval_status: "not_ready" },
1309
+ };
1310
+ }
1311
+ }
1312
+ if (!hasBackgroundShell(handle.env))
1313
+ return noTask(requestedId);
1314
+ // Spool mode (design/116 §7 G2b + B5 close-out): with a watcher attached, the registry owns the FULL
1315
+ // accumulated output — TaskOutput serves a RE-READABLE snapshot (CC semantics: repeated polls return the
1316
+ // same task output, not a consume-once increment). Blocked waits just watch handle.status (the watcher
1317
+ // is the only env-cursor consumer, so there is no double-consumption race).
1318
+ if (handle.spool !== undefined) {
1319
+ while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
1320
+ await sleepPollStep(deadline, signal);
1321
+ }
1322
+ const spool = handle.spool;
1323
+ const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile); // [K-PLATFORM-SWEEP] MINOR#7: win32 \r\n lines
1324
+ const running = handle.status === "running";
1325
+ const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
1326
+ const displayStatus = spool.exitCode !== undefined ? `exited(code ${spool.exitCode})` : running ? "running" : handle.status;
1327
+ const rolledNote = spool.rolledChars > 0 ? `[!] ${spool.rolledChars} earlier char(s) dropped from the spool (memory bound)\n` : "";
1328
+ const body = `status: ${displayStatus}\n${rolledNote}--- stdout (full, re-readable) ---\n${shape(spool.stdout) || "(none)"}\n` +
1329
+ `--- stderr ---\n${shape(spool.stderr) || "(none)"}`;
1330
+ return {
1331
+ content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
1332
+ details: {
1333
+ task_id: handle.id,
1334
+ type: "background_bash",
1335
+ status: handle.status,
1336
+ retrieval_status: retrieval,
1337
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
1338
+ details: { status: running ? "running" : handle.status, exitCode: spool.exitCode },
1339
+ },
1340
+ };
1341
+ }
1342
+ // Blocked wait (design/116 W3, CC `block`): loop the incremental poll until terminal/deadline/abort,
1343
+ // ACCUMULATING each round's stdout/stderr — every pollBackground consumes the env cursor, so a wait
1344
+ // that discarded intermediate increments would silently lose output.
1345
+ // Rolling bound (design/116 review B3): the seam allows up to 8MB per increment × 300 rounds — an
1346
+ // unbounded `+=` is a prompt-injection-reachable OOM. Keep head+tail within 2× the final clip cap and
1347
+ // count what rolled off; the terminal marker reports it honestly.
1348
+ const ROLL_CAP = 2 * TASK_OUTPUT_MAX_CHARS; // 2× the env-cap (not the default): a later poll may raise TASK_MAX_OUTPUT_LENGTH, and rolled-off chars are unrecoverable
1349
+ let rolledChars = 0;
1350
+ const roll = (s) => {
1351
+ if (s.length <= ROLL_CAP)
1352
+ return s;
1353
+ const half = Math.floor(ROLL_CAP / 2);
1354
+ rolledChars += s.length - 2 * half;
1355
+ return s.slice(0, half) + s.slice(s.length - half);
1356
+ };
1357
+ let stdout = "";
1358
+ let stderr = "";
1359
+ let dropped = 0;
1360
+ for (;;) {
1361
+ const r = await handle.env.pollBackground(handle.shellId);
1362
+ if (!r.ok) {
1363
+ if (r.error.code === "not_found")
1364
+ return noTask(requestedId);
1365
+ const retrieval_status = r.error.code === "timeout" ? "timeout" : "not_ready";
1366
+ // design/116 review S4: a mid-wait poll error must NOT discard output already consumed from the
1367
+ // env cursor (it is gone from the source) — return the error WITH what accumulated so far.
1368
+ const salvaged = stdout.length > 0 || stderr.length > 0
1369
+ ? `\n--- output accumulated before the error ---\n${clipTaskOutput(stdout, handle.outputFile)}${stderr ? `\n--- stderr ---\n${clipTaskOutput(stderr, handle.outputFile)}` : ""}`
1370
+ : "";
1371
+ return {
1372
+ content: `Error (TaskOutput): ${r.error.message}${salvaged}`,
1373
+ details: { task_id: handle.id, type: "background_bash", status: handle.status, retrieval_status, error: r.error.code },
1374
+ };
1375
+ }
1376
+ stdout = roll(stdout + r.value.stdout);
1377
+ stderr = roll(stderr + r.value.stderr);
1378
+ dropped += r.value.bytesDroppedBeforeCursor ?? 0;
1379
+ handle.status = statusFromBackground(r.value.status, r.value.exitCode);
1380
+ if (handle.status === "killed" && handle.stoppedBy === undefined)
1381
+ handle.stoppedBy = handle.stopSource ?? "system"; // design/134: killed observed via poll (out-of-band kill ⇒ system)
1382
+ handle.updatedAt = Date.now();
1383
+ const running = r.value.status === "running";
1384
+ if (!running || deadline === undefined || Date.now() >= deadline || signal?.aborted) {
1385
+ // design/116 review S5 (CC 187 semantics): a blocked wait that hit the deadline while the task is
1386
+ // STILL RUNNING reports retrieval_status "timeout", not "success" — the model can tell "finished"
1387
+ // from "gave up waiting" without parsing the status line.
1388
+ const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
1389
+ const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile); // [K-PLATFORM-SWEEP] MINOR#7: win32 \r\n lines
1390
+ const displayStatus = r.value.status === "exited" ? `exited(code ${r.value.exitCode})` : r.value.status;
1391
+ const droppedNote = (dropped > 0 ? `[!] ${dropped} earlier byte(s) permanently dropped before this window (8MB buffer evicted)\n` : "") +
1392
+ (rolledChars > 0 ? `[!] ${rolledChars} accumulated char(s) dropped from the middle during the blocked wait (memory bound)\n` : "");
1393
+ const body = `status: ${displayStatus}\n${droppedNote}--- new stdout ---\n${shape(stdout) || "(none)"}\n` +
1394
+ `--- new stderr ---\n${shape(stderr) || "(none)"}`;
1395
+ return {
1396
+ content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
1397
+ details: {
1398
+ task_id: handle.id,
1399
+ type: "background_bash",
1400
+ status: handle.status,
1401
+ retrieval_status: retrieval,
1402
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
1403
+ details: { status: r.value.status, exitCode: r.value.exitCode, bytesDroppedBeforeCursor: dropped },
1404
+ },
1405
+ };
1406
+ }
1407
+ await sleepPollStep(deadline, signal);
1408
+ }
1409
+ }
1410
+ /** 黑板 [405]: this poll SERVED the terminal state in-band — fire the (once-only) ack hook so the
1411
+ * deployment can drop the redundant completion-inbox entry. Swallow-guarded. */
1412
+ fireServedTerminal(handle) {
1413
+ const cb = handle.onServedTerminal;
1414
+ if (!cb)
1415
+ return;
1416
+ handle.onServedTerminal = undefined;
1417
+ try {
1418
+ cb();
1419
+ }
1420
+ catch {
1421
+ /* an ack hook must never break a poll */
1422
+ }
1423
+ }
1424
+ async pollWorkflow(handle, access, deadline, signal) {
1425
+ const store = handle.store;
1426
+ if (store) {
1427
+ for (;;) {
1428
+ const run = await store.get(handle.runId);
1429
+ if (!run || !canAccessWorkflowRun(run, access))
1430
+ break;
1431
+ if (run.status !== "running" || deadline === undefined || Date.now() >= deadline || signal?.aborted) {
1432
+ const out = formatWorkflowRun(run);
1433
+ if (run.status === "running" && deadline !== undefined && Date.now() >= deadline)
1434
+ out.details.retrieval_status = "timeout"; // S5
1435
+ else if (run.status !== "running")
1436
+ this.fireServedTerminal(handle); // [405] terminal actually served
1437
+ return out;
1438
+ }
1439
+ await sleepPollStep(deadline, signal);
1440
+ }
1441
+ }
1442
+ // In-memory fallback (no store): the handle status flips via the done-promise hooks — block on it too.
1443
+ while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
1444
+ await sleepPollStep(deadline, signal);
1445
+ }
1446
+ if (handle.status !== "running")
1447
+ this.fireServedTerminal(handle); // [405] terminal served (in-memory lane)
1448
+ return {
1449
+ content: JSON.stringify({
1450
+ task_id: handle.id,
1451
+ runId: handle.id,
1452
+ type: "workflow",
1453
+ status: handle.status,
1454
+ ...(handle.status === "completed" && handle.result !== undefined
1455
+ ? { result: delimitUntrusted("workflow result", handle.result) }
1456
+ : {}),
1457
+ // T2A-8: hand over the captured failure reason (store lane's formatWorkflowRun already does).
1458
+ ...(handle.status === "failed" && handle.error !== undefined
1459
+ ? { error: delimitUntrusted("workflow error", handle.error) }
1460
+ : {}),
1461
+ note: handle.status === "running"
1462
+ ? "still running — poll again shortly."
1463
+ : handle.status === "completed"
1464
+ ? handle.result !== undefined
1465
+ ? "completed — `result` above is the workflow's return value."
1466
+ : "completed — its result was delivered on the completion notification."
1467
+ : handle.status === "failed" && handle.error !== undefined
1468
+ ? "failed — see error."
1469
+ : "workflow reached a terminal state.",
1470
+ }),
1471
+ details: {
1472
+ task_id: handle.id,
1473
+ type: "workflow",
1474
+ status: handle.status,
1475
+ retrieval_status: handle.status === "running" && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success", // S5
1476
+ },
1477
+ };
1478
+ }
1479
+ async stopBackgroundBash(handle) {
1480
+ if (handle.watcher !== undefined) {
1481
+ clearInterval(handle.watcher);
1482
+ handle.watcher = undefined;
1483
+ }
1484
+ // design/134 §3.3 (R3 MED, 同族同批): TaskStop主路 = parent, marked BEFORE the kill lands — an earlier
1485
+ // marker (service-wire "user") wins. A failed kill leaves only the pending marker (status untouched).
1486
+ this.markStopSource(handle.id, "parent");
1487
+ const r = await handle.env.killBackground(handle.shellId);
1488
+ if (!r.ok) {
1489
+ if (r.error.code === "not_found")
1490
+ return noTask(handle.id);
1491
+ const retrieval_status = r.error.code === "timeout" ? "timeout" : "not_ready";
1492
+ return {
1493
+ content: `Error (TaskStop): ${r.error.message}`,
1494
+ details: { task_id: handle.id, type: "background_bash", status: handle.status, retrieval_status, error: r.error.code },
1495
+ };
1496
+ }
1497
+ if (handle.stoppedBy === undefined)
1498
+ handle.stoppedBy = handle.stopSource ?? "parent";
1499
+ handle.status = "killed";
1500
+ handle.updatedAt = Date.now();
1501
+ return {
1502
+ content: `Terminated ${handle.id}.`,
1503
+ details: { task_id: handle.id, type: "background_bash", status: "killed", retrieval_status: "success", stoppedBy: handle.stoppedBy },
1504
+ };
1505
+ }
1506
+ /** design/135 G2: TaskOutput over a monitor — a RE-READABLE spool snapshot (the watcher owns the env
1507
+ * cursor, exactly the bash G2b spool posture): repeated polls return the same accumulated output. */
1508
+ async pollMonitor(handle, filter, deadline, signal) {
1509
+ let rx;
1510
+ if (filter !== undefined) {
1511
+ try {
1512
+ rx = new RegExp(filter);
1513
+ }
1514
+ catch {
1515
+ return {
1516
+ content: `Error (TaskOutput): invalid filter regex: ${filter}`,
1517
+ details: { task_id: handle.id, type: "monitor", status: handle.status, retrieval_status: "not_ready" },
1518
+ };
1519
+ }
1520
+ }
1521
+ while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
1522
+ await sleepPollStep(deadline, signal);
1523
+ }
1524
+ const spool = handle.spool;
1525
+ const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
1526
+ const running = handle.status === "running";
1527
+ const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
1528
+ const displayStatus = spool.exitCode !== undefined ? `exited(code ${spool.exitCode})` : running ? "running" : handle.status;
1529
+ const rolledNote = spool.rolledChars > 0 ? `[!] ${spool.rolledChars} earlier char(s) dropped from the spool (memory bound)\n` : "";
1530
+ const body = `status: ${displayStatus}\n${rolledNote}--- stdout (full, re-readable) ---\n${shape(spool.stdout) || "(none)"}\n` +
1531
+ `--- stderr ---\n${shape(spool.stderr) || "(none)"}`;
1532
+ return {
1533
+ content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
1534
+ details: {
1535
+ task_id: handle.id,
1536
+ type: "monitor",
1537
+ status: handle.status,
1538
+ retrieval_status: retrieval,
1539
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
1540
+ details: { status: running ? "running" : handle.status, exitCode: spool.exitCode },
1541
+ },
1542
+ };
1543
+ }
1544
+ /** design/135 G2: TaskStop over a monitor — same shape/attribution discipline as stopBackgroundBash
1545
+ * (mark "parent" BEFORE the kill; an earlier service-wire "user" marker wins; watcher torn down first
1546
+ * so a racing tick can't fire a duplicate terminal notification after this receipt). */
1547
+ async stopMonitor(handle) {
1548
+ if (handle.watcher !== undefined) {
1549
+ handle.timers.clearInterval(handle.watcher);
1550
+ handle.watcher = undefined;
1551
+ }
1552
+ this.markStopSource(handle.id, "parent");
1553
+ const r = await handle.env.killBackground(handle.shellId);
1554
+ if (!r.ok) {
1555
+ if (r.error.code === "not_found")
1556
+ return noTask(handle.id);
1557
+ const retrieval_status = r.error.code === "timeout" ? "timeout" : "not_ready";
1558
+ return {
1559
+ content: `Error (TaskStop): ${r.error.message}`,
1560
+ details: { task_id: handle.id, type: "monitor", status: handle.status, retrieval_status, error: r.error.code },
1561
+ };
1562
+ }
1563
+ if (handle.status === "running") {
1564
+ if (handle.stoppedBy === undefined)
1565
+ handle.stoppedBy = handle.stopSource ?? "parent";
1566
+ handle.status = "killed";
1567
+ handle.updatedAt = Date.now();
1568
+ }
1569
+ return {
1570
+ content: `Terminated ${handle.id}.`,
1571
+ details: {
1572
+ task_id: handle.id,
1573
+ type: "monitor",
1574
+ status: handle.status,
1575
+ retrieval_status: "success",
1576
+ ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
1577
+ },
1578
+ };
1579
+ }
1580
+ async pollBackgroundAgent(handle, deadline, signal) {
1581
+ while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
1582
+ await sleepPollStep(deadline, signal);
1583
+ }
1584
+ const running = handle.status === "running";
1585
+ const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
1586
+ const body = running
1587
+ ? `status: running
1588
+ The agent is still working — you will be notified when it completes.`
1589
+ : `status: ${handle.status}
1590
+ ${handle.error ? `error: ${handle.error}
1591
+ ` : ""}${handle.result ? `--- result ---
1592
+ ${clipTaskOutput(handle.result, handle.outputFile)}` : "(no result text)"}`;
1593
+ return {
1594
+ content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
1595
+ details: {
1596
+ task_id: handle.id,
1597
+ type: "background_agent",
1598
+ status: handle.status,
1599
+ retrieval_status: retrieval,
1600
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
1601
+ },
1602
+ };
1603
+ }
1604
+ async stopBackgroundAgent(handle) {
1605
+ if (handle.status === "running") {
1606
+ // design/134 §3.3 (R3 BLOCKER): this path flips killed DIRECTLY (bypassing settle, whose
1607
+ // first-writer guard then never runs) — so attribution must land HERE. Order is load-bearing:
1608
+ // mark first (first-marker-wins keeps an earlier service-wire "user"), then abort/flip.
1609
+ this.markStopSource(handle.id, "parent");
1610
+ handle.abort.abort();
1611
+ handle.status = "killed";
1612
+ handle.stoppedBy = handle.stopSource ?? "parent";
1613
+ handle.updatedAt = Date.now();
1614
+ }
1615
+ return {
1616
+ content: `Terminated ${handle.id}.`,
1617
+ details: {
1618
+ task_id: handle.id,
1619
+ type: "background_agent",
1620
+ status: handle.status,
1621
+ retrieval_status: "success",
1622
+ ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
1623
+ },
1624
+ };
1625
+ }
1626
+ async stopWorkflow(handle) {
1627
+ // T2A-3 (2026-07-05 soak): a stop on an ALREADY-terminal workflow must not rewrite history — the old
1628
+ // unconditional flip turned completed→cancelled, dropped the captured result from every later poll, and
1629
+ // reported stopped:true for work it never stopped (cf. stopBackgroundAgent's running-only guard).
1630
+ // Known microtask race (codex F-5, accepted): if `done` has fulfilled but its hook hasn't run yet, this
1631
+ // still sees `running` → replies stopped:true; the hook then lands completed+result, so the durable state
1632
+ // stays truthful — only that one receipt can be optimistic. cancel() into a finished run is a no-op.
1633
+ if (handle.status !== "running") {
1634
+ return {
1635
+ content: JSON.stringify({
1636
+ task_id: handle.id,
1637
+ runId: handle.id,
1638
+ type: "workflow",
1639
+ status: handle.status,
1640
+ stopped: false,
1641
+ note: `workflow already ${handle.status} — nothing to stop; poll TaskOutput for its final state.`,
1642
+ }),
1643
+ details: { task_id: handle.id, type: "workflow", status: handle.status, retrieval_status: "success" },
1644
+ };
1645
+ }
1646
+ handle.status = "cancelled";
1647
+ handle.updatedAt = Date.now();
1648
+ handle.handle.cancel("TaskStop requested");
1649
+ return {
1650
+ content: JSON.stringify({
1651
+ task_id: handle.id,
1652
+ runId: handle.id,
1653
+ type: "workflow",
1654
+ status: "cancelled",
1655
+ stopped: true,
1656
+ note: "workflow cancellation requested; durable run status maps cancellation to failed when it finalizes.",
1657
+ }),
1658
+ details: { task_id: handle.id, type: "workflow", status: "cancelled", retrieval_status: "success" },
1659
+ };
1660
+ }
1661
+ }
1662
+ export const defaultTaskRegistry = new TaskRegistry();
1663
+ export function createTaskOutputTool(opts) {
1664
+ return defineTool({
1665
+ name: "TaskOutput",
1666
+ aliases: [...TASK_OUTPUT_ALIASES],
1667
+ description: "Retrieve output or status for a background task by task_id. Supports background Bash tasks and workflow " +
1668
+ "tasks returned by RunWorkflow. Returns only the caller's own task scope; unknown and out-of-scope ids are " +
1669
+ "reported the same way. Legacy bash_id and runId arguments are accepted for compatibility.",
1670
+ // CC-parity param surface (design/115 review S-3): canonical schema declares ONLY what CC's TaskOutput
1671
+ // exposes (task_id) plus sema-delta `filter` (design/103). Legacy `bash_id`/`runId` are accepted at
1672
+ // execute-time via the additional-properties pass-through (TypeBox Object is non-strict) but are NOT
1673
+ // advertised to the model — old persisted calls keep working without teaching new models dead params.
1674
+ parameters: Type.Object({
1675
+ task_id: Type.Optional(Type.String({ description: "The task_id returned by Bash(run_in_background) or RunWorkflow." })),
1676
+ // design/116 W3 (CC TaskOutput block/timeout): wait for completion instead of snapshotting. sema
1677
+ // default is false (cursor semantics preserved); CC's default is true — the describe says so to a
1678
+ // CC-trained model, and sending block:true gets the CC behavior.
1679
+ block: Type.Optional(Type.Boolean({ description: "Whether to wait for completion (default true — the call waits until the task finishes or `timeout`, saving re-poll turns). Pass false for an instantaneous snapshot." })),
1680
+ timeout: Type.Optional(Type.Number({ description: "Optional timeout in milliseconds when block is true (default 30000, max 600000)." })),
1681
+ filter: Type.Optional(Type.String({ description: "Optional regex filter for background Bash output (sema extension)." })),
1682
+ }),
1683
+ effect: "read",
1684
+ execute: async (rawArgs, ctx) => {
1685
+ const args = rawArgs;
1686
+ const id = firstString(args.task_id, args.bash_id, args.runId);
1687
+ if (!id)
1688
+ return "Error (TaskOutput): Missing required parameter: task_id";
1689
+ // Identity: two mount paths (design/115 review B2 resolution). Mounted directly by prepare-task,
1690
+ // ctx carries only {toolCallId, signal} → the opts closure (hostTaskId/principal) applies. Mounted
1691
+ // via spec.tools (this factory is a public export), prepare-task's wrapper injects the RUNNING
1692
+ // task's ctx.taskId/ctx.principal — which is the more precise identity, so it wins.
1693
+ const r = await opts.registry.pollTask(id, { owner: ctx.taskId ?? opts.owner, scope: ctx.principal ?? opts.scope, ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}) }, // design/129: session dimension for cross-turn poll
1694
+ {
1695
+ workflowStore: opts.workflowStore,
1696
+ filter: typeof args.filter === "string" ? args.filter : undefined,
1697
+ // CC 187 parity (block: default(!0), clay 拍 2026-07-02): block unless explicitly false. A persisted
1698
+ // pre-116 call (no block key) now waits — strictly better for the model (fewer blind re-polls).
1699
+ block: args.block !== false,
1700
+ timeoutMs: typeof args.timeout === "number" ? args.timeout : undefined,
1701
+ signal: ctx.signal,
1702
+ });
1703
+ // [378] flywheel ask: surface the unified output under the discriminant `type:"task-output"` so the
1704
+ // tool_end.structured allowlist passes it (198 renders TaskOutput as an itemized card). The unified
1705
+ // shape's OWN `type` (the task kind: background_bash/agent/workflow) moves to `taskType` — the outer
1706
+ // key is the card discriminant, not the task kind.
1707
+ const { type: taskType, ...rest } = r.details;
1708
+ return { content: r.content, details: { type: "task-output", ...(taskType !== undefined ? { taskType } : {}), ...rest } };
1709
+ },
1710
+ });
1711
+ }
1712
+ export function createTaskStopTool(opts) {
1713
+ return defineTool({
1714
+ name: "TaskStop",
1715
+ aliases: [...TASK_STOP_ALIASES],
1716
+ // CC206-B 欠账 (CC `Tzc` 206:281520-281527 = 207:240542+ 同文): the named-agent bullet is CC-verbatim;
1717
+ // CC's teammate bullet (`name@team` / bare teammate name) is honestly OMITTED — sema's team face is
1718
+ // in-process (team.ts), not addressable through this registry, and advertising it would be a lie.
1719
+ description: "- Stops a running background task by its ID\n" +
1720
+ "- Takes a task_id parameter identifying the task to stop\n" +
1721
+ "- To stop a background agent spawned with a name, pass that name as task_id\n" +
1722
+ "- Returns a success or failure status\n" +
1723
+ "- Use this tool when you need to terminate a long-running task",
1724
+ // CC-exact param surface (TaskStopTool.ts:12-17): {task_id, shell_id} both optional, presence enforced at
1725
+ // run time. Legacy `runId` is accepted at execute-time via non-strict pass-through, not advertised.
1726
+ parameters: Type.Object({
1727
+ task_id: Type.Optional(Type.String({ description: "The ID of the background task to stop" })),
1728
+ shell_id: Type.Optional(Type.String({ description: "Deprecated: use task_id instead" })),
1729
+ }),
1730
+ effect: "write",
1731
+ execute: async (rawArgs, ctx) => {
1732
+ const args = rawArgs;
1733
+ const id = firstString(args.task_id, args.shell_id, args.runId);
1734
+ if (!id)
1735
+ return "Missing required parameter: task_id";
1736
+ // Identity: ctx wins when the spec.tools wrapper injected it, else the opts closure (see TaskOutput above).
1737
+ const r = await opts.registry.stopTask(id, { owner: ctx.taskId ?? opts.owner, scope: ctx.principal ?? opts.scope, ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}) }, { workflowStore: opts.workflowStore });
1738
+ // 18-tool toolUseResult 尾批 ([378] TaskOutput 同款包装): CC 2.1.201 TaskStopOutput is
1739
+ // {message, task_id, task_type, command?} (sema-shell TaskStopTool outputSchema, all three required) —
1740
+ // the raw UnifiedTaskOutput rode `type: background_bash|workflow|background_agent`, which the
1741
+ // tool_end.structured allowlist (correctly) never passed. Outer `type:"task-stop"` is the card
1742
+ // discriminant; `message` = the model-facing line; the unified fields ride along additively.
1743
+ const { type: taskType, ...rest } = r.details;
1744
+ return {
1745
+ content: r.content,
1746
+ details: { type: "task-stop", message: r.content, task_type: taskType ?? "unknown", ...rest },
1747
+ };
1748
+ },
1749
+ });
1750
+ }
1751
+ //# sourceMappingURL=task-registry.js.map