@desplega.ai/agent-swarm 1.113.0 → 1.115.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 (237) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
  3. package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
  4. package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
  5. package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
  6. package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
  7. package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
  8. package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
  9. package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
  10. package/dist/cli-0pygq9r1.js +429 -0
  11. package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
  12. package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
  13. package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
  14. package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
  15. package/dist/cli-52fg7s7x.js +269 -0
  16. package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
  17. package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
  18. package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
  19. package/dist/cli-86fdnerz.js +405 -0
  20. package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
  21. package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
  22. package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
  23. package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
  24. package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
  25. package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
  26. package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
  27. package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
  28. package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
  29. package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
  30. package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
  31. package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
  32. package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
  33. package/dist/cli-jkhwmmgg.js +136 -0
  34. package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
  35. package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
  36. package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
  37. package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
  38. package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
  39. package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
  40. package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
  41. package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
  42. package/dist/cli-t0vqevnm.js +393 -0
  43. package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
  44. package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
  45. package/dist/cli-wh91zby6.js +187222 -0
  46. package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
  47. package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
  48. package/dist/cli.js +32 -15
  49. package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
  50. package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
  51. package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
  52. package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
  53. package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
  54. package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
  55. package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
  56. package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
  57. package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
  58. package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
  59. package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
  60. package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
  61. package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
  62. package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
  63. package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
  64. package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
  65. package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
  66. package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
  67. package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
  68. package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
  69. package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
  70. package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
  71. package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
  72. package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
  73. package/dist/rbac-roles-kfesjy9y.js +28 -0
  74. package/dist/rbac-roles-mwcwhbxc.js +283 -0
  75. package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
  76. package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
  77. package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
  78. package/openapi.json +1327 -20
  79. package/package.json +3 -3
  80. package/src/agentmail/handlers.ts +16 -6
  81. package/src/agentmail/templates.ts +25 -5
  82. package/src/be/audit-user.ts +28 -4
  83. package/src/be/db-queries/oauth.ts +42 -0
  84. package/src/be/db.ts +314 -28
  85. package/src/be/identity.ts +79 -0
  86. package/src/be/mcp-proxy.ts +132 -0
  87. package/src/be/migrations/109_rbac_roles.sql +79 -0
  88. package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
  89. package/src/be/migrations/112_script_connections_graphql.sql +96 -0
  90. package/src/be/migrations/113_task_routing_affinity.sql +9 -0
  91. package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
  92. package/src/be/modelsdev-cache.json +135 -0
  93. package/src/be/oauth-credential-bindings.ts +35 -0
  94. package/src/be/oauth-refresh-sweep.ts +159 -0
  95. package/src/be/rbac-audit.ts +52 -13
  96. package/src/be/rbac-roles.ts +424 -0
  97. package/src/be/script-connections.ts +459 -40
  98. package/src/be/script-credential-broker.ts +29 -3
  99. package/src/be/scripts/typecheck.ts +51 -7
  100. package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
  101. package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
  102. package/src/be/users.ts +24 -0
  103. package/src/cli.tsx +17 -0
  104. package/src/github/handlers.ts +10 -9
  105. package/src/github/templates.ts +45 -9
  106. package/src/gitlab/handlers.ts +20 -8
  107. package/src/gitlab/templates.ts +18 -6
  108. package/src/heartbeat/heartbeat.ts +143 -21
  109. package/src/http/all-routes.ts +3 -0
  110. package/src/http/approval-requests.ts +12 -0
  111. package/src/http/config.ts +44 -10
  112. package/src/http/core.ts +30 -3
  113. package/src/http/favorites.ts +1 -5
  114. package/src/http/index.ts +25 -1
  115. package/src/http/mcp-oauth.ts +2 -0
  116. package/src/http/mcp-servers.ts +44 -3
  117. package/src/http/oauth-generic.ts +105 -0
  118. package/src/http/poll.ts +20 -3
  119. package/src/http/script-connection-proxy.ts +116 -0
  120. package/src/http/script-connections.ts +1657 -0
  121. package/src/http/scripts.ts +27 -7
  122. package/src/http/skills.ts +11 -0
  123. package/src/http/tasks.ts +27 -13
  124. package/src/http/x.ts +6 -2
  125. package/src/integrations/kapso/inbound.ts +23 -6
  126. package/src/jira/sync.ts +111 -17
  127. package/src/jira/templates.ts +10 -2
  128. package/src/mcp-client/http-client.ts +194 -0
  129. package/src/oauth/app-validation.ts +16 -0
  130. package/src/oauth/ensure-token.ts +69 -14
  131. package/src/oauth/mcp-wrapper.ts +15 -0
  132. package/src/oauth/wrapper.ts +48 -20
  133. package/src/prompts/session-templates.ts +3 -3
  134. package/src/providers/codex-models.ts +100 -30
  135. package/src/providers/modelsdev-reasoning.json +51 -0
  136. package/src/providers/pi-mono-mcp-client.ts +2 -133
  137. package/src/providers/reasoning-effort.ts +11 -5
  138. package/src/rbac/admission.ts +68 -0
  139. package/src/rbac/index.ts +2 -0
  140. package/src/rbac/legacy-policy.ts +12 -0
  141. package/src/rbac/permissions.ts +48 -0
  142. package/src/scheduler/scheduler.ts +9 -1
  143. package/src/scripts-runtime/api-client.ts +151 -19
  144. package/src/scripts-runtime/api-types.ts +38 -6
  145. package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
  146. package/src/scripts-runtime/credential-broker/types.ts +8 -0
  147. package/src/scripts-runtime/ctx.ts +11 -1
  148. package/src/scripts-runtime/eval-harness.ts +5 -1
  149. package/src/scripts-runtime/executors/types.ts +2 -1
  150. package/src/scripts-runtime/loader.ts +3 -1
  151. package/src/scripts-runtime/mcp-client.ts +87 -0
  152. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  153. package/src/scripts-runtime/types/stdlib.d.ts +6 -0
  154. package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
  155. package/src/server-user.ts +114 -59
  156. package/src/server.ts +12 -0
  157. package/src/slack/assistant.ts +14 -6
  158. package/src/slack/enrich.ts +17 -0
  159. package/src/slack/handlers.ts +8 -22
  160. package/src/slack/responses.ts +91 -1
  161. package/src/slack/thread-buffer.ts +6 -3
  162. package/src/slack/watcher.ts +9 -6
  163. package/src/tasks/worker-follow-up.ts +100 -3
  164. package/src/tests/agentmail-handlers.test.ts +32 -3
  165. package/src/tests/approval-requests.test.ts +61 -0
  166. package/src/tests/audit-user.test.ts +47 -1
  167. package/src/tests/codex-adapter.test.ts +46 -23
  168. package/src/tests/credential-broker.test.ts +5 -2
  169. package/src/tests/delete-page-tool.test.ts +197 -0
  170. package/src/tests/github-handlers.test.ts +28 -2
  171. package/src/tests/gitlab-handlers.test.ts +125 -0
  172. package/src/tests/heartbeat.test.ts +48 -3
  173. package/src/tests/identity.test.ts +117 -0
  174. package/src/tests/jira-sync.test.ts +99 -1
  175. package/src/tests/kapso-inbound.test.ts +7 -0
  176. package/src/tests/mcp-tools-user.test.ts +60 -6
  177. package/src/tests/model-control.test.ts +8 -2
  178. package/src/tests/oauth-credential-bindings.test.ts +490 -0
  179. package/src/tests/oauth-refresh-sweep.test.ts +160 -0
  180. package/src/tests/pool-affinity.test.ts +401 -0
  181. package/src/tests/prompt-template-remaining.test.ts +2 -2
  182. package/src/tests/rbac-admission-e2e.test.ts +386 -0
  183. package/src/tests/rbac-admission.test.ts +492 -0
  184. package/src/tests/rbac-e2e-helpers.ts +3 -0
  185. package/src/tests/rbac-engine.test.ts +50 -0
  186. package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
  187. package/src/tests/rbac-roles.test.ts +328 -0
  188. package/src/tests/reasoning-effort.test.ts +15 -2
  189. package/src/tests/schedule-target-type.test.ts +50 -0
  190. package/src/tests/scheduled-tasks.test.ts +40 -0
  191. package/src/tests/script-apis-mcp.test.ts +37 -13
  192. package/src/tests/script-connections-http.test.ts +1158 -0
  193. package/src/tests/script-connections-mcp.test.ts +894 -0
  194. package/src/tests/script-connections.test.ts +754 -6
  195. package/src/tests/scripts-external-api.test.ts +49 -1
  196. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
  197. package/src/tests/scripts-runtime.test.ts +291 -0
  198. package/src/tests/seed-scripts.test.ts +36 -0
  199. package/src/tests/slack-identity-resolution.test.ts +37 -3
  200. package/src/tests/slack-inline-output.test.ts +65 -0
  201. package/src/tests/slack-thread-buffer.test.ts +28 -0
  202. package/src/tests/tool-annotations.test.ts +1 -0
  203. package/src/tests/user-token-rest-auth.test.ts +140 -1
  204. package/src/tests/workflow-swarm-script.test.ts +44 -0
  205. package/src/tests/workflow-triggers-v2.test.ts +300 -1
  206. package/src/tools/credential-bindings/tool.ts +277 -18
  207. package/src/tools/delete-page.ts +145 -0
  208. package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
  209. package/src/tools/request-human-input.ts +2 -0
  210. package/src/tools/resolve-user.ts +77 -27
  211. package/src/tools/script-apis.ts +62 -3
  212. package/src/tools/script-connections/tool.ts +275 -31
  213. package/src/tools/send-task.ts +15 -1
  214. package/src/tools/task-action.ts +34 -2
  215. package/src/tools/templates.ts +53 -1
  216. package/src/tools/tool-config.ts +2 -1
  217. package/src/tools/utils.ts +2 -0
  218. package/src/tools/workflows/create-workflow.ts +8 -1
  219. package/src/tools/workflows/update-workflow.ts +11 -2
  220. package/src/types.ts +90 -11
  221. package/src/utils/scoped-resource.ts +25 -0
  222. package/src/workflows/executors/human-in-the-loop.ts +1 -0
  223. package/src/workflows/executors/swarm-script.ts +7 -1
  224. package/src/workflows/triggers.ts +146 -18
  225. package/templates/skills/swarm-scripts/content.md +33 -8
  226. package/dist/cli-bgef578c.js +0 -771
  227. package/dist/cli-c9mtm09b.js +0 -43
  228. package/dist/cli-ceewqajw.js +0 -1698
  229. package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
  230. package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
  231. package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
  232. package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
  233. package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
  234. package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
  235. package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
  236. package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
  237. package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
