@desplega.ai/agent-swarm 1.123.0 → 1.124.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 (234) hide show
  1. package/README.md +2 -1
  2. package/dist/{actions-e4c9f0xy.js → actions-z3d0fk7z.js} +6 -6
  3. package/dist/{app-vmvmj2v3.js → app-q2rc9tdr.js} +4 -4
  4. package/dist/{assistant-yn7a792a.js → assistant-3stjq7fz.js} +9 -9
  5. package/dist/{boot-reembed-xg1r86h2.js → boot-reembed-5867whnb.js} +3 -3
  6. package/dist/{boot-reembed-chxty50a.js → boot-reembed-v6r1df32.js} +4 -4
  7. package/dist/{boot-scrub-logs-exwddvp5.js → boot-scrub-logs-8b8jegk3.js} +2 -2
  8. package/dist/{cli-1592nfd6.js → cli-0925phzv.js} +1 -1
  9. package/dist/{cli-hbbxmhec.js → cli-2yr6edr7.js} +4 -4
  10. package/dist/{cli-8cq4z4x7.js → cli-3z4q09tn.js} +182 -19
  11. package/dist/{cli-vz9drmce.js → cli-5jn6wnd9.js} +17 -8
  12. package/dist/{cli-5arfywtm.js → cli-5ncdb7ff.js} +19 -9
  13. package/dist/{cli-3tv9z4wx.js → cli-92bqgbgy.js} +1 -1
  14. package/dist/{cli-fjf04hpa.js → cli-dq2ssyf0.js} +48 -56
  15. package/dist/{cli-mngsj9fx.js → cli-gkvf8d83.js} +1 -1
  16. package/dist/{cli-mepk1j0t.js → cli-gzjsamwc.js} +2 -2
  17. package/dist/{cli-c9m59f83.js → cli-h89g0qrd.js} +1 -1
  18. package/dist/{cli-n8mf7x4b.js → cli-jp4mdj9d.js} +2 -2
  19. package/dist/{cli-822d4bjk.js → cli-kk58tg33.js} +7 -7
  20. package/dist/{cli-9hj1adgp.js → cli-mcez7z4p.js} +1 -1
  21. package/dist/{cli-byv3720g.js → cli-mmk5vagg.js} +2 -2
  22. package/dist/{cli-zdp11t8m.js → cli-p0ec4vv6.js} +1 -1
  23. package/dist/{cli-h2say6xk.js → cli-p0zgv8pj.js} +3 -1
  24. package/dist/{cli-8ef5ky75.js → cli-perb20ma.js} +1 -1
  25. package/dist/{cli-zkj6f2sk.js → cli-qf7acvyr.js} +90 -4
  26. package/dist/{cli-kmm1rwmp.js → cli-qwsagsmg.js} +1 -1
  27. package/dist/{cli-we91ywvy.js → cli-sqv6c9b8.js} +6 -6
  28. package/dist/{cli-np0yhmmk.js → cli-vm20sh8d.js} +3 -3
  29. package/dist/{cli-p11rf59d.js → cli-wn3m6q54.js} +3 -3
  30. package/dist/{cli-mkpf4ndf.js → cli-x268say8.js} +2976 -5621
  31. package/dist/{cli-ngbsk4yx.js → cli-y43n987v.js} +1 -1
  32. package/dist/{cli-4509mwm5.js → cli-ykthjw29.js} +8 -8
  33. package/dist/{cli-9eka6jwv.js → cli-zxb72ctk.js} +2 -2
  34. package/dist/cli.js +12 -10
  35. package/dist/{commands-sjk3zfyr.js → commands-sda76yn8.js} +2 -2
  36. package/dist/{db-2je5agjb.js → db-vn9e4rqj.js} +6 -2
  37. package/dist/{e2b-2s4psnbf.js → e2b-q345y8s4.js} +0 -4
  38. package/dist/{handlers-yzv369hf.js → handlers-xfja18ac.js} +9 -9
  39. package/dist/{hook-rfsj2r8x.js → hook-2medrp47.js} +1 -40
  40. package/dist/{http-v9rr144m.js → http-937pyjfc.js} +99 -44
  41. package/dist/{index-nr2199m6.js → index-2bqd2q9v.js} +11 -11
  42. package/dist/{index-3xtk777g.js → index-gw4en7jg.js} +9 -9
  43. package/dist/{index-c3tan44f.js → index-knqczmnm.js} +10 -10
  44. package/dist/{index-smjdc3h9.js → index-vah60vxp.js} +8 -8
  45. package/dist/{keepalive-5ds53bjd.js → keepalive-20rd6hxh.js} +5 -5
  46. package/dist/{lead-5xhez25b.js → lead-7w2k3a49.js} +21 -21
  47. package/dist/{maintenance-b43d24q6.js → maintenance-p3x918yw.js} +4 -4
  48. package/dist/{oauth-refresh-sweep-qe286vn2.js → oauth-refresh-sweep-shm0nzm4.js} +4 -4
  49. package/dist/{onboard-c4r3ds3z.js → onboard-py9dnf54.js} +6 -2
  50. package/dist/{otel-impl-nv3rjw61.js → otel-impl-7yjkfg68.js} +1 -1
  51. package/dist/{pi-mono-adapter-2syx1x4d.js → pi-mono-adapter-z6h98fva.js} +4 -38
  52. package/dist/{pricing-refresh-5ct2wp4k.js → pricing-refresh-3snb6370.js} +4 -4
  53. package/dist/{rbac-roles-m502w0qv.js → rbac-roles-5e72bbpk.js} +3 -3
  54. package/dist/{rbac-roles-b82cxpxn.js → rbac-roles-gqxdp8st.js} +4 -4
  55. package/dist/{seed-pricing-jhjphbf0.js → seed-pricing-bg42d7h2.js} +3 -3
  56. package/dist/{setup-zbaspydq.js → setup-nzk3kbbq.js} +2 -2
  57. package/dist/{worker-snvhpax5.js → worker-0p1jdzkm.js} +21 -21
  58. package/openapi.json +146 -1
  59. package/package.json +3 -1
  60. package/src/be/db.ts +81 -4
  61. package/src/be/models-catalog.ts +156 -0
  62. package/src/be/modelsdev-cache.ts +14 -1
  63. package/src/be/pricing-refresh.ts +2 -0
  64. package/src/be/scripts/typecheck.ts +17 -4
  65. package/src/be/seed-scripts/catalog/task-context-gathering.ts +1 -1
  66. package/src/commands/onboard/env-generator.ts +12 -0
  67. package/src/e2b/env.ts +0 -4
  68. package/src/hooks/hook.ts +0 -67
  69. package/src/http/all-routes.ts +1 -0
  70. package/src/http/index.ts +2 -0
  71. package/src/http/models-catalog.ts +65 -0
  72. package/src/http/workflows.ts +16 -4
  73. package/src/prompts/base-prompt.ts +8 -7
  74. package/src/prompts/session-templates.ts +106 -12
  75. package/src/providers/pi-mono-adapter.ts +12 -2
  76. package/src/providers/pi-mono-extension.ts +0 -64
  77. package/src/providers/pricing-sources.md +16 -1
  78. package/src/scripts-runtime/eval-harness.ts +18 -1
  79. package/src/scripts-runtime/swarm-sdk.ts +10 -0
  80. package/src/scripts-runtime/types/stdlib.d.ts +13 -0
  81. package/src/scripts-runtime/types/swarm-sdk.d.ts +13 -0
  82. package/src/server-user.ts +4 -7
  83. package/src/slack/blocks.ts +21 -8
  84. package/src/slack/responses.ts +27 -51
  85. package/src/slack/watcher.ts +22 -5
  86. package/src/telemetry.ts +169 -3
  87. package/src/tests/base-prompt.test.ts +79 -0
  88. package/src/tests/mcp-tools-user.test.ts +62 -16
  89. package/src/tests/mcp-tools.test.ts +6 -3
  90. package/src/tests/memory-edit.test.ts +21 -4
  91. package/src/tests/model-groups-live-catalog.test.ts +65 -0
  92. package/src/tests/models-catalog.test.ts +119 -0
  93. package/src/tests/oauth-access-token-tool.test.ts +71 -1
  94. package/src/tests/pricing-refresh.test.ts +25 -0
  95. package/src/tests/prompt-template-session.test.ts +142 -5
  96. package/src/tests/rbac-charact-skills.test.ts +81 -14
  97. package/src/tests/script-apis-mcp.test.ts +56 -8
  98. package/src/tests/scripts-mcp-e2e.test.ts +157 -8
  99. package/src/tests/scripts-typecheck.test.ts +17 -0
  100. package/src/tests/sdk-allowlist.test.ts +34 -1
  101. package/src/tests/seed-scripts.test.ts +43 -0
  102. package/src/tests/slack-blocks.test.ts +2 -1
  103. package/src/tests/slack-inline-output.test.ts +24 -30
  104. package/src/tests/slack-watcher.test.ts +42 -0
  105. package/src/tests/steer-task-tool.test.ts +37 -8
  106. package/src/tests/steering-transport.test.ts +18 -3
  107. package/src/tests/swarm-tool-result-gate.test.ts +399 -0
  108. package/src/tests/task-tools-ctx.test.ts +49 -17
  109. package/src/tests/task-tools-ownership.test.ts +41 -13
  110. package/src/tests/telemetry-init.test.ts +376 -2
  111. package/src/tests/tool-output-agent-id.test.ts +14 -2
  112. package/src/tests/workflow-http-v2.test.ts +116 -0
  113. package/src/tools/accept-steer.ts +45 -53
  114. package/src/tools/cancel-task.ts +31 -41
  115. package/src/tools/context-diff.ts +19 -63
  116. package/src/tools/context-history.ts +22 -51
  117. package/src/tools/create-channel.ts +24 -39
  118. package/src/tools/create-metric.ts +19 -82
  119. package/src/tools/create-page.ts +20 -81
  120. package/src/tools/credential-bindings/tool.ts +125 -246
  121. package/src/tools/db-query.ts +13 -24
  122. package/src/tools/delete-channel.ts +25 -71
  123. package/src/tools/delete-page.ts +27 -75
  124. package/src/tools/get-metrics.ts +29 -22
  125. package/src/tools/get-swarm.ts +44 -15
  126. package/src/tools/get-task-details.ts +150 -32
  127. package/src/tools/get-tasks.ts +63 -29
  128. package/src/tools/inject-learning.ts +8 -38
  129. package/src/tools/join-swarm.ts +45 -47
  130. package/src/tools/kv/kv-delete.ts +13 -37
  131. package/src/tools/kv/kv-get.ts +38 -29
  132. package/src/tools/kv/kv-incr.ts +28 -56
  133. package/src/tools/kv/kv-list.ts +40 -31
  134. package/src/tools/kv/kv-set.ts +34 -76
  135. package/src/tools/list-channels.ts +24 -17
  136. package/src/tools/list-services.ts +37 -45
  137. package/src/tools/manage-user.ts +53 -66
  138. package/src/tools/mcp-servers/mcp-server-create.ts +19 -55
  139. package/src/tools/mcp-servers/mcp-server-delete.ts +9 -39
  140. package/src/tools/mcp-servers/mcp-server-get.ts +9 -35
  141. package/src/tools/mcp-servers/mcp-server-install.ts +13 -60
  142. package/src/tools/mcp-servers/mcp-server-list.ts +28 -26
  143. package/src/tools/mcp-servers/mcp-server-uninstall.ts +10 -36
  144. package/src/tools/mcp-servers/mcp-server-update.ts +13 -57
  145. package/src/tools/memory-delete.ts +16 -46
  146. package/src/tools/memory-edit.ts +48 -49
  147. package/src/tools/memory-get.ts +40 -36
  148. package/src/tools/memory-rate.ts +14 -44
  149. package/src/tools/memory-search.ts +46 -52
  150. package/src/tools/my-agent-info.ts +39 -33
  151. package/src/tools/oauth-access-token.ts +9 -24
  152. package/src/tools/poll-task.ts +50 -77
  153. package/src/tools/post-message.ts +24 -38
  154. package/src/tools/prompt-templates/delete.ts +16 -48
  155. package/src/tools/prompt-templates/get.ts +45 -42
  156. package/src/tools/prompt-templates/list.ts +29 -44
  157. package/src/tools/prompt-templates/preview.ts +14 -40
  158. package/src/tools/prompt-templates/set.ts +30 -52
  159. package/src/tools/read-messages.ts +45 -54
  160. package/src/tools/register-agentmail-inbox.ts +28 -84
  161. package/src/tools/register-kapso-number.ts +20 -50
  162. package/src/tools/register-service.ts +36 -44
  163. package/src/tools/repos/get-repos.ts +29 -21
  164. package/src/tools/repos/update-repo.ts +30 -27
  165. package/src/tools/request-human-input.ts +10 -33
  166. package/src/tools/resolve-user.ts +59 -16
  167. package/src/tools/schedules/create-schedule.ts +58 -173
  168. package/src/tools/schedules/delete-schedule.ts +16 -56
  169. package/src/tools/schedules/list-schedules.ts +41 -67
  170. package/src/tools/schedules/patch-schedule.ts +57 -153
  171. package/src/tools/schedules/run-schedule-now.ts +18 -59
  172. package/src/tools/schedules/update-schedule.ts +57 -153
  173. package/src/tools/script-apis.ts +46 -28
  174. package/src/tools/script-common.ts +129 -34
  175. package/src/tools/script-connections/tool.ts +59 -154
  176. package/src/tools/script-query-types.ts +43 -3
  177. package/src/tools/script-run.ts +41 -2
  178. package/src/tools/script-runs.ts +69 -1
  179. package/src/tools/script-search.ts +24 -1
  180. package/src/tools/script-upsert.ts +18 -3
  181. package/src/tools/send-task.ts +32 -81
  182. package/src/tools/skills/skill-create.ts +14 -39
  183. package/src/tools/skills/skill-delete.ts +14 -42
  184. package/src/tools/skills/skill-get-file.ts +10 -43
  185. package/src/tools/skills/skill-get.ts +9 -32
  186. package/src/tools/skills/skill-install-remote.ts +16 -52
  187. package/src/tools/skills/skill-install.ts +14 -52
  188. package/src/tools/skills/skill-list.ts +24 -26
  189. package/src/tools/skills/skill-publish.ts +18 -54
  190. package/src/tools/skills/skill-search.ts +21 -18
  191. package/src/tools/skills/skill-sync-remote.ts +12 -24
  192. package/src/tools/skills/skill-uninstall.ts +10 -26
  193. package/src/tools/skills/skill-update.ts +24 -81
  194. package/src/tools/slack-delete.ts +8 -32
  195. package/src/tools/slack-download-file.ts +17 -57
  196. package/src/tools/slack-list-channels.ts +16 -43
  197. package/src/tools/slack-post.ts +11 -40
  198. package/src/tools/slack-read.ts +33 -103
  199. package/src/tools/slack-reply.ts +16 -53
  200. package/src/tools/slack-start-thread.ts +14 -55
  201. package/src/tools/slack-update.ts +8 -35
  202. package/src/tools/slack-upload-file.ts +22 -122
  203. package/src/tools/steer-task.ts +22 -35
  204. package/src/tools/store-progress.ts +11 -26
  205. package/src/tools/swarm-config/delete-config.ts +19 -57
  206. package/src/tools/swarm-config/get-config.ts +36 -44
  207. package/src/tools/swarm-config/list-config.ts +36 -44
  208. package/src/tools/swarm-config/set-config.ts +35 -73
  209. package/src/tools/swarm-x.ts +20 -26
  210. package/src/tools/task-action.ts +26 -46
  211. package/src/tools/task-tool-ctx.ts +5 -13
  212. package/src/tools/tracker/tracker-link-task.ts +8 -22
  213. package/src/tools/tracker/tracker-map-agent.ts +8 -22
  214. package/src/tools/tracker/tracker-status.ts +27 -19
  215. package/src/tools/tracker/tracker-sync-status.ts +8 -18
  216. package/src/tools/tracker/tracker-unlink.ts +4 -16
  217. package/src/tools/unregister-service.ts +19 -59
  218. package/src/tools/update-profile.ts +60 -100
  219. package/src/tools/update-service-status.ts +42 -67
  220. package/src/tools/utils.ts +189 -8
  221. package/src/tools/whatsapp-message.ts +19 -32
  222. package/src/tools/workflows/cancel-workflow-run.ts +4 -16
  223. package/src/tools/workflows/create-workflow.ts +9 -38
  224. package/src/tools/workflows/delete-workflow.ts +5 -17
  225. package/src/tools/workflows/get-workflow-run.ts +41 -31
  226. package/src/tools/workflows/get-workflow.ts +10 -21
  227. package/src/tools/workflows/list-workflow-runs.ts +107 -34
  228. package/src/tools/workflows/list-workflows.ts +5 -17
  229. package/src/tools/workflows/patch-workflow-node.ts +11 -39
  230. package/src/tools/workflows/patch-workflow.ts +11 -36
  231. package/src/tools/workflows/retry-workflow-run.ts +4 -16
  232. package/src/tools/workflows/trigger-workflow.ts +22 -58
  233. package/src/tools/workflows/update-workflow.ts +10 -42
  234. package/templates/skills/swarm-scripts/SKILL.md +1 -1
