@capagents/forgeqe 2.2.2 → 2.2.3

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.
@@ -66,6 +66,8 @@ Copied from test-agent-nexus and trimmed to what a CLI run needs:
66
66
 
67
67
  **Finishing and the judge.** `_GOAL_VERIFICATION_SYSTEM_MESSAGE` is added to every agent's system prompt: `done` with success only when every field holds exactly what the goal asked for (pre-filled or extra values in multi-value fields removed), every setting the goal names is applied through the page's own controls and shown by the page, and findings are read from the page, items a list already shows are read from it in one step, and a tab that looks blank right after opening is still loading. The engine library's judge (`use_judge`) is off by default: `browser.judge: true` (passed to the engine as its judge setting) or `FORGEQE_ENGINE_USE_JUDGE=1` turns it on, and it then makes one model call over the steps and screenshots. It was turned off because it failed completed runs whose answer came from one screenshot (five result scores) instead of one step per item, and ForgeQE's agent then retried with ever stricter goals. When the agent says success and the judge's verdict is false, the run fails with the judge's `failure_reason` ahead of the agent's report; the verdict goes into `run.json` as `judge`. A judge call that errors leaves the agent's result as it was.
68
68
 
69
+ **Window and video.** `window_launch_kwargs` sizes a local browser from `browser.viewport`: the page when headless, the window with `browser.window: viewport`. Otherwise a visible window opens screen-sized (the engine library's default) and, before the first page, `maximize_window` sets `windowState: maximized` over CDP. With `browser.video` on (the default), the browser is started before the replay or the agent, and `start_video` starts the library's recording watchdog on `<run>/video.mp4`; `stop_video` finishes it before the browser closes, and `run_with_events` adds it to the run's files. The watchdog encodes Chrome's screencast frames at 30 fps as they arrive, so the video's timing follows how often the page repaints, not the clock. The encoder (`browser-use[video]`: imageio with ffmpeg, numpy) is installed with the engine, and added to an engine set up before it. The report lists `video.mp4` among the run's files and plays it on the Artifacts tab.
70
+
69
71
  **Browser profile.** At the end of the first step (or before writing a trust-mode replay), `browser_profile` reads how the browser was set up: the engine's driver, channel and executable, headless, the default switches removed, extra HTTP headers (none), `Browser.getVersion`, and from the page `navigator.userAgent`, `navigator.webdriver`, language, time zone, and viewport. It goes into `run.json` as `browser`, and the handoff shows it so the coding agent can compare its test's browser with it.
70
72
 
71
73
  **Timeline.** `build_timeline` writes `steps.json` from the history (and the replayed steps): URL, title, the model's evaluation, memory and next goal, each action with its error, start time (`t0`) and duration, the part of it spent waiting for the model (`model_ms`, from wrapping `llm.ainvoke` and overlapping the calls with the step's `step_start_time`/`step_end_time`), and a JPEG of the page (max 1280 wide) under `steps/`, subject to `report.screenshots`. Each action on an element carries `box`: the element's `absolute_position` from the DOM snapshot the model saw, minus the page's scroll, as fractions of the viewport, so the report can outline it on that step's screenshot. Timeouts, stops, and crashes still write it from the partial history.
@@ -401,10 +403,12 @@ Before the model is called, `replay_known` asks `AppMemory.plan` for the goal's
401
403
  It checks mounts as uploads are checked (inside the project; nothing hidden, key-named or linked). It waits for a TCP or HTTP ready check. Docker's port proxy accepts connections before the service listens, so a TCP connection counts only when it stays open. It scrubs secrets from logs and errors, and tracks the containers it started, which the CLI removes on exit.
402
404
  - **`mock.ts`** makes WireMock stubs from `network.json` or a contract set.
403
405
  - **`compose.ts`** translates docker-compose services into forgeqe.yaml services, refusing settings that widen what a container reaches. `startOrder` (`config.ts`) orders services by `depends_on`. Every container joins the project network `forgeqe-<key>` under its service name.
