@nocobase/app-plugin-agents 0.1.0-beta.1 → 0.1.0-beta.11

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 (649) hide show
  1. package/CHANGELOG.md +398 -0
  2. package/README.md +129 -67
  3. package/dist/client/api/client.d.ts +25 -17
  4. package/dist/client/api/client.d.ts.map +1 -1
  5. package/dist/client/api/client.js +29 -27
  6. package/dist/client/api/client.js.map +1 -1
  7. package/dist/client/api/keys.d.ts +1 -1
  8. package/dist/client/api/keys.d.ts.map +1 -1
  9. package/dist/client/api/keys.js +1 -1
  10. package/dist/client/api/keys.js.map +1 -1
  11. package/dist/client/chat/api.d.ts +2 -7
  12. package/dist/client/chat/api.d.ts.map +1 -1
  13. package/dist/client/chat/api.js +0 -7
  14. package/dist/client/chat/api.js.map +1 -1
  15. package/dist/client/chat/consultation-identity.d.ts +7 -0
  16. package/dist/client/chat/consultation-identity.d.ts.map +1 -0
  17. package/dist/client/chat/consultation-identity.js +39 -0
  18. package/dist/client/chat/consultation-identity.js.map +1 -0
  19. package/dist/client/chat/message-model.d.ts +4 -2
  20. package/dist/client/chat/message-model.d.ts.map +1 -1
  21. package/dist/client/chat/message-model.js +28 -3
  22. package/dist/client/chat/message-model.js.map +1 -1
  23. package/dist/client/chat/use-chat.d.ts +2 -10
  24. package/dist/client/chat/use-chat.d.ts.map +1 -1
  25. package/dist/client/chat/use-chat.js +3 -10
  26. package/dist/client/chat/use-chat.js.map +1 -1
  27. package/dist/client/chat.d.ts +4 -1
  28. package/dist/client/chat.d.ts.map +1 -1
  29. package/dist/client/chat.js +3 -0
  30. package/dist/client/chat.js.map +1 -1
  31. package/dist/client/components/agent-picker.d.ts +6 -23
  32. package/dist/client/components/agent-picker.d.ts.map +1 -1
  33. package/dist/client/components/agent-picker.js +19 -39
  34. package/dist/client/components/agent-picker.js.map +1 -1
  35. package/dist/client/components/variables/variable-dialogs.d.ts +6 -1
  36. package/dist/client/components/variables/variable-dialogs.d.ts.map +1 -1
  37. package/dist/client/components/variables/variable-dialogs.js +27 -11
  38. package/dist/client/components/variables/variable-dialogs.js.map +1 -1
  39. package/dist/client/components/variables/variables-panel.d.ts.map +1 -1
  40. package/dist/client/components/variables/variables-panel.js +9 -2
  41. package/dist/client/components/variables/variables-panel.js.map +1 -1
  42. package/dist/client/hooks/use-agent-picker-labels.d.ts.map +1 -1
  43. package/dist/client/hooks/use-agent-picker-labels.js +3 -12
  44. package/dist/client/hooks/use-agent-picker-labels.js.map +1 -1
  45. package/dist/client/kit.d.ts +9 -12
  46. package/dist/client/kit.d.ts.map +1 -1
  47. package/dist/client/kit.js +12 -14
  48. package/dist/client/kit.js.map +1 -1
  49. package/dist/client/lib/agents.d.ts +5 -13
  50. package/dist/client/lib/agents.d.ts.map +1 -1
  51. package/dist/client/lib/agents.js +17 -32
  52. package/dist/client/lib/agents.js.map +1 -1
  53. package/dist/client/lib/install.js +1 -1
  54. package/dist/client/locales/chat.en-US.d.ts +9 -22
  55. package/dist/client/locales/chat.en-US.d.ts.map +1 -1
  56. package/dist/client/locales/chat.en-US.js +13 -25
  57. package/dist/client/locales/chat.en-US.js.map +1 -1
  58. package/dist/client/locales/chat.zh-CN.d.ts.map +1 -1
  59. package/dist/client/locales/chat.zh-CN.js +10 -22
  60. package/dist/client/locales/chat.zh-CN.js.map +1 -1
  61. package/dist/client/locales/en-US.d.ts +101 -219
  62. package/dist/client/locales/en-US.d.ts.map +1 -1
  63. package/dist/client/locales/en-US.js +106 -219
  64. package/dist/client/locales/en-US.js.map +1 -1
  65. package/dist/client/locales/runWait.en-US.d.ts +38 -0
  66. package/dist/client/locales/runWait.en-US.d.ts.map +1 -0
  67. package/dist/client/locales/runWait.en-US.js +37 -0
  68. package/dist/client/locales/runWait.en-US.js.map +1 -0
  69. package/dist/client/locales/runWait.zh-CN.d.ts +5 -0
  70. package/dist/client/locales/runWait.zh-CN.d.ts.map +1 -0
  71. package/dist/client/locales/runWait.zh-CN.js +32 -0
  72. package/dist/client/locales/runWait.zh-CN.js.map +1 -0
  73. package/dist/client/locales/runtimes.en-US.d.ts +54 -0
  74. package/dist/client/locales/runtimes.en-US.d.ts.map +1 -1
  75. package/dist/client/locales/runtimes.en-US.js +57 -3
  76. package/dist/client/locales/runtimes.en-US.js.map +1 -1
  77. package/dist/client/locales/runtimes.zh-CN.d.ts.map +1 -1
  78. package/dist/client/locales/runtimes.zh-CN.js +57 -3
  79. package/dist/client/locales/runtimes.zh-CN.js.map +1 -1
  80. package/dist/client/locales/skills.en-US.d.ts +1 -0
  81. package/dist/client/locales/skills.en-US.d.ts.map +1 -1
  82. package/dist/client/locales/skills.en-US.js +2 -1
  83. package/dist/client/locales/skills.en-US.js.map +1 -1
  84. package/dist/client/locales/skills.zh-CN.d.ts.map +1 -1
  85. package/dist/client/locales/skills.zh-CN.js +2 -1
  86. package/dist/client/locales/skills.zh-CN.js.map +1 -1
  87. package/dist/client/locales/zh-CN.d.ts.map +1 -1
  88. package/dist/client/locales/zh-CN.js +108 -221
  89. package/dist/client/locales/zh-CN.js.map +1 -1
  90. package/dist/client/online/services.d.ts.map +1 -1
  91. package/dist/client/online/services.js +5 -6
  92. package/dist/client/online/services.js.map +1 -1
  93. package/dist/client/pages/agents/agent-dialog.d.ts +9 -0
  94. package/dist/client/pages/agents/agent-dialog.d.ts.map +1 -0
  95. package/dist/client/pages/agents/agent-dialog.js +106 -0
  96. package/dist/client/pages/agents/agent-dialog.js.map +1 -0
  97. package/dist/client/pages/agents/agent-state.d.ts +11 -0
  98. package/dist/client/pages/agents/agent-state.d.ts.map +1 -0
  99. package/dist/client/pages/agents/agent-state.js +28 -0
  100. package/dist/client/pages/agents/agent-state.js.map +1 -0
  101. package/dist/client/pages/agents/index.d.ts +1 -1
  102. package/dist/client/pages/agents/index.d.ts.map +1 -1
  103. package/dist/client/pages/agents/index.js +6 -100
  104. package/dist/client/pages/agents/index.js.map +1 -1
  105. package/dist/client/pages/agents/runner-agents.d.ts +3 -0
  106. package/dist/client/pages/agents/runner-agents.d.ts.map +1 -0
  107. package/dist/client/pages/agents/runner-agents.js +172 -0
  108. package/dist/client/pages/agents/runner-agents.js.map +1 -0
  109. package/dist/client/pages/runtimes/connect.js +1 -1
  110. package/dist/client/pages/runtimes/connect.js.map +1 -1
  111. package/dist/client/pages/runtimes/index.d.ts.map +1 -1
  112. package/dist/client/pages/runtimes/index.js +3 -2
  113. package/dist/client/pages/runtimes/index.js.map +1 -1
  114. package/dist/client/pages/runtimes/runner-cells.d.ts +1 -1
  115. package/dist/client/pages/runtimes/runner-cells.d.ts.map +1 -1
  116. package/dist/client/pages/runtimes/runner-cells.js +4 -1
  117. package/dist/client/pages/runtimes/runner-cells.js.map +1 -1
  118. package/dist/client/pages/runtimes/runner-sheet.d.ts.map +1 -1
  119. package/dist/client/pages/runtimes/runner-sheet.js +12 -3
  120. package/dist/client/pages/runtimes/runner-sheet.js.map +1 -1
  121. package/dist/client/pages/runtimes/workspace-diagnostics.d.ts +9 -0
  122. package/dist/client/pages/runtimes/workspace-diagnostics.d.ts.map +1 -0
  123. package/dist/client/pages/runtimes/workspace-diagnostics.js +68 -0
  124. package/dist/client/pages/runtimes/workspace-diagnostics.js.map +1 -0
  125. package/dist/client/pages/skills/detail/index.d.ts.map +1 -1
  126. package/dist/client/pages/skills/detail/index.js +7 -10
  127. package/dist/client/pages/skills/detail/index.js.map +1 -1
  128. package/dist/client/pages/skills/index.d.ts.map +1 -1
  129. package/dist/client/pages/skills/index.js +2 -2
  130. package/dist/client/pages/skills/index.js.map +1 -1
  131. package/dist/client/pages/usage/index.js +2 -2
  132. package/dist/client/pages/usage/index.js.map +1 -1
  133. package/dist/client/profile/default-agent.d.ts.map +1 -1
  134. package/dist/client/profile/default-agent.js +2 -4
  135. package/dist/client/profile/default-agent.js.map +1 -1
  136. package/dist/client/routes.d.ts +1 -1
  137. package/dist/client/routes.d.ts.map +1 -1
  138. package/dist/client/routes.js +1 -13
  139. package/dist/client/routes.js.map +1 -1
  140. package/dist/client/runs/format-wait.d.ts +21 -0
  141. package/dist/client/runs/format-wait.d.ts.map +1 -0
  142. package/dist/client/runs/format-wait.js +90 -0
  143. package/dist/client/runs/format-wait.js.map +1 -0
  144. package/dist/client/runs/subject-runs.d.ts +5 -0
  145. package/dist/client/runs/subject-runs.d.ts.map +1 -1
  146. package/dist/client/runs/subject-runs.js +13 -7
  147. package/dist/client/runs/subject-runs.js.map +1 -1
  148. package/dist/client/runs.d.ts +3 -1
  149. package/dist/client/runs.d.ts.map +1 -1
  150. package/dist/client/runs.js +3 -1
  151. package/dist/client/runs.js.map +1 -1
  152. package/dist/database/migrations/.manifest.json +62 -2
  153. package/dist/database/migrations/202610020003_ag_create_runs.d.ts.map +1 -1
  154. package/dist/database/migrations/202610020003_ag_create_runs.js +0 -1
  155. package/dist/database/migrations/202610020003_ag_create_runs.js.map +1 -1
  156. package/dist/database/migrations/202610090001_ag_add_team_runner_variables.d.ts +9 -0
  157. package/dist/database/migrations/202610090001_ag_add_team_runner_variables.d.ts.map +1 -0
  158. package/dist/database/migrations/202610090001_ag_add_team_runner_variables.js +28 -0
  159. package/dist/database/migrations/202610090001_ag_add_team_runner_variables.js.map +1 -0
  160. package/dist/database/migrations/202610090002_ag_create_run_requests.d.ts +10 -0
  161. package/dist/database/migrations/202610090002_ag_create_run_requests.d.ts.map +1 -0
  162. package/dist/database/migrations/202610090002_ag_create_run_requests.js +105 -0
  163. package/dist/database/migrations/202610090002_ag_create_run_requests.js.map +1 -0
  164. package/dist/database/migrations/202610090003_ag_add_execution_history.d.ts +4 -0
  165. package/dist/database/migrations/202610090003_ag_add_execution_history.d.ts.map +1 -0
  166. package/dist/database/migrations/202610090003_ag_add_execution_history.js +17 -0
  167. package/dist/database/migrations/202610090003_ag_add_execution_history.js.map +1 -0
  168. package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.d.ts +9 -0
  169. package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.d.ts.map +1 -0
  170. package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.js +22 -0
  171. package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.js.map +1 -0
  172. package/dist/database/migrations/202610090005_ag_add_runner_variables.d.ts +10 -0
  173. package/dist/database/migrations/202610090005_ag_add_runner_variables.d.ts.map +1 -0
  174. package/dist/database/migrations/202610090005_ag_add_runner_variables.js +36 -0
  175. package/dist/database/migrations/202610090005_ag_add_runner_variables.js.map +1 -0
  176. package/dist/database/migrations/202610100001_ag_index_run_event_types.d.ts +4 -0
  177. package/dist/database/migrations/202610100001_ag_index_run_event_types.d.ts.map +1 -0
  178. package/dist/database/migrations/202610100001_ag_index_run_event_types.js +17 -0
  179. package/dist/database/migrations/202610100001_ag_index_run_event_types.js.map +1 -0
  180. package/dist/database/migrations/202610100002_ag_add_run_git_credentials.d.ts +9 -0
  181. package/dist/database/migrations/202610100002_ag_add_run_git_credentials.d.ts.map +1 -0
  182. package/dist/database/migrations/202610100002_ag_add_run_git_credentials.js +30 -0
  183. package/dist/database/migrations/202610100002_ag_add_run_git_credentials.js.map +1 -0
  184. package/dist/database/migrations/202610110001_ag_add_fresh_sessions.d.ts +4 -0
  185. package/dist/database/migrations/202610110001_ag_add_fresh_sessions.d.ts.map +1 -0
  186. package/dist/database/migrations/202610110001_ag_add_fresh_sessions.js +23 -0
  187. package/dist/database/migrations/202610110001_ag_add_fresh_sessions.js.map +1 -0
  188. package/dist/database/migrations/202610120001_ag_index_run_retries.d.ts +4 -0
  189. package/dist/database/migrations/202610120001_ag_index_run_retries.d.ts.map +1 -0
  190. package/dist/database/migrations/202610120001_ag_index_run_retries.js +17 -0
  191. package/dist/database/migrations/202610120001_ag_index_run_retries.js.map +1 -0
  192. package/dist/database/migrations/202610120002_ag_add_queue_entered_at.d.ts +4 -0
  193. package/dist/database/migrations/202610120002_ag_add_queue_entered_at.d.ts.map +1 -0
  194. package/dist/database/migrations/202610120002_ag_add_queue_entered_at.js +50 -0
  195. package/dist/database/migrations/202610120002_ag_add_queue_entered_at.js.map +1 -0
  196. package/dist/database/migrations/202610240020_ag_runner_agents.d.ts +4 -0
  197. package/dist/database/migrations/202610240020_ag_runner_agents.d.ts.map +1 -0
  198. package/dist/database/migrations/202610240020_ag_runner_agents.js +115 -0
  199. package/dist/database/migrations/202610240020_ag_runner_agents.js.map +1 -0
  200. package/dist/database/migrations/202610240040_ag_discovery_preferences.d.ts +4 -0
  201. package/dist/database/migrations/202610240040_ag_discovery_preferences.d.ts.map +1 -0
  202. package/dist/database/migrations/202610240040_ag_discovery_preferences.js +18 -0
  203. package/dist/database/migrations/202610240040_ag_discovery_preferences.js.map +1 -0
  204. package/dist/database/migrations/202610250001_ag_comment_runner_consent.d.ts +4 -0
  205. package/dist/database/migrations/202610250001_ag_comment_runner_consent.d.ts.map +1 -0
  206. package/dist/database/migrations/202610250001_ag_comment_runner_consent.js +41 -0
  207. package/dist/database/migrations/202610250001_ag_comment_runner_consent.js.map +1 -0
  208. package/dist/database/migrations/202610260001_ag_drop_directory_index.d.ts +4 -0
  209. package/dist/database/migrations/202610260001_ag_drop_directory_index.d.ts.map +1 -0
  210. package/dist/database/migrations/202610260001_ag_drop_directory_index.js +24 -0
  211. package/dist/database/migrations/202610260001_ag_drop_directory_index.js.map +1 -0
  212. package/dist/database/migrations/202610260001_ag_run_revision.d.ts +4 -0
  213. package/dist/database/migrations/202610260001_ag_run_revision.d.ts.map +1 -0
  214. package/dist/database/migrations/202610260001_ag_run_revision.js +19 -0
  215. package/dist/database/migrations/202610260001_ag_run_revision.js.map +1 -0
  216. package/dist/package.json +8 -10
  217. package/dist/server/cli/run-credential.js +1 -1
  218. package/dist/server/composition.d.ts +41 -37
  219. package/dist/server/composition.d.ts.map +1 -1
  220. package/dist/server/composition.js +66 -66
  221. package/dist/server/composition.js.map +1 -1
  222. package/dist/server/core/agents/agent.service.d.ts +51 -43
  223. package/dist/server/core/agents/agent.service.d.ts.map +1 -1
  224. package/dist/server/core/agents/agent.service.js +342 -317
  225. package/dist/server/core/agents/agent.service.js.map +1 -1
  226. package/dist/server/core/agents/agent.store.d.ts +24 -12
  227. package/dist/server/core/agents/agent.store.d.ts.map +1 -1
  228. package/dist/server/core/agents/agent.store.js +64 -66
  229. package/dist/server/core/agents/agent.store.js.map +1 -1
  230. package/dist/server/core/agents/agent.views.d.ts +29 -0
  231. package/dist/server/core/agents/agent.views.d.ts.map +1 -0
  232. package/dist/server/core/agents/agent.views.js +86 -0
  233. package/dist/server/core/agents/agent.views.js.map +1 -0
  234. package/dist/server/core/agents/discovery.d.ts +12 -0
  235. package/dist/server/core/agents/discovery.d.ts.map +1 -0
  236. package/dist/server/core/agents/discovery.js +39 -0
  237. package/dist/server/core/agents/discovery.js.map +1 -0
  238. package/dist/server/core/agents/index.d.ts +2 -1
  239. package/dist/server/core/agents/index.d.ts.map +1 -1
  240. package/dist/server/core/agents/index.js +2 -1
  241. package/dist/server/core/agents/index.js.map +1 -1
  242. package/dist/server/core/brief/brief.js +1 -1
  243. package/dist/server/core/brief/brief.js.map +1 -1
  244. package/dist/server/core/callers/index.d.ts +1 -1
  245. package/dist/server/core/conversations/availability.d.ts +11 -10
  246. package/dist/server/core/conversations/availability.d.ts.map +1 -1
  247. package/dist/server/core/conversations/availability.js +14 -32
  248. package/dist/server/core/conversations/availability.js.map +1 -1
  249. package/dist/server/core/conversations/chat-settings.d.ts.map +1 -1
  250. package/dist/server/core/conversations/chat-settings.js +5 -3
  251. package/dist/server/core/conversations/chat-settings.js.map +1 -1
  252. package/dist/server/core/conversations/conversation.service.d.ts +3 -5
  253. package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
  254. package/dist/server/core/conversations/conversation.service.js +161 -221
  255. package/dist/server/core/conversations/conversation.service.js.map +1 -1
  256. package/dist/server/core/conversations/ports.d.ts +3 -17
  257. package/dist/server/core/conversations/ports.d.ts.map +1 -1
  258. package/dist/server/core/reports/report.service.d.ts.map +1 -1
  259. package/dist/server/core/reports/report.service.js +5 -2
  260. package/dist/server/core/reports/report.service.js.map +1 -1
  261. package/dist/server/core/runs/availability.d.ts +18 -7
  262. package/dist/server/core/runs/availability.d.ts.map +1 -1
  263. package/dist/server/core/runs/availability.js +35 -24
  264. package/dist/server/core/runs/availability.js.map +1 -1
  265. package/dist/server/core/runs/claim.d.ts +47 -67
  266. package/dist/server/core/runs/claim.d.ts.map +1 -1
  267. package/dist/server/core/runs/claim.js +805 -313
  268. package/dist/server/core/runs/claim.js.map +1 -1
  269. package/dist/server/core/runs/comment-consent.d.ts +24 -0
  270. package/dist/server/core/runs/comment-consent.d.ts.map +1 -0
  271. package/dist/server/core/runs/comment-consent.js +116 -0
  272. package/dist/server/core/runs/comment-consent.js.map +1 -0
  273. package/dist/server/core/runs/eligibility.d.ts +51 -0
  274. package/dist/server/core/runs/eligibility.d.ts.map +1 -0
  275. package/dist/server/core/runs/eligibility.js +44 -0
  276. package/dist/server/core/runs/eligibility.js.map +1 -0
  277. package/dist/server/core/runs/execution-view.d.ts +10 -0
  278. package/dist/server/core/runs/execution-view.d.ts.map +1 -0
  279. package/dist/server/core/runs/execution-view.js +25 -0
  280. package/dist/server/core/runs/execution-view.js.map +1 -0
  281. package/dist/server/core/runs/execution.d.ts +13 -0
  282. package/dist/server/core/runs/execution.d.ts.map +1 -0
  283. package/dist/server/core/runs/execution.js +85 -0
  284. package/dist/server/core/runs/execution.js.map +1 -0
  285. package/dist/server/core/runs/extensions.d.ts +59 -4
  286. package/dist/server/core/runs/extensions.d.ts.map +1 -1
  287. package/dist/server/core/runs/extensions.js +45 -9
  288. package/dist/server/core/runs/extensions.js.map +1 -1
  289. package/dist/server/core/runs/git-credentials.d.ts +43 -0
  290. package/dist/server/core/runs/git-credentials.d.ts.map +1 -0
  291. package/dist/server/core/runs/git-credentials.js +129 -0
  292. package/dist/server/core/runs/git-credentials.js.map +1 -0
  293. package/dist/server/core/runs/identity.d.ts +9 -0
  294. package/dist/server/core/runs/identity.d.ts.map +1 -0
  295. package/dist/server/core/runs/identity.js +75 -0
  296. package/dist/server/core/runs/identity.js.map +1 -0
  297. package/dist/server/core/runs/index.d.ts +9 -5
  298. package/dist/server/core/runs/index.d.ts.map +1 -1
  299. package/dist/server/core/runs/index.js +9 -5
  300. package/dist/server/core/runs/index.js.map +1 -1
  301. package/dist/server/core/runs/input-gate.d.ts +34 -0
  302. package/dist/server/core/runs/input-gate.d.ts.map +1 -0
  303. package/dist/server/core/runs/input-gate.js +58 -0
  304. package/dist/server/core/runs/input-gate.js.map +1 -0
  305. package/dist/server/core/runs/lifecycle.d.ts +9 -19
  306. package/dist/server/core/runs/lifecycle.d.ts.map +1 -1
  307. package/dist/server/core/runs/lifecycle.js +98 -50
  308. package/dist/server/core/runs/lifecycle.js.map +1 -1
  309. package/dist/server/core/runs/policy.d.ts +8 -0
  310. package/dist/server/core/runs/policy.d.ts.map +1 -1
  311. package/dist/server/core/runs/policy.js +10 -0
  312. package/dist/server/core/runs/policy.js.map +1 -1
  313. package/dist/server/core/runs/ports.d.ts +91 -28
  314. package/dist/server/core/runs/ports.d.ts.map +1 -1
  315. package/dist/server/core/runs/ports.js +2 -13
  316. package/dist/server/core/runs/ports.js.map +1 -1
  317. package/dist/server/core/runs/preview.d.ts.map +1 -1
  318. package/dist/server/core/runs/preview.js +3 -3
  319. package/dist/server/core/runs/preview.js.map +1 -1
  320. package/dist/server/core/runs/run-data-purge.d.ts +15 -0
  321. package/dist/server/core/runs/run-data-purge.d.ts.map +1 -0
  322. package/dist/server/core/runs/run-data-purge.js +82 -0
  323. package/dist/server/core/runs/run-data-purge.js.map +1 -0
  324. package/dist/server/core/runs/run-requests.d.ts +245 -0
  325. package/dist/server/core/runs/run-requests.d.ts.map +1 -0
  326. package/dist/server/core/runs/run-requests.js +543 -0
  327. package/dist/server/core/runs/run-requests.js.map +1 -0
  328. package/dist/server/core/runs/run.service.d.ts +96 -11
  329. package/dist/server/core/runs/run.service.d.ts.map +1 -1
  330. package/dist/server/core/runs/run.service.js +921 -96
  331. package/dist/server/core/runs/run.service.js.map +1 -1
  332. package/dist/server/core/runs/run.store.d.ts +25 -3
  333. package/dist/server/core/runs/run.store.d.ts.map +1 -1
  334. package/dist/server/core/runs/run.store.js +52 -2
  335. package/dist/server/core/runs/run.store.js.map +1 -1
  336. package/dist/server/core/runs/runner-view.d.ts +6 -4
  337. package/dist/server/core/runs/runner-view.d.ts.map +1 -1
  338. package/dist/server/core/runs/runner-view.js +13 -11
  339. package/dist/server/core/runs/runner-view.js.map +1 -1
  340. package/dist/server/core/runs/selection.d.ts +13 -0
  341. package/dist/server/core/runs/selection.d.ts.map +1 -0
  342. package/dist/server/core/runs/selection.js +21 -0
  343. package/dist/server/core/runs/selection.js.map +1 -0
  344. package/dist/server/core/runs/sweeper.d.ts +1 -0
  345. package/dist/server/core/runs/sweeper.d.ts.map +1 -1
  346. package/dist/server/core/runs/sweeper.js +55 -10
  347. package/dist/server/core/runs/sweeper.js.map +1 -1
  348. package/dist/server/core/runs/transitions.d.ts +2 -2
  349. package/dist/server/core/runs/transitions.d.ts.map +1 -1
  350. package/dist/server/core/runs/transitions.js +80 -8
  351. package/dist/server/core/runs/transitions.js.map +1 -1
  352. package/dist/server/core/runs/workload.d.ts +3 -3
  353. package/dist/server/core/runs/workload.d.ts.map +1 -1
  354. package/dist/server/core/runs/workload.js +154 -45
  355. package/dist/server/core/runs/workload.js.map +1 -1
  356. package/dist/server/core/skills/skill.service.d.ts +6 -2
  357. package/dist/server/core/skills/skill.service.d.ts.map +1 -1
  358. package/dist/server/core/skills/skill.service.js +43 -58
  359. package/dist/server/core/skills/skill.service.js.map +1 -1
  360. package/dist/server/core/skills/skill.store.d.ts +14 -0
  361. package/dist/server/core/skills/skill.store.d.ts.map +1 -1
  362. package/dist/server/core/skills/skill.store.js +30 -0
  363. package/dist/server/core/skills/skill.store.js.map +1 -1
  364. package/dist/server/core/variables/index.d.ts +1 -1
  365. package/dist/server/core/variables/index.d.ts.map +1 -1
  366. package/dist/server/core/variables/index.js +1 -1
  367. package/dist/server/core/variables/index.js.map +1 -1
  368. package/dist/server/core/variables/variable.service.d.ts +38 -6
  369. package/dist/server/core/variables/variable.service.d.ts.map +1 -1
  370. package/dist/server/core/variables/variable.service.js +201 -30
  371. package/dist/server/core/variables/variable.service.js.map +1 -1
  372. package/dist/server/distribution/dist.service.d.ts +27 -5
  373. package/dist/server/distribution/dist.service.d.ts.map +1 -1
  374. package/dist/server/distribution/dist.service.js +41 -5
  375. package/dist/server/distribution/dist.service.js.map +1 -1
  376. package/dist/server/distribution/download-tokens.d.ts +1 -1
  377. package/dist/server/distribution/download-tokens.d.ts.map +1 -1
  378. package/dist/server/distribution/download-tokens.js +7 -4
  379. package/dist/server/distribution/download-tokens.js.map +1 -1
  380. package/dist/server/distribution/index.d.ts +11 -3
  381. package/dist/server/distribution/index.d.ts.map +1 -1
  382. package/dist/server/distribution/index.js +31 -8
  383. package/dist/server/distribution/index.js.map +1 -1
  384. package/dist/server/jobs/index.d.ts +1 -1
  385. package/dist/server/jobs/index.d.ts.map +1 -1
  386. package/dist/server/jobs/index.js +1 -1
  387. package/dist/server/jobs/index.js.map +1 -1
  388. package/dist/server/jobs/job.service.d.ts +3 -1
  389. package/dist/server/jobs/job.service.d.ts.map +1 -1
  390. package/dist/server/jobs/job.service.js +11 -3
  391. package/dist/server/jobs/job.service.js.map +1 -1
  392. package/dist/server/jobs/spec.d.ts +4 -0
  393. package/dist/server/jobs/spec.d.ts.map +1 -1
  394. package/dist/server/jobs/spec.js +6 -0
  395. package/dist/server/jobs/spec.js.map +1 -1
  396. package/dist/server/kernel/errors.d.ts +1 -1
  397. package/dist/server/kernel/errors.d.ts.map +1 -1
  398. package/dist/server/kernel/errors.js +1 -0
  399. package/dist/server/kernel/errors.js.map +1 -1
  400. package/dist/server/kernel/events.d.ts +34 -9
  401. package/dist/server/kernel/events.d.ts.map +1 -1
  402. package/dist/server/kernel/events.js.map +1 -1
  403. package/dist/server/kernel/redaction.d.ts +2 -0
  404. package/dist/server/kernel/redaction.d.ts.map +1 -1
  405. package/dist/server/kernel/redaction.js +17 -0
  406. package/dist/server/kernel/redaction.js.map +1 -1
  407. package/dist/server/kernel/scopes.d.ts.map +1 -1
  408. package/dist/server/kernel/scopes.js +3 -1
  409. package/dist/server/kernel/scopes.js.map +1 -1
  410. package/dist/server/online/index.d.ts +0 -5
  411. package/dist/server/online/index.d.ts.map +1 -1
  412. package/dist/server/online/index.js +0 -5
  413. package/dist/server/online/index.js.map +1 -1
  414. package/dist/server/online/routes.d.ts.map +1 -1
  415. package/dist/server/online/routes.js +6 -35
  416. package/dist/server/online/routes.js.map +1 -1
  417. package/dist/server/providers/agents.d.ts +8 -25
  418. package/dist/server/providers/agents.d.ts.map +1 -1
  419. package/dist/server/providers/agents.js +11 -75
  420. package/dist/server/providers/agents.js.map +1 -1
  421. package/dist/server/providers/runners.d.ts.map +1 -1
  422. package/dist/server/providers/runners.js +23 -0
  423. package/dist/server/providers/runners.js.map +1 -1
  424. package/dist/server/routes/admin.d.ts.map +1 -1
  425. package/dist/server/routes/admin.js +158 -209
  426. package/dist/server/routes/admin.js.map +1 -1
  427. package/dist/server/routes/chat.d.ts.map +1 -1
  428. package/dist/server/routes/chat.js +12 -70
  429. package/dist/server/routes/chat.js.map +1 -1
  430. package/dist/server/routes/index.d.ts.map +1 -1
  431. package/dist/server/routes/index.js +11 -1
  432. package/dist/server/routes/index.js.map +1 -1
  433. package/dist/server/routes/roster.d.ts.map +1 -1
  434. package/dist/server/routes/roster.js +6 -21
  435. package/dist/server/routes/roster.js.map +1 -1
  436. package/dist/server/routes/run-requests.d.ts +6 -0
  437. package/dist/server/routes/run-requests.d.ts.map +1 -0
  438. package/dist/server/routes/run-requests.js +216 -0
  439. package/dist/server/routes/run-requests.js.map +1 -0
  440. package/dist/server/routes/runners/admin.d.ts +9 -1
  441. package/dist/server/routes/runners/admin.d.ts.map +1 -1
  442. package/dist/server/routes/runners/admin.js +62 -24
  443. package/dist/server/routes/runners/admin.js.map +1 -1
  444. package/dist/server/routes/runners/dist.d.ts +5 -1
  445. package/dist/server/routes/runners/dist.d.ts.map +1 -1
  446. package/dist/server/routes/runners/dist.js +36 -9
  447. package/dist/server/routes/runners/dist.js.map +1 -1
  448. package/dist/server/routes/runners/install.d.ts.map +1 -1
  449. package/dist/server/routes/runners/install.js +98 -15
  450. package/dist/server/routes/runners/install.js.map +1 -1
  451. package/dist/server/routes/runners/runner.d.ts.map +1 -1
  452. package/dist/server/routes/runners/runner.js +27 -7
  453. package/dist/server/routes/runners/runner.js.map +1 -1
  454. package/dist/server/routes/runners/runs.d.ts +1 -1
  455. package/dist/server/routes/runners/runs.d.ts.map +1 -1
  456. package/dist/server/routes/runners/runs.js +23 -3
  457. package/dist/server/routes/runners/runs.js.map +1 -1
  458. package/dist/server/routes/schemas.d.ts +37 -8
  459. package/dist/server/routes/schemas.d.ts.map +1 -1
  460. package/dist/server/routes/schemas.js +381 -84
  461. package/dist/server/routes/schemas.js.map +1 -1
  462. package/dist/server/runners/index.d.ts +1 -0
  463. package/dist/server/runners/index.d.ts.map +1 -1
  464. package/dist/server/runners/index.js +1 -0
  465. package/dist/server/runners/index.js.map +1 -1
  466. package/dist/server/runners/runner.service.d.ts +32 -8
  467. package/dist/server/runners/runner.service.d.ts.map +1 -1
  468. package/dist/server/runners/runner.service.js +130 -14
  469. package/dist/server/runners/runner.service.js.map +1 -1
  470. package/dist/server/runners/runner.store.d.ts +10 -1
  471. package/dist/server/runners/runner.store.d.ts.map +1 -1
  472. package/dist/server/runners/runner.store.js +117 -4
  473. package/dist/server/runners/runner.store.js.map +1 -1
  474. package/dist/server/runners/sweeper.d.ts +5 -0
  475. package/dist/server/runners/sweeper.d.ts.map +1 -1
  476. package/dist/server/runners/sweeper.js +4 -2
  477. package/dist/server/runners/sweeper.js.map +1 -1
  478. package/dist/server/runners/workspaces.d.ts +33 -0
  479. package/dist/server/runners/workspaces.d.ts.map +1 -0
  480. package/dist/server/runners/workspaces.js +248 -0
  481. package/dist/server/runners/workspaces.js.map +1 -0
  482. package/dist/server/testing.d.ts +0 -2
  483. package/dist/server/testing.d.ts.map +1 -1
  484. package/dist/server/testing.js +1 -4
  485. package/dist/server/testing.js.map +1 -1
  486. package/dist/server/tokens.d.ts +13 -9
  487. package/dist/server/tokens.d.ts.map +1 -1
  488. package/dist/server/tokens.js +7 -2
  489. package/dist/server/tokens.js.map +1 -1
  490. package/dist/shared/access.d.ts +2 -6
  491. package/dist/shared/access.d.ts.map +1 -1
  492. package/dist/shared/access.js +2 -6
  493. package/dist/shared/access.js.map +1 -1
  494. package/dist/shared/agents.d.ts +128 -167
  495. package/dist/shared/agents.d.ts.map +1 -1
  496. package/dist/shared/agents.js +46 -97
  497. package/dist/shared/agents.js.map +1 -1
  498. package/dist/shared/conversations.d.ts +43 -89
  499. package/dist/shared/conversations.d.ts.map +1 -1
  500. package/dist/shared/conversations.js +5 -24
  501. package/dist/shared/conversations.js.map +1 -1
  502. package/dist/shared/model-catalog.d.ts +48 -0
  503. package/dist/shared/model-catalog.d.ts.map +1 -0
  504. package/dist/shared/model-catalog.js +148 -0
  505. package/dist/shared/model-catalog.js.map +1 -0
  506. package/dist/shared/models.js +1 -1
  507. package/dist/shared/models.js.map +1 -1
  508. package/dist/shared/runners.d.ts +107 -17
  509. package/dist/shared/runners.d.ts.map +1 -1
  510. package/dist/shared/runners.js +49 -7
  511. package/dist/shared/runners.js.map +1 -1
  512. package/dist/shared/runs.d.ts +213 -1
  513. package/dist/shared/runs.d.ts.map +1 -1
  514. package/dist/shared/runs.js +24 -0
  515. package/dist/shared/runs.js.map +1 -1
  516. package/dist/shared/skills.d.ts +8 -4
  517. package/dist/shared/skills.d.ts.map +1 -1
  518. package/dist/shared/skills.js +7 -2
  519. package/dist/shared/skills.js.map +1 -1
  520. package/dist/shared/variables.d.ts +22 -0
  521. package/dist/shared/variables.d.ts.map +1 -1
  522. package/dist/shared/variables.js +10 -2
  523. package/dist/shared/variables.js.map +1 -1
  524. package/package.json +24 -26
  525. package/skills/nocobase-app-plugin-agents/SKILL.md +8 -7
  526. package/skills/nocobase-app-plugin-agents/references/agents.md +30 -0
  527. package/skills/nocobase-app-plugin-agents/references/distribution.md +21 -4
  528. package/skills/nocobase-app-plugin-agents/references/extension-points.md +30 -20
  529. package/skills/nocobase-app-plugin-agents/references/model-services.md +17 -0
  530. package/skills/nocobase-app-plugin-agents/references/runners.md +15 -1
  531. package/skills/nocobase-app-plugin-agents/references/setup-and-config.md +4 -7
  532. package/skills/nocobase-app-plugin-agents/references/variables-skills-usage.md +6 -4
  533. package/skills/nocobase-app-plugin-agents/references/verification-and-troubleshooting.md +33 -32
  534. package/dist/client/chat/settings-section.d.ts +0 -5
  535. package/dist/client/chat/settings-section.d.ts.map +0 -1
  536. package/dist/client/chat/settings-section.js +0 -76
  537. package/dist/client/chat/settings-section.js.map +0 -1
  538. package/dist/client/components/agent-fields.d.ts +0 -45
  539. package/dist/client/components/agent-fields.d.ts.map +0 -1
  540. package/dist/client/components/agent-fields.js +0 -83
  541. package/dist/client/components/agent-fields.js.map +0 -1
  542. package/dist/client/components/agent-type.d.ts +0 -32
  543. package/dist/client/components/agent-type.d.ts.map +0 -1
  544. package/dist/client/components/agent-type.js +0 -80
  545. package/dist/client/components/agent-type.js.map +0 -1
  546. package/dist/client/components/model-entries.d.ts +0 -35
  547. package/dist/client/components/model-entries.d.ts.map +0 -1
  548. package/dist/client/components/model-entries.js +0 -82
  549. package/dist/client/components/model-entries.js.map +0 -1
  550. package/dist/client/hooks/use-agent-save.d.ts +0 -9
  551. package/dist/client/hooks/use-agent-save.d.ts.map +0 -1
  552. package/dist/client/hooks/use-agent-save.js +0 -40
  553. package/dist/client/hooks/use-agent-save.js.map +0 -1
  554. package/dist/client/hooks/use-model-catalog.d.ts +0 -9
  555. package/dist/client/hooks/use-model-catalog.d.ts.map +0 -1
  556. package/dist/client/hooks/use-model-catalog.js +0 -27
  557. package/dist/client/hooks/use-model-catalog.js.map +0 -1
  558. package/dist/client/lib/tool-policy.d.ts +0 -5
  559. package/dist/client/lib/tool-policy.d.ts.map +0 -1
  560. package/dist/client/lib/tool-policy.js +0 -10
  561. package/dist/client/lib/tool-policy.js.map +0 -1
  562. package/dist/client/online/default-model.d.ts +0 -5
  563. package/dist/client/online/default-model.d.ts.map +0 -1
  564. package/dist/client/online/default-model.js +0 -65
  565. package/dist/client/online/default-model.js.map +0 -1
  566. package/dist/client/pages/agents/agent-model.d.ts +0 -73
  567. package/dist/client/pages/agents/agent-model.d.ts.map +0 -1
  568. package/dist/client/pages/agents/agent-model.js +0 -172
  569. package/dist/client/pages/agents/agent-model.js.map +0 -1
  570. package/dist/client/pages/agents/detail/brief-preview.d.ts +0 -7
  571. package/dist/client/pages/agents/detail/brief-preview.d.ts.map +0 -1
  572. package/dist/client/pages/agents/detail/brief-preview.js +0 -43
  573. package/dist/client/pages/agents/detail/brief-preview.js.map +0 -1
  574. package/dist/client/pages/agents/detail/history.d.ts +0 -11
  575. package/dist/client/pages/agents/detail/history.d.ts.map +0 -1
  576. package/dist/client/pages/agents/detail/history.js +0 -149
  577. package/dist/client/pages/agents/detail/history.js.map +0 -1
  578. package/dist/client/pages/agents/detail/index.d.ts +0 -3
  579. package/dist/client/pages/agents/detail/index.d.ts.map +0 -1
  580. package/dist/client/pages/agents/detail/index.js +0 -241
  581. package/dist/client/pages/agents/detail/index.js.map +0 -1
  582. package/dist/client/pages/agents/detail/sections.d.ts +0 -22
  583. package/dist/client/pages/agents/detail/sections.d.ts.map +0 -1
  584. package/dist/client/pages/agents/detail/sections.js +0 -311
  585. package/dist/client/pages/agents/detail/sections.js.map +0 -1
  586. package/dist/client/pages/agents/detail/tab-draft.d.ts +0 -43
  587. package/dist/client/pages/agents/detail/tab-draft.d.ts.map +0 -1
  588. package/dist/client/pages/agents/detail/tab-draft.js +0 -115
  589. package/dist/client/pages/agents/detail/tab-draft.js.map +0 -1
  590. package/dist/client/pages/agents/detail/unsaved-bar.d.ts +0 -13
  591. package/dist/client/pages/agents/detail/unsaved-bar.d.ts.map +0 -1
  592. package/dist/client/pages/agents/detail/unsaved-bar.js +0 -21
  593. package/dist/client/pages/agents/detail/unsaved-bar.js.map +0 -1
  594. package/dist/client/pages/agents/new.d.ts +0 -3
  595. package/dist/client/pages/agents/new.d.ts.map +0 -1
  596. package/dist/client/pages/agents/new.js +0 -149
  597. package/dist/client/pages/agents/new.js.map +0 -1
  598. package/dist/client/pages/agents/preset-picker.d.ts +0 -8
  599. package/dist/client/pages/agents/preset-picker.d.ts.map +0 -1
  600. package/dist/client/pages/agents/preset-picker.js +0 -41
  601. package/dist/client/pages/agents/preset-picker.js.map +0 -1
  602. package/dist/server/core/consultations/consultation.service.d.ts +0 -59
  603. package/dist/server/core/consultations/consultation.service.d.ts.map +0 -1
  604. package/dist/server/core/consultations/consultation.service.js +0 -195
  605. package/dist/server/core/consultations/consultation.service.js.map +0 -1
  606. package/dist/server/core/consultations/index.d.ts +0 -3
  607. package/dist/server/core/consultations/index.d.ts.map +0 -1
  608. package/dist/server/core/consultations/index.js +0 -3
  609. package/dist/server/core/consultations/index.js.map +0 -1
  610. package/dist/server/core/consultations/prompt.d.ts +0 -41
  611. package/dist/server/core/consultations/prompt.d.ts.map +0 -1
  612. package/dist/server/core/consultations/prompt.js +0 -64
  613. package/dist/server/core/consultations/prompt.js.map +0 -1
  614. package/dist/server/core/presets/index.d.ts +0 -19
  615. package/dist/server/core/presets/index.d.ts.map +0 -1
  616. package/dist/server/core/presets/index.js +0 -28
  617. package/dist/server/core/presets/index.js.map +0 -1
  618. package/dist/server/online/cli-command.d.ts +0 -20
  619. package/dist/server/online/cli-command.d.ts.map +0 -1
  620. package/dist/server/online/cli-command.js +0 -345
  621. package/dist/server/online/cli-command.js.map +0 -1
  622. package/dist/server/online/consult-tool.d.ts +0 -54
  623. package/dist/server/online/consult-tool.d.ts.map +0 -1
  624. package/dist/server/online/consult-tool.js +0 -177
  625. package/dist/server/online/consult-tool.js.map +0 -1
  626. package/dist/server/online/executor.d.ts +0 -83
  627. package/dist/server/online/executor.d.ts.map +0 -1
  628. package/dist/server/online/executor.js +0 -714
  629. package/dist/server/online/executor.js.map +0 -1
  630. package/dist/server/online/sandbox.d.ts +0 -32
  631. package/dist/server/online/sandbox.d.ts.map +0 -1
  632. package/dist/server/online/sandbox.js +0 -246
  633. package/dist/server/online/sandbox.js.map +0 -1
  634. package/dist/server/online/scripted.d.ts +0 -12
  635. package/dist/server/online/scripted.d.ts.map +0 -1
  636. package/dist/server/online/scripted.js +0 -176
  637. package/dist/server/online/scripted.js.map +0 -1
  638. package/dist/server/online/tools.d.ts +0 -17
  639. package/dist/server/online/tools.d.ts.map +0 -1
  640. package/dist/server/online/tools.js +0 -31
  641. package/dist/server/online/tools.js.map +0 -1
  642. package/dist/server/vendor/THIRD_PARTY_LICENSES.txt +0 -782
  643. package/dist/server/vendor/just-bash.d.ts +0 -83
  644. package/dist/server/vendor/just-bash.js +0 -2102
  645. package/dist/shared/presets.d.ts +0 -30
  646. package/dist/shared/presets.d.ts.map +0 -1
  647. package/dist/shared/presets.js +0 -2
  648. package/dist/shared/presets.js.map +0 -1
  649. package/skills/nocobase-app-plugin-agents/references/online-agents.md +0 -31
