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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (631) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +20 -2
  4. package/bootstrap.json +22 -0
  5. package/dist/admin/admin.controller.d.ts +139 -0
  6. package/dist/admin/admin.controller.js +238 -0
  7. package/dist/admin/extensions.controller.d.ts +100 -0
  8. package/dist/admin/extensions.controller.js +226 -0
  9. package/dist/admin/setup.controller.d.ts +36 -0
  10. package/dist/admin/setup.controller.js +106 -0
  11. package/dist/admin/setup.service.d.ts +47 -0
  12. package/dist/admin/setup.service.js +90 -0
  13. package/dist/agents/agents.controller.d.ts +205 -0
  14. package/dist/agents/agents.controller.js +144 -0
  15. package/dist/agents/agents.service.d.ts +177 -0
  16. package/dist/agents/agents.service.js +677 -0
  17. package/dist/agents/definition.d.ts +198 -0
  18. package/dist/agents/definition.js +152 -0
  19. package/dist/agents/models.controller.d.ts +13 -0
  20. package/dist/agents/models.controller.js +31 -0
  21. package/dist/agents/templates.controller.d.ts +15 -0
  22. package/dist/agents/templates.controller.js +37 -0
  23. package/dist/agents/templates.d.ts +83 -0
  24. package/dist/agents/templates.js +173 -0
  25. package/dist/catalog/admin-catalog.controller.d.ts +143 -0
  26. package/dist/catalog/admin-catalog.controller.js +245 -0
  27. package/dist/catalog/catalog.service.d.ts +430 -0
  28. package/dist/catalog/catalog.service.js +882 -0
  29. package/dist/catalog/claude/agents.d.ts +27 -0
  30. package/dist/catalog/claude/agents.js +270 -0
  31. package/dist/catalog/claude/commands.d.ts +50 -0
  32. package/dist/catalog/claude/commands.js +179 -0
  33. package/dist/catalog/claude/manifest.d.ts +69 -0
  34. package/dist/catalog/claude/manifest.js +134 -0
  35. package/dist/catalog/claude/marketplace.d.ts +35 -0
  36. package/dist/catalog/claude/marketplace.js +194 -0
  37. package/dist/catalog/claude/mcp.d.ts +33 -0
  38. package/dist/catalog/claude/mcp.js +226 -0
  39. package/dist/catalog/claude/report.d.ts +86 -0
  40. package/dist/catalog/claude/report.js +121 -0
  41. package/dist/catalog/claude/translate.d.ts +56 -0
  42. package/dist/catalog/claude/translate.js +857 -0
  43. package/dist/catalog/cli.d.ts +46 -0
  44. package/dist/catalog/cli.js +211 -0
  45. package/dist/catalog/git-source.d.ts +127 -0
  46. package/dist/catalog/git-source.js +520 -0
  47. package/dist/catalog/marketplace-fetch.d.ts +99 -0
  48. package/dist/catalog/marketplace-fetch.js +203 -0
  49. package/dist/catalog/marketplaces.controller.d.ts +97 -0
  50. package/dist/catalog/marketplaces.controller.js +197 -0
  51. package/dist/catalog/marketplaces.service.d.ts +340 -0
  52. package/dist/catalog/marketplaces.service.js +1217 -0
  53. package/dist/catalog/org-catalog.controller.d.ts +128 -0
  54. package/dist/catalog/org-catalog.controller.js +277 -0
  55. package/dist/catalog/org-catalog.service.d.ts +246 -0
  56. package/dist/catalog/org-catalog.service.js +625 -0
  57. package/dist/catalog/plugin-import.d.ts +436 -0
  58. package/dist/catalog/plugin-import.js +1069 -0
  59. package/dist/catalog/plugin-install.d.ts +447 -0
  60. package/dist/catalog/plugin-install.js +1333 -0
  61. package/dist/catalog/plugin-package.d.ts +143 -0
  62. package/dist/catalog/plugin-package.js +382 -0
  63. package/dist/catalog/plugin-upgrade.d.ts +222 -0
  64. package/dist/catalog/plugin-upgrade.js +1219 -0
  65. package/dist/catalog/requests.service.d.ts +162 -0
  66. package/dist/catalog/requests.service.js +504 -0
  67. package/dist/catalog/schema-path.d.ts +7 -0
  68. package/dist/catalog/schema-path.js +63 -0
  69. package/dist/catalog/upgrade-targets.d.ts +59 -0
  70. package/dist/catalog/upgrade-targets.js +90 -0
  71. package/dist/catalog/versions.d.ts +31 -0
  72. package/dist/catalog/versions.js +44 -0
  73. package/dist/channels/actions.d.ts +37 -0
  74. package/dist/channels/actions.js +121 -0
  75. package/dist/channels/cards.d.ts +44 -0
  76. package/dist/channels/cards.js +156 -0
  77. package/dist/channels/connections.controller.d.ts +59 -0
  78. package/dist/channels/connections.controller.js +115 -0
  79. package/dist/channels/connections.service.d.ts +187 -0
  80. package/dist/channels/connections.service.js +534 -0
  81. package/dist/channels/features.d.ts +12 -0
  82. package/dist/channels/features.js +22 -0
  83. package/dist/channels/inbound.d.ts +120 -0
  84. package/dist/channels/inbound.js +332 -0
  85. package/dist/channels/outbound.d.ts +140 -0
  86. package/dist/channels/outbound.js +1125 -0
  87. package/dist/channels/phrases.d.ts +100 -0
  88. package/dist/channels/phrases.js +81 -0
  89. package/dist/channels/supervisor.d.ts +182 -0
  90. package/dist/channels/supervisor.js +625 -0
  91. package/dist/channels/threads.d.ts +29 -0
  92. package/dist/channels/threads.js +104 -0
  93. package/dist/channels/webhook-url.d.ts +16 -0
  94. package/dist/channels/webhook-url.js +34 -0
  95. package/dist/channels/webhook.controller.d.ts +19 -0
  96. package/dist/channels/webhook.controller.js +72 -0
  97. package/dist/channels/webhook.d.ts +73 -0
  98. package/dist/channels/webhook.js +274 -0
  99. package/dist/config/docs.d.ts +9 -0
  100. package/dist/config/docs.js +85 -0
  101. package/dist/config/env.d.ts +6 -0
  102. package/dist/config/env.js +73 -0
  103. package/dist/config/fields.d.ts +42 -0
  104. package/dist/config/fields.js +214 -0
  105. package/dist/config/file-source.d.ts +3 -0
  106. package/dist/config/file-source.js +31 -0
  107. package/dist/config/load.d.ts +20 -0
  108. package/dist/config/load.js +98 -0
  109. package/dist/config/nacos-source.d.ts +31 -0
  110. package/dist/config/nacos-source.js +229 -0
  111. package/dist/config/schema.d.ts +223 -0
  112. package/dist/config/schema.js +1151 -0
  113. package/dist/config/sources.d.ts +18 -0
  114. package/dist/config/sources.js +59 -0
  115. package/dist/config/types.d.ts +9 -0
  116. package/dist/config/types.js +7 -0
  117. package/dist/conversations/artifacts.d.ts +52 -0
  118. package/dist/conversations/artifacts.js +137 -0
  119. package/dist/conversations/attachments.controller.d.ts +28 -0
  120. package/dist/conversations/attachments.controller.js +96 -0
  121. package/dist/conversations/attachments.service.d.ts +180 -0
  122. package/dist/conversations/attachments.service.js +578 -0
  123. package/dist/conversations/conversations.controller.d.ts +288 -0
  124. package/dist/conversations/conversations.controller.js +607 -0
  125. package/dist/conversations/conversations.service.d.ts +321 -0
  126. package/dist/conversations/conversations.service.js +754 -0
  127. package/dist/conversations/event-log.d.ts +122 -0
  128. package/dist/conversations/event-log.js +259 -0
  129. package/dist/conversations/events.d.ts +856 -0
  130. package/dist/conversations/events.js +639 -0
  131. package/dist/conversations/files.d.ts +92 -0
  132. package/dist/conversations/files.js +213 -0
  133. package/dist/conversations/outputs-read.d.ts +34 -0
  134. package/dist/conversations/outputs-read.js +37 -0
  135. package/dist/conversations/queue.d.ts +169 -0
  136. package/dist/conversations/queue.js +527 -0
  137. package/dist/conversations/sandbox-status.d.ts +21 -0
  138. package/dist/conversations/sandbox-status.js +24 -0
  139. package/dist/conversations/selection.d.ts +46 -0
  140. package/dist/conversations/selection.js +75 -0
  141. package/dist/conversations/titles.d.ts +93 -0
  142. package/dist/conversations/titles.js +422 -0
  143. package/dist/ctx/bus.d.ts +16 -0
  144. package/dist/ctx/bus.js +101 -0
  145. package/dist/ctx/create-ctx.d.ts +109 -0
  146. package/dist/ctx/create-ctx.js +592 -0
  147. package/dist/ctx/kv.d.ts +4 -0
  148. package/dist/ctx/kv.js +31 -0
  149. package/dist/ctx/lock.d.ts +4 -0
  150. package/dist/ctx/lock.js +39 -0
  151. package/dist/ctx/namespace.d.ts +6 -0
  152. package/dist/ctx/namespace.js +12 -0
  153. package/dist/ctx/queue.d.ts +25 -0
  154. package/dist/ctx/queue.js +150 -0
  155. package/dist/ctx/secrets.d.ts +45 -0
  156. package/dist/ctx/secrets.js +175 -0
  157. package/dist/db/kernel-schema.d.ts +1122 -0
  158. package/dist/db/kernel-schema.js +5 -0
  159. package/dist/db/migrator.d.ts +28 -0
  160. package/dist/db/migrator.js +140 -0
  161. package/dist/db/pool.d.ts +21 -0
  162. package/dist/db/pool.js +28 -0
  163. package/dist/db/sql-statements.d.ts +34 -0
  164. package/dist/db/sql-statements.js +168 -0
  165. package/dist/errors.d.ts +210 -0
  166. package/dist/errors.js +212 -0
  167. package/dist/evals/budget.d.ts +61 -0
  168. package/dist/evals/budget.js +181 -0
  169. package/dist/evals/diff.d.ts +55 -0
  170. package/dist/evals/diff.js +133 -0
  171. package/dist/evals/judge.d.ts +53 -0
  172. package/dist/evals/judge.js +149 -0
  173. package/dist/evals/plan.d.ts +47 -0
  174. package/dist/evals/plan.js +77 -0
  175. package/dist/evals/reconstruct.d.ts +137 -0
  176. package/dist/evals/reconstruct.js +322 -0
  177. package/dist/evals/recorded-tools.d.ts +54 -0
  178. package/dist/evals/recorded-tools.js +167 -0
  179. package/dist/evals/run-replay.d.ts +86 -0
  180. package/dist/evals/run-replay.js +502 -0
  181. package/dist/evals/runs.controller.d.ts +100 -0
  182. package/dist/evals/runs.controller.js +199 -0
  183. package/dist/evals/runs.service.d.ts +275 -0
  184. package/dist/evals/runs.service.js +836 -0
  185. package/dist/evals/sets.controller.d.ts +61 -0
  186. package/dist/evals/sets.controller.js +183 -0
  187. package/dist/evals/sets.service.d.ts +128 -0
  188. package/dist/evals/sets.service.js +558 -0
  189. package/dist/evals/snapshot.d.ts +76 -0
  190. package/dist/evals/snapshot.js +207 -0
  191. package/dist/evals/step-replay.d.ts +113 -0
  192. package/dist/evals/step-replay.js +181 -0
  193. package/dist/evals/trajectory.controller.d.ts +41 -0
  194. package/dist/evals/trajectory.controller.js +88 -0
  195. package/dist/evals/trajectory.d.ts +144 -0
  196. package/dist/evals/trajectory.js +338 -0
  197. package/dist/evals/trajectory.service.d.ts +108 -0
  198. package/dist/evals/trajectory.service.js +323 -0
  199. package/dist/evals/worker.d.ts +101 -0
  200. package/dist/evals/worker.js +789 -0
  201. package/dist/extensions/admin-ops.d.ts +105 -0
  202. package/dist/extensions/admin-ops.js +353 -0
  203. package/dist/extensions/admin-view.d.ts +79 -0
  204. package/dist/extensions/admin-view.js +327 -0
  205. package/dist/extensions/approvals.d.ts +119 -0
  206. package/dist/extensions/approvals.js +205 -0
  207. package/dist/extensions/blobs.d.ts +45 -0
  208. package/dist/extensions/blobs.js +160 -0
  209. package/dist/extensions/bootstrap.d.ts +149 -0
  210. package/dist/extensions/bootstrap.js +441 -0
  211. package/dist/extensions/cli.d.ts +61 -0
  212. package/dist/extensions/cli.js +165 -0
  213. package/dist/extensions/config-json-schema.d.ts +9 -0
  214. package/dist/extensions/config-json-schema.js +73 -0
  215. package/dist/extensions/deployment-config.d.ts +138 -0
  216. package/dist/extensions/deployment-config.js +394 -0
  217. package/dist/extensions/enabled.d.ts +31 -0
  218. package/dist/extensions/enabled.js +55 -0
  219. package/dist/extensions/hooks.d.ts +161 -0
  220. package/dist/extensions/hooks.js +425 -0
  221. package/dist/extensions/host.d.ts +577 -0
  222. package/dist/extensions/host.js +2071 -0
  223. package/dist/extensions/inflight.d.ts +69 -0
  224. package/dist/extensions/inflight.js +277 -0
  225. package/dist/extensions/installs.d.ts +166 -0
  226. package/dist/extensions/installs.js +253 -0
  227. package/dist/extensions/instance.d.ts +167 -0
  228. package/dist/extensions/instance.js +329 -0
  229. package/dist/extensions/ledger.d.ts +30 -0
  230. package/dist/extensions/ledger.js +50 -0
  231. package/dist/extensions/lifecycle.d.ts +80 -0
  232. package/dist/extensions/lifecycle.js +152 -0
  233. package/dist/extensions/migrate-lock.d.ts +51 -0
  234. package/dist/extensions/migrate-lock.js +100 -0
  235. package/dist/extensions/org-config.controller.d.ts +24 -0
  236. package/dist/extensions/org-config.controller.js +59 -0
  237. package/dist/extensions/org-config.d.ts +169 -0
  238. package/dist/extensions/org-config.js +520 -0
  239. package/dist/extensions/package.d.ts +115 -0
  240. package/dist/extensions/package.js +246 -0
  241. package/dist/extensions/permission-set.d.ts +117 -0
  242. package/dist/extensions/permission-set.js +184 -0
  243. package/dist/extensions/permission-view.d.ts +194 -0
  244. package/dist/extensions/permission-view.js +284 -0
  245. package/dist/extensions/registry.d.ts +24 -0
  246. package/dist/extensions/registry.js +56 -0
  247. package/dist/extensions/resolve.d.ts +12 -0
  248. package/dist/extensions/resolve.js +71 -0
  249. package/dist/extensions/sources.d.ts +69 -0
  250. package/dist/extensions/sources.js +120 -0
  251. package/dist/extensions/sync.d.ts +40 -0
  252. package/dist/extensions/sync.js +242 -0
  253. package/dist/files/previewers.controller.d.ts +46 -0
  254. package/dist/files/previewers.controller.js +105 -0
  255. package/dist/files/previewers.d.ts +100 -0
  256. package/dist/files/previewers.js +250 -0
  257. package/dist/http/api.module.d.ts +114 -0
  258. package/dist/http/api.module.js +448 -0
  259. package/dist/http/app.d.ts +77 -0
  260. package/dist/http/app.js +170 -0
  261. package/dist/http/error.filter.d.ts +21 -0
  262. package/dist/http/error.filter.js +85 -0
  263. package/dist/http/gateway.module.d.ts +15 -0
  264. package/dist/http/gateway.module.js +13 -0
  265. package/dist/http/health.controller.d.ts +26 -0
  266. package/dist/http/health.controller.js +79 -0
  267. package/dist/http/nest-logger.d.ts +20 -0
  268. package/dist/http/nest-logger.js +28 -0
  269. package/dist/http/plugin-dispatch.d.ts +68 -0
  270. package/dist/http/plugin-dispatch.js +200 -0
  271. package/dist/http/tokens.d.ts +131 -0
  272. package/dist/http/tokens.js +131 -0
  273. package/dist/http/zod.pipe.d.ts +8 -0
  274. package/dist/http/zod.pipe.js +14 -0
  275. package/dist/identity/accounts.d.ts +93 -0
  276. package/dist/identity/accounts.js +367 -0
  277. package/dist/identity/api-tokens.d.ts +73 -0
  278. package/dist/identity/api-tokens.js +197 -0
  279. package/dist/identity/audit.d.ts +23 -0
  280. package/dist/identity/audit.js +41 -0
  281. package/dist/identity/auth.controller.d.ts +40 -0
  282. package/dist/identity/auth.controller.js +130 -0
  283. package/dist/identity/auth.guard.d.ts +41 -0
  284. package/dist/identity/auth.guard.js +162 -0
  285. package/dist/identity/binding.d.ts +47 -0
  286. package/dist/identity/binding.js +84 -0
  287. package/dist/identity/cookies.d.ts +16 -0
  288. package/dist/identity/cookies.js +32 -0
  289. package/dist/identity/credential-check.d.ts +17 -0
  290. package/dist/identity/credential-check.js +24 -0
  291. package/dist/identity/enterprise-admin.d.ts +14 -0
  292. package/dist/identity/enterprise-admin.js +27 -0
  293. package/dist/identity/external.d.ts +106 -0
  294. package/dist/identity/external.js +337 -0
  295. package/dist/identity/hash.d.ts +2 -0
  296. package/dist/identity/hash.js +3 -0
  297. package/dist/identity/login.d.ts +6 -0
  298. package/dist/identity/login.js +20 -0
  299. package/dist/identity/me.controller.d.ts +61 -0
  300. package/dist/identity/me.controller.js +159 -0
  301. package/dist/identity/passwords.d.ts +6 -0
  302. package/dist/identity/passwords.js +37 -0
  303. package/dist/identity/principal.d.ts +56 -0
  304. package/dist/identity/principal.js +38 -0
  305. package/dist/identity/rate-limit.d.ts +55 -0
  306. package/dist/identity/rate-limit.js +121 -0
  307. package/dist/identity/sessions.d.ts +84 -0
  308. package/dist/identity/sessions.js +306 -0
  309. package/dist/identity/settings.d.ts +121 -0
  310. package/dist/identity/settings.js +248 -0
  311. package/dist/identity/sso.controller.d.ts +59 -0
  312. package/dist/identity/sso.controller.js +192 -0
  313. package/dist/identity/sso.d.ts +83 -0
  314. package/dist/identity/sso.js +202 -0
  315. package/dist/inbox/inbox.service.d.ts +43 -0
  316. package/dist/inbox/inbox.service.js +57 -0
  317. package/dist/index.d.ts +67 -0
  318. package/dist/index.js +65 -0
  319. package/dist/kernel.d.ts +256 -0
  320. package/dist/kernel.js +1326 -0
  321. package/dist/log.d.ts +48 -0
  322. package/dist/log.js +229 -0
  323. package/dist/models/prices.d.ts +53 -0
  324. package/dist/models/prices.js +121 -0
  325. package/dist/models/purposes.controller.d.ts +19 -0
  326. package/dist/models/purposes.controller.js +56 -0
  327. package/dist/models/purposes.d.ts +140 -0
  328. package/dist/models/purposes.js +267 -0
  329. package/dist/net/proxy.d.ts +44 -0
  330. package/dist/net/proxy.js +162 -0
  331. package/dist/net/public-address.d.ts +60 -0
  332. package/dist/net/public-address.js +271 -0
  333. package/dist/net/safe-fetch.d.ts +38 -0
  334. package/dist/net/safe-fetch.js +140 -0
  335. package/dist/orgs/directory-sync.controller.d.ts +14 -0
  336. package/dist/orgs/directory-sync.controller.js +40 -0
  337. package/dist/orgs/directory-sync.d.ts +166 -0
  338. package/dist/orgs/directory-sync.js +558 -0
  339. package/dist/orgs/invitations.controller.d.ts +58 -0
  340. package/dist/orgs/invitations.controller.js +137 -0
  341. package/dist/orgs/invitations.service.d.ts +80 -0
  342. package/dist/orgs/invitations.service.js +217 -0
  343. package/dist/orgs/members.service.d.ts +63 -0
  344. package/dist/orgs/members.service.js +234 -0
  345. package/dist/orgs/offboarding.d.ts +141 -0
  346. package/dist/orgs/offboarding.js +398 -0
  347. package/dist/orgs/org-lock.d.ts +32 -0
  348. package/dist/orgs/org-lock.js +46 -0
  349. package/dist/orgs/org.guard.d.ts +35 -0
  350. package/dist/orgs/org.guard.js +95 -0
  351. package/dist/orgs/orgs.controller.d.ts +78 -0
  352. package/dist/orgs/orgs.controller.js +192 -0
  353. package/dist/orgs/orgs.service.d.ts +61 -0
  354. package/dist/orgs/orgs.service.js +209 -0
  355. package/dist/orgs/roles.d.ts +13 -0
  356. package/dist/orgs/roles.js +58 -0
  357. package/dist/orgs/settings.controller.d.ts +20 -0
  358. package/dist/orgs/settings.controller.js +53 -0
  359. package/dist/orgs/settings.service.d.ts +120 -0
  360. package/dist/orgs/settings.service.js +178 -0
  361. package/dist/packages/npm.d.ts +50 -0
  362. package/dist/packages/npm.js +218 -0
  363. package/dist/packages/provenance.d.ts +47 -0
  364. package/dist/packages/provenance.js +147 -0
  365. package/dist/packages/tgz.d.ts +76 -0
  366. package/dist/packages/tgz.js +225 -0
  367. package/dist/runtime/abort.d.ts +32 -0
  368. package/dist/runtime/abort.js +71 -0
  369. package/dist/runtime/checkpointer.d.ts +32 -0
  370. package/dist/runtime/checkpointer.js +243 -0
  371. package/dist/runtime/context/build.d.ts +254 -0
  372. package/dist/runtime/context/build.js +693 -0
  373. package/dist/runtime/context/claude-tools.d.ts +11 -0
  374. package/dist/runtime/context/claude-tools.js +24 -0
  375. package/dist/runtime/context/compaction.d.ts +26 -0
  376. package/dist/runtime/context/compaction.js +195 -0
  377. package/dist/runtime/context/pairing.d.ts +11 -0
  378. package/dist/runtime/context/pairing.js +82 -0
  379. package/dist/runtime/context/reminders.d.ts +5 -0
  380. package/dist/runtime/context/reminders.js +11 -0
  381. package/dist/runtime/context/skills.d.ts +105 -0
  382. package/dist/runtime/context/skills.js +283 -0
  383. package/dist/runtime/context/tokens.d.ts +19 -0
  384. package/dist/runtime/context/tokens.js +104 -0
  385. package/dist/runtime/conversation-lock.d.ts +40 -0
  386. package/dist/runtime/conversation-lock.js +97 -0
  387. package/dist/runtime/hook-calls.d.ts +84 -0
  388. package/dist/runtime/hook-calls.js +136 -0
  389. package/dist/runtime/langgraph-loop.d.ts +52 -0
  390. package/dist/runtime/langgraph-loop.js +247 -0
  391. package/dist/runtime/loop.d.ts +93 -0
  392. package/dist/runtime/loop.js +7 -0
  393. package/dist/runtime/maintenance.d.ts +80 -0
  394. package/dist/runtime/maintenance.js +147 -0
  395. package/dist/runtime/media-cache.d.ts +21 -0
  396. package/dist/runtime/media-cache.js +51 -0
  397. package/dist/runtime/model-call.d.ts +176 -0
  398. package/dist/runtime/model-call.js +1049 -0
  399. package/dist/runtime/providers.d.ts +86 -0
  400. package/dist/runtime/providers.js +224 -0
  401. package/dist/runtime/quota.d.ts +29 -0
  402. package/dist/runtime/quota.js +70 -0
  403. package/dist/runtime/run-finish.d.ts +45 -0
  404. package/dist/runtime/run-finish.js +69 -0
  405. package/dist/runtime/run-queue.d.ts +121 -0
  406. package/dist/runtime/run-queue.js +493 -0
  407. package/dist/runtime/run-worker.d.ts +138 -0
  408. package/dist/runtime/run-worker.js +805 -0
  409. package/dist/runtime/stream.controller.d.ts +34 -0
  410. package/dist/runtime/stream.controller.js +122 -0
  411. package/dist/runtime/stream.d.ts +63 -0
  412. package/dist/runtime/stream.js +262 -0
  413. package/dist/runtime/subagents/child.d.ts +80 -0
  414. package/dist/runtime/subagents/child.js +225 -0
  415. package/dist/runtime/subagents/definition.d.ts +103 -0
  416. package/dist/runtime/subagents/definition.js +392 -0
  417. package/dist/runtime/subagents/dispatch.d.ts +69 -0
  418. package/dist/runtime/subagents/dispatch.js +502 -0
  419. package/dist/runtime/subagents/limits.d.ts +23 -0
  420. package/dist/runtime/subagents/limits.js +34 -0
  421. package/dist/runtime/subagents/org-agents.d.ts +50 -0
  422. package/dist/runtime/subagents/org-agents.js +70 -0
  423. package/dist/runtime/subagents/preamble.d.ts +7 -0
  424. package/dist/runtime/subagents/preamble.js +11 -0
  425. package/dist/runtime/subagents/progress.d.ts +26 -0
  426. package/dist/runtime/subagents/progress.js +36 -0
  427. package/dist/runtime/subagents/roster.d.ts +55 -0
  428. package/dist/runtime/subagents/roster.js +111 -0
  429. package/dist/runtime/subagents/skill-fork.d.ts +123 -0
  430. package/dist/runtime/subagents/skill-fork.js +259 -0
  431. package/dist/runtime/usage-report.d.ts +163 -0
  432. package/dist/runtime/usage-report.js +550 -0
  433. package/dist/runtime/waits.d.ts +233 -0
  434. package/dist/runtime/waits.js +883 -0
  435. package/dist/sandbox/leases.d.ts +169 -0
  436. package/dist/sandbox/leases.js +515 -0
  437. package/dist/sandbox/lifecycle.d.ts +21 -0
  438. package/dist/sandbox/lifecycle.js +68 -0
  439. package/dist/sandbox/sandbox.service.d.ts +249 -0
  440. package/dist/sandbox/sandbox.service.js +908 -0
  441. package/dist/skills/catalog.d.ts +149 -0
  442. package/dist/skills/catalog.js +405 -0
  443. package/dist/skills/changes.controller.d.ts +56 -0
  444. package/dist/skills/changes.controller.js +136 -0
  445. package/dist/skills/changes.service.d.ts +175 -0
  446. package/dist/skills/changes.service.js +805 -0
  447. package/dist/skills/invocable.controller.d.ts +23 -0
  448. package/dist/skills/invocable.controller.js +50 -0
  449. package/dist/skills/invocation-keys.d.ts +23 -0
  450. package/dist/skills/invocation-keys.js +44 -0
  451. package/dist/skills/invoke.d.ts +135 -0
  452. package/dist/skills/invoke.js +286 -0
  453. package/dist/skills/package.d.ts +87 -0
  454. package/dist/skills/package.js +227 -0
  455. package/dist/skills/skills.controller.d.ts +40 -0
  456. package/dist/skills/skills.controller.js +110 -0
  457. package/dist/skills/skills.service.d.ts +192 -0
  458. package/dist/skills/skills.service.js +567 -0
  459. package/dist/storage/prefix.d.ts +36 -0
  460. package/dist/storage/prefix.js +104 -0
  461. package/dist/storage/purge.d.ts +27 -0
  462. package/dist/storage/purge.js +75 -0
  463. package/dist/storage/storage.service.d.ts +44 -0
  464. package/dist/storage/storage.service.js +145 -0
  465. package/dist/text.d.ts +13 -0
  466. package/dist/text.js +13 -0
  467. package/dist/tools/approvals.controller.d.ts +53 -0
  468. package/dist/tools/approvals.controller.js +128 -0
  469. package/dist/tools/approvals.service.d.ts +172 -0
  470. package/dist/tools/approvals.service.js +568 -0
  471. package/dist/tools/breaker.d.ts +28 -0
  472. package/dist/tools/breaker.js +72 -0
  473. package/dist/tools/builtins/agent.d.ts +81 -0
  474. package/dist/tools/builtins/agent.js +223 -0
  475. package/dist/tools/builtins/ask-user.d.ts +28 -0
  476. package/dist/tools/builtins/ask-user.js +139 -0
  477. package/dist/tools/builtins/read-tool-output.d.ts +18 -0
  478. package/dist/tools/builtins/read-tool-output.js +108 -0
  479. package/dist/tools/builtins/sandbox/bash.d.ts +16 -0
  480. package/dist/tools/builtins/sandbox/bash.js +137 -0
  481. package/dist/tools/builtins/sandbox/common.d.ts +114 -0
  482. package/dist/tools/builtins/sandbox/common.js +304 -0
  483. package/dist/tools/builtins/sandbox/deliver.d.ts +5 -0
  484. package/dist/tools/builtins/sandbox/deliver.js +180 -0
  485. package/dist/tools/builtins/sandbox/edit.d.ts +4 -0
  486. package/dist/tools/builtins/sandbox/edit.js +98 -0
  487. package/dist/tools/builtins/sandbox/glob-match.d.ts +25 -0
  488. package/dist/tools/builtins/sandbox/glob-match.js +240 -0
  489. package/dist/tools/builtins/sandbox/glob.d.ts +4 -0
  490. package/dist/tools/builtins/sandbox/glob.js +104 -0
  491. package/dist/tools/builtins/sandbox/grep.d.ts +9 -0
  492. package/dist/tools/builtins/sandbox/grep.js +133 -0
  493. package/dist/tools/builtins/sandbox/index.d.ts +12 -0
  494. package/dist/tools/builtins/sandbox/index.js +27 -0
  495. package/dist/tools/builtins/sandbox/names.d.ts +7 -0
  496. package/dist/tools/builtins/sandbox/names.js +20 -0
  497. package/dist/tools/builtins/sandbox/read.d.ts +8 -0
  498. package/dist/tools/builtins/sandbox/read.js +121 -0
  499. package/dist/tools/builtins/sandbox/write.d.ts +4 -0
  500. package/dist/tools/builtins/sandbox/write.js +88 -0
  501. package/dist/tools/builtins/skill-load.d.ts +53 -0
  502. package/dist/tools/builtins/skill-load.js +446 -0
  503. package/dist/tools/builtins/skill-write.d.ts +59 -0
  504. package/dist/tools/builtins/skill-write.js +244 -0
  505. package/dist/tools/builtins/tool-search.d.ts +31 -0
  506. package/dist/tools/builtins/tool-search.js +171 -0
  507. package/dist/tools/connector-content.controller.d.ts +34 -0
  508. package/dist/tools/connector-content.controller.js +78 -0
  509. package/dist/tools/connector-content.d.ts +104 -0
  510. package/dist/tools/connector-content.js +267 -0
  511. package/dist/tools/connectors.controller.d.ts +210 -0
  512. package/dist/tools/connectors.controller.js +234 -0
  513. package/dist/tools/connectors.service.d.ts +303 -0
  514. package/dist/tools/connectors.service.js +1102 -0
  515. package/dist/tools/credentials.d.ts +139 -0
  516. package/dist/tools/credentials.js +351 -0
  517. package/dist/tools/drift.d.ts +141 -0
  518. package/dist/tools/drift.js +539 -0
  519. package/dist/tools/elicitation.d.ts +81 -0
  520. package/dist/tools/elicitation.js +266 -0
  521. package/dist/tools/endpoint.d.ts +18 -0
  522. package/dist/tools/endpoint.js +115 -0
  523. package/dist/tools/info.d.ts +67 -0
  524. package/dist/tools/info.js +113 -0
  525. package/dist/tools/naming.d.ts +27 -0
  526. package/dist/tools/naming.js +47 -0
  527. package/dist/tools/oauth/discovery.d.ts +65 -0
  528. package/dist/tools/oauth/discovery.js +307 -0
  529. package/dist/tools/oauth/flow.d.ts +63 -0
  530. package/dist/tools/oauth/flow.js +237 -0
  531. package/dist/tools/oauth/oauth.controller.d.ts +31 -0
  532. package/dist/tools/oauth/oauth.controller.js +95 -0
  533. package/dist/tools/oauth/tokens.d.ts +111 -0
  534. package/dist/tools/oauth/tokens.js +258 -0
  535. package/dist/tools/outputs.d.ts +61 -0
  536. package/dist/tools/outputs.js +156 -0
  537. package/dist/tools/pipeline.d.ts +322 -0
  538. package/dist/tools/pipeline.js +1236 -0
  539. package/dist/tools/policy.d.ts +60 -0
  540. package/dist/tools/policy.js +236 -0
  541. package/dist/tools/questions.controller.d.ts +24 -0
  542. package/dist/tools/questions.controller.js +58 -0
  543. package/dist/tools/questions.service.d.ts +103 -0
  544. package/dist/tools/questions.service.js +295 -0
  545. package/dist/tools/redact.d.ts +20 -0
  546. package/dist/tools/redact.js +60 -0
  547. package/dist/tools/registry.d.ts +246 -0
  548. package/dist/tools/registry.js +481 -0
  549. package/dist/tools/resources.d.ts +16 -0
  550. package/dist/tools/resources.js +76 -0
  551. package/dist/tools/specs.d.ts +14 -0
  552. package/dist/tools/specs.js +31 -0
  553. package/dist/tools/validate.d.ts +20 -0
  554. package/dist/tools/validate.js +183 -0
  555. package/dist/tools/view.d.ts +43 -0
  556. package/dist/tools/view.js +225 -0
  557. package/dist/ui/embed-token.d.ts +31 -0
  558. package/dist/ui/embed-token.js +132 -0
  559. package/dist/ui/registry.d.ts +58 -0
  560. package/dist/ui/registry.js +90 -0
  561. package/dist/ui/ui-stream.d.ts +37 -0
  562. package/dist/ui/ui-stream.js +162 -0
  563. package/dist/ui/views.controller.d.ts +81 -0
  564. package/dist/ui/views.controller.js +261 -0
  565. package/dist/ui/views.d.ts +155 -0
  566. package/dist/ui/views.js +306 -0
  567. package/dist/version.d.ts +5 -0
  568. package/dist/version.js +6 -0
  569. package/package.json +67 -3
  570. package/src/migrations/0001_init.sql +8 -0
  571. package/src/migrations/0002_secrets.sql +8 -0
  572. package/src/migrations/0003_identity.sql +117 -0
  573. package/src/migrations/0004_plugin_org_config_nullable.sql +3 -0
  574. package/src/migrations/0005_agents.sql +27 -0
  575. package/src/migrations/0006_conversations.sql +37 -0
  576. package/src/migrations/0007_checkpoints.sql +26 -0
  577. package/src/migrations/0008_runs_trigger_seq.sql +3 -0
  578. package/src/migrations/0009_runs_active_unique.sql +4 -0
  579. package/src/migrations/0010_tools.sql +125 -0
  580. package/src/migrations/0011_approvals_spec.sql +10 -0
  581. package/src/migrations/0012_run_waits.sql +24 -0
  582. package/src/migrations/0013_app_tools_source.sql +4 -0
  583. package/src/migrations/0014_tool_specs.sql +8 -0
  584. package/src/migrations/0015_tool_drift.sql +20 -0
  585. package/src/migrations/0016_plugin_permissions.sql +11 -0
  586. package/src/migrations/0017_identity_sources.sql +6 -0
  587. package/src/migrations/0018_channels.sql +74 -0
  588. package/src/migrations/0019_identity_trust.sql +17 -0
  589. package/src/migrations/0020_channel_card_lookup.sql +10 -0
  590. package/src/migrations/0021_org_storage_purge.sql +9 -0
  591. package/src/migrations/0022_attachments.sql +29 -0
  592. package/src/migrations/0023_sandboxes.sql +37 -0
  593. package/src/migrations/0024_skills.sql +46 -0
  594. package/src/migrations/0025_skills_catalog_idx.sql +6 -0
  595. package/src/migrations/0026_skill_changes.sql +34 -0
  596. package/src/migrations/0027_model_prices.sql +18 -0
  597. package/src/migrations/0028_evals.sql +105 -0
  598. package/src/migrations/0029_model_purposes.sql +18 -0
  599. package/src/migrations/0030_plugin_installs.sql +73 -0
  600. package/src/migrations/0031_plugin_snapshot_source.sql +6 -0
  601. package/src/migrations/0032_plugin_deployment_configs.sql +10 -0
  602. package/src/migrations/0033_rename_apps_to_connectors.sql +25 -0
  603. package/src/migrations/0034_directory.sql +49 -0
  604. package/src/migrations/0035_catalogs.sql +40 -0
  605. package/src/migrations/0036_app_import_staging.sql +17 -0
  606. package/src/migrations/0037_directory_installs.sql +92 -0
  607. package/src/migrations/0038_channel_inbound_mode.sql +6 -0
  608. package/src/migrations/0039_channel_card_pending.sql +8 -0
  609. package/src/migrations/0040_usage_report_sweep.sql +10 -0
  610. package/src/migrations/0041_rename_plugins_to_extensions.sql +38 -0
  611. package/src/migrations/0042_rename_directory_to_catalog.sql +80 -0
  612. package/src/migrations/0043_rename_app_installs_to_plugin_installs.sql +20 -0
  613. package/src/migrations/0044_catalog_one_kind.sql +90 -0
  614. package/src/migrations/0045_plugin_dependencies.sql +9 -0
  615. package/src/migrations/0046_git_sources.sql +25 -0
  616. package/src/migrations/0047_marketplace_entry_heads.sql +7 -0
  617. package/src/migrations/0048_marketplace_entry_trees.sql +8 -0
  618. package/src/migrations/0049_plugin_upgrades.sql +37 -0
  619. package/src/migrations/0050_bootstrap.sql +53 -0
  620. package/src/migrations/0051_claude_compat.sql +26 -0
  621. package/src/migrations/0052_conversations_account_agent_idx.sql +5 -0
  622. package/src/migrations/0053_subagents.sql +73 -0
  623. package/src/migrations/0054_subagent_wait_expiry.sql +7 -0
  624. package/src/migrations/0055_subagent_parent_indexes.sql +10 -0
  625. package/src/migrations/0056_approval_reason.sql +3 -0
  626. package/src/migrations/0057_conversation_titles.sql +11 -0
  627. package/src/migrations/0058_conversation_selection.sql +4 -0
  628. package/src/migrations/0059_conversation_queue.sql +22 -0
  629. package/src/migrations/0060_file_previewers.sql +10 -0
  630. package/src/migrations/0061_queue_selection.sql +5 -0
  631. package/src/migrations/0062_queued_skill.sql +8 -0
