@desplega.ai/agent-swarm 1.151.0 → 1.152.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +3 -1
  2. package/dist/{acp-adapter-ex8hzjfc.js → acp-adapter-4s7q25ft.js} +1 -1
  3. package/dist/{actions-2bgpyhww.js → actions-cs6rhmvc.js} +13 -13
  4. package/dist/{app-1706zhr1.js → app-j54aq0dj.js} +3 -3
  5. package/dist/{assistant-bavke5p7.js → assistant-w1pgn7vf.js} +14 -14
  6. package/dist/{boot-reembed-3cmrrmpa.js → boot-reembed-42vgkrrj.js} +4 -4
  7. package/dist/{boot-reembed-nafcmmmc.js → boot-reembed-smy30z32.js} +3 -3
  8. package/dist/{boot-scrub-logs-cd6a2sq0.js → boot-scrub-logs-b6s72fsw.js} +2 -2
  9. package/dist/{claude-adapter-wfp2gzvz.js → claude-adapter-fh7rmcj9.js} +3 -3
  10. package/dist/{claude-sdk-session-bht9jjak.js → claude-sdk-session-xhynatn9.js} +3 -3
  11. package/dist/{cli-f1wky6zp.js → cli-3g61md6v.js} +1 -1
  12. package/dist/{cli-a1z3h107.js → cli-3pft2z6r.js} +4 -4
  13. package/dist/{cli-5tpgq60f.js → cli-72tgtzr1.js} +152 -23
  14. package/dist/{cli-0qb1z6s2.js → cli-7bwaerjt.js} +1 -1
  15. package/dist/{cli-czjtgfys.js → cli-b4dyetqt.js} +2 -2
  16. package/dist/{cli-s14agm5s.js → cli-bdt7w021.js} +12 -3
  17. package/dist/{cli-sjaae6ff.js → cli-dsyg66vt.js} +3 -3
  18. package/dist/{cli-2tbvf1z1.js → cli-ftdjz1mk.js} +8 -8
  19. package/dist/{cli-zkxymas5.js → cli-gfez0qmk.js} +1 -1
  20. package/dist/{cli-q88st9aj.js → cli-gxf2ebyz.js} +2 -2
  21. package/dist/{cli-ppm4975n.js → cli-jtvs5m8q.js} +1 -1
  22. package/dist/{cli-7tgcstn2.js → cli-jytf5mw9.js} +2 -2
  23. package/dist/{cli-g69fdc9g.js → cli-kf8qp2vf.js} +2 -2
  24. package/dist/{cli-5jd2z7av.js → cli-ks8a1cgq.js} +6 -2
  25. package/dist/{cli-gjyvhmmh.js → cli-ng4q6a9t.js} +5 -5
  26. package/dist/{cli-3v9xhjcn.js → cli-sakxd2rg.js} +2 -2
  27. package/dist/{cli-frcta2w4.js → cli-sqmh7jj1.js} +1 -1
  28. package/dist/{cli-vwm416m6.js → cli-v9sk4dzw.js} +1 -1
  29. package/dist/{cli-j9xczhaq.js → cli-vbk025fm.js} +1 -1
  30. package/dist/{cli-r2q6pevn.js → cli-wfve107r.js} +43 -21
  31. package/dist/{cli-9004vfdf.js → cli-y8tayczk.js} +2 -2
  32. package/dist/{cli-gbfwcd3k.js → cli-zfzznn7c.js} +7 -6
  33. package/dist/cli.js +11 -11
  34. package/dist/{codex-adapter-jjjk8w4m.js → codex-adapter-k6yq95sn.js} +2 -2
  35. package/dist/{codex-session-runner-nd6ayxkx.js → codex-session-runner-3rp84pk8.js} +2 -2
  36. package/dist/{commands-9v4psyq1.js → commands-pbr5jzy5.js} +2 -2
  37. package/dist/{db-wnt58tpe.js → db-z0xaa59a.js} +2 -2
  38. package/dist/{dispatcher-05jh1aqk.js → dispatcher-pwpgsj0x.js} +12 -12
  39. package/dist/{handlers-tw90pkh5.js → handlers-5mzk4mbg.js} +15 -15
  40. package/dist/{hook-sxe3r349.js → hook-8bz33zee.js} +1 -1
  41. package/dist/{hook-123e2scb.js → hook-kbh84zb1.js} +2 -2
  42. package/dist/{http-ecsar8ez.js → http-pppxg5vr.js} +100 -36
  43. package/dist/{index-hw26vj40.js → index-68bd9b5f.js} +12 -12
  44. package/dist/{index-16js521z.js → index-k1hq1qt6.js} +8 -8
  45. package/dist/{index-zj518gf9.js → index-mjpxsr6k.js} +13 -13
  46. package/dist/{index-ymn7em0p.js → index-y0te09pn.js} +3 -3
  47. package/dist/{index-nnrbpcjp.js → index-z0zwjtkn.js} +12 -12
  48. package/dist/{keepalive-asqtcm4y.js → keepalive-xvmwnh1d.js} +4 -4
  49. package/dist/{lead-xbfr7fvt.js → lead-pf8nvxt8.js} +16 -16
  50. package/dist/{maintenance-qma55qvw.js → maintenance-jk4q0wgg.js} +4 -4
  51. package/dist/{oauth-refresh-sweep-3s04c6qw.js → oauth-refresh-sweep-6207x9sj.js} +3 -3
  52. package/dist/{onboard-drskd386.js → onboard-b0csgm0q.js} +2 -2
  53. package/dist/{otel-impl-943c96er.js → otel-impl-b6rfehth.js} +1 -1
  54. package/dist/{pricing-refresh-mx51az05.js → pricing-refresh-4e201wtd.js} +4 -4
  55. package/dist/{rbac-roles-wcmrg6sv.js → rbac-roles-mkc5n5qa.js} +4 -4
  56. package/dist/{rbac-roles-78baecqd.js → rbac-roles-zp6e4geh.js} +3 -3
  57. package/dist/{render-v2-8zjntgf5.js → render-v2-4fazz9tf.js} +3 -3
  58. package/dist/{seed-pricing-jqjs6cws.js → seed-pricing-thxpn9vn.js} +3 -3
  59. package/dist/{setup-4pks0vfk.js → setup-wb0z8cat.js} +2 -2
  60. package/dist/{work-objects-wangt792.js → work-objects-h3498kwe.js} +18 -17
  61. package/dist/{worker-p2mr422x.js → worker-2m2tzxp3.js} +16 -16
  62. package/openapi.json +5 -5
  63. package/package.json +1 -1
  64. package/src/agentmail/inbound-archive.ts +71 -0
  65. package/src/agentmail/types.ts +2 -0
  66. package/src/be/db.ts +1 -1
  67. package/src/be/scripts/typecheck.ts +4 -0
  68. package/src/be/swarm-config-guard.ts +7 -1
  69. package/src/extensions/lifecycle.ts +26 -4
  70. package/src/http/extensions.ts +8 -3
  71. package/src/http/webhooks.ts +13 -5
  72. package/src/rbac/legacy-policy.ts +13 -1
  73. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  74. package/src/scripts-runtime/types/stdlib.d.ts +4 -0
  75. package/src/scripts-runtime/types/swarm-sdk.d.ts +4 -0
  76. package/src/server.ts +12 -0
  77. package/src/slack/render-v2.ts +10 -4
  78. package/src/slack/responses.ts +7 -4
  79. package/src/slack/task-output.ts +21 -0
  80. package/src/slack/watcher.ts +3 -2
  81. package/src/slack/work-objects.ts +2 -1
  82. package/src/tests/agentmail-inbound-archive.test.ts +418 -0
  83. package/src/tests/extensions-example-mcp-install.test.ts +2 -2
  84. package/src/tests/extensions-http.test.ts +84 -3
  85. package/src/tests/extensions-lead-equivalence.test.ts +21 -0
  86. package/src/tests/extensions-mcp-tools.test.ts +111 -3
  87. package/src/tests/fixtures/store-progress-malformed-output.json +4 -0
  88. package/src/tests/rbac-admission-e2e.test.ts +35 -4
  89. package/src/tests/rbac-e2e-helpers.ts +18 -0
  90. package/src/tests/rbac-engine.test.ts +2 -2
  91. package/src/tests/rbac-mcp-admission-e2e.test.ts +22 -4
  92. package/src/tests/reload-config.test.ts +17 -0
  93. package/src/tests/scripts-only-gating.test.ts +4 -0
  94. package/src/tests/slack-render-v2.test.ts +24 -2
  95. package/src/tests/slack-task-output.test.ts +36 -0
  96. package/src/tests/store-progress-attachments-handler.test.ts +100 -1
  97. package/src/tests/swarm-config-reserved-keys.test.ts +6 -1
  98. package/src/tests/tool-annotations.test.ts +1 -1
  99. package/src/tools/extension-activate-version.ts +38 -0
  100. package/src/tools/extension-common.ts +2 -1
  101. package/src/tools/extension-delete.ts +29 -0
  102. package/src/tools/extension-disable.ts +36 -0
  103. package/src/tools/extension-enable.ts +36 -0
  104. package/src/tools/extension-install.ts +2 -2
  105. package/src/tools/store-progress.ts +30 -0
  106. package/src/tools/tool-config.ts +5 -1
  107. package/templates/skills/agent-browser/SKILL.md +84 -0
  108. package/templates/skills/agent-browser/content.md +21 -0
