typeclaw 0.7.0 → 0.8.0

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.
@@ -342,17 +342,17 @@ The `docker.file` block has two layers of customization:
342
342
 
343
343
  ### Fields
344
344
 
345
- | Field | Required | Type | Notes |
346
- | ------------- | -------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
347
- | `tmux` | no | boolean \| string | Default `true`. `false` omits tmux from the apt install. String pins the Debian package version (e.g. `"3.3a-3"` → `tmux=3.3a-3`). |
348
- | `gh` | no | boolean \| string | Default `true`. `false` omits **both** the `gh` package and the GitHub CLI keyring bootstrap layer (skipping the network roundtrip on cold builds). String pins the version. |
349
- | `python` | no | boolean | Default `true`. Fans out to `python3 python3-pip python3-venv python-is-python3` (the bundle that makes `python` and `pip` resolve correctly inside the container). Boolean-only — no version pin, because Debian's `python3` is a meta-package that doesn't accept a useful pin. |
350
- | `ffmpeg` | no | boolean \| string | Default `false`. `true` apt-installs ffmpeg (~80 MB of codecs). String pins the version. |
351
- | `cjkFonts` | no | boolean | Default `true`. Installs `fonts-noto-cjk` (~56 MB) so Chromium (used by `agent-browser`) renders Korean/Japanese/Chinese glyphs correctly in screenshots, `page.pdf()`, and other raster output. `false` skips the layer entirely (DOM/innerText scraping is unaffected by font absence — only raster output shows tofu boxes). Boolean-only: the package is a metapackage tracking upstream Noto, no useful apt pin. |
352
- | `cloudflared` | no | boolean | Default `true`. Downloads the pinned `cloudflared` GitHub release (~35 MB) into the image so `cloudflare-quick` tunnels work on the next `start` without a separate Dockerfile edit. `false` skips the layer entirely on agents that don't use tunnels. Boolean-only — pinning is owned by the typeclaw release. |
353
- | `xvfb` | no | boolean | Default `true`. Installs `xvfb` (~5 MB) so the entrypoint shim can spawn a virtual X server and export `DISPLAY=:99`, giving headed Chrome (agent-browser `--headed`, headful Playwright) a real X11 display to defeat headless-mode WAF fingerprinting. `false` skips the layer; the shim self-heals (no `Xvfb` on PATH → execs the agent without `DISPLAY`). Boolean-only — xvfb tracks the upstream X server release with no useful apt pin. |
354
- | `claudeCode` | no | boolean | Default `false`. `true` runs Anthropic's official `curl -fsSL https://claude.ai/install.sh \| bash` in a dedicated layer (between agent-browser and the entrypoint shim). Not apt: no version-pin variant; the upstream installer manages channels via env vars. Pairs with the `typeclaw-claude-code` skill, which documents the auth + tmux-driven usage flow. |
355
- | `append` | no | array of strings | Each entry is a single Dockerfile line — schema **rejects** entries containing `\n` or `\r`. Defaults to `[]`. Splice happens just before `ENTRYPOINT`, after `ENV NODE_ENV=production`. |
345
+ | Field | Required | Type | Notes |
346
+ | ------------- | -------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
347
+ | `tmux` | no | boolean \| string | Default `true`. `false` omits tmux from the apt install. String pins the Debian package version (e.g. `"3.3a-3"` → `tmux=3.3a-3`). |
348
+ | `gh` | no | boolean \| string | Default `true`. `false` omits **both** the `gh` package and the GitHub CLI keyring bootstrap layer (skipping the network roundtrip on cold builds). String pins the version. |
349
+ | `python` | no | boolean | Default `true`. Fans out to `python3 python3-pip python3-venv python-is-python3` (the bundle that makes `python` and `pip` resolve correctly inside the container). Boolean-only — no version pin, because Debian's `python3` is a meta-package that doesn't accept a useful pin. |
350
+ | `ffmpeg` | no | boolean \| string | Default `false`. `true` apt-installs ffmpeg (~80 MB of codecs). String pins the version. |
351
+ | `cjkFonts` | no | boolean | Default `true`. Installs `fonts-noto-cjk` (~56 MB) so Chromium (used by `agent-browser`) renders Korean/Japanese/Chinese glyphs correctly in screenshots, `page.pdf()`, and other raster output. `false` skips the layer entirely (DOM/innerText scraping is unaffected by font absence — only raster output shows tofu boxes). Boolean-only: the package is a metapackage tracking upstream Noto, no useful apt pin. |
352
+ | `cloudflared` | no | boolean | Default `true`. Downloads the pinned `cloudflared` GitHub release (~35 MB) into the image so `cloudflare-quick` tunnels work on the next `start` without a separate Dockerfile edit. `false` skips the layer entirely on agents that don't use tunnels. Boolean-only — pinning is owned by the typeclaw release. |
353
+ | `xvfb` | no | boolean | Default `true`. Installs `xvfb` (~5 MB) so the entrypoint shim can spawn a virtual X server and export `DISPLAY=:99`, giving headed Chrome (agent-browser `--headed`, headful Playwright) a real X11 display to defeat headless-mode WAF fingerprinting. `false` skips the layer; the shim self-heals (no `Xvfb` on PATH → execs the agent without `DISPLAY`). Boolean-only — xvfb tracks the upstream X server release with no useful apt pin. |
354
+ | `claudeCode` | no | boolean | Default `false`. `true` runs Anthropic's official `curl -fsSL https://claude.ai/install.sh \| bash` in a dedicated layer (between agent-browser and the entrypoint shim) and pre-seeds `~/.claude.json` to skip the TTY-only theme picker on first launch (without it the agent's `tmux send-keys` would be eaten by the picker). Not apt: no version-pin variant; the upstream installer manages channels via env vars. Pairs with the `typeclaw-claude-code` skill, which documents the auth + tmux-driven usage flow including how to clear the post-seed API-key/trust dialogs. |
355
+ | `append` | no | array of strings | Each entry is a single Dockerfile line — schema **rejects** entries containing `\n` or `\r`. Defaults to `[]`. Splice happens just before `ENTRYPOINT`, after `ENV NODE_ENV=production`. |
356
356
 