404
- - **`tests.ts`** is a `runTestCommand` for `run_tests` with `containers.tests`. It runs the confirmed command with `sh -c` in the image, with the repository mounted at its own path, `--user` this machine's user, and only the environment's FORGEQE_* variables, the saved-login variables, and the listed env and secrets, by name.
406
+ - **`tests.ts`** is a `runTestCommand` for `run_tests` with `containers.tests`. It runs the confirmed command with `sh -c` in the image, with the repository mounted at its own path, `--user` this machine's user, and only the environment's FORGEQE_* variables, the saved-login variables, and the listed env and secrets, by name. The repository's credential files are covered (`masks.ts`), and `docker top` finds the browsers the test launched.
407
+ - **`workspace.ts`** is `containers.workspace`. `loadConfig` registers it for the repository (`active.ts`). Nothing starts until `openSession` or `runTestCommand` asks for it. Then one container per process and project waits (`sleep infinity`) with the repository, the home volume `forgeqe-<key>-home` and the project network. `openSession` sets the agent runtime's `SHELL` to a script that runs `exec --workdir "$PWD" … sh -c "$2"` in it, refuses a folder outside the repository, and adds `workspaceNote` to the agents' instructions. ForgeQE's own agent passes `hostShell`. `runTestCommand` sends a run in a covered folder to the workspace's runner (`tests.ts`, same image and home volume) unless `host` or `FORGEQE_HOST_RUN=1`.
408
+ - **`browser.ts`** is `browser.provider: docker`. `startDockerBrowser` runs Microsoft's Playwright image with `BROWSER_SERVER` (Node.js, no packages) mounted read-only. It starts Chromium on 127.0.0.1:9222 inside, headless or on Xvfb, and a gateway on 9223 that pipes a connection only when the path starts with the random token (constant-time compare) and any `Origin` is local. The token goes to the runtime by name. ForgeQE waits for `/<token>/json/version` and returns `ws://127.0.0.1:<port>/<token>/devtools/browser/<id>`. `run.ts` starts one per browser run and gives the engine `FORGEQE_BROWSER_CDP_URL` (`docker_browser_launch_kwargs`). The coding loop starts one per job when the provider is docker or `run.json` says the run used one: the replay connects with `connectOverCDP` (`FORGEQE_REPLAY_CDP`, one pass), and the live browser's MCP server reads `PLAYWRIGHT_MCP_CDP_ENDPOINT` from `{env:FORGEQE_LIVE_CDP_URL}`.
405
409
  - **The agent's tools** are `services` and `mock_service` in `src/mcp/containertools.ts`, and the CLI is `forgeqe services` (`command.ts`).
406
410
  - **The agents' shell:** `BASH_PERMISSION` denies docker, podman, nerdctl and compose commands for the agent and Scribe.
407
- - **Tests:** `tests/containers.test.ts` uses a fake runtime. `tests/containers.live.test.ts` uses real Docker and is skipped without it.
411
+ - **Tests:** `tests/containers.test.ts` and `tests/workspace.test.ts` use a fake runtime; `workspace.test.ts` also runs the gateway against a stand-in browser and the agent runtime against the workspace's script. `tests/containers.live.test.ts` uses real Docker and is skipped without it.
408
412
 
409
413
  ## Databases, test data and data flows (`src/data`, `py/forgeqe_data`)
410
414
 
@@ -10,7 +10,7 @@ real Docker (and is skipped without it):
10
10
 
11
11
  `.github/workflows/forgeqe-containers.yml` runs both on GitHub's Docker. There, a skipped live test fails the job.
12
12
 
13
- ForgeQE uses Docker or Podman, when one is installed, for three jobs:
13
+ ForgeQE uses Docker or Podman, when one is installed, for five jobs:
14
14
 
15
15
  - **Services a test run needs:** a database, a queue, a cache, or the API under test. The project declares them in
16
16
  forgeqe.yaml, or in a docker-compose file that forgeqe.yaml names.
@@ -18,6 +18,10 @@ ForgeQE uses Docker or Podman, when one is installed, for three jobs:
18
18
  the tests can run without the real service.
19
19
  - **The repository's tests in a container:** `run_tests` runs the command the user confirmed in an image, instead of
20
20
  on this machine, after starting the services the tests need.