@@ -3,20 +3,39 @@
3
3
  * cancelling and retrying, and reading runs and their transcripts. Who may read or change a run is decided by the
4
4
  * callers (the admin routes, the application); this service checks only that the person who wakes an agent may wake it.
5
5
  *
6
- * Work is keyed by (agent, subject, thread). New work for a key joins the run already working on it while it can
7
- * still be told (dispatched, or running with input), else the run waiting for it (queued), else starts a new run.
6
+ * Work is keyed by (agent, subject, thread). New work for a key joins the run already working on it as the same person
7
+ * while it can still be told (dispatched, or running with input), else that person's run waiting for it (queued), else
8
+ * starts a new run. Runs of one key working as different people are claimed one at a time (`claim.ts`).
8
9
  */
9
10
  import { ProtocolError, TERMINAL_RUN_STATUSES, } from '@nocobase/agent-protocol';
10
11
  import { conflict, forbidden, invalid, notFound, precondition, } from '../../kernel/errors.js';
11
- import { asJson, cleanList, jsonObject } from '../../kernel/values.js';
12
+ import { asJson, cleanList, jsonObject, stringArray, } from '../../kernel/values.js';
12
13
  import { findAgent, lockAgentForClaim } from '../agents/index.js';
14
+ import { agentNamesOf } from '../agents/agent.store.js';
15
+ import { createRunRequestService, insertRunRequest, } from './run-requests.js';
13
16
  import { authenticateRunToken } from './run-tokens.js';
