@nocobase/app-plugin-agents 2.0.0-beta.0 → 2.0.0-beta.10

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 (365) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +26 -4
  3. package/dist/client/chat/api.d.ts +6 -1
  4. package/dist/client/chat/api.d.ts.map +1 -1
  5. package/dist/client/chat/api.js +18 -0
  6. package/dist/client/chat/api.js.map +1 -1
  7. package/dist/client/chat/coalesced-invalidation.d.ts +3 -8
  8. package/dist/client/chat/coalesced-invalidation.d.ts.map +1 -1
  9. package/dist/client/chat/coalesced-invalidation.js +16 -21
  10. package/dist/client/chat/coalesced-invalidation.js.map +1 -1
  11. package/dist/client/chat/controller.d.ts +30 -3
  12. package/dist/client/chat/controller.d.ts.map +1 -1
  13. package/dist/client/chat/controller.js +477 -78
  14. package/dist/client/chat/controller.js.map +1 -1
  15. package/dist/client/chat/notices.d.ts +12 -0
  16. package/dist/client/chat/notices.d.ts.map +1 -0
  17. package/dist/client/chat/notices.js +15 -0
  18. package/dist/client/chat/notices.js.map +1 -0
  19. package/dist/client/chat/provider.d.ts.map +1 -1
  20. package/dist/client/chat/provider.js +7 -4
  21. package/dist/client/chat/provider.js.map +1 -1
  22. package/dist/client/chat/submissions.d.ts +16 -0
  23. package/dist/client/chat/submissions.d.ts.map +1 -1
  24. package/dist/client/chat/submissions.js +22 -0
  25. package/dist/client/chat/submissions.js.map +1 -1
  26. package/dist/client/chat/use-chat.d.ts +4 -2
  27. package/dist/client/chat/use-chat.d.ts.map +1 -1
  28. package/dist/client/chat/use-chat.js +83 -51
  29. package/dist/client/chat/use-chat.js.map +1 -1
  30. package/dist/client/chat.d.ts +1 -0
  31. package/dist/client/chat.d.ts.map +1 -1
  32. package/dist/client/chat.js +1 -0
  33. package/dist/client/chat.js.map +1 -1
  34. package/dist/client/components/page-container.js +1 -1
  35. package/dist/client/components/page-container.js.map +1 -1
  36. package/dist/client/components/route-child-page.d.ts +1 -1
  37. package/dist/client/components/route-child-page.d.ts.map +1 -1
  38. package/dist/client/components/route-child-page.js +18 -2
  39. package/dist/client/components/route-child-page.js.map +1 -1
  40. package/dist/client/components/route-overlay.d.ts +4 -1
  41. package/dist/client/components/route-overlay.d.ts.map +1 -1
  42. package/dist/client/components/route-overlay.js +32 -3
  43. package/dist/client/components/route-overlay.js.map +1 -1
  44. package/dist/client/components/ui/alert-dialog.d.ts +3 -1
  45. package/dist/client/components/ui/alert-dialog.d.ts.map +1 -1
  46. package/dist/client/components/ui/alert-dialog.js +2 -2
  47. package/dist/client/components/ui/alert-dialog.js.map +1 -1
  48. package/dist/client/components/unsaved-changes.d.ts.map +1 -1
  49. package/dist/client/components/unsaved-changes.js +17 -4
  50. package/dist/client/components/unsaved-changes.js.map +1 -1
  51. package/dist/client/components/use-route-overlay.d.ts +2 -9
  52. package/dist/client/components/use-route-overlay.d.ts.map +1 -1
  53. package/dist/client/components/use-route-overlay.js +5 -3
  54. package/dist/client/components/use-route-overlay.js.map +1 -1
  55. package/dist/client/hooks/use-realtime-topic.d.ts +1 -1
  56. package/dist/client/hooks/use-realtime-topic.d.ts.map +1 -1
  57. package/dist/client/hooks/use-realtime-topic.js +8 -4
  58. package/dist/client/hooks/use-realtime-topic.js.map +1 -1
  59. package/dist/client/kit.d.ts.map +1 -1
  60. package/dist/client/kit.js +30 -5
  61. package/dist/client/kit.js.map +1 -1
  62. package/dist/client/locales/de-DE.d.ts.map +1 -1
  63. package/dist/client/locales/de-DE.js +137 -109
  64. package/dist/client/locales/de-DE.js.map +1 -1
  65. package/dist/client/locales/en-US.d.ts +2 -0
  66. package/dist/client/locales/en-US.d.ts.map +1 -1
  67. package/dist/client/locales/en-US.js +4 -2
  68. package/dist/client/locales/en-US.js.map +1 -1
  69. package/dist/client/locales/es-ES.d.ts.map +1 -1
  70. package/dist/client/locales/es-ES.js +214 -186
  71. package/dist/client/locales/es-ES.js.map +1 -1
  72. package/dist/client/locales/fr-FR.d.ts.map +1 -1
  73. package/dist/client/locales/fr-FR.js +146 -118
  74. package/dist/client/locales/fr-FR.js.map +1 -1
  75. package/dist/client/locales/id-ID.d.ts.map +1 -1
  76. package/dist/client/locales/id-ID.js +135 -107
  77. package/dist/client/locales/id-ID.js.map +1 -1
  78. package/dist/client/locales/index.d.ts.map +1 -1
  79. package/dist/client/locales/index.js +2 -0
  80. package/dist/client/locales/index.js.map +1 -1
  81. package/dist/client/locales/ja-JP.d.ts.map +1 -1
  82. package/dist/client/locales/ja-JP.js +218 -190
  83. package/dist/client/locales/ja-JP.js.map +1 -1
  84. package/dist/client/locales/ko-KR.d.ts +5 -0
  85. package/dist/client/locales/ko-KR.d.ts.map +1 -0
  86. package/dist/client/locales/ko-KR.js +1311 -0
  87. package/dist/client/locales/ko-KR.js.map +1 -0
  88. package/dist/client/locales/pt-BR.d.ts.map +1 -1
  89. package/dist/client/locales/pt-BR.js +208 -180
  90. package/dist/client/locales/pt-BR.js.map +1 -1
  91. package/dist/client/locales/ru-RU.d.ts +5 -0
  92. package/dist/client/locales/ru-RU.d.ts.map +1 -0
  93. package/dist/client/locales/ru-RU.js +1341 -0
  94. package/dist/client/locales/ru-RU.js.map +1 -0
  95. package/dist/client/locales/runWait.en-US.d.ts +2 -0
  96. package/dist/client/locales/runWait.en-US.d.ts.map +1 -1
  97. package/dist/client/locales/runWait.en-US.js +2 -0
  98. package/dist/client/locales/runWait.en-US.js.map +1 -1
  99. package/dist/client/locales/runWait.zh-CN.d.ts.map +1 -1
  100. package/dist/client/locales/runWait.zh-CN.js +2 -0
  101. package/dist/client/locales/runWait.zh-CN.js.map +1 -1
  102. package/dist/client/locales/runtimes.en-US.d.ts +1 -0
  103. package/dist/client/locales/runtimes.en-US.d.ts.map +1 -1
  104. package/dist/client/locales/runtimes.en-US.js +3 -2
  105. package/dist/client/locales/runtimes.en-US.js.map +1 -1
  106. package/dist/client/locales/runtimes.zh-CN.d.ts.map +1 -1
  107. package/dist/client/locales/runtimes.zh-CN.js +3 -2
  108. package/dist/client/locales/runtimes.zh-CN.js.map +1 -1
  109. package/dist/client/locales/vi-VN.d.ts.map +1 -1
  110. package/dist/client/locales/vi-VN.js +174 -146
  111. package/dist/client/locales/vi-VN.js.map +1 -1
  112. package/dist/client/locales/zh-CN.d.ts.map +1 -1
  113. package/dist/client/locales/zh-CN.js +4 -2
  114. package/dist/client/locales/zh-CN.js.map +1 -1
  115. package/dist/client/locales/zh-TW.d.ts.map +1 -1
  116. package/dist/client/locales/zh-TW.js +151 -123
  117. package/dist/client/locales/zh-TW.js.map +1 -1
  118. package/dist/client/pages/agents/agent-dialog.js +4 -1
  119. package/dist/client/pages/agents/agent-dialog.js.map +1 -1
  120. package/dist/client/pages/runtimes/quota.d.ts +3 -2
  121. package/dist/client/pages/runtimes/quota.d.ts.map +1 -1
  122. package/dist/client/pages/runtimes/quota.js +8 -3
  123. package/dist/client/pages/runtimes/quota.js.map +1 -1
  124. package/dist/client/pages/runtimes/runner-runs.d.ts.map +1 -1
  125. package/dist/client/pages/runtimes/runner-runs.js +2 -8
  126. package/dist/client/pages/runtimes/runner-runs.js.map +1 -1
  127. package/dist/client/pages/runtimes/runner-tools.d.ts.map +1 -1
  128. package/dist/client/pages/runtimes/runner-tools.js +7 -16
  129. package/dist/client/pages/runtimes/runner-tools.js.map +1 -1
  130. package/dist/client/pages/runtimes/workspace-diagnostics.js +3 -3
  131. package/dist/client/pages/runtimes/workspace-diagnostics.js.map +1 -1
  132. package/dist/client/pages/usage/date-range-picker.d.ts.map +1 -1
  133. package/dist/client/pages/usage/date-range-picker.js +3 -1
  134. package/dist/client/pages/usage/date-range-picker.js.map +1 -1
  135. package/dist/client/routes.d.ts.map +1 -1
  136. package/dist/client/routes.js +7 -0
  137. package/dist/client/routes.js.map +1 -1
  138. package/dist/client/runs/format-wait.d.ts.map +1 -1
  139. package/dist/client/runs/format-wait.js +2 -0
  140. package/dist/client/runs/format-wait.js.map +1 -1
  141. package/dist/database/migrations/.manifest.json +16 -0
  142. package/dist/database/migrations/202611070001_ag_run_runner_pin.d.ts +4 -0
  143. package/dist/database/migrations/202611070001_ag_run_runner_pin.d.ts.map +1 -0
  144. package/dist/database/migrations/202611070001_ag_run_runner_pin.js +17 -0
  145. package/dist/database/migrations/202611070001_ag_run_runner_pin.js.map +1 -0
  146. package/dist/database/migrations/202611080001_ag_conversation_shares.d.ts +4 -0
  147. package/dist/database/migrations/202611080001_ag_conversation_shares.d.ts.map +1 -0
  148. package/dist/database/migrations/202611080001_ag_conversation_shares.js +25 -0
  149. package/dist/database/migrations/202611080001_ag_conversation_shares.js.map +1 -0
  150. package/dist/database/migrations/202611080002_ag_run_failure_params.d.ts +4 -0
  151. package/dist/database/migrations/202611080002_ag_run_failure_params.d.ts.map +1 -0
  152. package/dist/database/migrations/202611080002_ag_run_failure_params.js +17 -0
  153. package/dist/database/migrations/202611080002_ag_run_failure_params.js.map +1 -0
  154. package/dist/database/migrations/202611090001_ag_runner_preference.d.ts +9 -0
  155. package/dist/database/migrations/202611090001_ag_runner_preference.d.ts.map +1 -0
  156. package/dist/database/migrations/202611090001_ag_runner_preference.js +28 -0
  157. package/dist/database/migrations/202611090001_ag_runner_preference.js.map +1 -0
  158. package/dist/package.json +1 -1
  159. package/dist/server/composition.d.ts +7 -1
  160. package/dist/server/composition.d.ts.map +1 -1
  161. package/dist/server/composition.js +14 -2
  162. package/dist/server/composition.js.map +1 -1
  163. package/dist/server/core/agents/agent.service.d.ts +2 -1
  164. package/dist/server/core/agents/agent.service.d.ts.map +1 -1
  165. package/dist/server/core/agents/agent.service.js.map +1 -1
  166. package/dist/server/core/agents/agent.store.d.ts +2 -2
  167. package/dist/server/core/agents/agent.store.d.ts.map +1 -1
  168. package/dist/server/core/agents/agent.store.js +8 -2
  169. package/dist/server/core/agents/agent.store.js.map +1 -1
  170. package/dist/server/core/brief/brief.d.ts +10 -5
  171. package/dist/server/core/brief/brief.d.ts.map +1 -1
  172. package/dist/server/core/brief/brief.js +36 -13
  173. package/dist/server/core/brief/brief.js.map +1 -1
  174. package/dist/server/core/brief/index.d.ts +1 -1
  175. package/dist/server/core/brief/index.d.ts.map +1 -1
  176. package/dist/server/core/brief/index.js +1 -1
  177. package/dist/server/core/brief/index.js.map +1 -1
  178. package/dist/server/core/conversations/attachments.d.ts +2 -12
  179. package/dist/server/core/conversations/attachments.d.ts.map +1 -1
  180. package/dist/server/core/conversations/attachments.js +56 -14
  181. package/dist/server/core/conversations/attachments.js.map +1 -1
  182. package/dist/server/core/conversations/chat-settings.d.ts +1 -1
  183. package/dist/server/core/conversations/chat-settings.d.ts.map +1 -1
  184. package/dist/server/core/conversations/chat-settings.js +6 -0
  185. package/dist/server/core/conversations/chat-settings.js.map +1 -1
  186. package/dist/server/core/conversations/conversation.service.d.ts +3 -1
  187. package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
  188. package/dist/server/core/conversations/conversation.service.js +212 -21
  189. package/dist/server/core/conversations/conversation.service.js.map +1 -1
  190. package/dist/server/core/conversations/conversation.store.d.ts +2 -0
  191. package/dist/server/core/conversations/conversation.store.d.ts.map +1 -1
  192. package/dist/server/core/conversations/conversation.store.js.map +1 -1
  193. package/dist/server/core/conversations/index.d.ts +1 -0
  194. package/dist/server/core/conversations/index.d.ts.map +1 -1
  195. package/dist/server/core/conversations/links.d.ts +3 -0
  196. package/dist/server/core/conversations/links.d.ts.map +1 -1
  197. package/dist/server/core/conversations/links.js +43 -5
  198. package/dist/server/core/conversations/links.js.map +1 -1
  199. package/dist/server/core/conversations/ports.d.ts +8 -2
  200. package/dist/server/core/conversations/ports.d.ts.map +1 -1
  201. package/dist/server/core/conversations/shares.d.ts +38 -0
  202. package/dist/server/core/conversations/shares.d.ts.map +1 -0
  203. package/dist/server/core/conversations/shares.js +189 -0
  204. package/dist/server/core/conversations/shares.js.map +1 -0
  205. package/dist/server/core/runs/claim.d.ts +22 -3
  206. package/dist/server/core/runs/claim.d.ts.map +1 -1
  207. package/dist/server/core/runs/claim.js +260 -42
  208. package/dist/server/core/runs/claim.js.map +1 -1
  209. package/dist/server/core/runs/extensions.d.ts +30 -2
  210. package/dist/server/core/runs/extensions.d.ts.map +1 -1
  211. package/dist/server/core/runs/extensions.js +18 -1
  212. package/dist/server/core/runs/extensions.js.map +1 -1
  213. package/dist/server/core/runs/git-credentials.d.ts.map +1 -1
  214. package/dist/server/core/runs/git-credentials.js +24 -2
  215. package/dist/server/core/runs/git-credentials.js.map +1 -1
  216. package/dist/server/core/runs/identity.d.ts.map +1 -1
  217. package/dist/server/core/runs/identity.js +9 -0
  218. package/dist/server/core/runs/identity.js.map +1 -1
  219. package/dist/server/core/runs/index.d.ts +3 -2
  220. package/dist/server/core/runs/index.d.ts.map +1 -1
  221. package/dist/server/core/runs/index.js +2 -1
  222. package/dist/server/core/runs/index.js.map +1 -1
  223. package/dist/server/core/runs/lifecycle.d.ts.map +1 -1
  224. package/dist/server/core/runs/lifecycle.js +14 -0
  225. package/dist/server/core/runs/lifecycle.js.map +1 -1
  226. package/dist/server/core/runs/npm-credentials.d.ts +35 -0
  227. package/dist/server/core/runs/npm-credentials.d.ts.map +1 -0
  228. package/dist/server/core/runs/npm-credentials.js +83 -0
  229. package/dist/server/core/runs/npm-credentials.js.map +1 -0
  230. package/dist/server/core/runs/ports.d.ts +19 -2
  231. package/dist/server/core/runs/ports.d.ts.map +1 -1
  232. package/dist/server/core/runs/ports.js.map +1 -1
  233. package/dist/server/core/runs/preview.d.ts +1 -4
  234. package/dist/server/core/runs/preview.d.ts.map +1 -1
  235. package/dist/server/core/runs/preview.js +10 -5
  236. package/dist/server/core/runs/preview.js.map +1 -1
  237. package/dist/server/core/runs/quota-wait.d.ts +7 -0
  238. package/dist/server/core/runs/quota-wait.d.ts.map +1 -0
  239. package/dist/server/core/runs/quota-wait.js +56 -0
  240. package/dist/server/core/runs/quota-wait.js.map +1 -0
  241. package/dist/server/core/runs/run-data-purge.d.ts.map +1 -1
  242. package/dist/server/core/runs/run-data-purge.js +1 -0
  243. package/dist/server/core/runs/run-data-purge.js.map +1 -1
  244. package/dist/server/core/runs/run.service.d.ts +29 -5
  245. package/dist/server/core/runs/run.service.d.ts.map +1 -1
  246. package/dist/server/core/runs/run.service.js +101 -10
  247. package/dist/server/core/runs/run.service.js.map +1 -1
  248. package/dist/server/core/runs/run.store.d.ts +24 -0
  249. package/dist/server/core/runs/run.store.d.ts.map +1 -1
  250. package/dist/server/core/runs/run.store.js +44 -0
  251. package/dist/server/core/runs/run.store.js.map +1 -1
  252. package/dist/server/core/runs/sweeper.d.ts.map +1 -1
  253. package/dist/server/core/runs/sweeper.js +2 -0
  254. package/dist/server/core/runs/sweeper.js.map +1 -1
  255. package/dist/server/core/runs/transitions.d.ts +1 -0
  256. package/dist/server/core/runs/transitions.d.ts.map +1 -1
  257. package/dist/server/core/runs/transitions.js +15 -1
  258. package/dist/server/core/runs/transitions.js.map +1 -1
  259. package/dist/server/core/runs/workload.d.ts +2 -2
  260. package/dist/server/core/runs/workload.d.ts.map +1 -1
  261. package/dist/server/core/runs/workload.js +62 -9
  262. package/dist/server/core/runs/workload.js.map +1 -1
  263. package/dist/server/kernel/clock.d.ts +2 -0
  264. package/dist/server/kernel/clock.d.ts.map +1 -1
  265. package/dist/server/kernel/clock.js +14 -0
  266. package/dist/server/kernel/clock.js.map +1 -1
  267. package/dist/server/kernel/events.d.ts +6 -1
  268. package/dist/server/kernel/events.d.ts.map +1 -1
  269. package/dist/server/kernel/events.js.map +1 -1
  270. package/dist/server/locales/de-DE.js +3 -3
  271. package/dist/server/locales/de-DE.js.map +1 -1
  272. package/dist/server/locales/es-ES.js +6 -6
  273. package/dist/server/locales/es-ES.js.map +1 -1
  274. package/dist/server/locales/fr-FR.js +4 -4
  275. package/dist/server/locales/fr-FR.js.map +1 -1
  276. package/dist/server/locales/id-ID.js +5 -5
  277. package/dist/server/locales/id-ID.js.map +1 -1
  278. package/dist/server/locales/index.d.ts.map +1 -1
  279. package/dist/server/locales/index.js +2 -0
  280. package/dist/server/locales/index.js.map +1 -1
  281. package/dist/server/locales/ja-JP.d.ts.map +1 -1
  282. package/dist/server/locales/ja-JP.js +6 -6
  283. package/dist/server/locales/ja-JP.js.map +1 -1
  284. package/dist/server/locales/ko-KR.d.ts +5 -0
  285. package/dist/server/locales/ko-KR.d.ts.map +1 -0
  286. package/dist/server/locales/ko-KR.js +59 -0
  287. package/dist/server/locales/ko-KR.js.map +1 -0
  288. package/dist/server/locales/pt-BR.js +6 -6
  289. package/dist/server/locales/pt-BR.js.map +1 -1
  290. package/dist/server/locales/ru-RU.d.ts +5 -0
  291. package/dist/server/locales/ru-RU.d.ts.map +1 -0
  292. package/dist/server/locales/ru-RU.js +59 -0
  293. package/dist/server/locales/ru-RU.js.map +1 -0
  294. package/dist/server/locales/vi-VN.js +6 -6
  295. package/dist/server/locales/vi-VN.js.map +1 -1
  296. package/dist/server/locales/zh-TW.d.ts.map +1 -1
  297. package/dist/server/locales/zh-TW.js +6 -6
  298. package/dist/server/locales/zh-TW.js.map +1 -1
  299. package/dist/server/online/agent/persistence/store.d.ts.map +1 -1
  300. package/dist/server/online/agent/persistence/store.js +3 -0
  301. package/dist/server/online/agent/persistence/store.js.map +1 -1
  302. package/dist/server/online/agent/runner/backend.d.ts.map +1 -1
  303. package/dist/server/online/agent/runner/backend.js +17 -8
  304. package/dist/server/online/agent/runner/backend.js.map +1 -1
  305. package/dist/server/online/agent/runner/projection.d.ts +1 -1
  306. package/dist/server/online/agent/runner/projection.d.ts.map +1 -1
  307. package/dist/server/online/agent/runner/projection.js +8 -1
  308. package/dist/server/online/agent/runner/projection.js.map +1 -1
  309. package/dist/server/providers/agents.d.ts.map +1 -1
  310. package/dist/server/providers/agents.js +3 -0
  311. package/dist/server/providers/agents.js.map +1 -1
  312. package/dist/server/routes/admin.d.ts +1 -0
  313. package/dist/server/routes/admin.d.ts.map +1 -1
  314. package/dist/server/routes/admin.js +32 -2
  315. package/dist/server/routes/admin.js.map +1 -1
  316. package/dist/server/routes/chat.d.ts.map +1 -1
  317. package/dist/server/routes/chat.js +99 -16
  318. package/dist/server/routes/chat.js.map +1 -1
  319. package/dist/server/routes/index.d.ts.map +1 -1
  320. package/dist/server/routes/index.js +8 -2
  321. package/dist/server/routes/index.js.map +1 -1
  322. package/dist/server/routes/run.js +1 -1
  323. package/dist/server/routes/run.js.map +1 -1
  324. package/dist/server/routes/runners/admin.d.ts.map +1 -1
  325. package/dist/server/routes/runners/admin.js +3 -3
  326. package/dist/server/routes/runners/admin.js.map +1 -1
  327. package/dist/server/routes/runners/install.d.ts.map +1 -1
  328. package/dist/server/routes/runners/install.js +5 -0
  329. package/dist/server/routes/runners/install.js.map +1 -1
  330. package/dist/server/routes/runners/runner.d.ts.map +1 -1
  331. package/dist/server/routes/runners/runner.js +13 -22
  332. package/dist/server/routes/runners/runner.js.map +1 -1
  333. package/dist/server/routes/runners/runs.d.ts +1 -1
  334. package/dist/server/routes/runners/runs.d.ts.map +1 -1
  335. package/dist/server/routes/runners/runs.js +20 -1
  336. package/dist/server/routes/runners/runs.js.map +1 -1
  337. package/dist/server/routes/schemas.d.ts +5 -1
  338. package/dist/server/routes/schemas.d.ts.map +1 -1
  339. package/dist/server/routes/schemas.js +33 -8
  340. package/dist/server/routes/schemas.js.map +1 -1
  341. package/dist/server/runners/runner.service.d.ts.map +1 -1
  342. package/dist/server/runners/runner.service.js +17 -5
  343. package/dist/server/runners/runner.service.js.map +1 -1
  344. package/dist/server/tokens.d.ts +1 -1
  345. package/dist/server/tokens.d.ts.map +1 -1
  346. package/dist/server/tokens.js.map +1 -1
  347. package/dist/shared/briefs.d.ts +2 -6
  348. package/dist/shared/briefs.d.ts.map +1 -1
  349. package/dist/shared/conversations.d.ts +38 -1
  350. package/dist/shared/conversations.d.ts.map +1 -1
  351. package/dist/shared/conversations.js +10 -0
  352. package/dist/shared/conversations.js.map +1 -1
  353. package/dist/shared/runs.d.ts +11 -3
  354. package/dist/shared/runs.d.ts.map +1 -1
  355. package/dist/shared/runs.js +2 -0
  356. package/dist/shared/runs.js.map +1 -1
  357. package/dist/shared/ui-message.d.ts +2 -2
  358. package/dist/shared/ui-message.d.ts.map +1 -1
  359. package/dist/shared/ui-message.js +1 -1
  360. package/dist/shared/ui-message.js.map +1 -1
  361. package/docs/conversation-sharing.md +55 -0
  362. package/package.json +16 -16
  363. package/skills/nocobase-app-plugin-agents/references/extension-points.md +2 -0
  364. package/skills/nocobase-app-plugin-agents/references/runners.md +1 -1
  365. package/skills/nocobase-app-plugin-agents/references/verification-and-troubleshooting.md +2 -1