357
357
  Toggle version strings reject whitespace and `=` (apt-injection guard) — pass just the version, not `pkg=ver`.
358
358
 
@@ -427,7 +427,7 @@ The toggle-driven apt install benefits from BuildKit `--mount=type=cache` on `/v
427
427
 
428
428
  ## Gitignore
429
429
 
430
- `typeclaw start` rewrites the agent folder's `.gitignore` from a template baked into the typeclaw CLI on **every** invocation, then auto-commits it when the agent folder is a git repo and the file changed. The template protects two categories: truly-ignored paths (`.env`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) and system-managed runtime state (`sessions/`, `memory/`, `channels/`) that TypeClaw, not the agent, commits on its own schedule. Editing `.gitignore` by hand is temporary; the next `typeclaw start` overwrites it.
430
+ `typeclaw start` rewrites the agent folder's `.gitignore` from a template baked into the typeclaw CLI on **every** invocation, then auto-commits it when the agent folder is a git repo and the file changed. The template protects two categories: truly-ignored paths (`secrets.json`, `.env`, `.env.local`, `auth.json`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) and system-managed runtime state (`sessions/`, `memory/`, `channels/`) that TypeClaw, not the agent, commits on its own schedule. Editing `.gitignore` by hand is temporary; the next `typeclaw start` overwrites it.
431
431
 
432
432
  The `git.ignore.append` field (introduced when the legacy top-level `gitignore` key was nested under the `git` namespace for future extensibility — see **Legacy migration**) is the supported escape hatch for additional local ignore patterns. It is an array of strings, each treated as a single `.gitignore` line. The CLI splices them into the autogenerated `.gitignore` before TypeClaw's protected rules, prefixed with a `# Custom entries from typeclaw.json#git.ignore.append.` comment.
433
433
 