@@ -4,8 +4,9 @@ import {
4
4
  SwarmConfigCredentialBindingStore,
5
5
  } from "@/scripts-runtime/credential-broker";
6
6
  import type { EgressSecretEntry } from "@/scripts-runtime/executors/types";
7
- import { registerVolatileSecret } from "@/utils/secret-scrubber";
7
+ import { registerVolatileSecret, scrubSecrets } from "@/utils/secret-scrubber";
8
8
  import { getResolvedConfig, getSwarmConfigs } from "./db";
9
+ import { resolveOAuthBindingToken } from "./oauth-credential-bindings";
9
10
  import { listRelationalCredentialBindings } from "./script-connections";
10
11
 
11
12
  class RelationalCredentialBindingStore extends SwarmConfigCredentialBindingStore {
@@ -18,12 +19,37 @@ class RelationalCredentialBindingStore extends SwarmConfigCredentialBindingStore
18
19
  }
19
20
  }
20
21
 
21
- export function buildScriptCredentialBindings(input: {
22
+ export async function buildScriptCredentialBindings(input: {
22
23
  agentId?: string;
23
24
  repoId?: string;
24
- }): EgressSecretEntry[] {
25
+ }): Promise<EgressSecretEntry[]> {
25
26
  const resolvedConfigs = getResolvedConfig(input.agentId, input.repoId);
26
27
  const configMap = new Map(resolvedConfigs.map((config) => [config.key, config.value]));
28
+ const relationalBindings = listRelationalCredentialBindings(input);
29
+ for (const binding of relationalBindings) {
30
+ if (binding.authKind !== "oauth") continue;
31
+ configMap.set(binding.configKey, "");
32
+ if (!binding.oauthProvider) continue;
33
+
34
+ let accessToken: string | undefined;
35
+ try {
36
+ accessToken = await resolveOAuthBindingToken(binding.oauthProvider);
37
+ } catch (err) {
38
+ // A stale/broken provider must not take down unrelated script runs —
39
+ // skip just this binding (the "" shadow above keeps the placeholder
40
+ // unresolved instead of falling through to a same-named config value).
41
+ const message = err instanceof Error ? err.message : String(err);
42
+ console.warn(
43
+ `[script-credential-broker] skipping OAuth binding ${binding.configKey}: token refresh for provider ${binding.oauthProvider} failed: ${scrubSecrets(message)}`,
44
+ );
45
+ continue;
46
+ }
47
+ if (!accessToken) continue;
48
+
49
+ configMap.set(binding.configKey, accessToken);
50
+ registerVolatileSecret(accessToken, binding.configKey);
51
+ }
52
+
27
53
  const broker = new CredentialBroker(
28
54
  new RelationalCredentialBindingStore((filters) => getSwarmConfigs(filters)),
29
55
  (configKey) => configMap.get(configKey) ?? process.env[configKey],
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- import { getScriptApiTypes } from "@/be/script-connections";
2
+ import { getScriptApiTypes, getScriptMcpTypes } from "@/be/script-connections";
3
3
 
4
4
  /**
5
5
  * Structured diagnostic record returned to API callers when typecheck fails.
@@ -30,6 +30,17 @@ export const SCRIPT_SDK_TYPES = `
30
30
  export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
31
31
  export type ScriptScope = "agent" | "global";
32
32
  export type ScriptFsMode = "none" | "workspace-rw";
33
+ export type ScriptApiRawOptions = { raw: true };
34
+ export type ScriptApiDefaultOptions = { raw?: false };
35
+
36
+ export interface ScriptApiRawResult {
37
+ ok: boolean;
38
+ status: number;
39
+ statusText: string;
40
+ headers: Record<string, string>;
41
+ url: string;
42
+ response: Response;
43
+ }
33
44
 
34
45
  export interface Redacted<T> {
35
46
  readonly __redactedBrand?: T;
@@ -77,7 +88,7 @@ export interface SwarmSdk {
77
88
  swarm_get(args?: { includeFull?: boolean }): Promise<unknown>;
78
89
  agent_info(args?: Record<string, unknown>): Promise<unknown>;
79
90
  metrics_get(args?: Record<string, unknown>): Promise<unknown>;
80
- user_resolve(args?: { kind?: string; externalId?: string; email?: string; userId?: string }): Promise<unknown>;
91
+ user_resolve(args?: { kind?: string; externalId?: string; email?: string; userId?: string; name?: string }): Promise<unknown>;
81
92
  db_query(args: { sql: string; params?: unknown[] }): Promise<unknown>;
82
93
  // --- config ---
83
94
  config_get(args?: { agentId?: string; repoId?: string; key?: string; includeSecrets?: boolean }): Promise<unknown>;
@@ -210,6 +221,7 @@ export interface SwarmSdk {
210
221
 
211
222
  // --- pages & metrics ---
212
223
  page_create(args: Record<string, unknown>): Promise<unknown>;
224
+ page_delete(args: { pageId?: string; slug?: string }): Promise<unknown>;
213
225
  metric_create(args: Record<string, unknown>): Promise<unknown>;
214
226
 
215
227
  // --- human input ---
@@ -276,6 +288,7 @@ export interface ScriptContext {
276
288
  step?: ScriptWorkflowSteps;
277
289
  swarm: SwarmSdk & { config: SwarmConfig };
278
290
  api: ScriptApiRegistry;
291
+ mcp: ScriptMcpRegistry;
279
292
  stdlib: ScriptStdlib;
280
293
  logger: ScriptLogger;
281
294
  }
@@ -284,11 +297,14 @@ export interface ScriptContext {
284
297
  export type ScriptMain = (args: any, ctx: ScriptContext) => unknown | Promise<unknown>;
285
298
  `;
286
299
 
287
- export function scriptSdkTypesWithGeneratedApis(apiTypes = getScriptApiTypes()): string {
288
- return `${SCRIPT_SDK_TYPES}\n${apiTypes}\n`;
300
+ export function scriptSdkTypesWithGeneratedApis(
301
+ apiTypes = getScriptApiTypes(),
302
+ mcpTypes = getScriptMcpTypes(),
303
+ ): string {
304
+ return `${SCRIPT_SDK_TYPES}\n${apiTypes}\n${mcpTypes}\n`;
289
305
  }
290
306
 
291
- export const SCRIPT_STDLIB_TYPES = `
307
+ const STDLIB_MODULE_TYPES = `
292
308
  declare module "stdlib" {
293
309
  export interface Redacted<T> {
294
310
  readonly __redactedBrand?: T;
@@ -306,11 +322,36 @@ declare module "stdlib" {
306
322
  export function glob(pattern: string): Promise<string[]>;
307
323
  export function table(rows: Array<Record<string, unknown>>): string;
308
324
  }
325
+ `;
309
326
 
327
+ function stdlibTypesFor(sdkModuleBody: string): string {
328
+ return `${STDLIB_MODULE_TYPES}
310
329
  declare module "swarm-sdk" {
311
- ${SCRIPT_SDK_TYPES.replace(/^/gm, " ")}
330
+ ${sdkModuleBody.replace(/^/gm, " ")}
312
331
  }
313
332
  `;
333
+ }
334
+
335
+ export const SCRIPT_STDLIB_TYPES = stdlibTypesFor(SCRIPT_SDK_TYPES);
336
+
337
+ /**
338
+ * Stdlib blob whose ambient `declare module "swarm-sdk"` also carries the
339
+ * generated per-connection registries (`ScriptApiRegistry` / `ScriptMcpRegistry`
340
+ * and their `<Slug>Api` / `<Slug>Mcp` interfaces).
341
+ *
342
+ * Monaco resolves the bare `import ... from "swarm-sdk"` through this ambient
343
+ * module — unlike the server typechecker, whose custom resolver maps the
344
+ * specifier onto the flat SDK virtual file. Without the generated registries
345
+ * inlined here, the ambient copy of `ScriptContext` references names that do
346
+ * not exist in that module scope ("Cannot find name 'ScriptApiRegistry'") and
347
+ * `ctx.api.<slug>` completions break in the editor.
348
+ */
349
+ export function scriptStdlibTypesWithGeneratedApis(
350
+ apiTypes = getScriptApiTypes(),
351
+ mcpTypes = getScriptMcpTypes(),
352
+ ): string {
353
+ return stdlibTypesFor(`${SCRIPT_SDK_TYPES}\n${apiTypes}\n${mcpTypes}`);
354
+ }
314
355
 
315
356
  /**
316
357
  * Minimal ambient declarations for runtime globals the executor (Bun) actually
@@ -799,7 +840,10 @@ export function typecheckScript(
799
840
  types: [],
800
841
  };
801
842
 
802
- const sdkTypes = scriptSdkTypesWithGeneratedApis(getScriptApiTypes(context));
843
+ const sdkTypes = scriptSdkTypesWithGeneratedApis(
844
+ getScriptApiTypes(context),
845
+ getScriptMcpTypes(context),
846
+ );
803
847
  const files = new Map<string, string>([
804
848
  [USER_FILE, source],
805
849
  [SDK_FILE, sdkTypes],
@@ -47,6 +47,7 @@ function catalogReportHumanLabel(value: string): string {
47
47
 
48
48
  function catalogReportFormatMetric(value: unknown): string {
49
49
  if (typeof value === "number") return new Intl.NumberFormat("en-US").format(value);
50
+ if (typeof value === "boolean") return value ? "Yes" : "No";
50
51
  return catalogReportAsText(value);
51
52
  }
52
53
 
@@ -57,6 +58,25 @@ function catalogReportSeverityTone(value?: string): string {
57
58
  return "low";
58
59
  }
59
60
 
61
+ function catalogReportIsScalar(value: unknown): boolean {
62
+ return value == null || ["string", "number", "boolean"].includes(typeof value);
63
+ }
64
+
65
+ function catalogReportIsRecord(value: unknown): value is Record<string, unknown> {
66
+ return value !== null && typeof value === "object" && !Array.isArray(value);
67
+ }
68
+
69
+ function catalogReportValueClass(value: unknown): string {
70
+ if (typeof value === "number") return "numeric";
71
+ if (typeof value === "boolean") return value ? "positive" : "muted-value";
72
+ return "";
73
+ }
74
+
75
+ function catalogReportClassAttr(value: unknown): string {
76
+ const className = catalogReportValueClass(value);
77
+ return className ? ` class="${catalogReportHtmlEscape(className)}"` : "";
78
+ }
79
+
60
80
  function catalogReportRenderSampleValue(value: unknown): string {
61
81
  if (Array.isArray(value)) {
62
82
  return value
@@ -94,9 +114,116 @@ function catalogReportRenderSamples(samples?: unknown[]): string {
94
114
  </div>`;
95
115
  }
96
116
 
117
+ function catalogReportTableRows(rows: Record<string, unknown>[], limit = 12): string {
118
+ if (rows.length === 0) return '<p class="empty">No rows.</p>';
119
+ const columns = Array.from(new Set(rows.flatMap((row) => Object.keys(row).slice(0, 8)))).slice(0, 8);
120
+ const visible = rows.slice(0, limit);
121
+ const hiddenCount = Math.max(0, rows.length - visible.length);
122
+ return `<div class="data-table" role="region" aria-label="Data table">
123
+ <table>
124
+ <thead><tr>${columns.map((column) => `<th>${catalogReportHtmlEscape(catalogReportHumanLabel(column))}</th>`).join("")}</tr></thead>
125
+ <tbody>
126
+ ${visible
127
+ .map(
128
+ (row) =>
129
+ `<tr>${columns
130
+ .map((column) => {
131
+ const value = row[column];
132
+ return `<td${catalogReportClassAttr(value)}>${catalogReportHtmlEscape(
133
+ catalogReportRenderSampleValue(value),
134
+ )}</td>`;
135
+ })
136
+ .join("")}</tr>`,
137
+ )
138
+ .join("")}
139
+ </tbody>
140
+ </table>
141
+ ${hiddenCount ? `<p class="table-note">Showing ${visible.length} of ${rows.length} rows. Full payload remains in the appendix.</p>` : ""}
142
+ </div>`;
143
+ }
144
+
145
+ function catalogReportRenderDataValue(value: unknown): string {
146
+ if (catalogReportIsScalar(value)) {
147
+ return `<strong${catalogReportClassAttr(value)}>${catalogReportHtmlEscape(
148
+ catalogReportFormatMetric(value),
149
+ )}</strong>`;
150
+ }
151
+ if (Array.isArray(value)) {
152
+ if (value.every((item) => catalogReportIsRecord(item))) {
153
+ return catalogReportTableRows(value as Record<string, unknown>[]);
154
+ }
155
+ return `<div class="value-list">${value
156
+ .slice(0, 12)
157
+ .map((item) => `<span>${catalogReportHtmlEscape(catalogReportRenderSampleValue(item))}</span>`)
158
+ .join("")}</div>`;
159
+ }
160
+ if (catalogReportIsRecord(value)) {
161
+ const entries = Object.entries(value);
162
+ const scalarEntries = entries.filter(([, entryValue]) => catalogReportIsScalar(entryValue));
163
+ const complexEntries = entries.filter(([, entryValue]) => !catalogReportIsScalar(entryValue));
164
+ return `<div class="data-object">
165
+ ${
166
+ scalarEntries.length
167
+ ? `<div class="object-stats">${scalarEntries
168
+ .map(
169
+ ([label, entryValue]) =>
170
+ `<div><span>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</span><strong${catalogReportClassAttr(
171
+ entryValue,
172
+ )}>${catalogReportHtmlEscape(catalogReportFormatMetric(entryValue))}</strong></div>`,
173
+ )
174
+ .join("")}</div>`
175
+ : ""
176
+ }
177
+ ${complexEntries
178
+ .map(
179
+ ([label, entryValue]) => `<section class="data-nested">
180
+ <h4>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</h4>
181
+ ${catalogReportRenderDataValue(entryValue)}
182
+ </section>`,
183
+ )
184
+ .join("")}
185
+ </div>`;
186
+ }
187
+ return `<pre>${catalogReportHtmlEscape(JSON.stringify(value, null, 2))}</pre>`;
188
+ }
189
+
190
+ function catalogReportRenderDataPanels(entries: Array<[string, unknown]>): string {
191
+ if (entries.length === 0) return "";
192
+ return `<div class="data-panels">
193
+ ${entries
194
+ .map(
195
+ ([label, value]) => `<section class="data-panel">
196
+ <div class="data-panel-head">
197
+ <h3>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</h3>
198
+ <span>${catalogReportHtmlEscape(
199
+ Array.isArray(value)
200
+ ? `${value.length} rows`
201
+ : catalogReportIsRecord(value)
202
+ ? `${Object.keys(value).length} fields`
203
+ : "value",
204
+ )}</span>
205
+ </div>
206
+ ${catalogReportRenderDataValue(value)}
207
+ </section>`,
208
+ )
209
+ .join("")}
210
+ </div>`;
211
+ }
212
+
97
213
  export function renderCatalogReportPage(report: CatalogReport): string {
214
+ const nav = report.sections
215
+ .map(
216
+ (section) =>
217
+ `<a href="#${catalogReportHtmlEscape(section.key)}"><span>${catalogReportHtmlEscape(
218
+ section.label || catalogReportHumanLabel(section.key),
219
+ )}</span><strong>${catalogReportHtmlEscape(catalogReportFormatMetric(section.findingCount ?? section.findings?.length ?? 0))}</strong></a>`,
220
+ )
221
+ .join("");
98
222
  const sections = report.sections
99
223
  .map((section) => {
224
+ const checkEntries = Object.entries(section.checks || {});
225
+ const scalarChecks = checkEntries.filter(([, value]) => catalogReportIsScalar(value));
226
+ const dataChecks = checkEntries.filter(([, value]) => !catalogReportIsScalar(value));
100
227
  const findings = (section.findings || [])
101
228
  .map(
102
229
  (finding) => `<article class="finding ${catalogReportHtmlEscape(catalogReportSeverityTone(finding.severity))}">
@@ -114,25 +241,26 @@ export function renderCatalogReportPage(report: CatalogReport): string {
114
241
  </article>`,
115
242
  )
116
243
  .join("");
117
- const checks = Object.entries(section.checks || {})
244
+ const checks = scalarChecks
118
245
  .map(
119
246
  ([label, value]) =>
120
- `<div class="check"><span>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</span><strong>${catalogReportHtmlEscape(
121
- catalogReportFormatMetric(value),
122
- )}</strong></div>`,
247
+ `<div class="check"><span>${catalogReportHtmlEscape(catalogReportHumanLabel(label))}</span><strong${catalogReportClassAttr(
248
+ value,
249
+ )}>${catalogReportHtmlEscape(catalogReportFormatMetric(value))}</strong></div>`,
123
250
  )
124
251
  .join("");
125
- return `<section class="section">
252
+ return `<section class="section" id="${catalogReportHtmlEscape(section.key)}">
126
253
  <div class="section-grid">
127
254
  <aside class="checks">
128
255
  <p class="section-kicker">${catalogReportHtmlEscape(section.label || catalogReportHumanLabel(section.key))}</p>
129
- <div class="check-list">${checks}</div>
256
+ <div class="check-list">${checks || '<p class="empty">No scalar checks.</p>'}</div>
130
257
  </aside>
131
258
  <div>
132
259
  <div class="section-head">
133
260
  <h2>${catalogReportHtmlEscape(section.goal)}</h2>
134
261
  <span>${catalogReportHtmlEscape(catalogReportFormatMetric(section.findingCount ?? section.findings?.length ?? 0))} finding(s)</span>
135
262
  </div>
263
+ ${catalogReportRenderDataPanels(dataChecks)}
136
264
  <div class="findings">
137
265
  ${findings || '<p class="empty">No actionable findings in this cluster.</p>'}
138
266
  </div>
@@ -153,10 +281,14 @@ export function renderCatalogReportPage(report: CatalogReport): string {
153
281
  color-scheme: light;
154
282
  --bg: #f5f2ea;
155
283
  --panel: #ffffff;
284
+ --panel-soft: #fbfaf6;
156
285
  --ink: #18181b;
157
286
  --muted: #5f6368;
287
+ --muted-strong: #3f3f46;
158
288
  --line: #ded8cb;
289
+ --line-soft: #ebe5d7;
159
290
  --accent: #255c99;
291
+ --accent-soft: #edf5ff;
160
292
  --danger: #b42318;
161
293
  --danger-bg: #fff1f0;
162
294
  --warn: #b54708;
@@ -178,11 +310,19 @@ export function renderCatalogReportPage(report: CatalogReport): string {
178
310
  line-height: 1.55;
179
311
  }
180
312
  main {
181
- width: min(1120px, calc(100% - 32px));
313
+ width: min(1240px, calc(100% - 32px));
182
314
  margin: 0 auto;
183
315
  padding: 48px 0 72px;
184
316
  }
185
- header { margin-bottom: 28px; }
317
+ header {
318
+ display: grid;
319
+ grid-template-columns: minmax(0, 1fr) auto;
320
+ gap: 24px;
321
+ align-items: end;
322
+ margin-bottom: 24px;
323
+ padding-bottom: 22px;
324
+ border-bottom: 1px solid var(--line);
325
+ }
186
326
  .eyebrow {
187
327
  margin: 0 0 8px;
188
328
  color: var(--muted);
@@ -194,34 +334,48 @@ export function renderCatalogReportPage(report: CatalogReport): string {
194
334
  h1 {
195
335
  margin: 0;
196
336
  max-width: 860px;
197
- font-size: clamp(2rem, 4vw, 3rem);
337
+ font-size: clamp(2rem, 3.8vw, 3.25rem);
198
338
  line-height: 1.05;
199
339
  letter-spacing: 0;
200
340
  }
201
341
  .lede {
202
- max-width: 780px;
342
+ max-width: 840px;
203
343
  margin: 16px 0 0;
204
- color: var(--muted);
344
+ color: var(--muted-strong);
205
345
  font-size: 18px;
206
346
  }
347
+ .description {
348
+ max-width: 340px;
349
+ margin: 0;
350
+ color: var(--muted);
351
+ font-size: 14px;
352
+ text-align: right;
353
+ }
207
354
  .metrics {
208
355
  display: grid;
209
- grid-template-columns: repeat(4, minmax(0, 1fr));
356
+ grid-template-columns: repeat(6, minmax(0, 1fr));
210
357
  gap: 12px;
211
- margin: 32px 0;
358
+ margin: 24px 0 16px;
212
359
  }
213
- .metric, .section, details {
360
+ .metric, .section, details, .report-nav {
214
361
  background: var(--panel);
215
362
  border: 1px solid var(--line);
216
363
  border-radius: var(--radius);
217
364
  box-shadow: var(--shadow);
218
365
  }
219
- .metric { padding: 18px; }
366
+ .metric {
367
+ min-height: 112px;
368
+ padding: 16px;
369
+ display: flex;
370
+ flex-direction: column;
371
+ justify-content: space-between;
372
+ }
220
373
  .metric strong {
221
374
  display: block;
222
- font-size: 32px;
375
+ font-size: 30px;
223
376
  line-height: 1;
224
377
  font-variant-numeric: tabular-nums;
378
+ overflow-wrap: anywhere;
225
379
  }
226
380
  .metric span {
227
381
  display: block;
@@ -230,14 +384,45 @@ export function renderCatalogReportPage(report: CatalogReport): string {
230
384
  font-size: 13px;
231
385
  font-weight: 650;
232
386
  }
387
+ .report-nav {
388
+ display: grid;
389
+ grid-template-columns: repeat(6, minmax(0, 1fr));
390
+ gap: 1px;
391
+ overflow: hidden;
392
+ margin: 0 0 20px;
393
+ background: var(--line);
394
+ }
395
+ .report-nav a {
396
+ display: flex;
397
+ align-items: center;
398
+ justify-content: space-between;
399
+ gap: 10px;
400
+ min-height: 48px;
401
+ padding: 11px 13px;
402
+ background: var(--panel);
403
+ color: var(--muted-strong);
404
+ text-decoration: none;
405
+ font-size: 13px;
406
+ font-weight: 700;
407
+ }
408
+ .report-nav a:hover { background: var(--accent-soft); }
409
+ .report-nav strong {
410
+ color: var(--accent);
411
+ font-variant-numeric: tabular-nums;
412
+ }
233
413
  .section {
234
- margin-top: 18px;
235
- padding: 24px;
414
+ margin-top: 16px;
415
+ padding: 22px;
236
416
  }
237
417
  .section-grid {
238
418
  display: grid;
239
- grid-template-columns: 260px minmax(0, 1fr);
240
- gap: 28px;
419
+ grid-template-columns: 240px minmax(0, 1fr);
420
+ gap: 24px;
421
+ }
422
+ .checks {
423
+ position: sticky;
424
+ top: 18px;
425
+ align-self: start;
241
426
  }
242
427
  .section-kicker {
243
428
  margin: 0 0 12px;
@@ -259,6 +444,7 @@ export function renderCatalogReportPage(report: CatalogReport): string {
259
444
  padding: 10px 0;
260
445
  border-bottom: 1px solid var(--line);
261
446
  }
447
+ .check:last-child { border-bottom: 0; }
262
448
  .check span {
263
449
  color: var(--muted);
264
450
  font-size: 13px;
@@ -266,6 +452,8 @@ export function renderCatalogReportPage(report: CatalogReport): string {
266
452
  .check strong {
267
453
  font-size: 18px;
268
454
  font-variant-numeric: tabular-nums;
455
+ text-align: right;
456
+ overflow-wrap: anywhere;
269
457
  }
270
458
  .section-head {
271
459
  display: flex;
@@ -283,11 +471,113 @@ export function renderCatalogReportPage(report: CatalogReport): string {
283
471
  }
284
472
  .section-head > span {
285
473
  flex: 0 0 auto;
286
- color: var(--muted);
474
+ color: var(--accent);
475
+ background: var(--accent-soft);
476
+ border: 1px solid #cfe5ff;
477
+ border-radius: 999px;
478
+ padding: 5px 10px;
287
479
  font-size: 13px;
288
480
  font-weight: 700;
289
481
  white-space: nowrap;
290
482
  }
483
+ .data-panels {
484
+ display: grid;
485
+ gap: 12px;
486
+ margin: 0 0 16px;
487
+ }
488
+ .data-panel {
489
+ border: 1px solid var(--line-soft);
490
+ border-radius: var(--radius);
491
+ background: var(--panel-soft);
492
+ overflow: hidden;
493
+ }
494
+ .data-panel-head {
495
+ display: flex;
496
+ align-items: baseline;
497
+ justify-content: space-between;
498
+ gap: 16px;
499
+ padding: 12px 14px;
500
+ border-bottom: 1px solid var(--line-soft);
501
+ background: #fffdf8;
502
+ }
503
+ .data-panel-head h3,
504
+ .data-nested h4 {
505
+ margin: 0;
506
+ color: var(--muted-strong);
507
+ font-size: 14px;
508
+ line-height: 1.25;
509
+ letter-spacing: 0;
510
+ }
511
+ .data-panel-head span,
512
+ .table-note {
513
+ color: var(--muted);
514
+ font-size: 12px;
515
+ font-weight: 700;
516
+ }
517
+ .data-object {
518
+ display: grid;
519
+ gap: 12px;
520
+ padding: 14px;
521
+ }
522
+ .object-stats {
523
+ display: grid;
524
+ grid-template-columns: repeat(3, minmax(0, 1fr));
525
+ gap: 10px;
526
+ }
527
+ .object-stats > div {
528
+ min-height: 76px;
529
+ padding: 12px;
530
+ border: 1px solid var(--line-soft);
531
+ border-radius: var(--radius);
532
+ background: var(--panel);
533
+ }
534
+ .object-stats span,
535
+ .value-list span {
536
+ display: block;
537
+ color: var(--muted);
538
+ font-size: 12px;
539
+ font-weight: 700;
540
+ }
541
+ .object-stats strong {
542
+ display: block;
543
+ margin-top: 6px;
544
+ color: var(--ink);
545
+ font-size: 20px;
546
+ line-height: 1.1;
547
+ font-variant-numeric: tabular-nums;
548
+ overflow-wrap: anywhere;
549
+ }
550
+ .data-nested {
551
+ display: grid;
552
+ gap: 8px;
553
+ }
554
+ .data-table {
555
+ overflow-x: auto;
556
+ background: var(--panel);
557
+ }
558
+ .data-table .table-note {
559
+ margin: 0;
560
+ padding: 10px 12px;
561
+ border-top: 1px solid var(--line-soft);
562
+ background: var(--panel-soft);
563
+ }
564
+ .value-list {
565
+ display: flex;
566
+ flex-wrap: wrap;
567
+ gap: 8px;
568
+ padding: 14px;
569
+ }
570
+ .value-list span {
571
+ padding: 6px 9px;
572
+ border: 1px solid var(--line-soft);
573
+ border-radius: 999px;
574
+ background: var(--panel);
575
+ color: var(--muted-strong);
576
+ overflow-wrap: anywhere;
577
+ }
578
+ .numeric { font-variant-numeric: tabular-nums; }
579
+ .positive { color: var(--low); }
580
+ .muted-value { color: var(--muted); }
291
581
  .findings {
292
582
  display: grid;
293
583
  gap: 12px;
@@ -396,26 +686,36 @@ export function renderCatalogReportPage(report: CatalogReport): string {
396
686
  }
397
687
  @media (max-width: 860px) {
398
688
  main { width: min(100% - 24px, 1120px); padding-top: 32px; }
689
+ header { grid-template-columns: 1fr; }
690
+ .description { max-width: none; text-align: left; }
399
691
  .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
692
+ .report-nav { grid-template-columns: repeat(2, minmax(0, 1fr)); }
400
693
  .section-grid { grid-template-columns: 1fr; gap: 18px; }
694
+ .checks { position: static; }
401
695
  .section { padding: 18px; }
402
696
  .section-head { display: block; }
403
697
  .section-head > span { display: block; margin-top: 8px; }
698
+ .object-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
404
699
  }
405
700
  @media (max-width: 520px) {
406
701
  .metrics { grid-template-columns: 1fr; }
702
+ .report-nav { grid-template-columns: 1fr; }
407
703
  .lede { font-size: 16px; }
408
704
  .finding-head { display: block; }
409
705
  .pill { margin-top: 10px; }
706
+ .object-stats { grid-template-columns: 1fr; }
410
707
  }
411
708
  </style>
412
709
  </head>
413
710
  <body>
414
711
  <main>
415
712
  <header>
416
- <p class="eyebrow">Generated ${catalogReportHtmlEscape(report.generatedAt)}</p>
417
- <h1>${catalogReportHtmlEscape(report.title)}</h1>
418
- <p class="lede">${catalogReportHtmlEscape(report.lede)}</p>
713
+ <div>
714
+ <p class="eyebrow">Generated ${catalogReportHtmlEscape(report.generatedAt)}</p>
715
+ <h1>${catalogReportHtmlEscape(report.title)}</h1>
716
+ <p class="lede">${catalogReportHtmlEscape(report.lede)}</p>
717
+ </div>
718
+ <p class="description">${catalogReportHtmlEscape(report.description)}</p>
419
719
  </header>
420
720
  <section class="metrics" aria-label="Audit summary">
421
721
  ${report.metrics
@@ -427,6 +727,7 @@ export function renderCatalogReportPage(report: CatalogReport): string {
427
727
  )
428
728
  .join("")}
429
729
  </section>
730
+ <nav class="report-nav" aria-label="Report sections">${nav}</nav>
430
731
  ${sections}
431
732
  <details>
432
733
  <summary>Compressed JSON appendix</summary>