14
- import { ACTIVE, eventsRepo, findRunRecord, inputsOnSince, inputsRepo, isActive, isTerminal, lastSummary, openRunsOn, pendingInputs, reposRepo, runsOfKey, runsRepo, toInput, toInputView, toRepo, toRun, usageRepo, } from './run.store.js';
15
- import { dialectOf, takesType } from './ports.js';
17
+ import { ACTIVE, eventsRepo, findRunRecord, inputsOnSince, inputsRepo, isActive, isTerminal, lastSummary, openRunsOn, openRunsOnEach, pendingInputs, reposRepo, runsOfKey, runsRepo, toInput, toInputView, toRepo, toRun, usageRepo, } from './run.store.js';
16
18
  import { readWorkload } from './workload.js';
17
19
  import { finishRun } from './transitions.js';
20
+ import { gateInputs, gatePending } from './input-gate.js';
18
21
  import { findBrief, requestReset } from './workspace.store.js';
22
+ import { findCommentRoot, currentCommentOwner, commentConsent, commentFromOwner, } from './comment-consent.js';
19
23
  export const DEFAULT_THREAD = 'main';
24
+ /**
25
+ * The run work went into, for a caller that names no responsible (a conversation, a consultation): such work is never
26
+ * pending, so a pending result is a programming error.
27
+ */
28
+ export function queuedRun(result) {
29
+ if (result.outcome === 'pending')
30
+ throw new Error('Work that names no responsible was turned into a run request.');
31
+ return result;
32
+ }
33
+ /** Who a chain of work a run belongs to started with: who confirmed it, else who it was requested by. */
34
+ function chainSourceOf(run) {
35
+ return run.commentRootRunId
36
+ ? (run.requestedByUserId ?? run.actorUserId)
37
+ : (run.confirmedByUserId ?? run.requestedByUserId ?? run.actorUserId);
38
+ }
20
39
  /** A delay as the moment a run may be claimed, or null for now (and for any moment already past). */