21
+ - **A workspace for the coding agents:** with `containers.workspace`, Scribe's shell commands (build, restore, run,
22
+ git) and every test ForgeQE runs for it go to a container of the project's image. The agents stay on this machine.
23
+ - **A browser in a container:** with `browser.provider: docker`, the browser agent, the replay and Scribe's live
24
+ browser drive Chromium in a container of Microsoft's Playwright image.
21
25
 
22
26
  Scribe also has a `testcontainers` skill, for tests that start their own services from code (below).
23
27
 
@@ -146,6 +150,71 @@ Its confirmation says where the tests will run. The container:
146
150
 
147
151
  This needs a Linux or macOS machine, because the repository is mounted at its own path.
148
152
 
153
+ ## The workspace
154
+
155
+ ```yaml
156
+ containers:
157
+ workspace:
158
+ image: mcr.microsoft.com/playwright/dotnet:v1.63.0-noble # an exact version; the project's toolchain
159
+ services: [db] # started first; reached by name
160
+ env: { NEXUS_ENVIRONMENT: CI, DOTNET_ROLL_FORWARD: Major }
161
+ secrets: [TEST_DB_PASSWORD] # by name; values from the environment or .env
162
+ ```
163
+
164
+ With `containers.workspace` set:
165
+
166
+ - **Scribe's shell runs in the container.** The agent runtime runs each command as `$SHELL -c <command>`. ForgeQE
167
+ sets `SHELL` to a script that runs the same command with `docker exec … sh -c <command>`, in the same folder,
168
+ in the workspace container. A folder outside the repository is refused, since the container has only the
169
+ repository. The agent is told its commands run in a Linux container of that image.
170
+ - **Every test ForgeQE runs goes to the same image:** the coding loop's runs, `heal`, `suite`'s existing tests and
171
+ `run_tests`. Each run gets its own container (`docker run --rm`), so stopping a run removes it. Browsers the test
172
+ starts are found with `docker top`, as ForgeQE finds them on this machine.
173
+ - **What stays on this machine:** the agent runtime, the model key, ForgeQE's other secrets, the file tools (read,
174
+ edit, write; the repository is the same folder in both places), and ForgeQE's own agent, which works with you
175
+ on this machine and keeps its shell here. `FORGEQE_HOST_RUN=1` runs everything on this machine for a session.
176
+ - **The container:**
177
+ - one per ForgeQE process and project, `forgeqe-<project>-workspace-<id>`, removed when ForgeQE ends;
178
+ - the repository mounted read-write at its own path; credential files in it (`.env`, `.env.*` except templates,
179
+ `.npmrc`, `.netrc`, `*.pem`, `*.key`, `id_*` keys) covered with an empty read-only file, and `.ssh`, `.aws`,
180
+ `.azure`, `.docker`, `.kube` and `.forgeqe/sessions` with an empty folder;
181
+ - `HOME` on the project's volume `forgeqe-<project>-home`, so NuGet, npm and pip caches outlive the session. Docker
182
+ creates the volume for root, so ForgeQE runs `chown` in it once, with a fixed command;
183
+ - this machine's user (`--user uid:gid`), `no-new-privileges`, `--pids-limit 4096`, the project network;
184
+ - only `CI=1`, `HOME`, the `env` and `secrets` listed, and, in each command, the environment's `FORGEQE_APP_*` and
185
+ `FORGEQE_VAR_*` and the saved logins' variables, by name.
186
+ - **`containers.tests`** still names the image `run_tests` uses when it is set. Without it, `run_tests` uses the
187
+ workspace's image.
188
+
189
+ This needs a Linux or macOS machine, as tests in a container do.
190
+
191
+ ## The browser in a container
192
+
193
+ ```yaml
194
+ browser:
195
+ provider: docker # or per run: --browser-provider docker
196
+ containers:
197
+ browser: # optional
198
+ image: mcr.microsoft.com/playwright:v1.63.0-noble # default; needs Node.js and /ms-playwright
199
+ headless: false # default: browser.headless; a visible browser runs on a virtual display (Xvfb)
200
+ ```
201
+
202
+ - **One container per use:** each browser run (a suite runs `suite.parallel` at once) gets its own, and a coding job
203
+ gets one that its replay and live browser share. Each is removed when its run or job ends.
204
+ - **Connection:** Chromium listens on 127.0.0.1 inside the container. CDP has no authentication of its own, so a small
205
+ gateway in front of it (`BROWSER_SERVER` in `src/containers/browser.ts`, run with the image's Node.js) accepts a
206
+ connection only with a random 48-character token in its path, compared in constant time, and refuses a browser
207
+ `Origin` that is not local. The port is published on 127.0.0.1 only. The token reaches the engine, the replay and
208
+ the live browser's MCP server through the environment, never a file or a command line.
209
+ - **Visible or headless:** a visible browser runs on Xvfb inside the container. Sites that turn headless browsers
210
+ away see an ordinary one, but you do not see it on your screen. Runs keep screenshots and traces as usual.
211
+ - **Reaching the app:** the container joins the project network, so a declared service is reached by its name
212
+ (`http://order-api:8080`). An app this machine serves is `http://host.docker.internal:<port>`; `localhost` is the
213
+ container itself, and ForgeQE warns when a run starts there.
214
+ - **Replay:** one pass, in a new browser context. The second pass (without Playwright's automation switches) needs a
215
+ launch, which a running browser does not have.
216
+ - A run explored in a container is replayed in one even when the provider is changed back later (`run.json`).
217
+
149
218
  ## Testcontainers (Scribe)
150
219
 
151
220
  When a test needs a real service, or the repository already uses Testcontainers (Java, .NET, Node, Python, Go), Scribe
@@ -182,7 +251,8 @@ changed.
182
251
 
183
252
  ## Lifetime
184
253
 
185
- - **Services and mocks the agent starts** are removed when the shell, `forgeqe run`, or `forgeqe mcp` ends.
254
+ - **Services and mocks the agent starts** are removed when the shell, `forgeqe run`, or `forgeqe mcp` ends, as are
255
+ the workspace and browser containers (`forgeqe.kind` `workspace` and `browser`). The home volume stays.
186
256
  - **`forgeqe services up`** keeps its services running after the command ends, until `forgeqe services down`.
187
257
  - **Container names** are `forgeqe-<project>-<service>`. `<project>` is a hash of the project folder, so two projects
188
258
  never share a container. Starting a service replaces its earlier container. Removing a container also removes the anonymous volumes its image declares (a database's data folder), so nothing is left on the machine. A container is never reused with old
@@ -219,9 +289,15 @@ text could mount the whole disk or run privileged, so:
219
289
  | `src/containers/run.ts` | The runtime (docker or podman), `run` arguments, mount and secret checks, start and wait, status, logs, stop, the session's containers |
220
290
  | `src/containers/mock.ts` | WireMock stubs from run traffic and from contract sets; values from JSON schemas |
221
291
  | `src/containers/compose.ts` | docker-compose files as services: what is translated, ignored, left out or refused |
222
- | `src/containers/tests.ts` | `run_tests` in a container: arguments, environment, mounts, output |
292
+ | `src/containers/tests.ts` | `run_tests` in a container: arguments, environment, mounts, output, browsers from `docker top` |
293
+ | `src/containers/workspace.ts` | `containers.workspace`: the container, its home volume, the agent's shell script, its test runner |
294
+ | `src/containers/active.ts` | The registered workspaces, by repository: `runTestCommand` and `openSession` look them up |
295
+ | `src/containers/masks.ts` | The repository's credential files and folders, covered in every container that mounts it |
296
+ | `src/containers/browser.ts` | `browser.provider: docker`: the browser container, its token gateway, the endpoint |
223
297
  | `src/containers/command.ts` | `forgeqe services` |
224
298
  | `skills/testcontainers/SKILL.md` | Scribe's Testcontainers skill |
225
299
  | `src/mcp/containertools.ts` | The agent's `services` and `mock_service` |
226
300
 
227
- Tests: `bun test tests/containers.test.ts`, which uses a fake runtime that records each command. `tests/containers.live.test.ts` uses real Docker.
301
+ Tests: `bun test tests/containers.test.ts tests/workspace.test.ts`, which use a fake runtime that records each command
302
+ (`workspace.test.ts` also runs the gateway against a stand-in browser, and the agent runtime against the workspace's
303
+ script). `tests/containers.live.test.ts` uses real Docker.