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
8 changes: 4 additions & 4 deletions FORK_PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ When a conflict occurs during upstream merge, it passes through these resolvers

## 2026-04-17 β€” Claude Code bridge (Enchanted Mountain)

New fork-only plugin `extensions/tinkerclaw-cc-bridge/` registers a provider
New fork-only plugin `extensions/tinkerclaw-tinker-bridge/` registers a provider
`claude-code` that drives the real `claude` CLI as a persistent subprocess
per session. Inherits the Claude Code OAuth at `~/.claude/.credentials.json`
β€” flat-rate entitlement, no API key.
Expand All @@ -41,8 +41,8 @@ Plugin contents (all fork-only, so no merge collisions expected):

Config additions in `~/.openclaw/openclaw.json`:

- `plugins.allow` += `"tinkerclaw-cc-bridge"`
- `plugins.entries.tinkerclaw-cc-bridge.enabled = true`
- `plugins.allow` += `"tinkerclaw-tinker-bridge"`
- `plugins.entries.tinkerclaw-tinker-bridge.enabled = true`
- `auth.profiles.claude-code:oauth`
- `auth.order.claude-code = ["claude-code:oauth"]`
- `models.providers.claude-code` (baseUrl `local://claude-cli`, 3 models)
Expand All @@ -62,7 +62,7 @@ Risks / known-gaps:
been banned in the wild but is policy-forbidden. Accepted risk.

No core files modified. No new fork patch functions needed in the wiring
script. Plugin lives entirely under `extensions/tinkerclaw-cc-bridge/`.
script. Plugin lives entirely under `extensions/tinkerclaw-tinker-bridge/`.

## 2026-04-15 β€” Silent-failure trio

Expand Down
4 changes: 2 additions & 2 deletions FORK_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

The repo (`~/src/tinkerclaw/`) is upstream's. `git pull` will rewrite it. Your persona, briefing, recipes, and any prompt overrides go in `~/.openclaw/workspace/` β€” outside the repo, where `git pull` cannot reach.

The bundled defaults at `extensions/tinkerclaw-cc-bridge/{personas,prompts}/` are what you get on day 0: JARVIS persona, working briefing on `/new`, full grandma-proof tool narration. They work without any setup. To make Jarvis your own, drop a file at `~/.openclaw/workspace/SOUL.md` (overrides the persona) or `~/.openclaw/workspace/BRIEFING.md` (overrides the briefing template). The gateway always prefers your workspace file over the bundled default; `git pull` keeps refreshing the bundle without ever touching your override.
The bundled defaults at `extensions/tinkerclaw-tinker-bridge/{personas,prompts}/` are what you get on day 0: JARVIS persona, working briefing on `/new`, full grandma-proof tool narration. They work without any setup. To make Jarvis your own, drop a file at `~/.openclaw/workspace/SOUL.md` (overrides the persona) or `~/.openclaw/workspace/BRIEFING.md` (overrides the briefing template). The gateway always prefers your workspace file over the bundled default; `git pull` keeps refreshing the bundle without ever touching your override.

Resolution order for every overridable prompt:

```
1. Explicit config in ~/.openclaw/openclaw.json (outside repo)
2. ~/.openclaw/workspace/<file> (outside repo)
3. extensions/tinkerclaw-cc-bridge/.../<file> (in repo, bundled default)
3. extensions/tinkerclaw-tinker-bridge/.../<file> (in repo, bundled default)
```

