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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +20 -2
  4. package/bootstrap.json +26 -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 +1126 -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 +237 -0
  112. package/dist/config/schema.js +1192 -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 +423 -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 +151 -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 +211 -0
  166. package/dist/errors.js +213 -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 +790 -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 +176 -0
  210. package/dist/extensions/bootstrap.js +535 -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 +220 -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 +69 -0
  318. package/dist/index.js +67 -0
  319. package/dist/kernel.d.ts +259 -0
  320. package/dist/kernel.js +1326 -0
  321. package/dist/log-config.d.ts +9 -0
  322. package/dist/log-config.js +27 -0
  323. package/dist/log-context.d.ts +32 -0
  324. package/dist/log-context.js +40 -0
  325. package/dist/log-loki.d.ts +34 -0
  326. package/dist/log-loki.js +145 -0
  327. package/dist/log.d.ts +79 -0
  328. package/dist/log.js +303 -0
  329. package/dist/models/prices.d.ts +53 -0
  330. package/dist/models/prices.js +121 -0
  331. package/dist/models/purposes.controller.d.ts +19 -0
  332. package/dist/models/purposes.controller.js +56 -0
  333. package/dist/models/purposes.d.ts +140 -0
  334. package/dist/models/purposes.js +267 -0
  335. package/dist/net/proxy.d.ts +44 -0
  336. package/dist/net/proxy.js +162 -0
  337. package/dist/net/public-address.d.ts +60 -0
  338. package/dist/net/public-address.js +272 -0
  339. package/dist/net/safe-fetch.d.ts +38 -0
  340. package/dist/net/safe-fetch.js +140 -0
  341. package/dist/orgs/directory-sync.controller.d.ts +14 -0
  342. package/dist/orgs/directory-sync.controller.js +40 -0
  343. package/dist/orgs/directory-sync.d.ts +166 -0
  344. package/dist/orgs/directory-sync.js +558 -0
  345. package/dist/orgs/invitations.controller.d.ts +58 -0
  346. package/dist/orgs/invitations.controller.js +137 -0
  347. package/dist/orgs/invitations.service.d.ts +80 -0
  348. package/dist/orgs/invitations.service.js +217 -0
  349. package/dist/orgs/members.service.d.ts +63 -0
  350. package/dist/orgs/members.service.js +234 -0
  351. package/dist/orgs/offboarding.d.ts +141 -0
  352. package/dist/orgs/offboarding.js +398 -0
  353. package/dist/orgs/org-lock.d.ts +32 -0
  354. package/dist/orgs/org-lock.js +46 -0
  355. package/dist/orgs/org.guard.d.ts +35 -0
  356. package/dist/orgs/org.guard.js +95 -0
  357. package/dist/orgs/orgs.controller.d.ts +78 -0
  358. package/dist/orgs/orgs.controller.js +192 -0
  359. package/dist/orgs/orgs.service.d.ts +61 -0
  360. package/dist/orgs/orgs.service.js +209 -0
  361. package/dist/orgs/roles.d.ts +13 -0
  362. package/dist/orgs/roles.js +58 -0
  363. package/dist/orgs/settings.controller.d.ts +20 -0
  364. package/dist/orgs/settings.controller.js +53 -0
  365. package/dist/orgs/settings.service.d.ts +120 -0
  366. package/dist/orgs/settings.service.js +178 -0
  367. package/dist/packages/npm.d.ts +50 -0
  368. package/dist/packages/npm.js +218 -0
  369. package/dist/packages/provenance.d.ts +47 -0
  370. package/dist/packages/provenance.js +147 -0
  371. package/dist/packages/tgz.d.ts +76 -0
  372. package/dist/packages/tgz.js +225 -0
  373. package/dist/runtime/abort.d.ts +32 -0
  374. package/dist/runtime/abort.js +71 -0
  375. package/dist/runtime/checkpointer.d.ts +32 -0
  376. package/dist/runtime/checkpointer.js +243 -0
  377. package/dist/runtime/context/build.d.ts +254 -0
  378. package/dist/runtime/context/build.js +693 -0
  379. package/dist/runtime/context/claude-tools.d.ts +11 -0
  380. package/dist/runtime/context/claude-tools.js +24 -0
  381. package/dist/runtime/context/compaction.d.ts +26 -0
  382. package/dist/runtime/context/compaction.js +195 -0
  383. package/dist/runtime/context/pairing.d.ts +11 -0
  384. package/dist/runtime/context/pairing.js +82 -0
  385. package/dist/runtime/context/reminders.d.ts +5 -0
  386. package/dist/runtime/context/reminders.js +11 -0
  387. package/dist/runtime/context/skills.d.ts +105 -0
  388. package/dist/runtime/context/skills.js +283 -0
  389. package/dist/runtime/context/tokens.d.ts +19 -0
  390. package/dist/runtime/context/tokens.js +104 -0
  391. package/dist/runtime/conversation-lock.d.ts +40 -0
  392. package/dist/runtime/conversation-lock.js +97 -0
  393. package/dist/runtime/hook-calls.d.ts +84 -0
  394. package/dist/runtime/hook-calls.js +136 -0
  395. package/dist/runtime/langgraph-loop.d.ts +52 -0
  396. package/dist/runtime/langgraph-loop.js +247 -0
  397. package/dist/runtime/loop.d.ts +93 -0
  398. package/dist/runtime/loop.js +7 -0
  399. package/dist/runtime/maintenance.d.ts +80 -0
  400. package/dist/runtime/maintenance.js +148 -0
  401. package/dist/runtime/media-cache.d.ts +21 -0
  402. package/dist/runtime/media-cache.js +51 -0
  403. package/dist/runtime/model-call.d.ts +176 -0
  404. package/dist/runtime/model-call.js +1049 -0
  405. package/dist/runtime/providers.d.ts +86 -0
  406. package/dist/runtime/providers.js +224 -0
  407. package/dist/runtime/quota.d.ts +29 -0
  408. package/dist/runtime/quota.js +70 -0
  409. package/dist/runtime/run-finish.d.ts +45 -0
  410. package/dist/runtime/run-finish.js +69 -0
  411. package/dist/runtime/run-queue.d.ts +121 -0
  412. package/dist/runtime/run-queue.js +493 -0
  413. package/dist/runtime/run-worker.d.ts +138 -0
  414. package/dist/runtime/run-worker.js +806 -0
  415. package/dist/runtime/stream.controller.d.ts +34 -0
  416. package/dist/runtime/stream.controller.js +122 -0
  417. package/dist/runtime/stream.d.ts +63 -0
  418. package/dist/runtime/stream.js +262 -0
  419. package/dist/runtime/subagents/child.d.ts +80 -0
  420. package/dist/runtime/subagents/child.js +225 -0
  421. package/dist/runtime/subagents/definition.d.ts +103 -0
  422. package/dist/runtime/subagents/definition.js +392 -0
  423. package/dist/runtime/subagents/dispatch.d.ts +69 -0
  424. package/dist/runtime/subagents/dispatch.js +502 -0
  425. package/dist/runtime/subagents/limits.d.ts +23 -0
  426. package/dist/runtime/subagents/limits.js +34 -0
  427. package/dist/runtime/subagents/org-agents.d.ts +50 -0
  428. package/dist/runtime/subagents/org-agents.js +70 -0
  429. package/dist/runtime/subagents/preamble.d.ts +7 -0
  430. package/dist/runtime/subagents/preamble.js +11 -0
  431. package/dist/runtime/subagents/progress.d.ts +26 -0
  432. package/dist/runtime/subagents/progress.js +36 -0
  433. package/dist/runtime/subagents/roster.d.ts +55 -0
  434. package/dist/runtime/subagents/roster.js +111 -0
  435. package/dist/runtime/subagents/skill-fork.d.ts +123 -0
  436. package/dist/runtime/subagents/skill-fork.js +259 -0
  437. package/dist/runtime/usage-report.d.ts +163 -0
  438. package/dist/runtime/usage-report.js +551 -0
  439. package/dist/runtime/waits.d.ts +233 -0
  440. package/dist/runtime/waits.js +883 -0
  441. package/dist/sandbox/leases.d.ts +169 -0
  442. package/dist/sandbox/leases.js +515 -0
  443. package/dist/sandbox/lifecycle.d.ts +21 -0
  444. package/dist/sandbox/lifecycle.js +68 -0
  445. package/dist/sandbox/sandbox.service.d.ts +249 -0
  446. package/dist/sandbox/sandbox.service.js +908 -0
  447. package/dist/skills/catalog.d.ts +149 -0
  448. package/dist/skills/catalog.js +405 -0
  449. package/dist/skills/changes.controller.d.ts +56 -0
  450. package/dist/skills/changes.controller.js +136 -0
  451. package/dist/skills/changes.service.d.ts +175 -0
  452. package/dist/skills/changes.service.js +805 -0
  453. package/dist/skills/invocable.controller.d.ts +23 -0
  454. package/dist/skills/invocable.controller.js +50 -0
  455. package/dist/skills/invocation-keys.d.ts +23 -0
  456. package/dist/skills/invocation-keys.js +44 -0
  457. package/dist/skills/invoke.d.ts +135 -0
  458. package/dist/skills/invoke.js +286 -0
  459. package/dist/skills/package.d.ts +87 -0
  460. package/dist/skills/package.js +227 -0
  461. package/dist/skills/skills.controller.d.ts +40 -0
  462. package/dist/skills/skills.controller.js +110 -0
  463. package/dist/skills/skills.service.d.ts +192 -0
  464. package/dist/skills/skills.service.js +567 -0
  465. package/dist/storage/prefix.d.ts +36 -0
  466. package/dist/storage/prefix.js +104 -0
  467. package/dist/storage/purge.d.ts +27 -0
  468. package/dist/storage/purge.js +75 -0
  469. package/dist/storage/storage.service.d.ts +44 -0
  470. package/dist/storage/storage.service.js +145 -0
  471. package/dist/text.d.ts +13 -0
  472. package/dist/text.js +13 -0
  473. package/dist/tools/approvals.controller.d.ts +53 -0
  474. package/dist/tools/approvals.controller.js +128 -0
  475. package/dist/tools/approvals.service.d.ts +172 -0
  476. package/dist/tools/approvals.service.js +568 -0
  477. package/dist/tools/breaker.d.ts +28 -0
  478. package/dist/tools/breaker.js +72 -0
  479. package/dist/tools/builtins/agent.d.ts +81 -0
  480. package/dist/tools/builtins/agent.js +223 -0
  481. package/dist/tools/builtins/ask-user.d.ts +28 -0
  482. package/dist/tools/builtins/ask-user.js +139 -0
  483. package/dist/tools/builtins/read-tool-output.d.ts +18 -0
  484. package/dist/tools/builtins/read-tool-output.js +108 -0
  485. package/dist/tools/builtins/sandbox/bash.d.ts +16 -0
  486. package/dist/tools/builtins/sandbox/bash.js +137 -0
  487. package/dist/tools/builtins/sandbox/common.d.ts +116 -0
  488. package/dist/tools/builtins/sandbox/common.js +306 -0
  489. package/dist/tools/builtins/sandbox/deliver.d.ts +5 -0
  490. package/dist/tools/builtins/sandbox/deliver.js +180 -0
  491. package/dist/tools/builtins/sandbox/edit.d.ts +4 -0
  492. package/dist/tools/builtins/sandbox/edit.js +98 -0
  493. package/dist/tools/builtins/sandbox/glob-match.d.ts +25 -0
  494. package/dist/tools/builtins/sandbox/glob-match.js +240 -0
  495. package/dist/tools/builtins/sandbox/glob.d.ts +4 -0
  496. package/dist/tools/builtins/sandbox/glob.js +104 -0
  497. package/dist/tools/builtins/sandbox/grep.d.ts +9 -0
  498. package/dist/tools/builtins/sandbox/grep.js +133 -0
  499. package/dist/tools/builtins/sandbox/index.d.ts +12 -0
  500. package/dist/tools/builtins/sandbox/index.js +27 -0
  501. package/dist/tools/builtins/sandbox/names.d.ts +7 -0
  502. package/dist/tools/builtins/sandbox/names.js +20 -0
  503. package/dist/tools/builtins/sandbox/read.d.ts +8 -0
  504. package/dist/tools/builtins/sandbox/read.js +121 -0
  505. package/dist/tools/builtins/sandbox/write.d.ts +4 -0
  506. package/dist/tools/builtins/sandbox/write.js +88 -0
  507. package/dist/tools/builtins/skill-load.d.ts +53 -0
  508. package/dist/tools/builtins/skill-load.js +446 -0
  509. package/dist/tools/builtins/skill-write.d.ts +59 -0
  510. package/dist/tools/builtins/skill-write.js +244 -0
  511. package/dist/tools/builtins/tool-search.d.ts +31 -0
  512. package/dist/tools/builtins/tool-search.js +171 -0
  513. package/dist/tools/connector-content.controller.d.ts +34 -0
  514. package/dist/tools/connector-content.controller.js +78 -0
  515. package/dist/tools/connector-content.d.ts +104 -0
  516. package/dist/tools/connector-content.js +267 -0
  517. package/dist/tools/connectors.controller.d.ts +210 -0
  518. package/dist/tools/connectors.controller.js +234 -0
  519. package/dist/tools/connectors.service.d.ts +303 -0
  520. package/dist/tools/connectors.service.js +1102 -0
  521. package/dist/tools/credentials.d.ts +139 -0
  522. package/dist/tools/credentials.js +351 -0
  523. package/dist/tools/drift.d.ts +141 -0
  524. package/dist/tools/drift.js +539 -0
  525. package/dist/tools/elicitation.d.ts +81 -0
  526. package/dist/tools/elicitation.js +266 -0
  527. package/dist/tools/endpoint.d.ts +18 -0
  528. package/dist/tools/endpoint.js +115 -0
  529. package/dist/tools/info.d.ts +67 -0
  530. package/dist/tools/info.js +113 -0
  531. package/dist/tools/naming.d.ts +27 -0
  532. package/dist/tools/naming.js +47 -0
  533. package/dist/tools/oauth/discovery.d.ts +65 -0
  534. package/dist/tools/oauth/discovery.js +307 -0
  535. package/dist/tools/oauth/flow.d.ts +63 -0
  536. package/dist/tools/oauth/flow.js +237 -0
  537. package/dist/tools/oauth/oauth.controller.d.ts +31 -0
  538. package/dist/tools/oauth/oauth.controller.js +95 -0
  539. package/dist/tools/oauth/tokens.d.ts +111 -0
  540. package/dist/tools/oauth/tokens.js +258 -0
  541. package/dist/tools/outputs.d.ts +61 -0
  542. package/dist/tools/outputs.js +156 -0
  543. package/dist/tools/pipeline.d.ts +322 -0
  544. package/dist/tools/pipeline.js +1236 -0
  545. package/dist/tools/policy.d.ts +60 -0
  546. package/dist/tools/policy.js +236 -0
  547. package/dist/tools/questions.controller.d.ts +24 -0
  548. package/dist/tools/questions.controller.js +58 -0
  549. package/dist/tools/questions.service.d.ts +103 -0
  550. package/dist/tools/questions.service.js +295 -0
  551. package/dist/tools/redact.d.ts +20 -0
  552. package/dist/tools/redact.js +60 -0
  553. package/dist/tools/registry.d.ts +246 -0
  554. package/dist/tools/registry.js +481 -0
  555. package/dist/tools/resources.d.ts +16 -0
  556. package/dist/tools/resources.js +76 -0
  557. package/dist/tools/specs.d.ts +14 -0
  558. package/dist/tools/specs.js +31 -0
  559. package/dist/tools/validate.d.ts +20 -0
  560. package/dist/tools/validate.js +183 -0
  561. package/dist/tools/view.d.ts +43 -0
  562. package/dist/tools/view.js +225 -0
  563. package/dist/ui/embed-token.d.ts +31 -0
  564. package/dist/ui/embed-token.js +132 -0
  565. package/dist/ui/registry.d.ts +58 -0
  566. package/dist/ui/registry.js +90 -0
  567. package/dist/ui/ui-stream.d.ts +37 -0
  568. package/dist/ui/ui-stream.js +162 -0
  569. package/dist/ui/views.controller.d.ts +81 -0
  570. package/dist/ui/views.controller.js +261 -0
  571. package/dist/ui/views.d.ts +155 -0
  572. package/dist/ui/views.js +306 -0
  573. package/dist/version.d.ts +5 -0
  574. package/dist/version.js +6 -0
  575. package/package.json +69 -3
  576. package/src/migrations/0001_init.sql +8 -0
  577. package/src/migrations/0002_secrets.sql +8 -0
  578. package/src/migrations/0003_identity.sql +117 -0
  579. package/src/migrations/0004_plugin_org_config_nullable.sql +3 -0
  580. package/src/migrations/0005_agents.sql +27 -0
  581. package/src/migrations/0006_conversations.sql +37 -0
  582. package/src/migrations/0007_checkpoints.sql +26 -0
  583. package/src/migrations/0008_runs_trigger_seq.sql +3 -0
  584. package/src/migrations/0009_runs_active_unique.sql +4 -0
  585. package/src/migrations/0010_tools.sql +125 -0
  586. package/src/migrations/0011_approvals_spec.sql +10 -0
  587. package/src/migrations/0012_run_waits.sql +24 -0
  588. package/src/migrations/0013_app_tools_source.sql +4 -0
  589. package/src/migrations/0014_tool_specs.sql +8 -0
  590. package/src/migrations/0015_tool_drift.sql +20 -0
  591. package/src/migrations/0016_plugin_permissions.sql +11 -0
  592. package/src/migrations/0017_identity_sources.sql +6 -0
  593. package/src/migrations/0018_channels.sql +74 -0
  594. package/src/migrations/0019_identity_trust.sql +17 -0
  595. package/src/migrations/0020_channel_card_lookup.sql +10 -0
  596. package/src/migrations/0021_org_storage_purge.sql +9 -0
  597. package/src/migrations/0022_attachments.sql +29 -0
  598. package/src/migrations/0023_sandboxes.sql +37 -0
  599. package/src/migrations/0024_skills.sql +46 -0
  600. package/src/migrations/0025_skills_catalog_idx.sql +6 -0
  601. package/src/migrations/0026_skill_changes.sql +34 -0
  602. package/src/migrations/0027_model_prices.sql +18 -0
  603. package/src/migrations/0028_evals.sql +105 -0
  604. package/src/migrations/0029_model_purposes.sql +18 -0
  605. package/src/migrations/0030_plugin_installs.sql +73 -0
  606. package/src/migrations/0031_plugin_snapshot_source.sql +6 -0
  607. package/src/migrations/0032_plugin_deployment_configs.sql +10 -0
  608. package/src/migrations/0033_rename_apps_to_connectors.sql +25 -0
  609. package/src/migrations/0034_directory.sql +49 -0
  610. package/src/migrations/0035_catalogs.sql +40 -0
  611. package/src/migrations/0036_app_import_staging.sql +17 -0
  612. package/src/migrations/0037_directory_installs.sql +92 -0
  613. package/src/migrations/0038_channel_inbound_mode.sql +6 -0
  614. package/src/migrations/0039_channel_card_pending.sql +8 -0
  615. package/src/migrations/0040_usage_report_sweep.sql +10 -0
  616. package/src/migrations/0041_rename_plugins_to_extensions.sql +38 -0
  617. package/src/migrations/0042_rename_directory_to_catalog.sql +80 -0
  618. package/src/migrations/0043_rename_app_installs_to_plugin_installs.sql +20 -0
  619. package/src/migrations/0044_catalog_one_kind.sql +90 -0
  620. package/src/migrations/0045_plugin_dependencies.sql +9 -0
  621. package/src/migrations/0046_git_sources.sql +25 -0
  622. package/src/migrations/0047_marketplace_entry_heads.sql +7 -0
  623. package/src/migrations/0048_marketplace_entry_trees.sql +8 -0
  624. package/src/migrations/0049_plugin_upgrades.sql +37 -0
  625. package/src/migrations/0050_bootstrap.sql +53 -0
  626. package/src/migrations/0051_claude_compat.sql +26 -0
  627. package/src/migrations/0052_conversations_account_agent_idx.sql +5 -0
  628. package/src/migrations/0053_subagents.sql +73 -0
  629. package/src/migrations/0054_subagent_wait_expiry.sql +7 -0
  630. package/src/migrations/0055_subagent_parent_indexes.sql +10 -0
  631. package/src/migrations/0056_approval_reason.sql +3 -0
  632. package/src/migrations/0057_conversation_titles.sql +11 -0
  633. package/src/migrations/0058_conversation_selection.sql +4 -0
  634. package/src/migrations/0059_conversation_queue.sql +22 -0
  635. package/src/migrations/0060_file_previewers.sql +10 -0
  636. package/src/migrations/0061_queue_selection.sql +5 -0
  637. package/src/migrations/0062_queued_skill.sql +8 -0
