@capybara-hq/kernel 0.0.0-stage → 0.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (631) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +20 -2
  4. package/bootstrap.json +22 -0
  5. package/dist/admin/admin.controller.d.ts +139 -0
  6. package/dist/admin/admin.controller.js +238 -0
  7. package/dist/admin/extensions.controller.d.ts +100 -0
  8. package/dist/admin/extensions.controller.js +226 -0
  9. package/dist/admin/setup.controller.d.ts +36 -0
  10. package/dist/admin/setup.controller.js +106 -0
  11. package/dist/admin/setup.service.d.ts +47 -0
  12. package/dist/admin/setup.service.js +90 -0
  13. package/dist/agents/agents.controller.d.ts +205 -0
  14. package/dist/agents/agents.controller.js +144 -0
  15. package/dist/agents/agents.service.d.ts +177 -0
  16. package/dist/agents/agents.service.js +677 -0
  17. package/dist/agents/definition.d.ts +198 -0
  18. package/dist/agents/definition.js +152 -0
  19. package/dist/agents/models.controller.d.ts +13 -0
  20. package/dist/agents/models.controller.js +31 -0
  21. package/dist/agents/templates.controller.d.ts +15 -0
  22. package/dist/agents/templates.controller.js +37 -0
  23. package/dist/agents/templates.d.ts +83 -0
  24. package/dist/agents/templates.js +173 -0
  25. package/dist/catalog/admin-catalog.controller.d.ts +143 -0
  26. package/dist/catalog/admin-catalog.controller.js +245 -0
  27. package/dist/catalog/catalog.service.d.ts +430 -0
  28. package/dist/catalog/catalog.service.js +882 -0
  29. package/dist/catalog/claude/agents.d.ts +27 -0
  30. package/dist/catalog/claude/agents.js +270 -0
  31. package/dist/catalog/claude/commands.d.ts +50 -0
  32. package/dist/catalog/claude/commands.js +179 -0
  33. package/dist/catalog/claude/manifest.d.ts +69 -0
  34. package/dist/catalog/claude/manifest.js +134 -0
  35. package/dist/catalog/claude/marketplace.d.ts +35 -0
  36. package/dist/catalog/claude/marketplace.js +194 -0
  37. package/dist/catalog/claude/mcp.d.ts +33 -0
  38. package/dist/catalog/claude/mcp.js +226 -0
  39. package/dist/catalog/claude/report.d.ts +86 -0
  40. package/dist/catalog/claude/report.js +121 -0
  41. package/dist/catalog/claude/translate.d.ts +56 -0
  42. package/dist/catalog/claude/translate.js +857 -0
  43. package/dist/catalog/cli.d.ts +46 -0
  44. package/dist/catalog/cli.js +211 -0
  45. package/dist/catalog/git-source.d.ts +127 -0
  46. package/dist/catalog/git-source.js +520 -0
  47. package/dist/catalog/marketplace-fetch.d.ts +99 -0
  48. package/dist/catalog/marketplace-fetch.js +203 -0
  49. package/dist/catalog/marketplaces.controller.d.ts +97 -0
  50. package/dist/catalog/marketplaces.controller.js +197 -0
  51. package/dist/catalog/marketplaces.service.d.ts +340 -0
  52. package/dist/catalog/marketplaces.service.js +1217 -0
  53. package/dist/catalog/org-catalog.controller.d.ts +128 -0
  54. package/dist/catalog/org-catalog.controller.js +277 -0
  55. package/dist/catalog/org-catalog.service.d.ts +246 -0
  56. package/dist/catalog/org-catalog.service.js +625 -0
  57. package/dist/catalog/plugin-import.d.ts +436 -0
  58. package/dist/catalog/plugin-import.js +1069 -0
  59. package/dist/catalog/plugin-install.d.ts +447 -0
  60. package/dist/catalog/plugin-install.js +1333 -0
  61. package/dist/catalog/plugin-package.d.ts +143 -0
  62. package/dist/catalog/plugin-package.js +382 -0
  63. package/dist/catalog/plugin-upgrade.d.ts +222 -0
  64. package/dist/catalog/plugin-upgrade.js +1219 -0
  65. package/dist/catalog/requests.service.d.ts +162 -0
  66. package/dist/catalog/requests.service.js +504 -0
  67. package/dist/catalog/schema-path.d.ts +7 -0
  68. package/dist/catalog/schema-path.js +63 -0
  69. package/dist/catalog/upgrade-targets.d.ts +59 -0
  70. package/dist/catalog/upgrade-targets.js +90 -0
  71. package/dist/catalog/versions.d.ts +31 -0
  72. package/dist/catalog/versions.js +44 -0
  73. package/dist/channels/actions.d.ts +37 -0
  74. package/dist/channels/actions.js +121 -0
  75. package/dist/channels/cards.d.ts +44 -0
  76. package/dist/channels/cards.js +156 -0
  77. package/dist/channels/connections.controller.d.ts +59 -0
  78. package/dist/channels/connections.controller.js +115 -0
  79. package/dist/channels/connections.service.d.ts +187 -0
  80. package/dist/channels/connections.service.js +534 -0
  81. package/dist/channels/features.d.ts +12 -0
  82. package/dist/channels/features.js +22 -0
  83. package/dist/channels/inbound.d.ts +120 -0
  84. package/dist/channels/inbound.js +332 -0
  85. package/dist/channels/outbound.d.ts +140 -0
  86. package/dist/channels/outbound.js +1125 -0
  87. package/dist/channels/phrases.d.ts +100 -0
  88. package/dist/channels/phrases.js +81 -0
  89. package/dist/channels/supervisor.d.ts +182 -0
  90. package/dist/channels/supervisor.js +625 -0
  91. package/dist/channels/threads.d.ts +29 -0
  92. package/dist/channels/threads.js +104 -0
  93. package/dist/channels/webhook-url.d.ts +16 -0
  94. package/dist/channels/webhook-url.js +34 -0
  95. package/dist/channels/webhook.controller.d.ts +19 -0
  96. package/dist/channels/webhook.controller.js +72 -0
  97. package/dist/channels/webhook.d.ts +73 -0
  98. package/dist/channels/webhook.js +274 -0
  99. package/dist/config/docs.d.ts +9 -0
  100. package/dist/config/docs.js +85 -0
  101. package/dist/config/env.d.ts +6 -0
  102. package/dist/config/env.js +73 -0
  103. package/dist/config/fields.d.ts +42 -0
  104. package/dist/config/fields.js +214 -0
  105. package/dist/config/file-source.d.ts +3 -0
  106. package/dist/config/file-source.js +31 -0
  107. package/dist/config/load.d.ts +20 -0
  108. package/dist/config/load.js +98 -0
  109. package/dist/config/nacos-source.d.ts +31 -0
  110. package/dist/config/nacos-source.js +229 -0
  111. package/dist/config/schema.d.ts +223 -0
  112. package/dist/config/schema.js +1151 -0
  113. package/dist/config/sources.d.ts +18 -0
  114. package/dist/config/sources.js +59 -0
  115. package/dist/config/types.d.ts +9 -0
  116. package/dist/config/types.js +7 -0
  117. package/dist/conversations/artifacts.d.ts +52 -0
  118. package/dist/conversations/artifacts.js +137 -0
  119. package/dist/conversations/attachments.controller.d.ts +28 -0
  120. package/dist/conversations/attachments.controller.js +96 -0
  121. package/dist/conversations/attachments.service.d.ts +180 -0
  122. package/dist/conversations/attachments.service.js +578 -0
  123. package/dist/conversations/conversations.controller.d.ts +288 -0
  124. package/dist/conversations/conversations.controller.js +607 -0
  125. package/dist/conversations/conversations.service.d.ts +321 -0
  126. package/dist/conversations/conversations.service.js +754 -0
  127. package/dist/conversations/event-log.d.ts +122 -0
  128. package/dist/conversations/event-log.js +259 -0
  129. package/dist/conversations/events.d.ts +856 -0
  130. package/dist/conversations/events.js +639 -0
  131. package/dist/conversations/files.d.ts +92 -0
  132. package/dist/conversations/files.js +213 -0
  133. package/dist/conversations/outputs-read.d.ts +34 -0
  134. package/dist/conversations/outputs-read.js +37 -0
  135. package/dist/conversations/queue.d.ts +169 -0
  136. package/dist/conversations/queue.js +527 -0
  137. package/dist/conversations/sandbox-status.d.ts +21 -0
  138. package/dist/conversations/sandbox-status.js +24 -0
  139. package/dist/conversations/selection.d.ts +46 -0
  140. package/dist/conversations/selection.js +75 -0
  141. package/dist/conversations/titles.d.ts +93 -0
  142. package/dist/conversations/titles.js +422 -0
  143. package/dist/ctx/bus.d.ts +16 -0
  144. package/dist/ctx/bus.js +101 -0
  145. package/dist/ctx/create-ctx.d.ts +109 -0
  146. package/dist/ctx/create-ctx.js +592 -0
  147. package/dist/ctx/kv.d.ts +4 -0
  148. package/dist/ctx/kv.js +31 -0
  149. package/dist/ctx/lock.d.ts +4 -0
  150. package/dist/ctx/lock.js +39 -0
  151. package/dist/ctx/namespace.d.ts +6 -0
  152. package/dist/ctx/namespace.js +12 -0
  153. package/dist/ctx/queue.d.ts +25 -0
  154. package/dist/ctx/queue.js +150 -0
  155. package/dist/ctx/secrets.d.ts +45 -0
  156. package/dist/ctx/secrets.js +175 -0
  157. package/dist/db/kernel-schema.d.ts +1122 -0
  158. package/dist/db/kernel-schema.js +5 -0
  159. package/dist/db/migrator.d.ts +28 -0
  160. package/dist/db/migrator.js +140 -0
  161. package/dist/db/pool.d.ts +21 -0
  162. package/dist/db/pool.js +28 -0
  163. package/dist/db/sql-statements.d.ts +34 -0
  164. package/dist/db/sql-statements.js +168 -0
  165. package/dist/errors.d.ts +210 -0
  166. package/dist/errors.js +212 -0
  167. package/dist/evals/budget.d.ts +61 -0
  168. package/dist/evals/budget.js +181 -0
  169. package/dist/evals/diff.d.ts +55 -0
  170. package/dist/evals/diff.js +133 -0
  171. package/dist/evals/judge.d.ts +53 -0
  172. package/dist/evals/judge.js +149 -0
  173. package/dist/evals/plan.d.ts +47 -0
  174. package/dist/evals/plan.js +77 -0
  175. package/dist/evals/reconstruct.d.ts +137 -0
  176. package/dist/evals/reconstruct.js +322 -0
  177. package/dist/evals/recorded-tools.d.ts +54 -0
  178. package/dist/evals/recorded-tools.js +167 -0
  179. package/dist/evals/run-replay.d.ts +86 -0
  180. package/dist/evals/run-replay.js +502 -0
  181. package/dist/evals/runs.controller.d.ts +100 -0
  182. package/dist/evals/runs.controller.js +199 -0
  183. package/dist/evals/runs.service.d.ts +275 -0
  184. package/dist/evals/runs.service.js +836 -0
  185. package/dist/evals/sets.controller.d.ts +61 -0
  186. package/dist/evals/sets.controller.js +183 -0
  187. package/dist/evals/sets.service.d.ts +128 -0
  188. package/dist/evals/sets.service.js +558 -0
  189. package/dist/evals/snapshot.d.ts +76 -0
  190. package/dist/evals/snapshot.js +207 -0
  191. package/dist/evals/step-replay.d.ts +113 -0
  192. package/dist/evals/step-replay.js +181 -0
  193. package/dist/evals/trajectory.controller.d.ts +41 -0
  194. package/dist/evals/trajectory.controller.js +88 -0
  195. package/dist/evals/trajectory.d.ts +144 -0
  196. package/dist/evals/trajectory.js +338 -0
  197. package/dist/evals/trajectory.service.d.ts +108 -0
  198. package/dist/evals/trajectory.service.js +323 -0
  199. package/dist/evals/worker.d.ts +101 -0
  200. package/dist/evals/worker.js +789 -0
  201. package/dist/extensions/admin-ops.d.ts +105 -0
  202. package/dist/extensions/admin-ops.js +353 -0
  203. package/dist/extensions/admin-view.d.ts +79 -0
  204. package/dist/extensions/admin-view.js +327 -0
  205. package/dist/extensions/approvals.d.ts +119 -0
  206. package/dist/extensions/approvals.js +205 -0
  207. package/dist/extensions/blobs.d.ts +45 -0
  208. package/dist/extensions/blobs.js +160 -0
  209. package/dist/extensions/bootstrap.d.ts +149 -0
  210. package/dist/extensions/bootstrap.js +441 -0
  211. package/dist/extensions/cli.d.ts +61 -0
  212. package/dist/extensions/cli.js +165 -0
  213. package/dist/extensions/config-json-schema.d.ts +9 -0
  214. package/dist/extensions/config-json-schema.js +73 -0
  215. package/dist/extensions/deployment-config.d.ts +138 -0
  216. package/dist/extensions/deployment-config.js +394 -0
  217. package/dist/extensions/enabled.d.ts +31 -0
  218. package/dist/extensions/enabled.js +55 -0
  219. package/dist/extensions/hooks.d.ts +161 -0
  220. package/dist/extensions/hooks.js +425 -0
  221. package/dist/extensions/host.d.ts +577 -0
  222. package/dist/extensions/host.js +2071 -0
  223. package/dist/extensions/inflight.d.ts +69 -0
  224. package/dist/extensions/inflight.js +277 -0
  225. package/dist/extensions/installs.d.ts +166 -0
  226. package/dist/extensions/installs.js +253 -0
  227. package/dist/extensions/instance.d.ts +167 -0
  228. package/dist/extensions/instance.js +329 -0
  229. package/dist/extensions/ledger.d.ts +30 -0
  230. package/dist/extensions/ledger.js +50 -0
  231. package/dist/extensions/lifecycle.d.ts +80 -0
  232. package/dist/extensions/lifecycle.js +152 -0
  233. package/dist/extensions/migrate-lock.d.ts +51 -0
  234. package/dist/extensions/migrate-lock.js +100 -0
  235. package/dist/extensions/org-config.controller.d.ts +24 -0
  236. package/dist/extensions/org-config.controller.js +59 -0
  237. package/dist/extensions/org-config.d.ts +169 -0
  238. package/dist/extensions/org-config.js +520 -0
  239. package/dist/extensions/package.d.ts +115 -0
  240. package/dist/extensions/package.js +246 -0
  241. package/dist/extensions/permission-set.d.ts +117 -0
  242. package/dist/extensions/permission-set.js +184 -0
  243. package/dist/extensions/permission-view.d.ts +194 -0
  244. package/dist/extensions/permission-view.js +284 -0
  245. package/dist/extensions/registry.d.ts +24 -0
  246. package/dist/extensions/registry.js +56 -0
  247. package/dist/extensions/resolve.d.ts +12 -0
  248. package/dist/extensions/resolve.js +71 -0
  249. package/dist/extensions/sources.d.ts +69 -0
  250. package/dist/extensions/sources.js +120 -0
  251. package/dist/extensions/sync.d.ts +40 -0
  252. package/dist/extensions/sync.js +242 -0
  253. package/dist/files/previewers.controller.d.ts +46 -0
  254. package/dist/files/previewers.controller.js +105 -0
  255. package/dist/files/previewers.d.ts +100 -0
  256. package/dist/files/previewers.js +250 -0
  257. package/dist/http/api.module.d.ts +114 -0
  258. package/dist/http/api.module.js +448 -0
  259. package/dist/http/app.d.ts +77 -0
  260. package/dist/http/app.js +170 -0
  261. package/dist/http/error.filter.d.ts +21 -0
  262. package/dist/http/error.filter.js +85 -0
  263. package/dist/http/gateway.module.d.ts +15 -0
  264. package/dist/http/gateway.module.js +13 -0
  265. package/dist/http/health.controller.d.ts +26 -0
  266. package/dist/http/health.controller.js +79 -0
  267. package/dist/http/nest-logger.d.ts +20 -0
  268. package/dist/http/nest-logger.js +28 -0
  269. package/dist/http/plugin-dispatch.d.ts +68 -0
  270. package/dist/http/plugin-dispatch.js +200 -0
  271. package/dist/http/tokens.d.ts +131 -0
  272. package/dist/http/tokens.js +131 -0
  273. package/dist/http/zod.pipe.d.ts +8 -0
  274. package/dist/http/zod.pipe.js +14 -0
  275. package/dist/identity/accounts.d.ts +93 -0
  276. package/dist/identity/accounts.js +367 -0
  277. package/dist/identity/api-tokens.d.ts +73 -0
  278. package/dist/identity/api-tokens.js +197 -0
  279. package/dist/identity/audit.d.ts +23 -0
  280. package/dist/identity/audit.js +41 -0
  281. package/dist/identity/auth.controller.d.ts +40 -0
  282. package/dist/identity/auth.controller.js +130 -0
  283. package/dist/identity/auth.guard.d.ts +41 -0
  284. package/dist/identity/auth.guard.js +162 -0
  285. package/dist/identity/binding.d.ts +47 -0
  286. package/dist/identity/binding.js +84 -0
  287. package/dist/identity/cookies.d.ts +16 -0
  288. package/dist/identity/cookies.js +32 -0
  289. package/dist/identity/credential-check.d.ts +17 -0
  290. package/dist/identity/credential-check.js +24 -0
  291. package/dist/identity/enterprise-admin.d.ts +14 -0
  292. package/dist/identity/enterprise-admin.js +27 -0
  293. package/dist/identity/external.d.ts +106 -0
  294. package/dist/identity/external.js +337 -0
  295. package/dist/identity/hash.d.ts +2 -0
  296. package/dist/identity/hash.js +3 -0
  297. package/dist/identity/login.d.ts +6 -0
  298. package/dist/identity/login.js +20 -0
  299. package/dist/identity/me.controller.d.ts +61 -0
  300. package/dist/identity/me.controller.js +159 -0
  301. package/dist/identity/passwords.d.ts +6 -0
  302. package/dist/identity/passwords.js +37 -0
  303. package/dist/identity/principal.d.ts +56 -0
  304. package/dist/identity/principal.js +38 -0
  305. package/dist/identity/rate-limit.d.ts +55 -0
  306. package/dist/identity/rate-limit.js +121 -0
  307. package/dist/identity/sessions.d.ts +84 -0
  308. package/dist/identity/sessions.js +306 -0
  309. package/dist/identity/settings.d.ts +121 -0
  310. package/dist/identity/settings.js +248 -0
  311. package/dist/identity/sso.controller.d.ts +59 -0
  312. package/dist/identity/sso.controller.js +192 -0
  313. package/dist/identity/sso.d.ts +83 -0
  314. package/dist/identity/sso.js +202 -0
  315. package/dist/inbox/inbox.service.d.ts +43 -0
  316. package/dist/inbox/inbox.service.js +57 -0
  317. package/dist/index.d.ts +67 -0
  318. package/dist/index.js +65 -0
  319. package/dist/kernel.d.ts +256 -0
  320. package/dist/kernel.js +1326 -0
  321. package/dist/log.d.ts +48 -0
  322. package/dist/log.js +229 -0
  323. package/dist/models/prices.d.ts +53 -0
  324. package/dist/models/prices.js +121 -0
  325. package/dist/models/purposes.controller.d.ts +19 -0
  326. package/dist/models/purposes.controller.js +56 -0
  327. package/dist/models/purposes.d.ts +140 -0
  328. package/dist/models/purposes.js +267 -0
  329. package/dist/net/proxy.d.ts +44 -0
  330. package/dist/net/proxy.js +162 -0
  331. package/dist/net/public-address.d.ts +60 -0
  332. package/dist/net/public-address.js +271 -0
  333. package/dist/net/safe-fetch.d.ts +38 -0
  334. package/dist/net/safe-fetch.js +140 -0
  335. package/dist/orgs/directory-sync.controller.d.ts +14 -0
  336. package/dist/orgs/directory-sync.controller.js +40 -0
  337. package/dist/orgs/directory-sync.d.ts +166 -0
  338. package/dist/orgs/directory-sync.js +558 -0
  339. package/dist/orgs/invitations.controller.d.ts +58 -0
  340. package/dist/orgs/invitations.controller.js +137 -0
  341. package/dist/orgs/invitations.service.d.ts +80 -0
  342. package/dist/orgs/invitations.service.js +217 -0
  343. package/dist/orgs/members.service.d.ts +63 -0
  344. package/dist/orgs/members.service.js +234 -0
  345. package/dist/orgs/offboarding.d.ts +141 -0
  346. package/dist/orgs/offboarding.js +398 -0
  347. package/dist/orgs/org-lock.d.ts +32 -0
  348. package/dist/orgs/org-lock.js +46 -0
  349. package/dist/orgs/org.guard.d.ts +35 -0
  350. package/dist/orgs/org.guard.js +95 -0
  351. package/dist/orgs/orgs.controller.d.ts +78 -0
  352. package/dist/orgs/orgs.controller.js +192 -0
  353. package/dist/orgs/orgs.service.d.ts +61 -0
  354. package/dist/orgs/orgs.service.js +209 -0
  355. package/dist/orgs/roles.d.ts +13 -0
  356. package/dist/orgs/roles.js +58 -0
  357. package/dist/orgs/settings.controller.d.ts +20 -0
  358. package/dist/orgs/settings.controller.js +53 -0
  359. package/dist/orgs/settings.service.d.ts +120 -0
  360. package/dist/orgs/settings.service.js +178 -0
  361. package/dist/packages/npm.d.ts +50 -0
  362. package/dist/packages/npm.js +218 -0
  363. package/dist/packages/provenance.d.ts +47 -0
  364. package/dist/packages/provenance.js +147 -0
  365. package/dist/packages/tgz.d.ts +76 -0
  366. package/dist/packages/tgz.js +225 -0
  367. package/dist/runtime/abort.d.ts +32 -0
  368. package/dist/runtime/abort.js +71 -0
  369. package/dist/runtime/checkpointer.d.ts +32 -0
  370. package/dist/runtime/checkpointer.js +243 -0
  371. package/dist/runtime/context/build.d.ts +254 -0
  372. package/dist/runtime/context/build.js +693 -0
  373. package/dist/runtime/context/claude-tools.d.ts +11 -0
  374. package/dist/runtime/context/claude-tools.js +24 -0
  375. package/dist/runtime/context/compaction.d.ts +26 -0
  376. package/dist/runtime/context/compaction.js +195 -0
  377. package/dist/runtime/context/pairing.d.ts +11 -0
  378. package/dist/runtime/context/pairing.js +82 -0
  379. package/dist/runtime/context/reminders.d.ts +5 -0
  380. package/dist/runtime/context/reminders.js +11 -0
  381. package/dist/runtime/context/skills.d.ts +105 -0
  382. package/dist/runtime/context/skills.js +283 -0
  383. package/dist/runtime/context/tokens.d.ts +19 -0
  384. package/dist/runtime/context/tokens.js +104 -0
  385. package/dist/runtime/conversation-lock.d.ts +40 -0
  386. package/dist/runtime/conversation-lock.js +97 -0
  387. package/dist/runtime/hook-calls.d.ts +84 -0
  388. package/dist/runtime/hook-calls.js +136 -0
  389. package/dist/runtime/langgraph-loop.d.ts +52 -0
  390. package/dist/runtime/langgraph-loop.js +247 -0
  391. package/dist/runtime/loop.d.ts +93 -0
  392. package/dist/runtime/loop.js +7 -0
  393. package/dist/runtime/maintenance.d.ts +80 -0
  394. package/dist/runtime/maintenance.js +147 -0
  395. package/dist/runtime/media-cache.d.ts +21 -0
  396. package/dist/runtime/media-cache.js +51 -0
  397. package/dist/runtime/model-call.d.ts +176 -0
  398. package/dist/runtime/model-call.js +1049 -0
  399. package/dist/runtime/providers.d.ts +86 -0
  400. package/dist/runtime/providers.js +224 -0
  401. package/dist/runtime/quota.d.ts +29 -0
  402. package/dist/runtime/quota.js +70 -0
  403. package/dist/runtime/run-finish.d.ts +45 -0
  404. package/dist/runtime/run-finish.js +69 -0
  405. package/dist/runtime/run-queue.d.ts +121 -0
  406. package/dist/runtime/run-queue.js +493 -0
  407. package/dist/runtime/run-worker.d.ts +138 -0
  408. package/dist/runtime/run-worker.js +805 -0
  409. package/dist/runtime/stream.controller.d.ts +34 -0
  410. package/dist/runtime/stream.controller.js +122 -0
  411. package/dist/runtime/stream.d.ts +63 -0
  412. package/dist/runtime/stream.js +262 -0
  413. package/dist/runtime/subagents/child.d.ts +80 -0
  414. package/dist/runtime/subagents/child.js +225 -0
  415. package/dist/runtime/subagents/definition.d.ts +103 -0
  416. package/dist/runtime/subagents/definition.js +392 -0
  417. package/dist/runtime/subagents/dispatch.d.ts +69 -0
  418. package/dist/runtime/subagents/dispatch.js +502 -0
  419. package/dist/runtime/subagents/limits.d.ts +23 -0
  420. package/dist/runtime/subagents/limits.js +34 -0
  421. package/dist/runtime/subagents/org-agents.d.ts +50 -0
  422. package/dist/runtime/subagents/org-agents.js +70 -0
  423. package/dist/runtime/subagents/preamble.d.ts +7 -0
  424. package/dist/runtime/subagents/preamble.js +11 -0
  425. package/dist/runtime/subagents/progress.d.ts +26 -0
  426. package/dist/runtime/subagents/progress.js +36 -0
  427. package/dist/runtime/subagents/roster.d.ts +55 -0
  428. package/dist/runtime/subagents/roster.js +111 -0
  429. package/dist/runtime/subagents/skill-fork.d.ts +123 -0
  430. package/dist/runtime/subagents/skill-fork.js +259 -0
  431. package/dist/runtime/usage-report.d.ts +163 -0
  432. package/dist/runtime/usage-report.js +550 -0
  433. package/dist/runtime/waits.d.ts +233 -0
  434. package/dist/runtime/waits.js +883 -0
  435. package/dist/sandbox/leases.d.ts +169 -0
  436. package/dist/sandbox/leases.js +515 -0
  437. package/dist/sandbox/lifecycle.d.ts +21 -0
  438. package/dist/sandbox/lifecycle.js +68 -0
  439. package/dist/sandbox/sandbox.service.d.ts +249 -0
  440. package/dist/sandbox/sandbox.service.js +908 -0
  441. package/dist/skills/catalog.d.ts +149 -0
  442. package/dist/skills/catalog.js +405 -0
  443. package/dist/skills/changes.controller.d.ts +56 -0
  444. package/dist/skills/changes.controller.js +136 -0
  445. package/dist/skills/changes.service.d.ts +175 -0
  446. package/dist/skills/changes.service.js +805 -0
  447. package/dist/skills/invocable.controller.d.ts +23 -0
  448. package/dist/skills/invocable.controller.js +50 -0
  449. package/dist/skills/invocation-keys.d.ts +23 -0
  450. package/dist/skills/invocation-keys.js +44 -0
  451. package/dist/skills/invoke.d.ts +135 -0
  452. package/dist/skills/invoke.js +286 -0
  453. package/dist/skills/package.d.ts +87 -0
  454. package/dist/skills/package.js +227 -0
  455. package/dist/skills/skills.controller.d.ts +40 -0
  456. package/dist/skills/skills.controller.js +110 -0
  457. package/dist/skills/skills.service.d.ts +192 -0
  458. package/dist/skills/skills.service.js +567 -0
  459. package/dist/storage/prefix.d.ts +36 -0
  460. package/dist/storage/prefix.js +104 -0
  461. package/dist/storage/purge.d.ts +27 -0
  462. package/dist/storage/purge.js +75 -0
  463. package/dist/storage/storage.service.d.ts +44 -0
  464. package/dist/storage/storage.service.js +145 -0
  465. package/dist/text.d.ts +13 -0
  466. package/dist/text.js +13 -0
  467. package/dist/tools/approvals.controller.d.ts +53 -0
  468. package/dist/tools/approvals.controller.js +128 -0
  469. package/dist/tools/approvals.service.d.ts +172 -0
  470. package/dist/tools/approvals.service.js +568 -0
  471. package/dist/tools/breaker.d.ts +28 -0
  472. package/dist/tools/breaker.js +72 -0
  473. package/dist/tools/builtins/agent.d.ts +81 -0
  474. package/dist/tools/builtins/agent.js +223 -0
  475. package/dist/tools/builtins/ask-user.d.ts +28 -0
  476. package/dist/tools/builtins/ask-user.js +139 -0
  477. package/dist/tools/builtins/read-tool-output.d.ts +18 -0
  478. package/dist/tools/builtins/read-tool-output.js +108 -0
  479. package/dist/tools/builtins/sandbox/bash.d.ts +16 -0
  480. package/dist/tools/builtins/sandbox/bash.js +137 -0
  481. package/dist/tools/builtins/sandbox/common.d.ts +114 -0
  482. package/dist/tools/builtins/sandbox/common.js +304 -0
  483. package/dist/tools/builtins/sandbox/deliver.d.ts +5 -0
  484. package/dist/tools/builtins/sandbox/deliver.js +180 -0
  485. package/dist/tools/builtins/sandbox/edit.d.ts +4 -0
  486. package/dist/tools/builtins/sandbox/edit.js +98 -0
  487. package/dist/tools/builtins/sandbox/glob-match.d.ts +25 -0
  488. package/dist/tools/builtins/sandbox/glob-match.js +240 -0
  489. package/dist/tools/builtins/sandbox/glob.d.ts +4 -0
  490. package/dist/tools/builtins/sandbox/glob.js +104 -0
  491. package/dist/tools/builtins/sandbox/grep.d.ts +9 -0
  492. package/dist/tools/builtins/sandbox/grep.js +133 -0
  493. package/dist/tools/builtins/sandbox/index.d.ts +12 -0
  494. package/dist/tools/builtins/sandbox/index.js +27 -0
  495. package/dist/tools/builtins/sandbox/names.d.ts +7 -0
  496. package/dist/tools/builtins/sandbox/names.js +20 -0
  497. package/dist/tools/builtins/sandbox/read.d.ts +8 -0
  498. package/dist/tools/builtins/sandbox/read.js +121 -0
  499. package/dist/tools/builtins/sandbox/write.d.ts +4 -0
  500. package/dist/tools/builtins/sandbox/write.js +88 -0
  501. package/dist/tools/builtins/skill-load.d.ts +53 -0
  502. package/dist/tools/builtins/skill-load.js +446 -0
  503. package/dist/tools/builtins/skill-write.d.ts +59 -0
  504. package/dist/tools/builtins/skill-write.js +244 -0
  505. package/dist/tools/builtins/tool-search.d.ts +31 -0
  506. package/dist/tools/builtins/tool-search.js +171 -0
  507. package/dist/tools/connector-content.controller.d.ts +34 -0
  508. package/dist/tools/connector-content.controller.js +78 -0
  509. package/dist/tools/connector-content.d.ts +104 -0
  510. package/dist/tools/connector-content.js +267 -0
  511. package/dist/tools/connectors.controller.d.ts +210 -0
  512. package/dist/tools/connectors.controller.js +234 -0
  513. package/dist/tools/connectors.service.d.ts +303 -0
  514. package/dist/tools/connectors.service.js +1102 -0
  515. package/dist/tools/credentials.d.ts +139 -0
  516. package/dist/tools/credentials.js +351 -0
  517. package/dist/tools/drift.d.ts +141 -0
  518. package/dist/tools/drift.js +539 -0
  519. package/dist/tools/elicitation.d.ts +81 -0
  520. package/dist/tools/elicitation.js +266 -0
  521. package/dist/tools/endpoint.d.ts +18 -0
  522. package/dist/tools/endpoint.js +115 -0
  523. package/dist/tools/info.d.ts +67 -0
  524. package/dist/tools/info.js +113 -0
  525. package/dist/tools/naming.d.ts +27 -0
  526. package/dist/tools/naming.js +47 -0
  527. package/dist/tools/oauth/discovery.d.ts +65 -0
  528. package/dist/tools/oauth/discovery.js +307 -0
  529. package/dist/tools/oauth/flow.d.ts +63 -0
  530. package/dist/tools/oauth/flow.js +237 -0
  531. package/dist/tools/oauth/oauth.controller.d.ts +31 -0
  532. package/dist/tools/oauth/oauth.controller.js +95 -0
  533. package/dist/tools/oauth/tokens.d.ts +111 -0
  534. package/dist/tools/oauth/tokens.js +258 -0
  535. package/dist/tools/outputs.d.ts +61 -0
  536. package/dist/tools/outputs.js +156 -0
  537. package/dist/tools/pipeline.d.ts +322 -0
  538. package/dist/tools/pipeline.js +1236 -0
  539. package/dist/tools/policy.d.ts +60 -0
  540. package/dist/tools/policy.js +236 -0
  541. package/dist/tools/questions.controller.d.ts +24 -0
  542. package/dist/tools/questions.controller.js +58 -0
  543. package/dist/tools/questions.service.d.ts +103 -0
  544. package/dist/tools/questions.service.js +295 -0
  545. package/dist/tools/redact.d.ts +20 -0
  546. package/dist/tools/redact.js +60 -0
  547. package/dist/tools/registry.d.ts +246 -0
  548. package/dist/tools/registry.js +481 -0
  549. package/dist/tools/resources.d.ts +16 -0
  550. package/dist/tools/resources.js +76 -0
  551. package/dist/tools/specs.d.ts +14 -0
  552. package/dist/tools/specs.js +31 -0
  553. package/dist/tools/validate.d.ts +20 -0
  554. package/dist/tools/validate.js +183 -0
  555. package/dist/tools/view.d.ts +43 -0
  556. package/dist/tools/view.js +225 -0
  557. package/dist/ui/embed-token.d.ts +31 -0
  558. package/dist/ui/embed-token.js +132 -0
  559. package/dist/ui/registry.d.ts +58 -0
  560. package/dist/ui/registry.js +90 -0
  561. package/dist/ui/ui-stream.d.ts +37 -0
  562. package/dist/ui/ui-stream.js +162 -0
  563. package/dist/ui/views.controller.d.ts +81 -0
  564. package/dist/ui/views.controller.js +261 -0
  565. package/dist/ui/views.d.ts +155 -0
  566. package/dist/ui/views.js +306 -0
  567. package/dist/version.d.ts +5 -0
  568. package/dist/version.js +6 -0
  569. package/package.json +67 -3
  570. package/src/migrations/0001_init.sql +8 -0
  571. package/src/migrations/0002_secrets.sql +8 -0
  572. package/src/migrations/0003_identity.sql +117 -0
  573. package/src/migrations/0004_plugin_org_config_nullable.sql +3 -0
  574. package/src/migrations/0005_agents.sql +27 -0
  575. package/src/migrations/0006_conversations.sql +37 -0
  576. package/src/migrations/0007_checkpoints.sql +26 -0
  577. package/src/migrations/0008_runs_trigger_seq.sql +3 -0
  578. package/src/migrations/0009_runs_active_unique.sql +4 -0
  579. package/src/migrations/0010_tools.sql +125 -0
  580. package/src/migrations/0011_approvals_spec.sql +10 -0
  581. package/src/migrations/0012_run_waits.sql +24 -0
  582. package/src/migrations/0013_app_tools_source.sql +4 -0
  583. package/src/migrations/0014_tool_specs.sql +8 -0
  584. package/src/migrations/0015_tool_drift.sql +20 -0
  585. package/src/migrations/0016_plugin_permissions.sql +11 -0
  586. package/src/migrations/0017_identity_sources.sql +6 -0
  587. package/src/migrations/0018_channels.sql +74 -0
  588. package/src/migrations/0019_identity_trust.sql +17 -0
  589. package/src/migrations/0020_channel_card_lookup.sql +10 -0
  590. package/src/migrations/0021_org_storage_purge.sql +9 -0
  591. package/src/migrations/0022_attachments.sql +29 -0
  592. package/src/migrations/0023_sandboxes.sql +37 -0
  593. package/src/migrations/0024_skills.sql +46 -0
  594. package/src/migrations/0025_skills_catalog_idx.sql +6 -0
  595. package/src/migrations/0026_skill_changes.sql +34 -0
  596. package/src/migrations/0027_model_prices.sql +18 -0
  597. package/src/migrations/0028_evals.sql +105 -0
  598. package/src/migrations/0029_model_purposes.sql +18 -0
  599. package/src/migrations/0030_plugin_installs.sql +73 -0
  600. package/src/migrations/0031_plugin_snapshot_source.sql +6 -0
  601. package/src/migrations/0032_plugin_deployment_configs.sql +10 -0
  602. package/src/migrations/0033_rename_apps_to_connectors.sql +25 -0
  603. package/src/migrations/0034_directory.sql +49 -0
  604. package/src/migrations/0035_catalogs.sql +40 -0
  605. package/src/migrations/0036_app_import_staging.sql +17 -0
  606. package/src/migrations/0037_directory_installs.sql +92 -0
  607. package/src/migrations/0038_channel_inbound_mode.sql +6 -0
  608. package/src/migrations/0039_channel_card_pending.sql +8 -0
  609. package/src/migrations/0040_usage_report_sweep.sql +10 -0
  610. package/src/migrations/0041_rename_plugins_to_extensions.sql +38 -0
  611. package/src/migrations/0042_rename_directory_to_catalog.sql +80 -0
  612. package/src/migrations/0043_rename_app_installs_to_plugin_installs.sql +20 -0
  613. package/src/migrations/0044_catalog_one_kind.sql +90 -0
  614. package/src/migrations/0045_plugin_dependencies.sql +9 -0
  615. package/src/migrations/0046_git_sources.sql +25 -0
  616. package/src/migrations/0047_marketplace_entry_heads.sql +7 -0
  617. package/src/migrations/0048_marketplace_entry_trees.sql +8 -0
  618. package/src/migrations/0049_plugin_upgrades.sql +37 -0
  619. package/src/migrations/0050_bootstrap.sql +53 -0
  620. package/src/migrations/0051_claude_compat.sql +26 -0
  621. package/src/migrations/0052_conversations_account_agent_idx.sql +5 -0
  622. package/src/migrations/0053_subagents.sql +73 -0
  623. package/src/migrations/0054_subagent_wait_expiry.sql +7 -0
  624. package/src/migrations/0055_subagent_parent_indexes.sql +10 -0
  625. package/src/migrations/0056_approval_reason.sql +3 -0
  626. package/src/migrations/0057_conversation_titles.sql +11 -0
  627. package/src/migrations/0058_conversation_selection.sql +4 -0
  628. package/src/migrations/0059_conversation_queue.sql +22 -0
  629. package/src/migrations/0060_file_previewers.sql +10 -0
  630. package/src/migrations/0061_queue_selection.sql +5 -0
  631. package/src/migrations/0062_queued_skill.sql +8 -0