21
40
  function fireAtOf(value, now) {
22
41
  if (value === undefined)
@@ -26,8 +45,20 @@ function fireAtOf(value, now) {
26
45
  throw invalid('fireAt is not a date.');
27
46
  return at.getTime() > now.getTime() ? at.toISOString() : null;
28
47
  }
48
+ const COMMENT_CANCEL_DETAIL = 'The personal runner request was declined or switched to the commenter.';
29
49
  export function createRunService(deps) {
30
50
  const { tx, ids, clock } = deps;
51
+ /** Ends one unfinished run of a declined chain; a run that moved since it was read rolls the decision back. */
52
+ async function endCommentRun(unit, run, byUserId) {
53
+ const ended = await finishRun(unit, deps, run, {
54
+ status: 'cancelled',
55
+ reason: null,
56
+ detail: COMMENT_CANCEL_DETAIL,
57
+ cancelledById: byUserId,
58
+ });
59
+ if (!ended)
60
+ throw conflict('The comment work changed; read it again.');
61
+ }
31
62
  const require = async (conn, id) => {
32
63
  const run = await findRunRecord(conn, id);
33
64
  if (!run)
@@ -52,43 +83,55 @@ export function createRunService(deps) {
52
83
  },
53
84
  });
54
85
  unit.emit({ type: 'run.input', runId, inputId: id });
86
+ await runsRepo(unit.conn).updateMany({
87
+ filter: { id: runId, status: 'queued' },
88
+ // New input may change the scopes its variables come from: the next claim tells again.
89
+ values: { teamOnlyVariables: null },
90
+ });
55
91
  return id;
56
92
  }
57
93
  async function requireInvocable(unit, agentId, userId) {
58
94
  const agent = await findAgent(unit.conn, agentId);
59
95
  if (!agent)
60
96
  throw notFound('Agent');
61
- if (agent.archivedAt)
62
- throw precondition('AGENT_ARCHIVED', 'The agent is archived.');
97
+ if (agent.status !== 'enabled')
98
+ throw precondition('AGENT_NOT_ENABLED', 'The agent is not enabled.');
63
99
  if (!deps.agents.mayInvoke(agent, userId))
64
100
  throw forbidden('You may not wake this agent.');
65
101
  return agent;
66
102
  }