@@ -19,6 +19,17 @@ import { ACTIVE_JOB, jobsRepo } from '../../jobs/job.store.js';
19
19
  * agent's entries whose tool the runner runs and has room for, so an agent listing two tools runs with the second while
20
20
  * the first is full; a run none of whose tools has room is passed over for the runs behind it.
21
21
  *
22
+ * Runners share a queue in proportion to each tool's limit, not by who asks first: before it locks anything, a claim
23
+ * waits until the run has been queued for the share of the tool's limit the runner already runs, times one second
24
+ * (`claimPace`). The runners' route hands out one run per request and the runner asks again at once, so of several
25
+ * runners woken for the same runs, the least busy with each tool takes the next one. A request that ends while it
26
+ * waits takes nothing.
27
+ *
28
+ * A run whose actor prefers one kind of runner (`runnerPreference`, `personal` or `team`, noted when it was queued)
29
+ * makes the other kind wait one second more (`preferenceDelay`), so an idle runner of the preferred kind takes it
30
+ * first, and one of the other kind takes it about a second later when none can. Work that only one runner may take
31
+ * (pinned, reserved, continuing where its session is, or for shared runners only) adds no wait. Nothing else is read.
32
+ *
22
33
  * Variables are checked and opened in the claim's own transaction, on its connection: which ones the assembled payload
