Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,569 changes: 112 additions & 1,457 deletions README.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ unless this index says otherwise.

## Current Contracts

- [CLI reference](./cli-reference.md) explains every `wp-codebox` command,
its arguments, run lifecycle, and previews.
- [Artifact bundles](./artifact-bundles.md) describes the bundle layout,
verification, and runtime evidence attestation.
- [Runtime episodes](./runtime-episodes.md) defines the episode record that
replays a run.
- [WordPress plugin](./wordpress-plugin.md) explains the parent-site control
plane, browser site operations, and the browser runner.
- [Host tool registry](./host-tool-registry.md) and
[in-sandbox workspace contract](./sandbox-workspace-contract.md) define the
agent tool surfaces.
- [Distribution](./distribution.md) covers release assets, installation, and
the release pipeline.
- [Architecture](./architecture.md) explains the product boundary, package map,
and ownership rules.
- [Recipe contract](./recipe-contract.md) is the authoring guide for
Expand Down
2 changes: 1 addition & 1 deletion docs/agent-runtime-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ wp-codebox agent-task-run --input-file=/path/to/request.json --json \

The command reads one JSON request from `--input-file`, writes a single JSON envelope to stdout, and exits non-zero when the normalized result is not successful. Orchestrators should treat stdout JSON as the contract and stderr as diagnostic text only.

Direct `wp-codebox agent-sandbox-run` remains an operator/debug command. Product orchestrators should call `agent-task-run` or the parent-site `wp-codebox/run-agent-task` ability so WP Codebox can build the private recipe, capture artifacts, normalize no-op/failure evidence, and clean up temporary recipe files.
`wp-codebox.agent-sandbox-run` is a private recipe step, not a CLI command. Product orchestrators call `agent-task-run` or the parent-site `wp-codebox/run-agent-task` ability so WP Codebox can build the private recipe, capture artifacts, normalize no-op/failure evidence, and clean up temporary recipe files.

Named provider stacks, workspace managers, and sandbox backends are implementation
details behind the Codebox-owned CLI command, ability ids, and `wp-codebox/*`
Expand Down
225 changes: 225 additions & 0 deletions docs/artifact-bundles.md

Large diffs are not rendered by default.

414 changes: 414 additions & 0 deletions docs/cli-reference.md

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions docs/distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Distribution

## Release Assets

Every GitHub Release attaches three artifacts built from the same commit:

| Asset | What it is |
| --- | --- |
| `wp-codebox-cli-<platform>-<arch>.tar.gz` | Self-contained CLI: compiled packages, production dependencies, a pinned Node.js runtime under `vendor/node`, and a `bin/wp-codebox` launcher. Built by `npm run release:package`. |
| `wp-codebox-workspace-<version>.tgz` | The root workspace package. Installing it globally exposes the `wp-codebox` binary from `packages/cli/dist/index.js` on your own Node.js. |
| `wp-codebox.zip` | The WordPress plugin (`packages/wordpress-plugin`), with the vendored CLI runtime. Built by `npm run package:wordpress-plugin`. |

The scoped npm packages (`@automattic/wp-codebox-core`, `-playground`, `-cli`)
are prepared in the workspace but not published to the npm registry. Install
from a release asset or a checkout.

## Installing The CLI

Self-contained tarball, no Node.js required on the host:

```bash
curl -L -o wp-codebox.tar.gz https://github.com/Automattic/wp-codebox/releases/latest/download/wp-codebox-cli-linux-x64.tar.gz
tar -xzf wp-codebox.tar.gz
./wp-codebox-cli/bin/wp-codebox commands --json
```

The launcher prefers the bundled `vendor/node/bin/node`, then `node` or
`nodejs` on `PATH`; set `WP_CODEBOX_NODE_BIN` to choose one explicitly.

Workspace tarball on an existing Node.js:

```bash
npm install -g https://github.com/Automattic/wp-codebox/releases/download/v<VERSION>/wp-codebox-workspace-<VERSION>.tgz
wp-codebox commands --json
```

From a checkout:

```bash
npm install
npm run build
npm run wp-codebox -- commands --json
```

## Installing The WordPress Plugin

Install `wp-codebox.zip` on the parent site, then tell it where the CLI is. The
runner resolves the binary from ability input first, then the `wp_codebox_bin`
option, then the `wp_codebox_bin` filter. Multisite installs read the option
from network options, because the executable path is host-level configuration.