@@ -0,0 +1,883 @@
1
+ import { DelayedError } from "bullmq";
2
+ import { sql } from "kysely";
3
+ import { APPROVAL_CANCELLED_TEXT, INPUT_CANCELLED_TEXT, RUN_STOPPED_TEXT, } from "../tools/pipeline.js";
4
+ import { subagentsWaitId, subagentWaitingIdemKey } from "./loop.js";
5
+ import { MAINTENANCE_QUEUE_NAME } from "./maintenance.js";
6
+ import { assistantIdemKey } from "./model-call.js";
7
+ import { finishRunIn, notifyFinished } from "./run-finish.js";
8
+ import { ACTIVE_RUN_STATUSES } from "./run-queue.js";
9
+ /** 等待超时任务所在的内核维护队列(见 maintenance.ts)。 */
10
+ export { MAINTENANCE_QUEUE_NAME };
11
+ /** 等待超时任务的名字与 jobId(每个审批 / 输入请求一个,重复投递被 BullMQ 忽略)。 */
12
+ export const WAIT_TIMEOUT_JOB = "wait-timeout";
13
+ export const waitTimeoutJobId = (waitId) => `wait-timeout-${waitId}`;
14
+ /** 续跑任务的 jobId:与按消息生成的 `run-<conversationId>-<seq>` 不冲突,同一等待只会续跑一次。 */
15
+ export const resumeJobId = (conversationId, waitId) => `run-${conversationId}-resume-${waitId}`;
16
+ /**
17
+ * 等待兜底扫描(Task 22):任务名、job scheduler id(不含 `:`)、周期、宽限期与每类每次最多处理的条数。
18
+ * 宽限期:waiting 超过它、等待却已全部处理的运行才视为「续跑丢失」,避免与正常续跑(暂停后的复查等)竞争。
19
+ */
20
+ export const WAIT_SWEEP_JOB = "wait-sweep";
21
+ export const WAIT_SWEEP_SCHEDULER_ID = "wait-sweep-every-10m";
22
+ export const WAIT_SWEEP_EVERY_MS = 600_000;
23
+ export const WAIT_SWEEP_GRACE_MS = 60_000;
24
+ export const WAIT_SWEEP_BATCH = 500;
25
+ const TERMINAL_RUN_STATUSES = ["completed", "failed", "aborted"];
26
+ /** 超时任务处理出错(如 PG 暂不可用)时的重试。 */
27
+ const TIMEOUT_JOB_ATTEMPTS = 5;
28
+ const TIMEOUT_JOB_BACKOFF_MS = 5_000;
29
+ /**
30
+ * 运行等待人处理(spec 4.4 / Task 9):暂停、续跑、超时与中止。状态全部在 PG(`runs.status = 'waiting'`、
31
+ * `approvals` / `questions` 行),延迟与重试走 BullMQ(维护队列的 `wait-timeout` 任务),没有进程内状态。
32
+ *
33
+ * 只续跑一次:`resume` 是一条条件更新 `waiting → queued`(且该运行已没有 pending 的等待),并发调用只有一个成功,
34
+ * 成功者投递 jobId 为 `run-<conversationId>-resume-<waitId>` 的运行任务。人工决定、超时任务、
35
+ * 暂停后的复查都可能调用它,谁先谁后都只有一次续跑。
36
+ */
37
+ export class Waits {
38
+ d;
39
+ constructor(d) {
40
+ this.d = d;
41
+ d.maintenance.handle(WAIT_TIMEOUT_JOB, (job, token) => this.process(job, token));
42
+ d.maintenance.schedule({
43
+ id: WAIT_SWEEP_SCHEDULER_ID,
44
+ name: WAIT_SWEEP_JOB,
45
+ everyMs: () => WAIT_SWEEP_EVERY_MS,
46
+ });
47
+ d.maintenance.handle(WAIT_SWEEP_JOB, async () => {
48
+ await this.sweep();
49
+ });
50
+ }
51
+ /**
52
+ * 兜底扫描(维护队列每 10 分钟一次,防延迟任务 / 续跑丢失)。每项修复都复用常规路径,与并发的超时任务、
53
+ * 人工决定、其他副本的扫描竞争时由行锁与条件更新保证只生效一次;每项生效的修复记一行 warn(只含 id)。
54
+ * 顺序:先取消终态运行的等待(这些不该再按超时处理),再处理过期的等待,最后续跑「续跑丢失」的运行。
55
+ * 单项失败只记日志,不影响其他项。
56
+ */
57
+ async sweep() {
58
+ const out = {
59
+ expired: 0,
60
+ resumed: 0,
61
+ cancelled: 0,
62
+ repaired: 0,
63
+ requeued: 0,
64
+ orphaned: 0,
65
+ stopped: 0,
66
+ };
67
+ out.cancelled = await this.sweepFinishedRuns();
68
+ out.expired = await this.sweepOverdue();
69
+ Object.assign(out, await this.sweepSubagentOrphans());
70
+ out.repaired = await this.sweepOpenAgentCalls();
71
+ out.stopped = await this.sweepChildrenOfFinished();
72
+ out.resumed = await this.sweepStuckRuns();
73
+ return out;
74
+ }
75
+ /**
76
+ * loop 返回 `suspended` 之后(worker 已释放会话锁与配额):条件更新 `running → waiting`(记 `waiting_since`),
77
+ * 投递延迟到 `expiresAt` 的超时任务,再复查一次等待是否已被处理——处理者的 `resume` 可能发生在本次置 waiting
78
+ * 之前(那时条件更新不成立),复查保证这种情况下仍会续跑。
79
+ * 运行已不是 running(例如已被其他路径结束)→ 什么都不做。
80
+ * 暂停时已收到用户中止(`abort`),或置 waiting 之后补查到中止标记 → 直接按 {@link abortWaiting} 取消等待并结束
81
+ * 运行(置 waiting 之后到达的中止由 `POST .../abort` 看到 waiting 直接处理,两边不会都漏掉)。
82
+ */
83
+ async suspend(runId, conversationId, waitingFor, abort) {
84
+ const r = await this.d.db
85
+ .updateTable("runs")
86
+ .set({
87
+ status: "waiting",
88
+ waiting_since: sql `now()`,
89
+ // a sub-agents wait has no row of its own: its expiry lives on the run, for the sweep
90
+ wait_expires_at: waitingFor.kind === "subagents" ? new Date(waitingFor.expiresAt) : null,
91
+ })
92
+ .where("id", "=", runId)
93
+ .where("status", "=", "running")
94
+ .executeTakeFirst();
95
+ if (Number(r.numUpdatedRows) === 0) {
96
+ this.d.log.warn("run is no longer running; not suspending", { runId });
97
+ return;
98
+ }
99
+ const stop = abort ?? (await this.d.abortRequested?.(runId)) ?? null;
100
+ if (stop && (await this.abortWaiting(conversationId, runId, stop.byAccountId, stop.detail))) {
101
+ return;
102
+ }
103
+ await this.surfaceInParent(runId, waitingFor);
104
+ await this.scheduleTimeout(waitingFor, runId);
105
+ if (await this.isSettled(waitingFor, runId))
106
+ await this.resume(runId, waitingFor.id);
107
+ }
108
+ /**
109
+ * A sub-agent's run waits for an approval or an input (sub-agents spec §9): appends
110
+ * `subagent_waiting` to the parent conversation (idem `subagent_waiting:<childRunId>:<waitId>`),
111
+ * so the web and the channel outbound show the card there. The approval or question itself stays
112
+ * the child run's, with the parent run's starter as the person asked. Not a run of a sub-agent,
113
+ * or a `subagents` wait: nothing. Display only: a failure is logged, the wait goes on.
114
+ */
115
+ async surfaceInParent(runId, waitingFor) {
116
+ if (waitingFor.kind === "subagents")
117
+ return;
118
+ try {
119
+ const d = await this.d.db
120
+ .selectFrom("subagent_dispatches")
121
+ .select(["parent_conversation_id", "parent_run_id", "child_conversation_id"])
122
+ .where("child_run_id", "=", runId)
123
+ .executeTakeFirst();
124
+ if (!d)
125
+ return;
126
+ const row = waitingFor.kind === "approval"
127
+ ? await this.d.db
128
+ .selectFrom("approvals")
129
+ .select(["tool_key", "expires_at"])
130
+ .where("id", "=", waitingFor.id)
131
+ .executeTakeFirst()
132
+ : await this.d.db
133
+ .selectFrom("questions")
134
+ .select([sql `spec->>'toolKey'`.as("tool_key"), "expires_at"])
135
+ .where("id", "=", waitingFor.id)
136
+ .executeTakeFirst();
137
+ if (!row)
138
+ return;
139
+ await this.d.eventLog.append(d.parent_conversation_id, [
140
+ {
141
+ type: "subagent_waiting",
142
+ runId: d.parent_run_id,
143
+ idemKey: subagentWaitingIdemKey(runId, waitingFor.id),
144
+ payload: {
145
+ subagentId: d.child_conversation_id,
146
+ childRunId: runId,
147
+ kind: waitingFor.kind,
148
+ ...(waitingFor.kind === "approval"
149
+ ? { approvalId: waitingFor.id }
150
+ : { questionId: waitingFor.id }),
151
+ toolKey: row.tool_key,
152
+ expiresAt: new Date(row.expires_at).toISOString(),
153
+ },
154
+ },
155
+ ]);
156
+ }
157
+ catch (err) {
158
+ this.d.log.error("cannot surface a sub-agent's wait in its parent", { runId, err });
159
+ }
160
+ }
161
+ /**
162
+ * 续跑:一条条件更新 `waiting → queued`(且该运行没有 pending 的审批 / 输入请求),把本次等待时长累加进
163
+ * `waited_ms`,记下续跑任务的 jobId;成功(只有一个调用方)则投递运行任务并返回 true。
164
+ * 投递失败 → 撤回为 waiting(等待时长已计入,重新从现在起计),之后超时任务到点会再次续跑,并抛出。
165
+ */
166
+ async resume(runId, waitId) {
167
+ const row = await this.d.db
168
+ .updateTable("runs")
169
+ .set({
170
+ status: "queued",
171
+ queued_at: sql `now()`,
172
+ job_id: sql `'run-' || conversation_id || '-resume-' || ${waitId}::text`,
173
+ waited_ms: sql `waited_ms + COALESCE(floor(extract(epoch FROM now() - waiting_since) * 1000), 0)::bigint`,
174
+ waiting_since: null,
175
+ wait_expires_at: null,
176
+ })
177
+ .where("id", "=", runId)
178
+ .where("status", "=", "waiting")
179
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("approvals")
180
+ .select(sql `1`.as("one"))
181
+ .where("approvals.run_id", "=", runId)
182
+ .where("approvals.status", "=", "pending"))))
183
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("questions")
184
+ .select(sql `1`.as("one"))
185
+ .where("questions.run_id", "=", runId)
186
+ .where("questions.status", "=", "pending"))))
187
+ // sub-agents (spec §7.2): no child run of this run is still active, so a step waiting on
188
+ // an approval and on sub-agents resumes once both are done, whichever finishes last
189
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("subagent_dispatches as sd")
190
+ .innerJoin("runs as cr", "cr.id", "sd.child_run_id")
191
+ .select(sql `1`.as("one"))
192
+ .where("sd.parent_run_id", "=", runId)
193
+ .where("cr.status", "in", ACTIVE_RUN_STATUSES))))
194
+ .returning(["conversation_id"])
195
+ .executeTakeFirst();
196
+ if (!row)
197
+ return false;
198
+ const jobId = resumeJobId(row.conversation_id, waitId);
199
+ try {
200
+ await this.d.runQueue.enqueue({ runId, conversationId: row.conversation_id }, jobId);
201
+ }
202
+ catch (err) {
203
+ this.d.log.error("cannot enqueue resumed run; back to waiting", { runId, jobId, err });
204
+ await this.d.db
205
+ .updateTable("runs")
206
+ .set({ status: "waiting", waiting_since: sql `now()`, job_id: null, queued_at: null })
207
+ .where("id", "=", runId)
208
+ .where("status", "=", "queued")
209
+ .where("job_id", "=", jobId)
210
+ .execute();
211
+ throw err;
212
+ }
213
+ return true;
214
+ }
215
+ /**
216
+ * 超时(`wait-timeout` 任务):仍 pending 且已到期 → 置 expired 并写决定 / 回答事件;随后(无论本次是否生效)
217
+ * 尝试续跑——若人工决定之后的续跑没能发生(进程崩溃、投递失败),超时任务兜底。
218
+ * 尚未到期(任务被提前执行)→ 返回到期时间,由任务重新延迟。
219
+ */
220
+ async expire(kind, id, at) {
221
+ if (kind === "subagents") {
222
+ // A safety net (spec §7.2, §12): the children of that step still active are stopped; each
223
+ // stopped child reports to the parent, and the last one resumes it. The resume tried here
224
+ // covers a step whose children were all done already.
225
+ if (!at) {
226
+ this.d.log.warn("sub-agent wait timeout without its run; ignored", { id });
227
+ return {};
228
+ }
229
+ const { runId, step } = at;
230
+ const stopped = (await this.d.subagents?.stop(runId, null, { step, detail: "wait_timeout" })) ?? 0;
231
+ if (stopped > 0) {
232
+ this.d.log.warn("sub-agent wait timed out; stopping its sub-agents", {
233
+ runId,
234
+ id,
235
+ stopped,
236
+ });
237
+ }
238
+ const resumed = await this.resume(runId, id);
239
+ return { acted: stopped > 0 || resumed };
240
+ }
241
+ const r = kind === "approval" ? await this.d.approvals.expire(id) : await this.d.questions.expire(id);
242
+ if (r.outcome === "not_due" && r.record) {
243
+ return { notDueUntil: new Date(r.record.expiresAt).getTime() };
244
+ }
245
+ if (r.record)
246
+ await this.resume(r.record.runId, id);
247
+ return {};
248
+ }
249
+ /**
250
+ * 中止等待中的运行(`POST .../abort`):同一事务里锁会话行与运行行,运行仍是 waiting 时——取消它的 pending 审批 /
251
+ * 输入请求(写 `approval_decided{cancelled}` / `question_answered{cancelled}`),给等待中的调用写取消说明的
252
+ * `tool_result`(同一步里尚未执行的调用写「未执行」),再写 `run_aborted` 终态;提交后删除 checkpoint。
253
+ * 运行已不是 waiting(例如刚被续跑)→ 返回 false,由调用方走常规中止。
254
+ */
255
+ async abortWaiting(conversationId, runId, byAccountId, detail) {
256
+ let stopped;
257
+ const toSeq = await this.d.db.transaction().execute(async (t) => {
258
+ stopped = undefined;
259
+ await t
260
+ .selectFrom("conversations")
261
+ .select("id")
262
+ .where("id", "=", conversationId)
263
+ .forUpdate()
264
+ .executeTakeFirst();
265
+ const run = await t
266
+ .selectFrom("runs")
267
+ .select("status")
268
+ .where("id", "=", runId)
269
+ .where("conversation_id", "=", conversationId)
270
+ .forUpdate()
271
+ .executeTakeFirst();
272
+ if (run?.status !== "waiting")
273
+ return null;
274
+ await this.d.approvals.cancelPendingIn(t, runId);
275
+ await this.d.questions.cancelPendingIn(t, runId);
276
+ // 本运行全部审批 / 输入请求涉及的调用(不只刚取消的):决定已提交、续跑尚未发生时被中止的调用同样没有结果,
277
+ // 一并写取消说明(已有结果的按幂等键命中、不重复)。同一调用先审批后输入,后者覆盖前者。
278
+ const cancelled = new Map();
279
+ const steps = new Set();
280
+ const approvals = await t
281
+ .selectFrom("approvals")
282
+ .select(["id", "call_id", "tool_key"])
283
+ .where("run_id", "=", runId)
284
+ .orderBy("created_at")
285
+ .execute();
286
+ for (const a of approvals) {
287
+ cancelled.set(a.call_id, { toolKey: a.tool_key, text: APPROVAL_CANCELLED_TEXT });
288
+ const step = await this.approvalStep(t, conversationId, a.id);
289
+ if (step !== null)
290
+ steps.add(step);
291
+ }
292
+ const questions = await t
293
+ .selectFrom("questions")
294
+ .select(["call_id", "spec"])
295
+ .where("run_id", "=", runId)
296
+ .orderBy("created_at")
297
+ .execute();
298
+ for (const q of questions) {
299
+ const spec = q.spec;
300
+ cancelled.set(q.call_id, { toolKey: spec.toolKey ?? "", text: INPUT_CANCELLED_TEXT });
301
+ if (typeof spec.step === "number")
302
+ steps.add(spec.step);
303
+ }
304
+ // a parent waiting on sub-agents (spec §12): its open `agent` calls get their results
305
+ const agents = await this.d.subagents?.stopOpenCalls?.(t, runId);
306
+ for (const step of agents?.steps ?? [])
307
+ steps.add(step);
308
+ const results = await this.stoppedResults(t, conversationId, runId, [...steps], cancelled, agents?.callIds);
309
+ if (results.length > 0)
310
+ await this.d.eventLog.append(conversationId, results, t);
311
+ stopped = agents;
312
+ return finishRunIn(t, this.d.eventLog, {
313
+ conversationId,
314
+ runId,
315
+ status: "aborted",
316
+ consumed: null,
317
+ event: {
318
+ type: "run_aborted",
319
+ payload: { byAccountId, reason: "user", ...(detail !== undefined && { detail }) },
320
+ },
321
+ });
322
+ });
323
+ if (toSeq === null)
324
+ return false;
325
+ await notifyFinished(this.d.eventLog, conversationId, toSeq, "aborted");
326
+ await this.d.checkpoints
327
+ ?.deleteThread(runId)
328
+ .catch((err) => this.d.log.warn("checkpoint delete failed", { runId, err }));
329
+ stopped?.fire();
330
+ // a stopped sub-agent reports to its parent now, not at the next sweep (a no-op for other runs)
331
+ await this.d.subagents
332
+ ?.finished(runId)
333
+ .catch((err) => this.d.log.error("cannot report stopped sub-agent run", { runId, err }));
334
+ // a stopped parent stops its children (spec §12), on the parent's behalf (no account: they
335
+ // report "stopped with this run"); the wait sweep is the backstop
336
+ await this.d.subagents
337
+ ?.stop(runId, null)
338
+ .catch((err) => this.d.log.error("cannot stop the sub-agents of a run", { runId, err }));
339
+ return true;
340
+ }
341
+ /** 扫描 3:运行已终态(如暂停途中失败)仍 pending 的审批 / 输入请求 → 取消(写决定 / 回答事件)。 */
342
+ async sweepFinishedRuns() {
343
+ const { db } = this.d;
344
+ const runs = await db
345
+ .selectFrom("runs")
346
+ .select(["id", "conversation_id"])
347
+ .where("status", "in", TERMINAL_RUN_STATUSES)
348
+ .where(({ or, exists, selectFrom }) => or([
349
+ exists(selectFrom("approvals")
350
+ .select(sql `1`.as("one"))
351
+ .whereRef("approvals.run_id", "=", "runs.id")
352
+ .where("approvals.status", "=", "pending")),
353
+ exists(selectFrom("questions")
354
+ .select(sql `1`.as("one"))
355
+ .whereRef("questions.run_id", "=", "runs.id")
356
+ .where("questions.status", "=", "pending")),
357
+ ]))
358
+ .limit(WAIT_SWEEP_BATCH)
359
+ .execute();
360
+ let total = 0;
361
+ for (const run of runs) {
362
+ try {
363
+ const n = await this.cancelWaitsOfFinished(run.conversation_id, run.id);
364
+ if (n > 0) {
365
+ total += n;
366
+ this.d.log.warn("wait sweep: cancelled waits of finished run", {
367
+ runId: run.id,
368
+ conversationId: run.conversation_id,
369
+ count: n,
370
+ });
371
+ }
372
+ }
373
+ catch (err) {
374
+ this.d.log.error("wait sweep: cannot cancel waits of finished run", { runId: run.id, err });
375
+ }
376
+ }
377
+ return total;
378
+ }
379
+ /**
380
+ * 同一事务里锁会话行与运行行(与 {@link abortWaiting} 同序),运行仍是终态时取消其 pending 等待;
381
+ * 提交后发布事件。返回取消的条数(并发的另一副本拿到锁时已无 pending → 0)。
382
+ */
383
+ async cancelWaitsOfFinished(conversationId, runId) {
384
+ const r = await this.d.db.transaction().execute(async (t) => {
385
+ await t
386
+ .selectFrom("conversations")
387
+ .select("id")
388
+ .where("id", "=", conversationId)
389
+ .forUpdate()
390
+ .executeTakeFirst();
391
+ const run = await t
392
+ .selectFrom("runs")
393
+ .select("status")
394
+ .where("id", "=", runId)
395
+ .forUpdate()
396
+ .executeTakeFirst();
397
+ if (!run || !TERMINAL_RUN_STATUSES.includes(run.status))
398
+ return 0;
399
+ const a = await this.d.approvals.cancelPendingIn(t, runId);
400
+ const q = await this.d.questions.cancelPendingIn(t, runId);
401
+ return a.length + q.length;
402
+ });
403
+ if (r > 0) {
404
+ const last = await this.d.db
405
+ .selectFrom("conversation_events")
406
+ .select(sql `max(seq)`.as("seq"))
407
+ .where("conversation_id", "=", conversationId)
408
+ .executeTakeFirst();
409
+ if (last?.seq != null)
410
+ await this.d.eventLog.notify(conversationId, Number(last.seq));
411
+ }
412
+ return r;
413
+ }
414
+ /** 扫描 1:`expires_at < now()` 仍 pending 的审批 / 输入请求 → 与超时任务同一路径(置 expired、写事件、续跑)。 */
415
+ async sweepOverdue() {
416
+ const { db } = this.d;
417
+ const approvals = await db
418
+ .selectFrom("approvals")
419
+ .select(["id", "run_id"])
420
+ .where("status", "=", "pending")
421
+ .where("expires_at", "<", sql `now()`)
422
+ .orderBy("expires_at")
423
+ .limit(WAIT_SWEEP_BATCH)
424
+ .execute();
425
+ const questions = await db
426
+ .selectFrom("questions")
427
+ .select(["id", "run_id"])
428
+ .where("status", "=", "pending")
429
+ .where("expires_at", "<", sql `now()`)
430
+ .orderBy("expires_at")
431
+ .limit(WAIT_SWEEP_BATCH)
432
+ .execute();
433
+ const due = [
434
+ ...approvals.map((a) => ({ kind: "approval", id: a.id, runId: a.run_id })),
435
+ ...questions.map((q) => ({ kind: "input", id: q.id, runId: q.run_id })),
436
+ ];
437
+ let total = 0;
438
+ for (const w of due) {
439
+ try {
440
+ const r = w.kind === "approval"
441
+ ? await this.d.approvals.expire(w.id)
442
+ : await this.d.questions.expire(w.id);
443
+ if (r.outcome !== "expired")
444
+ continue;
445
+ total++;
446
+ this.d.log.warn("wait sweep: expired overdue wait", {
447
+ kind: w.kind,
448
+ id: w.id,
449
+ runId: w.runId,
450
+ });
451
+ if (r.record)
452
+ await this.resume(r.record.runId, w.id);
453
+ }
454
+ catch (err) {
455
+ this.d.log.error("wait sweep: cannot expire overdue wait", { kind: w.kind, id: w.id, err });
456
+ }
457
+ }
458
+ return total + (await this.sweepOverdueSubagents());
459
+ }
460
+ /**
461
+ * Sub-agents waits past their `runs.wait_expires_at` (their timeout job was lost): expired the
462
+ * way the timeout job would ({@link expire}: the step's active children stop as `timed out`, the
463
+ * parent resumes once they are done). The step is the run's latest dispatch step: a waiting run
464
+ * has active children only from the step it waits in. A run stays here until its children have
465
+ * stopped; a pass that neither stopped a child nor resumed the run is not counted.
466
+ */
467
+ async sweepOverdueSubagents() {
468
+ if (!this.d.subagents)
469
+ return 0;
470
+ const runs = await this.d.db
471
+ .selectFrom("runs")
472
+ .select(["id"])
473
+ .where("status", "=", "waiting")
474
+ .where("wait_expires_at", "<", sql `now()`)
475
+ .orderBy("wait_expires_at")
476
+ .limit(WAIT_SWEEP_BATCH)
477
+ .execute();
478
+ let total = 0;
479
+ for (const r of runs) {
480
+ try {
481
+ const last = await this.d.db
482
+ .selectFrom("subagent_dispatches")
483
+ .select(sql `max(step)`.as("step"))
484
+ .where("parent_run_id", "=", r.id)
485
+ .executeTakeFirst();
486
+ if (last?.step == null)
487
+ continue;
488
+ const step = Number(last.step);
489
+ const id = subagentsWaitId(r.id, step);
490
+ // children asked to stop may take a while to end, so the same wait can come back on the
491
+ // next sweep: only a pass that stopped a child or resumed the run counts and logs (M3)
492
+ const { acted } = await this.expire("subagents", id, { runId: r.id, step });
493
+ if (!acted)
494
+ continue;
495
+ this.d.log.warn("wait sweep: expired overdue wait", { kind: "subagents", id, runId: r.id });
496
+ total++;
497
+ }
498
+ catch (err) {
499
+ this.d.log.error("wait sweep: cannot expire overdue wait", {
500
+ kind: "subagents",
501
+ runId: r.id,
502
+ err,
503
+ });
504
+ }
505
+ }
506
+ return total;
507
+ }
508
+ /**
509
+ * Sub-agent runs nobody will run (spec §7.5; `RunQueue.repairOrphan`, the same orphan test as
510
+ * `ensureRun`, which never runs for a child): `queued` past the grace period without a job → enqueued
511
+ * again (the dispatch committed, its enqueue was lost); `running` without job and lock → failed
512
+ * `run.orphaned` and reported to the parent, which then resumes. Also a chat run left `queued`
513
+ * by a resume whose enqueue was lost (final review m8): enqueued again under its resume job id
514
+ * (`ensureRun` would only repair it at the conversation's next message).
515
+ */
516
+ async sweepSubagentOrphans() {
517
+ const out = { requeued: 0, orphaned: 0 };
518
+ const sub = this.d.subagents;
519
+ if (!sub)
520
+ return out;
521
+ const runs = await this.d.db
522
+ .selectFrom("runs")
523
+ .innerJoin("conversations as c", "c.id", "runs.conversation_id")
524
+ .select(["runs.id", "runs.conversation_id"])
525
+ .where((eb) => eb.or([
526
+ eb.and([eb("c.kind", "=", "subagent"), eb("runs.status", "in", ["queued", "running"])]),
527
+ // a parent (or any chat run) set `queued` by Waits.resume whose replica died before its
528
+ // queue.add: child completions make that a frequent cross-replica resume (m8); its job
529
+ // id is the resume job id recorded with the update
530
+ eb.and([
531
+ eb("c.kind", "=", "chat"),
532
+ eb("runs.status", "=", "queued"),
533
+ eb("runs.job_id", "like", "run-%-resume-%"),
534
+ ]),
535
+ ]))
536
+ .where(sql `COALESCE(runs.queued_at, runs.created_at)`, "<", sql `now() - ${WAIT_SWEEP_GRACE_MS}::integer * interval '1 millisecond'`)
537
+ .limit(WAIT_SWEEP_BATCH)
538
+ .execute();
539
+ for (const run of runs) {
540
+ try {
541
+ const r = await this.d.runQueue.repairOrphan(run.conversation_id, run.id);
542
+ if (r === null)
543
+ continue;
544
+ if (r === "failed") {
545
+ out.orphaned++;
546
+ await sub.finished(run.id);
547
+ }
548
+ else
549
+ out.requeued++;
550
+ this.d.log.warn("wait sweep: repaired orphaned sub-agent run", {
551
+ runId: run.id,
552
+ conversationId: run.conversation_id,
553
+ outcome: r,
554
+ });
555
+ }
556
+ catch (err) {
557
+ this.d.log.error("wait sweep: cannot repair orphaned sub-agent run", {
558
+ runId: run.id,
559
+ err,
560
+ });
561
+ }
562
+ }
563
+ return out;
564
+ }
565
+ /**
566
+ * Finished parent runs (past the grace period, within the last day) with an `agent` call that has
567
+ * no `tool_result` (final review I3): a terminal path that skipped `stopOpenAgentCalls`, such as
568
+ * an old replica's abort, would otherwise leave an unpaired tool call that every later request of
569
+ * the conversation sends and providers refuse. Each is repaired under the conversation row lock
570
+ * with the normal `stopOpenCalls` (a child that ended gives its result, an active one the
571
+ * stopped text; idempotency keys make it safe against the regular paths); its active children
572
+ * are then stopped by {@link sweepChildrenOfFinished}.
573
+ */
574
+ async sweepOpenAgentCalls() {
575
+ const stopOpenCalls = this.d.subagents?.stopOpenCalls;
576
+ if (!stopOpenCalls)
577
+ return 0;
578
+ const agentCalls = await this.d.db
579
+ .selectFrom("subagent_dispatches as sd")
580
+ .innerJoin("runs as pr", "pr.id", "sd.parent_run_id")
581
+ .select(["sd.parent_run_id", "sd.parent_conversation_id"])
582
+ .distinct()
583
+ .where("pr.status", "in", TERMINAL_RUN_STATUSES)
584
+ .where("pr.finished_at", "<", sql `now() - ${WAIT_SWEEP_GRACE_MS}::integer * interval '1 millisecond'`)
585
+ .where("pr.finished_at", ">", sql `now() - interval '1 day'`)
586
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("conversation_events as e")
587
+ .select(sql `1`.as("one"))
588
+ .whereRef("e.conversation_id", "=", "sd.parent_conversation_id")
589
+ .where("e.idem_key", "=", sql `'tool_result:' || sd.parent_run_id || ':' || sd.parent_call_id`))))
590
+ // a fork invocation's dispatch (phase D) has no tool call: the query below covers it
591
+ .where(sql `NOT (sd.step = 0 AND sd.parent_call_id LIKE 'skill-%')`)
592
+ .limit(WAIT_SWEEP_BATCH)
593
+ .execute();
594
+ const forkRuns = await unreportedForkRuns(this.d.db);
595
+ const runs = [...agentCalls, ...forkRuns].filter((r, i, all) => all.findIndex((x) => x.parent_run_id === r.parent_run_id) === i);
596
+ let total = 0;
597
+ for (const r of runs) {
598
+ try {
599
+ let fire;
600
+ const toSeq = await this.d.db.transaction().execute(async (t) => {
601
+ fire = undefined;
602
+ await t
603
+ .selectFrom("conversations")
604
+ .select("id")
605
+ .where("id", "=", r.parent_conversation_id)
606
+ .forUpdate()
607
+ .executeTakeFirst();
608
+ const run = await t
609
+ .selectFrom("runs")
610
+ .select("status")
611
+ .where("id", "=", r.parent_run_id)
612
+ .executeTakeFirst();
613
+ if (run === undefined ||
614
+ !TERMINAL_RUN_STATUSES.includes(run.status)) {
615
+ return null;
616
+ }
617
+ const stopped = await stopOpenCalls(t, r.parent_run_id);
618
+ if (stopped.callIds.size === 0)
619
+ return null;
620
+ fire = stopped.fire;
621
+ return stopped.toSeq ?? null;
622
+ });
623
+ if (fire === undefined)
624
+ continue;
625
+ if (toSeq !== null)
626
+ await this.d.eventLog.notify(r.parent_conversation_id, toSeq);
627
+ fire();
628
+ total++;
629
+ this.d.log.warn("wait sweep: wrote missing agent results of a finished run", {
630
+ runId: r.parent_run_id,
631
+ conversationId: r.parent_conversation_id,
632
+ });
633
+ }
634
+ catch (err) {
635
+ this.d.log.error("wait sweep: cannot write missing agent results of a finished run", {
636
+ runId: r.parent_run_id,
637
+ err,
638
+ });
639
+ }
640
+ }
641
+ return total;
642
+ }
643
+ /**
644
+ * Active sub-agent runs of a parent run that finished more than the grace period ago (spec §7.5,
645
+ * §12: a parent's terminal state stops its children; this is the backstop) → stopped.
646
+ */
647
+ async sweepChildrenOfFinished() {
648
+ const sub = this.d.subagents;
649
+ if (!sub)
650
+ return 0;
651
+ const parents = await this.d.db
652
+ .selectFrom("subagent_dispatches as sd")
653
+ .innerJoin("runs as cr", "cr.id", "sd.child_run_id")
654
+ .innerJoin("runs as pr", "pr.id", "sd.parent_run_id")
655
+ .select("sd.parent_run_id")
656
+ .distinct()
657
+ .where("cr.status", "in", ACTIVE_RUN_STATUSES)
658
+ .where("pr.status", "in", TERMINAL_RUN_STATUSES)
659
+ .where("pr.finished_at", "<", sql `now() - ${WAIT_SWEEP_GRACE_MS}::integer * interval '1 millisecond'`)
660
+ .limit(WAIT_SWEEP_BATCH)
661
+ .execute();
662
+ let total = 0;
663
+ for (const p of parents) {
664
+ try {
665
+ const n = await sub.stop(p.parent_run_id, null);
666
+ if (n === 0)
667
+ continue;
668
+ total += n;
669
+ this.d.log.warn("wait sweep: stopped sub-agents of a finished run", {
670
+ runId: p.parent_run_id,
671
+ count: n,
672
+ });
673
+ }
674
+ catch (err) {
675
+ this.d.log.error("wait sweep: cannot stop sub-agents of a finished run", {
676
+ runId: p.parent_run_id,
677
+ err,
678
+ });
679
+ }
680
+ }
681
+ return total;
682
+ }
683
+ /**
684
+ * 扫描 2:waiting 超过宽限期、却已没有 pending 等待的运行(决定提交后进程崩溃、续跑投递失败回滚等)→
685
+ * {@link resume}(条件更新 `waiting → queued` + 投递)。续跑任务的 jobId 用该运行最近一次等待的 id
686
+ * (与人工决定 / 超时任务一致,已投递过的同一续跑被 BullMQ 去重)。
687
+ */
688
+ async sweepStuckRuns() {
689
+ const runs = await this.d.db
690
+ .selectFrom("runs")
691
+ .select(["id", "conversation_id"])
692
+ .where("status", "=", "waiting")
693
+ .where("waiting_since", "<", sql `now() - ${WAIT_SWEEP_GRACE_MS}::integer * interval '1 millisecond'`)
694
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("approvals")
695
+ .select(sql `1`.as("one"))
696
+ .whereRef("approvals.run_id", "=", "runs.id")
697
+ .where("approvals.status", "=", "pending"))))
698
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("questions")
699
+ .select(sql `1`.as("one"))
700
+ .whereRef("questions.run_id", "=", "runs.id")
701
+ .where("questions.status", "=", "pending"))))
702
+ // the resume condition's third clause: a parent with an active child is not stuck
703
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("subagent_dispatches as sd")
704
+ .innerJoin("runs as cr", "cr.id", "sd.child_run_id")
705
+ .select(sql `1`.as("one"))
706
+ .whereRef("sd.parent_run_id", "=", "runs.id")
707
+ .where("cr.status", "in", ACTIVE_RUN_STATUSES))))
708
+ .limit(WAIT_SWEEP_BATCH)
709
+ .execute();
710
+ let total = 0;
711
+ for (const run of runs) {
712
+ try {
713
+ const waitId = (await this.lastWaitId(run.id)) ?? run.id;
714
+ if (!(await this.resume(run.id, waitId)))
715
+ continue;
716
+ total++;
717
+ this.d.log.warn("wait sweep: resumed stuck waiting run", {
718
+ runId: run.id,
719
+ conversationId: run.conversation_id,
720
+ waitId,
721
+ });
722
+ }
723
+ catch (err) {
724
+ this.d.log.error("wait sweep: cannot resume stuck waiting run", { runId: run.id, err });
725
+ }
726
+ }
727
+ return total;
728
+ }
729
+ /**
730
+ * The id of the run's latest wait (by creation time): an approval, an input request or a
731
+ * sub-agent dispatch, whose wait id is its child run id (the id the child's finisher resumes
732
+ * with, so a sweep resume and a late finisher resume share one job id).
733
+ */
734
+ async lastWaitId(runId) {
735
+ const row = await this.d.db
736
+ .selectFrom(this.d.db
737
+ .selectFrom("approvals")
738
+ .select(["id", "created_at"])
739
+ .where("run_id", "=", runId)
740
+ .unionAll(this.d.db
741
+ .selectFrom("questions")
742
+ .select(["id", "created_at"])
743
+ .where("run_id", "=", runId))
744
+ .unionAll(this.d.db
745
+ .selectFrom("subagent_dispatches")
746
+ .select(["child_run_id as id", "created_at"])
747
+ .where("parent_run_id", "=", runId))
748
+ .as("w"))
749
+ .select("w.id")
750
+ .orderBy("w.created_at", "desc")
751
+ .limit(1)
752
+ .executeTakeFirst();
753
+ return row?.id ?? null;
754
+ }
755
+ async process(job, token) {
756
+ const { kind, id, runId, step } = job.data;
757
+ const at = runId !== undefined && step !== undefined ? { runId, step } : undefined;
758
+ const r = await this.expire(kind, id, at);
759
+ if (r.notDueUntil !== undefined) {
760
+ await job.moveToDelayed(r.notDueUntil, token);
761
+ throw new DelayedError();
762
+ }
763
+ }
764
+ /** 投递超时任务(延迟到 expiresAt)。失败只记日志:人工处理仍可续跑,只是不会自动超时。 */
765
+ async scheduleTimeout(w, runId) {
766
+ const data = w.kind === "subagents" && w.step !== undefined
767
+ ? { kind: w.kind, id: w.id, runId, step: w.step }
768
+ : { kind: w.kind, id: w.id };
769
+ try {
770
+ await this.d.maintenance.add(WAIT_TIMEOUT_JOB, data, {
771
+ jobId: waitTimeoutJobId(w.id),
772
+ delay: Math.max(0, new Date(w.expiresAt).getTime() - Date.now()),
773
+ attempts: TIMEOUT_JOB_ATTEMPTS,
774
+ backoff: { type: "exponential", delay: TIMEOUT_JOB_BACKOFF_MS },
775
+ removeOnComplete: true,
776
+ removeOnFail: true,
777
+ });
778
+ }
779
+ catch (err) {
780
+ this.d.log.error("cannot schedule wait timeout", { waitId: w.id, err });
781
+ }
782
+ }
783
+ /**
784
+ * Whether the wait is over: an approval or input request is no longer pending (decided, expired
785
+ * or cancelled); `subagents`: no child run of the run is active.
786
+ */
787
+ async isSettled(w, runId) {
788
+ if (w.kind === "subagents") {
789
+ const active = await this.d.db
790
+ .selectFrom("subagent_dispatches as sd")
791
+ .innerJoin("runs as cr", "cr.id", "sd.child_run_id")
792
+ .select("cr.id")
793
+ .where("sd.parent_run_id", "=", runId)
794
+ .where("cr.status", "in", ACTIVE_RUN_STATUSES)
795
+ .executeTakeFirst();
796
+ return active === undefined;
797
+ }
798
+ const row = w.kind === "approval"
799
+ ? await this.d.db
800
+ .selectFrom("approvals")
801
+ .select("status")
802
+ .where("id", "=", w.id)
803
+ .executeTakeFirst()
804
+ : await this.d.db
805
+ .selectFrom("questions")
806
+ .select("status")
807
+ .where("id", "=", w.id)
808
+ .executeTakeFirst();
809
+ return row !== undefined && row.status !== "pending";
810
+ }
811
+ /** 审批所在的步(`approval_requested` 事件里记的)。 */
812
+ async approvalStep(t, conversationId, approvalId) {
813
+ const ev = await t
814
+ .selectFrom("conversation_events")
815
+ .select("payload")
816
+ .where("conversation_id", "=", conversationId)
817
+ .where("idem_key", "=", `approval_requested:${approvalId}`)
818
+ .executeTakeFirst();
819
+ const step = ev?.payload?.step;
820
+ return typeof step === "number" ? step : null;
821
+ }
822
+ /**
823
+ * 等待中被中止的运行:这些步里每个调用都补一条 `tool_result`(已有结果的按幂等键命中、不重复),
824
+ * 等待中的调用给取消说明,其余(排在等待调用之后、尚未执行的)给「未执行」说明。
825
+ */
826
+ async stoppedResults(t, conversationId, runId, steps, cancelled,
827
+ /** Calls whose result the caller writes itself (open `agent` calls). */
828
+ skip = new Set()) {
829
+ const out = [];
830
+ for (const step of steps.sort((a, b) => a - b)) {
831
+ const msg = await t
832
+ .selectFrom("conversation_events")
833
+ .select("payload")
834
+ .where("conversation_id", "=", conversationId)
835
+ .where("idem_key", "=", assistantIdemKey(runId, step))
836
+ .executeTakeFirst();
837
+ const content = msg?.payload?.content ?? [];
838
+ for (const b of content) {
839
+ if (b.type !== "tool_call" || b.id === undefined || skip.has(b.id))
840
+ continue;
841
+ const c = cancelled.get(b.id);
842
+ out.push({
843
+ type: "tool_result",
844
+ runId,
845
+ idemKey: `tool_result:${runId}:${b.id}`,
846
+ payload: {
847
+ step,
848
+ callId: b.id,
849
+ toolKey: c?.toolKey ?? b.name ?? "",
850
+ content: c?.text ?? RUN_STOPPED_TEXT,
851
+ isError: true,
852
+ },
853
+ });
854
+ }
855
+ }
856
+ return out;
857
+ }
858
+ }
859
+ /**
860
+ * Phase D: finished chat runs (past the grace period, within the last day) triggered by a fork
861
+ * invocation that has no report (dispatched or not); the wait sweep writes it
862
+ * (`pairStoppedForks`).
863
+ */
864
+ export function unreportedForkRuns(db) {
865
+ return (db
866
+ .selectFrom("runs as r")
867
+ .innerJoin("conversation_events as m", (j) => j.onRef("m.conversation_id", "=", "r.conversation_id").onRef("m.seq", "=", "r.trigger_seq"))
868
+ // a sub-agent's task message carries the skill record too: chat conversations only (I1)
869
+ .innerJoin("conversations as c", "c.id", "r.conversation_id")
870
+ .select(["r.id as parent_run_id", "r.conversation_id as parent_conversation_id"])
871
+ .where("c.kind", "=", "chat")
872
+ .where("r.status", "in", TERMINAL_RUN_STATUSES)
873
+ .where("r.finished_at", "<", sql `now() - ${WAIT_SWEEP_GRACE_MS}::integer * interval '1 millisecond'`)
874
+ .where("r.finished_at", ">", sql `now() - interval '1 day'`)
875
+ .where("m.type", "=", "user_message")
876
+ .where(sql `m.payload -> 'skill' ->> 'mode' = 'fork'`)
877
+ .where(({ not, exists, selectFrom }) => not(exists(selectFrom("conversation_events as e")
878
+ .select(sql `1`.as("one"))
879
+ .whereRef("e.conversation_id", "=", "r.conversation_id")
880
+ .where("e.idem_key", "=", sql `'skill_fork_result:' || r.trigger_seq`))))
881
+ .limit(WAIT_SWEEP_BATCH)
882
+ .execute());
883
+ }