23
34
  * gets, whether any is for shared runners only, and their values, so what the runner receives is what was checked. The
24
35
  * names taken from the runner go in `workspace.passthrough`, without values.
@@ -45,10 +56,10 @@ import { ACTIVE_JOB, jobsRepo } from '../../jobs/job.store.js';
45
56
  */
46
57
  import { GLOBAL_SKILL_TARGET } from '../../../shared/skills.js';
47
58
  import { ANNOUNCED_FAILURES, isProtocolSupported, policyAllowsAgent, policyAllowsRepo, policyAllowsSubject, TIMINGS, } from '@nocobase/agent-protocol';
48
- import { chatEffortCapabilities, runnerLowOnDisk, runsEntry, toolLimit, supportsWorkspaceKey, } from '../../../shared/runners.js';
59
+ import { chatEffortCapabilities, quotaForAgent, runnerLowOnDisk, runsEntry, toolLimit, supportsWorkspaceKey, } from '../../../shared/runners.js';
49
60
  import { queueStart } from './selection.js';
50
61
  import { createHash } from 'node:crypto';
51
- import { later } from '../../kernel/clock.js';
62
+ import { later, pause } from '../../kernel/clock.js';
52
63
  import { notFound } from '../../kernel/errors.js';
53
64
  import { runSecretsKey } from '../../kernel/redaction.js';