- **Self-hosted control planes:** install the CLI on the host that runs PHP and
point `wp_codebox_bin` at it. Supply runtime components through the
`wp_codebox_component_contracts` option or filter.
- **Local development:** point `wp_codebox_bin` at `bin/wp-codebox-source.mjs` in
a checkout. It runs the incremental source build before every invocation, so
ignored `dist/` output never predates the checked-out source.
- **Hosted control planes:** deploy the CLI and plugin zip from the same release,
configure the binary and component contracts centrally, and expose only the
parent-site abilities to products.

## Releases

Releases are continuous. Every push to `main` runs the release workflow
(`.github/workflows/release.yml`), which derives the version from conventional
commits since the last tag (`fix:` is a patch, `feat:` a minor, a breaking change
a major; `chore:`, `ci:`, `docs:`, and `test:` do not release), runs the quality
gates, tags, writes `CHANGELOG.md`, and publishes the GitHub Release with its
assets. Run the workflow manually with `dry-run` to preview the next version.

The workspace packages and the plugin share one version: keep
`packages/wordpress-plugin/wp-codebox.php` `Version:` aligned with
`package.json`.

`npm run smoke -- --group package` validates the artifact shapes: the CLI pack
contains compiled `dist/` without TypeScript source, the root tarball installs a
working `wp-codebox` binary that can emit the command catalog, and the plugin zip
contains the bootstrap, PHP sources, browser runtime asset, and vendored CLI
without package metadata or generated artifacts.
80 changes: 80 additions & 0 deletions docs/host-tool-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Host Tool Registry

`HostToolRegistry` is a WP Codebox transport adapter, not a generic tool
contract. The caller owns the canonical tool declarations, concrete tool
sources, execution result semantics, pending external-tool states, runtime
metadata, and product policy.
WP Codebox only exposes caller-provided per-run tool declarations to sandbox
agents, routes allowed calls across the browser/host boundary, and records
transport diagnostics.

Host products can register a caller-provided canonical tool declaration plus a
host-side handler without adding product-specific logic to WP Codebox core. The
runtime still gates execution through `RuntimePolicy.commands`, so callers must
explicitly allow each registered canonical tool name before a sandbox can invoke
it.

```ts
import { createHostToolRegistry } from "@automattic/wp-codebox-core/public"

const hostTools = createHostToolRegistry([
{
declaration: {
name: "client/echo",
description: "Echo a structured payload from the host bridge.",
parameters: {
type: "object",
required: ["message"],
properties: { message: { type: "string" } },
additionalProperties: false,
},
executor: "client",
scope: "run",
runtime: { completion_signal: "progress" },
},
name: "client/echo",
description: "Echo a structured payload from the host bridge.",
outputSchema: {
type: "object",
required: ["message"],
properties: { message: { type: "string" } },
additionalProperties: false,
},
policy: { capability: "client/echo", risk: "read" },
handler: (input) => input,
},
])

// Pass `hostTools` through the Codebox runtime task/session options for the run
// that is allowed to execute `client/echo`.
```

Host tool output is a Codebox transport diagnostic envelope with schema
`wp-codebox/host-tool-result/v1`. Successful calls return `status: "ok"`, an
`output` value validated against the transport output schema, and `toolResult`
using the caller's canonical tool result shape: `success`, `tool_name`,
`result`, `metadata`, and optional `runtime`. Invalid input, invalid output,
malformed JSON, and handler failures return `status: "error"` with a stable
transport error code while `toolResult` maps the same failure to a canonical
tool error. The `diagnostics` object is the Codebox-owned portion of the
envelope and preserves the transport, policy command, validation schemas, and
resolved policy metadata.

Product-specific tools such as evidence commands should live in product
extensions that provide canonical tool declarations and handlers through this
transport surface. Codebox should not encode product policy semantics,
product tool names, or cross-product tool mediation rules in this layer.

Trusted worker hosts that need repo-local commands can register a Codebox host
tool adapter instead of exposing arbitrary shell.
Each adapter is still a named host tool such as `host.pnpm-test`,
`host.cargo-test`, or `host.project-check`; the runtime denies it unless that
exact name appears in `policy.commands`. The adapter runs one configured
executable with `child_process.spawn()` and `shell: false`, appends only
structured string-array arguments from the input, enforces allowed cwd roots,
uses an explicit timeout, captures bounded stdout/stderr, and passes only
configured environment variables plus explicitly allowlisted input env keys.

