@desplega.ai/agent-swarm 1.129.0 → 1.131.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 (226) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-kfv2dawe.js → actions-w53mbns9.js} +11 -11
  3. package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
  4. package/dist/{app-q8vjk6e8.js → app-m9gwqsqv.js} +8 -8
  5. package/dist/{artifact-hg4t1nx7.js → artifact-jysj3wrg.js} +2 -2
  6. package/dist/{assistant-g037r345.js → assistant-bp65m6f3.js} +13 -13
  7. package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
  8. package/dist/{boot-reembed-18pzsrxa.js → boot-reembed-62p0at2k.js} +6 -6
  9. package/dist/{boot-reembed-wpks2cj8.js → boot-reembed-xmfp6wdh.js} +7 -7
  10. package/dist/{boot-scrub-logs-n7szd57r.js → boot-scrub-logs-rr8q2dms.js} +5 -5
  11. package/dist/{claude-adapter-qz4r0ajt.js → claude-adapter-2y47mrkd.js} +9 -9
  12. package/dist/{cli-mhjnbgb6.js → cli-0rcyq6d9.js} +450 -186
  13. package/dist/{cli-ttkmehy0.js → cli-2q72qz2s.js} +274 -25
  14. package/dist/{cli-gztgc3hz.js → cli-2wadx7p2.js} +2 -2
  15. package/dist/cli-3fj60etf.js +153 -0
  16. package/dist/{cli-7ecgm5v0.js → cli-3kf51j15.js} +1 -1
  17. package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
  18. package/dist/{cli-2mbe7vk0.js → cli-5dsbcjjw.js} +1 -1
  19. package/dist/{cli-etr62zy6.js → cli-5kx36evn.js} +2 -2
  20. package/dist/{cli-v2tr6amf.js → cli-5n6e5yfn.js} +30 -8
  21. package/dist/{cli-hx02s9zq.js → cli-5v4rw6yd.js} +2 -2
  22. package/dist/{cli-v37yr6mf.js → cli-5ze9rv97.js} +4 -4
  23. package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
  24. package/dist/{cli-h0457a67.js → cli-70xqj0z3.js} +3 -3
  25. package/dist/{cli-kk4f9r17.js → cli-72q8m8gk.js} +8 -6
  26. package/dist/{cli-7padk69a.js → cli-78xwvkhj.js} +2 -2
  27. package/dist/{cli-dxtxacy7.js → cli-9dr5qzc0.js} +45 -8
  28. package/dist/{cli-17zxdgsq.js → cli-a40rnf71.js} +6 -6
  29. package/dist/{cli-t53vnnxz.js → cli-a4jzdpa3.js} +2514 -1055
  30. package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
  31. package/dist/{cli-1qkk5er0.js → cli-bmze986d.js} +1 -1
  32. package/dist/{cli-ga12dc1d.js → cli-bqw0mt1n.js} +3 -3
  33. package/dist/{cli-0sf92efp.js → cli-cdeg08wx.js} +1 -1
  34. package/dist/{cli-a8ynhkyj.js → cli-cvgjwnh1.js} +17 -15
  35. package/dist/{cli-9fxg3v5m.js → cli-d2tfkcga.js} +4 -4
  36. package/dist/{cli-nhcd7x43.js → cli-d3x3x1c9.js} +2 -2
  37. package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
  38. package/dist/{cli-yjbgmysx.js → cli-e7ckcg4v.js} +12 -12
  39. package/dist/{cli-4n2gcjpd.js → cli-efxfnq6q.js} +4 -3
  40. package/dist/{cli-z9xqqhfk.js → cli-er3a4bb0.js} +1 -1
  41. package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
  42. package/dist/{cli-xy4cwsm9.js → cli-fywms69x.js} +1 -1
  43. package/dist/{cli-33jj76y8.js → cli-g6b8x1h8.js} +1 -1
  44. package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
  45. package/dist/{cli-rkndnn90.js → cli-k9qkf1hw.js} +6 -4
  46. package/dist/{cli-zejvt395.js → cli-kh3ba2zf.js} +1 -1
  47. package/dist/{cli-gb25p1h2.js → cli-nm5gtp1x.js} +2 -2
  48. package/dist/{cli-akxnpx17.js → cli-pm6xbh84.js} +1829 -1629
  49. package/dist/{cli-2k0ztbfp.js → cli-psh3c4va.js} +1 -1
  50. package/dist/{cli-39vgrsep.js → cli-qv61hp37.js} +2 -2
  51. package/dist/{cli-hv2fndhm.js → cli-r6kqqbj0.js} +6 -4
  52. package/dist/{cli-1y1cm570.js → cli-rzbpmxdr.js} +1 -1
  53. package/dist/{cli-mmrk00vp.js → cli-t1xze5qh.js} +1 -1
  54. package/dist/{cli-g252a9ky.js → cli-t55yw3a1.js} +2 -2
  55. package/dist/{cli-7bz8dzqa.js → cli-t76vw5db.js} +1184 -1522
  56. package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
  57. package/dist/{cli-fpaa5k2q.js → cli-yw8vghx2.js} +54 -18
  58. package/dist/cli-zsspvvm2.js +490 -0
  59. package/dist/cli.js +27 -25
  60. package/dist/{codex-adapter-7v6t7ff6.js → codex-adapter-d24q75wk.js} +7 -7
  61. package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
  62. package/dist/{codex-session-runner-e7j4khe2.js → codex-session-runner-g50vcsex.js} +7 -7
  63. package/dist/{commands-ex0k5tbf.js → commands-c3nrc5hg.js} +5 -5
  64. package/dist/{db-df7jatfc.js → db-nt7fg3tw.js} +5 -5
  65. package/dist/{google-generative-ai-y42hnw9p.js → google-generative-ai-hysrd8qx.js} +7 -5
  66. package/dist/{google-vertex-4sg66gb6.js → google-vertex-p5arrga0.js} +7 -5
  67. package/dist/{handlers-1j2cfqt2.js → handlers-fkr6swat.js} +13 -13
  68. package/dist/{hook-nx9a0mj3.js → hook-77wpvnjc.js} +7 -7
  69. package/dist/{hook-w6yaf6sy.js → hook-g019yehy.js} +8 -8
  70. package/dist/{http-4tz5h8wg.js → http-jewj0syk.js} +2993 -716
  71. package/dist/{index-0bpkzb09.js → index-070v4eyb.js} +3 -3
  72. package/dist/{index-0s67qv7h.js → index-6gm5he8h.js} +11 -11
  73. package/dist/{index-35gtv6gj.js → index-7n2xpbd4.js} +310 -40
  74. package/dist/{index-ker3gccb.js → index-9znhar3x.js} +4 -4
  75. package/dist/{index-5n2c8yay.js → index-hase6zbe.js} +2 -2
  76. package/dist/{index-kcf3tm9h.js → index-hfvggd0z.js} +12 -12
  77. package/dist/{index-7c0jqna0.js → index-hp1kdbjj.js} +13 -13
  78. package/dist/{keepalive-3j4a9168.js → keepalive-j0fjvqfd.js} +7 -7
  79. package/dist/{lead-6kpevymb.js → lead-1n8x30xe.js} +33 -33
  80. package/dist/{maintenance-m1k82ean.js → maintenance-gvev11sa.js} +7 -7
  81. package/dist/{mistral-conversations-bjkdfb6z.js → mistral-conversations-81fwav0j.js} +6 -6
  82. package/dist/{oauth-refresh-sweep-881k8e4q.js → oauth-refresh-sweep-s9ecsvh0.js} +6 -6
  83. package/dist/{onboard-c0ykffjn.js → onboard-pvm5b38w.js} +2 -2
  84. package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
  85. package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
  86. package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
  87. package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
  88. package/dist/{otel-impl-sqagjerv.js → otel-impl-p6jvgd9c.js} +6 -4
  89. package/dist/{otel-3mp81ppd.js → otel-nqa12wf3.js} +2 -2
  90. package/dist/{pi-mono-adapter-k584k16g.js → pi-mono-adapter-t091s27g.js} +17117 -7517
  91. package/dist/{pricing-refresh-y82whbf9.js → pricing-refresh-k49rcyed.js} +7 -7
  92. package/dist/{rbac-roles-nvkqmzyd.js → rbac-roles-30pspvy7.js} +7 -7
  93. package/dist/{rbac-roles-4ht8gegc.js → rbac-roles-cr80g96t.js} +6 -6
  94. package/dist/{render-v2-wvqdgvvp.js → render-v2-xm457ng1.js} +8 -8
  95. package/dist/{seed-pricing-fy3rxhde.js → seed-pricing-yhpv35fr.js} +6 -6
  96. package/dist/{setup-ak5cnc6f.js → setup-nytnmwwr.js} +2 -2
  97. package/dist/{worker-d8769q8a.js → worker-msytr2cw.js} +33 -33
  98. package/openapi.json +34039 -11036
  99. package/package.json +8 -6
  100. package/src/apps/definition.ts +368 -26
  101. package/src/apps/row-store.ts +66 -10
  102. package/src/apps/schema-migrate.ts +95 -1
  103. package/src/apps/store.ts +13 -4
  104. package/src/apps/sync-run-as.ts +18 -0
  105. package/src/apps/sync.ts +934 -0
  106. package/src/apps/version.ts +16 -2
  107. package/src/assets/key.ts +8 -1
  108. package/src/be/asset-key-audit.ts +3 -1
  109. package/src/be/db-queries/mcp-oauth.ts +297 -24
  110. package/src/be/db.ts +42 -1
  111. package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
  112. package/src/be/scripts/db.ts +5 -2
  113. package/src/be/scripts/typecheck.ts +1 -0
  114. package/src/be/seed-scripts/catalog/app-sync-run.ts +32 -0
  115. package/src/be/seed-scripts/catalog/github-issues-pull.ts +102 -0
  116. package/src/be/seed-scripts/index.ts +18 -0
  117. package/src/be/seed-skills/index.ts +7 -0
  118. package/src/http/active-sessions.ts +26 -18
  119. package/src/http/agents.ts +94 -27
  120. package/src/http/api-keys.ts +91 -17
  121. package/src/http/approval-requests.ts +87 -10
  122. package/src/http/apps.ts +361 -53
  123. package/src/http/assets.ts +165 -31
  124. package/src/http/budgets.ts +7 -5
  125. package/src/http/codex-oauth-keep-warm.ts +21 -3
  126. package/src/http/config.ts +46 -16
  127. package/src/http/context.ts +33 -6
  128. package/src/http/ecosystem.ts +20 -6
  129. package/src/http/events.ts +24 -9
  130. package/src/http/favorites.ts +20 -6
  131. package/src/http/fs.ts +60 -17
  132. package/src/http/heartbeat.ts +18 -4
  133. package/src/http/inbox-state.ts +12 -6
  134. package/src/http/integrations.ts +25 -5
  135. package/src/http/kv.ts +36 -15
  136. package/src/http/mcp-bridge.ts +5 -1
  137. package/src/http/mcp-oauth.ts +332 -17
  138. package/src/http/mcp-servers.ts +56 -18
  139. package/src/http/memory.ts +282 -29
  140. package/src/http/metrics.ts +80 -17
  141. package/src/http/oauth-callback.ts +10 -4
  142. package/src/http/oauth-generic.ts +5 -1
  143. package/src/http/oauth-locks.ts +6 -3
  144. package/src/http/openapi.ts +57 -9
  145. package/src/http/page-proxy.ts +25 -5
  146. package/src/http/pages-public.ts +37 -20
  147. package/src/http/pages.ts +93 -28
  148. package/src/http/poll.ts +89 -5
  149. package/src/http/pricing.ts +9 -6
  150. package/src/http/prompt-templates.ts +85 -21
  151. package/src/http/route-def.ts +67 -4
  152. package/src/http/schedules.ts +63 -14
  153. package/src/http/script-connection-proxy.ts +30 -4
  154. package/src/http/script-connections.ts +393 -43
  155. package/src/http/script-runs.ts +73 -19
  156. package/src/http/scripts.ts +257 -39
  157. package/src/http/session-data.ts +87 -14
  158. package/src/http/sessions.ts +80 -5
  159. package/src/http/skills.ts +115 -35
  160. package/src/http/stats.ts +94 -13
  161. package/src/http/task-templates.ts +6 -4
  162. package/src/http/tasks.ts +230 -61
  163. package/src/http/trackers/jira.ts +90 -30
  164. package/src/http/trackers/linear.ts +46 -12
  165. package/src/http/users.ts +162 -25
  166. package/src/http/utils.ts +12 -2
  167. package/src/http/webhooks.ts +30 -16
  168. package/src/http/workflow-events.ts +18 -5
  169. package/src/http/workflows.ts +128 -39
  170. package/src/http/x.ts +37 -6
  171. package/src/oauth/ensure-mcp-token.ts +16 -1
  172. package/src/oauth/mcp-wrapper.ts +281 -26
  173. package/src/providers/pi-mono-adapter.ts +1 -1
  174. package/src/scripts-runtime/credential-broker/README.md +1 -1
  175. package/src/scripts-runtime/credential-broker/fetch-patch.ts +12 -2
  176. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  177. package/src/scripts-runtime/stdlib/fetch.ts +6 -0
  178. package/src/scripts-runtime/types/stdlib.d.ts +1 -0
  179. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
  180. package/src/server.ts +2 -0
  181. package/src/tests/apps-rbac.test.ts +110 -0
  182. package/src/tests/apps-spike2.test.ts +4 -1
  183. package/src/tests/apps-sync-engine.test.ts +2540 -0
  184. package/src/tests/apps-sync.test.ts +1627 -0
  185. package/src/tests/asset-key-api.test.ts +48 -2
  186. package/src/tests/asset-key-invariants.test.ts +92 -0
  187. package/src/tests/asset-key-migration.test.ts +174 -8
  188. package/src/tests/asset-key-normalization.test.ts +2 -0
  189. package/src/tests/credential-broker.test.ts +27 -2
  190. package/src/tests/mcp-oauth-dcr-reuse.test.ts +743 -0
  191. package/src/tests/mcp-oauth-ensure-token.test.ts +45 -0
  192. package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
  193. package/src/tests/mcp-oauth-queries.test.ts +153 -0
  194. package/src/tests/mcp-oauth-wrapper.test.ts +432 -0
  195. package/src/tests/memory-http-recall-gating.test.ts +2 -0
  196. package/src/tests/openapi-response-contract.test.ts +138 -0
  197. package/src/tests/rbac-admission-e2e.test.ts +68 -0
  198. package/src/tests/script-connections.test.ts +3 -0
  199. package/src/tests/scripts-runtime-fetch.test.ts +121 -0
  200. package/src/tests/scripts-runtime-secret-egress.test.ts +4 -2
  201. package/src/tests/seed-scripts.test.ts +2 -2
  202. package/src/tests/swarm-tool-result-gate.test.ts +32 -0
  203. package/src/tests/task-working-dir.test.ts +8 -4
  204. package/src/tests/whoami-route.test.ts +127 -0
  205. package/src/tests/workflow-engine-v2.test.ts +80 -7
  206. package/src/tests/workflow-swarm-script.test.ts +104 -0
  207. package/src/tools/app-get.ts +10 -2
  208. package/src/tools/app-rollback.ts +1 -0
  209. package/src/tools/app-sync.ts +112 -0
  210. package/src/tools/script-common.ts +20 -0
  211. package/src/tools/tool-config.ts +2 -1
  212. package/src/tools/utils.ts +11 -0
  213. package/src/tools/workflows/create-workflow.ts +4 -1
  214. package/src/types.ts +1394 -1206
  215. package/src/utils/template.ts +8 -1
  216. package/src/utils/zod-openapi.ts +11 -0
  217. package/src/workflows/engine.ts +27 -4
  218. package/src/workflows/executors/swarm-script.ts +28 -0
  219. package/src/workflows/retry-poller.ts +19 -2
  220. package/templates/skills/apps/content.md +94 -5
  221. package/templates/skills/asset-namespaces/content.md +2 -2
  222. package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
  223. package/templates/skills/scheduled-task-resilience/config.json +14 -0
  224. package/templates/skills/scheduled-task-resilience/content.md +97 -0
  225. package/dist/cli-7wmybrt5.js +0 -115
  226. package/dist/cli-d91pvmq1.js +0 -14