54
65
  import { cleanList, covers, jsonObject } from '../../kernel/values.js';
@@ -57,7 +68,7 @@ import { joinBrief, renderBrief } from '../brief/index.js';
57
68
  import { cliPackageFor } from './cli-package.js';
58
69
  import { executionHistory } from './execution.js';
59
70
  import { describeVariables, variableRefs, } from '../variables/index.js';
60
- import { mountsFor, prepareExtensions, repoAccessFor, responsibleOf, sessionKeyOf, withSections, } from './extensions.js';
71
+ import { mountsFor, prepareExtensions, repoAccessFor, RepoAccessError, responsibleOf, sessionKeyOf, withSections, } from './extensions.js';
61
72
  import { MAX_CLAIM_FAILURES, toolPolicyFor } from './policy.js';
62
73
  import { mintRunToken } from './run-tokens.js';
63
74
  import { ACTIVE, countActive, eventsRepo, findRunRecord, markDelivered, pendingInputs, runsOfKey, runsRepo, inputsRepo, sessionsRepo, toInput, toExecutions, toRun, } from './run.store.js';
@@ -71,6 +82,11 @@ import { consumeReset, saveBrief } from './workspace.store.js';
71
82
  const CANDIDATES = 50;
72
83
  /** The initial page plus at most two further pages per claim request, shared by all free slots. */