@@ -0,0 +1,1049 @@
1
+ import { isModelError, isRetryableKind, ModelError, sanitizeParams, } from "@capybara-hq/plugin-sdk";
2
+ import { sql } from "kysely";
3
+ import { MEDIA_EXCLUDED_MAX_IDS } from "../conversations/events.js";
4
+ import { ApiError } from "../errors.js";
5
+ import { usageTokens } from "../evals/budget.js";
6
+ import { ExtensionUnloadedError } from "../extensions/inflight.js";
7
+ import { audit } from "../identity/audit.js";
8
+ import { hasInvocation, skillLoadOffered } from "../skills/catalog.js";
9
+ import { recordToolSpecs } from "../tools/specs.js";
10
+ import { buildModelRequest, latestSummary, mediaRefs, planMedia, } from "./context/build.js";
11
+ import { acceptSummary, buildCompactionRequest, compactionBackoff, compactionPlan, shouldCompact, } from "./context/compaction.js";
12
+ import { guardToolPairs } from "./context/pairing.js";
13
+ import { turnLimitReminder } from "./context/reminders.js";
14
+ import { calibrate, estimateTokens } from "./context/tokens.js";
15
+ import { fireObserve, hookDeniedAudit, hookDeniedEvent, hookRequestOf, hookScope, recordHookDenial, } from "./hook-calls.js";
16
+ import { MediaCache } from "./media-cache.js";
17
+ import { ProviderListError } from "./providers.js";
18
+ import { subagentBudgetUsedUp } from "./subagents/limits.js";
19
+ /** 单轮收尾的尾部提醒(模型可见,英文;spec 6 轮次额度收尾)。 */
20
+ export const WRAP_UP_REMINDER = "Step limit reached. Do not call tools. Summarize what you found and what remains for the user.";
21
+ /** 所用模型不支持工具(`capabilities.tools` 为 false)时的提醒(模型可见,英文)。 */
22
+ export const TOOLS_UNSUPPORTED_REMINDER = "Tools are unavailable for this model. Answer without calling tools.";
23
+ /** 不可用工具提醒的前缀(模型可见,英文)。 */
24
+ export const TOOLS_UNAVAILABLE_PREFIX = "The following tools are unavailable in this run: ";
25
+ /** 本 run 中此前有工具不可用、现在全部恢复时的提醒(模型可见,英文)。 */
26
+ export const TOOLS_AVAILABLE_AGAIN = "All previously unavailable tools are available again.";
27
+ /** 不可用工具提醒:`The following tools are unavailable in this run: <ref> (<reason>), ...`。 */
28
+ export function toolsUnavailableReminder(unavailable) {
29
+ return `${TOOLS_UNAVAILABLE_PREFIX}${unavailable.map((u) => `${u.ref} (${u.reason})`).join(", ")}`;
30
+ }
31
+ /** `run_failed` message of a model request denied by a policy hook (user-facing, never model-visible). */
32
+ const modelDeniedText = (plugin, reason) => `Model request blocked by the "${plugin}" policy hook: ${reason}`;
33
+ /** 可重试错误在同一模型上重试前的默认等待(供应商未给 retryAfterMs 时)。 */
34
+ const DEFAULT_RETRY_DELAY_MS = 1000;
35
+ /**
36
+ * 供应商建议等待(retryAfterMs)的上限。超过它就不在同一模型上等待重试,直接切兜底(没有兜底则失败),
37
+ * 避免一次运行被「请 10 分钟后再试」之类的建议长时间挂起。
38
+ */
39
+ const MAX_RETRY_AFTER_MS = 10_000;
40
+ /** 读事件日志的分页大小。 */
41
+ const READ_PAGE = 1000;
42
+ /**
43
+ * 取附件时存储暂时不可用(Ruling M4):不把它当成「附件不在」发占位,而是让本次尝试按可重试错误失败
44
+ * (`run.storage_unavailable`),走降级梯子的重试。
45
+ */
46
+ export class MediaUnavailableError extends Error {
47
+ constructor(cause) {
48
+ super(`Attachment storage is temporarily unavailable: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
49
+ this.name = "MediaUnavailableError";
50
+ }
51
+ }
52
+ /**
53
+ * 运行级失败:`code` 写入 `run_failed.error.code`。模型错误为 `model.<kind>`,其余为
54
+ * `run.<reason>`(如 `run.model_unavailable`、`run.provider_unavailable`、`run.provider_error`)。
55
+ */
56
+ export class RunError extends Error {
57
+ code;
58
+ constructor(code, message) {
59
+ super(message);
60
+ this.code = code;
61
+ this.name = "RunError";
62
+ }
63
+ }
64
+ /** 幂等键。`usage` 与 `assistant_message` 同批写入,带键保证重放时不重复记账。 */
65
+ /** 某步 assistant_message 的幂等键(tools 节点据此找到本步的工具调用)。 */
66
+ export const assistantIdemKey = (runId, step) => `assistant:${runId}:${step}`;
67
+ /** 某步第 attempt 次模型调用的 model_call 幂等键(管线据此找到模型实际收到的工具)。 */
68
+ export const modelCallIdemKey = (runId, step, attempt) => `model_call:${runId}:${step}:${attempt}`;
69
+ /** 一步最多的模型调用尝试次数(主模型、同模型重试、兜底,见 `ladder`)。 */
70
+ export const MAX_MODEL_ATTEMPTS = 3;
71
+ const idem = {
72
+ assistant: assistantIdemKey,
73
+ usage: (runId, step) => `usage:${runId}:${step}`,
74
+ reminder: (runId, step) => `reminder:${runId}:${step}`,
75
+ toolsUnavailable: (runId, step) => `tools_unavailable:${runId}:${step}`,
76
+ toolsUnsupported: (runId, step) => `tools_unsupported:${runId}:${step}`,
77
+ modelCall: modelCallIdemKey,
78
+ hookDenied: (runId, step, attempt) => `hook_denied:${runId}:${step}:${attempt}`,
79
+ /** Usage of a compaction call: `n` is the compaction attempt (consecutive failures + 1). */
80
+ compactionUsage: (runId, step, n) => `usage_compaction:${runId}:${step}:${n}`,
81
+ mediaExcluded: (runId, step, attempt, part) => `media_excluded:${runId}:${step}:${attempt}:${part}`,
82
+ };
83
+ /** 外部 signal 已触发:本步按「中止」收尾。 */
84
+ class StepAborted extends Error {
85
+ constructor() {
86
+ super("model step aborted");
87
+ this.name = "StepAborted";
88
+ }
89
+ }
90
+ /**
91
+ * 一步模型调用:幂等 → 读日志 → 压缩 → 尾部提醒 → 构建请求 → 流式(看门狗)→ 降级梯子 → 写事件。
92
+ * 模型看到的一切先写进日志(`model_call` / `reminder` / `compaction_summary`),请求可由日志还原。
93
+ */
94
+ export function runModelStep(deps, input) {
95
+ return runStep(deps, input, false);
96
+ }
97
+ /**
98
+ * 单轮收尾:到达步数上限时调用一次。写入收尾 `reminder`(位于请求末尾);照常带工具(历史里有工具调用时
99
+ * 部分供应商要求请求声明工具,也保持缓存前缀稳定),但 `toolChoice: "none"`——模型只能回复文本。
100
+ * 其余与 `runModelStep` 相同(幂等、降级、中止)。
101
+ */
102
+ export function runFinalWrapUp(deps, input) {
103
+ return runStep(deps, input, true);
104
+ }
105
+ async function runStep(deps, input, wrapUp) {
106
+ const { conversationId, runId, step, signal } = input;
107
+ const existing = await findExisting(deps.eventLog, conversationId, runId, step);
108
+ if (existing) {
109
+ // resumed after a crash between writing usage and enqueueing it: enqueue again (job id dedupes)
110
+ if (deps.usageReport && existing.usage !== null) {
111
+ const u = await deps.eventLog.findByIdem(conversationId, idem.usage(runId, step));
112
+ if (u)
113
+ void deps.usageReport.enqueue(conversationId, u.seq);
114
+ }
115
+ return existing;
116
+ }
117
+ const runtime = deps.config().runtime;
118
+ /** 本次尝试已流出的文本;中止时写进 aborted 的 assistant_message。 */
119
+ const streamed = { text: "" };
120
+ try {
121
+ if (signal.aborted)
122
+ throw new StepAborted();
123
+ let events = await readAll(deps.eventLog, conversationId);
124
+ // sticky (M-4): a model.request denial already logged for this step (any attempt, e.g. a crash
125
+ // after recording it) is final: fail the same way, before compaction or any model call. Read from
126
+ // the events just loaded, so it costs no query (M-2).
127
+ if (hookScope(deps.hooks, input)) {
128
+ const prior = stickyModelDenial(events, runId, step);
129
+ if (prior) {
130
+ throw new RunError("run.hook_denied", modelDeniedText(prior.payload.plugin, prior.payload.reason));
131
+ }
132
+ }
133
+ // 每步实时解析(连接器 / 插件状态可能在步骤之间变化),解析结果记在 model_call;收尾调用同样带工具
134
+ // a skills catalog snapshot (Ruling M17; an empty one too) or a person's skill invocation in the
135
+ // log → skill_load is offered (see skillLoadOffered)
136
+ const resolved = await deps.tools.forAgent(input.orgId, {
137
+ tools: input.agent.tools ?? [],
138
+ subagents: input.agent.subagents,
139
+ skillLoad: skillLoadOffered(events.findLast((e) => e.type === "skills_catalog"), hasInvocation(events)),
140
+ });
141
+ const tools = {
142
+ list: resolved.tools,
143
+ toolSearch: resolved.toolSearch === true,
144
+ media: {
145
+ looked: new Set(),
146
+ info: new Map(),
147
+ bytes: new Map(),
148
+ run: input.mediaCache ?? new MediaCache(deps.config().attachments.requestMediaBytes),
149
+ },
150
+ };
151
+ const beforeCompaction = events.length;
152
+ events = await maybeCompact(deps, input, events, runtime, tools);
153
+ // a sub-agent checks its budget again after a compaction call, so the step's own request is
154
+ // not a second call over the cap (review M5; the loop checks before each step)
155
+ if (input.parentRunId !== undefined &&
156
+ events.length !== beforeCompaction &&
157
+ (await subagentBudgetUsedUp(deps.db, deps.config, input.orgId, input.parentRunId))) {
158
+ return {
159
+ content: [],
160
+ stopReason: "end_turn",
161
+ reused: false,
162
+ aborted: false,
163
+ usage: null,
164
+ budget: true,
165
+ };
166
+ }
167
+ const last = lastToolsReminder(events, runId);
168
+ // 有不可用工具 → 列出;此前列过、现在全部恢复 → 说明已恢复,避免过时的「不可用」成为最后一句
169
+ // (收尾调用不得调用工具,可用性变化不写)。
170
+ const text = wrapUp
171
+ ? null
172
+ : resolved.unavailable.length > 0
173
+ ? toolsUnavailableReminder(resolved.unavailable)
174
+ : last !== null
175
+ ? TOOLS_AVAILABLE_AGAIN
176
+ : null;
177
+ if (text !== null) {
178
+ // 本 run 里模型仍能看到的上一条同类提醒内容相同 → 不重复写(避免每步追加同样的提醒)。
179
+ if (last !== text) {
180
+ events = await appendLocal(deps.eventLog, conversationId, events, {
181
+ type: "reminder",
182
+ runId,
183
+ idemKey: idem.toolsUnavailable(runId, step),
184
+ payload: { step, text },
185
+ });
186
+ }
187
+ }
188
+ const { outcome, ref } = await ladder(deps, input, events, runtime, tools, wrapUp, streamed);
189
+ // assistant_message 与 usage 同一次 append = 同一事务。
190
+ const appended = await appendCounted(deps, input, [
191
+ {
192
+ type: "assistant_message",
193
+ runId,
194
+ idemKey: idem.assistant(runId, step),
195
+ payload: { step, content: outcome.content, stopReason: outcome.stopReason },
196
+ },
197
+ {
198
+ type: "usage",
199
+ runId,
200
+ idemKey: idem.usage(runId, step),
201
+ payload: { step, provider: ref.provider, model: ref.model, ...outcome.usage },
202
+ },
203
+ ]);
204
+ // usage.report metering (never awaited on the run's path; enqueue logs and swallows failures)
205
+ const usageEvent = appended.find((e) => e.type === "usage");
206
+ if (deps.usageReport && usageEvent)
207
+ void deps.usageReport.enqueue(conversationId, usageEvent.seq);
208
+ return {
209
+ content: outcome.content,
210
+ stopReason: outcome.stopReason,
211
+ reused: false,
212
+ aborted: false,
213
+ usage: outcome.usage,
214
+ };
215
+ }
216
+ catch (e) {
217
+ if (!(e instanceof StepAborted))
218
+ throw e;
219
+ // 中止:已流出的文本组装成 aborted 消息;一个字都没流出时 content 为 [](构建请求时不回放)。
220
+ const content = streamed.text === "" ? [] : [{ type: "text", text: streamed.text }];
221
+ await deps.eventLog.append(conversationId, [
222
+ {
223
+ type: "assistant_message",
224
+ runId,
225
+ idemKey: idem.assistant(runId, step),
226
+ payload: { step, content, stopReason: "aborted" },
227
+ },
228
+ ]);
229
+ return { content, stopReason: "aborted", reused: false, aborted: true, usage: null };
230
+ }
231
+ }
232
+ /**
233
+ * Appends a model call's events (its reply or compaction outcome with its `usage`). For a
234
+ * sub-agent's run (`input.parentRunId`) in one transaction that also adds the tokens of the usage
235
+ * events it newly wrote to the parent run's `subagent_tokens_used` (sub-agents spec §8.3): a usage
236
+ * event already in the log (an idempotent hit after a crash) adds nothing, so each call counts
237
+ * exactly once.
238
+ */
239
+ async function appendCounted(deps, input, events) {
240
+ const { conversationId, parentRunId } = input;
241
+ if (parentRunId === undefined)
242
+ return deps.eventLog.append(conversationId, events);
243
+ const out = await deps.db.transaction().execute(async (t) => {
244
+ // the conversation row lock the append takes anyway, taken first to read the seq before it
245
+ const before = await t
246
+ .selectFrom("conversations")
247
+ .select("last_seq")
248
+ .where("id", "=", conversationId)
249
+ .forUpdate()
250
+ .executeTakeFirstOrThrow();
251
+ const written = await deps.eventLog.append(conversationId, events, t);
252
+ const fresh = written.filter((e) => e.seq > Number(before.last_seq));
253
+ let tokens = 0;
254
+ for (const e of fresh)
255
+ if (e.type === "usage")
256
+ tokens += usageTokens(e.payload);
257
+ if (tokens > 0) {
258
+ await t
259
+ .updateTable("runs")
260
+ .set({ subagent_tokens_used: sql `subagent_tokens_used + ${tokens}` })
261
+ .where("id", "=", parentRunId)
262
+ .execute();
263
+ }
264
+ return { written, toSeq: fresh.length > 0 ? Math.max(...fresh.map((e) => e.seq)) : null };
265
+ });
266
+ if (out.toSeq !== null)
267
+ await deps.eventLog.notify(conversationId, out.toSeq);
268
+ return out.written;
269
+ }
270
+ /** The `hook_denied` of a `model.request` denial already logged for this run step (any attempt). */
271
+ function stickyModelDenial(events, runId, step) {
272
+ return events.findLast((e) => e.type === "hook_denied" &&
273
+ e.runId === runId &&
274
+ e.payload.mount === "model.request" &&
275
+ e.payload.step === step);
276
+ }
277
+ /** 幂等命中:返回已写入的 assistant_message(与同批的 usage)对应的结果。 */
278
+ async function findExisting(eventLog, conversationId, runId, step) {
279
+ const msg = await eventLog.findByIdem(conversationId, idem.assistant(runId, step));
280
+ if (msg?.type !== "assistant_message")
281
+ return null;
282
+ const u = await eventLog.findByIdem(conversationId, idem.usage(runId, step));
283
+ const usage = u?.type === "usage"
284
+ ? {
285
+ inputTokens: u.payload.inputTokens,
286
+ outputTokens: u.payload.outputTokens,
287
+ cacheReadTokens: u.payload.cacheReadTokens,
288
+ cacheWriteTokens: u.payload.cacheWriteTokens,
289
+ }
290
+ : null;
291
+ const { stopReason } = msg.payload;
292
+ return {
293
+ content: msg.payload.content,
294
+ stopReason,
295
+ reused: true,
296
+ aborted: stopReason === "aborted",
297
+ usage,
298
+ };
299
+ }
300
+ /** 本 run 中、最近摘要之后的最后一条工具可用性提醒(不可用 / 已恢复)的文本;没有则 null。 */
301
+ function lastToolsReminder(events, runId) {
302
+ const floor = latestSummary(events)?.payload.coversUpToSeq ?? Number.NEGATIVE_INFINITY;
303
+ let text = null;
304
+ for (const e of events) {
305
+ if (e.seq > floor &&
306
+ e.type === "reminder" &&
307
+ e.runId === runId &&
308
+ (e.payload.text.startsWith(TOOLS_UNAVAILABLE_PREFIX) ||
309
+ e.payload.text === TOOLS_AVAILABLE_AGAIN)) {
310
+ text = e.payload.text;
311
+ }
312
+ }
313
+ return text;
314
+ }
315
+ /** 读会话的全部事件(读到当前 last_seq),按 seq 升序。 */
316
+ async function readAll(eventLog, conversationId) {
317
+ const out = [];
318
+ let after = 0;
319
+ for (;;) {
320
+ const page = await eventLog.readAfter(conversationId, after, READ_PAGE);
321
+ out.push(...page);
322
+ const last = page.at(-1);
323
+ if (page.length < READ_PAGE || !last)
324
+ return out;
325
+ after = last.seq;
326
+ }
327
+ }
328
+ /**
329
+ * Appends one event and merges it into the step's local event list (an idempotent hit already in
330
+ * the list is not added twice).
331
+ *
332
+ * Events written elsewhere after `readAll` (an interjection, a fork report the wait sweep wrote)
333
+ * have a lower seq than this one: when the written seq is more than one above the local maximum,
334
+ * the gap is read from the log and merged first (batch 5 review I2). So every event with
335
+ * seq ≤ the request's `toSeq` is in hand when the request is built, exactly as `reconstruct`
336
+ * (evals) reads the log; gap user_messages above `absorbedUpToSeq` are skipped by placement in
337
+ * both.
338
+ */
339
+ async function appendLocal(eventLog, conversationId, events, event) {
340
+ const [written] = await eventLog.append(conversationId, [event]);
341
+ if (!written || events.some((e) => e.seq === written.seq))
342
+ return events;
343
+ const localMax = events.reduce((m, e) => Math.max(m, e.seq), 0);
344
+ const gap = [];
345
+ let after = localMax;
346
+ while (written.seq > after + 1) {
347
+ const page = await eventLog.readAfter(conversationId, after, Math.min(READ_PAGE, written.seq - after - 1));
348
+ const inGap = page.filter((e) => e.seq < written.seq);
349
+ gap.push(...inGap);
350
+ const last = inGap.at(-1);
351
+ if (!last || inGap.length < page.length)
352
+ break;
353
+ after = last.seq;
354
+ }
355
+ return [...events, ...gap, written];
356
+ }
357
+ /**
358
+ * 某次尝试的模型参数:主模型尝试用 `agent.modelParams`,兜底尝试用 `agent.fallbackParams`,
359
+ * 按该模型的声明宽松处理(丢弃未声明 / 不再合法的项并补默认值,见 `sanitizeParams`)。
360
+ * 较早保存的定义可能没有这两个字段,按空处理。
361
+ */
362
+ function attemptParams(agent, info, fallback) {
363
+ const saved = (fallback ? agent.fallbackParams : agent.modelParams) ?? {};
364
+ return sanitizeParams(info.parameters, saved);
365
+ }
366
+ /**
367
+ * 发给模型的请求(纯函数):`buildModelRequest`(注入 `params` 与本步工具:模型不支持工具时不带;收尾调用带
368
+ * `toolChoice: "none"`)→ `guardToolPairs`。输出上限:参数里有 `maxOutputTokens` 旋钮时取
369
+ * `min(模型上限, 旋钮)`,否则 `min(模型上限, defaultMaxOutputTokens)`。
370
+ * 运行时({@link runModelStep})与由日志还原请求(`evals/reconstruct.ts`)共用这一份,二者不会漂移。
371
+ */
372
+ export function assembleModelRequest(input) {
373
+ const { defaultMaxOutputTokens, tools, ...rest } = input;
374
+ const built = buildModelRequest({
375
+ ...rest,
376
+ tools: input.model.capabilities.tools ? [...tools] : [],
377
+ });
378
+ const { request } = built;
379
+ request.messages = guardToolPairs(request.messages);
380
+ const knob = request.params.maxOutputTokens;
381
+ request.maxOutputTokens = Math.min(input.model.maxOutputTokens, typeof knob === "number" ? knob : defaultMaxOutputTokens);
382
+ return built;
383
+ }
384
+ /**
385
+ * 由事件构建本步的模型请求({@link assembleModelRequest})并给出输入 token 估算。
386
+ *
387
+ * 调用方不变式:不得在「已记录 tool_call、尚未记录其 tool_result」之间构建请求——
388
+ * 否则配对守卫会给进行中的调用补上 unknown 结果并发给模型。运行循环在工具结果落日志后才进入下一步。
389
+ */
390
+ function buildRequest(deps, events, input, info, params, runtime, tools, wrapUp) {
391
+ const { request, meta, mediaTokens } = assembleModelRequest({
392
+ events,
393
+ agent: input.agent,
394
+ agentVersion: input.agentVersion,
395
+ model: info,
396
+ step: input.step,
397
+ absorbedUpToSeq: input.absorbedUpToSeq,
398
+ params,
399
+ tools: tools.list,
400
+ toolSearch: tools.toolSearch,
401
+ ...(wrapUp && { toolChoice: "none" }),
402
+ mediaInfo: tools.media.info,
403
+ media: tools.media.bytes,
404
+ mediaBudgetBytes: deps.config().attachments.requestMediaBytes,
405
+ defaultMaxOutputTokens: runtime.maxOutputTokens,
406
+ });
407
+ return {
408
+ request,
409
+ meta,
410
+ estimate: estimateTokens(request.messages, request.system, request.tools, mediaTokens),
411
+ };
412
+ }
413
+ /**
414
+ * 取本次构建要用到的附件(只取该模型会用到的:不支持的图片 / PDF 只发占位,行与字节都不取):先查行信息,
415
+ * 按 {@link planMedia}(只看声明大小、页数与日志)决定保留哪些,再只取保留的字节(`text/*` 只取开头),
416
+ * 并入 `tools.media`。先查本次运行的缓存(键含 etag),未命中才取。
417
+ * 取不到(确定不存在)的缓存为 null;暂时故障抛 {@link MediaUnavailableError},不缓存,下一次尝试重新取。
418
+ */
419
+ async function loadMedia(deps, input, events, info, tools) {
420
+ const cache = tools.media;
421
+ const look = mediaRefs(events, info).filter((id) => !cache.looked.has(id));
422
+ if (look.length > 0) {
423
+ if (deps.media) {
424
+ let rows;
425
+ try {
426
+ rows = await deps.media.info(input.orgId, input.conversationId, look);
427
+ }
428
+ catch (err) {
429
+ throw new MediaUnavailableError(err);
430
+ }
431
+ for (const [id, row] of rows)
432
+ cache.info.set(id, row);
433
+ }
434
+ for (const id of look)
435
+ cache.looked.add(id);
436
+ }
437
+ const budget = deps.config().attachments.requestMediaBytes;
438
+ const plan = planMedia(events, info, cache.info, budget);
439
+ const runKey = (id) => MediaCache.key(id, cache.info.get(id), plan.caps.get(id));
440
+ const need = [];
441
+ for (const id of plan.fetch) {
442
+ if (cache.bytes.has(id))
443
+ continue;
444
+ const hit = cache.run.get(runKey(id));
445
+ if (hit)
446
+ cache.bytes.set(id, hit);
447
+ else
448
+ need.push(id);
449
+ }
450
+ if (need.length === 0 || !deps.media)
451
+ return;
452
+ const caps = new Map(need.flatMap((id) => (plan.caps.has(id) ? [[id, plan.caps.get(id)]] : [])));
453
+ let got;
454
+ try {
455
+ got = await deps.media.fetch(input.orgId, input.conversationId, need, caps.size > 0 ? caps : undefined);
456
+ }
457
+ catch (err) {
458
+ throw new MediaUnavailableError(err);
459
+ }
460
+ for (const id of need) {
461
+ const b = got.get(id) ?? null;
462
+ cache.bytes.set(id, b);
463
+ if (b)
464
+ cache.run.set(runKey(id), b);
465
+ }
466
+ }
467
+ /**
468
+ * 上一次成功调用的「未校正估算 vs 真实输入 token」:最近的 usage 及其之前最近的 model_call。
469
+ * 真实输入取 `inputTokens + cacheReadTokens + cacheWriteTokens`(`inputTokens` 只是未缓存部分,
470
+ * 见 plugin-sdk `ModelUsage`);只用 `inputTokens` 会在大量缓存命中时把估算压到下限,压缩永不触发。
471
+ */
472
+ function lastActual(events) {
473
+ let usage;
474
+ // compaction calls have their own request (not a model_call): only step usage calibrates
475
+ for (const e of events)
476
+ if (e.type === "usage" && e.payload.purpose === undefined)
477
+ usage = e;
478
+ if (!usage)
479
+ return null;
480
+ let estimate;
481
+ for (const e of events) {
482
+ if (e.seq >= usage.seq)
483
+ break;
484
+ if (e.type === "model_call")
485
+ estimate = e.payload.request.estimatedInputTokens;
486
+ }
487
+ if (estimate === undefined)
488
+ return null;
489
+ const { inputTokens, cacheReadTokens, cacheWriteTokens } = usage.payload;
490
+ return { estimate, inputTokens: inputTokens + cacheReadTokens + cacheWriteTokens };
491
+ }
492
+ /** 最近一条摘要之后连续失败的压缩次数。 */
493
+ function consecutiveCompactionFailures(events) {
494
+ let n = 0;
495
+ for (const e of events) {
496
+ if (e.type === "compaction_summary")
497
+ n = 0;
498
+ else if (e.type === "compaction_failed")
499
+ n++;
500
+ }
501
+ return n;
502
+ }
503
+ /**
504
+ * 压缩:校正后的估算达到阈值、且不在失败退避期内时,用主模型生成摘要(非流式消费)。
505
+ * 通过 → `compaction_summary`;过短或出错 → `compaction_failed`(attempt 从 1 起,连续失败递增),
506
+ * 本步按原样继续。主模型此刻解析不了时跳过压缩,交给降级梯子处理。
507
+ */
508
+ async function maybeCompact(deps, input, events, runtime, tools) {
509
+ if (compactionBackoff(events))
510
+ return events;
511
+ let provider;
512
+ let info;
513
+ try {
514
+ ({ provider, info } = await deps.registry.resolve(input.agent.model));
515
+ }
516
+ catch (err) {
517
+ deps.log.warn("compaction skipped: model unavailable", { model: input.agent.model, err });
518
+ return events;
519
+ }
520
+ try {
521
+ await loadMedia(deps, input, events, info, tools);
522
+ }
523
+ catch (err) {
524
+ if (!(err instanceof MediaUnavailableError))
525
+ throw err;
526
+ // 存储暂时不可用:本步跳过压缩,交给降级梯子(会按可重试错误处理)
527
+ deps.log.warn("compaction skipped: attachment storage unavailable", {
528
+ conversationId: input.conversationId,
529
+ err,
530
+ });
531
+ return events;
532
+ }
533
+ const { estimate: raw } = buildRequest(deps, events, input, info, attemptParams(input.agent, info, false).value, runtime, tools, false);
534
+ const estimate = calibrate(raw, lastActual(events));
535
+ if (!shouldCompact(estimate, info, runtime.compactionThreshold))
536
+ return events;
537
+ const plan = compactionPlan(events, runtime.keepRecentTurns);
538
+ if (!plan)
539
+ return events;
540
+ const { runId, step, signal, conversationId } = input;
541
+ const ref = input.agent.model;
542
+ const request = buildCompactionRequest(plan.transcript, info.id);
543
+ const attemptNo = consecutiveCompactionFailures(events) + 1;
544
+ // hooks see compaction calls like step attempts (I-1): same scope, chat runs only
545
+ const scope = hookScope(deps.hooks, input);
546
+ const hookBase = scope && {
547
+ ...scope,
548
+ purpose: "compaction",
549
+ step,
550
+ attempt: 1,
551
+ provider: ref.provider,
552
+ model: ref.model,
553
+ };
554
+ if (hookBase && deps.hooks?.has("model.request")) {
555
+ let verdict;
556
+ try {
557
+ verdict = await deps.hooks.run("model.request", { ...hookBase, request: hookRequestOf(request, deps.log) }, signal);
558
+ }
559
+ catch (e) {
560
+ // run() rejects only when the run's signal aborted: an abort, never a deny
561
+ if (signal.aborted)
562
+ throw new StepAborted();
563
+ throw e;
564
+ }
565
+ if (!verdict.allowed) {
566
+ // compaction fails (generic reason in the log, raw detail only in the audit row, R12); the
567
+ // step continues and its own request meets the hooks next
568
+ const site = {
569
+ orgId: input.orgId,
570
+ conversationId,
571
+ runId,
572
+ ...(input.accountId !== undefined && { accountId: input.accountId }),
573
+ ...(input.agentId !== undefined && { agentId: input.agentId }),
574
+ idemKey: `hook_denied_compaction:${runId}:${step}:${attemptNo}`,
575
+ };
576
+ const denied = hookDeniedEvent(site, "model.request", verdict, { step, attempt: 1 });
577
+ const entry = hookDeniedAudit(site, denied.payload, verdict);
578
+ entry.detail = { ...entry.detail, purpose: "compaction" };
579
+ const failed = {
580
+ type: "compaction_failed",
581
+ runId,
582
+ payload: {
583
+ attempt: attemptNo,
584
+ reason: `compaction request blocked by the "${verdict.plugin}" policy hook (${verdict.cause})`,
585
+ },
586
+ };
587
+ deps.log.warn("compaction blocked by a policy hook", {
588
+ conversationId,
589
+ plugin: verdict.plugin,
590
+ cause: verdict.cause,
591
+ });
592
+ const written = await deps.db.transaction().execute(async (t) => {
593
+ const out = await deps.eventLog.append(conversationId, [failed], t);
594
+ await audit(t, entry);
595
+ return out;
596
+ });
597
+ if (written.length > 0) {
598
+ await deps.eventLog.notify(conversationId, Math.max(...written.map((e) => e.seq)));
599
+ }
600
+ return mergeLocal(events, written);
601
+ }
602
+ }
603
+ let failure = null;
604
+ let summary = "";
605
+ let usage = null;
606
+ const sentAt = Date.now();
607
+ try {
608
+ let partial = "";
609
+ const out = await consumeModelStream(provider, request, info.streamIdleTimeoutMs ?? runtime.streamIdleMs, signal, (ev) => {
610
+ if (ev.type === "text_delta")
611
+ partial += ev.text;
612
+ });
613
+ usage = out.usage;
614
+ if (hookBase && deps.hooks) {
615
+ fireObserve(deps.hooks, "model.response", {
616
+ ...hookBase,
617
+ outcome: "ok",
618
+ content: out.content,
619
+ stopReason: out.stopReason,
620
+ usage: out.usage,
621
+ latencyMs: Date.now() - sentAt,
622
+ }, signal, deps.log);
623
+ }
624
+ const final = out.content.flatMap((b) => (b.type === "text" ? [b.text] : [])).join("");
625
+ summary = (final || partial).trim();
626
+ if (!acceptSummary(summary, plan.transcript)) {
627
+ failure = `summary too short (${summary.length} chars)`;
628
+ }
629
+ }
630
+ catch (err) {
631
+ if (err instanceof StepAborted)
632
+ throw err;
633
+ if (hookBase && deps.hooks) {
634
+ fireObserve(deps.hooks, "model.response", {
635
+ ...hookBase,
636
+ outcome: "failed",
637
+ content: [],
638
+ stopReason: null,
639
+ usage: null,
640
+ latencyMs: Date.now() - sentAt,
641
+ errorCode: classify(err).code,
642
+ }, signal, deps.log);
643
+ }
644
+ failure = err instanceof Error ? err.message : String(err);
645
+ }
646
+ const event = failure === null
647
+ ? {
648
+ type: "compaction_summary",
649
+ runId,
650
+ payload: { coversUpToSeq: plan.coversUpToSeq, text: summary },
651
+ }
652
+ : {
653
+ type: "compaction_failed",
654
+ runId,
655
+ payload: { attempt: attemptNo, reason: failure },
656
+ };
657
+ if (failure !== null) {
658
+ deps.log.warn("compaction failed", { conversationId, reason: failure });
659
+ }
660
+ // the compaction call's usage (I-1): logged with the outcome in one append (one transaction), marked
661
+ // `purpose: "compaction"` so step consumers (calibration, trajectory steps, replay plans) skip it
662
+ const batch = [event];
663
+ if (usage !== null) {
664
+ batch.push({
665
+ type: "usage",
666
+ runId,
667
+ idemKey: idem.compactionUsage(runId, step, attemptNo),
668
+ payload: { step, provider: ref.provider, model: ref.model, ...usage, purpose: "compaction" },
669
+ });
670
+ }
671
+ const written = await appendCounted(deps, input, batch);
672
+ const usageEvent = written.find((e) => e.type === "usage");
673
+ if (deps.usageReport && usageEvent)
674
+ void deps.usageReport.enqueue(conversationId, usageEvent.seq);
675
+ return mergeLocal(events, written);
676
+ }
677
+ /** Merges written events into the local list (idempotent hits already in it are not added twice). */
678
+ function mergeLocal(events, written) {
679
+ const fresh = written.filter((w) => !events.some((e) => e.seq === w.seq));
680
+ return fresh.length === 0 ? events : [...events, ...fresh];
681
+ }
682
+ /**
683
+ * 降级梯子:主模型 → 主模型重试一次(等 `retryAfterMs ?? 1000`,可被中止)→ `agent.fallback`(若有)。
684
+ * 建议等待超过 `MAX_RETRY_AFTER_MS` 时跳过同模型重试。
685
+ * 每次尝试解析出模型后、写 `model_call` 之前写本步的尾部提醒(均按本步幂等,只写一次):所用模型不支持工具而本步有工具
686
+ * → {@link TOOLS_UNSUPPORTED_REMINDER};然后是轮次提醒或收尾提醒(收尾提醒因此位于请求末尾)。
687
+ * 每次实际尝试写自己的 `model_call`(attempt 从 1 连续递增,跳过的不计);重试前经 sink 发 `step_reset`。
688
+ * 不可重试的失败或梯子用尽 → `RunError`。
689
+ */
690
+ async function ladder(deps, input, events, runtime, tools, wrapUp, streamed) {
691
+ const { conversationId, runId, step, signal, sink, agent } = input;
692
+ const turnReminder = wrapUp
693
+ ? WRAP_UP_REMINDER
694
+ : turnLimitReminder(step, input.maxSteps ?? runtime.maxSteps);
695
+ let evs = events;
696
+ const rungs = [
697
+ { ref: agent.model, fallback: false },
698
+ { ref: agent.model, fallback: false },
699
+ ];
700
+ if (agent.fallback)
701
+ rungs.push({ ref: agent.fallback, fallback: true });
702
+ let last;
703
+ let attempt = 0;
704
+ // hooks fire only for chat runs that have a registry (eval replays omit it, P-18)
705
+ const scope = hookScope(deps.hooks, input);
706
+ /** 本次尝试发出的请求里带了字节的附件(供应商拒收时写 media_excluded)。 */
707
+ let sentMedia = [];
708
+ for (const rung of rungs) {
709
+ if (last && !rung.fallback && (last.retryAfterMs ?? 0) > MAX_RETRY_AFTER_MS) {
710
+ deps.log.warn("model retry skipped: suggested wait exceeds cap", {
711
+ conversationId,
712
+ runId,
713
+ step,
714
+ retryAfterMs: last.retryAfterMs,
715
+ });
716
+ continue;
717
+ }
718
+ attempt++;
719
+ if (last) {
720
+ sink({ kind: "step_reset", runId, step });
721
+ streamed.text = "";
722
+ if (!rung.fallback)
723
+ await abortableSleep(last.retryAfterMs ?? DEFAULT_RETRY_DELAY_MS, signal);
724
+ }
725
+ sentMedia = [];
726
+ /** When the request went out (model.response fires only for sent requests). */
727
+ let sentAt;
728
+ try {
729
+ const { provider, info } = await deps.registry.resolve(rung.ref);
730
+ if (tools.list.length > 0 && !info.capabilities.tools) {
731
+ evs = await appendLocal(deps.eventLog, conversationId, evs, {
732
+ type: "reminder",
733
+ runId,
734
+ idemKey: idem.toolsUnsupported(runId, step),
735
+ payload: { step, text: TOOLS_UNSUPPORTED_REMINDER },
736
+ });
737
+ }
738
+ if (turnReminder !== null) {
739
+ evs = await appendLocal(deps.eventLog, conversationId, evs, {
740
+ type: "reminder",
741
+ runId,
742
+ idemKey: idem.reminder(runId, step),
743
+ payload: { step, text: turnReminder },
744
+ });
745
+ }
746
+ const params = attemptParams(agent, info, rung.fallback);
747
+ await loadMedia(deps, input, evs, info, tools);
748
+ const { request, meta, estimate } = buildRequest(deps, evs, input, info, params.value, runtime, tools, wrapUp);
749
+ sentMedia = (meta.media ?? []).flatMap((m) => m.outcome === "bytes" ? [m.attachmentId] : []);
750
+ // model.request before model_call: a denied request is never logged as sent nor sent (a denial
751
+ // logged before a crash is sticky: runStep checks it before compaction)
752
+ if (scope && deps.hooks?.has("model.request")) {
753
+ let verdict;
754
+ try {
755
+ verdict = await deps.hooks.run("model.request", {
756
+ ...scope,
757
+ purpose: "step",
758
+ step,
759
+ attempt,
760
+ provider: rung.ref.provider,
761
+ model: rung.ref.model,
762
+ request: hookRequestOf(request, deps.log),
763
+ }, signal);
764
+ }
765
+ catch (e) {
766
+ // run() rejects only when the run's signal aborted: an abort, never a deny
767
+ if (signal.aborted)
768
+ throw new StepAborted();
769
+ throw e;
770
+ }
771
+ if (!verdict.allowed) {
772
+ await recordHookDenial(deps, {
773
+ orgId: input.orgId,
774
+ conversationId,
775
+ runId,
776
+ ...(input.accountId !== undefined && { accountId: input.accountId }),
777
+ ...(input.agentId !== undefined && { agentId: input.agentId }),
778
+ idemKey: idem.hookDenied(runId, step, attempt),
779
+ }, "model.request", verdict, { step, attempt });
780
+ throw new RunError("run.hook_denied", modelDeniedText(verdict.plugin, verdict.reason));
781
+ }
782
+ }
783
+ // 请求里的工具定义(含目录里的)按指纹存进 tool_specs,与 model_call 同一事务
784
+ // (请求可由日志 + tool_specs 还原)
785
+ const sent = new Set([...meta.tools, ...(meta.catalog ?? [])].map((t) => t.specHash));
786
+ const written = await deps.db.transaction().execute(async (t) => {
787
+ await recordToolSpecs(t, tools.list.filter((x) => sent.has(x.specHash)));
788
+ return deps.eventLog.append(conversationId, [
789
+ {
790
+ type: "model_call",
791
+ runId,
792
+ idemKey: idem.modelCall(runId, step, attempt),
793
+ payload: {
794
+ step,
795
+ attempt,
796
+ provider: rung.ref.provider,
797
+ model: rung.ref.model,
798
+ fallback: rung.fallback,
799
+ request: {
800
+ ...meta,
801
+ maxOutputTokens: request.maxOutputTokens,
802
+ ...(params.dropped.length > 0 && { droppedParams: params.dropped }),
803
+ estimatedInputTokens: estimate,
804
+ },
805
+ },
806
+ },
807
+ ], t);
808
+ });
809
+ await deps.eventLog.notify(conversationId, Math.max(...written.map((e) => e.seq)));
810
+ sentAt = Date.now();
811
+ const outcome = await consumeModelStream(provider, request, info.streamIdleTimeoutMs ?? runtime.streamIdleMs, signal, (ev) => {
812
+ if (ev.type === "text_delta") {
813
+ streamed.text += ev.text;
814
+ sink({ kind: "text", runId, step, text: ev.text });
815
+ }
816
+ else {
817
+ sink({ kind: "thinking", runId, step, text: ev.text });
818
+ }
819
+ });
820
+ if (scope && deps.hooks) {
821
+ fireObserve(deps.hooks, "model.response", {
822
+ ...scope,
823
+ purpose: "step",
824
+ step,
825
+ attempt,
826
+ provider: rung.ref.provider,
827
+ model: rung.ref.model,
828
+ outcome: "ok",
829
+ content: outcome.content,
830
+ stopReason: outcome.stopReason,
831
+ usage: outcome.usage,
832
+ latencyMs: Date.now() - sentAt,
833
+ }, signal, deps.log);
834
+ }
835
+ return { outcome, ref: rung.ref };
836
+ }
837
+ catch (err) {
838
+ if (err instanceof StepAborted)
839
+ throw err;
840
+ if (signal.aborted)
841
+ throw new StepAborted();
842
+ // a hook denial is final (not retried, not reclassified)
843
+ if (err instanceof RunError)
844
+ throw err;
845
+ const f = classify(err);
846
+ if (scope && deps.hooks && sentAt !== undefined) {
847
+ fireObserve(deps.hooks, "model.response", {
848
+ ...scope,
849
+ purpose: "step",
850
+ step,
851
+ attempt,
852
+ provider: rung.ref.provider,
853
+ model: rung.ref.model,
854
+ outcome: "failed",
855
+ content: [],
856
+ stopReason: null,
857
+ usage: null,
858
+ latencyMs: Date.now() - sentAt,
859
+ errorCode: f.code,
860
+ }, signal, deps.log);
861
+ }
862
+ deps.log.warn("model call attempt failed", {
863
+ conversationId,
864
+ runId,
865
+ step,
866
+ attempt,
867
+ code: f.code,
868
+ retryable: f.retryable,
869
+ error: f.message,
870
+ });
871
+ if (!f.retryable) {
872
+ if (REJECTS_MEDIA.has(f.code) && sentMedia.length > 0) {
873
+ // 记录失败不能掩盖原来的失败码:只记 warn,本次运行照常以原错误失败
874
+ try {
875
+ await excludeMedia(deps, input, attempt, sentMedia);
876
+ }
877
+ catch (e) {
878
+ deps.log.warn("cannot record media_excluded", { conversationId, runId, step, err: e });
879
+ }
880
+ }
881
+ throw new RunError(f.code, f.message);
882
+ }
883
+ last = f;
884
+ }
885
+ }
886
+ const f = last;
887
+ throw new RunError(f.code, f.message);
888
+ }
889
+ /** 不可重试、且可能由请求里的媒体引起的失败:带了媒体字节时写 `media_excluded`(Ruling M8)。 */
890
+ const REJECTS_MEDIA = new Set(["model.invalid_request", "model.context_overflow"]);
891
+ /**
892
+ * 供应商拒收了带媒体的请求:把这些附件记进 `media_excluded`(每条最多 {@link MEDIA_EXCLUDED_MAX_IDS} 个),
893
+ * 之后的请求一律换成占位,会话不会因同一个附件永久卡住(可由日志推出)。本次运行照常失败。
894
+ */
895
+ async function excludeMedia(deps, input, attempt, ids) {
896
+ const { conversationId, runId, step } = input;
897
+ const events = [];
898
+ for (let i = 0; i < ids.length; i += MEDIA_EXCLUDED_MAX_IDS) {
899
+ events.push({
900
+ type: "media_excluded",
901
+ runId,
902
+ idemKey: idem.mediaExcluded(runId, step, attempt, i / MEDIA_EXCLUDED_MAX_IDS),
903
+ payload: {
904
+ attachmentIds: ids.slice(i, i + MEDIA_EXCLUDED_MAX_IDS),
905
+ reason: "provider_rejected",
906
+ },
907
+ });
908
+ }
909
+ deps.log.warn("model provider rejected a request with media; excluding the attachments", {
910
+ conversationId,
911
+ runId,
912
+ step,
913
+ attachmentIds: ids,
914
+ });
915
+ await deps.eventLog.append(conversationId, events);
916
+ }
917
+ function classify(err) {
918
+ if (isModelError(err)) {
919
+ return {
920
+ retryable: isRetryableKind(err.kind),
921
+ code: `model.${err.kind}`,
922
+ message: err.message,
923
+ retryAfterMs: err.retryAfterMs,
924
+ };
925
+ }
926
+ if (err instanceof MediaUnavailableError) {
927
+ return {
928
+ retryable: true,
929
+ code: "run.storage_unavailable",
930
+ message: err.message,
931
+ retryAfterMs: undefined,
932
+ };
933
+ }
934
+ // 供应商插件在调用途中被撤销 / 升级 / 重新加载(排空时限到了,或调用落在正在撤销的实例上):同模型重试会实时
935
+ // 向注册表取到新实例(I2);插件真的停用了时重试解析失败,按 model_unavailable 结束
936
+ if (err instanceof ExtensionUnloadedError ||
937
+ (err instanceof Error && err.cause instanceof ExtensionUnloadedError)) {
938
+ return {
939
+ retryable: true,
940
+ code: "run.provider_unavailable",
941
+ message: err.message,
942
+ retryAfterMs: undefined,
943
+ };
944
+ }
945
+ if (err instanceof ProviderListError) {
946
+ return {
947
+ retryable: true,
948
+ code: "run.provider_unavailable",
949
+ message: err.message,
950
+ retryAfterMs: undefined,
951
+ };
952
+ }
953
+ if (err instanceof ApiError && err.code === "agent.model_unavailable") {
954
+ return {
955
+ retryable: false,
956
+ code: "run.model_unavailable",
957
+ message: err.message,
958
+ retryAfterMs: undefined,
959
+ };
960
+ }
961
+ return {
962
+ retryable: false,
963
+ code: "run.provider_error",
964
+ message: err instanceof Error ? err.message : String(err),
965
+ retryAfterMs: undefined,
966
+ };
967
+ }
968
+ /** 可被中止的等待(本进程发起的模型调用自己的计时器,结束即清除);中止 → StepAborted。 */
969
+ function abortableSleep(ms, signal) {
970
+ return new Promise((resolve, reject) => {
971
+ if (signal.aborted)
972
+ return reject(new StepAborted());
973
+ const onAbort = () => {
974
+ clearTimeout(timer);
975
+ reject(new StepAborted());
976
+ };
977
+ const timer = setTimeout(() => {
978
+ signal.removeEventListener("abort", onAbort);
979
+ resolve();
980
+ }, ms);
981
+ signal.addEventListener("abort", onAbort, { once: true });
982
+ });
983
+ }
984
+ /**
985
+ * 消费一次模型流,带空闲看门狗:两次事件之间超过 `idleMs` → 本地中止并按 `ModelError("timeout")` 抛出。
986
+ * 外部 `signal` 触发 → `StepAborted`。每次 `next()` 都与停止信号竞速,供应商不响应 signal 也不会卡住。
987
+ * 运行时与评测单步回放(`evals/step-replay.ts`)、评委共用。
988
+ */
989
+ export async function consumeModelStream(provider, request, idleMs, signal, onDelta) {
990
+ if (signal.aborted)
991
+ throw new StepAborted();
992
+ const local = new AbortController();
993
+ let timedOut = false;
994
+ let timer;
995
+ let stop;
996
+ const stopped = new Promise((_, reject) => {
997
+ stop = () => reject(new StepAborted());
998
+ });
999
+ stopped.catch(() => { });
1000
+ const halt = () => {
1001
+ if (local.signal.aborted)
1002
+ return;
1003
+ local.abort();
1004
+ stop();
1005
+ };
1006
+ const arm = () => {
1007
+ clearTimeout(timer);
1008
+ timer = setTimeout(() => {
1009
+ timedOut = true;
1010
+ halt();
1011
+ }, idleMs);
1012
+ };
1013
+ signal.addEventListener("abort", halt, { once: true });
1014
+ let it;
1015
+ try {
1016
+ arm();
1017
+ it = provider.stream(request, { signal: local.signal })[Symbol.asyncIterator]();
1018
+ for (;;) {
1019
+ const next = it.next();
1020
+ next.catch(() => { });
1021
+ const r = await Promise.race([next, stopped]);
1022
+ if (r.done)
1023
+ break;
1024
+ arm();
1025
+ const ev = r.value;
1026
+ if (ev.type === "message_complete") {
1027
+ return { content: ev.content, stopReason: ev.stopReason, usage: ev.usage };
1028
+ }
1029
+ onDelta(ev);
1030
+ }
1031
+ throw new ModelError("server", "Model stream ended without message_complete");
1032
+ }
1033
+ catch (err) {
1034
+ if (signal.aborted)
1035
+ throw new StepAborted();
1036
+ if (timedOut) {
1037
+ throw new ModelError("timeout", `Model stream idle for more than ${idleMs}ms`, {
1038
+ cause: err,
1039
+ });
1040
+ }
1041
+ throw err;
1042
+ }
1043
+ finally {
1044
+ clearTimeout(timer);
1045
+ signal.removeEventListener("abort", halt);
1046
+ // 提前结束(完成、出错、超时、中止)都通知生成器收尾;不等待,避免卡在不响应的供应商上。
1047
+ it?.return?.()?.catch(() => { });
1048
+ }
1049
+ }