@@ -0,0 +1,156 @@
1
+ import { loadModelsDevCache, type ModelsDevCache, type ModelsDevModel } from "./modelsdev-cache";
2
+
3
+ /**
4
+ * Live model catalog for the UI model picker.
5
+ *
6
+ * The pricing-refresh loop (`src/be/pricing-refresh.ts`) already fetches the
7
+ * full models.dev payload at boot and every 12h. This module keeps a slim
8
+ * in-memory projection of that payload — only the providers the four local
9
+ * harnesses' pickers can reach, and only the fields the picker reads — and
10
+ * serves it through `GET /api/models-catalog` (`src/http/models-catalog.ts`).
11
+ *
12
+ * Until the first successful fetch (or if models.dev is unreachable), the
13
+ * vendored `src/be/modelsdev-cache.json` snapshot is slimmed and served
14
+ * instead, so the endpoint is never empty. The UI additionally bundles the
15
+ * same snapshot as its own fallback while the request is in flight.
16
+ */
17
+
18
+ /** Mirrors `SNAPSHOT_ORDER` + `BEDROCK_SNAPSHOT_ID` in `apps/ui/src/lib/agent-runtime-models.ts`. */
19
+ export const CATALOG_PROVIDER_IDS = [
20
+ "openrouter",
21
+ "anthropic",
22
+ "openai",
23
+ "amazon-bedrock",
24
+ ] as const;
25
+
26
+ export type CatalogProviderId = (typeof CATALOG_PROVIDER_IDS)[number];
27
+
28
+ /**
29
+ * Limited-availability models that are intentionally vendored even when
30
+ * models.dev does not list them yet, as "provider/model-id". Shared with
31
+ * `scripts/refresh-modelsdev-pricing.ts` (which pins them into the snapshot)
32
+ * — the live catalog re-merges them from the snapshot so they never drop out
33
+ * of the picker between models.dev listing gaps.
34
+ */
35
+ export const PINNED_MODELSDEV_ENTRIES = [
36
+ "anthropic/claude-mythos-5",
37
+ "anthropic/claude-sonnet-5",
38
+ "amazon-bedrock/anthropic.claude-sonnet-5",
39
+ ] as const;
40
+
41
+ export interface CatalogReasoningOption {
42
+ type: string;
43
+ values?: string[];
44
+ }
45
+
46
+ /** Field names deliberately match raw models.dev (`reasoning_options`, not camelCase) so the UI can treat live and bundled-snapshot data identically. */
47
+ export interface CatalogModel {
48
+ id: string;
49
+ name?: string;
50
+ cost?: { input?: number; output?: number };
51
+ limit?: { context?: number };
52
+ reasoning?: boolean;
53
+ reasoning_options?: CatalogReasoningOption[];
54
+ }
55
+
56
+ export interface CatalogProvider {
57
+ id: string;
58
+ name?: string;
59
+ models: Record<string, CatalogModel>;
60
+ }
61
+
62
+ export type ModelsCatalog = Partial<Record<CatalogProviderId, CatalogProvider>>;
63
+
64
+ export interface ModelsCatalogResult {
65
+ source: "live" | "snapshot";
66
+ /** Epoch ms of the last successful models.dev fetch; null for snapshot data. */
67
+ updatedAt: number | null;
68
+ providers: ModelsCatalog;
69
+ }
70
+
71
+ function slimModel(modelKey: string, model: ModelsDevModel): CatalogModel {
72
+ const reasoningOptions = Array.isArray(model.reasoning_options)
73
+ ? model.reasoning_options
74
+ .filter((o) => typeof o.type === "string")
75
+ .map((o) => ({
76
+ type: o.type as string,
77
+ ...(Array.isArray(o.values) ? { values: o.values } : {}),
78
+ }))
79
+ : [];
80
+ return {
81
+ id: model.id ?? modelKey,
82
+ ...(model.name !== undefined ? { name: model.name } : {}),
83
+ ...(model.cost !== undefined
84
+ ? { cost: { input: model.cost.input, output: model.cost.output } }
85
+ : {}),
86
+ ...(model.limit?.context !== undefined ? { limit: { context: model.limit.context } } : {}),
87
+ ...(model.reasoning !== undefined ? { reasoning: model.reasoning } : {}),
88
+ ...(reasoningOptions.length > 0 ? { reasoning_options: reasoningOptions } : {}),
89
+ };
90
+ }
91
+
92
+ /** Slim a full models.dev payload down to the picker-reachable providers/fields. */
93
+ export function buildModelsCatalog(cache: ModelsDevCache): ModelsCatalog {
94
+ const catalog: ModelsCatalog = {};
95
+ for (const providerId of CATALOG_PROVIDER_IDS) {
96
+ const provider = cache[providerId];
97
+ if (!provider?.models) continue;
98
+ const models: Record<string, CatalogModel> = {};
99
+ for (const [modelKey, model] of Object.entries(provider.models)) {
100
+ models[modelKey] = slimModel(modelKey, model);
101
+ }
102
+ catalog[providerId] = {
103
+ id: provider.id ?? providerId,
104
+ ...(provider.name !== undefined ? { name: provider.name } : {}),
105
+ models,
106
+ };
107
+ }
108
+ return catalog;
109
+ }
110
+
111
+ let liveCatalog: ModelsCatalog | null = null;
112
+ let liveUpdatedAt: number | null = null;
113
+ /** Memoized slim of the vendored snapshot; `null` = load attempted and failed. */
114
+ let snapshotCatalog: ModelsCatalog | null | undefined;
115
+
116
+ function bundledSnapshotCatalog(): ModelsCatalog {
117
+ if (snapshotCatalog === undefined) {
118
+ const raw = loadModelsDevCache();
119
+ snapshotCatalog = raw ? buildModelsCatalog(raw) : null;
120
+ }
121
+ return snapshotCatalog ?? {};
122
+ }
123
+
124
+ function mergePinnedEntries(catalog: ModelsCatalog): void {
125
+ const snapshot = bundledSnapshotCatalog();
126
+ for (const entryPath of PINNED_MODELSDEV_ENTRIES) {
127
+ const slashIndex = entryPath.indexOf("/");
128
+ const providerId = entryPath.slice(0, slashIndex) as CatalogProviderId;
129
+ const modelId = entryPath.slice(slashIndex + 1);
130
+ if (catalog[providerId]?.models[modelId]) continue;
131
+ const pinned = snapshot[providerId]?.models[modelId];
132
+ if (!pinned) continue;
133
+ catalog[providerId] ??= { id: providerId, models: {} };
134
+ catalog[providerId].models[modelId] = pinned;
135
+ }
136
+ }
137
+
138
+ /** Called by the pricing-refresh loop after every successful full models.dev fetch. */
139
+ export function updateLiveModelsCatalog(cache: ModelsDevCache, now = Date.now()): void {
140
+ const catalog = buildModelsCatalog(cache);
141
+ mergePinnedEntries(catalog);
142
+ liveCatalog = catalog;
143
+ liveUpdatedAt = now;
144
+ }
145
+
146
+ export function getModelsCatalog(): ModelsCatalogResult {
147
+ if (liveCatalog) {
148
+ return { source: "live", updatedAt: liveUpdatedAt, providers: liveCatalog };
149
+ }
150
+ return { source: "snapshot", updatedAt: null, providers: bundledSnapshotCatalog() };
151
+ }
152
+
153
+ export function resetModelsCatalogForTests(): void {
154
+ liveCatalog = null;
155
+ liveUpdatedAt = null;
156
+ }
@@ -8,12 +8,23 @@ export interface ModelsDevCostBlock {
8
8
  cache_write?: number;
9
9
  }