The adapter configuration names one executable, fixed base arguments, allowed
working-directory roots, timeout and output limits, and explicitly inherited
environment variables.
143 changes: 143 additions & 0 deletions docs/runtime-episodes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Runtime Episodes

Use `createRuntimeEpisode()` when a caller needs a stateful sandbox loop instead
of a one-shot command or recipe. The episode wrapper is generic: it records reset
observations, step executions, optional per-step observations, snapshots, and
artifact bundles without knowing benchmark, reward, or scenario semantics.

Use the Codebox runtime task/session APIs to execute bounded runtime steps such
as `wordpress.wp-cli` or `wordpress.run-php`, then collect the returned artifact
refs from the Codebox result envelope.

When `collectArtifacts()` runs for an episode, WP Codebox persists the trace into
the artifact bundle at `files/runtime-episode-trace.json` and writes a compact
stream form to `files/runtime-episode.jsonl`. The bundle manifest advertises
those files with `runtime-episode-trace` and `runtime-episode-events` kinds,
`metadata.json` lists them under `artifacts.runtimeEpisodeTrace` and
`artifacts.runtimeEpisodeEvents`, and `files/review.json` includes
`evidence.runtimeEpisodeTrace` for review tooling. `verifyArtifactBundle()`
checks the advertised trace file exists and validates against
`wp-codebox/runtime-episode-trace/v1`.

Artifact bundles also include `files/runtime-reference-manifest.json` using
schema `wp-codebox/runtime-reference-manifest/v1`. This manifest is a stable,
hashable index of runtime-related refs: the artifact-bundle id/digest,
bundle-relative artifact file refs with SHA-256 values, optional trace/events
refs, and snapshot refs. Its id is `runtime-reference-manifest-sha256-<digest>`,
where the digest is computed from the declared refs rather than presentation
fields such as `createdAt`. `verifyArtifactBundle()` validates the manifest
shape, referenced file hashes, artifact-bundle digest, and id/digest pairing.

Products such as eval harnesses can project this generic episode trace into their
own action, observation, reward, and report schemas outside WP Codebox.

The episode trace is a versioned machine-verifiable contract with schema
`wp-codebox/runtime-episode-trace/v1`. `@automattic/wp-codebox-core` exports
`RUNTIME_EPISODE_TRACE_JSON_SCHEMA` for schema-aware consumers and
`validateRuntimeEpisodeTrace()` for lightweight runtime checks. The trace stays
generic: it carries runtime, reset, step, action, execution, observation,
snapshot, and artifact references only. Domain fields such as reward, grader,
scenario, benchmark, task-set, and model-eval belong in consumers that project
the trace into their own schemas.

Episode steps include stable ids and refs for the step, requested action,
runtime execution, optional observation, and collected artifact bundle. Actions
use the generic `wp-codebox/runtime-episode-action/v1` envelope with
`kind: "command" | "filesystem" | "http" | "browser"`, the underlying command
execution name, string args, optional replay intent fields such as `method`,
`url`, `path`, `operation`, `selector`, and a SHA-256 digest over that canonical
payload. Non-command action kinds preserve higher-level runtime intent while the
command execution remains the compatibility primitive.

Observations use the generic `wp-codebox/runtime-episode-observation/v1`
envelope with id, type, data, timestamp, optional artifact refs, and digest. The
Playground backend provides structured observation types for `runtime-info`,
`mounts`, `command-result`, `wordpress-state`, `http-response`,
`browser-result`, `runtime-events`, and `runtime-logs`. Large observation
payloads can be stored as bundle-relative artifacts and referenced by digest.
Refs carry matching digests so callers can compare action args, observations,
executions, snapshots, and artifacts without parsing presentation logs.

`wordpress-state` is a generic WordPress runtime state export with schema
`wp-codebox/wordpress-state-export/v1`. The default request remains small and
safe: `{ type: "wordpress-state" }` exports the summary section only, including
site/home URLs, WordPress version, active theme/plugins, and post counts. Callers
that need richer evidence can pass a section allowlist:

```ts
await episode.observe({
type: "wordpress-state",
sections: ["summary", "posts", "terms", "menus", "templates", "media", "options", "users", "rest-routes", "abilities"],
optionNames: ["blogname", "permalink_structure"],
userFields: ["roles"],
})
```