67
103
  async function createRun(unit, agent, values) {
68
- if (!takesType(deps.subjects.get(values.subject.kind), agent.type))
69
- throw invalid(`A ${agent.type} agent cannot work on a ${values.subject.kind}.`, { reason: 'AGENT_TYPE_NOT_ALLOWED', agentType: agent.type });
70
104
  const now = clock.now().toISOString();
71
105
  const id = ids.next();
72
106
  await runsRepo(unit.conn).createOne({
73
107
  values: {
74
108
  id,
75
109
  agentId: agent.id,
76
- agentType: agent.type,
110
+ agentType: 'runner',
77
111
  runnerId: null,
78
112
  status: 'queued',
79
113
  priority: values.priority,
80
114
  attempt: 1,
81
115
  maxAttempts: values.maxAttempts ?? agent.maxAttempts,
82
116
  retryOfRunId: values.retryOfRunId,
117
+ freshSession: values.freshSession === true,
83
118
  parentRunId: values.parentRunId ?? null,
84
119
  subjectKind: values.subject.kind,
85
120
  subjectId: values.subject.id,
86
121
  threadScope: values.threadScope,
122
+ commentRootRunId: values.commentRootRunId === ''
123
+ ? id
124
+ : (values.commentRootRunId ?? null),
125
+ commentExecution: values.commentExecution ?? null,
126
+ commentOwnerKey: values.commentOwnerKey ?? null,
87
127
  actorUserId: values.actorUserId,
128
+ requestedByUserId: values.requestedByUserId,
129
+ confirmedByUserId: values.confirmedByUserId,
88
130
  ownerUserId: values.ownerUserId,
89
131
  requires: cleanList([...values.requires]),
90
132
  acceptsInput: false,
91
133
  availableAt: values.availableAt ?? null,
134
+ queueEnteredAt: now,
92
135
  leaseExpiresAt: null,
93
136
  dispatchedAt: null,
94
137
  startedAt: null,
@@ -103,6 +146,7 @@ export function createRunService(deps) {
103
146
  workDir: null,
104
147
  directoryKey: null,
105
148
  claimFailures: 0,
149
+ teamOnlyVariables: null,
106
150
  payloadFingerprint: null,
107
151
  createdAt: now,
108
152
  updatedAt: now,
@@ -152,53 +196,630 @@ export function createRunService(deps) {
152
196
  }
153
197
  return [...totals.values()];
154
198
  }
199
+ /** Legacy runs have no snapshots; report their known primary-tool models without inventing attempt boundaries. */
200
+ async function readRuns(records) {
201
+ const runs = records.map(toRun);
202
+ const legacy = runs.filter((run) => run.executions?.length === 0 &&
203
+ (run.dispatchedAt !== null ||
204
+ run.tool !== null ||
205
+ run.modelService !== null));
206
+ if (legacy.length === 0)
207
+ return runs;
208
+ const usage = await usageRepo(tx.read()).findMany({
209
+ select: (select) => select.fields('runId', 'tool', 'model'),
210
+ distinct: ['runId', 'tool', 'model'],
211
+ filter: (f) => f.and([
212
+ f.string('model').ne(null),
213
+ f.or(legacy.map((run) => f.and([
214
+ f.string('runId').eq(run.id),
215
+ f.string('tool').eq(run.tool ?? 'online'),
216
+ ]))),
217
+ ]),
218
+ });
219
+ const models = new Map();
220
+ for (const row of usage)
221
+ if (row.model)
222
+ models.set(row.runId, [...(models.get(row.runId) ?? []), row.model]);
223
+ return runs.map((run) => run.executions?.length
224
+ ? run
225
+ : {
226
+ ...run,
227
+ actualModels: models.get(run.id) ?? [],
228
+ });
229
+ }
230
+ /** Cross-root appends require both sources to retain the same owner's authorization. */
231
+ async function equivalentCommentAuthority(unit, run, work) {
232
+ if (run.ownerUserId !== work.ownerUserId ||
233
+ (run.commentOwnerKey ?? null) !== (work.commentOwnerKey ?? null) ||
234
+ (run.commentExecution ?? 'owner') !== (work.commentExecution ?? 'owner'))
235
+ return false;
236
+ if ((run.commentRootRunId ?? null) === (work.commentRootRunId ?? null)) {
237
+ if (!run.commentRootRunId)
238
+ return true;
239
+ const root = await findCommentRoot(unit.conn, run.commentRootRunId);
240
+ // Requester execution keeps its own identity; it does not borrow the owner's consent.
241
+ if (root.commentExecution === 'requester')
242
+ return (root.actorUserId === work.actorUserId &&
243
+ (await currentCommentOwner(unit.conn, deps.subjects, root)));
244
+ return await authorizedRoot(root);
245
+ }
246
+ if (work.commentExecution === 'requester' ||
247
+ run.commentExecution === 'requester' ||
248
+ !work.sourceRunId)
249
+ return false;
250
+ const source = await findRunRecord(unit.conn, work.sourceRunId);
251
+ if (!source ||
252
+ source.subjectKind !== run.subjectKind ||
253
+ source.subjectId !== run.subjectId ||
254
+ source.ownerUserId !== run.ownerUserId ||
255
+ source.actorUserId !== run.actorUserId)
256
+ return false;
257
+ const sourceRoot = source.commentRootRunId
258
+ ? await findCommentRoot(unit.conn, source.commentRootRunId)
259
+ : source;
260
+ const targetRoot = run.commentRootRunId
261
+ ? await findCommentRoot(unit.conn, run.commentRootRunId)
262
+ : run;
263
+ return ((await authorizedRoot(sourceRoot)) && (await authorizedRoot(targetRoot)));
264
+ async function authorizedRoot(root) {
265
+ if (!(await currentCommentOwner(unit.conn, deps.subjects, root)))
266
+ return false;
267
+ if (!root.commentRootRunId)
268
+ return chainSourceOf(root) === root.ownerUserId;
269
+ const { decision } = await commentConsent(unit.conn, root);
270
+ if (decision === 'pending' || decision === 'rejected')
271
+ return false;
272
+ return (decision === 'confirmed' || (await commentFromOwner(unit.conn, root)));
273
+ }
274
+ }
275
+ /**
276
+ * Queues `work` for `agent`: it joins the run working on its key while that run can still be told, else the run
277
+ * waiting for it, else starts a new run.
278
+ */
279
+ async function queue(unit, agent, work) {
280
+ if (work.commentRootRunId) {
281
+ // A plain read: the root's subject never changes, and the agent lock below serializes the chain's enqueues.
282
+ const root = await findCommentRoot(unit.conn, work.commentRootRunId);
283
+ if (root.subjectKind !== work.subject.kind ||
284
+ root.subjectId !== work.subject.id)
285
+ throw invalid('A comment authorization cannot cross subjects.');
286
+ }
287
+ // Serializes enqueues and claims for this agent, so a key never gets two waiting runs.
288
+ await lockAgentForClaim(unit.conn, agent.id, clock.now().toISOString());
289
+ if (work.preferActive) {
290
+ const commentId = jsonObject(work.input.payload).commentId;
291
+ if (typeof commentId === 'string') {
292
+ const prior = await inputsRepo(unit.conn).findOne({
293
+ filter: (f) => f.and([
294
+ f.string('type').eq('comment'),
295
+ f.json('payload').path(['commentId']).eq(commentId),
296
+ f
297
+ .relation('run')
298
+ .some((r) => r.and([
299
+ r.string('agentId').eq(agent.id),
300
+ r.string('subjectKind').eq(work.subject.kind),
301
+ r.string('subjectId').eq(work.subject.id),
302
+ ])),
303
+ ]),
304
+ });
305
+ if (prior) {
306
+ const run = (await findRunRecord(unit.conn, prior.runId));
307
+ return {
308
+ runId: run.id,
309
+ inputId: prior.id,
310
+ outcome: 'appended',
311
+ status: run.status,
312
+ duplicate: true,
313
+ };
314
+ }
315
+ }
316
+ }
317
+ const key = {
318
+ agentId: agent.id,
319
+ subjectKind: work.subject.kind,
320
+ subjectId: work.subject.id,
321
+ threadScope: work.threadScope,
322
+ };
323
+ // Only into work done as the same person: merged into another's run, it would borrow their identity, or wait
324
+ // for a runner that never takes it.
325
+ const unfiltered = await runsOfKey(unit.conn, key, ['queued', ...ACTIVE], work.actorUserId);
326
+ const open = unfiltered.filter((run) => (run.commentRootRunId ?? null) === (work.commentRootRunId ?? null) &&
327
+ (run.commentExecution ?? null) === (work.commentExecution ?? null));
328
+ let working;
329
+ for (let candidate of work.preferActive ? unfiltered : open) {
330
+ if (work.freshSession ||
331
+ candidate.cancelRequestedAt ||
332
+ !(candidate.status === 'dispatched' ||
333
+ (candidate.status === 'running' && Boolean(candidate.acceptsInput))))
334
+ continue;
335
+ if (work.preferActive) {
336
+ if (!candidate.runnerId ||
337
+ !['claude', 'codex', 'pi', 'opencode'].includes(candidate.tool ?? ''))
338
+ continue;
339
+ const runner = await deps.runners.find(unit.conn, candidate.runnerId);
340
+ if (!runner?.features.includes('steer'))
341
+ continue;
342
+ }
343
+ for (let attempt = 0; attempt < 4; attempt += 1) {
344
+ if (work.preferActive &&
345
+ !(await equivalentCommentAuthority(unit, candidate, work)))
346
+ break;
347
+ const input = {
348
+ id: '',
349
+ at: clock.now().toISOString(),
350
+ ...work.input,
351
+ ...(candidate.commentRootRunId
352
+ ? {
353
+ payload: {
354
+ ...jsonObject(work.input.payload),
355
+ commentRootRunId: candidate.commentRootRunId,
356
+ },
357
+ }
358
+ : {}),
359
+ };
360
+ if ((await gateInputs(unit.conn, deps, candidate, agent, [input]))
361
+ .length === 0)
362
+ break;
363
+ const touched = await runsRepo(unit.conn).updateMany({
364
+ filter: (f) => f.and([
365
+ f.string('id').eq(candidate.id),
366
+ f.string('status').eq(candidate.status),
367
+ f.number('revision').eq(candidate.revision),
368
+ f.date('cancelRequestedAt').empty(),
369
+ ]),
370
+ values: { updatedAt: clock.now().toISOString() },
371
+ });
372
+ if (touched.updatedCount === 1) {
373
+ working = candidate;
374
+ break;
375
+ }
376
+ const current = await findRunRecord(unit.conn, candidate.id);
377
+ if (!current ||
378
+ current.cancelRequestedAt ||
379
+ !(current.status === 'dispatched' ||
380
+ (current.status === 'running' && current.acceptsInput)))
381
+ break;
382
+ // A claim/start may change the holder or tool; re-evaluate those on a subsequent dispatch.
383
+ if (current.actorUserId !== work.actorUserId ||
384
+ current.threadScope !== work.threadScope ||
385
+ current.runnerId !== candidate.runnerId ||
386
+ current.tool !== candidate.tool)
387
+ break;
388
+ candidate = current;
389
+ if (attempt === 3)
390
+ throw conflict('The run revision changed during append; retry the dispatch.');
391
+ }
392
+ if (working)
393
+ break;
394
+ }
395
+ const waiting = open.find((run) => run.status === 'queued');
396
+ const target = working ?? waiting;
397
+ const commentInput = (rootId) => rootId
398
+ ? {
399
+ ...work.input,
400
+ payload: {
401
+ ...jsonObject(work.input.payload),
402
+ commentRootRunId: rootId,
403
+ },
404
+ }
405
+ : work.input;
406
+ if (!working && waiting)
407
+ await hurry(unit, waiting, work.priority, work.fireAt);
408
+ if (target) {
409
+ if (!working && work.freshSession && !target.freshSession)
410
+ await runsRepo(unit.conn).updateMany({
411
+ filter: { id: target.id, status: 'queued' },
412
+ values: { freshSession: true, updatedAt: clock.now().toISOString() },
413
+ });
414
+ const inputId = await insertInput(unit, target.id, commentInput(target.commentRootRunId));
415
+ return {
416
+ runId: target.id,
417
+ outcome: working ? 'appended' : 'merged',
418
+ status: target.status,
419
+ inputId,
420
+ };
421
+ }
422
+ const runId = await createRun(unit, agent, {
423
+ commentRootRunId: work.commentRootRunId,
424
+ commentExecution: work.commentExecution,
425
+ commentOwnerKey: work.commentOwnerKey,
426
+ freshSession: work.freshSession,
427
+ subject: work.subject,
428
+ threadScope: work.threadScope,
429
+ actorUserId: work.actorUserId,
430
+ requestedByUserId: work.requestedByUserId,
431
+ confirmedByUserId: work.confirmedByUserId,
432
+ ownerUserId: work.ownerUserId,
433
+ priority: work.priority,
434
+ requires: work.requires,
435
+ retryOfRunId: null,
436
+ availableAt: work.fireAt,
437
+ ...(work.parentRunId ? { parentRunId: work.parentRunId } : {}),
438
+ ...(work.maxAttempts ? { maxAttempts: work.maxAttempts } : {}),
439
+ });
440
+ const inputId = await insertInput(unit, runId, commentInput(work.commentRootRunId === '' ? runId : work.commentRootRunId));
441
+ return { runId, outcome: 'created', status: 'queued', inputId };
442
+ }
443
+ /** The source of the chain of work a request belongs to (`EnqueueRequest.requestedByUserId`). */
444
+ async function sourceOf(unit, request) {
445
+ if (request.comment &&
446
+ request.input.actor.kind === 'user' &&
447
+ jsonObject(request.input.payload).byAgent !== true &&
448
+ (request.requestedByUserId ?? request.actorUserId))
449
+ return (request.requestedByUserId ?? request.actorUserId);
450
+ if (request.causedByRunId) {
451
+ const cause = await findRunRecord(unit.conn, request.causedByRunId);
452
+ if (!cause)
453
+ throw invalid('causedByRunId names no run.');
454
+ return request.comment
455
+ ? (cause.requestedByUserId ?? cause.actorUserId)
456
+ : chainSourceOf(cause);
457
+ }
458
+ const source = request.requestedByUserId ??
459
+ request.actorUserId ??
460
+ request.responsibleUserId;
461
+ if (!source)
462
+ throw invalid('Name who woke the agent: actorUserId, requestedByUserId or responsibleUserId.');
463
+ return source;
464
+ }
465
+ async function selectedIdentity(unit, request, commentExecution) {
466
+ const identity = await deps.subjects
467
+ .get(request.subject.kind)
468
+ ?.actsAs?.(unit.conn, {
469
+ subjectId: request.subject.id,
470
+ agentId: request.agentId,
471
+ causedByRunId: request.causedByRunId,
472
+ commentExecution,
473
+ actorUserId: request.actorUserId,
474
+ inputs: [request.input],
475
+ });
476
+ if (identity && !identity.actorUserId)
477
+ throw new ProtocolError('FORBIDDEN', 'The subject has no valid execution identity.', { identityReason: identity.reason ?? 'noOwner' });
478
+ return identity;
479
+ }
480
+ /** Refuses work run as `userId` when no runner they may use can run the agent now: it would only wait. */
481
+ async function requireRunnerFor(unit, agent, userId, requires) {
482
+ // By the claim's own rules, the agent's variables included; the subject's are the claim's to check.
483
+ if (!(await deps.eligibility.canClaim(unit.conn, agent, {
484
+ actorUserId: userId,
485
+ requires: requires,
486
+ })))
487
+ throw precondition('NO_RUNNER_AVAILABLE', 'No runner you may use can run this agent now: connect one of your own, or ask the person who answers for this to confirm it.', { agentId: agent.id });
488
+ }
489
+ const requests = createRunRequestService({
490
+ tx,
491
+ ids,
492
+ clock,
493
+ people: deps.people,
494
+ currentResponsible: (unit, subject) => deps.subjects
495
+ .get(subject.kind)
496
+ ?.responsibleUserId?.(unit.conn, subject.id),
497
+ mayInvoke: (agent, userId) => deps.agents.mayInvoke(agent, userId),
498
+ requireInvocable,
499
+ requireRunnerFor,
500
+ prepareCommentActor: async (record, byUserId) => {
501
+ const mayRead = deps.subjects.get(record.subjectKind)?.commentActorMayRead;
502
+ if (mayRead && !(await mayRead(record.subjectId, byUserId)))
503
+ throw forbidden('The acting user may no longer read this issue.');
504
+ },
505
+ checkCommentRequest: async (unit, record) => {
506
+ const root = await findCommentRoot(unit.conn, record.commentRootRunId);
507
+ if (!(await currentCommentOwner(unit.conn, deps.subjects, root)) ||
508
+ root.commentExecution !== 'owner' ||
509
+ root.ownerUserId !== record.responsibleUserId ||
510
+ (root.commentOwnerKey ?? null) !== (record.commentOwnerKey ?? null))
511
+ throw precondition('RUN_REQUEST_SETTLED', 'This comment request belongs to an earlier owner assignment.');
512
+ },
513
+ releaseCommentRequest: async (unit, record, byUserId) => {
514
+ const run = await findRunRecord(unit.conn, record.commentRunId);
515
+ if (!run ||
516
+ run.status !== 'queued' ||
517
+ run.commentRootRunId !== record.commentRootRunId)
518
+ throw precondition('RUN_REQUEST_SETTLED', 'The requested work is no longer waiting.');
519
+ const inputs = await pendingInputs(unit.conn, run.id);
520
+ const now = clock.now().toISOString();
521
+ // Guarded on the requested run itself: a claim that took it first wins, and this confirmation rolls back.
522
+ const released = await runsRepo(unit.conn).updateMany({
523
+ filter: { id: run.id, status: 'queued' },
524
+ values: { confirmedByUserId: byUserId, updatedAt: now },
525
+ });
526
+ if (released.updatedCount !== 1)
527
+ throw precondition('RUN_REQUEST_SETTLED', 'The requested work is no longer waiting.');
528
+ await runsRepo(unit.conn).updateMany({
529
+ filter: {
530
+ commentRootRunId: record.commentRootRunId,
531
+ status: 'queued',
532
+ actorUserId: record.responsibleUserId,
533
+ },
534
+ values: { confirmedByUserId: byUserId, updatedAt: now },
535
+ });
536
+ unit.emit({ type: 'run.changed', runId: run.id, status: 'queued' });
537
+ return {
538
+ runId: run.id,
539
+ outcome: 'merged',
540
+ status: 'queued',
541
+ inputId: inputs[0].id,
542
+ };
543
+ },
544
+ cancelCommentRequest: async (unit, record, byUserId) => {
545
+ for (const run of await runsRepo(unit.conn).findMany({
546
+ filter: { commentRootRunId: record.commentRootRunId },
547
+ }))
548
+ if (!isTerminal(run.status))
549
+ await endCommentRun(unit, run, byUserId);
550
+ },
551
+ runCommentAsRequester: async (unit, record, byUserId) => {
552
+ // Queuing the replacements takes their agents' rows, as claims do before they take a run. Taking every agent of
553
+ // the chain first, in one order, keeps this from holding a run a claim waits on while it waits for that claim.
554
+ const agentIds = new Set((await runsRepo(unit.conn).findMany({
555
+ filter: { commentRootRunId: record.commentRootRunId },
556
+ })).map((run) => run.agentId));
557
+ for (const agentId of [...agentIds].sort())
558
+ await lockAgentForClaim(unit.conn, agentId, clock.now().toISOString());
559
+ const chain = await runsRepo(unit.conn).findMany({
560
+ filter: { commentRootRunId: record.commentRootRunId },
561
+ sort: (sort) => [sort.field('createdAt').asc(), sort.field('id').asc()],
562
+ });
563
+ const targets = [];
564
+ for (const old of chain) {
565
+ // Rejection ends the queued chain, but leaves its inputs unhandled. Do not revive independently ended work.
566
+ const declined = record.status === 'rejected' &&
567
+ old.status === 'cancelled' &&
568
+ old.cancelledById === record.responsibleUserId &&
569
+ old.failureDetail === COMMENT_CANCEL_DETAIL;
570
+ if (isTerminal(old.status) && !declined)
571
+ continue;
572
+ const inputs = await pendingInputs(unit.conn, old.id);
573
+ if (inputs.length === 0)
574
+ continue;
575
+ await selectedIdentity(unit, {
576
+ agentId: old.agentId,
577
+ subject: { kind: old.subjectKind, id: old.subjectId },
578
+ input: toInput(inputs[0]),
579
+ actorUserId: byUserId,
580
+ }, 'requester');
581
+ const agent = await requireInvocable(unit, old.agentId, byUserId);
582
+ const requires = stringArray(old.requires);
583
+ await requireRunnerFor(unit, agent, byUserId, requires);
584
+ targets.push({ old, inputs, agent, requires });
585
+ }
586
+ if (!targets.length)
587
+ throw precondition('RUN_REQUEST_SETTLED', 'No comment work is still waiting.');
588
+ // Validate all targets before stopping any of them. Each stop is guarded on the status read, so a run a claim took
589
+ // meanwhile rolls this back rather than being replaced twice; the settled request covers repeat clicks.
590
+ for (const old of chain)
591
+ if (!isTerminal(old.status))
592
+ await endCommentRun(unit, old, byUserId);
593
+ const replacements = new Map();
594
+ let rootId;
595
+ for (const { old, inputs, agent, requires } of targets) {
596
+ const first = inputs[0];
597
+ const next = await queue(unit, agent, {
598
+ commentRootRunId: rootId ?? '',
599
+ commentExecution: 'requester',
600
+ commentOwnerKey: record.commentOwnerKey ?? null,
601
+ freshSession: Boolean(old.freshSession),
602
+ subject: { kind: old.subjectKind, id: old.subjectId },
603
+ threadScope: old.threadScope,
604
+ actorUserId: byUserId,
605
+ requestedByUserId: byUserId,
606
+ confirmedByUserId: null,
607
+ ownerUserId: old.ownerUserId,
608
+ priority: Number(old.priority),
609
+ fireAt: fireAtOf(old.availableAt ?? undefined, clock.now()),
610
+ requires,
611
+ maxAttempts: Number(old.maxAttempts),
612
+ input: toInput(first),
613
+ });
614
+ rootId ??= next.runId;
615
+ replacements.set(old.id, next);
616
+ for (const input of inputs.slice(1))
617
+ await insertInput(unit, next.runId, {
618
+ ...toInput(input),
619
+ payload: { ...jsonObject(input.payload), commentRootRunId: rootId },
620
+ });
621
+ }
622
+ // Remap causal references only for replaced work; completed ancestors remain the original historical runs.
623
+ for (const { old } of targets) {
624
+ const next = replacements.get(old.id);
625
+ if (old.parentRunId)
626
+ await runsRepo(unit.conn).updateMany({
627
+ filter: { id: next.runId },
628
+ values: {
629
+ parentRunId: replacements.get(old.parentRunId)?.runId ?? old.parentRunId,
630
+ },
631
+ });
632
+ for (const input of await pendingInputs(unit.conn, next.runId)) {
633
+ const payload = jsonObject(input.payload);
634
+ const cause = typeof payload.causedByRunId === 'string'
635
+ ? replacements.get(payload.causedByRunId)
636
+ : undefined;
637
+ if (cause)
638
+ await inputsRepo(unit.conn).updateMany({
639
+ filter: { id: input.id },
640
+ values: {
641
+ payload: asJson({ ...payload, causedByRunId: cause.runId }),
642
+ },
643
+ });
644
+ }
645
+ }
646
+ const rootInputs = await pendingInputs(unit.conn, rootId);
647
+ if (!rootInputs.some((input) => input.type === record.inputType &&
648
+ input.text === record.inputText &&
649
+ jsonObject(input.payload).commentId ===
650
+ jsonObject(record.inputPayload).commentId)) {
651
+ const sourceId = await insertInput(unit, rootId, {
652
+ type: record.inputType,
653
+ actor: {
654
+ kind: record.inputActorKind,
655
+ id: record.inputActorId,
656
+ name: record.inputActorName,
657
+ },
658
+ text: record.inputText,
659
+ payload: {
660
+ ...jsonObject(record.inputPayload),
661
+ commentSourceSnapshot: true,
662
+ commentRootRunId: rootId,
663
+ },
664
+ });
665
+ await inputsRepo(unit.conn).updateMany({
666
+ filter: { id: sourceId },
667
+ values: { handledAt: clock.now().toISOString() },
668
+ });
669
+ }
670
+ return (replacements.get(record.commentRunId) ??
671
+ replacements.values().next().value);
672
+ },
673
+ direct: async (unit, run) => {
674
+ const identity = await selectedIdentity(unit, {
675
+ agentId: run.agent.id,
676
+ subject: run.subject,
677
+ input: run.input,
678
+ actorUserId: run.actorUserId,
679
+ });
680
+ const actorUserId = identity?.actorUserId ?? run.actorUserId;
681
+ const agent = await requireInvocable(unit, run.agent.id, actorUserId);
682
+ return queue(unit, agent, {
683
+ freshSession: run.freshSession,
684
+ subject: run.subject,
685
+ threadScope: run.threadScope,
686
+ actorUserId,
687
+ requestedByUserId: run.requestedByUserId,
688
+ confirmedByUserId: run.confirmedByUserId,
689
+ ownerUserId: identity ? actorUserId : run.ownerUserId,
690
+ priority: run.priority,
691
+ fireAt: fireAtOf(run.fireAt ?? undefined, clock.now()),
692
+ requires: run.requires,
693
+ ...(run.maxAttempts === null ? {} : { maxAttempts: run.maxAttempts }),
694
+ input: run.input,
695
+ });
696
+ },
697
+ });
155
698
  return {
156
699
  enqueue: (request, outer) => tx.run(async (unit) => {
157
- const agent = await requireInvocable(unit, request.agentId, request.actorUserId);
158
- // Serializes enqueues and claims for this agent, so a key never gets two waiting runs.
159
- await lockAgentForClaim(unit.conn, agent.id, clock.now().toISOString());
160
- const key = {
161
- agentId: agent.id,
162
- subjectKind: request.subject.kind,
163
- subjectId: request.subject.id,
164
- threadScope: request.threadScope ?? DEFAULT_THREAD,
165
- };
166
- const open = await runsOfKey(unit.conn, key, ['queued', ...ACTIVE]);
167
- const working = open.find((run) => run.status === 'dispatched' ||
168
- (run.status === 'running' && Boolean(run.acceptsInput)));
169
- const waiting = open.find((run) => run.status === 'queued');
170
- const target = working ?? waiting;
171
- const fireAt = fireAtOf(request.fireAt, clock.now());
172
- if (!working && waiting)
173
- await hurry(unit, waiting, request.priority ?? 0, fireAt);
174
- if (target) {
175
- const inputId = await insertInput(unit, target.id, request.input);
700
+ const source = await sourceOf(unit, request);
701
+ const cause = request.causedByRunId
702
+ ? await findRunRecord(unit.conn, request.causedByRunId)
703
+ : null;
704
+ const inherited = !request.comment &&
705
+ cause?.commentRootRunId &&
706
+ cause.subjectKind === request.subject.kind &&
707
+ cause.subjectId === request.subject.id
708
+ ? cause
709
+ : null;
710
+ const commentRootRunId = request.comment?.rootRunId ??
711
+ inherited?.commentRootRunId ??
712
+ (request.comment ? '' : null);
713
+ const commentExecution = inherited?.commentExecution ?? (request.comment ? 'owner' : null);
714
+ const commentOwnerKey = request.comment?.ownerKey ?? inherited?.commentOwnerKey ?? null;
715
+ const identity = await selectedIdentity(unit, request, commentExecution);
716
+ const responsible = identity
717
+ ? null
718
+ : (request.responsibleUserId ?? null);
719
+ const threadScope = request.threadScope ?? DEFAULT_THREAD;
720
+ const foreign = responsible !== null && source !== responsible;
721
+ if (foreign && (request.execution ?? 'auto') === 'auto') {
722
+ // A consultation's asker waits for its answer at once: it can never wait for someone's confirmation.
723
+ if (request.parentRunId)
724
+ throw invalid('A consultation cannot wait for confirmation: name no responsible for it.');
725
+ // Someone else's work waits for the responsible, who must be able to run it once they confirm it.
726
+ const agent = await requireInvocable(unit, request.agentId, source);
727
+ if (!deps.agents.mayInvoke(agent, responsible))
728
+ throw forbidden('The person who answers for this may not wake this agent.');
729
+ const created = await insertRunRequest(unit, { ids, clock }, {
730
+ agentId: agent.id,
731
+ freshSession: request.freshSession,
732
+ subject: request.subject,
733
+ threadScope,
734
+ responsibleUserId: responsible,
735
+ requestedByUserId: source,
736
+ ownerUserId: request.ownerUserId ?? null,
737
+ priority: request.priority ?? 0,
738
+ requires: request.requires ?? [],
739
+ fireAt: fireAtOf(request.fireAt, clock.now()),
740
+ maxAttempts: request.maxAttempts ?? null,
741
+ input: request.input,
742
+ });
176
743
  return {
177
- runId: target.id,
178
- outcome: working ? 'appended' : 'merged',
179
- status: target.status,
180
- inputId,
744
+ outcome: 'pending',
745
+ requestId: created.id,
746
+ status: 'pending',
747
+ runId: null,
748
+ inputId: null,
181
749
  };
182
750
  }
183
- const runId = await createRun(unit, agent, {
751
+ // As the responsible when they caused it; as the source when they run it as themselves (`mine`); as whoever
752
+ // woke the agent when nobody answers for the subject.
753
+ const actorUserId = (commentExecution === 'requester'
754
+ ? inherited?.actorUserId
755
+ : identity?.actorUserId) ??
756
+ (foreign ? source : (responsible ?? request.actorUserId ?? source));
757
+ const agent = await requireInvocable(unit, request.agentId, actorUserId);
758
+ if (foreign)
759
+ await requireRunnerFor(unit, agent, actorUserId, request.requires ?? []);
760
+ const result = await queue(unit, agent, {
761
+ preferActive: request.preferActive,
762
+ sourceRunId: request.causedByRunId,
763
+ commentRootRunId,
764
+ commentExecution,
765
+ commentOwnerKey,
766
+ freshSession: request.freshSession,
184
767
  subject: request.subject,
185
- threadScope: key.threadScope,
186
- actorUserId: request.actorUserId,
187
- ownerUserId: request.ownerUserId ?? null,
768
+ threadScope,
769
+ actorUserId,
770
+ requestedByUserId: source,
771
+ confirmedByUserId: inherited?.confirmedByUserId ?? null,
772
+ ownerUserId: commentExecution === 'requester'
773
+ ? (inherited?.ownerUserId ?? request.ownerUserId ?? null)
774
+ : identity
775
+ ? actorUserId
776
+ : (request.ownerUserId ?? null),
188
777
  priority: request.priority ?? 0,
778
+ fireAt: fireAtOf(request.fireAt, clock.now()),
189
779
  requires: request.requires ?? [],
190
- retryOfRunId: null,
191
- availableAt: fireAt,
192
780
  ...(request.parentRunId ? { parentRunId: request.parentRunId } : {}),
193
781
  ...(request.maxAttempts ? { maxAttempts: request.maxAttempts } : {}),
782
+ input: request.input,
194
783
  });
195
- const inputId = await insertInput(unit, runId, request.input);
196
- return { runId, outcome: 'created', status: 'queued', inputId };
784
+ if (commentRootRunId !== null)
785
+ await deps.reconcileCommentConsent?.(unit, result.runId);
786
+ return result;
197
787
  }, outer),
788
+ requests,
789
+ identityInputs: async (conn, runId) => {
790
+ const run = await findRunRecord(conn, runId);
791
+ if (!run)
792
+ return null;
793
+ const inputs = await inputsRepo(conn).findMany({ filter: { runId } });
794
+ return { run: toRun(run), inputs: inputs.map(toInput) };
795
+ },
198
796
  addInput: (runId, input, outer) => tx.run(async (unit) => {
199
797
  const run = await require(unit.conn, runId);
798
+ if (run.commentRootRunId &&
799
+ input.type === 'comment' &&
800
+ jsonObject(input.payload).commentRootRunId !== run.commentRootRunId)
801
+ throw forbidden('A comment must be queued in its own authorized run chain.');
200
802
  if (isTerminal(run.status))
201
803
  throw new ProtocolError('RUN_NOT_ACTIVE', `The run is ${run.status}.`);
804
+ if (isActive(run.status)) {
805
+ let current = run;
806
+ for (let attempt = 0; attempt < 4; attempt += 1) {
807
+ const updated = await runsRepo(unit.conn).updateMany({
808
+ filter: {
809
+ id: current.id,
810
+ status: current.status,
811
+ revision: current.revision,
812
+ },
813
+ values: { updatedAt: clock.now().toISOString() },
814
+ });
815
+ if (updated.updatedCount === 1)
816
+ return insertInput(unit, current.id, input);
817
+ current = await require(unit.conn, run.id);
818
+ if (!isActive(current.status))
819
+ throw conflict('The run moved on before input was appended.');
820
+ }
821
+ throw conflict('The run revision changed during append; retry the input.');
822
+ }
202
823
  return insertInput(unit, run.id, input);
203
824
  }, outer),
204
825
  cancel: (runId, byUserId, outer) => tx.run(async (unit) => {
@@ -263,44 +884,192 @@ export function createRunService(deps) {
263
884
  }
264
885
  return ended;
265
886
  }, outer),
266
- retry: (runId, byUserId) => tx.run(async (unit) => {
267
- const run = await require(unit.conn, runId);
268
- if (run.status !== 'failed' && run.status !== 'cancelled')
269
- throw precondition('RUN_NOT_RETRYABLE', 'Only a failed or cancelled run can be retried.');
270
- if (run.parentRunId)
271
- throw precondition('RUN_NOT_RETRYABLE', 'A consultation is not retried: the agent that asked asks again.');
272
- const agent = await requireInvocable(unit, run.agentId, byUserId);
273
- await lockAgentForClaim(unit.conn, agent.id, clock.now().toISOString());
274
- const open = await runsOfKey(unit.conn, run, ['queued', ...ACTIVE]);
275
- if (open.length > 0)
276
- throw precondition('AGENT_BUSY', 'The agent is already working on this.', {
277
- runId: open[0].id,
887
+ retry: async (runId, byUserId, source = 'api') => {
888
+ const target = await require(tx.read(), runId);
889
+ // Resolve collection metadata outside the transaction too. The lock must
890
+ // precede consistent reads so repeatable-read databases see a concurrent
891
+ // retry that committed while this request waited for the lock.
892
+ await findAgent(tx.read(), target.agentId);
893
+ return tx.run(async (unit) => {
894
+ await lockAgentForClaim(unit.conn, target.agentId, clock.now().toISOString());
895
+ const run = await require(unit.conn, runId);
896
+ if (run.status !== 'failed' && run.status !== 'cancelled')
897
+ throw precondition('RUN_NOT_RETRYABLE', 'Only a failed or cancelled run can be retried.');
898
+ if (run.parentRunId)
899
+ throw precondition('RUN_NOT_RETRYABLE', 'A consultation is not retried: the agent that asked asks again.');
900
+ // The agent lock serializes retry and enqueue across server instances. Check
901
+ // the relationship even after the replacement has already ended.
902
+ const retried = await runsRepo(unit.conn).findOne({
903
+ filter: { retryOfRunId: run.id },
904
+ sort: (sort) => [
905
+ sort.field('createdAt').asc(),
906
+ sort.field('id').asc(),
907
+ ],
278
908
  });
279
- const newId = await createRun(unit, agent, {
280
- subject: { kind: run.subjectKind, id: run.subjectId },
281
- threadScope: run.threadScope,
282
- actorUserId: byUserId,
283
- ownerUserId: run.ownerUserId,
284
- priority: Number(run.priority),
285
- requires: toRun(run).requires,
286
- retryOfRunId: run.id,
287
- });
288
- for (const input of await pendingInputs(unit.conn, run.id))
909
+ if (retried)
910
+ throw precondition('RUN_ALREADY_RETRIED', 'This run has already been retried.', {
911
+ runId: retried.id,
912
+ });
913
+ const previousInputs = await inputsRepo(unit.conn).findMany({
914
+ filter: { runId: run.id },
915
+ });
916
+ if (!run.commentRootRunId &&
917
+ deps.subjects.get(run.subjectKind)?.commentOwnerKey &&
918
+ previousInputs.some((input) => input.type === 'comment' &&
919
+ !(input.actorKind === 'user' &&
920
+ input.actorId === run.ownerUserId &&
921
+ jsonObject(input.payload).byAgent !== true)))
922
+ throw precondition('RUN_NOT_RETRYABLE', 'Post the comment again to start a new authorized comment chain.');
923
+ const oldRoot = run.commentRootRunId
924
+ ? await findRunRecord(unit.conn, run.commentRootRunId)
925
+ : null;
926
+ const renewComment = oldRoot
927
+ ? !(await currentCommentOwner(unit.conn, deps.subjects, oldRoot))
928
+ : false;
929
+ const commentExecution = renewComment &&
930
+ run.commentExecution === 'requester' &&
931
+ byUserId !== run.actorUserId
932
+ ? 'owner'
933
+ : run.commentExecution;
934
+ const identity = await deps.subjects
935
+ .get(run.subjectKind)
936
+ ?.actsAs?.(unit.conn, {
937
+ subjectId: run.subjectId,
938
+ agentId: run.agentId,
939
+ inputs: previousInputs.map(toInput),
940
+ causedByRunId: run.retryOfRunId ?? undefined,
941
+ commentExecution,
942
+ actorUserId: run.actorUserId,
943
+ });
944
+ if (identity && !identity.actorUserId)
945
+ throw forbidden(identity.reason ?? 'noOwner');
946
+ const actorUserId = commentExecution === 'requester'
947
+ ? run.actorUserId
948
+ : (identity?.actorUserId ?? byUserId);
949
+ const agent = await requireInvocable(unit, run.agentId, actorUserId);
950
+ const open = await runsOfKey(unit.conn, run, ['queued', ...ACTIVE], actorUserId);
951
+ const sameChain = run.commentRootRunId
952
+ ? open.filter((candidate) => candidate.commentRootRunId === run.commentRootRunId)
953
+ : open;
954
+ if (sameChain.length > 0)
955
+ throw precondition('AGENT_BUSY', 'The agent is already working on this.', {
956
+ runId: sameChain[0].id,
957
+ });
958
+ const newId = await createRun(unit, agent, {
959
+ subject: { kind: run.subjectKind, id: run.subjectId },
960
+ threadScope: run.threadScope,
961
+ actorUserId,
962
+ commentRootRunId: renewComment ? '' : run.commentRootRunId,
963
+ commentExecution,
964
+ commentOwnerKey: renewComment
965
+ ? ((await deps.subjects
966
+ .get(run.subjectKind)
967
+ ?.commentOwnerKey?.(unit.conn, run.subjectId)) ?? null)
968
+ : run.commentOwnerKey,
969
+ requestedByUserId: run.commentRootRunId
970
+ ? (run.requestedByUserId ?? run.actorUserId)
971
+ : byUserId,
972
+ confirmedByUserId: null,
973
+ ownerUserId: run.commentRootRunId
974
+ ? ((await deps.subjects
975
+ .get(run.subjectKind)
976
+ ?.responsibleUserId?.(unit.conn, run.subjectId)) ?? null)
977
+ : identity
978
+ ? actorUserId
979
+ : run.ownerUserId,
980
+ priority: Number(run.priority),
981
+ requires: toRun(run).requires,
982
+ retryOfRunId: run.id,
983
+ freshSession: run.freshSession,
984
+ });
985
+ if (renewComment && oldRoot) {
986
+ const origins = await inputsRepo(unit.conn).findMany({
987
+ filter: { runId: oldRoot.id },
988
+ sort: (s) => [s.field('createdAt').asc(), s.field('id').asc()],
989
+ });
990
+ const origin = origins.find((input) => input.type === 'comment' &&
991
+ jsonObject(input.payload).commentSourceSnapshot === true) ?? origins.find((input) => input.type === 'comment');
992
+ if (origin) {
993
+ const snapshotId = await insertInput(unit, newId, {
994
+ type: 'comment',
995
+ actor: toInput(origin).actor,
996
+ text: origin.text,
997
+ payload: {
998
+ ...jsonObject(origin.payload),
999
+ commentRootRunId: newId,
1000
+ commentSourceSnapshot: true,
1001
+ },
1002
+ });
1003
+ await inputsRepo(unit.conn).updateMany({
1004
+ filter: { id: snapshotId },
1005
+ values: { handledAt: clock.now().toISOString() },
1006
+ });
1007
+ }
1008
+ }
1009
+ for (const input of await pendingInputs(unit.conn, run.id))
1010
+ await insertInput(unit, newId, {
1011
+ type: input.type,
1012
+ actor: toInput(input).actor,
1013
+ text: input.text,
1014
+ ...(input.payload === null
1015
+ ? {}
1016
+ : {
1017
+ payload: {
1018
+ ...jsonObject(input.payload),
1019
+ ...(renewComment ? { commentRootRunId: newId } : {}),
1020
+ },
1021
+ }),
1022
+ });
289
1023
  await insertInput(unit, newId, {
290
- type: input.type,
291
- actor: toInput(input).actor,
292
- text: input.text,
293
- ...(input.payload === null ? {} : { payload: input.payload }),
1024
+ type: 'retry',
1025
+ actor: {
1026
+ kind: 'user',
1027
+ id: byUserId,
1028
+ name: (await deps.people.names(unit.conn, [byUserId])).get(byUserId) ??
1029
+ byUserId,
1030
+ },
1031
+ text: 'Retry the work of the previous run.',
1032
+ payload: {
1033
+ retryOfRunId: run.id,
1034
+ failureReason: run.failureReason,
1035
+ source,
1036
+ },
1037
+ });
1038
+ unit.emit({
1039
+ type: 'run.retried',
1040
+ runId: newId,
1041
+ retryOfRunId: run.id,
1042
+ previousStatus: run.status,
294
1043
  });
295
- await insertInput(unit, newId, {
296
- type: 'retry',
297
- actor: { kind: 'user', id: byUserId, name: byUserId },
298
- text: 'Retry the work of the previous run.',
299
- payload: { retryOfRunId: run.id, failureReason: run.failureReason },
1044
+ if (run.commentRootRunId)
1045
+ await deps.reconcileCommentConsent?.(unit, newId);
1046
+ return toRun(await require(unit.conn, newId));
300
1047
  });
301
- return toRun(await require(unit.conn, newId));
302
- }),
303
- get: async (runId) => toRun(await require(tx.read(), runId)),
1048
+ },
1049
+ get: async (runId) => (await readRuns([await require(tx.read(), runId)]))[0],
1050
+ async retryInfo(runId) {
1051
+ const conn = tx.read();
1052
+ const next = await runsRepo(conn).findOne({
1053
+ filter: { retryOfRunId: runId },
1054
+ sort: (sort) => [sort.field('createdAt').asc(), sort.field('id').asc()],
1055
+ });
1056
+ if (!next)
1057
+ return null;
1058
+ const input = await inputsRepo(conn).findOne({
1059
+ filter: { runId: next.id, type: 'retry' },
1060
+ sort: (sort) => [
1061
+ sort.field('createdAt').desc(),
1062
+ sort.field('id').desc(),
1063
+ ],
1064
+ });
1065
+ const source = input?.payload?.source;
1066
+ return {
1067
+ runId: next.id,
1068
+ actorUserId: next.actorUserId,
1069
+ actorName: input ? toInputView(input).actor.name : next.actorUserId,
1070
+ source: typeof source === 'string' ? source : 'unknown',
1071
+ };
1072
+ },
304
1073
  async detail(runId) {
305
1074
  const conn = tx.read();
306
1075
  const run = await require(conn, runId);
@@ -309,17 +1078,39 @@ export function createRunService(deps) {
309
1078
  sort: (sort) => [sort.field('createdAt').asc(), sort.field('id').asc()],
310
1079
  });
311
1080
  const repos = await reposRepo(conn).findMany({ filter: { runId } });
1081
+ const retried = await runsRepo(conn).findOne({
1082
+ filter: { retryOfRunId: runId },
1083
+ sort: (sort) => [sort.field('createdAt').asc(), sort.field('id').asc()],
1084
+ });
312
1085
  const children = await runsRepo(conn).findMany({
313
1086
  filter: { parentRunId: runId },
314
1087
  sort: (sort) => [sort.field('createdAt').asc(), sort.field('id').asc()],
315
1088
  });
316
- const names = new Map();
317
- for (const child of children)
318
- if (!names.has(child.agentId))
319
- names.set(child.agentId, (await findAgent(conn, child.agentId))?.name ?? null);
1089
+ const names = await agentNamesOf(conn, [
1090
+ ...children.map((child) => child.agentId),
1091
+ ...inputs.flatMap((input) => input.actorKind === 'agent' && input.actorId ? [input.actorId] : []),
1092
+ ]);
320
1093
  return {
321
- ...toRun(run),
322
- inputs: inputs.map(toInputView),
1094
+ ...(await readRuns([run]))[0],
1095
+ actorName: (await deps.people.names(conn, [run.actorUserId])).get(run.actorUserId) ?? run.actorUserId,
1096
+ confirmedByName: run.confirmedByUserId
1097
+ ? ((await deps.people.names(conn, [run.confirmedByUserId])).get(run.confirmedByUserId) ?? run.confirmedByUserId)
1098
+ : undefined,
1099
+ requestedByName: (await deps.people.names(conn, [
1100
+ run.requestedByUserId ?? run.actorUserId,
1101
+ ])).get(run.requestedByUserId ?? run.actorUserId) ??
1102
+ run.requestedByUserId ??
1103
+ run.actorUserId,
1104
+ retriedRunId: retried?.id ?? null,
1105
+ inputs: inputs.map((input) => {
1106
+ const view = toInputView(input);
1107
+ const name = view.actor.kind === 'agent' && view.actor.id
1108
+ ? names.get(view.actor.id)
1109
+ : undefined;
1110
+ return name === undefined
1111
+ ? view
1112
+ : { ...view, actor: { ...view.actor, name } };
1113
+ }),
323
1114
  repos: repos.map(toRepo),
324
1115
  usage: await usageOf(conn, runId),
325
1116
  children: children.map((child) => ({
@@ -361,7 +1152,7 @@ export function createRunService(deps) {
361
1152
  limit: Math.min(Math.max(filter.limit ?? 50, 1), 200),
362
1153
  ...(filter.cursor ? { cursor: filter.cursor } : {}),
363
1154
  });
364
- return records.map(toRun);
1155
+ return readRuns(records);
365
1156
  },
366
1157
  async lastEnded(subjectKind, subjectIds) {
367
1158
  if (subjectIds.length === 0)
@@ -383,16 +1174,44 @@ export function createRunService(deps) {
383
1174
  if (!newest.has(key))
384
1175
  newest.set(key, record);
385
1176
  }
386
- return [...newest.values()].map(toRun);
1177
+ return readRuns([...newest.values()]);
1178
+ },
1179
+ async workingOn(subjectKind, subjectIds) {
1180
+ const ids = [...new Set(subjectIds)];
1181
+ if (ids.length === 0)
1182
+ return [];
1183
+ const records = await runsRepo(tx.read()).findMany({
1184
+ select: (select) => select.fields('subjectId', 'agentId'),
1185
+ filter: (f) => f.and([
1186
+ f.string('subjectKind').eq(subjectKind),
1187
+ f.or(ids.map((id) => f.string('subjectId').eq(id))),
1188
+ f.or(['dispatched', 'running'].map((status) => f.string('status').eq(status))),
1189
+ ]),
1190
+ });
1191
+ const pairs = new Map();
1192
+ for (const record of records) {
1193
+ pairs.set(JSON.stringify([record.subjectId, record.agentId]), {
1194
+ subjectId: record.subjectId,
1195
+ agentId: record.agentId,
1196
+ working: true,
1197
+ });
1198
+ }
1199
+ return [...pairs.values()];
387
1200
  },
388
1201
  workload(query) {
389
- return readWorkload(tx.read(), clock, deps.runners, query);
1202
+ return readWorkload(tx.read(), clock, deps.runners, query, deps.diagnose);
390
1203
  },
391
- async events(runId, afterSeq, limit) {
1204
+ async events(runId, afterSeq, limit, types) {
392
1205
  const conn = tx.read();
393
1206
  await require(conn, runId);
394
1207
  const rows = await eventsRepo(conn).findMany({
395
- filter: (f) => f.and([f.string('runId').eq(runId), f.number('seq').gt(afterSeq)]),
1208
+ filter: (f) => f.and([
1209
+ f.string('runId').eq(runId),
1210
+ f.number('seq').gt(afterSeq),
1211
+ ...(types?.length
1212
+ ? [f.or(types.map((type) => f.string('type').eq(type)))]
1213
+ : []),
1214
+ ]),
396
1215
  sort: (sort) => sort.field('seq').asc(),
397
1216
  limit: Math.min(Math.max(limit, 1), 1000),
398
1217
  });
@@ -416,7 +1235,16 @@ export function createRunService(deps) {
416
1235
  lastSeq: events.length > 0 ? events[events.length - 1].seq : afterSeq,
417
1236
  };
418
1237
  },
419
- authenticateToken: (value) => authenticateRunToken(tx.read(), clock, value),
1238
+ authenticateToken: async (value) => {
1239
+ const conn = tx.read();
1240
+ const identity = await authenticateRunToken(conn, clock, value);
1241
+ if (identity.run.commentRootRunId) {
1242
+ const root = await findRunRecord(conn, identity.run.commentRootRunId);
1243
+ if (!root || !(await currentCommentOwner(conn, deps.subjects, root)))
1244
+ throw precondition('RUN_TOKEN_INVALID', 'The comment authorization belongs to an earlier owner assignment.');
1245
+ }
1246
+ return identity;
1247
+ },
420
1248
  async self({ run }) {
421
1249
  const conn = tx.read();
422
1250
  const agent = await findAgent(conn, run.agentId);
@@ -428,7 +1256,8 @@ export function createRunService(deps) {
428
1256
  subjectRef: { kind: run.subjectKind, id: run.subjectId },
429
1257
  actorUserId: run.actorUserId,
430
1258
  cancelRequested: run.cancelRequestedAt !== null,
431
- inputs: (await pendingInputs(conn, run.id)).map(toInput),
1259
+ // What the run may act on: a read never withholds anything, it only leaves out what delivery would.
1260
+ inputs: (await gatePending(conn, deps, run)).kept.map(toInput),
432
1261
  };
433
1262
  },
434
1263
  async brief(runId) {
@@ -444,14 +1273,10 @@ export function createRunService(deps) {
444
1273
  const conn = tx.read();
445
1274
  const binding = deps.subjects.get(run.subjectKind);
446
1275
  const agent = await findAgent(conn, run.agentId);
447
- // An online run is held by the application itself, not by a registered runner.
448
- const runner = run.runnerId && agent?.type === 'runner'
1276
+ const runner = run.runnerId
449
1277
  ? await deps.runners.find(conn, run.runnerId)
450
1278
  : null;
451
- if (!binding ||
452
- !agent ||
453
- (agent.type === 'runner' && !runner) ||
454
- !isActive(run.status))
1279
+ if (!binding || !agent || !runner || !isActive(run.status))
455
1280
  throw notFound('Run context');
456
1281
  const assembly = await binding.context.assemble(conn, {
457
1282
  run: toRun(run),
@@ -460,11 +1285,11 @@ export function createRunService(deps) {
460
1285
  inputs: (await pendingInputs(conn, run.id)).map(toInput),
461
1286
  cli: deps.cliName,
462
1287
  appName: deps.appName,
463
- dialect: dialectOf(agent),
464
- });
1288
+ }, await binding.context.prepare?.(toRun(run)));
465
1289
  return assembly.data;
466
1290
  },
467
1291
  openOn: openRunsOn,
1292
+ openOnEach: openRunsOnEach,
468
1293
  pendingInputs: async (conn, runId) => (await pendingInputs(conn, runId)).map(toInput),
469
1294
  inputsSince: inputsOnSince,
470
1295
  lastSummary,