@desplega.ai/agent-swarm 1.119.6 → 1.121.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 (207) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-qe5zjcbr.js → actions-mwshtkf7.js} +6 -6
  3. package/dist/{anthropic-messages-qjxzsabf.js → anthropic-messages-4gn944cn.js} +29 -13
  4. package/dist/{app-31x7k3vj.js → app-skbty91x.js} +4 -4
  5. package/dist/{assistant-4crt9dve.js → assistant-tg7dhfj8.js} +13 -13
  6. package/dist/{azure-openai-responses-ntfd05zd.js → azure-openai-responses-79zc0wyy.js} +28 -13
  7. package/dist/{boot-reembed-8g69ttz4.js → boot-reembed-ntjmeyh2.js} +4 -4
  8. package/dist/{boot-reembed-q648j733.js → boot-reembed-q5x0xpcp.js} +5 -5
  9. package/dist/{boot-scrub-logs-sfmzd2hj.js → boot-scrub-logs-5gznsfbe.js} +4 -4
  10. package/dist/{claude-adapter-9n3nzt65.js → claude-adapter-vxvcym3r.js} +3 -3
  11. package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-g807894s.js} +2 -2
  12. package/dist/{cli-bpvgs2y6.js → cli-072t49hf.js} +2123 -1533
  13. package/dist/{cli-g6qz2kts.js → cli-0df2g86x.js} +336 -18
  14. package/dist/cli-1728qh70.js +791 -0
  15. package/dist/{cli-vg1jxfqz.js → cli-3b1ayz83.js} +5 -5
  16. package/dist/{cli-r93nz80j.js → cli-3fxqwfh3.js} +1 -1
  17. package/dist/{cli-w6rhc30s.js → cli-3jrdwnn9.js} +20 -5
  18. package/dist/{cli-pasyk3vc.js → cli-46etyprn.js} +10 -5
  19. package/dist/{cli-433c0f4b.js → cli-4pfweq4x.js} +8 -8
  20. package/dist/{cli-2r7s8jhp.js → cli-5c4327dx.js} +7 -1
  21. package/dist/{cli-4s84mw72.js → cli-5hpvd15x.js} +2 -2
  22. package/dist/{cli-branks1w.js → cli-6s3ytc5x.js} +1 -1
  23. package/dist/{cli-da3x3btq.js → cli-7pt9sc83.js} +2 -2
  24. package/dist/{cli-2gv8ygwr.js → cli-7pzae66m.js} +2 -2
  25. package/dist/{cli-qs7nb3zn.js → cli-8wrympd7.js} +27 -1
  26. package/dist/{cli-qw1ccvxm.js → cli-95rbe11h.js} +70 -51
  27. package/dist/{cli-6rvm3jkk.js → cli-agdrxyex.js} +1 -1
  28. package/dist/{cli-wr706gez.js → cli-av2s7my4.js} +167 -66
  29. package/dist/{cli-59jy217p.js → cli-avw9ev3x.js} +55 -21
  30. package/dist/{cli-fkxxh5d4.js → cli-b2mqkg1h.js} +2 -2
  31. package/dist/{cli-1arw00dp.js → cli-ccjwgarc.js} +12 -3
  32. package/dist/{cli-pscrc6tw.js → cli-ch7t5v7n.js} +5 -0
  33. package/dist/{cli-tghre7g6.js → cli-cw70hymf.js} +1 -0
  34. package/dist/{cli-n61x2aph.js → cli-dmzv0986.js} +816 -1335
  35. package/dist/{cli-d1reabed.js → cli-g9vva2qh.js} +2 -2
  36. package/dist/{cli-ner6af7m.js → cli-gdpx70nc.js} +24048 -23715
  37. package/dist/{cli-96feg24n.js → cli-ghq3v4c9.js} +2 -2
  38. package/dist/{cli-wxbnjgce.js → cli-ky8esb8t.js} +3 -3
  39. package/dist/{cli-gdqdkr3j.js → cli-nf7sadev.js} +2 -2
  40. package/dist/{cli-h6cf69x5.js → cli-p9w7kb3h.js} +1 -1
  41. package/dist/{cli-xeatwfbe.js → cli-ry656xem.js} +24 -17
  42. package/dist/{cli-q8dway4w.js → cli-s8bdcke2.js} +1 -1
  43. package/dist/cli-s8s499xy.js +81 -0
  44. package/dist/{cli-sbkwvn5e.js → cli-srjqn7fd.js} +1 -1
  45. package/dist/cli-tv7vekye.js +367 -0
  46. package/dist/{cli-gg5bd32n.js → cli-vg3zrw6h.js} +4 -4
  47. package/dist/{cli-vx0y68jv.js → cli-vp9r4ap4.js} +5 -4
  48. package/dist/{cli-mg280wsv.js → cli-we156bmz.js} +1 -1
  49. package/dist/{cli-86fdnerz.js → cli-wwsaj97q.js} +9 -4
  50. package/dist/{cli-e0y603nn.js → cli-x0e0yvnq.js} +101 -1
  51. package/dist/{cli-mw3xpgwb.js → cli-x4apc9t3.js} +4 -4
  52. package/dist/cli.js +26 -21
  53. package/dist/{codex-adapter-6j2b38tg.js → codex-adapter-mmy741zd.js} +8 -9
  54. package/dist/{codex-session-runner-vz215wgw.js → codex-session-runner-sazmx9zt.js} +8 -9
  55. package/dist/{commands-6xr20jbe.js → commands-cxxye5e4.js} +3 -3
  56. package/dist/{db-1mmvk1z4.js → db-ab1954b7.js} +5 -3
  57. package/dist/{google-generative-ai-n1d3xt4n.js → google-generative-ai-wwa2ked5.js} +14 -18
  58. package/dist/{google-vertex-mxst23gy.js → google-vertex-71xmdd7n.js} +14 -18
  59. package/dist/{handlers-xvrknk8w.js → handlers-fmfe2343.js} +13 -13
  60. package/dist/{hook-qm7he8wn.js → hook-ygnw3cvt.js} +7 -8
  61. package/dist/{http-hj08zq67.js → http-kactcc0z.js} +1128 -536
  62. package/dist/{index-688jph6a.js → index-6erq0wvk.js} +11 -11
  63. package/dist/{index-gze4fg2w.js → index-g86j2yt5.js} +9 -9
  64. package/dist/{index-etz7d8md.js → index-gmzs6a2q.js} +10 -10
  65. package/dist/{index-wj8dfpwh.js → index-hmp82wn0.js} +12 -12
  66. package/dist/{keepalive-94wn9yxw.js → keepalive-3cvt26dw.js} +18 -14
  67. package/dist/{lead-rvcqkp11.js → lead-ysf26cjr.js} +31 -32
  68. package/dist/{maintenance-qhfc4dnp.js → maintenance-9npwptbb.js} +5 -5
  69. package/dist/{mistral-conversations-ykcg66za.js → mistral-conversations-26ya4yah.js} +17 -13
  70. package/dist/{oauth-refresh-sweep-rj8wgvsw.js → oauth-refresh-sweep-0r2v76vw.js} +23 -17
  71. package/dist/{onboard-8bv8q1wc.js → onboard-ddb1674a.js} +2 -2
  72. package/dist/{openai-codex-responses-zy0907j9.js → openai-codex-responses-6rr2ye0s.js} +78 -38
  73. package/dist/{openai-completions-xk4p56n9.js → openai-completions-zfa9nkvt.js} +130 -33
  74. package/dist/{openai-responses-4gv3ca5d.js → openai-responses-9wmdks0g.js} +38 -14
  75. package/dist/{opencode-adapter-5bwzvbwj.js → opencode-adapter-h6rkxwrk.js} +2 -2
  76. package/dist/{openrouter-images-4fsrrzes.js → openrouter-images-dgq98729.js} +10 -3
  77. package/dist/{otel-impl-1kf6fr2m.js → otel-impl-b43bkq8e.js} +4 -4
  78. package/dist/{pi-messages-zb8v76wx.js → pi-messages-nzjbbz47.js} +4 -6
  79. package/dist/{pi-mono-adapter-9sg69kg3.js → pi-mono-adapter-ke789738.js} +2328 -1281
  80. package/dist/{pricing-refresh-e84tar2s.js → pricing-refresh-2rqf114z.js} +5 -5
  81. package/dist/{rbac-roles-atytq5ch.js → rbac-roles-5wkhc8xv.js} +5 -5
  82. package/dist/{rbac-roles-pg0zvnwn.js → rbac-roles-6vtafvzb.js} +4 -4
  83. package/dist/{seed-pricing-xd6nxq7k.js → seed-pricing-23wrb873.js} +4 -4
  84. package/dist/{setup-4z389yk0.js → setup-6rny4fk6.js} +2 -2
  85. package/dist/{worker-m61r0t7m.js → worker-yapewe14.js} +31 -32
  86. package/openapi.json +2671 -1811
  87. package/package.json +10 -5
  88. package/plugin/commands/swarm-chat.md +2 -0
  89. package/plugin/pi-skills/swarm-chat/SKILL.md +2 -0
  90. package/src/be/connection-bindings-blob-migration.ts +123 -0
  91. package/src/be/db-queries/mcp-oauth.ts +409 -247
  92. package/src/be/db-queries/oauth.ts +813 -131
  93. package/src/be/db.ts +97 -6
  94. package/src/be/migrations/117_unified_oauth.sql +624 -0
  95. package/src/be/migrations/119_agent_avatar.sql +7 -0
  96. package/src/be/migrations/120_task_title.sql +3 -0
  97. package/src/be/oauth-credential-bindings.ts +48 -14
  98. package/src/be/oauth-encryption-backfill.ts +84 -0
  99. package/src/be/oauth-refresh-sweep.ts +38 -23
  100. package/src/be/script-connections.ts +1130 -163
  101. package/src/be/script-credential-broker.ts +37 -27
  102. package/src/be/scripts/typecheck.ts +3 -2
  103. package/src/be/seed-capabilities.ts +62 -0
  104. package/src/be/seed-pricing.ts +1 -1
  105. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  106. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  107. package/src/be/vendored-openapi.ts +98 -0
  108. package/src/commands/runner.ts +69 -6
  109. package/src/http/agents.ts +23 -5
  110. package/src/http/all-routes.ts +1 -0
  111. package/src/http/core.ts +8 -5
  112. package/src/http/index.ts +18 -5
  113. package/src/http/mcp-bridge.ts +5 -2
  114. package/src/http/mcp-oauth.ts +103 -101
  115. package/src/http/oauth-callback.ts +332 -0
  116. package/src/http/oauth-generic.ts +14 -54
  117. package/src/http/poll.ts +5 -1
  118. package/src/http/script-connections.ts +651 -170
  119. package/src/http/scripts.ts +4 -2
  120. package/src/http/sessions.ts +1 -1
  121. package/src/http/tasks.ts +35 -0
  122. package/src/http/trackers/jira.ts +22 -34
  123. package/src/http/trackers/linear.ts +21 -36
  124. package/src/http/x.ts +6 -2
  125. package/src/jira/app.ts +12 -6
  126. package/src/jira/oauth.ts +48 -48
  127. package/src/linear/app.ts +44 -2
  128. package/src/linear/oauth.ts +12 -37
  129. package/src/oauth/app-validation.ts +27 -9
  130. package/src/oauth/ensure-mcp-token.ts +1 -0
  131. package/src/oauth/ensure-token.ts +331 -108
  132. package/src/oauth/identity-capture.ts +103 -0
  133. package/src/oauth/keepalive.ts +14 -9
  134. package/src/oauth/presets.ts +242 -0
  135. package/src/oauth/wrapper.ts +197 -76
  136. package/src/prompts/base-prompt.ts +48 -4
  137. package/src/prompts/session-templates.ts +13 -1
  138. package/src/providers/claude-managed-models.ts +8 -0
  139. package/src/rbac/legacy-policy.ts +2 -1
  140. package/src/rbac/permissions.ts +7 -3
  141. package/src/scripts-runtime/credential-broker/broker.ts +51 -8
  142. package/src/scripts-runtime/credential-broker/fetch-patch.ts +30 -3
  143. package/src/scripts-runtime/credential-broker/index.ts +1 -1
  144. package/src/scripts-runtime/credential-broker/types.ts +55 -8
  145. package/src/scripts-runtime/egress-secrets.ts +6 -2
  146. package/src/scripts-runtime/eval-harness.ts +2 -2
  147. package/src/scripts-runtime/executors/types.ts +2 -1
  148. package/src/scripts-runtime/loader.ts +3 -0
  149. package/src/scripts-runtime/types/stdlib.d.ts +11 -0
  150. package/src/server.ts +230 -126
  151. package/src/tests/agent-avatar-resolution.test.ts +123 -0
  152. package/src/tests/agents-list-model-display.test.ts +10 -0
  153. package/src/tests/base-prompt.test.ts +72 -0
  154. package/src/tests/claude-managed-adapter.test.ts +6 -0
  155. package/src/tests/connection-embedded-auth.test.ts +709 -0
  156. package/src/tests/context-window.test.ts +1 -0
  157. package/src/tests/credential-broker.test.ts +123 -125
  158. package/src/tests/db-queries-oauth.test.ts +126 -1
  159. package/src/tests/ensure-token.test.ts +44 -2
  160. package/src/tests/list-endpoint-slimming.test.ts +45 -0
  161. package/src/tests/mcp-oauth-manual-client.test.ts +20 -1
  162. package/src/tests/mcp-oauth-queries.test.ts +154 -8
  163. package/src/tests/oauth-access-token-tool.test.ts +37 -1
  164. package/src/tests/oauth-callback-flow.test.ts +534 -0
  165. package/src/tests/oauth-credential-bindings.test.ts +50 -22
  166. package/src/tests/oauth-presets.test.ts +275 -0
  167. package/src/tests/oauth-refresh-failure.test.ts +263 -0
  168. package/src/tests/oauth-refresh-sweep.test.ts +83 -3
  169. package/src/tests/oauth-wrapper.test.ts +77 -55
  170. package/src/tests/prompt-template-session.test.ts +6 -5
  171. package/src/tests/rbac-charact-misc-tools.test.ts +60 -0
  172. package/src/tests/rbac-engine.test.ts +2 -1
  173. package/src/tests/script-connections-http.test.ts +356 -11
  174. package/src/tests/script-connections.test.ts +102 -0
  175. package/src/tests/sdk-allowlist.test.ts +4 -1
  176. package/src/tests/seed-capabilities.test.ts +92 -0
  177. package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
  178. package/src/tests/sessions.test.ts +46 -0
  179. package/src/tests/spec-base-url.test.ts +266 -0
  180. package/src/tests/status.test.ts +3 -4
  181. package/src/tests/swarm-config-reserved-keys.test.ts +4 -2
  182. package/src/tests/task-title-route.test.ts +106 -0
  183. package/src/tests/tool-annotations.test.ts +6 -3
  184. package/src/tests/tracker-fold.test.ts +412 -0
  185. package/src/tests/unified-oauth-migration.test.ts +455 -0
  186. package/src/tests/vendored-openapi.test.ts +263 -0
  187. package/src/tools/credential-bindings/tool.ts +232 -77
  188. package/src/tools/oauth-access-token.ts +55 -6
  189. package/src/tools/script-connections/tool.ts +73 -53
  190. package/src/tools/swarm-config/set-config.ts +24 -50
  191. package/src/tools/update-profile.ts +15 -5
  192. package/src/tracker/types.ts +12 -0
  193. package/src/types.ts +29 -0
  194. package/src/utils/context-window.ts +1 -0
  195. package/src/workflows/executors/swarm-script.ts +6 -2
  196. package/vendored-openapi/README.md +22 -0
  197. package/vendored-openapi/github.json +7759 -0
  198. package/vendored-openapi/gmail.json +393 -0
  199. package/vendored-openapi/jira.json +3396 -0
  200. package/vendored-openapi/linear.json +252 -0
  201. package/vendored-openapi/manifest.json +128 -0
  202. package/vendored-openapi/slack.json +2895 -0
  203. package/dist/cli-0pygq9r1.js +0 -429
  204. package/dist/cli-8hhms4hp.js +0 -393
  205. package/dist/cli-z9gzq07x.js +0 -27
  206. package/src/scripts-runtime/credential-broker/store.ts +0 -72
  207. package/dist/{otel-g299k41b.js → otel-63jr5v38.js} +1 -1