Each exported section is written as a bundle-relative artifact under
`files/observations/`, with a SHA-256 digest in both the observation data and the
observation `artifactRefs`. The inline observation data summarizes non-summary
sections by count/key metadata so traces stay compact. Posts, templates,
template parts, and global styles include content hashes by default; pass
`includeContent: true` only when full post content, template content, template
part content, or the raw global-style stylesheet is needed. Options require
`optionNames`; users are redacted by default, expose only allowed
role/capability fields, and include identity fields only with `redaction: "none"`
plus an explicit `userFields` allowlist.

The canonical replayable WordPress state artifact contracts are:

- `wp-codebox/wordpress-state-export/v1`: the inline observation data. It is an
object with `schema`, `version: 1`, `generatedAt`, the normalized request
`config`, compact `sections` summaries, and an `artifacts` map keyed by section
name. Each artifact entry contains `artifact`, `sha256`, and `bytes`.
- `wp-codebox/wordpress-state-section/v1`: the artifact-backed full section. It
is an object with `schema`, `version: 1`, `section`, and `data`. The `section`
value matches the key in the export `artifacts` map and the artifact filename.

Stable `wordpress-state-section/v1` section fields are intentionally generic
WordPress state, not replay/grader semantics:

| Section | Stable fields |
| --- | --- |
| `summary` | `siteUrl`, `homeUrl`, `wordpressVersion`, `activeTheme`, `activePlugins`, `postCounts` keyed by post type and status. |
| `posts` | Array of public post types ordered by ascending `ID`. Entries include `id`, `type`, `slug`, `status`, `title`, `contentHash`, `modifiedGmt`, and `content` when `includeContent: true`. This covers posts, pages, and public custom post types. |
| `templates` | `theme`, `templates`, `templateParts`, and `globalStyles`. Template entries include `id`, `slug`, `theme`, `type`, `source`, and `contentHash`; template part entries include `id`, `slug`, `theme`, `area`, `source`, and `contentHash`; global styles expose `stylesheetHash` when available. |
| `options` | Object keyed by explicitly requested `optionNames`; no options are exported implicitly. |
| `terms` | Array of terms with `id`, `taxonomy`, `slug`, `name`, `parent`, and `count`. |
| `menus` | Array of menus with `id`, `slug`, `name`, and `items`; menu items include `id`, `title`, `url`, `parentId`, `object`, and `type`. |
| `media` | Array of attachments ordered by ascending `ID`. Entries include `id`, `slug`, `title`, `status`, `mimeType`, `sourceUrl`, `altText`, and `metadataHash`. |
| `users` | Array ordered by ascending `ID`. Entries always include `id` and `redacted`; `roles` and `caps` are allowed safe fields. Identity fields such as `user_login` and `display_name` are emitted only when requested and `redaction: "none"`. |

Redaction is part of the contract. The default `redaction: "safe"` avoids user
identity fields and requires explicit allowlists for options and users.
`redaction: "none"` is an opt-in export mode for trusted callers that need identity fields.
Consumers should verify artifact SHA-256 values before replay and should branch
on `schema` and `version` instead of projecting per-consumer state shapes.

Replay is bounded to the generic runtime contract. A consumer can replay a step
by creating a compatible backend runtime, applying the same mounts/artifact
inputs, and executing the action envelope in order. WP Codebox intentionally
does not record rewards, scenario ids, benchmark metadata, model-eval fields, or
product-specific success semantics; those belong to the caller's projection of
the trace. Backend behavior still matters: command availability, WordPress
version, installed packages, mounted files, network policy, and secrets policy
must be reproduced by the replay harness.

Runtime snapshots are explicit about their semantics. The Playground backend
returns `semantics: "runtime-state-artifact"` for generic WordPress runtime
snapshots. The snapshot artifact declares `wp-codebox/wordpress-runtime-snapshot/v1`
and captures database tables, `wp-content` files, mounted input metadata, active
theme/plugins, WordPress/PHP compatibility metadata, and SHA-256 hashes. Call
`restoreRuntime(snapshot, backend, { runtime, mounts })` to restore a compatible
snapshot into a fresh runtime. The primitive is intentionally generic WordPress
state capture: caller-specific scoring, evaluation, and success semantics belong
outside WP Codebox.

`collectArtifacts({ includeRuntimeSnapshotBundles: true })` includes snapshot
artifact refs in the artifact manifest and runtime reference indexes. Consumers
should branch on the snapshot `semantics` field and verify artifact hashes before
restore or replay.

The runtime reference manifest repeats each snapshot's `semantics` and adds an
explicit `replay.status`. For Playground runtime-state artifacts that status is
`runtime-state-artifact`, with snapshot artifact refs available for restore.
Loading
Loading