@@ -0,0 +1,1219 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { sql } from "kysely";
3
+ import { putTemplateVersionIn } from "../agents/templates.js";
4
+ import { ApiError } from "../errors.js";
5
+ import { isExtensionEnabled } from "../extensions/enabled.js";
6
+ import { audit } from "../identity/audit.js";
7
+ import { lockOrg } from "../orgs/org-lock.js";
8
+ import { packagePath } from "../packages/tgz.js";
9
+ import { skillConflictError } from "../skills/skills.service.js";
10
+ import { canonicalJson } from "../tools/registry.js";
11
+ import { getPath, lockOrgForActions, orgTemplateView, readCatalogAgentTemplates, readCatalogFiles, readCatalogTemplates, requiredActions, setPath, skillOf, templatePluginProblem, } from "./org-catalog.service.js";
12
+ import { catalogSkillNames, InstallFailure, journalEntries, missingTemplateInput, planActions, planSkillConflicts, resolveDependencies, under, } from "./plugin-install.js";
13
+ import { assertItemInstallable } from "./requests.service.js";
14
+ import { deployedExtension, deploymentTarget } from "./upgrade-targets.js";
15
+ import { satisfiesRange } from "./versions.js";
16
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17
+ const installNotFound = () => new ApiError("plugin.install_not_found", "Plugin install not found");
18
+ const inProgress = () => new ApiError("plugin.install_in_progress", "This plugin install is being installed, upgraded or uninstalled; try again when it finishes");
19
+ const stale = () => new ApiError("plugin.upgrade_stale", "The upgrade preview is out of date (the catalog version or this install's components changed); preview the upgrade again");
20
+ const targetInvalid = (message, details) => new ApiError("plugin.upgrade_target_invalid", message, details);
21
+ const fail = (path, message) => new ApiError("validation_failed", "Request validation failed", [{ path, message }]);
22
+ const sha256 = (s) => createHash("sha256").update(s).digest("hex");
23
+ /**
24
+ * What a credential of `auth` looks like: two auths with the same key take the same stored
25
+ * credential (static: the type family, ownership and header names; OAuth: any change re-enters).
26
+ */
27
+ function credentialKey(auth) {
28
+ switch (auth.type) {
29
+ case "none":
30
+ return "none";
31
+ case "bearer":
32
+ case "api_key":
33
+ return `token:${auth.ownership}`;
34
+ case "basic":
35
+ return `basic:${auth.ownership}`;
36
+ case "headers":
37
+ return `headers:${auth.ownership}:${[...auth.names].sort().join(",")}`;
38
+ default:
39
+ return canonicalJson(auth);
40
+ }
41
+ }
42
+ /** The digest of all files of a catalog version. */
43
+ const filesDigest = (row) => sha256(row.files
44
+ .map((f) => `${f.path}\0${f.sha256}\n`)
45
+ .sort()
46
+ .join(""));
47
+ /** The digest of the files under `dir/` of a catalog version (paths relative to it). */
48
+ function dirDigest(row, dir) {
49
+ const prefix = `${dir}/`;
50
+ return sha256(row.files
51
+ .filter((f) => f.path.startsWith(prefix))
52
+ .map((f) => `${f.path.slice(prefix.length)}\0${f.sha256}\n`)
53
+ .sort()
54
+ .join(""));
55
+ }
56
+ /**
57
+ * Org in-place upgrade (and, for declarative plugins, rollback) of an installed plugin (package
58
+ * ecosystem spec §10, Decisions 9–11):
59
+ * - **preview** diffs the installed and the target version per component (extension, dependency
60
+ * plugins, skills by name, connector templates by id) and returns the inputs needed and a
61
+ * `diffHash`; the same checks as an install run first (dependencies resolve, extension and
62
+ * template connectors deployed, skill conflicts -> `plugin.skill_conflict`), plus the target
63
+ * rules (`plugin.upgrade_target_invalid`): an extension-bearing plugin can move only to the
64
+ * catalog version carrying the deployment's extension, and installs depending on this plugin must
65
+ * still accept the target;
66
+ * - **upgrade** re-checks the `diffHash` (`plugin.upgrade_stale`), sets the install `upgrading`
67
+ * under the org lock (another upgrade or an uninstall -> `plugin.install_in_progress`), then:
68
+ * added dependencies (installed by the install flow), the added extension (enabled), added and
69
+ * changed skills (a new version of the same skill; agents keep selecting it by id), added
70
+ * connectors (created from the template), and in one final transaction the changed connectors
71
+ * (template-owned fields only, `applyTemplateIn`), the removals by the uninstall rules, the new
72
+ * version and the audit `plugin.org_upgraded`. Each step is journaled in
73
+ * `plugin_installs.upgrade`; on failure the journal is undone (created components removed, skill
74
+ * versions reverted; changed connectors were never committed), the install stays `installed` at
75
+ * the old version with `error = { step, code }`, audit `plugin.org_upgrade_failed`, 502
76
+ * `plugin.upgrade_failed`. An upgrade interrupted for longer than `catalog.staleInstallMinutes`
77
+ * is undone by the next preview, upgrade or uninstall.
78
+ * Approval follows the item's policy (the org catalog service creates the request). No in-process
79
+ * state.
80
+ */
81
+ export class PluginUpgrader {
82
+ installer;
83
+ d;
84
+ constructor(installer) {
85
+ this.installer = installer;
86
+ this.d = installer.deps;
87
+ }
88
+ /** The upgrade preview (see the class comment). */
89
+ async preview(orgId, actor, installId, version) {
90
+ await this.recoverInterrupted(orgId, actor, installId);
91
+ const diff = await this.diff(orgId, actor, installId, version, null);
92
+ await this.d.db.transaction().execute((t) => lockOrgForActions(t, orgId, actor, diff.actions));
93
+ return {
94
+ from: diff.from.version,
95
+ to: diff.to.version,
96
+ diffHash: diff.diffHash,
97
+ components: diff.components,
98
+ inputs: diff.inputs,
99
+ templates: diff.templates.flatMap((c) => c.target !== undefined &&
100
+ (diff.inputs.connectors.includes(c.id) || diff.inputs.credentialsChanged.includes(c.id))
101
+ ? [orgTemplateView(c.id, c.target)]
102
+ : []),
103
+ };
104
+ }
105
+ /**
106
+ * The checks of {@link upgrade} before an approval request (the org catalog service creates one
107
+ * for a needs_approval item without a grant): the diff with the input, the `diffHash`. Returns
108
+ * the item and the org actions the upgrade needs.
109
+ */
110
+ async check(orgId, actor, installId, input) {
111
+ await this.recoverInterrupted(orgId, actor, installId);
112
+ const diff = await this.diff(orgId, actor, installId, input.version, input);
113
+ if (diff.diffHash !== input.diffHash)
114
+ throw stale();
115
+ return { itemId: diff.itemId, name: diff.name, actions: diff.actions, diff };
116
+ }
117
+ /**
118
+ * Runs an upgrade (see the class comment); returns the install view. `checked` is the diff
119
+ * {@link check} computed for the same input (re-verified under the org lock: the install's
120
+ * version and ledger must be unchanged), otherwise it is computed here.
121
+ */
122
+ async upgrade(orgId, actor, installId, input, opts = {}, checked) {
123
+ let diff;
124
+ if (checked !== undefined &&
125
+ checked.installId === installId &&
126
+ checked.to.version === input.version &&
127
+ checked.diffHash === input.diffHash) {
128
+ diff = checked;
129
+ }
130
+ else {
131
+ await this.recoverInterrupted(orgId, actor, installId);
132
+ diff = await this.diff(orgId, actor, installId, input.version, input);
133
+ if (diff.diffHash !== input.diffHash)
134
+ throw stale();
135
+ }
136
+ const { itemId } = diff;
137
+ const upgradeId = randomUUID();
138
+ // Start: the install `upgrading`, under the org lock (one upgrade or uninstall at a time)
139
+ await this.d.db.transaction().execute(async (t) => {
140
+ await lockOrgForActions(t, orgId, actor, diff.actions);
141
+ const row = await t
142
+ .selectFrom("plugin_installs")
143
+ .select(["status", "version"])
144
+ .where("id", "=", installId)
145
+ .where("org_id", "=", orgId)
146
+ .forUpdate()
147
+ .executeTakeFirst();
148
+ if (!row || row.status === "failed")
149
+ throw installNotFound();
150
+ if (row.status !== "installed")
151
+ throw inProgress();
152
+ if (row.version !== diff.from.version ||
153
+ ledgerHash(await ledgerOf(t, installId)) !== diff.ledgerHash) {
154
+ throw stale();
155
+ }
156
+ await assertItemInstallable(t, orgId, itemId);
157
+ await t
158
+ .updateTable("plugin_installs")
159
+ .set({
160
+ status: "upgrading",
161
+ upgrade: sql `jsonb_build_object('id', ${upgradeId}::text, 'from', ${diff.from.version}::text, 'to', ${diff.to.version}::text, 'startedAt', now(), 'done', '[]'::jsonb)`,
162
+ })
163
+ .where("id", "=", installId)
164
+ .execute();
165
+ });
166
+ const done = [];
167
+ /** The step whose journal write found the journal gone (cleaned up as interrupted meanwhile). */
168
+ let unjournaled;
169
+ // Appends a step to the journal (the journal gone: the upgrade was cleaned up as interrupted)
170
+ const journal = async (s, t) => {
171
+ done.push(s);
172
+ unjournaled = s;
173
+ const r = await t
174
+ .updateTable("plugin_installs")
175
+ .set({
176
+ upgrade: sql `jsonb_set(upgrade, '{done}', (upgrade->'done') || ${JSON.stringify([s])}::jsonb)`,
177
+ })
178
+ .where("id", "=", installId)
179
+ .where(sql `upgrade->>'id'`, "=", upgradeId)
180
+ .executeTakeFirst();
181
+ if (r.numUpdatedRows === 0n)
182
+ throw cleanedUp();
183
+ unjournaled = undefined;
184
+ };
185
+ // Records a component in the ledger and the journal, in the given transaction
186
+ const record = async (c, t) => {
187
+ const inserted = await t
188
+ .insertInto("plugin_install_components")
189
+ .values({
190
+ install_id: installId,
191
+ kind: c.kind,
192
+ ref: c.ref,
193
+ created: c.created,
194
+ skill_version: c.skillVersion,
195
+ template_id: c.templateId ?? null,
196
+ })
197
+ .onConflict((oc) => oc.columns(["install_id", "kind", "ref"]).doNothing())
198
+ .returning("ref")
199
+ .executeTakeFirst();
200
+ if (c.created)
201
+ await journal({ op: "created", kind: c.kind, ref: c.ref }, t);
202
+ else if (inserted)
203
+ await journal({ op: "recorded", kind: c.kind, ref: c.ref }, t);
204
+ };
205
+ let step = "dependencies";
206
+ const disabled = [];
207
+ try {
208
+ for (const dep of diff.dependencies.filter((x) => x.change === "added")) {
209
+ const resolved = dep.resolved;
210
+ const rec = (c, t) => record(c, t);
211
+ if ((await this.installer.recordDependency(orgId, resolved, rec)) !== undefined)
212
+ continue;
213
+ if (!dep.plan) {
214
+ throw new ApiError("catalog.dependency_unresolved", `Dependency ${dep.name}@${resolved.range} cannot be resolved: its install in this organization was removed meanwhile; try again`, [{ dependency: dep.name, range: resolved.range }]);
215
+ }
216
+ let child;
217
+ try {
218
+ child = await this.installer.execute(orgId, actor, dep.plan, opts, {
219
+ actions: requiredActions(dep.plan.manifest),
220
+ approvedBy: itemId,
221
+ parent: installId,
222
+ });
223
+ }
224
+ catch (e) {
225
+ if (e instanceof InstallFailure) {
226
+ throw new InstallFailure(e.step, e.cause, [dep.name, ...e.dependency], e.rolledBack);
227
+ }
228
+ if (e instanceof ApiError &&
229
+ e.code === "plugin.already_installed" &&
230
+ (await this.installer.recordDependency(orgId, resolved, rec)) !== undefined) {
231
+ continue;
232
+ }
233
+ throw new InstallFailure("dependencies", e, [dep.name], true);
234
+ }
235
+ await this.installer.recordDependency(orgId, resolved, rec, child);
236
+ }
237
+ step = "extension";
238
+ if (diff.extension?.change === "added") {
239
+ const name = diff.extension.name;
240
+ await this.d.orgConfigs.put(orgId, name, {
241
+ enabled: true,
242
+ ...(input.extensionConfig !== undefined && { config: input.extensionConfig }),
243
+ }, actor, opts.actorTokenId, {
244
+ viaItem: itemId,
245
+ ifDisabled: { skipped: false },
246
+ inTransaction: (t, skipped) => record({ kind: "extension", ref: name, created: !skipped, skillVersion: null }, t),
247
+ });
248
+ }
249
+ step = "skills";
250
+ for (const s of diff.skills) {
251
+ if (s.change !== "added" && s.change !== "changed")
252
+ continue;
253
+ const files = await readCatalogFiles(this.d.storage, diff.to.object_prefix, diff.to.files, s.dir);
254
+ const r = await this.d.skills.createVersionFromFiles(orgId, actor, { ...skillOf(files), files }, {
255
+ catalog: {
256
+ itemId,
257
+ version: diff.to.version,
258
+ format: diff.to.source_format,
259
+ suggestPinned: s.suggestPinned === true,
260
+ },
261
+ ...opts,
262
+ });
263
+ await this.d.db.transaction().execute(async (t) => {
264
+ if (r.created) {
265
+ await record({ kind: "skill", ref: r.skill.id, created: true, skillVersion: null }, t);
266
+ return;
267
+ }
268
+ await journal({ op: "skill_version", ref: r.skill.id, version: r.version }, t);
269
+ await record({ kind: "skill", ref: r.skill.id, created: false, skillVersion: r.version }, t);
270
+ });
271
+ }
272
+ step = "agent_templates";
273
+ for (const a of diff.agentTemplates) {
274
+ if (a.change !== "added" && a.change !== "changed")
275
+ continue;
276
+ await this.d.db.transaction().execute(async (t) => {
277
+ await lockOrg(t, orgId);
278
+ const r = await putTemplateVersionIn(t, {
279
+ orgId,
280
+ plugin: diff.name,
281
+ catalogItemId: itemId,
282
+ catalogVersion: diff.to.version,
283
+ sourceFormat: diff.to.source_format,
284
+ }, a.template);
285
+ if (r.created) {
286
+ await record({ kind: "agent_template", ref: r.id, created: true, skillVersion: null }, t);
287
+ return;
288
+ }
289
+ if (r.changed)
290
+ await journal({ op: "template_version", ref: r.id, version: r.version }, t);
291
+ await record({
292
+ kind: "agent_template",
293
+ ref: r.id,
294
+ created: false,
295
+ skillVersion: r.changed ? r.version : null,
296
+ }, t);
297
+ });
298
+ }
299
+ step = "connectors";
300
+ for (const [templateId, ci] of diff.creates) {
301
+ const r = await this.d.connectors.create(orgId, actor, ci, opts.actorTokenId);
302
+ await this.d.db.transaction().execute((t) => record({
303
+ kind: "connector",
304
+ ref: r.connector.id,
305
+ created: true,
306
+ skillVersion: null,
307
+ templateId,
308
+ }, t));
309
+ }
310
+ const prepared = [];
311
+ for (const tpl of diff.templates) {
312
+ const update = diff.updates.get(tpl.id);
313
+ if (!update || !tpl.connector)
314
+ continue;
315
+ try {
316
+ prepared.push({
317
+ connectorId: tpl.connector.id,
318
+ p: await this.d.connectors.prepareTemplateUpdate(actor, tpl.connector.connector, update),
319
+ });
320
+ }
321
+ catch (e) {
322
+ throw under(e, `connectors.${tpl.id}`);
323
+ }
324
+ }
325
+ // Finish, in one transaction: changed connectors, removals, the version switch
326
+ await this.d.db.transaction().execute(async (t) => {
327
+ await lockOrgForActions(t, orgId, actor, diff.actions);
328
+ const row = await t
329
+ .selectFrom("plugin_installs")
330
+ .select(["status", sql `upgrade->>'id'`.as("upgradeId")])
331
+ .where("id", "=", installId)
332
+ .forUpdate()
333
+ .executeTakeFirst();
334
+ if (row?.status !== "upgrading" || row.upgradeId !== upgradeId)
335
+ throw cleanedUp();
336
+ await assertItemInstallable(t, orgId, itemId);
337
+ for (const c of prepared) {
338
+ await this.d.connectors.applyTemplateIn(t, orgId, c.connectorId, actor, c.p, opts.actorTokenId);
339
+ }
340
+ step = "finish";
341
+ for (const dep of diff.dependencies) {
342
+ if (dep.change !== "changed" || !dep.child)
343
+ continue;
344
+ const live = await t
345
+ .selectFrom("plugin_installs")
346
+ .select("version")
347
+ .where("id", "=", dep.child.installId)
348
+ .forShare()
349
+ .executeTakeFirst();
350
+ if (!live || !satisfiesRange(live.version, dep.range)) {
351
+ throw new ApiError("catalog.dependency_unresolved", `Dependency ${dep.name}@${dep.range} cannot be resolved: its install in this organization changed meanwhile; preview the upgrade again`, [{ dependency: dep.name, range: dep.range }]);
352
+ }
353
+ }
354
+ const report = await this.removeIn(t, orgId, actor, diff, disabled);
355
+ // An unchanged skill gets no new version, so the target's pinned suggestion goes onto its
356
+ // current version (when that version is this plugin's) here, with the version switch.
357
+ for (const sk of diff.skills) {
358
+ if (sk.change !== "unchanged" || !sk.ledger)
359
+ continue;
360
+ await t
361
+ .updateTable("skill_versions as v")
362
+ .set({ suggest_pinned: sk.suggestPinned === true })
363
+ .from("skills as s")
364
+ .whereRef("s.id", "=", "v.skill_id")
365
+ .whereRef("s.current_version", "=", "v.version")
366
+ .where("s.id", "=", sk.ledger.ref)
367
+ .where("s.org_id", "=", orgId)
368
+ .where("v.catalog_item_id", "=", itemId)
369
+ .where("v.suggest_pinned", "<>", sk.suggestPinned === true)
370
+ .execute();
371
+ }
372
+ await t
373
+ .updateTable("plugin_installs")
374
+ .set({ version: diff.to.version, status: "installed", upgrade: null, error: null })
375
+ .where("id", "=", installId)
376
+ .execute();
377
+ await this.d.requests.resolveByInstall(t, orgId, itemId, actor, opts);
378
+ await audit(t, {
379
+ actorAccountId: actor,
380
+ ...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
381
+ orgId,
382
+ action: "plugin.org_upgraded",
383
+ target: installId,
384
+ detail: {
385
+ itemId,
386
+ name: diff.name,
387
+ from: diff.from.version,
388
+ to: diff.to.version,
389
+ components: diff.components,
390
+ removed: report.removed,
391
+ kept: report.kept,
392
+ },
393
+ });
394
+ });
395
+ }
396
+ catch (e) {
397
+ throw await this.failed(orgId, actor, installId, upgradeId, diff, { done, unjournaled }, step, e, opts);
398
+ }
399
+ for (const p of disabled)
400
+ await this.d.orgConfigs.afterDisable(orgId, p);
401
+ const [view] = await this.installer.list(orgId, installId);
402
+ if (!view)
403
+ throw installNotFound();
404
+ return view;
405
+ }
406
+ /**
407
+ * Undoes a failed upgrade (its journal, in one transaction): the install back `installed` at the
408
+ * old version with `error = { step, code }`, audit `plugin.org_upgrade_failed`. When the journal
409
+ * is no longer this upgrade's (another request cleaned it up as interrupted and undid what it
410
+ * journaled), only the step whose journal write failed is undone and the install row is left as
411
+ * the cleanup set it. Returns the error to throw: 502 `plugin.upgrade_failed` (a dependency
412
+ * another install is still installing: its 409 as it is).
413
+ */
414
+ async failed(orgId, actor, installId, upgradeId, diff, steps, step, e, opts) {
415
+ const failure = e instanceof InstallFailure ? e : undefined;
416
+ const cause = failure ? failure.cause : e;
417
+ const code = cause instanceof ApiError ? cause.code : "internal";
418
+ const at = failure ? failure.step : step;
419
+ let rolledBack = failure?.rolledBack ?? true;
420
+ try {
421
+ const removed = await this.d.db.transaction().execute(async (t) => {
422
+ await lockOrg(t, orgId);
423
+ const row = await t
424
+ .selectFrom("plugin_installs")
425
+ .select(sql `upgrade->>'id'`.as("upgradeId"))
426
+ .where("id", "=", installId)
427
+ .forUpdate()
428
+ .executeTakeFirst();
429
+ const ours = row?.upgradeId === upgradeId;
430
+ // Cleaned up meanwhile: only the step whose journal write found the journal gone is ours to
431
+ // undo. When that step ran in the same transaction as its journal write (the extension
432
+ // enable, the ledger rows), the transaction rolled back with the failed write, so undoing
433
+ // it is a no-op (a never-enabled extension, a never-inserted row); undoing is still safe.
434
+ const undo = ours ? steps.done : steps.unjournaled ? [steps.unjournaled] : [];
435
+ const out = await this.installer.undoUpgradeIn(t, orgId, actor, installId, undo, "plugin_upgrade_rollback");
436
+ await t
437
+ .updateTable("plugin_installs")
438
+ .set({ status: "installed", upgrade: null, error: JSON.stringify({ step: at, code }) })
439
+ .where("id", "=", installId)
440
+ .where(sql `upgrade->>'id'`, "=", upgradeId)
441
+ .execute();
442
+ await audit(t, {
443
+ actorAccountId: actor,
444
+ ...(opts.actorTokenId !== undefined && { actorTokenId: opts.actorTokenId }),
445
+ orgId,
446
+ action: "plugin.org_upgrade_failed",
447
+ target: installId,
448
+ detail: {
449
+ itemId: diff.itemId,
450
+ name: diff.name,
451
+ from: diff.from.version,
452
+ to: diff.to.version,
453
+ step: at,
454
+ code,
455
+ rolledBack: undo,
456
+ ...(!ours && { cleanedUpMeanwhile: true }),
457
+ },
458
+ });
459
+ return out;
460
+ });
461
+ for (const r of removed) {
462
+ if (r.kind === "extension")
463
+ await this.d.orgConfigs.afterDisable(orgId, r.ref);
464
+ }
465
+ }
466
+ catch (u) {
467
+ // Left `upgrading` with its journal: undone once it is older than catalog.staleInstallMinutes
468
+ rolledBack = false;
469
+ this.d.log?.warn("plugin upgrade rollback failed", {
470
+ orgId,
471
+ installId,
472
+ error: u instanceof Error ? u.message : String(u),
473
+ });
474
+ }
475
+ if (cause instanceof ApiError && cause.code === "plugin.install_in_progress" && rolledBack) {
476
+ return cause;
477
+ }
478
+ const dependency = failure && failure.dependency.length > 0 ? failure.dependency : [];
479
+ const dep = dependency.length > 0 ? ` in dependency ${dependency.join(" > ")}` : "";
480
+ return new ApiError("plugin.upgrade_failed", rolledBack
481
+ ? `Upgrading the plugin failed at step ${at}${dep} (${code}); this organization's components are back at version ${diff.from.version}`
482
+ : `Upgrading the plugin failed at step ${at}${dep} (${code}) and rolling back failed; it is undone after catalog.staleInstallMinutes`, [
483
+ {
484
+ step: at,
485
+ code,
486
+ ...(dependency.length > 0 && { dependency: dependency.join(" > ") }),
487
+ ...(cause instanceof ApiError && { message: cause.message }),
488
+ ...(cause instanceof ApiError &&
489
+ cause.details !== undefined && { causeDetails: cause.details }),
490
+ rolledBack,
491
+ },
492
+ ]);
493
+ }
494
+ /**
495
+ * An upgrade interrupted for longer than `catalog.staleInstallMinutes` (a replica crashed
496
+ * midway): its journal is undone, the install back `installed` at the old version (error step
497
+ * `interrupted`), audit `plugin.org_upgrade_failed`. Nothing to do otherwise.
498
+ */
499
+ async recoverInterrupted(orgId, actor, installId) {
500
+ if (!UUID_RE.test(installId))
501
+ throw installNotFound();
502
+ const staleMinutes = this.d.staleInstallMinutes();
503
+ const removed = await this.d.db.transaction().execute(async (t) => {
504
+ const peek = await t
505
+ .selectFrom("plugin_installs")
506
+ .select("status")
507
+ .where("id", "=", installId)
508
+ .where("org_id", "=", orgId)
509
+ .executeTakeFirst();
510
+ if (peek?.status !== "upgrading")
511
+ return [];
512
+ await lockOrg(t, orgId);
513
+ const row = await t
514
+ .selectFrom("plugin_installs as a")
515
+ .innerJoin("catalog_items as i", "i.id", "a.item_id")
516
+ .select([
517
+ "a.status",
518
+ "a.upgrade",
519
+ "a.item_id",
520
+ "i.name",
521
+ sql `coalesce((a.upgrade->>'startedAt')::timestamptz < now() - make_interval(mins => ${staleMinutes}), false)`.as("stale"),
522
+ ])
523
+ .where("a.id", "=", installId)
524
+ .where("a.org_id", "=", orgId)
525
+ .forUpdate("a")
526
+ .executeTakeFirst();
527
+ if (row?.status !== "upgrading" || !row.stale)
528
+ return [];
529
+ const steps = journalEntries(row.upgrade);
530
+ const out = await this.installer.undoUpgradeIn(t, orgId, actor, installId, steps, "plugin_upgrade_cleanup");
531
+ await t
532
+ .updateTable("plugin_installs")
533
+ .set({
534
+ status: "installed",
535
+ upgrade: null,
536
+ error: JSON.stringify({ step: "interrupted", code: "internal" }),
537
+ })
538
+ .where("id", "=", installId)
539
+ .execute();
540
+ const j = row.upgrade;
541
+ await audit(t, {
542
+ actorAccountId: actor,
543
+ orgId,
544
+ action: "plugin.org_upgrade_failed",
545
+ target: installId,
546
+ detail: {
547
+ itemId: row.item_id,
548
+ name: row.name,
549
+ from: j?.from ?? null,
550
+ to: j?.to ?? null,
551
+ step: "interrupted",
552
+ code: "internal",
553
+ rolledBack: steps,
554
+ },
555
+ });
556
+ return out;
557
+ });
558
+ for (const r of removed) {
559
+ if (r.kind === "extension")
560
+ await this.d.orgConfigs.afterDisable(orgId, r.ref);
561
+ }
562
+ }
563
+ /**
564
+ * The removals of an upgrade, in the final transaction, by the uninstall rules
565
+ * (`decideRemovalIn`): only components this install created are removed, and only when nothing
566
+ * else uses them; one still used (by an agent, a connector or another extension) stays in the
567
+ * ledger marked `dropped` ("no longer in the plugin", still this install's), every other removed
568
+ * component leaves the ledger; a dropped component the target brings back is un-dropped. A dependency
569
+ * install is uninstalled by the same rules (another install using it takes it over). Skills ->
570
+ * connectors -> extension -> dependencies.
571
+ */
572
+ async removeIn(t, orgId, actor, diff, disabled) {
573
+ const report = { removed: [], kept: [] };
574
+ const reason = "plugin_upgrade";
575
+ const keep = (kind, ref, why) => report.kept.push({ kind, ref, reason: why });
576
+ const row = (kind, ref) => t
577
+ .updateTable("plugin_install_components")
578
+ .where("install_id", "=", diff.installId)
579
+ .where("kind", "=", kind)
580
+ .where("ref", "=", ref);
581
+ const dropRow = (kind, ref) => t
582
+ .deleteFrom("plugin_install_components")
583
+ .where("install_id", "=", diff.installId)
584
+ .where("kind", "=", kind)
585
+ .where("ref", "=", ref)
586
+ .execute();
587
+ const removed = [
588
+ ...diff.skills.flatMap((x) => (x.change === "removed" && x.ledger ? [x.ledger] : [])),
589
+ ...diff.agentTemplates.flatMap((x) => (x.change === "removed" && x.ledger ? [x.ledger] : [])),
590
+ ...diff.templates.flatMap((x) => (x.change === "removed" && x.ledger ? [x.ledger] : [])),
591
+ ...(diff.extension?.change === "removed"
592
+ ? diff.ledger.filter((l) => l.kind === "extension" && l.ref === diff.extension?.name)
593
+ : []),
594
+ ];
595
+ const agentRefs = await this.installer.agentRefs(t, orgId);
596
+ const self = new Set([diff.installId]);
597
+ for (const l of removed) {
598
+ if (!l.created) {
599
+ await dropRow(l.kind, l.ref);
600
+ keep(l.kind, l.ref, "pre_existing");
601
+ continue;
602
+ }
603
+ const outcome = await this.installer.decideRemovalIn(t, orgId, actor, { kind: l.kind, ref: l.ref, created: true, skillVersion: null }, self, agentRefs, reason);
604
+ if (outcome === "used_by_agents" ||
605
+ outcome === "used_by_connectors" ||
606
+ outcome === "used_by_plugins") {
607
+ // Still used: stays this install's, marked dropped, so an uninstall (or a later upgrade
608
+ // bringing it back) still finds it
609
+ await row(l.kind, l.ref).set({ dropped: true }).execute();
610
+ keep(l.kind, l.ref, outcome);
611
+ continue;
612
+ }
613
+ await dropRow(l.kind, l.ref);
614
+ if (outcome === "removed") {
615
+ report.removed.push({ kind: l.kind, ref: l.ref });
616
+ if (l.kind === "extension")
617
+ disabled.push(l.ref);
618
+ }
619
+ else if (outcome !== "gone")
620
+ keep(l.kind, l.ref, outcome);
621
+ }
622
+ // Components back in the plugin (dropped by an earlier upgrade, brought again by the target)
623
+ const back = [
624
+ ...diff.skills.flatMap((x) => x.change !== "removed" && x.ledger?.dropped
625
+ ? [{ kind: "skill", ref: x.ledger.ref }]
626
+ : []),
627
+ ...diff.agentTemplates.flatMap((x) => x.change !== "removed" && x.ledger?.dropped
628
+ ? [{ kind: "agent_template", ref: x.ledger.ref }]
629
+ : []),
630
+ ...diff.templates.flatMap((x) => x.change !== "removed" && x.ledger?.dropped
631
+ ? [{ kind: "connector", ref: x.ledger.ref }]
632
+ : []),
633
+ ...(diff.extension && diff.extension.change !== "removed"
634
+ ? diff.ledger
635
+ .filter((l) => l.kind === "extension" && l.ref === diff.extension?.name && l.dropped)
636
+ .map((l) => ({ kind: "extension", ref: l.ref }))
637
+ : []),
638
+ ];
639
+ for (const b of back)
640
+ await row(b.kind, b.ref).set({ dropped: false }).execute();
641
+ for (const dep of diff.dependencies) {
642
+ if (dep.change !== "removed" || !dep.child)
643
+ continue;
644
+ const ref = dep.child.installId;
645
+ await dropRow("plugin", ref);
646
+ if (!dep.child.ledger.created) {
647
+ keep("plugin", ref, "pre_existing");
648
+ continue;
649
+ }
650
+ const sub = { removed: [], kept: [] };
651
+ const outcome = await this.installer.uninstallIn(t, orgId, actor, ref, {
652
+ report: sub,
653
+ removing: new Set(),
654
+ from: diff.installId,
655
+ authorize: false,
656
+ reason,
657
+ opts: {},
658
+ });
659
+ for (const r of sub.removed)
660
+ if (r.kind === "extension")
661
+ disabled.push(r.ref);
662
+ if (outcome === "removed")
663
+ report.removed.push({ kind: "plugin", ref });
664
+ else if (outcome === "kept")
665
+ keep("plugin", ref, "used_by_other_install");
666
+ }
667
+ return report;
668
+ }
669
+ /**
670
+ * Computes an upgrade (see the class comment). `input` null: the preview (no input checks);
671
+ * otherwise the connector inputs are built and checked as at install.
672
+ */
673
+ async diff(orgId, actor, installId, version, input) {
674
+ const db = this.d.db;
675
+ if (!UUID_RE.test(installId))
676
+ throw installNotFound();
677
+ const install = await db
678
+ .selectFrom("plugin_installs as a")
679
+ .innerJoin("catalog_items as i", "i.id", "a.item_id")
680
+ .select(["a.item_id", "a.version", "a.status", "i.name", "i.policy", "i.deleted_at"])
681
+ .where("a.id", "=", installId)
682
+ .where("a.org_id", "=", orgId)
683
+ .executeTakeFirst();
684
+ if (!install || install.status === "failed")
685
+ throw installNotFound();
686
+ if (install.status !== "installed")
687
+ throw inProgress();
688
+ const itemId = install.item_id;
689
+ const name = install.name;
690
+ if (install.deleted_at !== null) {
691
+ throw new ApiError("catalog.item_not_found", "This plugin is no longer in the enterprise catalog");
692
+ }
693
+ if (install.policy === "disabled") {
694
+ throw new ApiError("catalog.item_disabled", "This catalog item is disabled by the enterprise admin and cannot be upgraded");
695
+ }
696
+ const versions = await db
697
+ .selectFrom("catalog_item_versions")
698
+ .select([
699
+ "version",
700
+ "manifest",
701
+ "object_prefix",
702
+ "files",
703
+ "extension_sha256",
704
+ "imported_at",
705
+ "source_format",
706
+ ])
707
+ .where("item_id", "=", itemId)
708
+ .execute();
709
+ const fromRow = versions.find((v) => v.version === install.version);
710
+ const toRow = versions.find((v) => v.version === version);
711
+ if (!fromRow) {
712
+ throw new ApiError("catalog.item_not_found", `The installed version ${install.version} is no longer in the enterprise catalog`);
713
+ }
714
+ if (!toRow)
715
+ throw new ApiError("catalog.item_not_found", `Version ${version} of this item not found`);
716
+ const from = fromRow;
717
+ const to = toRow;
718
+ if (to.version === from.version) {
719
+ // Confirming a preview whose upgrade already ran (a concurrent confirm won): stale
720
+ if (input !== null)
721
+ throw stale();
722
+ throw targetInvalid(`This organization already has version ${from.version} installed`, [
723
+ { reason: "same_version", version: from.version },
724
+ ]);
725
+ }
726
+ // Target rules (Decision 10)
727
+ if (to.manifest.extension) {
728
+ const deployed = await deployedExtension(db, this.d.host, name);
729
+ const allowed = deploymentTarget(versions, deployed);
730
+ if (allowed?.version !== to.version) {
731
+ throw targetInvalid(deployed === undefined
732
+ ? `The deployment does not run the extension of plugin ${name}; ask the enterprise admin to install it first`
733
+ : `The deployment runs the extension of plugin ${name} at version ${deployed.version}; this organization can upgrade only to ${allowed ? `catalog version ${allowed.version}` : "a catalog version carrying that extension, and none is imported"}`, [
734
+ {
735
+ reason: "deployment_version",
736
+ deploymentVersion: deployed?.version ?? null,
737
+ allowedVersion: allowed?.version ?? null,
738
+ },
739
+ ]);
740
+ }
741
+ }
742
+ // Installs in this org depending on this one must accept the target
743
+ const parents = await db
744
+ .selectFrom("plugin_install_components as c")
745
+ .innerJoin("plugin_installs as a", "a.id", "c.install_id")
746
+ .innerJoin("catalog_item_versions as v", (j) => j.onRef("v.item_id", "=", "a.item_id").onRef("v.version", "=", "a.version"))
747
+ .innerJoin("catalog_items as i", "i.id", "a.item_id")
748
+ .select(["i.name", "v.manifest"])
749
+ .where("c.kind", "=", "plugin")
750
+ .where("c.ref", "=", installId)
751
+ .where("a.org_id", "=", orgId)
752
+ .where("a.status", "<>", "failed")
753
+ .execute();
754
+ for (const p of parents) {
755
+ const range = p.manifest.dependencies?.find((x) => x.name === name)?.version;
756
+ if (range !== undefined && !satisfiesRange(to.version, range)) {
757
+ throw targetInvalid(`Plugin ${p.name} installed in this organization depends on ${name}@${range}, which version ${to.version} does not satisfy`, [{ reason: "dependent_range", dependent: p.name, range, version: to.version }]);
758
+ }
759
+ }
760
+ const ledger = await ledgerOf(db, installId);
761
+ const components = [];
762
+ // Extension
763
+ let extension = null;
764
+ let extensionConfig = false;
765
+ if (from.manifest.extension || to.manifest.extension) {
766
+ const change = !from.manifest.extension
767
+ ? "added"
768
+ : !to.manifest.extension
769
+ ? "removed"
770
+ : from.extension_sha256 !== to.extension_sha256 ||
771
+ from.manifest.extension.manifest.version !== to.manifest.extension.manifest.version
772
+ ? "changed"
773
+ : "unchanged";
774
+ extension = { change, name };
775
+ if (change === "added") {
776
+ await this.installer.planExtension(orgId, to.manifest);
777
+ extensionConfig = !(await isExtensionEnabled(db, orgId, name));
778
+ }
779
+ components.push({
780
+ kind: "extension",
781
+ ref: name,
782
+ change,
783
+ ...(change === "changed" && {
784
+ detail: `${from.manifest.extension?.manifest.version} -> ${to.manifest.extension?.manifest.version}`,
785
+ }),
786
+ });
787
+ }
788
+ // Dependencies (by plugin name)
789
+ const resolved = await resolveDependencies(db, to.manifest);
790
+ const childIds = ledger.filter((l) => l.kind === "plugin").map((l) => l.ref);
791
+ const children = childIds.length === 0
792
+ ? []
793
+ : await db
794
+ .selectFrom("plugin_installs as a")
795
+ .innerJoin("catalog_items as i", "i.id", "a.item_id")
796
+ .select(["a.id", "a.version", "i.name"])
797
+ .where("a.id", "in", childIds)
798
+ .execute();
799
+ const childOf = (n) => {
800
+ const c = children.find((x) => x.name === n);
801
+ const l = c && ledger.find((x) => x.kind === "plugin" && x.ref === c.id);
802
+ return c && l ? { installId: c.id, version: c.version, ledger: l } : undefined;
803
+ };
804
+ const dependencies = [];
805
+ const fromDeps = from.manifest.dependencies ?? [];
806
+ for (const r of resolved) {
807
+ const before = fromDeps.find((x) => x.name === r.name);
808
+ const child = childOf(r.name);
809
+ if (before && child) {
810
+ const change = before.version === r.range ? "unchanged" : "changed";
811
+ if (change === "changed" && !satisfiesRange(child.version, r.range)) {
812
+ throw new ApiError("catalog.dependency_unresolved", `Dependency ${r.name}@${r.range} cannot be resolved: this organization has version ${child.version} installed; upgrade it first`, [{ dependency: r.name, range: r.range }]);
813
+ }
814
+ dependencies.push({
815
+ name: r.name,
816
+ change,
817
+ range: r.range,
818
+ fromRange: before.version,
819
+ resolved: r,
820
+ child,
821
+ });
822
+ continue;
823
+ }
824
+ // New (or its install was not recorded): installed by the install flow
825
+ const live = await this.installer.liveDependency(db, orgId, r);
826
+ dependencies.push({
827
+ name: r.name,
828
+ change: "added",
829
+ range: r.range,
830
+ resolved: r,
831
+ plan: live !== undefined
832
+ ? null
833
+ : await this.installer.plan(orgId, actor, r.itemId, r.version, input === null ? null : {}, `dependencies.${r.name}.`, itemId),
834
+ });
835
+ }
836
+ for (const before of fromDeps) {
837
+ if (resolved.some((r) => r.name === before.name))
838
+ continue;
839
+ const child = childOf(before.name);
840
+ dependencies.push({
841
+ name: before.name,
842
+ change: "removed",
843
+ fromRange: before.version,
844
+ ...(child && { child }),
845
+ });
846
+ }
847
+ for (const dep of dependencies) {
848
+ components.push({
849
+ kind: "plugin",
850
+ ref: dep.name,
851
+ change: dep.change,
852
+ ...(dep.change === "changed" && { detail: `${dep.fromRange} -> ${dep.range}` }),
853
+ ...(dep.change === "added" && {
854
+ detail: dep.plan === null ? "already installed" : `installs ${dep.resolved?.version}`,
855
+ }),
856
+ });
857
+ }
858
+ // Skills (by name)
859
+ const fromNames = await catalogSkillNames(this.d.storage, from, from.manifest);
860
+ const toNames = await catalogSkillNames(this.d.storage, to, to.manifest);
861
+ const dirOf = (m, i) => packagePath((m.skills ?? [])[i]?.path);
862
+ const fromSkills = new Map(fromNames.map((n, i) => [n, dirOf(from.manifest, i)]));
863
+ const toSkills = new Map(toNames.map((n, i) => [n, dirOf(to.manifest, i)]));
864
+ const toPinned = new Set(toNames.filter((_, i) => (to.manifest.skills ?? [])[i]?.pinned === true));
865
+ const skillRefs = ledger.filter((l) => l.kind === "skill").map((l) => l.ref);
866
+ const liveSkills = skillRefs.length === 0
867
+ ? []
868
+ : await db
869
+ .selectFrom("skills")
870
+ .select(["id", "name"])
871
+ .where("id", "in", skillRefs)
872
+ .where("org_id", "=", orgId)
873
+ .where("deleted_at", "is", null)
874
+ .execute();
875
+ const ledgerOfSkill = (n) => {
876
+ const s = liveSkills.find((x) => x.name === n);
877
+ return s && ledger.find((l) => l.kind === "skill" && l.ref === s.id);
878
+ };
879
+ const skills = [];
880
+ for (const [n, dir] of toSkills) {
881
+ const before = fromSkills.get(n);
882
+ const l = ledgerOfSkill(n);
883
+ const change = before === undefined
884
+ ? "added"
885
+ : dirDigest(from, before) === dirDigest(to, dir)
886
+ ? "unchanged"
887
+ : "changed";
888
+ skills.push({
889
+ name: n,
890
+ change,
891
+ dir,
892
+ suggestPinned: toPinned.has(n),
893
+ ...(l && { ledger: l }),
894
+ });
895
+ }
896
+ for (const n of fromSkills.keys()) {
897
+ if (toSkills.has(n))
898
+ continue;
899
+ const l = ledgerOfSkill(n);
900
+ skills.push({ name: n, change: "removed", ...(l && { ledger: l }) });
901
+ }
902
+ for (const s of skills)
903
+ components.push({ kind: "skill", ref: s.name, change: s.change });
904
+ // The same skill conflict check as an install (spec §9.2)
905
+ const conflicts = await planSkillConflicts(db, orgId, {
906
+ itemId,
907
+ name,
908
+ version: to.version,
909
+ skillNames: toNames,
910
+ dependencies: dependencies
911
+ .filter((x) => x.change === "added")
912
+ .map((x) => ({ plan: x.plan ?? null })),
913
+ });
914
+ if (conflicts.length > 0)
915
+ throw skillConflictError(conflicts);
916
+ // Agent templates (by name; phase C)
917
+ const fromAgents = await readCatalogAgentTemplates(this.d.storage, from);
918
+ const toAgents = await readCatalogAgentTemplates(this.d.storage, to);
919
+ const templateRefs = ledger.filter((l) => l.kind === "agent_template").map((l) => l.ref);
920
+ const liveTemplates = templateRefs.length === 0
921
+ ? []
922
+ : await db
923
+ .selectFrom("agent_templates")
924
+ .select(["id", "name"])
925
+ .where("id", "in", templateRefs)
926
+ .where("org_id", "=", orgId)
927
+ .where("deleted_at", "is", null)
928
+ .execute();
929
+ const ledgerOfTemplate = (n) => {
930
+ const r = liveTemplates.find((x) => x.name === n);
931
+ return r && ledger.find((l) => l.kind === "agent_template" && l.ref === r.id);
932
+ };
933
+ const agentTemplates = [];
934
+ for (const a of toAgents) {
935
+ const n = a.template.name;
936
+ const before = fromAgents.find((x) => x.template.name === n);
937
+ const l = ledgerOfTemplate(n);
938
+ const change = before === undefined
939
+ ? "added"
940
+ : canonicalJson(before.template) === canonicalJson(a.template)
941
+ ? "unchanged"
942
+ : "changed";
943
+ agentTemplates.push({ name: n, change, template: a.template, ...(l && { ledger: l }) });
944
+ }
945
+ for (const a of fromAgents) {
946
+ const n = a.template.name;
947
+ if (toAgents.some((x) => x.template.name === n))
948
+ continue;
949
+ const l = ledgerOfTemplate(n);
950
+ agentTemplates.push({ name: n, change: "removed", ...(l && { ledger: l }) });
951
+ }
952
+ for (const a of agentTemplates) {
953
+ components.push({ kind: "agent_template", ref: a.name, change: a.change });
954
+ }
955
+ // Connector templates (by id)
956
+ const fromTemplates = await readCatalogTemplates(this.d.storage, from);
957
+ const toTemplates = await readCatalogTemplates(this.d.storage, to);
958
+ // The install's connectors by template; a dropped one (kept only for the agents using it) counts
959
+ // only while its connector is live, so a template the target brings back maps onto it
960
+ const connectorRows = ledger.filter((l) => l.kind === "connector" && l.template_id !== null);
961
+ const liveConnectors = connectorRows.length === 0
962
+ ? []
963
+ : await db
964
+ .selectFrom("connectors")
965
+ .select(["id", "connector", "endpoint", "auth"])
966
+ .where("id", "in", connectorRows.map((l) => l.ref))
967
+ .where("org_id", "=", orgId)
968
+ .where("deleted_at", "is", null)
969
+ .execute();
970
+ const connectorOf = (templateId) => {
971
+ const rows = connectorRows.filter((x) => x.template_id === templateId);
972
+ const l = rows.find((x) => !x.dropped) ??
973
+ rows.find((x) => liveConnectors.some((c) => c.id === x.ref));
974
+ const c = l && liveConnectors.find((x) => x.id === l.ref);
975
+ return {
976
+ ...(l && { ledger: l }),
977
+ ...(c && {
978
+ connector: {
979
+ id: c.id,
980
+ connector: c.connector,
981
+ endpoint: c.endpoint,
982
+ auth: c.auth,
983
+ },
984
+ }),
985
+ };
986
+ };
987
+ const deps = this.installer.templateDeps();
988
+ const templates = [];
989
+ for (const ct of toTemplates) {
990
+ const problem = templatePluginProblem(deps, ct.file.template);
991
+ const before = fromTemplates.find((x) => x.id === ct.id);
992
+ const mapped = connectorOf(ct.id);
993
+ if (!before && mapped.ledger?.dropped && mapped.connector) {
994
+ // Back in the plugin (an earlier upgrade dropped it, its connector was kept): the kept
995
+ // connector is updated to the template, not created again (its slug would be taken)
996
+ if (problem !== undefined)
997
+ throw new ApiError("catalog.not_deployed", problem);
998
+ if (mapped.connector.connector !== ct.file.template.connector) {
999
+ throw targetInvalid(`Connector template ${ct.id} comes back for another connector (${ct.file.template.plugin} "${ct.file.template.connector}"); uninstall the plugin and install the new version instead`, [
1000
+ {
1001
+ reason: "template_connector",
1002
+ template: ct.id,
1003
+ plugin: ct.file.template.plugin,
1004
+ connector: ct.file.template.connector,
1005
+ },
1006
+ ]);
1007
+ }
1008
+ const credentialsChanged = credentialKey(mapped.connector.auth) !==
1009
+ credentialKey(ct.file.template.auth);
1010
+ templates.push({
1011
+ id: ct.id,
1012
+ change: "changed",
1013
+ target: ct.file,
1014
+ ...mapped,
1015
+ credentialsChanged,
1016
+ });
1017
+ continue;
1018
+ }
1019
+ if (!before) {
1020
+ if (problem !== undefined)
1021
+ throw new ApiError("catalog.not_deployed", problem);
1022
+ templates.push({ id: ct.id, change: "added", target: ct.file, credentialsChanged: false });
1023
+ continue;
1024
+ }
1025
+ const change = canonicalJson(before.file) === canonicalJson(ct.file) ? "unchanged" : "changed";
1026
+ if (change === "changed") {
1027
+ if (before.file.template.plugin !== ct.file.template.plugin ||
1028
+ before.file.template.connector !== ct.file.template.connector) {
1029
+ throw targetInvalid(`Connector template ${ct.id} moves to another connector (${ct.file.template.plugin} "${ct.file.template.connector}"); uninstall the plugin and install the new version instead`, [
1030
+ {
1031
+ reason: "template_connector",
1032
+ template: ct.id,
1033
+ plugin: ct.file.template.plugin,
1034
+ connector: ct.file.template.connector,
1035
+ },
1036
+ ]);
1037
+ }
1038
+ if (problem !== undefined && mapped.connector) {
1039
+ throw new ApiError("catalog.not_deployed", problem);
1040
+ }
1041
+ }
1042
+ const credentialsChanged = change === "changed" &&
1043
+ mapped.connector !== undefined &&
1044
+ credentialKey(mapped.connector.auth) !==
1045
+ credentialKey(ct.file.template.auth);
1046
+ templates.push({ id: ct.id, change, target: ct.file, ...mapped, credentialsChanged });
1047
+ }
1048
+ for (const ct of fromTemplates) {
1049
+ if (toTemplates.some((x) => x.id === ct.id))
1050
+ continue;
1051
+ templates.push({
1052
+ id: ct.id,
1053
+ change: "removed",
1054
+ ...connectorOf(ct.id),
1055
+ credentialsChanged: false,
1056
+ });
1057
+ }
1058
+ for (const c of templates) {
1059
+ components.push({
1060
+ kind: "connector",
1061
+ ref: c.id,
1062
+ change: c.change,
1063
+ ...(c.credentialsChanged && { detail: "credentials change" }),
1064
+ ...(c.change !== "added" &&
1065
+ c.change !== "removed" &&
1066
+ !c.connector && { detail: "no connector in this organization" }),
1067
+ });
1068
+ }
1069
+ const inputs = {
1070
+ connectors: templates.filter((c) => c.change === "added").map((c) => c.id),
1071
+ credentialsChanged: templates.filter((c) => c.credentialsChanged).map((c) => c.id),
1072
+ extensionConfig,
1073
+ };
1074
+ // The inputs (as at install), when given
1075
+ const creates = new Map();
1076
+ const updates = new Map();
1077
+ if (input !== null) {
1078
+ for (const k of Object.keys(input.connectors ?? {})) {
1079
+ if (!inputs.connectors.includes(k) && !inputs.credentialsChanged.includes(k)) {
1080
+ throw fail(`connectors.${k}`, "This upgrade takes no input for this connector template (only added templates and changed credentials)");
1081
+ }
1082
+ }
1083
+ if (input.extensionConfig !== undefined && !inputs.extensionConfig) {
1084
+ throw fail("extensionConfig", "This upgrade does not enable the plugin's extension");
1085
+ }
1086
+ for (const c of templates) {
1087
+ if (c.change === "added") {
1088
+ creates.set(c.id, this.installer.planConnector(actor, { itemId, version: to.version, name }, { id: c.id, file: c.target }, input, "", undefined));
1089
+ continue;
1090
+ }
1091
+ if (c.change !== "changed" || !c.connector || !c.target)
1092
+ continue;
1093
+ updates.set(c.id, this.templateUpdate(itemId, to.version, c, input));
1094
+ }
1095
+ }
1096
+ const actions = new Set([
1097
+ ...requiredActions(from.manifest),
1098
+ ...requiredActions(to.manifest),
1099
+ ]);
1100
+ for (const dep of dependencies)
1101
+ if (dep.plan)
1102
+ for (const a of planActions(dep.plan))
1103
+ actions.add(a);
1104
+ const removedDeps = dependencies.filter((x) => x.change === "removed" && x.child);
1105
+ if (removedDeps.length > 0) {
1106
+ const manifests = await db
1107
+ .selectFrom("plugin_installs as a")
1108
+ .innerJoin("catalog_item_versions as v", (j) => j.onRef("v.item_id", "=", "a.item_id").onRef("v.version", "=", "a.version"))
1109
+ .select("v.manifest")
1110
+ .where("a.id", "in", removedDeps.map((x) => x.child?.installId))
1111
+ .execute();
1112
+ for (const m of manifests)
1113
+ for (const a of requiredActions(m.manifest))
1114
+ actions.add(a);
1115
+ }
1116
+ const lh = ledgerHash(ledger);
1117
+ const diffHash = sha256(canonicalJson({
1118
+ installId,
1119
+ from: from.version,
1120
+ to: to.version,
1121
+ fromFiles: filesDigest(from),
1122
+ toFiles: filesDigest(to),
1123
+ ledger: lh,
1124
+ components,
1125
+ inputs,
1126
+ }));
1127
+ return {
1128
+ installId,
1129
+ itemId,
1130
+ name,
1131
+ from,
1132
+ to,
1133
+ ledger,
1134
+ extension,
1135
+ skills,
1136
+ agentTemplates,
1137
+ templates,
1138
+ dependencies,
1139
+ components,
1140
+ inputs,
1141
+ diffHash,
1142
+ ledgerHash: lh,
1143
+ actions: [...actions],
1144
+ creates,
1145
+ updates,
1146
+ };
1147
+ }
1148
+ /**
1149
+ * The update of a changed template's connector: the new endpoint preset with the org's values at
1150
+ * the editable paths, the new auth preset and policies; credentials only when the auth method or
1151
+ * the credentials it needs changed (then the input must give what the template needs).
1152
+ */
1153
+ templateUpdate(itemId, version, c, input) {
1154
+ const template = c.target.template;
1155
+ const connector = c.connector;
1156
+ const path = `connectors.${c.id}`;
1157
+ const given = input.connectors?.[c.id];
1158
+ if (given !== undefined) {
1159
+ for (const k of ["name", "slug", "endpoint"]) {
1160
+ if (given[k] !== undefined) {
1161
+ throw fail(`${path}.${k}`, "An existing connector keeps its name, slug and endpoint fields");
1162
+ }
1163
+ }
1164
+ }
1165
+ const endpoint = structuredClone(template.endpoint);
1166
+ for (const e of template.editableEndpoint ?? []) {
1167
+ const v = getPath(connector.endpoint, e);
1168
+ if (v !== undefined)
1169
+ setPath(endpoint, e, v);
1170
+ }
1171
+ const auth = template.auth;
1172
+ const oauth = auth.type === "oauth_code" || auth.type === "oauth_client";
1173
+ if (c.credentialsChanged) {
1174
+ const missing = missingTemplateInput(template, given, { personalCredential: false });
1175
+ if (missing !== undefined) {
1176
+ throw fail(`${path}.${missing}`, `The auth method of this connector template changed; it needs ${missing === "client" ? "an OAuth client" : "an org credential"}`);
1177
+ }
1178
+ if (oauth && given?.credential !== undefined) {
1179
+ throw fail(`${path}.credential`, "This connector uses OAuth; provide client instead");
1180
+ }
1181
+ if (!oauth && given?.client !== undefined) {
1182
+ throw fail(`${path}.client`, "This connector does not use OAuth; provide credential instead");
1183
+ }
1184
+ }
1185
+ const credential = oauth ? given?.client : given?.credential;
1186
+ return {
1187
+ endpoint,
1188
+ auth,
1189
+ template: {
1190
+ itemId,
1191
+ version,
1192
+ templateId: c.id,
1193
+ ...(template.defaultToolPolicies !== undefined && {
1194
+ defaultToolPolicies: template.defaultToolPolicies,
1195
+ }),
1196
+ },
1197
+ resetCredentials: c.credentialsChanged,
1198
+ ...(c.credentialsChanged && credential !== undefined && { credential }),
1199
+ };
1200
+ }
1201
+ }
1202
+ const cleanedUp = () => new ApiError("plugin.install_not_found", "The upgrade was cleaned up as interrupted while it was running");
1203
+ /** The install's ledger rows. */
1204
+ function ledgerOf(db, installId) {
1205
+ return db
1206
+ .selectFrom("plugin_install_components")
1207
+ .select(["kind", "ref", "created", "skill_version", "template_id", "dropped"])
1208
+ .where("install_id", "=", installId)
1209
+ .orderBy("kind")
1210
+ .orderBy("ref")
1211
+ .execute();
1212
+ }
1213
+ /** A fingerprint of the ledger (order independent). */
1214
+ function ledgerHash(rows) {
1215
+ return sha256(rows
1216
+ .map((r) => `${r.kind}\0${r.ref}\0${r.created}\0${r.skill_version}\0${r.template_id}\0${r.dropped}\n`)
1217
+ .sort()
1218
+ .join(""));
1219
+ }