See `TINKER_UI_DESIGN_BIBLE.md` Β§5.76 for the full contract, the "Sam test" (fresh-clone day-0 experience) and the "Day-90 test" (existing user `git pull` safety).
Expand Down
97 changes: 36 additions & 61 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions TINKER_UI_DESIGN_BIBLE/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This directory IS the bible. The monolithic `TINKER_UI_DESIGN_BIBLE.md` was spli
- "what is this config key, who reads it, who can override?" β†’ `config-shape.md`
- "when X breaks, where does the symptom appear?" β†’ `failures.md`
- "what probes exist for inspecting Y?" β†’ `probes.md`
- "why is cc-bridge's tool loop different?" β†’ `tool-loop.md`
- "why is tinker-bridge's tool loop different?" β†’ `tool-loop.md`
- "which model gets picked, in what order?" β†’ `auth-routing.md`
- "which crons run, when, last status?" β†’ `crons.md`
- "is this string safe to publish?" β†’ `pii-boundary.md`
Expand Down Expand Up @@ -63,7 +63,7 @@ This directory IS the bible. The monolithic `TINKER_UI_DESIGN_BIBLE.md` was spli
| `config-shape.md` | Settings flow from openclaw.json to runtime; override chains; dead-code traps | mixed | ~400 |
| `failures.md` | Failure-mode propagation maps | hand | ~300 |
| `probes.md` | Inspection primitives registry (live + proposed) | auto from gateway methods + hand | ~150 |
| `tool-loop.md` | Why cc-bridge tool calls don't round-trip through OpenClaw exec | hand | ~150 |
| `tool-loop.md` | Why tinker-bridge tool calls don't round-trip through OpenClaw exec | hand | ~150 |
| `auth-routing.md` | Cost-aware model routing, failover, billing tiers | mixed | ~150 |
| `crons.md` | Cron registry + auto-merge policy | auto from jobs.json + hand | ~150 |
| `pii-boundary.md` | Public-OK vs private-only, leak-grep regex, sanitization workflow | hand | ~100 |
Expand Down
25 changes: 24 additions & 1 deletion TINKER_UI_DESIGN_BIBLE/auth-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ verify:
cmd: python3 -c 'import json,os; cfg=json.load(open(os.path.expanduser("~/.openclaw/openclaw.json"))); p=cfg["agents"]["defaults"]["model"]["primary"]; assert p.startswith("claude-code/"), f"primary {p} is metered, not the flat-rate subscription"'
- name: primary model is the best-ranked subscription model (rank table is cron-updated daily β€” derive it per design-principles.md, never freeze a model name)
cmd: python3 -c 'import json,os; cfg=json.load(open(os.path.expanduser("~/.openclaw/openclaw.json"))); d=cfg["agents"]["defaults"]; p=d["model"]["primary"]; subs={k:v["rank"] for k,v in d["models"].items() if k.startswith("claude-code/") and isinstance(v,dict) and "rank" in v}; best=min(subs,key=subs.get); assert p==best, f"primary {p} != best-ranked subscription model {best} @ rank {subs[best]}"'
- name: all four thinking-level resolution sites clamp via resolveSupportedThinkingLevel (none rejects an over-ceiling level)
cmd: python3 -c 'import os; r=os.path.expanduser("~/src/tinkerclaw/src"); sites=["auto-reply/reply/get-reply-run.ts","auto-reply/reply/directive-handling.impl.ts","gateway/sessions-patch.ts","agents/agent-command.ts"]; [exec("t=open(os.path.join(r,s)).read(); assert \"resolveSupportedThinkingLevel\" in t, s+\": resolveSupportedThinkingLevel call missing β€” over-ceiling thinking level may hard-reject again\"") for s in sites]; sp=open(os.path.join(r,"gateway/sessions-patch.ts")).read(); assert "next.thinkingLevel = resolveSupportedThinkingLevel" in sp, "sessions-patch.ts no longer clamps the persisted thinkingLevel"'
---