@@ -0,0 +1,36 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { createToolRegistrar, toolOk } from "@/tools/utils";
4
+ import {
5
+ coerceExtensionSummary,
6
+ extensionToolOutputSchema,
7
+ proxyExtensionsApi,
8
+ } from "./extension-common";
9
+
10
+ export const registerExtensionDisableTool = (server: McpServer) => {
11
+ createToolRegistrar(server)(
12
+ "extension-disable",
13
+ {
14
+ title: "Extension Disable",
15
+ description:
16
+ "Unload and disable an installed extension. Requires a lead, operator, or dashboard user.",
17
+ annotations: { openWorldHint: false },
18
+ inputSchema: z
19
+ .object({
20
+ id: z.string().uuid().describe("Installed extension ID from extension-list."),
21
+ })
22
+ .strict(),
23
+ outputSchema: extensionToolOutputSchema,
24
+ },
25
+ async (args, requestInfo) =>
26
+ proxyExtensionsApi({
27
+ method: "POST",
28
+ path: `/api/extensions/${args.id}/disable`,
29
+ requestInfo,
30
+ success: (data) =>
31
+ toolOk("Extension disabled.", {
32
+ data: coerceExtensionSummary((data as { extension?: unknown }).extension),
33
+ }),
34
+ }),
35
+ );
36
+ };
@@ -0,0 +1,36 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { createToolRegistrar, toolOk } from "@/tools/utils";
4
+ import {
5
+ coerceExtensionSummary,
6
+ extensionToolOutputSchema,
7
+ proxyExtensionsApi,
8
+ } from "./extension-common";
9
+
10
+ export const registerExtensionEnableTool = (server: McpServer) => {
11
+ createToolRegistrar(server)(
12
+ "extension-enable",
13
+ {
14
+ title: "Extension Enable",
15
+ description:
16
+ "Load and enable an installed extension. Requires a lead, operator, or dashboard user.",
17
+ annotations: { openWorldHint: false },
18
+ inputSchema: z
19
+ .object({
20
+ id: z.string().uuid().describe("Installed extension ID from extension-list."),
21
+ })
22
+ .strict(),
23
+ outputSchema: extensionToolOutputSchema,
24
+ },
25
+ async (args, requestInfo) =>
26
+ proxyExtensionsApi({
27
+ method: "POST",
28
+ path: `/api/extensions/${args.id}/enable`,
29
+ requestInfo,
30
+ success: (data) =>
31
+ toolOk("Extension enabled.", {
32
+ data: coerceExtensionSummary((data as { extension?: unknown }).extension),
33
+ }),
34
+ }),
35
+ );
36
+ };
@@ -9,7 +9,7 @@ import {
9
9
  } from "./extension-common";
