typeclaw 0.6.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.
- package/README.md +29 -77
- package/package.json +1 -1
- package/scripts/dump-system-prompt.ts +12 -1
- package/src/agent/auth.ts +3 -3
- package/src/agent/index.ts +61 -8
- package/src/agent/multimodal/read-redirect.ts +43 -0
- package/src/agent/plugin-tools.ts +95 -13
- package/src/agent/session-origin.ts +6 -13
- package/src/agent/system-prompt.ts +41 -7
- package/src/channels/adapters/discord-bot-slash-commands.ts +186 -0
- package/src/channels/adapters/discord-bot.ts +163 -1
- package/src/channels/adapters/slack-bot-slash-commands.ts +82 -0
- package/src/channels/adapters/slack-bot.ts +139 -1
- package/src/channels/router.ts +127 -1
- package/src/cli/init.ts +8 -1
- package/src/cli/oauth-callbacks.ts +64 -34
- package/src/cli/provider.ts +9 -4
- package/src/cli/role.ts +7 -2
- package/src/cli/tunnel.ts +13 -1
- package/src/cli/ui.ts +25 -1
- package/src/config/config.ts +28 -4
- package/src/config/index.ts +1 -0
- package/src/config/models-mutation.ts +10 -2
- package/src/config/providers.ts +106 -0
- package/src/init/dockerfile.ts +89 -2
- package/src/init/models-dev.ts +1 -0
- package/src/shared/index.ts +1 -1
- package/src/shared/local-time.ts +17 -0
- package/src/skills/typeclaw-claude-code/SKILL.md +30 -5
- package/src/skills/typeclaw-config/SKILL.md +37 -32
- package/src/skills/typeclaw-git/SKILL.md +2 -2
- package/src/skills/typeclaw-plugins/SKILL.md +1 -1
- package/typeclaw.schema.json +6 -0
package/src/config/providers.ts
CHANGED
|
@@ -108,6 +108,112 @@ export const KNOWN_PROVIDERS = {
|
|
|
108
108
|
},
|
|
109
109
|
},
|
|
110
110
|
},
|
|
111
|
+
// Anthropic Claude — both the Anthropic Console API (ANTHROPIC_API_KEY)
|
|
112
|
+
// and Claude Pro/Max/Team/Enterprise subscriptions (OAuth) reach the same
|
|
113
|
+
// /v1/messages endpoint and share one provider id. Auth path determines
|
|
114
|
+
// which headers pi-ai's `anthropic-messages` transport injects: API key
|
|
115
|
+
// sends a plain `x-api-key`; OAuth sends Bearer + Claude Code identity
|
|
116
|
+
// (anthropic-beta: claude-code-20250219,oauth-2025-04-20 +
|
|
117
|
+
// user-agent: claude-cli/<version>), which is exactly the surface a
|
|
118
|
+
// subscriber's `claude setup-token` credential authorizes. The OAuth dance
|
|
119
|
+
// itself is authorization-code + PKCE against `claude.ai/oauth/authorize`
|
|
120
|
+
// with a localhost callback server (not device-code); the existing
|
|
121
|
+
// `typeclaw-claude-code` skill documents the user-side flow for getting
|
|
122
|
+
// a subscription credential onto the agent when the in-container browser
|
|
123
|
+
// callback can't reach the user's machine.
|
|
124
|
+
//
|
|
125
|
+
// anthropic is the FIRST provider in the registry where both auth modes
|
|
126
|
+
// coexist on one entry. The runtime in src/agent/auth.ts has a load-bearing
|
|
127
|
+
// resolution rule: when secrets.json#providers.anthropic carries an OAuth
|
|
128
|
+
// credential, `ANTHROPIC_API_KEY` in .env is IGNORED (OAuth-on-disk wins
|
|
129
|
+
// because env-wins only applies to api-key-shaped credentials). For
|
|
130
|
+
// api-key-only providers this is invisible; for anthropic it surfaces as
|
|
131
|
+
// "I added the env var but the agent still uses OAuth." The mitigation is
|
|
132
|
+
// to remove the OAuth credential explicitly (`typeclaw provider remove
|
|
133
|
+
// anthropic`) before relying on the env-var path. Same rule applies to any
|
|
134
|
+
// future dual-auth provider — keep the surprise in mind when expanding.
|
|
135
|
+
//
|
|
136
|
+
// Model lineup is the current GA tier as of 2026-04-16: Opus 4.7 (top,
|
|
137
|
+
// released Apr 16 2026), Sonnet 4.6 (mid, Feb 5 2026), Haiku 4.5 (fast,
|
|
138
|
+
// Oct 1 2025). Anthropic's own model overview lists these three as the
|
|
139
|
+
// current recommended set and flags earlier Opus/Sonnet variants with
|
|
140
|
+
// "Consider migrating to current models." Opus 4 / Sonnet 4 are deprecated
|
|
141
|
+
// (retirement: Jun 15 2026); the 4.5/4.6 alternates remain Active but are
|
|
142
|
+
// not the recommended path.
|
|
143
|
+
//
|
|
144
|
+
// ID semantics differ across the lineup and matter for forward-compat:
|
|
145
|
+
// - `claude-haiku-4-5` is a 4.5-generation CONVENIENCE ALIAS that
|
|
146
|
+
// resolves to the latest dated snapshot (currently `-20251001`). Per
|
|
147
|
+
// Anthropic's model-id docs, pre-4.6 dateless ids are evergreen
|
|
148
|
+
// pointers — Anthropic can ship a new dated snapshot under the same
|
|
149
|
+
// alias and we pick it up automatically.
|
|
150
|
+
// - `claude-sonnet-4-6` and `claude-opus-4-7` are 4.6+-generation PINNED
|
|
151
|
+
// SNAPSHOTS, not aliases. Anthropic explicitly says "the dateless ID is
|
|
152
|
+
// the canonical model ID for that release. It maps to a single, fixed
|
|
153
|
+
// model snapshot." A future Sonnet 4.6.1 (if it ever exists) would ship
|
|
154
|
+
// under a new id, NOT silently replace `claude-sonnet-4-6`.
|
|
155
|
+
// Consequence for refresh discipline: bumping Haiku is a no-op (alias
|
|
156
|
+
// catches the latest); bumping Sonnet/Opus to a future 4.7+ family is a
|
|
157
|
+
// real edit here. Don't assume `claude-opus-4-7` will silently advance.
|
|
158
|
+
//
|
|
159
|
+
// Opus 4.7 specifics that affect cost accounting:
|
|
160
|
+
// - New tokenizer: same input maps to 1.0-1.3x more tokens than prior
|
|
161
|
+
// generations depending on content type. Per-token price is unchanged
|
|
162
|
+
// vs Opus 4.6, but total cost on identical workloads can rise meaningfully.
|
|
163
|
+
// - 1M token context window (vs 200k on Haiku) and 128k max output (vs
|
|
164
|
+
// 64k on Sonnet/Haiku). 1M context is at standard pricing — no surcharge.
|
|
165
|
+
// - New `xhigh` effort level between `high` and `max` (pi-ai 0.67.x may
|
|
166
|
+
// not surface this knob yet; check before relying on it).
|
|
167
|
+
//
|
|
168
|
+
// Pricing mirrors Anthropic's official table as of 2026-05; cacheWrite is
|
|
169
|
+
// the 5m-TTL rate (1.25x input). 1h TTL is ~2x input (not modeled here —
|
|
170
|
+
// pi-ai's `cacheWrite` field captures the default 5m rate only).
|
|
171
|
+
anthropic: {
|
|
172
|
+
id: 'anthropic',
|
|
173
|
+
name: 'Anthropic',
|
|
174
|
+
baseUrl: 'https://api.anthropic.com',
|
|
175
|
+
auth: ['api-key', 'oauth'],
|
|
176
|
+
apiKeyEnv: 'ANTHROPIC_API_KEY',
|
|
177
|
+
oauthProviderId: 'anthropic',
|
|
178
|
+
models: {
|
|
179
|
+
'claude-haiku-4-5': {
|
|
180
|
+
id: 'claude-haiku-4-5',
|
|
181
|
+
name: 'Claude Haiku 4.5',
|
|
182
|
+
api: 'anthropic-messages',
|
|
183
|
+
provider: 'anthropic',
|
|
184
|
+
baseUrl: 'https://api.anthropic.com',
|
|
185
|
+
reasoning: true,
|
|
186
|
+
input: ['text', 'image'],
|
|
187
|
+
cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
188
|
+
contextWindow: 200000,
|
|
189
|
+
maxTokens: 64000,
|
|
190
|
+
},
|
|
191
|
+
'claude-sonnet-4-6': {
|
|
192
|
+
id: 'claude-sonnet-4-6',
|
|
193
|
+
name: 'Claude Sonnet 4.6',
|
|
194
|
+
api: 'anthropic-messages',
|
|
195
|
+
provider: 'anthropic',
|
|
196
|
+
baseUrl: 'https://api.anthropic.com',
|
|
197
|
+
reasoning: true,
|
|
198
|
+
input: ['text', 'image'],
|
|
199
|
+
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
200
|
+
contextWindow: 1000000,
|
|
201
|
+
maxTokens: 64000,
|
|
202
|
+
},
|
|
203
|
+
'claude-opus-4-7': {
|
|
204
|
+
id: 'claude-opus-4-7',
|
|
205
|
+
name: 'Claude Opus 4.7',
|
|
206
|
+
api: 'anthropic-messages',
|
|
207
|
+
provider: 'anthropic',
|
|
208
|
+
baseUrl: 'https://api.anthropic.com',
|
|
209
|
+
reasoning: true,
|
|
210
|
+
input: ['text', 'image'],
|
|
211
|
+
cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
212
|
+
contextWindow: 1000000,
|
|
213
|
+
maxTokens: 128000,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
111
217
|
// ChatGPT Plus/Pro subscription via the OAuth Codex backend. No API key
|
|
112
218
|
// path here on purpose — the Codex backend is OAuth-only upstream.
|
|
113
219
|
//
|
package/src/init/dockerfile.ts
CHANGED
|
@@ -394,14 +394,101 @@ RUN echo "${encoded}" | base64 -d > ${TYPECLAW_ENTRYPOINT_PATH} \\
|
|
|
394
394
|
// `~/.local/bin/claude` shim, which itself dereferences to the versioned
|
|
395
395
|
// binary under `~/.local/share/claude/versions/<ver>/`, so upgrades via
|
|
396
396
|
// `claude update` keep working without re-running this layer.
|
|
397
|
+
// `~/.claude.json` is Claude Code's internal state file (NOT
|
|
398
|
+
// `~/.claude/settings.json`, which is user-facing). On first run with an
|
|
399
|
+
// empty or missing file, `claude` enters a TTY-only theme picker:
|
|
400
|
+
// "Welcome to Claude Code … Choose the text style that looks best with
|
|
401
|
+
// your terminal" with 7 options. The picker is unskippable via CLI
|
|
402
|
+
// flags or env vars (no `--skip-onboarding`, no `--theme=dark`;
|
|
403
|
+
// `IS_DEMO=1` exists but has documented side effects). The single
|
|
404
|
+
// official escape hatch is writing `{"hasCompletedOnboarding": true,
|
|
405
|
+
// "theme": "dark"}` to `~/.claude.json` before the first launch —
|
|
406
|
+
// confirmed by Anthropic in multiple GitHub issues
|
|
407
|
+
// (anthropics/claude-code#4714, #8938, #13827) and the empirical
|
|
408
|
+
// answer used by metabase/metabase's `bin/claude-dangerous`, the
|
|
409
|
+
// `claudeCodeAlDevContainer` feature, and dozens of other Docker
|
|
410
|
+
// integrations.
|
|
411
|
+
//
|
|
412
|
+
// Without the pre-seed, the very first agent-driven `tmux new-session …
|
|
413
|
+
// claude` invocation hangs on the theme picker: the agent's
|
|
414
|
+
// `send-keys "<prompt>" Enter` arrives at the picker, gets interpreted
|
|
415
|
+
// as picker input, and never reaches claude's actual prompt. The
|
|
416
|
+
// `typeclaw-claude-code` skill is structured around a `Stop`-hook
|
|
417
|
+
// sentinel, which never fires while the picker is up, so the polling
|
|
418
|
+
// loop only learns of the hang at the 10-minute wall-clock budget.
|
|
419
|
+
// Pre-seeding here costs ~85 bytes on disk and zero runtime overhead.
|
|
420
|
+
//
|
|
421
|
+
// SCOPE: this seed is NECESSARY but not SUFFICIENT for a fully
|
|
422
|
+
// no-questions-asked first launch. Claude Code also shows two
|
|
423
|
+
// post-seed modal dialogs that this file deliberately does NOT
|
|
424
|
+
// pre-clear:
|
|
425
|
+
// 1. "Detected a custom API key from environment. Do you want to use
|
|
426
|
+
// this API key?" — fires when ANTHROPIC_API_KEY is set. Options
|
|
427
|
+
// `[No (recommended), Yes]`, focus on No, picker does NOT wrap.
|
|
428
|
+
// 2. Workspace trust ("Do you trust the files in this folder?") —
|
|
429
|
+
// fires on every new cwd. Options `[Yes, proceed, No, exit]`,
|
|
430
|
+
// focus on Yes.
|
|
431
|
+
// Both are kept as runtime decisions handled by the
|
|
432
|
+
// `typeclaw-claude-code` skill (see its "Driving the session" section,
|
|
433
|
+
// "Clear startup dialogs" step, which uses dialog-specific keystrokes
|
|
434
|
+
// because the picker doesn't wrap). Pre-seeding
|
|
435
|
+
// `hasTrustDialogAccepted` or `customApiKeyResponses.approved` here
|
|
436
|
+
// would silently widen the trust surface in ways the operator hasn't
|
|
437
|
+
// consented to — the seed's job is strictly cosmetic-wizard removal,
|
|
438
|
+
// not trust/permission preemption.
|
|
439
|
+
//
|
|
440
|
+
// `theme: "dark"` matches typeclaw's default TUI theme so the visual
|
|
441
|
+
// transition between the typeclaw TUI and a tmux-attached claude pane
|
|
442
|
+
// is consistent. Users on light terminals can override by editing
|
|
443
|
+
// `~/.claude.json` (which persists across container restarts only if
|
|
444
|
+
// they mount it; in the default container-ephemeral state it resets
|
|
445
|
+
// to this default on every rebuild, which is fine — `claude` reads
|
|
446
|
+
// the file at startup and the theme has no behavioral impact).
|
|
447
|
+
//
|
|
448
|
+
// `lastOnboardingVersion` is INTENTIONALLY OMITTED. ii-agent and a
|
|
449
|
+
// few other templates ship `lastOnboardingVersion: "1.0.30"`, but
|
|
450
|
+
// that value is version-coupled and goes stale on every Claude Code
|
|
451
|
+
// release. Empirically against Claude Code 2.1.146, the current
|
|
452
|
+
// `hasCompletedOnboarding: true` alone is honored without a version
|
|
453
|
+
// pin. If a future Claude version starts re-triggering the picker
|
|
454
|
+
// when the field is missing, capture `claude --version` output at
|
|
455
|
+
// build time and inject it then — don't hardcode a stale value.
|
|
456
|
+
//
|
|
457
|
+
// `installMethod: "native"` and `numStartups: 1` match the shape
|
|
458
|
+
// Claude Code itself writes after a clean first launch; keeping them
|
|
459
|
+
// makes our seed indistinguishable from a real post-onboarding state,
|
|
460
|
+
// which minimizes the chance of a future "if the file looks like
|
|
461
|
+
// agent-pre-seed, redo onboarding" detection heuristic landing on us.
|
|
462
|
+
//
|
|
463
|
+
// Built via `JSON.stringify` rather than a hand-written string
|
|
464
|
+
// literal so quote/escape bugs surface as TS errors at compile time,
|
|
465
|
+
// not as a corrupt `~/.claude.json` discovered only when the build
|
|
466
|
+
// runs. The `printf '%s\\n' '<JSON>'` shell pattern relies on the
|
|
467
|
+
// JSON containing no single quotes (true by construction — JSON.
|
|
468
|
+
// stringify only emits double quotes); a regression test parses the
|
|
469
|
+
// emitted JSON back to confirm.
|
|
470
|
+
const CLAUDE_CODE_ONBOARDING_SEED = JSON.stringify({
|
|
471
|
+
hasCompletedOnboarding: true,
|
|
472
|
+
theme: 'dark',
|
|
473
|
+
installMethod: 'native',
|
|
474
|
+
numStartups: 1,
|
|
475
|
+
})
|
|
476
|
+
|
|
397
477
|
function renderClaudeCodeInstallLayer(enabled: boolean): string {
|
|
398
478
|
if (!enabled) return ''
|
|
399
479
|
return `# Layer 5.6 (toggle): install Anthropic's Claude Code CLI. Opt-in via
|
|
400
480
|
# typeclaw.json#docker.file.claudeCode. The skill \`typeclaw-claude-code\`
|
|
401
|
-
# documents the auth + usage flow.
|
|
481
|
+
# documents the auth + usage flow. Pre-seed ~/.claude.json so the first
|
|
482
|
+
# launch skips the TTY-only theme picker; see CLAUDE_CODE_ONBOARDING_SEED
|
|
483
|
+
# above for the rationale and what the seed deliberately does NOT cover.
|
|
484
|
+
# The seed write runs LAST in the chain so the final layer state is
|
|
485
|
+
# exactly the seeded config — independent of whether any earlier command
|
|
486
|
+
# (or a future Claude version's \`--version\` smoke test) writes a
|
|
487
|
+
# default \`~/.claude.json\` partway through the layer.
|
|
402
488
|
RUN curl -fsSL https://claude.ai/install.sh | bash \\
|
|
403
489
|
&& ln -sf "$HOME/.local/bin/claude" /usr/local/bin/claude \\
|
|
404
|
-
&& claude --version > /dev/null
|
|
490
|
+
&& claude --version > /dev/null \\
|
|
491
|
+
&& printf '%s\\n' '${CLAUDE_CODE_ONBOARDING_SEED}' > "$HOME/.claude.json"`
|
|
405
492
|
}
|
|
406
493
|
|
|
407
494
|
// Shared-library runtime deps Chrome for Testing needs to launch on amd64
|
package/src/init/models-dev.ts
CHANGED
|
@@ -13,6 +13,7 @@ const PROVIDER_TO_MODELS_DEV: Record<KnownProviderId, string> = {
|
|
|
13
13
|
// (Codex is a backend, not a separate provider in their taxonomy). Curated
|
|
14
14
|
// entries are surfaced regardless of upstream membership.
|
|
15
15
|
'openai-codex': 'openai',
|
|
16
|
+
anthropic: 'anthropic',
|
|
16
17
|
fireworks: 'fireworks-ai',
|
|
17
18
|
zai: 'zai',
|
|
18
19
|
// zai-coding (GLM Coding Plan) is a billing surface, not a separate model
|
package/src/shared/index.ts
CHANGED
package/src/shared/local-time.ts
CHANGED
|
@@ -19,3 +19,20 @@ function formatTimezoneOffset(date: Date): string {
|
|
|
19
19
|
const abs = Math.abs(offsetMinutes)
|
|
20
20
|
return `${sign}${pad2(Math.floor(abs / 60))}:${pad2(abs % 60)}`
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
// IANA timezone name of the process (e.g. `Asia/Seoul`). Reads the resolved
|
|
24
|
+
// zone from Intl, falling back to `UTC` if the runtime cannot resolve one —
|
|
25
|
+
// this should never happen on Bun + tzdata-equipped containers, but the
|
|
26
|
+
// fallback keeps the prompt renderable rather than throwing during session
|
|
27
|
+
// creation. The returned name is what the agent shows the user when asked
|
|
28
|
+
// "what time is it" — pairing the wall clock with a recognizable zone name
|
|
29
|
+
// is what disambiguates "15:31 +09:00" from "15:31 KST" for a non-technical
|
|
30
|
+
// reader.
|
|
31
|
+
export function resolveLocalTimezoneName(): string {
|
|
32
|
+
try {
|
|
33
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
34
|
+
return zone && zone.length > 0 ? zone : 'UTC'
|
|
35
|
+
} catch {
|
|
36
|
+
return 'UTC'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -9,6 +9,12 @@ You can delegate work to Claude Code, Anthropic's official coding agent. The age
|
|
|
9
9
|
|
|
10
10
|
This skill is for the case where Claude Code is the right tool: hard architecture work, multi-file refactors, deep code analysis, a second-opinion read on something you wrote. It is **not** for trivial edits — the round-trip cost (worktree setup + process spawn + auth check + TUI init + at least one full Claude turn) is 15–45 seconds and several thousand tokens of someone else's context window. Do trivial edits yourself.
|
|
11
11
|
|
|
12
|
+
## Run the delegation inside `operator`, not inline
|
|
13
|
+
|
|
14
|
+
Once you've decided Claude Code is the right tool, spawn the bundled `operator` subagent to do the actual driving — don't run the worktree setup, the tmux session, the polling loop, the multi-turn decision loop, and the cleanup inline in your own context. The whole loop typically takes several minutes and produces large amounts of intermediate output (TUI buffer captures, Stop sentinels per turn, JSONL transcript references); running it inline blocks the user from talking to you and burns through your context window before you ever get to the synthesis step. `operator` is write-capable and runs the same loop, then returns a clean final report (what claude produced, what `git diff main..cc-<id>` shows, what you should review). You ship the worktree, the prompt, and the safety constraints to operator; operator ships you back the diff and the summary.
|
|
15
|
+
|
|
16
|
+
Exception: a quick sanity ping (`claude --version` to check the binary exists, `env | grep ANTHROPIC` to check auth). Those are single fast bash calls — do them inline. The "spawn through operator" rule applies to anything that runs `claude` itself as an interactive TUI.
|
|
17
|
+
|
|
12
18
|
## When to delegate to Claude Code
|
|
13
19
|
|
|
14
20
|
Use Claude Code for:
|
|
@@ -79,6 +85,7 @@ Before you spawn `claude` for any real work:
|
|
|
79
85
|
- **`docker.file.claudeCode: true`** in `typeclaw.json`. Verify with `which claude`; if missing, the toggle isn't on. Tell the user to enable it and `typeclaw start --build`.
|
|
80
86
|
- **`docker.file.tmux: true`** (default `true`, but check). Verify with `which tmux`.
|
|
81
87
|
- **Auth set up** — see above. Verify with `env | grep -E '^(ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN)='`.
|
|
88
|
+
- **Onboarding pre-seeded.** The Dockerfile layer writes `~/.claude.json` with `hasCompletedOnboarding: true` and `theme: "dark"` so the first `claude` invocation skips the TTY-only theme picker / welcome wizard. **This is necessary but not sufficient** — even with the seed, Claude Code can still land on two other pre-prompt modals: the "Detected a custom API key from environment. Do you want to use this API key?" confirmation (when `ANTHROPIC_API_KEY` is set in env — default focus is **No**, so `Down Enter` is needed to accept) and the workspace trust dialog ("Do you trust the files in this folder?", default focus already on **Yes**, so a bare `Enter` accepts). The "Driving the session" section below clears them as a loop. If `~/.claude.json` is empty or missing entirely (custom mount, manual `rm`, a `CLAUDE_CONFIG_DIR` pointing at a fresh directory), the theme picker also reappears. Self-heal: `printf '%s\n' '{"hasCompletedOnboarding":true,"theme":"dark","installMethod":"native","numStartups":1}' > "$HOME/.claude.json"` before spawning, then retry.
|
|
82
89
|
- **Agent folder is a git repo.** Verify with `git -C /agent rev-parse --is-inside-work-tree`. The worktree model below requires it. If the user's agent folder somehow isn't a repo (rare — `typeclaw init` scaffolds one), tell them to `git init && git add -A && git commit -m "initial"` first.
|
|
83
90
|
- **No uncommitted changes that you care about.** `git -C /agent status --porcelain` should be clean, or you should be willing to set the working tree aside before delegating. The worktree is a separate checkout, so claude can't see your uncommitted changes — meaning claude operates on the last committed state. If the user wants claude to work with in-progress edits, commit them first (even on a WIP branch).
|
|
84
91
|
|
|
@@ -165,11 +172,29 @@ The minimum protocol — translate to your actual tool calls:
|
|
|
165
172
|
1. Create the worktree, write the hook config (above).
|
|
166
173
|
2. `tmux new-session -d -s cc-<id> -c /tmp/cc-<id> claude`.
|
|
167
174
|
3. Wait ~3 seconds for the TUI to initialize.
|
|
168
|
-
4. `
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
4. **Clear startup dialogs (BEFORE sending the task prompt).** Even with `~/.claude.json` pre-seeded, claude can land on one or both pre-prompt modals. Run this as a **loop**, not a one-shot: clearing one dialog can immediately reveal the next, and you must keep polling until claude's actual input prompt is visible (it renders a bottom-of-pane input box with a `╭` / `╰` border).
|
|
176
|
+
|
|
177
|
+
The two known modals, with the exact keystrokes for each (Claude Code's select widget does NOT wrap — pressing `Up` from the first option is a no-op, so the direction must match the dialog's option order):
|
|
178
|
+
- **Custom API key confirmation** — "Detected a custom API key from environment. Do you want to use this API key?" Fires when `ANTHROPIC_API_KEY` is set (exactly typeclaw's auth path). Options are `[No (recommended), Yes]` with focus initialized on **No**. Resolution: `tmux send-keys -t cc-<id> Down Enter` to advance to **Yes** and submit. Sending `Up Enter` would submit the **No** answer, which can persist as a rejection in `customApiKeyResponses.rejected` and break subsequent launches — never do that here.
|
|
179
|
+
|
|
180
|
+
- **Workspace trust** — "Do you trust the files in this folder?" Fires on first launch in any new cwd, so every fresh `/tmp/cc-<id>/` worktree triggers it. Options are `[Yes, proceed, No, exit]` with focus on the first option (**Yes**) by default. Resolution: bare `tmux send-keys -t cc-<id> Enter` — no arrow key needed. Always verify the pane text matches the trust dialog before pressing Enter; a misidentified modal would submit a different default.
|
|
181
|
+
|
|
182
|
+
Loop shape (translate to your tool calls):
|
|
183
|
+
1. Capture the last ~15 lines: `tmux capture-pane -t cc-<id> -p -S -15`.
|
|
184
|
+
2. If the capture contains the API key dialog text → `send-keys Down Enter`, sleep 500ms, goto 1.
|
|
185
|
+
3. If the capture contains the trust dialog text → `send-keys Enter`, sleep 500ms, goto 1.
|
|
186
|
+
4. If the capture shows the input box (`╭` border on a bottom line, no dialog text above it) → ready; exit the loop.
|
|
187
|
+
5. Otherwise sleep 500ms, goto 1. Apply a wall-clock budget of ~10 seconds; if the loop hasn't reached step 4 by then, abort with `/exit` and surface to the user — claude is in a state this skill doesn't model.
|
|
188
|
+
|
|
189
|
+
Do not use a fixed 2-second wait then send the prompt — cold-start and slow-disk cases can deliver a dialog at 2.5s+, and sending the task prompt into a modal corrupts the session.
|
|
190
|
+
|
|
191
|
+
**Safety note**: accepting workspace trust on a fresh `/tmp/cc-<id>/` worktree is the right call **only when its `HEAD` is the intended clean state** — typically the agent folder's last good commit on a branch the user controls. If the user just merged a third-party PR, pulled a remote branch, or checked out an untrusted ref, the worktree carries that content too and "trusting" it gives claude tool access on potentially hostile code. Before auto-accepting trust, sanity-check: if the user hasn't said something equivalent to "delegate this to Claude Code", or if you're not confident the current `HEAD` is one the user authored or reviewed, surface the trust dialog to them instead. Do NOT extend even a legitimate trust acceptance to in-session permission prompts (Bash, Edit, etc.) — those still need per-turn judgment per the multi-turn decision loop below.
|
|
192
|
+
|
|
193
|
+
5. `tmux send-keys -t cc-<id> "<your prompt>" Enter`.
|
|
194
|
+
6. **Poll** for `/tmp/cc-<id>/.done` in a 500ms-cadence loop with a wall-clock budget (default 10 minutes). On every iteration, also check `tmux has-session -t cc-<id>` — if the session died, claude crashed or auth failed.
|
|
195
|
+
7. When `.done` exists: `rm .done`, read `sentinel.json`, examine `last_assistant_message`.
|
|
196
|
+
8. Decide using the multi-turn loop below.
|
|
197
|
+
9. When done: `tmux send-keys -t cc-<id> "/exit" Enter && sleep 1 && tmux kill-session -t cc-<id>`.
|
|
173
198
|
|
|
174
199
|
The full polling implementation, the ANSI-handling rules for `capture-pane` fallbacks, and the "tmux session died unexpectedly" recovery path are in `references/tmux-driving.md`.
|
|
175
200
|
|
|
@@ -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
|
|
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.
|
|
520
|
-
| `openai/gpt-5.4-mini` | GPT-5.4 mini | OpenAI | API key |
|
|
521
|
-
| `openai/gpt-5.4` | GPT-5.4 | OpenAI | API key |
|
|
522
|
-
| `openai/gpt-5.5` | GPT-5.5 | OpenAI | API key | Flagship.
|
|
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 |
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 `
|
|
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
|
package/typeclaw.schema.json
CHANGED
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"openai/gpt-5.4-mini",
|
|
27
27
|
"openai/gpt-5.4",
|
|
28
28
|
"openai/gpt-5.5",
|
|
29
|
+
"anthropic/claude-haiku-4-5",
|
|
30
|
+
"anthropic/claude-sonnet-4-6",
|
|
31
|
+
"anthropic/claude-opus-4-7",
|
|
29
32
|
"openai-codex/gpt-5.4-mini",
|
|
30
33
|
"openai-codex/gpt-5.4",
|
|
31
34
|
"openai-codex/gpt-5.5",
|
|
@@ -50,6 +53,9 @@
|
|
|
50
53
|
"openai/gpt-5.4-mini",
|
|
51
54
|
"openai/gpt-5.4",
|
|
52
55
|
"openai/gpt-5.5",
|
|
56
|
+
"anthropic/claude-haiku-4-5",
|
|
57
|
+
"anthropic/claude-sonnet-4-6",
|
|
58
|
+
"anthropic/claude-opus-4-7",
|
|
53
59
|
"openai-codex/gpt-5.4-mini",
|
|
54
60
|
"openai-codex/gpt-5.4",
|
|
55
61
|
"openai-codex/gpt-5.5",
|