package/src/server.ts CHANGED
@@ -5,6 +5,7 @@ import { startPricingRefreshLoop } from "./be/pricing-refresh";
5
5
  import { ensureRbacSeedsSynced } from "./be/rbac-roles";
6
6
  import { seedPricingFromModelsDev } from "./be/seed-pricing";
7
7
  import { registerGithubTaskReactions } from "./github/task-reactions";
8
+ import { loadGlobalConfigsIntoEnv } from "./http/core";
8
9
  import { isRbacEnabled } from "./rbac";
9
10
  import { registerCancelTaskTool } from "./tools/cancel-task";
10
11
  import { registerContextDiffTool } from "./tools/context-diff";
@@ -160,20 +161,83 @@ import {
160
161
  } from "./tools/workflows";
161
162
  import { resolveScriptsOnlyMode } from "./utils/scripts-only-mode";
162
163
 
164
+ // Every known capability, including the ones disabled by default. Exported for
165
+ // surfaces that must see the full tool registry regardless of deployment
166
+ // defaults (tests, drift checks).
167
+ export const ALL_CAPABILITIES = [
168
+ "core",
169
+ "task-pool",
170
+ "scripts",
171
+ "config",
172
+ "prompt-templates",
173
+ "mcp",
174
+ "profiles",
175
+ "services",
176
+ "scheduling",
177
+ "memory",
178
+ "workflows",
179
+ "pages",
180
+ "metrics",
181
+ "kv",
182
+ "slack",
183
+ "tracker",
184
+ "skills",
185
+ "messaging",
186
+ "repo",
187
+ "agentmail",
188
+ "kapso",
189
+ "swarm-x",
190
+ ] as const;
191
+
192
+ type CAPABILITIES_T = (typeof ALL_CAPABILITIES)[number];
193
+
163
194
  // Capability-based feature flags
