@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.
- package/README.md +3 -1
- package/dist/{acp-adapter-ex8hzjfc.js → acp-adapter-4s7q25ft.js} +1 -1
- package/dist/{actions-2bgpyhww.js → actions-cs6rhmvc.js} +13 -13
- package/dist/{app-1706zhr1.js → app-j54aq0dj.js} +3 -3
- package/dist/{assistant-bavke5p7.js → assistant-w1pgn7vf.js} +14 -14
- package/dist/{boot-reembed-3cmrrmpa.js → boot-reembed-42vgkrrj.js} +4 -4
- package/dist/{boot-reembed-nafcmmmc.js → boot-reembed-smy30z32.js} +3 -3
- package/dist/{boot-scrub-logs-cd6a2sq0.js → boot-scrub-logs-b6s72fsw.js} +2 -2
- package/dist/{claude-adapter-wfp2gzvz.js → claude-adapter-fh7rmcj9.js} +3 -3
- package/dist/{claude-sdk-session-bht9jjak.js → claude-sdk-session-xhynatn9.js} +3 -3
- package/dist/{cli-f1wky6zp.js → cli-3g61md6v.js} +1 -1
- package/dist/{cli-a1z3h107.js → cli-3pft2z6r.js} +4 -4
- package/dist/{cli-5tpgq60f.js → cli-72tgtzr1.js} +152 -23
- package/dist/{cli-0qb1z6s2.js → cli-7bwaerjt.js} +1 -1
- package/dist/{cli-czjtgfys.js → cli-b4dyetqt.js} +2 -2
- package/dist/{cli-s14agm5s.js → cli-bdt7w021.js} +12 -3
- package/dist/{cli-sjaae6ff.js → cli-dsyg66vt.js} +3 -3
- package/dist/{cli-2tbvf1z1.js → cli-ftdjz1mk.js} +8 -8
- package/dist/{cli-zkxymas5.js → cli-gfez0qmk.js} +1 -1
- package/dist/{cli-q88st9aj.js → cli-gxf2ebyz.js} +2 -2
- package/dist/{cli-ppm4975n.js → cli-jtvs5m8q.js} +1 -1
- package/dist/{cli-7tgcstn2.js → cli-jytf5mw9.js} +2 -2
- package/dist/{cli-g69fdc9g.js → cli-kf8qp2vf.js} +2 -2
- package/dist/{cli-5jd2z7av.js → cli-ks8a1cgq.js} +6 -2
- package/dist/{cli-gjyvhmmh.js → cli-ng4q6a9t.js} +5 -5
- package/dist/{cli-3v9xhjcn.js → cli-sakxd2rg.js} +2 -2
- package/dist/{cli-frcta2w4.js → cli-sqmh7jj1.js} +1 -1
- package/dist/{cli-vwm416m6.js → cli-v9sk4dzw.js} +1 -1
- package/dist/{cli-j9xczhaq.js → cli-vbk025fm.js} +1 -1
- package/dist/{cli-r2q6pevn.js → cli-wfve107r.js} +43 -21
- package/dist/{cli-9004vfdf.js → cli-y8tayczk.js} +2 -2
- package/dist/{cli-gbfwcd3k.js → cli-zfzznn7c.js} +7 -6
- package/dist/cli.js +11 -11
- package/dist/{codex-adapter-jjjk8w4m.js → codex-adapter-k6yq95sn.js} +2 -2
- package/dist/{codex-session-runner-nd6ayxkx.js → codex-session-runner-3rp84pk8.js} +2 -2
- package/dist/{commands-9v4psyq1.js → commands-pbr5jzy5.js} +2 -2
- package/dist/{db-wnt58tpe.js → db-z0xaa59a.js} +2 -2
- package/dist/{dispatcher-05jh1aqk.js → dispatcher-pwpgsj0x.js} +12 -12
- package/dist/{handlers-tw90pkh5.js → handlers-5mzk4mbg.js} +15 -15
- package/dist/{hook-sxe3r349.js → hook-8bz33zee.js} +1 -1
- package/dist/{hook-123e2scb.js → hook-kbh84zb1.js} +2 -2
- package/dist/{http-ecsar8ez.js → http-pppxg5vr.js} +100 -36
- package/dist/{index-hw26vj40.js → index-68bd9b5f.js} +12 -12
- package/dist/{index-16js521z.js → index-k1hq1qt6.js} +8 -8
- package/dist/{index-zj518gf9.js → index-mjpxsr6k.js} +13 -13
- package/dist/{index-ymn7em0p.js → index-y0te09pn.js} +3 -3
- package/dist/{index-nnrbpcjp.js → index-z0zwjtkn.js} +12 -12
- package/dist/{keepalive-asqtcm4y.js → keepalive-xvmwnh1d.js} +4 -4
- package/dist/{lead-xbfr7fvt.js → lead-pf8nvxt8.js} +16 -16
- package/dist/{maintenance-qma55qvw.js → maintenance-jk4q0wgg.js} +4 -4
- package/dist/{oauth-refresh-sweep-3s04c6qw.js → oauth-refresh-sweep-6207x9sj.js} +3 -3
- package/dist/{onboard-drskd386.js → onboard-b0csgm0q.js} +2 -2
- package/dist/{otel-impl-943c96er.js → otel-impl-b6rfehth.js} +1 -1
- package/dist/{pricing-refresh-mx51az05.js → pricing-refresh-4e201wtd.js} +4 -4
- package/dist/{rbac-roles-wcmrg6sv.js → rbac-roles-mkc5n5qa.js} +4 -4
- package/dist/{rbac-roles-78baecqd.js → rbac-roles-zp6e4geh.js} +3 -3
- package/dist/{render-v2-8zjntgf5.js → render-v2-4fazz9tf.js} +3 -3
- package/dist/{seed-pricing-jqjs6cws.js → seed-pricing-thxpn9vn.js} +3 -3
- package/dist/{setup-4pks0vfk.js → setup-wb0z8cat.js} +2 -2
- package/dist/{work-objects-wangt792.js → work-objects-h3498kwe.js} +18 -17
- package/dist/{worker-p2mr422x.js → worker-2m2tzxp3.js} +16 -16
- package/openapi.json +5 -5
- package/package.json +1 -1
- package/src/agentmail/inbound-archive.ts +71 -0
- package/src/agentmail/types.ts +2 -0
- package/src/be/db.ts +1 -1
- package/src/be/scripts/typecheck.ts +4 -0
- package/src/be/swarm-config-guard.ts +7 -1
- package/src/extensions/lifecycle.ts +26 -4
- package/src/http/extensions.ts +8 -3
- package/src/http/webhooks.ts +13 -5
- package/src/rbac/legacy-policy.ts +13 -1
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/types/stdlib.d.ts +4 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +4 -0
- package/src/server.ts +12 -0
- package/src/slack/render-v2.ts +10 -4
- package/src/slack/responses.ts +7 -4
- package/src/slack/task-output.ts +21 -0
- package/src/slack/watcher.ts +3 -2
- package/src/slack/work-objects.ts +2 -1
- package/src/tests/agentmail-inbound-archive.test.ts +418 -0
- package/src/tests/extensions-example-mcp-install.test.ts +2 -2
- package/src/tests/extensions-http.test.ts +84 -3
- package/src/tests/extensions-lead-equivalence.test.ts +21 -0
- package/src/tests/extensions-mcp-tools.test.ts +111 -3
- package/src/tests/fixtures/store-progress-malformed-output.json +4 -0
- package/src/tests/rbac-admission-e2e.test.ts +35 -4
- package/src/tests/rbac-e2e-helpers.ts +18 -0
- package/src/tests/rbac-engine.test.ts +2 -2
- package/src/tests/rbac-mcp-admission-e2e.test.ts +22 -4
- package/src/tests/reload-config.test.ts +17 -0
- package/src/tests/scripts-only-gating.test.ts +4 -0
- package/src/tests/slack-render-v2.test.ts +24 -2
- package/src/tests/slack-task-output.test.ts +36 -0
- package/src/tests/store-progress-attachments-handler.test.ts +100 -1
- package/src/tests/swarm-config-reserved-keys.test.ts +6 -1
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tools/extension-activate-version.ts +38 -0
- package/src/tools/extension-common.ts +2 -1
- package/src/tools/extension-delete.ts +29 -0
- package/src/tools/extension-disable.ts +36 -0
- package/src/tools/extension-enable.ts +36 -0
- package/src/tools/extension-install.ts +2 -2
- package/src/tools/store-progress.ts +30 -0
- package/src/tools/tool-config.ts +5 -1
- package/templates/skills/agent-browser/SKILL.md +84 -0
- 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
|
|
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
|
-
"
|
|
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.
|
package/src/tools/tool-config.ts
CHANGED
|
@@ -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 (
|
|
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 `` 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.
|