Source builds are primarily for development and local testing.
make build
make upThe compose stack passes PUID / PGID by default (1000:1000). To match the container agent user/group to your host user:
PUID=$(id -u) PGID=$(id -g) make upThe default compose container name is pibox:
docker exec -u agent -it pibox bash
cd /workspace && piRun build/package commands from the repo root:
make build-piclaw # full build: vendor bundle + web assets + TypeScript
make vendor # rebuild vendored assets
make lint # Oxlint
make test # full test suite
make ci-fast # canonical fast CI guardrails + web build
make local-install # pack and install piclaw globally (no restart)
make restart # restart piclaw via the detected service managerFor the current end-to-end GitHub Actions flow (triggers, job dependencies, and step-level map), see:
The implementation lives under runtime/, so direct Bun test runs should target that subtree. Sequential mode is recommended for SQLite safety:
cd runtime && bun test --max-concurrency=1The default full-suite path uses the controlled staged runner:
bun run testDefault controlled runs do not write runtime/generated/controlled-test-report.json or any other JSON report. They print stage summaries, exit codes, and memory measurements to the terminal so a clean source worktree stays clean after bun run test, bun run quality, make ci-fast, and make pre-push-ci.
Use --report for #394 or performance evidence that needs a durable JSON artifact:
bun run runtime/scripts/controlled-test-runner.ts --report artifacts/performance/controlled-test-report.jsonUse runtime/generated/reports/ for disposable local reports. Use artifacts/performance/ or another deliberate artifact path when the report should be reviewed or retained.
Piclaw's Pi runtime packages are sourced from @earendil-works/* and are pinned together at 0.84.0. The runtime uses upstream model/auth services, provider composition, model catalogs, pricing, scoped extension models, raw provider stop reasons, OAuth minimum-validity refresh, native max thinking, compaction estimation, summarization retries, Anthropic signature handling, configurable shellPath behaviour, generation-checked provider catalog publication, remote-catalog revalidation, and Claude Opus 5 support on Anthropic, Amazon Bedrock, and GitHub Copilot.
| Piclaw layer | Why Piclaw still owns it | Focused coverage |
|---|---|---|
runtime/src/extensions/github-copilot-dynamic-models.ts |
Adds authenticated live Copilot catalog data through native provider registration while preserving Piclaw model labels and OAuth behaviour | runtime/test/extensions/github-copilot-dynamic-models.test.ts, runtime/test/agent-pool/earendil-0810-provider-regressions.test.ts |
runtime/src/extensions/azure-openai-api.ts and runtime/extensions/integrations/azure-openai.ts |
Handles Azure deployment routing, managed identity, secondary Foundry endpoints, replay sanitation, context budgeting, and throttle feedback | runtime/test/extensions/azure-openai-*.test.ts, runtime/test/runtime/provider-bootstrap.test.ts |
runtime/src/agent-pool/usage.ts and runtime/src/db/token-usage.ts |
Persists assistant, tool, compaction, and branch-summary usage in Piclaw's SQLite token_usage table |
runtime/test/agent-pool/usage.test.ts, runtime/test/db/token-usage.test.ts |
runtime/src/agent-pool/runtime-facade.ts |
Adds Piclaw web payload fields for per-model thinking levels and non-secret provider diagnostics | runtime/test/agent-pool/runtime-facade.test.ts |
runtime/src/agent-control/provider-defs.ts |
Supplies static fallback names, hints, and setup notes for Piclaw's /login picker when runtime provider enrichment is unavailable |
runtime/test/agent-control/provider-defs.test.ts |
runtime/src/channels/web/sse/agent-events.ts |
Converts upstream summarization retry lifecycle events into web agent_status payloads |
runtime/test/channels/web/sse/agent-events.test.ts |
scripts/audit-model-catalog-delta.ts |
Compares two local pi-ai provider catalog snapshots before and after dependency upgrades |
runtime/test/scripts/model-catalog-delta-audit.test.ts |
runtime/src/tools/tracked-bash.ts |
Keeps Piclaw's process tracking and keychain injection while honoring Pi's configured shellPath |
runtime/test/tools/tracked-bash.test.ts |
runtime/src/extensions/mcp-timeout-patch.ts |
Preserves Piclaw's outer PICLAW_MCP_TOOL_TIMEOUT_MS timeout/abort guard around tools registered by the packaged adapter |
runtime/test/extensions/mcp-timeout-patch.test.ts |
runtime/vendor-manifests/ plus generated *.meta.json |
Keeps browser dependencies reproducible and auditable | runtime/test/scripts/runtime-vendors.test.ts |
The compatibility layers fail conservatively: dynamic model discovery falls back to the static catalog, the MCP wrapper leaves unrelated tools untouched and can be disabled independently, and statistics omit optional diagnostics when provider data is unavailable.
Useful focused validation from the repository root:
bun test \
runtime/test/agent-pool/runtime-facade.test.ts \
runtime/test/agent-pool/usage.test.ts \
runtime/test/channels/web/sse/agent-events.test.ts \
runtime/test/extensions/mcp-timeout-patch.test.ts \
runtime/test/extensions/github-copilot-dynamic-models.test.ts \
runtime/test/extensions/azure-openai-api.test.ts \
runtime/test/extensions/azure-openai-retry-after.test.ts \
runtime/test/extensions/azure-openai-routing.test.tsUser/operator behavior is documented in configuration.md, tools-and-skills.md, mcp.md, azure-openai-extension.md, and vendored-widget-libraries.md.
PiClaw bundles pi-mcp-adapter as a normal package dependency and loads it as a packaged session extension from node_modules/.
Relevant files when working on MCP integration:
package.json/bun.lockruntime/src/agent-pool/session.tsdocs/mcp.mdskel/.mcp.json.exampleskel/.pi/mcp.json.exampleskel/.pi/skills/mcp-adapter/SKILL.md
Piclaw uses pi-mcp-adapter 2.11.0, which forwards abort signals and applies its configured requestTimeoutMs to protocol requests. Piclaw independently wraps MCP tools after their session_start registration so existing PICLAW_MCP_TOOL_TIMEOUT_MS behavior remains stable across adapter upgrades.
Focused regression tests:
PICLAW_DB_IN_MEMORY=1 bun test \
runtime/test/agent-pool/mcp-adapter-bundled.test.ts \
runtime/test/extensions/mcp-timeout-patch.test.tsRecent Azure OpenAI work hardened the shared helper resolution path, improved image-output formatting, and added transparent PNG support to /image.
Relevant files:
runtime/extensions/integrations/azure-openai.tsruntime/src/extensions/azure-openai-api.tsruntime/test/extensions/azure-openai-api.test.tsruntime/test/extensions/azure-openai-image-output.test.tsdocs/azure/azure-openai-extension.md
Focused regression tests:
bun test \
runtime/test/extensions/azure-openai-api.test.ts \
runtime/test/extensions/azure-openai-image-output.test.tsNotes:
/image --transparentrequests transparent PNG output on the Azure OpenAI image path./fluxstill rejects transparent background requests.- Successful image runs format results as workspace-backed inline images plus file listings rather than raw download links.
Use the standalone harness when you need provider-level evidence without reloading the running Piclaw process.
Relevant files:
runtime/scripts/azure-openai-harness.tsruntime/extensions/experimental/azure-openai.harness.tsruntime/extensions/integrations/azure-openai.tsruntime/src/extensions/azure-openai-api.tsdocs/azure/azure-openai-extension.md
Typical commands:
bun run scripts/azure-openai-harness.ts --list
bun run scripts/azure-openai-harness.ts --models gpt-5-3-codex,gpt-5-4 --cases json,tool,history --tool-rounds 2 --history-turns 3
AOAI_EXPERIMENT_AZURE_CLIENT_REQUEST_ID=1 bun run scripts/azure-openai-harness.ts --providers azure-openai --models gpt-5-3-codex --cases json,tool,history --tool-rounds 2 --history-turns 3Notes:
- the harness bundles to
/workspace/piclaw/.tmp/azure-openai.harness.bundle.mjsso Bun resolves this repo's dependencies correctly - the live Azure extension aligns
prompt_cache_key,session_id, andx-client-request-idfrom the active session id on the Azure Responses path - the harness checks those correlation fields automatically and fails if they drift
- the harness also fails if replayed request payloads still contain leaked
partialJsonscratch buffers - historical
0.67.2live-provider evidence forgpt-5-3-codexandgpt-5-4is recorded in azure-openai-extension.md; deterministic0.81.0behavior is covered by the focused tests in the Earendil section above AOAI_EXPERIMENT_AZURE_CLIENT_REQUEST_ID=1remains available for the optionalx-ms-client-request-idexperiment
Recent workspace explorer changes added an index-status surface and manual reindex control on top of the existing FTS search/indexing pipeline.
Relevant files:
runtime/src/workspace-search.tsruntime/src/extensions/workspace-search.tsruntime/src/channels/web/handlers/workspace.tsruntime/src/channels/web/workspace/service.tsruntime/web/src/components/workspace-explorer.ts
Focused regression tests:
PICLAW_DB_IN_MEMORY=1 bun test \
runtime/test/channels/web/http-dispatch-workspace.test.ts \
runtime/test/channels/web/http-route-classification.test.ts \
runtime/test/channels/web/workspace-service.test.ts \
runtime/test/workspace-search.test.tsA realistic OOBE browser pass can be run against a local Docker container rather than only against the in-process dedicated web test instance.
Default command:
bun run test:oobe:local-containerThe script:
- ensures Playwright Chromium is available
- builds a local image (
piclaw-oobe-test:local) unless skipped - mounts the repo's current
runtime/web/static/distinto the container so web-bundle changes can be validated against the latest local build without requiring a fresh image for every UI-only tweak - starts a temporary local Piclaw container on a random localhost port
- runs Playwright against the live web UI
- writes screenshots, DOM dumps, state captures, and container logs under
artifacts/oobe-local-container/ - validates:
- provider-missing OOBE panel copy
/logincompose prefill- dismiss persistence after reload
- provider-ready OOBE state
/modelcompose prefill- ready-state completion persistence after reload
Useful flags/env:
# Skip the image rebuild when only the web bundle changed locally.
# Rebuild the local web assets first so the mounted dist is current.
cd runtime && bun run build:web && cd ..
PICLAW_OOBE_TEST_SKIP_BUILD=1 bun run test:oobe:local-container
PICLAW_OOBE_TEST_IMAGE=pibox:latest bun run test:oobe:local-container
PICLAW_OOBE_TEST_HEADLESS=0 bun run test:oobe:local-containerImplementation surface:
runtime/scripts/playwright/oobe-local-container.ts
Notes:
- The generated
artifacts/oobe-local-container/files are local smoke-test artefacts, not release payloads. - Clean them up before tagging if you do not intend to keep the latest repro bundle around.
The editor pane polls GET /workspace/stat?path=<file> every 5s while the tab is focused and shows a conflict resolution bar when the on-disk mtime advances past the last known mtime. The same FileConflictMonitor can be reused by pane-style editors, and add-ons can vendor the same pattern for specialized editors such as kanban-editor.
Relevant files:
runtime/extensions/viewers/editor/editor-extension.tsruntime/web/src/panes/file-conflict-monitor.ts
Blank-turn detection, compaction stall bounding, the recovery chip, and the held-failed-run retry/skip model are documented in architecture.md under "Per-chat turn lifecycle and failure model", "Recovery chip", "Blank turn detection", and "Compaction stall guard".
Relevant files:
runtime/src/agent-pool/blank-turn-detection.tsruntime/src/agent-pool/prompt-utils.ts—waitForSessionIdle, session idle defaultsruntime/src/agent-pool/automatic-recovery.ts— auto-recovery classification / retry policyruntime/src/channels/web/handlers/agent.ts— web turn finalization, held-failure behavior, retry/skip resolution pointsruntime/src/channels/web/runtime/chat-run-control.ts— explicit retry/skip cursor helpersruntime/src/db/chat-cursors.ts—beginChatRun, inflight rollback, failed-run storage, rollback-with-errorruntime/src/extensions/smart-compaction.ts— working-indicator UI hooks
Focused regression tests:
bun test \
runtime/test/db/chat-cursors.test.ts \
runtime/test/channels/web/runtime/chat-run-control.test.ts \
runtime/test/channels/web/recovery.test.ts \
runtime/test/channels/web/web-channel-recovery-state.test.ts \
runtime/test/channels/web/web-channel.test.tsSee architecture.md for the full source layout and module boundaries.
New skills go in .pi/skills/<name>/SKILL.md (workspace-local) or
skel/.pi/skills/<name>/ (shipped with the skel for new installs).
New internal tools register through the extension API:
pi.registerTool({ name, description, parameters, execute });For visual artifacts, always load and follow:
/workspace/.pi/skills/visual-artifact-generator/SKILL.md/workspace/.pi/skills/visual-design/SKILL.md
Use the mermaid-fixup.js helper for any artifact that renders Mermaid diagrams.
New GET /agent/* or POST /agent/* endpoints follow this chain:
runtime/src/channels/web/http/dispatch-agent.ts— register the routeruntime/src/channels/web/endpoints/channel-endpoint-facade-service.ts— add handler methodruntime/src/channels/web/core/web-channel-http-surface-service.ts— delegate from surfaceruntime/src/channels/web/core/web-channel-contracts.ts— declare the interfaceruntime/src/channels/web/core/web-channel-prototype.ts— bind the prototype method
See runtime/src/channels/web/agent/agent-commands.ts (GET /agent/commands)
as the canonical simple example.
When shipping new features, update:
docs/tools-and-skills.md— if new tools, skills, or slash commands are addeddocs/architecture.md— if new endpoints or subsystems are addeddocs/configuration.md— if new environment variables or config keys are addedREADME.md— if the feature merits a bullet in the Why/Feature overviewdocs/vendored-widget-libraries.md— if new vendored libraries or fonts are added