164
- // Default: all capabilities enabled
165
- const DEFAULT_CAPABILITIES =
166
- "core,task-pool,profiles,services,scheduling,memory,workflows,pages,metrics,kv";
167
- const CAPABILITIES = new Set(
168
- (process.env.CAPABILITIES || DEFAULT_CAPABILITIES).split(",").map((s) => s.trim()),
169
- );
170
-
171
- export function hasCapability(cap: string): boolean {
172
- return CAPABILITIES.has(cap);
195
+ const DEFAULT_CAPABILITIES: string = [
196
+ "core",
197
+ "task-pool",
198
+ "scripts",
199
+ "config",
200
+ "mcp",
201
+ "profiles",
202
+ "scheduling",
203
+ "memory",
204
+ "workflows",
205
+ "pages",
206
+ "metrics",
207
+ "kv",
208
+ "slack",
209
+ "tracker",
210
+ "skills",
211
+ "repo",
212
+ //
213
+ // Disabled by default
214
+ //
215
+ // "services",
216
+ // "prompt-templates",
217
+ // "messaging",
218
+ // "swarm-x",
219
+ // "agentmail",
220
+ // "kapso",
221
+ ].join(",");
222
+
223
+ // Note: unknown names are kept (they never match hasCapability); workers
224
+ // reuse this env var for free-form skill tags, so dropping them here would
225
+ // break agent capability declarations. Empty entries (trailing commas) are
226
+ // filtered so they can't leak into enabledCapabilities payloads.
227
+ const getCapabilities = (): Set<CAPABILITIES_T> =>
228
+ new Set(
229
+ (process.env.CAPABILITIES || DEFAULT_CAPABILITIES)
230
+ .split(",")
231
+ .map((s) => s.trim() as CAPABILITIES_T)
232
+ .filter((s) => s.length > 0),
233
+ );
234
+
235
+ export function hasCapability(cap: CAPABILITIES_T): boolean {
236
+ return getCapabilities().has(cap);
173
237
  }
174
238
 
175
- export function getEnabledCapabilities(): string[] {
176
- return Array.from(CAPABILITIES);
239
+ export function getEnabledCapabilities(): CAPABILITIES_T[] {
240
+ return Array.from(getCapabilities());
177
241
  }
178
242
 
179
243
  /**
@@ -187,16 +251,28 @@ export function isScriptsOnlyMcp(): boolean {
187
251
  return resolveScriptsOnlyMode({ env: process.env.SCRIPTS_ONLY_MCP });
188
252
  }
189
253
 
190
- export function createServer(opts: { scriptsOnly?: boolean } = {}) {
254
+ export function createServer(opts: { scriptsOnly?: boolean; fullSurface?: boolean } = {}) {
255
+ // Reload env
256
+ loadGlobalConfigsIntoEnv(true);
257
+
258
+ // Capability flags shape the externally exposed MCP tool list only. Internal
259
+ // full-surface consumers (the scripts SDK bridge, drift-check tests) pass
260
+ // fullSurface to register every tool group regardless of CAPABILITIES.
261
+ // This shadows the module-level hasCapability for the registrations below.
262
+ const hasCapability = (cap: CAPABILITIES_T): boolean =>
263
+ opts.fullSurface === true || getCapabilities().has(cap);
264
+
191
265
  // Initialize database with WAL mode
192
266
  // Uses DATABASE_PATH env var for Docker volume compatibility (WAL needs .sqlite, .sqlite-wal, .sqlite-shm on same filesystem)
193
267
  initDb(process.env.DATABASE_PATH);
268
+
194
269
  // Phase 2: project the vendored models.dev snapshot into the pricing table.
195
270
  // Idempotent (INSERT OR IGNORE keyed on PK with effective_from=0); safe to
196
271
  // call on every boot. See src/be/seed-pricing.ts for the projection logic
197
272
  // and the manual-override constants for runtime-fee / ACU pricing.
198
273
  seedPricingFromModelsDev();
199
274
  startPricingRefreshLoop();
275
+
200
276
  try {
201
277
  ensureRbacSeedsSynced();
202
278
  } catch (err) {
@@ -233,94 +309,69 @@ export function createServer(opts: { scriptsOnly?: boolean } = {}) {
233
309
  registerScriptDeleteTool(server);
234
310
  registerScriptQueryTypesTool(server);
235
311
  registerScriptRunsTools(server);
312
+
236
313
  return server;
237
314
  }
238
315
 
239
- // Core tools - always registered
240
- registerJoinSwarmTool(server);
241
- registerPollTaskTool(server);
242
- registerGetSwarmTool(server);
243
- registerGetTasksTool(server);
244
- registerGetMetricsTool(server);
245
- registerSendTaskTool(server);
246
- registerGetTaskDetailsTool(server);
247
- registerStoreProgressTool(server);
248
- registerMyAgentInfoTool(server);
249
- registerCancelTaskTool(server);
250
-
251
- // User identity tools - always registered
252
- registerResolveUserTool(server);
253
- registerManageUserTool(server); // self-guards with lead check
254
-
255
- // Debug tools - always registered (self-guards with lead check)
256
- registerDbQueryTool(server);
257
- registerGetOauthAccessTokenTool(server);
258
-
259
- // Swarm config tools - always registered (config management is fundamental)
260
- registerSetConfigTool(server);
261
- registerGetConfigTool(server);
262
- registerListConfigTool(server);
263
- registerDeleteConfigTool(server);
264
- registerCredentialBindingsTool(server);
265
-
266
- // Repo management tools - always registered (repo config is fundamental)
267
- registerGetReposTool(server);
268
- registerUpdateRepoTool(server);
269
-
270
- // Prompt template tools - always registered (prompt management is fundamental)
271
- registerListPromptTemplatesTool(server);
272
- registerGetPromptTemplateTool(server);
273
- registerSetPromptTemplateTool(server);
274
- registerDeletePromptTemplateTool(server);
275
- registerPreviewPromptTemplateTool(server);
276
-
277
- // Reusable script catalog tools - always registered (HTTP MCP only in v1).
278
- registerScriptSearchTool(server);
279
- registerScriptConnectionsTool(server);
280
- registerScriptApisTool(server);
281
- registerScriptRunTool(server);
282
- registerScriptUpsertTool(server);
283
- registerScriptDeleteTool(server);
284
- registerScriptQueryTypesTool(server);
285
- registerScriptRunsTools(server);
286
-
287
- // External command routes - mirrors the `agent-swarm x ...` CLI surface.
288
- registerSwarmXTool(server);
289
-
290
- // Slack integration tools (always registered, will no-op if Slack not configured)
291
- registerSlackReplyTool(server);
292
- registerSlackReadTool(server);
293
- registerSlackPostTool(server);
294
- registerSlackStartThreadTool(server);
295
- registerSlackListChannelsTool(server);
296
- registerSlackUploadFileTool(server);
297
- registerSlackDownloadFileTool(server);
298
- registerSlackDeleteTool(server);
299
- registerSlackUpdateTool(server);
300
-
301
- // AgentMail integration tool (always registered, self-service inbox mapping)
302
- registerRegisterAgentmailInboxTool(server);
303
-
304
- // Kapso/WhatsApp integration tools (native inbound provisioning + outbound)
305
- registerRegisterKapsoNumberTool(server);
306
- registerUnregisterKapsoNumberTool(server);
307
- registerSendWhatsappMessageTool(server);
308
- registerReplyWhatsappMessageTool(server);
316
+ // Start of default-enabled capabilities
317
+
318
+ // Core capability - swarm membership, task flow, progress, user identity, and lead debug tools
319
+ if (hasCapability("core")) {
320
+ registerJoinSwarmTool(server);
321
+ registerPollTaskTool(server);
322
+ registerGetSwarmTool(server);
323
+ registerGetTasksTool(server);
324
+ registerGetMetricsTool(server);
325
+ registerSendTaskTool(server);
326
+ registerGetTaskDetailsTool(server);
327
+ registerStoreProgressTool(server);
328
+ registerMyAgentInfoTool(server);
329
+ registerCancelTaskTool(server);
330
+
331
+ // User identity tools
332
+ registerResolveUserTool(server);
333
+ registerManageUserTool(server); // self-guards with lead check
334
+
335
+ // Debug tools (self-guard with lead check)
336
+ registerDbQueryTool(server);
337
+ registerGetOauthAccessTokenTool(server);
338
+ }
309
339
 
310
340
  // Task pool capability - task pool operations (create unassigned, claim, release, accept, reject)
311
341
  if (hasCapability("task-pool")) {
312
342
  registerTaskActionTool(server);
313
343
  }
314
344
 
315
- // Core messaging tools - always registered (post/read are CORE_TOOLS)
316
- registerPostMessageTool(server);
317
- registerReadMessagesTool(server);
345
+ // Config capability - swarm config management and credential bindings
346
+ if (hasCapability("config")) {
347
+ registerSetConfigTool(server);
348
+ registerGetConfigTool(server);
349
+ registerListConfigTool(server);
350
+ registerDeleteConfigTool(server);
351
+ registerCredentialBindingsTool(server);
352
+ }
318
353
 
319
- // Messaging capability - channel management (CRUD on channels)
320
- if (hasCapability("messaging")) {
321
- registerListChannelsTool(server);
322
- registerCreateChannelTool(server);
323
- registerDeleteChannelTool(server);
354
+ // Scripts capability - reusable script catalog (HTTP MCP only in v1)
355
+ if (hasCapability("scripts")) {
356
+ registerScriptSearchTool(server);
357
+ registerScriptConnectionsTool(server);
358
+ registerScriptApisTool(server);
359
+ registerScriptRunTool(server);
360
+ registerScriptUpsertTool(server);
361
+ registerScriptDeleteTool(server);
362
+ registerScriptQueryTypesTool(server);
363
+ registerScriptRunsTools(server);
364
+ }
365
+
366
+ // MCP capability - managed MCP server registry (CRUD + install/uninstall)
367
+ if (hasCapability("mcp")) {
368
+ registerMcpServerCreateTool(server);
369
+ registerMcpServerUpdateTool(server);
370
+ registerMcpServerDeleteTool(server);
371
+ registerMcpServerGetTool(server);
372
+ registerMcpServerListTool(server);
373
+ registerMcpServerInstallTool(server);
374
+ registerMcpServerUninstallTool(server);
324
375
  }
325
376
 
326
377
  // Profiles capability - agent profile management
@@ -330,12 +381,10 @@ export function createServer(opts: { scriptsOnly?: boolean } = {}) {
330
381
  registerContextDiffTool(server);
331
382
  }
332
383
 
333
- // Services capability - PM2/background service registry
334
- if (hasCapability("services")) {
335
- registerRegisterServiceTool(server);
336
- registerUnregisterServiceTool(server);
337
- registerListServicesTool(server);
338
- registerUpdateServiceStatusTool(server);
384
+ // Repo capability - repository configuration management
385
+ if (hasCapability("repo")) {
386
+ registerGetReposTool(server);
387
+ registerUpdateRepoTool(server);
339
388
  }
340
389
 
341
390
  // Scheduling capability - scheduled task management
@@ -359,11 +408,13 @@ export function createServer(opts: { scriptsOnly?: boolean } = {}) {
359
408
  }
360
409
 
361
410
  // Tracker capability - external issue tracker integration
362
- registerTrackerStatusTool(server);
363
- registerTrackerLinkTaskTool(server);
364
- registerTrackerUnlinkTool(server);
365
- registerTrackerSyncStatusTool(server);
366
- registerTrackerMapAgentTool(server);
411
+ if (hasCapability("tracker")) {
412
+ registerTrackerStatusTool(server);
413
+ registerTrackerLinkTaskTool(server);
414
+ registerTrackerUnlinkTool(server);
415
+ registerTrackerSyncStatusTool(server);
416
+ registerTrackerMapAgentTool(server);
417
+ }
367
418
 
368
419
  // Workflows capability - DAG-based automation workflows
369
420
  if (hasCapability("workflows")) {
@@ -382,35 +433,34 @@ export function createServer(opts: { scriptsOnly?: boolean } = {}) {
382
433
  registerRequestHumanInputTool(server);
383
434
  }
384
435
 
385
- // Skills - always registered (skill management is available to all agents)
386
- registerSkillCreateTool(server);
387
- registerSkillUpdateTool(server);
388
- registerSkillDeleteTool(server);
389
- registerSkillGetTool(server);
390
- registerSkillGetFileTool(server);
391
- registerSkillListTool(server);
392
- registerSkillSearchTool(server);
393
- registerSkillInstallTool(server);
394
- registerSkillUninstallTool(server);
395
- registerSkillInstallRemoteTool(server);
396
- registerSkillSyncRemoteTool(server);
397
- registerSkillPublishTool(server);
436
+ // Skills capability - installable skill packages (create, search, install, publish)
437
+ if (hasCapability("skills")) {
438
+ registerSkillCreateTool(server);
439
+ registerSkillUpdateTool(server);
440
+ registerSkillDeleteTool(server);
441
+ registerSkillGetTool(server);
442
+ registerSkillGetFileTool(server);
443
+ registerSkillListTool(server);
444
+ registerSkillSearchTool(server);
445
+ registerSkillInstallTool(server);
446
+ registerSkillUninstallTool(server);
447
+ registerSkillInstallRemoteTool(server);
448
+ registerSkillSyncRemoteTool(server);
449
+ registerSkillPublishTool(server);
450
+ }
398
451
 
399
452
  // Pages capability - DB-backed lightweight artifacts (HTML / JSON specs).
400
- // Enabled by default (added to DEFAULT_CAPABILITIES in step-9 of the
401
- // db-backed-pages plan). Operators can disable via explicit
402
- // `CAPABILITIES=...` env without `pages`.
403
453
  if (hasCapability("pages")) {
404
454
  registerCreatePageTool(server);
405
455
  registerDeletePageTool(server);
406
456
  }
407
457
 
458
+ // Metrics capability - time-series metrics (DB-backed, for dashboards).
408
459
  if (hasCapability("metrics")) {
409
460
  registerCreateMetricTool(server);
410
461
  }
411
462
 
412
463
  // KV capability — namespaced Redis-like key/value (see src/be/migrations/061_kv_store.sql).
413
- // Enabled by default; opt out via `CAPABILITIES=...` without `kv`.
414
464
  if (hasCapability("kv")) {
415
465
  registerKvGetTool(server);
416
466
  registerKvSetTool(server);
@@ -419,14 +469,68 @@ export function createServer(opts: { scriptsOnly?: boolean } = {}) {
419
469
  registerKvListTool(server);
420
470
  }
421
471
 
422
- // MCP Servers - always registered
423
- registerMcpServerCreateTool(server);
424
- registerMcpServerUpdateTool(server);
425
- registerMcpServerDeleteTool(server);
426
- registerMcpServerGetTool(server);
427
- registerMcpServerListTool(server);
428
- registerMcpServerInstallTool(server);
429
- registerMcpServerUninstallTool(server);
472
+ // Slack capability - Slack integration tools (no-op if Slack is not configured)
473
+ if (hasCapability("slack")) {
474
+ registerSlackReplyTool(server);
475
+ registerSlackReadTool(server);
476
+ registerSlackPostTool(server);
477
+ registerSlackStartThreadTool(server);
478
+ registerSlackListChannelsTool(server);
479
+ registerSlackUploadFileTool(server);
480
+ registerSlackDownloadFileTool(server);
481
+ registerSlackDeleteTool(server);
482
+ registerSlackUpdateTool(server);
483
+ }
484
+
485
+ // End of default-enabled capabilities
486
+ // ----------------------------
487
+ // Start of default-disabled capabilities
488
+
489
+ // Prompt-templates capability - prompt template management (list/get/set/delete/preview)
490
+ if (hasCapability("prompt-templates")) {
491
+ registerListPromptTemplatesTool(server);
492
+ registerGetPromptTemplateTool(server);
493
+ registerSetPromptTemplateTool(server);
494
+ registerDeletePromptTemplateTool(server);
495
+ registerPreviewPromptTemplateTool(server);
496
+ }
497
+
498
+ // Agentmail capability - AgentMail integration (self-service inbox mapping)
499
+ if (hasCapability("agentmail")) {
500
+ registerRegisterAgentmailInboxTool(server);
501
+ }
502
+
503
+ // Kapso capability - Kapso/WhatsApp integration (native inbound provisioning + outbound)
504
+ if (hasCapability("kapso")) {
505
+ registerRegisterKapsoNumberTool(server);
506
+ registerUnregisterKapsoNumberTool(server);
507
+ registerSendWhatsappMessageTool(server);
508
+ registerReplyWhatsappMessageTool(server);
509
+ }
510
+
511
+ // Swarm-x capability - external command routes mirroring the `agent-swarm x ...` CLI surface
512
+ if (hasCapability("swarm-x")) {
513
+ registerSwarmXTool(server);
514
+ }
515
+
516
+ // Messaging capability - internal swarm chat (post/read messages, channel CRUD)
517
+ if (hasCapability("messaging")) {
518
+ registerPostMessageTool(server);
519
+ registerReadMessagesTool(server);
520
+
521
+ // Channel management (CRUD on channels)
522
+ registerListChannelsTool(server);
523
+ registerCreateChannelTool(server);
524
+ registerDeleteChannelTool(server);
525
+ }
526
+
527
+ // Services capability - PM2/background service registry
528
+ if (hasCapability("services")) {
529
+ registerRegisterServiceTool(server);
530
+ registerUnregisterServiceTool(server);
531
+ registerListServicesTool(server);
532
+ registerUpdateServiceStatusTool(server);
533
+ }
430
534
 
431
535
  return server;
432
536
  }
@@ -0,0 +1,123 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { getAgentColorToken, resolveAgentColor } from "../../apps/ui/src/lib/agent-color";
3
+ import {
4
+ AVATAR_ICON_CATALOG,
5
+ getAgentIcon,
6
+ resolveAgentIcon,
7
+ } from "../../apps/ui/src/lib/agent-icon";
8
+
9
+ /**
10
+ * Covers the pure avatar-resolution logic behind the customizable agent
11
+ * avatar feature (icon + color). These are the deterministic building
12
+ * blocks consumed by `agent-avatar.tsx` and the appearance picker — no React
13
+ * rendering involved, so they're testable with the existing `bun test`
14
+ * harness (apps/ui has no component-test infra; see PR discussion).
15
+ */
16
+ describe("agent avatar resolution", () => {
17
+ describe("getAgentIcon (deterministic fallback)", () => {
18
+ test("lead always resolves to Crown, regardless of id", () => {
19
+ expect(getAgentIcon({ agentId: "any-id", isLead: true })).toBe(AVATAR_ICON_CATALOG.crown);
20
+ expect(getAgentIcon({ role: "lead", agentId: "any-id" })).toBe(AVATAR_ICON_CATALOG.crown);
21
+ expect(getAgentIcon({ agentName: "Lead", agentId: "any-id" })).toBe(
22
+ AVATAR_ICON_CATALOG.crown,
23
+ );
24
+ });
25
+
26
+ test("same agentId always resolves to the same icon (deterministic)", () => {
27
+ const first = getAgentIcon({ agentId: "agent-123" });
28
+ const second = getAgentIcon({ agentId: "agent-123" });
29
+ expect(first).toBe(second);
30
+ });
31
+
32
+ test("different agentIds can resolve to different icons", () => {
33
+ // Not a strict guarantee for every pair (hash collisions are possible),
34
+ // but across a spread of ids we should see more than one icon.
35
+ const icons = new Set(
36
+ Array.from({ length: 20 }, (_, i) => getAgentIcon({ agentId: `worker-${i}` })),
37
+ );
38
+ expect(icons.size).toBeGreaterThan(1);
39
+ });
40
+
41
+ test("empty/missing seed falls back to Bot", () => {
42
+ expect(getAgentIcon({})).toBe(AVATAR_ICON_CATALOG.bot);
43
+ expect(getAgentIcon({ agentId: "", agentName: "" })).toBe(AVATAR_ICON_CATALOG.bot);
44
+ });
45
+ });
46
+
47
+ describe("resolveAgentIcon (custom avatar overrides deterministic fallback)", () => {
48
+ test("a known catalog icon wins over the deterministic fallback", () => {
49
+ const avatar = { type: "lucide" as const, icon: "trophy" };
50
+ const fallback = { agentId: "some-agent" };
51
+ expect(resolveAgentIcon(avatar, fallback)).toBe(AVATAR_ICON_CATALOG.trophy);
52
+ // Sanity check it's actually overriding, not coincidentally equal.
53
+ expect(resolveAgentIcon(avatar, fallback)).not.toBe(getAgentIcon(fallback));
54
+ });
55
+
56
+ test("an unknown icon name falls back to the deterministic default", () => {
57
+ const avatar = { type: "lucide" as const, icon: "not-a-real-icon" };
58
+ const fallback = { agentId: "some-agent" };
59
+ expect(resolveAgentIcon(avatar, fallback)).toBe(getAgentIcon(fallback));
60
+ });
61
+
62
+ test("null/undefined avatar (reset to default) falls back to the deterministic default", () => {
63
+ const fallback = { agentId: "some-agent", isLead: false };
64
+ expect(resolveAgentIcon(null, fallback)).toBe(getAgentIcon(fallback));
65
+ expect(resolveAgentIcon(undefined, fallback)).toBe(getAgentIcon(fallback));
66
+ });
67
+ });
68
+
69
+ describe("getAgentColorToken (deterministic fallback)", () => {
70
+ test("lead always resolves to primary", () => {
71
+ expect(getAgentColorToken({ role: "lead", agentId: "x" })).toBe("primary");
72
+ expect(getAgentColorToken({ agentName: "Lead", agentId: "x" })).toBe("primary");
73
+ });
74
+
75
+ test("same agentId always resolves to the same token (deterministic)", () => {
76
+ expect(getAgentColorToken({ agentId: "agent-abc" })).toBe(
77
+ getAgentColorToken({ agentId: "agent-abc" }),
78
+ );
79
+ });
80
+
81
+ test("empty/missing seed falls back to action-default", () => {
82
+ expect(getAgentColorToken({})).toBe("action-default");
83
+ });
84
+ });
85
+
86
+ describe("resolveAgentColor (custom hex overrides deterministic fallback)", () => {
87
+ test("a custom hex wins over the deterministic token", () => {
88
+ const avatar = { type: "lucide" as const, icon: "star", color: "#ff00aa" };
89
+ const fallback = { agentId: "some-agent" };
90
+ expect(resolveAgentColor(avatar, fallback)).toEqual({ kind: "custom", hex: "#ff00aa" });
91
+ });
92
+
93
+ test("a lucide avatar with no color falls back to the deterministic token", () => {
94
+ const avatar = { type: "lucide" as const, icon: "star" };
95
+ const fallback = { agentId: "some-agent" };
96
+ expect(resolveAgentColor(avatar, fallback)).toEqual({
97
+ kind: "token",
98
+ token: getAgentColorToken(fallback),
99
+ });
100
+ });
101
+
102
+ test("an empty-string color is treated as unset (falls back)", () => {
103
+ const avatar = { type: "lucide" as const, icon: "star", color: "" };
104
+ const fallback = { agentId: "some-agent" };
105
+ expect(resolveAgentColor(avatar, fallback)).toEqual({
106
+ kind: "token",
107
+ token: getAgentColorToken(fallback),
108
+ });
109
+ });
110
+
111
+ test("null/undefined avatar (reset to default) falls back to the deterministic token", () => {
112
+ const fallback = { agentId: "some-agent" };
113
+ expect(resolveAgentColor(null, fallback)).toEqual({
114
+ kind: "token",
115
+ token: getAgentColorToken(fallback),
116
+ });
117
+ expect(resolveAgentColor(undefined, fallback)).toEqual({
118
+ kind: "token",
119
+ token: getAgentColorToken(fallback),
120
+ });
121
+ });
122
+ });
123
+ });
@@ -44,6 +44,11 @@ describe("agents list model display", () => {
44
44
  });
45
45
 
46
46
  test("presents latest Anthropic direct model ids as readable labels", () => {
47
+ expect(getAgentModelPresentation("claude-opus-5")).toMatchObject({
48
+ label: "Claude Opus 5",
49
+ provider: "Anthropic",
50
+ providerId: "anthropic",
51
+ });
47
52
  expect(getAgentModelPresentation("claude-fable-5")).toMatchObject({
48
53
  label: "Claude Fable 5",
49
54
  provider: "Anthropic",
@@ -59,6 +64,11 @@ describe("agents list model display", () => {
59
64
  provider: "Anthropic",
60
65
  providerId: "anthropic",
61
66
  });
67
+ expect(getAgentModelPresentation("opus")).toMatchObject({
68
+ label: "Claude Opus 5",
69
+ provider: "Anthropic",
70
+ providerId: "anthropic",
71
+ });
62
72
  });
63
73
 
64
74
  // ── Phase 6 (reasoning-effort plan) ─────────────────────────────────────────
@@ -739,3 +739,75 @@ describe("getBasePrompt — conditional Slack templates", () => {
739
739
  expect(result).toContain("C123");
740
740
  });
741
741
  });
742
+
743
+ // ---------------------------------------------------------------------------
744
+ // Server capability gating (serverCapabilities = the API server's enabled
745
+ // tool groups, reported on registration; distinct from agent skill tags)
746
+ // ---------------------------------------------------------------------------
747
+
748
+ describe("getBasePrompt — serverCapabilities gating", () => {
749
+ test("messaging section kept when serverCapabilities is unknown (legacy servers registered the tools unconditionally)", async () => {
750
+ const result = await getBasePrompt(minimalArgs);
751
+ expect(result).toContain("post-message");
752
+ expect(result).toContain("read-messages");
753
+ });
754
+
755
+ test("messaging section omitted when server capabilities omit messaging", async () => {
756
+ const result = await getBasePrompt({
757
+ ...minimalArgs,
758
+ serverCapabilities: ["core"],
759
+ });
760
+ expect(result).not.toContain("post-message");
761
+ expect(result).not.toContain("read-messages");
762
+ });
763
+
764
+ test("messaging section included when the server enables messaging", async () => {
765
+ const result = await getBasePrompt({
766
+ ...minimalArgs,
767
+ serverCapabilities: ["core", "messaging"],
768
+ });
769
+ expect(result).toContain("#### Swarm Messaging");
770
+ expect(result).toContain("post-message");
771
+ expect(result).toContain("read-messages");
772
+ });
773
+
774
+ test("services section dropped when server capabilities omit services", async () => {
775
+ const result = await getBasePrompt({
776
+ ...minimalArgs,
777
+ serverCapabilities: ["core"],
778
+ });
779
+ expect(result).not.toContain("register-service");
780
+ });
781
+
782
+ test("services section kept for legacy servers that report no capabilities", async () => {
783
+ const result = await getBasePrompt(minimalArgs);
784
+ expect(result).toContain("register-service");
785
+ });
786
+
787
+ test("server-enabled services shows section even without the agent skill tag", async () => {
788
+ const result = await getBasePrompt({
789
+ ...minimalArgs,
790
+ capabilities: ["typescript"],
791
+ serverCapabilities: ["core", "services"],
792
+ });
793
+ expect(result).toContain("register-service");
794
+ });
795
+
796
+ test("slack section dropped when server capabilities omit slack", async () => {
797
+ enableSlackPromptTools();
798
+ const result = await getBasePrompt({
799
+ ...minimalArgs,
800
+ serverCapabilities: ["core"],
801
+ });
802
+ expect(result).not.toContain("slack-list-channels");
803
+ });
804
+
805
+ test("slack section kept when the server enables slack", async () => {
806
+ enableSlackPromptTools();
807
+ const result = await getBasePrompt({
808
+ ...minimalArgs,
809
+ serverCapabilities: ["core", "slack"],
810
+ });
811
+ expect(result).toContain("slack-list-channels");
812
+ });
813
+ });
@@ -802,6 +802,12 @@ describe("ClaudeManagedAdapter (Phase 4) — repo provisioning + cost data", ()
802
802
  });
803
803
 
804
804
  test("CLAUDE_MANAGED_MODEL_PRICING covers sonnet, opus, haiku at minimum", () => {
805
+ expect(CLAUDE_MANAGED_MODEL_PRICING["claude-opus-5"]).toEqual({
806
+ inputPerMillion: 5.0,
807
+ outputPerMillion: 25.0,
808
+ cacheReadPerMillion: 0.5,
809
+ cacheWritePerMillion: 6.25,
810
+ });
805
811
  expect(CLAUDE_MANAGED_MODEL_PRICING["claude-fable-5"]).toEqual({
806
812
  inputPerMillion: 10.0,
807
813
  outputPerMillion: 50.0,