@@ -1,6 +1,8 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { z } from "zod";
3
+ import { type AppValidationIssue, collectScriptReferences } from "../apps/definition";
3
4
  import { getScriptAppTypes } from "../apps/script-types";
5
+ import { listAppRecords } from "../apps/store";
4
6
  import { resolveHttpAuditUserId } from "../be/audit-user";
5
7
  import { getAgentById, recordInlineScriptRun, upsertKv } from "../be/db";
6
8
  import { createEvent } from "../be/events";
@@ -34,16 +36,19 @@ import {
34
36
  typecheckScript,
35
37
  } from "../be/scripts/typecheck";
36
38
  import { can } from "../rbac";
37
- import { extractScriptSignature } from "../scripts-runtime/extract-signature";
39
+ import { extractScriptSignature, type ScriptSignature } from "../scripts-runtime/extract-signature";
38
40
  import { runScript } from "../scripts-runtime/loader";
39
41
  import {
40
42
  ScriptApiAuthModeSchema,
43
+ ScriptApiRecordSchema,
41
44
  type ScriptDetail,
42
45
  ScriptFsModeSchema,
43
46
  type ScriptListItem,
44
47
  type ScriptRecord,
48
+ ScriptRecordSchema,
45
49
  type ScriptScope,
46
50
  ScriptScopeSchema,
51
+ ScriptVersionRecordSchema,
47
52
  } from "../types";