73
84
  const CANDIDATE_PAGES = 3;
85
+ /**
86
+ * What a runner waits for a run when it already runs its tool at the limit, and what one of the kind of runner the
87
+ * run's actor does not prefer waits on top (`preferenceDelay`).
88
+ */
89
+ const PACE_MS = 1000;
74
90
  export function createClaimScan() {
75
91
  return { remainingPages: CANDIDATE_PAGES };
76
92
  }
@@ -109,6 +125,43 @@ function toolRoom(tools) {
109
125
  },
110
126
  };
111
127
  }
128
+ /**
129
+ * How long `runner` waits before it takes `run` with `tool`, at `now`: the share of the tool's limit it already runs
130
+ * (`toolLimit`), of the busier of what this application counts (`byTool`) and what the runner last reported
131
+ * (`Runner.toolLoad`), times one second, counted from when the run entered the queue (`queueStart`). Several runners
132
+ * woken for one run take it in order of how busy each is with its tool, whichever asked first and however many runs
133
+ * each took before; an idle runner, and work queued longer than its share, wait nothing.
134
+ */
135
+ export function claimPace(runner, tool, byTool, run, now,
136
+ /** Added to the share and counted from the same moment (`preferenceDelay`). */
137
+ extraMs = 0) {
138
+ const limit = toolLimit(runner, tool);
139
+ if (limit <= 0)
140
+ return 0;
141
+ const load = runner.toolLoad?.[tool];
142
+ const used = Math.max(byTool[tool] ?? 0, load ? load.slots - load.free : 0);
143
+ const share = (Math.min(used, limit) / limit) * PACE_MS + extraMs;
144
+ return Math.min(share, Math.max(0, queueStart(run) + share - now));
145
+ }
146
+ /**
147
+ * What `runner` waits on top of its share (`claimPace`) for `run`, from the kind of runner its actor prefers: one
148
+ * second when the run prefers the other kind, nothing otherwise. Nothing either when only this runner may take it
149
+ * (`bound`: pinned, reserved or continuing where its session is), or when it prefers personal runners but has
150
+ * variables for shared runners only, which no personal runner may receive.
151
+ */
152
+ export function preferenceDelay(runner, run, bound) {
153
+ const preferred = run.runnerPreference;
154
+ if (preferred !== 'personal' && preferred !== 'team')
155
+ return 0;
156
+ if (bound || run.runnerPinId)
157
+ return 0;
158
+ if (preferred === (runner.trust === 'team' ? 'team' : 'personal'))
159
+ return 0;
160
+ if (preferred === 'personal' &&
161
+ variableRefs(run.teamOnlyVariables).length > 0)
162
+ return 0;
163
+ return PACE_MS;
164
+ }
112
165
  /**
113
166
  * The entry `runner` takes a run of `agent` with: the first whose tool it has enabled, installed and signed in, with
114
167
  * the entry's model among those it reports (`runsEntry`). Null when it has none of them.
@@ -146,9 +199,24 @@ export function fitsActor(runner, agent, actorUserId, requires, now) {
146
199
  return false;
147
200
  return runner.trust === 'team' || runner.ownerUserId === actorUserId;
148
201
  }
202
+ /** A strict continuation keeps the tool/model that owns its original session. */
203
+ function quotaAgent(run, agent) {
204
+ if (!run.runnerPinId || run.freshSession)
205
+ return agent;
206
+ const last = toExecutions(run.executionHistory).at(-1);
207
+ const tool = last?.tool ?? run.tool;
208
+ const model = last ? last.model : run.model;
209
+ return tool
210
+ ? {
211
+ ...agent,
212
+ modelEntries: agent.modelEntries.filter((entry) => entry.tool === tool && entry.model === model),
213
+ }
214
+ : agent;
215
+ }
149
216
  /** Whether `runner` may run `run` of `agent`, before any lock is taken and before its payload is known. */
150
217
  export function fits(runner, agent, run, now) {
151
- return fitsActor(runner, agent, run.actorUserId, toRun(run).requires, now);
218
+ return ((!run.runnerPinId || run.runnerPinId === runner.id) &&
219
+ fitsActor(runner, quotaAgent(run, agent), run.actorUserId, toRun(run).requires, now));
152
220
  }
153
221
  /** The runner features a payload needs. */
154
222
  export function payloadRequires(dirs, skills, variables, workspaceKey = false) {
@@ -185,7 +253,7 @@ export function skillTargets(_agent, assembly) {
185
253
  ...assembly.dirs.flatMap((dir) => dir.scopeId ? [{ scope: 'workdir', scopeId: dir.scopeId }] : []),
186
254
  ];
187
255
  }