10
10
 
11
11
  export const EXTENSION_INSTALL_DESCRIPTION =
12
- "Validate and store a TypeScript extension bundle (manifest plus a files map with one hooks.ts). Fetch the hook contract first: GET /api/extensions/type-defs returns swarm-extension.d.ts with every event and modify shape. The extension remains disabled until an operator enables it.";
12
+ "Validate and store a TypeScript extension bundle (manifest plus a files map with one hooks.ts). Fetch the hook contract first: GET /api/extensions/type-defs returns swarm-extension.d.ts with every event and modify shape. The extension remains disabled until a lead, operator, or dashboard user enables it with extension-enable.";
13
13
 
14
14
  function extensionInstallResult(data: unknown, fallbackName: string) {
15
15
  const body = data as {
@@ -21,7 +21,7 @@ function extensionInstallResult(data: unknown, fallbackName: string) {
21
21
  const version = typeof extension.version === "number" ? ` v${extension.version}` : "";
22
22
  const deduped = body.contentDeduped === true ? " Content was unchanged." : "";
23
23
  const enableInstruction =
24
- "An operator must enable it from the dashboard or `POST /api/extensions/{id}/enable`.";
24
+ "A lead, operator, or dashboard user can enable it with `extension-enable` or `POST /api/extensions/{id}/enable`.";
25
25
 
26
26
  return toolOk(`Extension \`${name}\`${version} installed.${deduped} ${enableInstruction}`, {
27
27
  data: {
@@ -167,6 +167,36 @@ export const registerStoreProgressTool = (server: McpServer) => {
167
167
  ? requestedStatus
168
168
  : undefined;
169
169
 
170
+ // Stock Claude occasionally puts the next tool parameter inside output.
171
+ // Strip the leaked tail before validation/persistence; recovered pointers
172
+ // must still pass the ordinary attachment schema and verification below.
173
+ const leakedParameter = output?.match(
174
+ /(?:<\/(?:output|parameter)>\s*)?<parameter name="([a-zA-Z_]+)">\s*([\s\S]*)$/,
175
+ );
176
+ if (leakedParameter) {
177
+ output = output?.slice(0, leakedParameter.index);
178
+ let recovered = false;
179
+ if (leakedParameter[1] === "attachments") {
180
+ try {
181
+ const parsed = z
182
+ .array(AttachmentInputSchema)
183
+ .max(20)
184
+ .safeParse(JSON.parse(leakedParameter[2]!.replace(/\s*<\/parameter>\s*$/, "")));
185
+ if (parsed.success && (attachments?.length ?? 0) + parsed.data.length <= 20) {
186
+ attachments = [...(attachments ?? []), ...parsed.data];
187
+ recovered = true;
188
+ }
189
+ } catch {
190
+ // Malformed JSON must not prevent saving the cleaned task result.
191
+ }
192
+ }
193
+ if (!recovered) {
194
+ console.warn(
195
+ `[store-progress] Stripped malformed output tail for task ${taskId}; parameter ${leakedParameter[1]} was not recovered.`,
196
+ );
197
+ }
198
+ }
199
+
170
200
  // Verify agent-fs pointers before opening the write transaction. The
171
201
  // registering agent's resolved config selects both credentials and the
172
202
  // exact org/drive; never let the provider fall back to a personal drive.
@@ -197,13 +197,17 @@ export const DEFERRED_TOOLS = new Set([
197
197
  "room-reset",
198
198
  "room-decode",
199
199
 
200
- // Reusable scripts and extensions (8)
200
+ // Reusable scripts and extensions (12)
201
201
  "script-upsert",
202
202
  "script-delete",
203
203
  "script-apis",
204
204
  "launch-script-run",
205
205
  "get-script-run",
206
206
  "list-script-runs",
207
+ "extension-delete",
208
+ "extension-enable",
209
+ "extension-disable",
210
+ "extension-activate-version",
207
211
  "extension-install",
208
212
  "extension-list",
209
213
 
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: agent-browser
3
+ description: "Drive a real browser with the agent-browser CLI (headless Chromium over CDP). Use for any browser task: verifying a UI change, taking a screenshot, walking a web page, filling a form, extracting page text, reproducing a frontend bug, or attaching UI evidence to a PR, issue, Linear comment, or Slack thread. Starts with a `command -v agent-browser` check and falls back to manual QA on the slim worker image. Screenshots are shared through agent-fs signed URLs. This is the only browser automation tool; do not use qa-use."
4
+ ---
5
+
6
+ # agent-browser
7
+
8
+ `agent-browser` (vercel-labs) is the swarm's browser-automation CLI. It drives a headless Chromium over CDP and returns accessibility-tree snapshots with compact `@eN` refs. Use it for any browser task: verify a UI change, take a screenshot, walk a page, fill a form, extract page text, reproduce a frontend bug, or attach UI evidence to a PR, issue, Linear comment, or Slack thread.
9
+
10
+ Do not use `qa-use`. It is not installed in the worker image any more.
11
+
12
+ ## Step 0: check that the CLI is present
13
+
14
+ The full worker image (`agent-swarm-worker:latest`) ships `agent-browser` and a headless Chromium. The slim image (`:slim`) has neither. The first command of any browser work is:
15
+
16
+ ```bash
17
+ command -v agent-browser
18
+ ```
19
+
20
+ If it prints nothing, you are on the slim image. Say so in your progress report ("agent-browser is not available on this worker image") and fall back to manual QA: write the exact steps a human should run and mark the browser test cases as **Blocked**, not failed. Do not install a browser or the CLI yourself.
21
+
22
+ ## Step 1: load the version-matched guide
23
+
24
+ The CLI bundles its own usage guide. Load it once per task before the first browser command. Do not guess flags from memory.
25
+
26
+ ```bash
27
+ agent-browser skills get core # overview + common patterns
28
+ agent-browser skills get core --full # adds the full command reference
29
+ agent-browser skills list # specialized guides (dogfood, electron, slack, ...)
30
+ ```
31
+
32
+ ## Step 2: the core loop
33
+
34
+ ```bash
35
+ agent-browser open <url>
36
+ agent-browser snapshot -i # accessibility tree, interactive elements only, @eN refs
37
+ agent-browser click @e12 # act on refs from the snapshot
38
+ agent-browser fill @e7 "text"
39
+ agent-browser screenshot /tmp/<name>.png
40
+ agent-browser close
41
+ ```
42
+
43
+ - Re-run `snapshot` after every navigation or state change. Refs are only valid for the snapshot that produced them.
44
+ - Run one browser session at a time. Call `store-progress` between page batches so the heartbeat watchdog sees you.
45
+ - The browser is headless. Local servers inside the worker container are reachable at `http://localhost:<port>`.
46
+ - The image launches Chromium with `--no-sandbox,--disable-dev-shm-usage` through `AGENT_BROWSER_ARGS`. If you pass your own `--args`, include `--no-sandbox` again or Chrome will not start.
47
+
48
+ ## Recording
49
+
50
+ Default: record with `--cursor`, drive every supported pointer action (`click`, `mouse move`, `drag`) with `--human`, and ship the **1.5x video**. Keep the 1x original only if a reviewer asks for it. Human pointer movement follows an eased curve; use `--seed <n>` on `mouse move` to make the take reproducible instead of using a bare move.
51
+
52
+ Open the page first, then record the interaction:
53
+
54
+ ```bash
55
+ agent-browser record start ./in.mp4 --cursor
56
+ agent-browser snapshot -i
57
+ agent-browser mouse move 600 400 --human --seed 42 --duration 1400
58
+ agent-browser click @e3 --human # use a ref from the current snapshot
59
+ # Perform the remaining QA steps; use --human for drag actions too.
60
+ agent-browser record stop
61
+ ffmpeg -y -i in.mp4 -filter:v "setpts=PTS/1.5,fps=30" -an \
62
+ -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p out-1.5x.mp4
63
+ ```
64
+
65
+ Speed-up is a post-process step: `record start` has no speed or human flag. Keep `fps=30` to preserve a steady 30 fps output rather than uneven frame drops at an inferred rate. `-an` disables audio because agent-browser recordings carry no audio track. The worker ships only `ffmpeg`, not `ffprobe`; inspect duration with `ffmpeg -i out-1.5x.mp4 2>&1 | grep Duration`.
66
+
67
+ Add `--contact-sheet` to the start command for a timestamped PNG summary of visual changes. Recording needs `ffmpeg` on PATH; check `agent-browser doctor` before starting. Stop the recording before closing the browser so the video is saved. Upload the 1.5x file through the same agent-fs QA path and signed-URL steps as screenshots below, and link it in the PR body.
68
+
69
+ ## Step 3: share the screenshot through agent-fs
70
+
71
+ Screenshots on the worker disk disappear with the task. Upload them to agent-fs under the qa path convention and share the signed URL. `--file` is binary-safe. `--content` is text-only and mangles PNGs.
72
+
73
+ ```bash
74
+ agent-fs write thoughts/<agent-id>/qa/<topic>-screenshots/<name>.png \
75
+ --file /tmp/<name>.png -m "<what it shows>"
76
+ agent-fs stat thoughts/<agent-id>/qa/<topic>-screenshots/<name>.png --json # confirm size > 0
77
+ agent-fs signed-url thoughts/<agent-id>/qa/<topic>-screenshots/<name>.png --json # 24h default, --expires-in up to 7d
78
+ ```
79
+
80
+ - Embed the URL as `![caption](<url>)` in the PR body, review comment, Linear comment, or Slack message.
81
+ - In `store-progress`, list the upload in the `attachments` field with `kind: "agent-fs"` and the path, and paste the signed URL in the progress text.
82
+ - If `agent-fs auth whoami` fails, report the local path, say the upload was skipped, and continue.
83
+
84
+ The `artifacts` skill holds the full agent-fs recipe and the naming conventions.
@@ -40,6 +40,27 @@ agent-browser close
40
40
  - The browser is headless. Local servers inside the worker container are reachable at `http://localhost:<port>`.
41
41
  - The image launches Chromium with `--no-sandbox,--disable-dev-shm-usage` through `AGENT_BROWSER_ARGS`. If you pass your own `--args`, include `--no-sandbox` again or Chrome will not start.
42
42
 
43
+ ## Recording
44
+
45
+ Default: record with `--cursor`, drive every supported pointer action (`click`, `mouse move`, `drag`) with `--human`, and ship the **1.5x video**. Keep the 1x original only if a reviewer asks for it. Human pointer movement follows an eased curve; use `--seed <n>` on `mouse move` to make the take reproducible instead of using a bare move.
46
+
47
+ Open the page first, then record the interaction:
48
+
49
+ ```bash
50
+ agent-browser record start ./in.mp4 --cursor
51
+ agent-browser snapshot -i
52
+ agent-browser mouse move 600 400 --human --seed 42 --duration 1400
53
+ agent-browser click @e3 --human # use a ref from the current snapshot
54
+ # Perform the remaining QA steps; use --human for drag actions too.
55
+ agent-browser record stop
56
+ ffmpeg -y -i in.mp4 -filter:v "setpts=PTS/1.5,fps=30" -an \
57
+ -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p out-1.5x.mp4
58
+ ```
59
+
60
+ Speed-up is a post-process step: `record start` has no speed or human flag. Keep `fps=30` to preserve a steady 30 fps output rather than uneven frame drops at an inferred rate. `-an` disables audio because agent-browser recordings carry no audio track. The worker ships only `ffmpeg`, not `ffprobe`; inspect duration with `ffmpeg -i out-1.5x.mp4 2>&1 | grep Duration`.
61
+
62
+ Add `--contact-sheet` to the start command for a timestamped PNG summary of visual changes. Recording needs `ffmpeg` on PATH; check `agent-browser doctor` before starting. Stop the recording before closing the browser so the video is saved. Upload the 1.5x file through the same agent-fs QA path and signed-URL steps as screenshots below, and link it in the PR body.
63
+
43
64
  ## Step 3: share the screenshot through agent-fs
44
65
 
45
66
  Screenshots on the worker disk disappear with the task. Upload them to agent-fs under the qa path convention and share the signed URL. `--file` is binary-safe. `--content` is text-only and mangles PNGs.