@@ -439,7 +439,7 @@ The `git.ignore.append` field (introduced when the legacy top-level `gitignore`
439
439
 
440
440
  ### Ordering and protected paths
441
441
 
442
- `.gitignore` is order-sensitive: later `!` negation rules can unignore earlier ignore rules. TypeClaw therefore renders `git.ignore.append` **before** its own truly-ignored and system-managed entries, so even a custom `!sessions/` or `!.env` cannot override TypeClaw's protections. Custom ordinary ignore patterns still work because they add additional ignores; they just do not get the final word over TypeClaw-owned paths.
442
+ `.gitignore` is order-sensitive: later `!` negation rules can unignore earlier ignore rules. TypeClaw therefore renders `git.ignore.append` **before** its own truly-ignored and system-managed entries, so even a custom `!sessions/`, `!secrets.json`, or `!.env` cannot override TypeClaw's protections. Custom ordinary ignore patterns still work because they add additional ignores; they just do not get the final word over TypeClaw-owned paths.
443
443
 
444
444
  Materialized shape when `append` is non-empty:
445
445
 
@@ -449,6 +449,7 @@ scratch/
449
449
  *.local.log
450
450
 
451
451
  # Truly ignored: ...
452
+ secrets.json
452
453
  .env
453
454
  Dockerfile
454
455
 
@@ -514,16 +515,16 @@ Do **not** invent plugin blocks; their existence is determined by the plugins li
514
515
 
515
516
  The model registry currently has these entries:
516
517
 
517
- | `model` value | Display name | Provider | Auth | Notes |
518
- | ------------------------------------------------------ | --------------- | ------------ | ------------------- | ---------------------------------------------------------------------------------------- |
519
- | `openai/gpt-5.4-nano` | GPT-5.4 nano | OpenAI | API key | Default. Requires `OPENAI_API_KEY` in `.env`. Reasoning model, 400K context. |
520
- | `openai/gpt-5.4-mini` | GPT-5.4 mini | OpenAI | API key | Requires `OPENAI_API_KEY` in `.env`. Reasoning model, 400K context. |
521
- | `openai/gpt-5.4` | GPT-5.4 | OpenAI | API key | Requires `OPENAI_API_KEY` in `.env`. Reasoning model, 1.05M context. |
522
- | `openai/gpt-5.5` | GPT-5.5 | OpenAI | API key | Flagship. Requires `OPENAI_API_KEY` in `.env`. Reasoning model, 1.05M context. |
523
- | `openai-codex/gpt-5.4-mini` | GPT-5.4 mini | OpenAI Codex | OAuth (ChatGPT P/P) | Cheaper Codex tier. Requires OAuth login at init. Persisted to `secrets.json`. 272K ctx. |
524
- | `openai-codex/gpt-5.4` | GPT-5.4 | OpenAI Codex | OAuth (ChatGPT P/P) | Codex mid-tier. Requires OAuth login at init. Persisted to `secrets.json`. 272K context. |
525
- | `openai-codex/gpt-5.5` | GPT-5.5 | OpenAI Codex | OAuth (ChatGPT P/P) | Flagship Codex. Requires OAuth login at init. Persisted to `secrets.json`. 272K context. |
526
- | `fireworks/accounts/fireworks/routers/kimi-k2p6-turbo` | Kimi K2.6 Turbo | Fireworks | API key | Requires `FIREWORKS_API_KEY` in `.env`. Reasoning model, 256K context. |
518
+ | `model` value | Display name | Provider | Auth | Notes |
519
+ | ------------------------------------------------------ | --------------- | ------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
520
+ | `openai/gpt-5.4-nano` | GPT-5.4 nano | OpenAI | API key | Default. API key in `secrets.json#providers.openai.key.value` (or `OPENAI_API_KEY` env override). Reasoning model, 400K context. |
521
+ | `openai/gpt-5.4-mini` | GPT-5.4 mini | OpenAI | API key | API key in `secrets.json#providers.openai.key.value` (or `OPENAI_API_KEY` env override). Reasoning model, 400K context. |
522
+ | `openai/gpt-5.4` | GPT-5.4 | OpenAI | API key | API key in `secrets.json#providers.openai.key.value` (or `OPENAI_API_KEY` env override). Reasoning model, 1.05M context. |
523
+ | `openai/gpt-5.5` | GPT-5.5 | OpenAI | API key | Flagship. API key in `secrets.json#providers.openai.key.value` (or `OPENAI_API_KEY` env override). Reasoning model, 1.05M context. |
524
+ | `openai-codex/gpt-5.4-mini` | GPT-5.4 mini | OpenAI Codex | OAuth (ChatGPT P/P) | Cheaper Codex tier. Requires OAuth login at init. Persisted to `secrets.json`. 272K ctx. |
525
+ | `openai-codex/gpt-5.4` | GPT-5.4 | OpenAI Codex | OAuth (ChatGPT P/P) | Codex mid-tier. Requires OAuth login at init. Persisted to `secrets.json`. 272K context. |
526
+ | `openai-codex/gpt-5.5` | GPT-5.5 | OpenAI Codex | OAuth (ChatGPT P/P) | Flagship Codex. Requires OAuth login at init. Persisted to `secrets.json`. 272K context. |
527
+ | `fireworks/accounts/fireworks/routers/kimi-k2p6-turbo` | Kimi K2.6 Turbo | Fireworks | API key | API key in `secrets.json#providers.fireworks.key.value` (or `FIREWORKS_API_KEY` env override). Reasoning model, 256K context. |
527
528
 
528
529
  **Do not write any other value into `model`.** The schema enum will reject the file at load, and the runtime will refuse to boot the agent process. If the user names a model that isn't in this table — "use Claude", "switch to o3" — be honest:
529
530
 
@@ -533,12 +534,9 @@ Do **not** edit `typeclaw.json` to a model the registry doesn't know, even if th
533
534
 
534
535
  ## Provider credentials
535
536
 
536
- `typeclaw.json` does **not** hold API keys or OAuth tokens. Credentials live in two gitignored files:
537
+ `typeclaw.json` does **not** hold API keys or OAuth tokens. Credentials live in two gitignored files, with `secrets.json` as the canonical store and `.env` retained for env-var overrides and parity with non-typeclaw tooling that reads from the environment:
537
538
 
538
- - **`./.env`** (any environment variable, including API keys): plain `KEY=value` lines, loaded by Docker via `--env-file` at container start. The canonical env-var names per provider:
539
- - `OPENAI_API_KEY` — for any `openai/...` model.
540
- - `FIREWORKS_API_KEY` — for any `fireworks/...` model.
541
- - **`./secrets.json`** (structured store): a `v2` envelope managed by `SecretsBackend` (wraps `pi-coding-agent`'s `AuthStorage`). Two top-level slices:
539
+ - **`./secrets.json`** (canonical structured store): a `v2` envelope managed by `SecretsBackend` (wraps `pi-coding-agent`'s `AuthStorage`). Written by `typeclaw init`, the OAuth refresh path, and explicit user-driven rotation. Two top-level slices:
542
540
  - `providers.*` — per-provider credentials. API-key providers store `{ type: 'api_key', key: <Secret> }`. OAuth providers store the `pi-coding-agent` token blob `{ type: 'oauth', access_token, refresh_token, expires_at, ... }`. The container auto-refreshes OAuth tokens with file locking; api-key writes only happen on explicit user-driven rotation.
543
541
  - `channels.*` — per-adapter credentials, with named fields per adapter:
544
542
  - `discord-bot: { token: <Secret> }`
@@ -547,6 +545,13 @@ Do **not** edit `typeclaw.json` to a model the registry doesn't know, even if th
547
545
 
548
546
  (Pre-v2 agent folders carry the older `llm` slice and channel-env-var-keyed shape; they are upgraded transparently on first read. Pre-rename folders may even carry the file as `auth.json`; it is renamed to `secrets.json` on the next boot.)
549
547
 
548
+ - **`./.env`** (env-var overrides): plain `KEY=value` lines, loaded by Docker via `--env-file` at container start. When set, an env var **wins** over the file value (see resolution rules below). Useful for CI, transient rotations, or any tooling outside typeclaw that reads from the environment. The canonical env-var names per provider:
549
+ - `OPENAI_API_KEY` — for any `openai/...` model.
550
+ - `FIREWORKS_API_KEY` — for any `fireworks/...` model.
551
+ - `ANTHROPIC_API_KEY` — for any `anthropic/...` model when using API-key auth.
552
+
553
+ New typeclaw secrets should land in `secrets.json` (via `typeclaw init` or a structured edit) — `.env` is no longer the default home.
554
+
550
555
  ### The `Secret` shape and env-wins resolution
551
556
 
552
557
  Every secret-bearing field in `secrets.json` is a **`Secret`**: either a plain string or an object `{ value?, env? }`.
@@ -580,11 +585,11 @@ Every secret-bearing field in `secrets.json` is a **`Secret`**: either a plain s
580
585
 
581
586
  ### Switching credentials
582
587
 
583
- If a user wants to switch from API key to OAuth (or vice versa) for a provider that supports both, the easiest path is to delete the relevant entry from `.env` / `secrets.json#providers` and re-run `typeclaw init` from inside the agent folder — it'll prompt for the auth method again.
588
+ If a user wants to switch from API key to OAuth (or vice versa) for a provider that supports both, the easiest path is to delete the relevant entry from `secrets.json#providers` (and any matching env-var override in `.env`) and re-run `typeclaw init` from inside the agent folder — it'll prompt for the auth method again.
584
589
 
585
- If the user wants to rotate an api-key, edit either `.env` (env-wins picks it up immediately) or `secrets.json#providers.<provider>.key` (rewrite the `value` field, or remove the entry if the env var should take over). After either, `typeclaw restart` on the host stage.
590
+ If the user wants to rotate an api-key, edit `secrets.json#providers.<provider>.key` rewrite the `value` field (preserving any `env` binding), or remove the entry entirely if an env-var override is taking over. `.env` is a secondary path that still works (env-wins picks it up immediately), but `secrets.json` is the durable home. After either, `typeclaw restart` on the host stage.
586
591
 
587
- Never echo, log, or commit values from `.env` or `secrets.json`. Both are gitignored by default — keep them that way.
592
+ Never echo, log, or commit values from `secrets.json` or `.env`. Both are gitignored by default — keep them that way.
588
593
 
589
594
  ## Editing `typeclaw.json` safely
590
595
 
@@ -627,7 +632,7 @@ Never echo, log, or commit values from `.env` or `secrets.json`. Both are gitign
627
632
  ## Things you must not do
628
633
 
629
634
  - **Do not invent fields the schema doesn't support** (no `provider`, `apiKey`, `temperature`, `maxTokens`, `systemPrompt`, `tools`, `timeout`, `retry`, etc.). They will be silently dropped or, worse, mistaken for a plugin config block. Lying to the user that "I added a temperature field" when the runtime ignores it is a worse failure than refusing.
630
- - **Do not move secrets into `typeclaw.json`.** It is committed to git. API keys belong in `.env`.
635
+ - **Do not move secrets into `typeclaw.json`.** It is committed to git. API keys and channel tokens belong in `secrets.json` (or, for env-override use cases, `.env`).
631
636
  - **Do not change `port` casually.** The host-stage `typeclaw start` launcher publishes a port mapping it learned at `start` time. Changing the port in `typeclaw.json` without re-running `typeclaw start` (which re-reads it) means the TUI will connect to the wrong port and silently fail. If you change `port`, tell the user explicitly that the next `typeclaw start` will pick the new mapping.
632
637
  - **Do not change `model` to something not in the registry.** The schema enum will reject the file at load, and the runtime will refuse to boot the agent process. If the user wants a model that isn't there, this is a typeclaw-side change, not a config edit.
633
638
  - **Do not edit `typeclaw.json` from inside an `exec` cron job's `command`.** That mutates the file behind the runtime's back. Live-reloadable fields still won't update until something triggers a `reload`, and restart-required fields are guaranteed wrong.
@@ -9,7 +9,7 @@ Your agent folder is a git repo. Almost every file in it (`typeclaw.json`, `cron
9
9
 
10
10
  The contents of `.gitignore` split into two distinct categories — the distinction matters for this skill:
11
11
 
12
- - **Truly ignored** (`.env`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) — never in history, ever. Secrets, runtime junk, your free-write zone, and regenerated-on-start system files.
12
+ - **Truly ignored** (`secrets.json`, `.env`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) — never in history, ever. Secrets, runtime junk, your free-write zone, and regenerated-on-start system files.
13
13
  - **System-managed** (`sessions/`, `memory/`, `channels/`) — gitignored so _you_ don't stage them, but TypeClaw force-commits them on its own schedule. `sessions/` is auto-backed up by the runtime; `memory/` is committed by the dreaming subagent; `channels/` is runtime-owned channel state. Treat them as runtime-owned: do not `git add` them, do not write commit messages about them, and do not be alarmed when they appear in `git log`.
14
14
 
15
15
  Everything not in either bucket is yours to commit.
@@ -80,7 +80,7 @@ If you discover an unrelated dirty file from a previous turn, commit it separate
80
80
  - **Do not skip the commit** "because the change is small." Small changes are exactly the ones that get lost. Toggling `enabled: false` on a cron job is a decision; commit it.
81
81
  - **Do not write empty or generic messages** ("update", "fix", "change config"). The history exists to be read.
82
82
  - **Do not amend or force-push** to clean up later. Sloppy history with real commits beats clean history that lies about when decisions happened.
83
- - **Do not commit `.env` or anything truly-ignored.** If `git status` shows a truly-ignored file as staged, something is wrong with `.gitignore` — fix that first, don't commit the secret.
83
+ - **Do not commit `secrets.json`, `.env`, or anything truly-ignored.** If `git status` shows a truly-ignored file as staged, something is wrong with `.gitignore` — fix that first, don't commit the secret.
84
84
  - **Do not commit `sessions/` or `memory/` either, even though `git log` shows them.** They're system-managed: TypeClaw's auto-backup and dreaming subagent own those commits. If you find one of them staged in your working tree, unstage it (`git restore --staged sessions/ memory/`) — your edit got mixed up with the runtime's domain.
85
85
  - **Do not bundle unrelated changes.** One commit, one decision.
86
86
 
@@ -718,7 +718,7 @@ Plugin `ToolContext` is `{ signal, sessionId, agentDir, logger }`. There is no `
718
718
  - `session.prompt`: `src/agent/index.ts` `createResourceLoader` (after default prompt assembly)
719
719
  - `session.idle`: `src/server/index.ts` `drain()` — fires immediately after every `session.prompt()` resolves (success or error)
720
720
  - `session.start`/`session.end`: `src/server/index.ts` ws open/close
721
- - `tool.before`/`tool.after`: `src/agent/plugin-tools.ts` `wrapPluginTool`, `wrapSystemTool`, and `wrapSystemAgentTool`
721
+ - `tool.before`/`tool.after`: `src/agent/plugin-tools.ts` `wrapPluginTool`, `wrapSystemTool`, `wrapSystemAgentTool`, and `wrapAgentToolAsCustomToolDefinition`. The last one is the load-bearing path for pi's builtin coding tools (`read`/`bash`/`edit`/`write`/`grep`/`find`/`ls`): pi-coding-agent 0.67.3 treats `createAgentSession({ tools })` as a name filter only, so the wrapping has to ride in `customTools` to actually override the builtin implementations. See the top-of-file contract block in `plugin-tools.ts` for the full reasoning.
722
722
  - **Schema additions**: `src/config/config.ts` (`plugins` array, `.catchall(z.unknown())` for per-plugin blocks, `extractPluginConfigs`)
723
723
 
724
724
  ### Audit log on boot