10
10
 
11
+ export interface ModelsDevReasoningOption {
12
+ type?: string;
13
+ values?: string[];
14
+ }
15
+
11
16
  export interface ModelsDevModel {
12
17
  id?: string;
18
+ name?: string;
13
19
  cost?: ModelsDevCostBlock;
20
+ limit?: { context?: number };
21
+ reasoning?: boolean;
22
+ reasoning_options?: ModelsDevReasoningOption[];
14
23
  }
15
24
 
16
25
  export interface ModelsDevProvider {
26
+ id?: string;
27
+ name?: string;
17
28
  models?: Record<string, ModelsDevModel>;
18
29
  }
19
30
 
@@ -28,7 +39,9 @@ export const MODELSDEV_CACHE_PATH = path.join("src", "be", "modelsdev-cache.json
28
39
  * This file is now fallback-only for pricing freshness: boot seeding uses it
29
40
  * when the DB is empty or models.dev is unavailable, while
30
41
  * `src/be/pricing-refresh.ts` owns live price updates. The UI model picker
31
- * still imports the same snapshot for names, labels, and context windows.
42
+ * fetches the live catalog from `GET /api/models-catalog`
43
+ * (`src/be/models-catalog.ts`) and only falls back to its bundled copy of
44
+ * this snapshot when that request hasn't resolved.
32
45
  */
33
46
  export function loadModelsDevCache(): ModelsDevCache | null {
34
47
  const explicitPath = process.env.MODELSDEV_CACHE_PATH;
@@ -6,6 +6,7 @@ import {
6
6
  type InsertPricingRowInput,
7
7
  insertPricingRow,
8
8
  } from "./db";
9
+ import { updateLiveModelsCatalog } from "./models-catalog";
9
10
  import type { ModelsDevCache } from "./modelsdev-cache";
10
11
  import { buildModelsDevSeedRows, type PricingSeedRow } from "./seed-pricing";
11
12
 
@@ -148,6 +149,7 @@ export async function refreshPricingFromModelsDev(
148
149
 
149
150
  const cache = (await response.json()) as ModelsDevCache;
150
151
  const etag = response.headers.get("etag");
152
+ updateLiveModelsCatalog(cache, now);
151
153
  const rows = buildModelsDevSeedRows(cache);
152
154
  const { inserted, unchanged } = insertChangedPricingRows(rows, now);
153
155
  const pruned = prunePricingHistory(2);
@@ -106,7 +106,7 @@ export interface SwarmSdk {
106
106
  // --- workflows ---
107
107
  workflow_list(args?: { enabled?: boolean; includeFull?: boolean; consecutiveErrorsMin?: number; lastRunStatus?: "running" | "waiting" | "completed" | "failed" | "skipped" | "cancelled" }): Promise<unknown>;
108
108
  workflow_get(args: { id: string }): Promise<unknown>;
109
- workflow_listRuns(args: { workflowId: string; status?: "running" | "waiting" | "completed" | "failed" | "skipped" | "cancelled" }): Promise<unknown>;
109
+ workflow_listRuns(args: { workflowId: string; status?: "running" | "waiting" | "completed" | "failed" | "skipped" | "cancelled"; limit?: number; offset?: number }): Promise<unknown>;
110
110
  workflow_getRun(args: { id: string }): Promise<unknown>;
111
111
  // --- prompt templates ---
112
112
  prompt_list(args?: { eventType?: string; scope?: "global" | "agent" | "repo"; scopeId?: string; isDefault?: boolean }): Promise<unknown>;
@@ -295,6 +295,17 @@ export interface ScriptContext {
295
295
  logger: ScriptLogger;
296
296
  }
297
297
 
298
+ /**
299
+ * A swarm script's default export. \`args\` comes FIRST, \`ctx\` second — never swap them.
300
+ *
301
+ * @example
302
+ * import type { ScriptContext } from "swarm-sdk";
303
+ *
304
+ * export default async function (args: { name: string }, ctx: ScriptContext) {
305
+ * await ctx.logger.log(\`hello \${args.name}\`);
306
+ * return { ok: true };
307
+ * }
308
+ */
298
309
  // biome-ignore lint/suspicious/noExplicitAny: scripts may narrow their args type at the entrypoint.
299
310
  export type ScriptMain = (args: any, ctx: ScriptContext) => unknown | Promise<unknown>;
300
311
  `;
@@ -878,15 +889,17 @@ void _scriptMain;
878
889
 
879
890
  if (diagnostics.length === 0) return { ok: true };
880
891
 
881
- const formatted = ts.formatDiagnosticsWithColorAndContext(diagnostics, {
892
+ const formatHost: ts.FormatDiagnosticsHost = {
882
893
  getCanonicalFileName: (fileName) => fileName,
883
894
  getCurrentDirectory: () => "/virtual",
884
895
  getNewLine: () => "\n",
885
- });
896
+ };
886
897
 
887
898
  return {
888
899
  ok: false,
889
- diagnostics: formatted.split("\n\n").filter(Boolean),
900
+ diagnostics: diagnostics.map((diagnostic) =>
901
+ ts.formatDiagnosticsWithColorAndContext([diagnostic], formatHost).trimEnd(),
902
+ ),
890
903
  structured: diagnostics.map(toStructured),
891
904
  };
892
905
  }
@@ -81,7 +81,7 @@ export default async function taskContextGathering(args: any, ctx: any) {
81
81
  memories.sort((a: any, b: any) => b.compositeScore - a.compositeScore);
82
82
 
83
83
  return {
84
- task: slimTask(taskPayload?.task),
84
+ task: slimTask(taskPayload),
85
85
  requestedBy: taskPayload?.requestedBy,
86
86
  attachments: taskPayload?.attachments ?? [],
87
87
  queriesRun: queries.length,
@@ -16,6 +16,18 @@ export function generateEnv(state: OnboardState): string {
16
16
  lines.push(`MCP_BASE_URL=http://localhost:${port}`);
17
17
  lines.push("APP_URL=https://app.agent-swarm.dev");
18
18
 
19
+ // Install path — read by src/telemetry.ts at boot to attribute the
20
+ // "which entry point produced this install" cohort. Boolean/enum only,
21
+ // never an identifier. Installs that never ran the wizard (hand-written
22
+ // docker-compose, manual `bun run start:http`) simply won't have this
23
+ // var set, and telemetry falls back to "manual".
24
+ lines.push(
25
+ `INSTALL_METHOD=${state.nonInteractive ? "onboard_noninteractive" : "onboard_interactive"}`,
26
+ );
27
+ if (state.presetId) {
28
+ lines.push(`INSTALL_PRESET=${state.presetId}`);
29
+ }
30
+
19
31
  // ── Authentication ──
20
32
  lines.push("");
21
33
  lines.push("# === Authentication ===");
package/src/e2b/env.ts CHANGED
@@ -36,10 +36,6 @@ export const DEFAULT_E2B_FORWARD_KEYS = [
36
36
  "MANAGED_ENVIRONMENT_ID",
37
37
  "MANAGED_AGENT_MODEL",
38
38
  "MANAGED_GITHUB_TOKEN",
39
- "ARCHIL_MOUNT_TOKEN",
40
- "ARCHIL_REGION",
41
- "ARCHIL_SHARED_DISK_NAME",
42
- "ARCHIL_PERSONAL_DISK_NAME",
43
39
  "HARNESS_PROVIDER",
44
40
  "MODEL_OVERRIDE",
45
41
  "STARTUP_SCRIPT_STRICT",
package/src/hooks/hook.ts CHANGED
@@ -105,31 +105,6 @@ export function shouldShowRegistrationNudge(opts: {
105
105
  return true;
106
106
  }
107
107
 
108
- /**
109
- * Check if a path is under the agent's own subdirectory on the shared disk.
110
- * Shared disk categories: thoughts, memory, downloads, misc.
111
- * Each agent can only write to /workspace/shared/{category}/{agentId}/
112
- */
113
- function isOwnedSharedPath(path: string, agentId: string): boolean {
114
- const sharedCategories = ["thoughts", "memory", "downloads", "misc"];
115
- return sharedCategories.some((cat) => path.startsWith(`/workspace/shared/${cat}/${agentId}/`));
116
- }
117
-
118
- /**
119
- * Build the shared disk write warning message for a given agent ID.
120
- */
121
- function sharedDiskWriteWarning(agentId: string): string {
122
- return (
123
- `⚠️ This write will fail: You don't have write access to this directory.\n\n` +
124
- `On shared workspaces, each agent can only write to their own directories:\n` +
125
- `- /workspace/shared/thoughts/${agentId}/\n` +
126
- `- /workspace/shared/memory/${agentId}/\n` +
127
- `- /workspace/shared/downloads/${agentId}/\n` +
128
- `- /workspace/shared/misc/${agentId}/\n\n` +
129
- `You CAN read any file on the shared disk. For writes, use your own subdirectory.`
130
- );
131
- }
132
-
133
108
  /**
134
109
  * Hook response for blocking actions
135
110
  * See: https://code.claude.com/docs/en/hooks
@@ -1089,20 +1064,6 @@ export async function handleHook(): Promise<void> {
1089
1064
  }
1090
1065
  }
1091
1066
 
1092
- // Shared disk write prevention (Archil only — skip in local dev)
1093
- if (process.env.ARCHIL_MOUNT_TOKEN) {
1094
- const preAgentId = process.env.AGENT_ID;
1095
- if (preAgentId && (msg.tool_name === "Write" || msg.tool_name === "Edit")) {
1096
- const targetPath =
1097
- (msg.tool_input as { file_path?: string } | undefined)?.file_path ?? "";
1098
- if (
1099
- targetPath.startsWith("/workspace/shared/") &&
1100
- !isOwnedSharedPath(targetPath, preAgentId)
1101
- ) {
1102
- console.log(sharedDiskWriteWarning(preAgentId));
1103
- }
1104
- }
1105
- }
1106
1067
  break;
1107
1068
  }
1108
1069
 
@@ -1128,34 +1089,6 @@ export async function handleHook(): Promise<void> {
1128
1089
  });
1129
1090
  }
1130
1091
 
1131
- // Shared disk write failure detection (Archil only — safety net)
1132
- if (process.env.ARCHIL_MOUNT_TOKEN) {
1133
- const postAgentId = process.env.AGENT_ID;
1134
- if (
1135
- postAgentId &&
1136
- (msg.tool_name === "Write" || msg.tool_name === "Edit" || msg.tool_name === "Bash")
1137
- ) {
1138
- const toolResponse = msg.tool_response as
1139
- | { output?: string; stderr?: string }
1140
- | undefined;
1141
- const responseStr = JSON.stringify(toolResponse ?? "");
1142
- if (responseStr.includes("Read-only file system")) {
1143
- const postTargetPath =
1144
- (msg.tool_input as { file_path?: string; command?: string } | undefined)?.file_path ??
1145
- "";
1146
- if (
1147
- postTargetPath.startsWith("/workspace/shared/") &&
1148
- !isOwnedSharedPath(postTargetPath, postAgentId)
1149
- ) {
1150
- console.log(sharedDiskWriteWarning(postAgentId));
1151
- } else if (!postTargetPath) {
1152
- // Bash tool — no file_path, just warn generically
1153
- console.log(sharedDiskWriteWarning(postAgentId));
1154
- }
1155
- }
1156
- }
1157
- }
1158
-
1159
1092
  if (agentInfo) {
1160
1093
  // Sync workspace file edits back to DB
1161
1094
  const toolName = msg.tool_name;
@@ -29,6 +29,7 @@ import "./integrations";
29
29
  import "./kv";
30
30
  import "./memory";
31
31
  import "./metrics";
32
+ import "./models-catalog";
32
33
  import "./oauth-locks";
33
34
  import "./oauth-callback";
34
35
  import "./oauth-generic";
package/src/http/index.ts CHANGED
@@ -72,6 +72,7 @@ import { handleMcpServers } from "./mcp-servers";
72
72
  import { closeIdleMcpUserTransports, handleMcpUser } from "./mcp-user";
73
73
  import { handleMemory, startMemoryGc, stopMemoryGc } from "./memory";
74
74
  import { handleMetrics } from "./metrics";
75
+ import { handleModelsCatalog } from "./models-catalog";
75
76
  import { handleOAuthCallback, startOAuthPendingGc, stopOAuthPendingGc } from "./oauth-callback";
76
77
  import { handleGenericOAuth } from "./oauth-generic";
77
78
  import { handleOAuthLocks } from "./oauth-locks";
@@ -329,6 +330,7 @@ const httpServer = createHttpServer(async (req, res) => {
329
330
  () => handlePromptTemplates(req, res, pathSegments, queryParams),
330
331
  () => handleDbQuery(req, res, pathSegments, queryParams),
331
332
  () => handleMetrics(req, res, pathSegments, queryParams, myAgentId),
333
+ () => handleModelsCatalog(req, res, pathSegments, queryParams),
332
334
  () => handleRepos(req, res, pathSegments, queryParams),
333
335
  () => handleSkills(req, res, pathSegments, queryParams, myAgentId),
334
336
  () => handleScriptConnections(req, res, pathSegments, queryParams, myAgentId),
@@ -0,0 +1,65 @@
1
+ // Live model catalog for the UI model picker.
2
+ //
3
+ // Serves the slim in-memory projection of the models.dev payload maintained
4
+ // by the pricing-refresh loop (`src/be/models-catalog.ts`). Read-only: the
5
+ // catalog is refreshed server-side (boot + every 12h); the UI polls this
6
+ // endpoint instead of relying on the build-time snapshot bundled into the SPA.
7
+
8
+ import type { IncomingMessage, ServerResponse } from "node:http";
9
+ import { z } from "zod";
10
+ import { getModelsCatalog } from "../be/models-catalog";
11
+ import { route } from "./route-def";
12
+ import { json } from "./utils";
13
+
14
+ const CatalogModelSchema = z.object({
15
+ id: z.string(),
16
+ name: z.string().optional(),
17
+ cost: z.object({ input: z.number().optional(), output: z.number().optional() }).optional(),
18
+ limit: z.object({ context: z.number().optional() }).optional(),
19
+ reasoning: z.boolean().optional(),
20
+ reasoning_options: z
21
+ .array(z.object({ type: z.string(), values: z.array(z.string()).optional() }))
22
+ .optional(),
23
+ });
24
+
25
+ const CatalogProviderSchema = z.object({
26
+ id: z.string(),
27
+ name: z.string().optional(),
28
+ models: z.record(z.string(), CatalogModelSchema),
29
+ });
30
+
31
+ const getCatalog = route({
32
+ method: "get",
33
+ path: "/api/models-catalog",
34
+ pattern: ["api", "models-catalog"],
35
+ summary: "Get the live model catalog for the picker-reachable providers",
36
+ description:
37
+ "Slim projection of the models.dev payload (openrouter / anthropic / openai / amazon-bedrock only), refreshed server-side at boot and every 12h by the pricing-refresh loop. `source` is 'snapshot' with `updatedAt: null` until the first successful fetch (or when models.dev is unreachable), in which case the vendored snapshot is served instead.",
38
+ tags: ["Pricing"],
39
+ responses: {
40
+ 200: {
41
+ description: "Model catalog",
42
+ schema: z.object({
43
+ source: z.enum(["live", "snapshot"]),
44
+ updatedAt: z.number().nullable(),
45
+ providers: z.record(z.string(), CatalogProviderSchema),
46
+ }),
47
+ },
48
+ },
49
+ });
50
+
51
+ export async function handleModelsCatalog(
52
+ req: IncomingMessage,
53
+ res: ServerResponse,
54
+ pathSegments: string[],
55
+ queryParams: URLSearchParams,
56
+ ): Promise<boolean> {
57
+ if (getCatalog.match(req.method, pathSegments)) {
58
+ const parsed = await getCatalog.parse(req, res, pathSegments, queryParams);
59
+ if (!parsed) return true;
60
+ json(res, getModelsCatalog());
61
+ return true;
62
+ }
63
+
64
+ return false;
65
+ }
@@ -11,6 +11,7 @@ import {
11
11
  getWorkflowVersion,
12
12
  getWorkflowVersions,
13
13
  listWorkflowRuns,
14
+ listWorkflowRunsPage,
14
15
  listWorkflows,
15
16
  updateWorkflow,
16
17
  withFavoriteFlags,
@@ -209,6 +210,8 @@ const listWorkflowRunsRoute = route({
209
210
  params: z.object({ id: z.string() }),
210
211
  query: z.object({
211
212
  status: WorkflowRunStatusSchema.optional(),
213
+ limit: z.coerce.number().int().min(1).max(100).optional(),
214
+ offset: z.coerce.number().int().min(0).optional(),
212
215
  }),
213
216
  responses: {
214
217
  200: { description: "Workflow run list" },
@@ -754,11 +757,20 @@ export async function handleWorkflows(
754
757
  if (listWorkflowRunsRoute.match(req.method, pathSegments)) {
755
758
  const parsed = await listWorkflowRunsRoute.parse(req, res, pathSegments, queryParams);
756
759
  if (!parsed) return true;
757
- let runs = listWorkflowRuns(parsed.params.id);
758
- // Apply optional status filter
759
- if (parsed.query?.status) {
760
- runs = runs.filter((r) => r.status === parsed.query.status);
760
+ const paginationRequested =
761
+ parsed.query?.limit !== undefined || parsed.query?.offset !== undefined;
762
+ if (paginationRequested) {
763
+ const page = listWorkflowRunsPage(parsed.params.id, {
764
+ status: parsed.query?.status,
765
+ limit: parsed.query?.limit ?? 20,
766
+ offset: parsed.query?.offset ?? 0,
767
+ });
768
+ json(res, page);
769
+ return true;
761
770
  }
771
+ // Preserve the pre-pagination response for the UI when limit/offset are
772
+ // omitted: a bare array containing every matching run.
773
+ const runs = listWorkflowRuns(parsed.params.id, { status: parsed.query?.status });
762
774
  json(res, runs);
763
775
  return true;
764
776
  }
@@ -116,15 +116,16 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
116
116
  if (!hasMcp) {
117
117
  // If no MCP, role cannot be lead
118
118
  compositeEventType = "system.session.worker.remote";
119
+ } else if (args.provider === "pi") {
120
+ // Pi has no context-mode MCP wiring yet, so it uses composites that omit
121
+ // the context-mode tool list while still including the shared script
122
+ // rubric (and its authoring contract) plus seed-script guidance. Checked
123
+ // before the role branch so pi *leads* aren't told about `ctx_*` tools that
124
+ // don't exist for them either. All other local providers (claude, codex,
125
+ // opencode) keep the full context block via the standard composites.
126
+ compositeEventType = role === "lead" ? "system.session.lead.pi" : "system.session.worker.pi";
119
127
  } else if (role === "lead") {
120
128
  compositeEventType = "system.session.lead";
121
- } else if (args.provider === "pi") {
122
- // Pi has no context-mode MCP wiring yet, so it uses a worker composite that
123
- // omits the context-mode tool list while still including the shared script
124
- // rubric and seed-script guidance. All other local providers (claude,
125
- // codex, opencode) keep the full context block via the standard worker
126
- // composite.
127
- compositeEventType = "system.session.worker.pi";
128
129
  } else {
129
130
  compositeEventType = "system.session.worker";
130
131
  }