# Auth + model routing
Expand All @@ -23,6 +25,8 @@ Source of truth: `agents.defaults.models[<provider/model>].rank` in `openclaw.js

**The primary is DERIVED, not frozen (design-principles.md #19).** `agents.defaults.model.primary` is the **best-ranked _subscription_ (cli-gm / `claude-code/*`) model** β€” never a metered model, however highly the leaderboard ranks it. The rank numbers churn daily; the routing rule does not. The frontmatter `verify:` enforces the derived rule (primary is a `claude-code/*` model AND equals the lowest-rank `claude-code/*` entry), so it survives a new model landing at the top instead of re-breaking on every cron run. The table below is a dated snapshot, illustrative only.

**Live-config rank decision (2026-06-23, in `~/.openclaw/openclaw.json`, uncommitted config β€” NOT a tinkerclaw code change):** `agents.defaults.models` ranks were set so `claude-code/claude-opus-4-8` = **rank 1** (matching `agents.defaults.model.primary`) and the **unavailable** `claude-code/claude-fable-5` was demoted to **rank 25**. This makes the derived-primary `verify:` (line below β€” primary == best-ranked `claude-code/*` entry) pass: with fable-5 no longer the lowest-rank subscription model, the best-ranked subscription model is opus-4-8, which IS the primary. The `verify:` reads `~/.openclaw/openclaw.json` **live** (not a snapshot), so this config change alone flips the gate green β€” no fork code or bible regeneration is needed. Fable 5 is export-controlled / UNAVAILABLE, so it must never be allowed to win the best-rank derivation.

Current snapshot (2026-06-10):

| Rank | Model | Alias | Tier | Notes |
Expand Down Expand Up @@ -55,7 +59,7 @@ Current snapshot (2026-06-10):

### claude-code (Anthropic subscription via claude-cli)

- **Driver:** `tinkerclaw-cc-bridge` plugin β†’ claude-cli subprocess.
- **Driver:** `tinkerclaw-tinker-bridge` plugin β†’ claude-cli subprocess.
- **Auth profile:** `anthropic:cli-gm` (OAuth, `~/.claude/.credentials-gm.json`).
- **Order:** `[cli-gm]` only. The metered `anthropic:api` profile is DISABLED in `auth.order.anthropic`.
- **Tier:** subscription (max_20x at $200/month per `env.ANTHROPIC_SUBSCRIPTION_TIER`).
Expand Down Expand Up @@ -83,6 +87,25 @@ Current snapshot (2026-06-10):
- **Base URL:** `http://127.0.0.1:11434`.
- **Tier:** free / local. Currently used only for `mxbai-embed-large` (memorySearch embeddings), not for chat.

## Thinking-level clamp β€” unsupported levels clamp, never reject (cross-model, FORK 2026-06-24)

Each model exposes a thinking profile (the ordered set of levels it admits, ranked by `THINKING_LEVEL_RANKS` in `thinking.shared.ts`). The effort slider's top stop is **Max**, but not every model admits `max` β€” e.g. `openai/gpt-5.5` tops out at `xhigh`. When the requested level exceeds a model's ceiling, the resolver **clamps DOWN to that model's highest supported level and proceeds** β€” it never hard-errors the turn.

The canonical resolver is `resolveSupportedThinkingLevel({ provider, model, level, catalog })` (`src/auto-reply/thinking.ts`): if the level is in the profile it passes through, otherwise it returns the highest profile level whose rank `<=` the requested rank (falling back to the highest non-`off` level, then `off`). This is the single source of truth for "what level does this model actually get."

There are FOUR resolution sites where a requested level meets a model that may not support it; ALL FOUR clamp via `resolveSupportedThinkingLevel` (none rejects):

| Path | Site | Surfaces the clamp via |
| ------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------- |
| chat.send / Tinker | `get-reply-run.ts` (~:630) | `logVerbose` info note (no ack channel on this path) |
| `/think` directive | `directive-handling.impl.ts` (~:318) | ack note, guarded on `requested !== applied` |
| persisted `thinkingLevel` patch | `sessions-patch.ts` (~:512) | silent clamp β€” the patch always succeeds (previously a `"thinkingLevel" in patch β†’ invalid` REJECT) |
| CLI `agent` | `agent-command.ts` (~:862) | stderr note |

**Why the slider could trigger a reject:** the slider's Max injects an **EXPLICIT** `/think max` directive (`chat-command-body.ts`), so the resolver classified it as explicit and (pre-fix) took a reject branch instead of the clamp that already existed for the non-explicit case. The fix unified all four sites to clamp regardless of explicit-vs-derived. The slider's Max is a **ceiling request**, not a contract the model must honor exactly. Models that DO support `max` (`claude-code/*`) are unaffected. This is the cross-model analogue of the 2026-06-19 `claude-code` thinking-profile gate (which rejected a level the model DID support because its profile was missing β€” opposite cause, same "reject instead of admit/clamp" symptom). See bug-log.md `### FIXED [think-clamp+detection-pattern]` (2026-06-24).

The "all four sites clamp via `resolveSupportedThinkingLevel`, none rejects" contract is enforced by this file's frontmatter `verify:` block (asserts each of the four source files calls the resolver and that `sessions-patch.ts` still clamps the persisted level).

## Failover and cost-aware routing

The fork patched the upstream failover bug 2026-02-19 (bible Β§11.x):
Expand Down
Loading
Loading