48
53
  import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
49
54
  import { route } from "./route-def";
@@ -89,6 +94,121 @@ const listScriptsQuerySchema = z.object({
89
94
  includeScratch: z.enum(["true", "false"]).optional(),
90
95
  });
91
96
 
97
+ // ─── Response schemas ──────────────────────────────────────────────────────
98
+ // `signatureJson` is always produced by `signatureJsonFor` (below) /
99
+ // `extractScriptSignature`, so `JSON.parse(script.signatureJson)` is always
100
+ // exactly this shape.
101
+ const scriptSignatureSchema = z.object({
102
+ argsType: z.string(),
103
+ resultType: z.string(),
104
+ description: z.string(),
105
+ });
106
+
107
+ // `argsJsonSchema` is an arbitrary JSON Schema blob produced by
108
+ // `extractArgsJsonSchema` (always `zod.toJSONSchema()` output, i.e. a JSON
109
+ // object) or `null` when the script exports no `argsSchema`. Modelled as an
110
+ // open object rather than `z.unknown()` so the property stays REQUIRED in the
111
+ // emitted spec — every route below always writes the key.
112
+ const argsJsonSchemaValueSchema = z.record(z.string(), z.unknown()).nullable();
113
+
114
+ /**
115
+ * `rowToScript` / `rowToScriptVersion` (src/be/scripts/db.ts) build their
116
+ * records by spreading a `SELECT *` row, so the audit columns added by
117
+ * migration 082 ride along on the wire for every route that serves a mapped
118
+ * row verbatim. Declared (optional — the TS row types don't model them, so
119
+ * `respond()` can't require them at the call site) so the spec stops omitting
120
+ * fields that are actually sent. The tidier fix is projecting them out in the
121
+ * row mappers, which lives outside this file.
122
+ */
123
+ const rowAuditColumnsShape = {
124
+ created_by: z.string().nullable().optional(),
125
+ updated_by: z.string().nullable().optional(),
126
+ };
127
+
128
+ /** Lean projection served by `GET /api/scripts` — mirrors `ScriptListItem`. */
129
+ const scriptListItemSchema = ScriptRecordSchema.omit({
130
+ source: true,
131
+ signatureJson: true,
132
+ argsJsonSchema: true,
133
+ contentHash: true,
134
+ });
135
+
136
+ /**
137
+ * Full record served by `GET /api/scripts/{id}` — mirrors `ScriptDetail`.
138
+ * `ScriptDetail` widens `signature`/`argsJsonSchema` to `unknown`, but the
139
+ * wire values are exactly what `typesRoute`/`searchRoute` below serve, and both
140
+ * keys are always written — so they are declared precisely (and required) here
141
+ * too rather than as an untyped, optional blob.
142
+ */
143
+ const scriptDetailSchema = ScriptRecordSchema.omit({ argsJsonSchema: true }).extend({
144
+ signature: scriptSignatureSchema,
145
+ argsJsonSchema: argsJsonSchemaValueSchema,
146
+ ...rowAuditColumnsShape,
147
+ });
148
+
149
+ /** Mirrors `ScriptApiWithSecret` — a `ScriptApiRecord` plus the plaintext token. */
150
+ const scriptApiWithSecretSchema = ScriptApiRecordSchema.extend({
151
+ token: z.string().nullable(),
152
+ });
153
+
154
+ // Mirrors `RunScriptOutput` (scripts-runtime/loader.ts), scrubbed via
155
+ // `scrubObject`, plus the two optional persistence-side-effect markers this
156
+ // route adds.
157
+ const scriptRunResponseSchema = z.object({
158
+ result: z.unknown().optional(),
159
+ autoSaved: z.object({ slug: z.string(), reason: z.string() }).optional(),
160
+ kvSaved: z.object({ namespace: z.string(), key: z.string() }).optional(),
161
+ truncated: z.object({ stdout: z.boolean(), stderr: z.boolean() }),
162
+ durationMs: z.number(),
163
+ stdout: z.string(),
164
+ stderr: z.string(),
165
+ exitCode: z.number(),
166
+ error: z
167
+ .enum(["timeout", "oom", "killed", "import_violation", "eval_error", "executor_error"])
168
+ .optional(),
169
+ runtimeError: z
170
+ .object({
171
+ name: z.string(),
172
+ message: z.string(),
173
+ stack: z.string(),
174
+ userFrames: z.array(
175
+ z.object({
176
+ file: z.string(),
177
+ line: z.number(),
178
+ column: z.number(),
179
+ raw: z.string(),
180
+ }),
181
+ ),
182
+ userScriptLine: z.number().optional(),
183
+ userScriptColumn: z.number().optional(),
184
+ })
185
+ .optional(),
186
+ });
187
+
188
+ const searchResponseSchema = z.object({
189
+ results: z.array(
190
+ z.object({
191
+ name: z.string(),
192
+ signature: scriptSignatureSchema,
193
+ argsJsonSchema: argsJsonSchemaValueSchema,
194
+ description: z.string(),
195
+ score: z.number(),
196
+ }),
197
+ ),
198
+ });
199
+
200
+ const scriptTypesResponseSchema = z.object({
201
+ signature: scriptSignatureSchema,
202
+ argsJsonSchema: argsJsonSchemaValueSchema,
203
+ sdkTypes: z.string(),
204
+ stdlibTypes: z.string(),
205
+ });
206
+
207
+ const typeDefsResponseSchema = z.object({
208
+ sdkTypes: z.string(),
209
+ stdlibTypes: z.string(),
210
+ });
211
+
92
212
  const upsertRoute = route({
93
213
  method: "post",
94
214
  path: "/api/scripts/upsert",
@@ -99,7 +219,14 @@ const upsertRoute = route({
99
219
  tags: ["Scripts"],
100
220
  body: upsertBodySchema,
101
221
  responses: {
102
- 200: { description: "Script upserted" },
222
+ 200: {
223
+ description: "Script upserted",
224
+ schema: z.object({
225
+ name: z.string(),
226
+ version: z.number(),
227
+ contentDeduped: z.boolean(),
228
+ }),
229
+ },
103
230
  400: { description: "Validation or typecheck failure" },
104
231
  403: { description: "Global write requires lead agent" },
105
232
  },
@@ -117,7 +244,7 @@ const runRoute = route({
117
244
  tags: ["Scripts"],
118
245
  body: runBodySchema,
119
246
  responses: {
120
- 200: { description: "Script run completed" },
247
+ 200: { description: "Script run completed", schema: scriptRunResponseSchema },
121
248
  400: { description: "Validation error" },
122
249
  404: { description: "Script not found" },
123
250
  501: { description: "workspace-rw scripts are not supported in v1" },
@@ -134,7 +261,7 @@ const searchRoute = route({
134
261
  tags: ["Scripts"],
135
262
  body: searchBodySchema,
136
263
  responses: {
137
- 200: { description: "Matching scripts" },
264
+ 200: { description: "Matching scripts", schema: searchResponseSchema },
138
265
  400: { description: "Validation error" },
139
266
  },
140
267
  rbac: { permission: "script.search" },
@@ -150,9 +277,10 @@ const deleteRoute = route({
150
277
  params: nameParamsSchema,
151
278
  query: scopeQuerySchema,
152
279
  responses: {
153
- 200: { description: "Delete result" },
280
+ 200: { description: "Delete result", schema: z.object({ deleted: z.boolean() }) },
154
281
  400: { description: "Validation error" },
155
282
  403: { description: "Global delete requires lead agent" },
283
+ 409: { description: "Script is referenced by an app definition" },
156
284
  },
157
285
  rbac: { permission: "script.global.delete" },
158
286
  });
@@ -167,7 +295,7 @@ const typesRoute = route({
167
295
  params: nameParamsSchema,
168
296
  query: optionalScopeQuerySchema,
169
297
  responses: {
170
- 200: { description: "Script signature and type blobs" },
298
+ 200: { description: "Script signature and type blobs", schema: scriptTypesResponseSchema },
171
299
  404: { description: "Script not found" },
172
300
  },
173
301
  });
@@ -189,7 +317,10 @@ const listScriptsRoute = route({
189
317
  tags: ["Scripts"],
190
318
  query: listScriptsQuerySchema,
191
319
  responses: {
192
- 200: { description: "Saved scripts" },
320
+ 200: {
321
+ description: "Saved scripts",
322
+ schema: z.object({ scripts: z.array(scriptListItemSchema) }),
323
+ },
193
324
  400: { description: "Validation error" },
194
325
  },
195
326
  });
@@ -207,7 +338,10 @@ const typeDefsRoute = route({
207
338
  "Generated .d.ts blobs for editor integration (e.g. Monaco extraLibs), including per-app types. Cacheable.",
208
339
  tags: ["Scripts"],
209
340
  responses: {
210
- 200: { description: "SDK and stdlib type definition blobs" },
341
+ 200: {
342
+ description: "SDK and stdlib type definition blobs",
343
+ schema: typeDefsResponseSchema,
344
+ },
211
345
  },
212
346
  });
213
347
 
@@ -221,7 +355,7 @@ const getScriptByIdRoute = route({
221
355
  tags: ["Scripts"],
222
356
  params: idParamsSchema,
223
357
  responses: {
224
- 200: { description: "Script detail" },
358
+ 200: { description: "Script detail", schema: z.object({ script: scriptDetailSchema }) },
225
359
  404: { description: "Script not found" },
226
360
  },
227
361
  });
@@ -236,7 +370,13 @@ const listVersionsRoute = route({
236
370
  tags: ["Scripts"],
237
371
  params: idParamsSchema,
238
372
  responses: {
239
- 200: { description: "Script versions" },
373
+ 200: {
374
+ description: "Script versions",
375
+ // `listScriptVersions` spreads a `SELECT *` row — see `rowAuditColumnsShape`.
376
+ schema: z.object({
377
+ versions: z.array(ScriptVersionRecordSchema.extend(rowAuditColumnsShape)),
378
+ }),
379
+ },
240
380
  404: { description: "Script not found" },
241
381
  },
242
382
  });
@@ -272,7 +412,7 @@ const createScriptApiRoute = route({
272
412
  params: idParamsSchema,
273
413
  body: createScriptApiBodySchema,
274
414
  responses: {
275
- 201: { description: "Endpoint created" },
415
+ 201: { description: "Endpoint created", schema: scriptApiWithSecretSchema },
276
416
  400: { description: "Validation error or script has no owning agent" },
277
417
  404: { description: "Script not found" },
278
418
  },
@@ -288,7 +428,10 @@ const listScriptApisRoute = route({
288
428
  tags: ["Scripts"],
289
429
  params: idParamsSchema,
290
430
  responses: {
291
- 200: { description: "Endpoints (without secrets)" },
431
+ 200: {
432
+ description: "Endpoints (without secrets)",
433
+ schema: z.object({ apis: z.array(ScriptApiRecordSchema) }),
434
+ },
292
435
  404: { description: "Script not found" },
293
436
  },
294
437
  });
@@ -302,7 +445,10 @@ const revealScriptApiSecretRoute = route({
302
445
  tags: ["Scripts"],
303
446
  params: apiEndpointParamsSchema,
304
447
  responses: {
305
- 200: { description: "Decrypted token (null when authMode is 'none')" },
448
+ 200: {
449
+ description: "Decrypted token (null when authMode is 'none')",
450
+ schema: z.object({ token: z.string().nullable() }),
451
+ },
306
452
  404: { description: "Endpoint not found" },
307
453
  },
308
454
  rbac: { permission: "script.api.read.secrets" },
@@ -318,7 +464,17 @@ const patchScriptApiRoute = route({
318
464
  params: apiEndpointParamsSchema,
319
465
  body: patchScriptApiBodySchema,
320
466
  responses: {
321
- 200: { description: "Updated endpoint" },
467
+ // `updateScriptApi` is typed `ScriptApiRecord | null` (the id could vanish
468
+ // between the existence check and the UPDATE); the handler sends its
469
+ // result verbatim without a null guard, so this is honestly nullable.
470
+ // Spelled as an explicit union, not `.nullable()`: `.nullable()` on a
471
+ // `.openapi()`-registered schema emits `allOf: [$ref, {type:[object,null]}]`,
472
+ // an intersection that `null` can never satisfy — i.e. it silently documents
473
+ // the opposite of the handler's behavior.
474
+ 200: {
475
+ description: "Updated endpoint",
476
+ schema: z.union([ScriptApiRecordSchema, z.null()]),
477
+ },
322
478
  404: { description: "Endpoint not found" },
323
479
  },
324
480
  rbac: { permission: "script.api.update" },
@@ -333,7 +489,10 @@ const rotateScriptApiRoute = route({
333
489
  tags: ["Scripts"],
334
490
  params: apiEndpointParamsSchema,
335
491
  responses: {
336
- 200: { description: "Endpoint with new plaintext token" },
492
+ 200: {
493
+ description: "Endpoint with new plaintext token",
494
+ schema: scriptApiWithSecretSchema,
495
+ },
337
496
  400: { description: "Endpoint uses 'none' auth — nothing to rotate" },
338
497
  404: { description: "Endpoint not found" },
339
498
  },
@@ -349,12 +508,45 @@ const deleteScriptApiRoute = route({
349
508
  tags: ["Scripts"],
350
509
  params: apiEndpointParamsSchema,
351
510
  responses: {
352
- 200: { description: "Deleted" },
511
+ 200: { description: "Deleted", schema: z.object({ deleted: z.boolean() }) },
353
512
  404: { description: "Endpoint not found" },
354
513
  },
355
514
  rbac: { permission: "script.api.delete" },
356
515
  });
357
516
 
517
+ /**
518
+ * Every app definition that references `scriptId`, as path-bearing issues.
519
+ *
520
+ * Two-pass by design: the tolerant collector walks the stored JSON (so an app
521
+ * whose definition no longer parses still reports its references with exact
522
+ * paths), and any app that failed to decode at all — invalid stored JSON, where
523
+ * the "definition" is a raw string — falls back to a substring probe. A broken
524
+ * app is not consent to break it further.
525
+ *
526
+ * Cost: O(apps x definition size) per delete, all in memory. Deletes are rare
527
+ * and single-app installs hold tens of apps, so no index is warranted.
528
+ */
529
+ function appScriptReferenceIssues(scriptId: string): AppValidationIssue[] {
530
+ const issues: AppValidationIssue[] = [];
531
+ for (const app of listAppRecords()) {
532
+ const paths = collectScriptReferences(app.definition).get(scriptId) ?? [];
533
+ if (
534
+ paths.length === 0 &&
535
+ app.definitionError !== undefined &&
536
+ JSON.stringify(app.definition ?? null).includes(scriptId)
537
+ ) {
538
+ paths.push("its (unparseable) definition");
539
+ }
540
+ for (const path of paths) {
541
+ issues.push({
542
+ path: `apps.${app.id}`,
543
+ message: `app "${app.name}" (${app.id}) uses this script at ${path}`,
544
+ });
545
+ }
546
+ }
547
+ return issues;
548
+ }
549
+
358
550
  function requireAgent(res: ServerResponse, agentId: string | undefined) {
359
551
  if (!agentId) {
360
552
  jsonError(res, "X-Agent-ID required for scripts API", 400);
@@ -487,7 +679,7 @@ export async function handleScripts(
487
679
  });
488
680
  }
489
681
 
490
- json(res, {
682
+ upsertRoute.respond(res, 200, {
491
683
  name: result.script.name,
492
684
  version: result.script.version,
493
685
  contentDeduped: result.contentDeduped,
@@ -608,8 +800,9 @@ export async function handleScripts(
608
800
  // swallow — the run already executed; persistence is observability only.
609
801
  }
610
802
 
611
- json(
803
+ runRoute.respond(
612
804
  res,
805
+ 200,
613
806
  scrubObject({
614
807
  result: output.result,
615
808
  autoSaved,
@@ -639,12 +832,12 @@ export async function handleScripts(
639
832
  limit: parsed.body.limit,
640
833
  });
641
834
 
642
- json(res, {
835
+ searchRoute.respond(res, 200, {
643
836
  results: matches.map(({ script, score }) => ({
644
837
  name: script.name,
645
838
  signature: JSON.parse(script.signatureJson),
646
839
  argsJsonSchema: script.argsJsonSchema
647
- ? (JSON.parse(script.argsJsonSchema) as unknown)
840
+ ? (JSON.parse(script.argsJsonSchema) as Record<string, unknown>)
648
841
  : null,
649
842
  description: script.description,
650
843
  score,
@@ -676,7 +869,7 @@ export async function handleScripts(
676
869
  createdAt: script.createdAt,
677
870
  updatedAt: script.updatedAt,
678
871
  }));
679
- json(res, { scripts });
872
+ listScriptsRoute.respond(res, 200, { scripts });
680
873
  return true;
681
874
  }
682
875
 
@@ -686,7 +879,7 @@ export async function handleScripts(
686
879
  const apiTypes = getScriptApiTypes();
687
880
  const mcpTypes = getScriptMcpTypes();
688
881
  const appTypes = getScriptAppTypes();
689
- json(res, {
882
+ typeDefsRoute.respond(res, 200, {
690
883
  sdkTypes: scriptSdkTypesWithGeneratedApis(apiTypes, mcpTypes, appTypes),
691
884
  stdlibTypes: scriptStdlibTypesWithGeneratedApis(apiTypes, mcpTypes, appTypes),
692
885
  });
@@ -703,12 +896,14 @@ export async function handleScripts(
703
896
  }
704
897
  // `source` is author-supplied TS (same trust surface as script_runs.source,
705
898
  // already served raw by GET /api/script-runs/{id}) — no env/secret material.
706
- const detail: ScriptDetail = {
899
+ const detail = {
707
900
  ...script,
708
- signature: JSON.parse(script.signatureJson) as unknown,
709
- argsJsonSchema: script.argsJsonSchema ? (JSON.parse(script.argsJsonSchema) as unknown) : null,
710
- };
711
- json(res, { script: detail });
901
+ signature: JSON.parse(script.signatureJson) as ScriptSignature,
902
+ argsJsonSchema: script.argsJsonSchema
903
+ ? (JSON.parse(script.argsJsonSchema) as Record<string, unknown>)
904
+ : null,
905
+ } satisfies ScriptDetail;
906
+ getScriptByIdRoute.respond(res, 200, { script: detail });
712
907
  return true;
713
908
  }
714
909
 
@@ -719,7 +914,7 @@ export async function handleScripts(
719
914
  jsonError(res, "Script not found", 404);
720
915
  return true;
721
916
  }
722
- json(res, { versions: listScriptVersions(parsed.params.id) });
917
+ listVersionsRoute.respond(res, 200, { versions: listScriptVersions(parsed.params.id) });
723
918
  return true;
724
919
  }
725
920
 
@@ -734,9 +929,11 @@ export async function handleScripts(
734
929
  jsonError(res, "Script not found", 404);
735
930
  return true;
736
931
  }
737
- json(res, {
932
+ typesRoute.respond(res, 200, {
738
933
  signature: JSON.parse(script.signatureJson),
739
- argsJsonSchema: script.argsJsonSchema ? (JSON.parse(script.argsJsonSchema) as unknown) : null,
934
+ argsJsonSchema: script.argsJsonSchema
935
+ ? (JSON.parse(script.argsJsonSchema) as Record<string, unknown>)
936
+ : null,
740
937
  sdkTypes: scriptSdkTypesWithGeneratedApis(
741
938
  getScriptApiTypes({ agentId: agent.id }),
742
939
  getScriptMcpTypes({ agentId: agent.id }),
@@ -772,12 +969,33 @@ export async function handleScripts(
772
969
  }
773
970
  }
774
971
 
775
- const deleted = deleteScript({
972
+ const identity = {
776
973
  name: parsed.params.name,
777
974
  scope: parsed.query.scope,
778
975
  scopeId: parsed.query.scope === "agent" ? agent.id : null,
779
- });
780
- json(res, { deleted });
976
+ };
977
+ const existing = getScript(identity);
978
+ if (existing) {
979
+ // An app that wires this script as a source or a script action would be
980
+ // left with a dangling reference: its definition stops parsing and every
981
+ // write 409s "needs repair". Refuse the delete instead. UPDATES stay
982
+ // allowed — a contract break there is a pass error with zero row churn.
983
+ const references = appScriptReferenceIssues(existing.id);
984
+ if (references.length > 0) {
985
+ json(
986
+ res,
987
+ {
988
+ error: "script is referenced by an app definition",
989
+ issues: references,
990
+ },
991
+ 409,
992
+ );
993
+ return true;
994
+ }
995
+ }
996
+
997
+ const deleted = deleteScript(identity);
998
+ deleteRoute.respond(res, 200, { deleted });
781
999
  return true;
782
1000
  }
783
1001
 
@@ -804,7 +1022,7 @@ export async function handleScripts(
804
1022
  label: parsed.body.label ?? null,
805
1023
  createdBy: resolveHttpAuditUserId(req, agentId),
806
1024
  });
807
- json(res, endpoint, 201);
1025
+ createScriptApiRoute.respond(res, 201, endpoint);
808
1026
  return true;
809
1027
  }
810
1028
 
@@ -815,7 +1033,7 @@ export async function handleScripts(
815
1033
  jsonError(res, "Script not found", 404);
816
1034
  return true;
817
1035
  }
818
- json(res, { apis: listScriptApisForScript(parsed.params.id) });
1036
+ listScriptApisRoute.respond(res, 200, { apis: listScriptApisForScript(parsed.params.id) });
819
1037
  return true;
820
1038
  }
821
1039
 
@@ -827,7 +1045,7 @@ export async function handleScripts(
827
1045
  jsonError(res, "Endpoint not found", 404);
828
1046
  return true;
829
1047
  }
830
- json(res, { token: getScriptApiSecret(endpoint.id) });
1048
+ revealScriptApiSecretRoute.respond(res, 200, { token: getScriptApiSecret(endpoint.id) });
831
1049
  return true;
832
1050
  }
833
1051
 
@@ -844,7 +1062,7 @@ export async function handleScripts(
844
1062
  jsonError(res, "Cannot rotate a token on a 'none' auth endpoint", 400);
845
1063
  return true;
846
1064
  }
847
- json(res, rotated);
1065
+ rotateScriptApiRoute.respond(res, 200, rotated);
848
1066
  return true;
849
1067
  }
850
1068
 
@@ -861,7 +1079,7 @@ export async function handleScripts(
861
1079
  label: parsed.body.label,
862
1080
  updatedBy: resolveHttpAuditUserId(req, agentId),
863
1081
  });
864
- json(res, updated);
1082
+ patchScriptApiRoute.respond(res, 200, updated);
865
1083
  return true;
866
1084
  }
867
1085
 
@@ -873,7 +1091,7 @@ export async function handleScripts(
873
1091
  jsonError(res, "Endpoint not found", 404);
874
1092
  return true;
875
1093
  }
876
- json(res, { deleted: deleteScriptApi(endpoint.id) });
1094
+ deleteScriptApiRoute.respond(res, 200, { deleted: deleteScriptApi(endpoint.id) });
877
1095
  return true;
878
1096
  }
879
1097