188
- function fingerprint(agent, entry, layers, skills) {
256
+ function fingerprint(agent, entry, layers, skills, readOnly) {
189
257
  return createHash('sha256')
190
258
  .update(JSON.stringify([
191
259
  entry.tool,
@@ -194,12 +262,16 @@ function fingerprint(agent, entry, layers, skills) {
194
262
  layers.system,
195
263
  layers.agent,
196
264
  skills.map((skill) => [skill.slug, skill.hash]),
265
+ // Keep existing writable fingerprints unchanged across upgrades.
266
+ ...(readOnly ? [{ readOnly: true }] : []),
197
267
  ]))
198
268
  .digest('hex');
199
269
  }
200
270
  /** The brief for `assembly`, with the skills a run would get. */
201
271
  export function briefOf(agent, assembly, skills, cli, appName) {
202
- const rendered = renderBrief({
272
+ // Custom system prompts (such as conversations) keep their own layout.
273
+ const stablePrefix = assembly.briefLayout === 'stable-prefix' && assembly.system === undefined;
274
+ const input = {
203
275
  agentName: agent.name,
204
276
  subject: { noun: assembly.subject.noun, key: assembly.subject.key },
205
277
  task: assembly.task,
@@ -210,14 +282,20 @@ export function briefOf(agent, assembly, skills, cli, appName) {
210
282
  cli,
211
283
  appName,
212
284
  ...(assembly.guidance ? { guidance: assembly.guidance } : {}),
213
- });
285
+ };
286
+ const rendered = renderBrief(input);
214
287
  const layers = assembly.system === undefined
215
288
  ? rendered
216
289
  : { ...rendered, system: assembly.system };
217
290
  const turn = assembly.turn.previousSummary
218
291
  ? `${assembly.turn.prompt}\n\nWhat earlier runs on this already did:\n\n${assembly.turn.previousSummary}`
219
292
  : assembly.turn.prompt;
220
- return { layers, turn, prompt: joinBrief(layers) };
293
+ // Keep the logical layers (and their session fingerprint) unchanged. Only the outgoing text is reordered,
294
+ // so an ordinary follow-up can still resume the session created by an independent review.
295
+ const prompt = stablePrefix
296
+ ? joinBrief(renderBrief({ ...input, stablePrefix: true }), true)
297
+ : joinBrief(layers);
298
+ return { layers, turn, prompt };
221
299
  }
222
300
  /** Whether `runner` asks for repository credentials on demand rather than taking them with the claim. */
223
301
  export function asksCredentialsOnDemand(runner) {
@@ -330,8 +408,16 @@ export function createClaimService(deps) {
330
408
  const installed = connected.filter((runner) => runner.tools.some((tool) => tool.kind === entry.tool));
331
409
  const usable = installed.filter((runner) => runsEntry(runner, entry, clock.now().getTime()));
332
410
  const online = usable.filter((runner) => runner.trust === 'team' || runner.ownerUserId === run.actorUserId);
411
+ const quota = usable.length
412
+ ? []
413
+ : quotaForAgent(installed, single, run.actorUserId, now.getTime());
333
414
  const compatible = [];
334
- const loads = [];
415
+ const loads = quota.map((runner) => ({
416
+ name: runner.runnerName,
417
+ ownerUserId: installed.find((item) => item.id === runner.runnerId)
418
+ .ownerUserId,
419
+ reason: 'toolQuota',
420
+ }));
335
421
  let unknown = false;
336
422
  let incompatible = false;
337
423
  let ready = false;
@@ -429,11 +515,13 @@ export function createClaimService(deps) {
429
515
  : 'restricted'
430
516
  : usable.length
431
517
  ? 'noSharedRunner'
432
- : installed.length
433
- ? 'toolUnavailable'
434
- : connected.length
435
- ? 'toolNotInstalled'
436
- : 'offline';
518
+ : quota.length
519
+ ? 'toolQuota'
520
+ : installed.length
521
+ ? 'toolUnavailable'
522
+ : connected.length
523
+ ? 'toolNotInstalled'
524
+ : 'offline';
437
525
  if (!ready &&
438
526
  compatible.length > 0 &&
439
527
  loads.every((runner) => runner.reason === 'updatePending' || runner.reason === 'pruning'))
@@ -454,6 +542,13 @@ export function createClaimService(deps) {
454
542
  tool: entry.tool,
455
543
  model: entry.model,
456
544
  reason,
545
+ ...(reason === 'toolQuota'
546
+ ? {
547
+ until: quota
548
+ .flatMap((runner) => runner.resetsAt ? [runner.resetsAt] : [])
549
+ .sort()[0] ?? null,
550
+ }
551
+ : {}),
457
552
  compatibleCount: compatible.length,
458
553
  blockers: [
459
554
  ...(unknown ? ['unknown'] : []),
@@ -667,6 +762,7 @@ export function createClaimService(deps) {
667
762
  const token = await mintRunToken(conn, deps, run, runner.id);
668
763
  const git = await repoAccessFor(conn, deps.repoAccess, {
669
764
  claim,
765
+ readOnly: assembly.readOnly === true,
670
766
  repos: dirs.filter((dir) => dir.kind === 'repo'),
671
767
  onDemand: asksCredentialsOnDemand(runner),
672
768
  tools: wrapsHostTools(runner),
@@ -683,16 +779,20 @@ export function createClaimService(deps) {
683
779
  });
684
780
  const render = async () => briefOf(agent, await withSections(conn, deps.sections, effectiveClaim, assembly, prepared), skills, deps.cli.name, deps.app.name);
685
781
  let brief = await render();
686
- const briefFingerprint = fingerprint(agent, entry, brief.layers, skills);
687
- const resume = !run.freshSession &&
688
- !clean &&
689
- session &&
690
- !session.poisoned &&
691
- session.fingerprint === briefFingerprint;
782
+ const briefFingerprint = fingerprint(agent, entry, brief.layers, skills, assembly.readOnly === true);
783
+ const strict = Boolean(run.runnerPinId && !run.freshSession);
784
+ const resume = strict
785
+ ? Boolean(run.sessionId)
786
+ : !run.freshSession &&
787
+ binding.sessionPolicy?.(effectiveClaim) !== 'fresh' &&
788
+ !clean &&
789
+ session &&
790
+ !session.poisoned &&
791
+ session.fingerprint === briefFingerprint;
692
792
  // A kept session that cannot be resumed: the new one is told what came before, as a first one is. The history
693
793
  // is outside the fingerprint, so it does not change.
694
794
  let recoveryContext;
695
- if (session) {
795
+ if (session && !strict) {
696
796
  const fresh = await binding.context.assemble(conn, { ...claim, freshSession: true }, prepared.get('subject'));
697
797
  if (resume) {
698
798
  // Should the kept session be gone on the runner, the new one hears what came before from this.
@@ -726,6 +826,11 @@ export function createClaimService(deps) {
726
826
  'The previous attempt was interrupted by a runner upgrade. Continue from the current state.',
727
827
  ]
728
828
  : []),
829
+ ...(strict
830
+ ? [
831
+ 'Quota recovery: continue the interrupted step from the existing session, progress and files.',
832
+ ]
833
+ : []),
729
834
  ...resumeNotes,
730
835
  ...(sessionNote ? [sessionNote] : []),
731
836
  ].join('\n\n');
@@ -738,6 +843,7 @@ export function createClaimService(deps) {
738
843
  createdAt: nowText,
739
844
  });
740
845
  const payload = {
846
+ ...(strict ? { session: { strict: true } } : {}),
741
847
  ...(run.subjectKind === 'conversation' &&
742
848
  runner.features.includes('outputBlocks')
743
849
  ? { outputBlocks: true }
@@ -768,8 +874,14 @@ export function createClaimService(deps) {
768
874
  prompt: {
769
875
  system: brief.prompt,
770
876
  turn,
771
- session: resume ? 'resume' : 'fresh',
772
- ...(resume ? { resumeSessionId: session.sessionId } : {}),
877
+ session: strict || resume ? 'resume' : 'fresh',
878
+ ...(strict
879
+ ? run.sessionId
880
+ ? { resumeSessionId: run.sessionId }
881
+ : {}
882
+ : resume && session
883
+ ? { resumeSessionId: session.sessionId }
884
+ : {}),
773
885
  ...(resume && recoveryContext ? { recoveryContext } : {}),
774
886
  },
775
887
  inputs,
@@ -1033,7 +1145,11 @@ export function createClaimService(deps) {
1033
1145
  const pinned = await pin.runnerOf(conn, toRun(run));
1034
1146
  return pinned === null || pinned === runner.id;
1035
1147
  }
1036
- async function attempt(runner, agent, candidate, reported) {
1148
+ async function attempt(runner, agent, candidate, reported, pass,
1149
+ /** Only this runner may take it: no preference wait (`preferenceDelay`). */
1150
+ bound) {
1151
+ if (candidate.runnerPinId && candidate.runnerPinId !== runner.id)
1152
+ return { kind: 'skipped' };
1037
1153
  const conn = tx.read();
1038
1154
  // Rules that do not depend on who the run acts as come first, and settle the rest of the pass: an agent at its
1039
1155
  // limit is checked once per poll, not once per queued run.
@@ -1061,6 +1177,17 @@ export function createClaimService(deps) {
1061
1177
  const consent = await commentConsentFor(conn, runner, candidate);
1062
1178
  if (!consent.allowed)
1063
1179
  return { kind: 'skipped' };
1180
+ // Wait this runner's turn for the run, after every read-only rule and before any lock or write. The transaction
1181
+ // checks again whether the run is still queued and which tool has room.
1182
+ const paced = pickOpenEntry(runner, agent, (tool) => (pass.byTool[tool] ?? 0) < toolLimit(runner, tool) &&
1183
+ reported.has(tool), clock.now().getTime());
1184
+ if (!paced)
1185
+ return { kind: 'skipped' };
1186
+ const wait = claimPace(runner, paced.tool, pass.byTool, candidate, clock.now().getTime(), preferenceDelay(runner, candidate, bound));
1187
+ if (wait > 0)
1188
+ await (deps.pause ?? pause)(wait, pass.signal);
1189
+ if (pass.signal?.aborted)
1190
+ return { kind: 'aborted' };
1064
1191
  // Read once, before the transaction, and handed to every repository access hook of this claim. A failed read
1065
1192
  // leaves the run queued for the next poll rather than letting a provider pick an identity without it.
1066
1193
  let responsibleUserId = null;
@@ -1136,6 +1263,8 @@ export function createClaimService(deps) {
1136
1263
  status: 'dispatched',
1137
1264
  failureReason: null,
1138
1265
  failureDetail: null,
1266
+ failureParams: null,
1267
+ claimFailures: 0,
1139
1268
  runnerId: runner.id,
1140
1269
  // Brief providers observe this reservation inside the claim transaction.
1141
1270
  // Replace a previous attempt's tool before computing the roster.
@@ -1223,7 +1352,16 @@ export function createClaimService(deps) {
1223
1352
  : null,
1224
1353
  },
1225
1354
  });
1226
- unit.emit({ type: 'run.changed', runId: run.id, status: 'dispatched' });
1355
+ if (current.runnerPinId && !current.freshSession)
1356
+ await deps.subjects
1357
+ .get(run.subjectKind)
1358
+ ?.sink?.onQuotaResumed?.(unit, toRun(run));
1359
+ unit.emit({
1360
+ type: 'run.changed',
1361
+ ...(current.runnerPinId ? { quotaWait: false } : {}),
1362
+ runId: run.id,
1363
+ status: 'dispatched',
1364
+ });
1227
1365
  if (variableRefs(candidate.teamOnlyVariables).length > 0)
1228
1366
  unit.emit({
1229
1367
  type: 'notice.cleared',
@@ -1266,28 +1404,98 @@ export function createClaimService(deps) {
1266
1404
  }
1267
1405
  }
1268
1406
  }
1269
- /** Counts a failed assembly; the run fails once it has failed too often. */
1270
- async function recordFailure(runId, error) {
1271
- deps.onClaimFailure?.(runId, error.cause);
1407
+ /** Repository failures have their own budget; ordinary assembly failures keep the existing three-try limit. */
1408
+ async function recordFailure(candidate, error) {
1409
+ deps.onClaimFailure?.(candidate.id, error.cause);
1272
1410
  await tx.run(async (unit) => {
1273
- const run = await findRunRecord(unit.conn, runId);
1274
- if (!run || run.status !== 'queued')
1411
+ const run = await findRunRecord(unit.conn, candidate.id);
1412
+ if (!run ||
1413
+ run.status !== 'queued' ||
1414
+ run.revision !== candidate.revision ||
1415
+ Number(run.attempt) !== Number(candidate.attempt) ||
1416
+ run.actorUserId !== candidate.actorUserId ||
1417
+ !(await queuedIdentityCurrent(unit.conn, deps, run)))
1418
+ return;
1419
+ const previous = jsonObject(run.failureParams);
1420
+ const cause = error.cause;
1421
+ if (cause instanceof RepoAccessError) {
1422
+ const now = clock.now().getTime();
1423
+ const repoFailures = Number(previous.repoFailures ?? 0) + 1;
1424
+ const first = typeof previous.repoFirstFailureAt === 'string'
1425
+ ? Date.parse(previous.repoFirstFailureAt)
1426
+ : now;
1427
+ const retryAt = typeof cause.params.retryAt === 'string'
1428
+ ? Date.parse(cause.params.retryAt)
1429
+ : NaN;
1430
+ const delays = [1, 2, 4, 8, 15];
1431
+ const next = Math.max(now + (delays[repoFailures - 1] ?? 15) * 60_000, Number.isFinite(retryAt) ? retryAt : 0);
1432
+ const params = {
1433
+ ...cause.params,
1434
+ repoFailures,
1435
+ repoFirstFailureAt: new Date(first).toISOString(),
1436
+ };
1437
+ const reason = cause.kind === 'denied'
1438
+ ? 'repoAccessDenied'
1439
+ : 'repoAccessUnavailable';
1440
+ if (cause.kind === 'denied' ||
1441
+ repoFailures >= 6 ||
1442
+ next > first + 60 * 60_000) {
1443
+ await finishRun(unit, deps, run, {
1444
+ status: 'failed',
1445
+ reason,
1446
+ detail: cause.message,
1447
+ failureParams: params,
1448
+ }, true);
1449
+ return;
1450
+ }
1451
+ const availableAt = new Date(next).toISOString();
1452
+ const changed = await runsRepo(unit.conn).updateMany({
1453
+ filter: {
1454
+ id: run.id,
1455
+ status: 'queued',
1456
+ revision: run.revision,
1457
+ attempt: Number(run.attempt),
1458
+ actorUserId: run.actorUserId,
1459
+ },
1460
+ values: {
1461
+ availableAt,
1462
+ failureReason: reason,
1463
+ failureDetail: cause.message.slice(0, 10_000),
1464
+ failureParams: params,
1465
+ updatedAt: clock.now().toISOString(),
1466
+ },
1467
+ });
1468
+ if (changed.updatedCount > 0) {
1469
+ unit.emit({ type: 'run.changed', runId: run.id, status: 'queued' });
1470
+ unit.emit({ type: 'run.requeued', runId: run.id, availableAt });
1471
+ }
1275
1472
  return;
1473
+ }
1276
1474
  const failures = Number(run.claimFailures) + 1;
1475
+ // Preserve only the independent repository retry budget across an unrelated preparation failure.
1476
+ const budget = previous.repoFailures
1477
+ ? {
1478
+ repoFailures: previous.repoFailures,
1479
+ repoFirstFailureAt: previous.repoFirstFailureAt,
1480
+ repoAccess: previous.repoAccess ?? previous,
1481
+ }
1482
+ : null;
1277
1483
  if (failures >= MAX_CLAIM_FAILURES) {
1278
1484
  await finishRun(unit, deps, run, {
1279
1485
  status: 'failed',
1280
1486
  reason: 'setupFailed',
1281
1487
  detail: error.message,
1282
- });
1488
+ failureParams: null,
1489
+ }, true);
1283
1490
  return;
1284
1491
  }
1285
1492
  await runsRepo(unit.conn).updateMany({
1286
- filter: { id: runId },
1493
+ filter: { id: run.id, status: 'queued', revision: run.revision },
1287
1494
  values: {
1288
1495
  claimFailures: failures,
1289
1496
  failureReason: 'setupFailed',
1290
1497
  failureDetail: error.message.slice(0, 10_000),
1498
+ failureParams: budget,
1291
1499
  },
1292
1500
  });
1293
1501
  });
@@ -1511,8 +1719,8 @@ export function createClaimService(deps) {
1511
1719
  }
1512
1720
  return true;
1513
1721
  }
1514
- /** False stops the whole request when a transactional capacity check finds the runner full. */
1515
- async function claimPage(runner, seen, reported, queued, full) {
1722
+ /** False stops the whole request when a transactional capacity check finds the runner full, or the request ended. */
1723
+ async function claimPage(runner, seen, reported, queued, full, pass) {
1516
1724
  const conn = tx.read();
1517
1725
  const affinity = await affinityBatch(queued, runner);
1518
1726
  const agents = await findAgents(conn, queued
@@ -1524,10 +1732,13 @@ export function createClaimService(deps) {
1524
1732
  ? deps.runners.all(conn)
1525
1733
  : Promise.resolve([runner]));
1526
1734
  for (const candidate of queued) {
1527
- if (seen.has(candidate.id) || full.has(candidate.agentId))
1735
+ if (seen.has(candidate.id) ||
1736
+ full.has(candidate.agentId) ||
1737
+ (candidate.runnerPinId && candidate.runnerPinId !== runner.id))
1528
1738
  continue;
1529
1739
  seen.add(candidate.id);
1530
- const agent = agents.get(candidate.agentId);
1740
+ const configured = agents.get(candidate.agentId);
1741
+ const agent = configured ? quotaAgent(candidate, configured) : undefined;
1531
1742
  if (!agent ||
1532
1743
  (!deps.subjects.get(candidate.subjectKind)?.actsAs &&
1533
1744
  !fits(runner, agent, candidate, clock.now().getTime())))
@@ -1537,7 +1748,8 @@ export function createClaimService(deps) {
1537
1748
  continue;
1538
1749
  // Work bound to another runner waits for it; work bound here takes no other preference.
1539
1750
  const pin = deps.subjects.get(candidate.subjectKind)?.runnerPin;
1540
- const pinned = pin ? await pin.runnerOf(conn, toRun(candidate)) : null;
1751
+ const pinned = candidate.runnerPinId ??
1752
+ (pin ? await pin.runnerOf(conn, toRun(candidate)) : null);
1541
1753
  if (pinned !== null && pinned !== runner.id)
1542
1754
  continue;
1543
1755
  const preferred = affinity.preferred.get(candidate.id);
@@ -1551,9 +1763,13 @@ export function createClaimService(deps) {
1551
1763
  if (!pickOpenEntry(runner, agent, (tool) => reported.has(tool), clock.now().getTime()))
1552
1764
  continue;
1553
1765
  try {
1554
- const result = await attempt(runner, agent, candidate, reported);
1766
+ const result = await attempt(runner, agent, candidate, reported, pass, reserved === runner.id ||
1767
+ pinned !== null ||
1768
+ preferred?.id === runner.id);
1555
1769
  if (result.kind === 'claimed')
1556
1770
  return result.payload;
1771
+ if (result.kind === 'aborted')
1772
+ return false;
1557
1773
  if (result.kind === 'full') {
1558
1774
  if (result.scope === 'runner')
1559
1775
  return false;
@@ -1568,7 +1784,7 @@ export function createClaimService(deps) {
1568
1784
  continue;
1569
1785
  if (!(error instanceof AssemblyError))
1570
1786
  throw error;
1571
- await recordFailure(candidate.id, error);
1787
+ await recordFailure(candidate, error);
1572
1788
  }
1573
1789
  }
1574
1790
  return null;
@@ -1615,7 +1831,7 @@ export function createClaimService(deps) {
1615
1831
  const agent = await findAgent(conn, run.agentId);
1616
1832
  return agent ? selectionFacts(conn, toRun(run), agent, false) : null;
1617
1833
  },
1618
- async claim(runner, free, tools, scan = createClaimScan()) {
1834
+ async claim(runner, free, tools, scan = createClaimScan(), signal) {
1619
1835
  if (scan.remainingPages <= 0 ||
1620
1836
  runner.status !== 'online' ||
1621
1837
  runner.draining ||
@@ -1628,8 +1844,10 @@ export function createClaimService(deps) {
1628
1844
  const pages = candidates(runner, scan);
1629
1845
  let queued = [];
1630
1846
  while (payloads.length < limit) {
1631
- if ((await deps.slotsUsed(tx.read(), runner.id)) >= runner.slots)
1847
+ const held = await deps.slotsHeld(tx.read(), runner.id);
1848
+ if (held.runs + held.jobs >= runner.slots)
1632
1849
  break;
1850
+ const pass = { byTool: held.byTool ?? {}, signal };
1633
1851
  const full = new Set();
1634
1852
  let payload;
1635
1853
  do {
@@ -1639,7 +1857,7 @@ export function createClaimService(deps) {
1639
1857
  return payloads;
1640
1858
  queued = page.value;
1641
1859
  }
1642
- payload = await claimPage(runner, seen, reported, queued, full);
1860
+ payload = await claimPage(runner, seen, reported, queued, full, pass);
1643
1861
  if (payload === false)
1644
1862
  return payloads;
1645
1863
  if (!payload)