Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
39abf4c
docs(plan): design the Codex Set prompt composer against upstream evi…
lidge-jun Aug 2, 2026
a47a167
docs(plan): research API-side client-integration toggles (cc-switch r…
lidge-jun Aug 2, 2026
91b3921
docs(plan): rebuild the prompt-composer design around its audit blockers
lidge-jun Aug 2, 2026
921070b
docs(plan): drop the TOML dependency after measuring Bun's parser
lidge-jun Aug 2, 2026
3195c71
docs(plan): stop journal recovery from overwriting other people's edits
lidge-jun Aug 2, 2026
ef71180
docs(plan): allocate 12 must-fix bugs across 5 bugfix worktrees
lidge-jun Aug 2, 2026
c327fdb
docs(plan): make the journal one state machine and finish the WP1 con…
lidge-jun Aug 2, 2026
470618d
docs(plan): fold audit blockers into bugfix worktree units
lidge-jun Aug 2, 2026
14eae93
docs(plan): reclassify Plugins after reading the OR it actually depen…
lidge-jun Aug 2, 2026
072b8df
docs(plan): close three of the four NEAR-PASS residuals before building
lidge-jun Aug 2, 2026
60b417c
docs(plan): design the unified Integrations surface with guaranteed r…
lidge-jun Aug 2, 2026
e0b15fe
feat(codex): prompt-layer inventory and a TOML encoder we can trust
lidge-jun Aug 2, 2026
478354e
docs(plan): use ~ in example paths so privacy:scan stays green
lidge-jun Aug 2, 2026
47e7d61
docs(plan): split wt3 roadmap into per-bug decade docs with researche…
lidge-jun Aug 2, 2026
2bb3d91
docs(plan): fold audit round-1 blockers into wt3 bug-a docs
lidge-jun Aug 2, 2026
6a9b79e
docs(plan): fold audit round-2 blockers — sol in built-in set, additi…
lidge-jun Aug 2, 2026
62bbeb5
docs(plan): fold audit round-3 — uniform seven-model count, honest pr…
lidge-jun Aug 2, 2026
14c8214
fix(providers): route Copilot Responses-only models off chat completions
lidge-jun Aug 2, 2026
e67c0df
docs(providers): Copilot Responses-only GPT-5 defaults + modelAdapter…
lidge-jun Aug 2, 2026
cbd345b
docs(plan): wp-b P — concrete file map and reviewed service_tier sema…
lidge-jun Aug 2, 2026
198a317
docs(plan): fold wp-b audit round-1 blockers
lidge-jun Aug 2, 2026
b09596e
docs(plan): fold wp-b audit round-2 residual — fail-closed scope word…
lidge-jun Aug 2, 2026
b42d573
fix(responses): gate service_tier by provider capability, preserve De…
lidge-jun Aug 2, 2026
1e3c050
docs(providers): supportsServiceTier + preserveResponsesReasoningCont…
lidge-jun Aug 2, 2026
9f801e3
fix(providers): keep service-tier/reasoning capabilities registry-only
lidge-jun Aug 2, 2026
a0ea408
docs(plan): wp-c P — stale check narrows scope to registry map + agen…
lidge-jun Aug 2, 2026
2843eb5
docs(plan): fold wp-c audit round-1 — generated metadata + pinned tes…
lidge-jun Aug 2, 2026
0b30283
fix(providers): give Claude 4.6/4.7 their 1M context windows; authori…
lidge-jun Aug 2, 2026
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
134 changes: 134 additions & 0 deletions devlog/_plan/260802_client_toggle_api/000_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# 000 — Plan: client-integration toggle research (API-side on/off switch)

Research unit. Docs-only: this unit produces survey and design-option documents,
not production code. Implementation, if approved later, gets its own decade docs
in a follow-up cycle.

Parent unit: `devlog/_plan/260731_client_config_export/` (the read-only export
surface this unit extends). Sibling survey `001_client_config_survey.md` there
already covers Pi/OpenCode injection layers and the no-standard landscape; this
unit does not re-survey them.

## Loop spec

- Loop archetype: spec-satisfaction research (verifier defines done).
- Trigger: user request — can client integrations be applied as an API-side
on/off switch (enable writes the provider block into the client config,
disable removes it), for Hermes Agent, OpenClaw, Kimi Code CLI, and
Gajae Code, with cc-switch as the reference implementation.
- Goal: durable devlog evidence answering (a) how cc-switch implements
apply/remove, (b) what each of the four clients requires for a safe external
toggle, (c) what shape an opencodex management-API toggle would take and its
risks.
- Non-goals: no `src/`, `gui/`, or test changes; no new clients serialized in
`config-export.ts`; no implementation plan at diff level (that is the next
cycle's decade docs, only if the design is approved).
- Verifier: every load-bearing claim carries a source URL or repo file path
opened this cycle (cxc-search Tier 2); unverifiable claims are marked
`candidate — unverified` and listed as open questions. `bun run
privacy:scan` stays green.
- Stop condition: all four clients + cc-switch lane questions answered or
explicitly marked unreachable.
- Memory artifact: this unit's 001–003 docs.
- Expected terminal outcomes: DONE (docs written, claims sourced) or BLOCKED
(sources unreachable, named).
- Escalation: a client whose config cannot be toggled safely from outside is a
finding, not a failure — record the blocker and mark that client UNSAFE for
the toggle design.

## Research questions

### Q1 — cc-switch reference mechanics (doc 001)

- How does cc-switch apply a provider into each app's config (writer modules,
atomic write, backup/restore), and how does it remove or restore one?
- Does it offer any local HTTP API / relay ("universal endpoint") as an
alternative to file writes?
- What state does it own (`~/.cc-switch/config.json`) versus mutate in the
client's files?
Comment on lines +41 to +48

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use the cc-switch state paths from the research result.

Line 47 names ~/.cc-switch/config.json as cc-switch-owned state. 001_ccswitch_toggle_analysis.md identifies ~/.cc-switch/cc-switch.db as the provider store and separates settings.json and backups/. Update Q1 to distinguish provider state, device settings, and backups. Otherwise, this plan can drive the wrong ownership model.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@devlog/_plan/260802_client_toggle_api/000_plan.md` around lines 41 - 48,
Update Q1 in the cc-switch reference mechanics section to distinguish
cc-switch’s provider store at ~/.cc-switch/cc-switch.db, device settings in
settings.json, and backup data under backups/. Replace the current ownership
reference to ~/.cc-switch/config.json and clarify which state cc-switch owns
versus which client files it mutates.


### Q2 — Per-client toggle requirements (doc 002)

For each of Hermes Agent, OpenClaw, Kimi Code CLI, Gajae Code:

- Config file, format, and the minimal provider block that points at
`http://127.0.0.1:<port>/v1`.
- Hot-reload semantics: does the client re-read the file, or only at
session/process start? What does "applies on toggle" mean in practice?
- Non-interactive management surface, if any (CLI subcommands, local API),
which would beat raw file writes.
- Credential handling: env-reference support versus literal-only (decides
whether the no-secret-serialization invariant survives a write path).
- Removal semantics: what must be cleaned up beyond the provider block
(default model pointers, sessions, auth storage).

### Q3 — opencodex API toggle design options (doc 003)

- Current surface: `GET /api/client-config` is read-only by design; mutating
precedents exist (`PUT /api/disabled-models`, `PUT /api/model-visibility`).
- Trust boundary (A-gate amendment, blocker 2): the toggle endpoint lives on
the **management plane** — every `/api/*` request passes
`requireManagementAuth`, loopback GUI sessions are origin-bound, and
mutations require CSRF (`src/server/management-auth.ts`,
`src/server/index.ts:448`). This is separate from **data-plane** admission
(`resolveApiAuth` in `src/server/auth-cors.ts`), whose loopback shortcut
only decides what a *client* must send to `/v1`. Doc 003 must not conflate
the two.
- Launcher precedence (A-gate amendment, blocker 3): `ocx opencode` injects
`provider.opencodex` through `OPENCODE_CONFIG_CONTENT`, which outranks the
disk config for that process (`src/cli/opencode.ts`). Read-back must
distinguish "disk state" from "runtime state when launched via `ocx`", and
the design must say whether the disk toggle even applies to OpenCode.
- Design space: `PUT /api/client-config/:client {enabled: bool}` or a
`/api/client-integrations` resource; read-back/health (is our block present
and current?) versus fire-and-forget writes.
- State model (A-gate amendment, blocker 4): no cc-switch-style DB exists, so
"is it on?" must be read back from the client file. A boolean `enabled`
hides drift; the design needs richer states — `absent`, `current`, `stale`
(our block but not what we would generate now), `conflict` (a block with our
provider id that we did not write), `unsafe` (unparseable file / damaged
ownership markers).
- Invariants to carry over: no secret serialized, additive merge only,
preserve unknown fields, atomic write + backup, never touch blocks we did
not write.

### Q4 — Local writer/read-back precedents (doc 003, A-gate amendment)

- `src/grok/inject.ts` — the repo's one existing third-party config writer:
BEGIN/END managed fence in `~/.grok/config.toml`, orphan-marker refusal,
non-loopback refusal with credential-fallthrough reasoning, placeholder
`api_key = "opencodex-loopback"` (never a real secret), `stripGrokConfig`
removal path.
- `src/grok/status.ts` — the read-only status reader paired with the writer;
parses only our own fenced region.
- `src/config.ts` `atomicWriteFile`/`renameAtomicFile` — temp+rename, Windows
EBUSY/EPERM/EACCES retry with backoff, 0o600 mode, ACL hardening, residual
temp scrubbing.
- Claude Desktop writer/reader — second precedent to survey in 003.
- Risk register: format fidelity (YAML/JSON5/TOML round-trip), concurrent
writes while the client runs, clients that rewrite their own config
(Kimi Code), drift between proxy catalog and written model list.

## Doc map (000-range, research only)

- `000_plan.md` — this document.
- `001_ccswitch_toggle_analysis.md` — Q1 findings.
- `002_client_toggle_matrix.md` — Q2 findings, one section per client.
- `003_api_design_options.md` — Q3 design space + risk register + open
questions. Options, not a commitment.
- `004_ux_design.md` — (cycle 2) GUI design for the unified integrations
surface: tab rename, hero with install detection + switches, per-client
sub-pages, and the rollback UX that makes the toggle trustworthy. Design
spec only; component diffs belong to a later implementation cycle.

## Dispatch plan

- 5 research lanes (subagents, read-only): cc-switch, Hermes, OpenClaw,
Kimi Code, Gajae Code. Lane output is candidate evidence; load-bearing
claims are re-opened by the main agent before promotion (cxc-search proof
handoff).
- Local (main agent): current management-API surface, GUI consumer, loopback
admission semantics — already read this cycle:
`src/clients/config-export.ts`, `src/server/management/model-routes.ts`,
`src/server/auth-cors.ts` (`resolveApiAuth`, loopback admission),
`gui/src/components/apikeys-workspace/client-config-clients.ts`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# 001 — cc-switch toggle mechanics (reference implementation)

Research only. No diffs here. Sources were opened against upstream `main` on
2026-08-02 via the GitHub API (research lane Zeno, main-agent spot-check of the
two load-bearing claims).

cc-switch v3.17.0 (Tauri 2, Rust + React) manages Claude Code, Claude Desktop,
Codex, Gemini CLI, Grok Build, OpenCode, OpenClaw, and Hermes Agent. It is the
closest shipping implementation of the "on/off switch per client app" this unit
evaluates. Its own provider store is `~/.cc-switch/cc-switch.db` (SQLite), with
`settings.json` for device preferences and `backups/` for database backups
([README.md#L302-L305](https://github.com/farion1231/cc-switch/blob/main/README.md#L302-L305),
[database/mod.rs#L96-L109](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/database/mod.rs#L96-L109)).
Comment on lines +3 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

printf '%s\n' '--- relevant files ---'
git ls-files 'devlog/_plan/260802_client_toggle_api/*'

printf '%s\n' '--- cited sections ---'
for f in \
  devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md \
  devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md \
  devlog/_plan/260802_client_toggle_api/003_api_design_options.md \
  devlog/_plan/260802_client_toggle_api/004_ux_design.md
do
  echo "--- $f ---"
  wc -l "$f"
done

sed -n '1,25p' devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md
sed -n '15,65p' devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md
sed -n '95,125p' devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md
sed -n '165,180p' devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md
sed -n '155,220p' devlog/_plan/260802_client_toggle_api/003_api_design_options.md
sed -n '1,25p' devlog/_plan/260802_client_toggle_api/004_ux_design.md

Repository: lidge-jun/opencodex

Length of output: 15750


🌐 Web query:

Resolve the immutable revisions and documentation URLs for farion1231/cc-switch v3.17.0, NousResearch/hermes-agent main documentation revision around 2026-08-02, and openclaw/openclaw main plus docs.openclaw.ai revision around 2026-08-02. Include whether each source uses a moving branch, a release tag, or an exact commit.

💡 Result:

The requested revisions and documentation sources as of August 2, 2026, are resolved as follows: farion1231/cc-switch v3.17.0 - Documentation URL: The documentation for this version is located at https://github.com/farion1231/cc-switch/blob/v3.17.0/docs/release-notes/v3.17.0-en.md [1]. - Revision Type: This is an immutable release tag (v3.17.0) [2][1]. NousResearch/hermes-agent (main) - Documentation URL: The documentation is hosted at https://hermes-agent.nousresearch.com/docs/ [3][4]. - Revision Type: This uses a moving branch (main) [5][6]. As of August 2, 2026, the documentation at this URL reflects the latest state of the main branch [3]. openclaw/openclaw (main) and docs.openclaw.ai - Documentation URL: The documentation is hosted at https://docs.openclaw.ai [7][8]. The source of truth for these documents resides in the main branch of the https://github.com/openclaw/openclaw repository [9][10]. - Revision Type: Both the main repository and the documentation site use a moving branch (main) [9][8][10]. The docs.openclaw.ai site is a mirror that syncs from the main branch of the source repository [10].

Citations:


🏁 Script executed:

printf '%s\n' '--- exact cited ranges ---'
nl -ba devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md | sed -n '1,18p'
nl -ba devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md | sed -n '15,65p'
nl -ba devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md | sed -n '100,125p'
nl -ba devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md | sed -n '168,178p'
nl -ba devlog/_plan/260802_client_toggle_api/003_api_design_options.md | sed -n '1,22p;160,175p;210,218p'

printf '%s\n' '--- repository and source URLs ---'
git remote -v
rg -n 'github\.com|docs\.openclaw|hermes-agent|cc-switch|Upstream:|Sources were opened' \
  devlog/_plan/260802_client_toggle_api

printf '%s\n' '--- GitHub API tag/ref metadata ---'
for spec in \
  'farion1231/cc-switch refs/tags/v3.17.0' \
  'NousResearch/hermes-agent refs/heads/main' \
  'openclaw/openclaw refs/heads/main'
do
  set -- $spec
  url="https://api.github.com/repos/$1/git/$2"
  echo "--- $url ---"
  curl -LfsS "$url" | jq '{ref:.ref, object:.object}'
done

Repository: lidge-jun/opencodex

Length of output: 10046


🏁 Script executed:

printf '%s\n' '--- exact cited ranges ---'
for spec in \
  'devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md:1:18' \
  'devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md:15:65' \
  'devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md:100:125' \
  'devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md:168:178' \
  'devlog/_plan/260802_client_toggle_api/003_api_design_options.md:1:22' \
  'devlog/_plan/260802_client_toggle_api/003_api_design_options.md:160:175' \
  'devlog/_plan/260802_client_toggle_api/003_api_design_options.md:210:218'
do
  file=${spec%%:*}
  rest=${spec#*:}
  start=${rest%%:*}
  end=${rest##*:}
  echo "--- $file:$start-$end ---"
  awk -v s="$start" -v e="$end" 'NR >= s && NR <= e { printf "%6d %s\n", NR, $0 }' "$file"
done

printf '%s\n' '--- immutable ref discovery ---'
for repo in farion1231/cc-switch NousResearch/hermes-agent openclaw/openclaw; do
  echo "--- $repo ---"
  git ls-remote --refs --tags "https://github.com/$repo.git" 'refs/tags/v3.17.0' 'refs/tags/*' 2>/dev/null |
    grep -E 'v3\.17\.0$|v3\.17\.0\^\{\}$' | tail -5 || true
  git ls-remote "https://github.com/$repo.git" \
    'refs/heads/main' 'refs/tags/v3.17.0^{}' 2>/dev/null || true
done

Repository: lidge-jun/opencodex

Length of output: 12060


🌐 Web query:

site:github.com/NousResearch/hermes-agent/commits/main "2026-08-02" OR site:github.com/openclaw/openclaw/commits/main "2026-08-02"

💡 Result:

No matching GitHub commit pages were found for either repository on August 2, 2026:

  • NousResearch/hermes-agent
  • openclaw/openclaw

The search results did not surface commits dated 2026-08-02 for either repository.


Pin research sources to immutable revisions.

  • In 001_ccswitch_toggle_analysis.md:3-13, replace every blob/main cc-switch link with blob/v3.17.0.
  • In 002_client_toggle_matrix.md:25-27 and :56-58, record the exact Hermes Agent and OpenClaw commits used on 2026-08-02 and link each citation to its commit.

Moving main links make these load-bearing claims non-reproducible. Do not use the current branch heads as substitutes for the revisions read on 2026-08-02.

📍 Affects 2 files
  • devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md#L3-L13 (this comment)
  • devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md#L25-L27
  • devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md#L56-L58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md` around
lines 3 - 13, Pin all cited research sources to immutable revisions: in
devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md:3-13,
replace every cc-switch blob/main link with blob/v3.17.0; in
devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md:25-27 and
:56-58, identify the exact Hermes Agent and OpenClaw commits used on 2026-08-02
and link each citation to its commit, without substituting current branch heads.


## 1. Two app classes: exclusive vs additive

The toggle semantics split on whether the client keeps one active provider or
many coexisting ones:

| Class | Clients | "Switch on" means | "Switch off" means |
|-------|---------|-------------------|--------------------|
| Exclusive | Claude Code, Codex, Gemini | backfill current live config into the stored provider record, then overwrite live config with the target provider | switching away backfills and overwrites with the next provider |
| Additive | OpenCode, OpenClaw, Hermes | insert the provider entry into the client's live config, alongside existing ones | `remove_provider_from_live_config` deletes only that live entry; the provider stays in cc-switch's DB, marked `live_config_managed=false` |

Source: [provider/mod.rs#L2893-L2951](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/provider/mod.rs#L2893-L2951),
[provider/mod.rs#L2954-L2966](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/provider/mod.rs#L2954-L2966),
[provider/mod.rs#L3087-L3146](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/provider/mod.rs#L3087-L3146).
Main-agent spot-check: `live_config_managed` markers and
`provider_live_config_managed` confirmed in `provider/mod.rs` (lines 1870,
2437-2450).

All four of this unit's target clients (Hermes, OpenClaw, and by their config
shapes Kimi Code and Gajae Code) are **additive-class**: a toggle writes and
removes one provider entry, never a whole-file takeover.
Comment on lines +32 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis\.md|.*cc.?switch.*|.*toggle.*|.*kimi.*|.*gajae.*|.*hermes.*|.*openclaw.*)' || true

printf '%s\n' '--- target document ---'
wc -l devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md
cat -n devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md

printf '%s\n' '--- references to the four clients and classification ---'
rg -n -i -C 4 'Hermes|OpenClaw|Kimi Code|Gajae Code|additive-class|whole-file|provider entry|removal|remove' . \
  -g '!node_modules' -g '!dist' -g '!build' || true

Repository: lidge-jun/opencodex

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -eu

log=/tmp/coderabbit-shell-logs/shell-output-bxl5Tv

printf '%s\n' '--- target-document output in prior log ---'
rg -n -A 8 -B 8 '001_ccswitch_toggle_analysis\.md|additive-class|Kimi Code|Gajae Code' "$log" | head -n 240

printf '%s\n' '--- focused tracked-file search ---'
git ls-files | rg 'ccswitch|cc-switch|toggle|kimi|gajae|hermes|openclaw' | head -n 240

printf '%s\n' '--- focused repository search ---'
rg -n -i -C 3 'cc.?switch|additive-class|Kimi Code|Gajae Code|OpenClaw|Hermes Agent' \
  devlog src tests .github 2>/dev/null | head -n 400 || true

Repository: lidge-jun/opencodex

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- cc-switch analysis ---'
cat -n devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md

printf '%s\n' '--- client matrix ---'
cat -n devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md

printf '%s\n' '--- design sections that rely on the classification ---'
sed -n '140,205p' devlog/_plan/260802_client_toggle_api/003_api_design_options.md
sed -n '205,225p' devlog/_plan/260802_client_toggle_api/004_ux_design.md

Repository: lidge-jun/opencodex

Length of output: 27432


🌐 Web query:

MoonshotAI kimi-code e22479a core-impl.ts provider remove cascades config TOML unknown fields preserved

💡 Result:

The query refers to technical components and configuration management patterns found in projects like MoonshotAI's Kimi Code CLI [1][2] and related agent-core architectures [3]. Regarding the specific elements mentioned: Provider Removal and Cascades: In the context of Kimi Code's architecture (specifically packages/agent-core-v2), the ProviderService manages provider configurations [3]. When a provider is removed, the system ensures consistency by clearing dependent configurations, such as the default provider, if it matches the one being deleted [3][4]. The removal process typically involves updating the entire provider section by replacing it with the filtered set [3][4], rather than just modifying the single entry in place, to ensure the state remains synchronized [3][4]. TOML Configuration and Preservation of Unknown Fields: The requirement to preserve unknown fields in TOML configuration files is a common challenge in CLI tool development. When tools round-trip configurations (parse TOML -> edit in-memory -> write back to TOML), naive serialization often drops comments or keys not explicitly defined in the application's configuration schema [5][6][7]. To address this, developers typically adopt one of two strategies: 1. Recursive Preservation: Using libraries like toml_edit to parse the file into a document model that maintains key order, comments, and structure, allowing the application to update specific fields while leaving the rest of the document untouched [6]. 2. Flattening/Extra Fields: Using Serde's #[serde(flatten)] attribute to capture unknown keys into a catch-all map (often named extra), which can then be serialized back into the file [8][7][9]. The specific commit reference e22479a appears to be a local or specific internal identifier not indexed in public repositories for MoonshotAI/kimi-code [1][10], but the behaviors described align with standard practices for maintaining configuration durability in agent-based CLI tools [3][8][7].

Citations:


🌐 Web query:

Yeachan-Heo gajae-code 5f2e7cd models.yml provider setup remove atomic writer preserves unrelated configuration

💡 Result:

The query refers to functionality within the Yeachan-Heo/gajae-code project (also known as GJC) [1][2]. While there is no specific public commit with the hash 5f2e7cd documented in the repository's public history, the project manages configuration through a models.yml file [1][3] and uses a configuration system that interacts with provider setups [4][5]. In this context, the project's configuration management has evolved to improve the robustness of how models.yml is handled: 1. Preservation of Unrelated Configuration: The project's configuration loaders (e.g., ModelsConfigFile) are designed to merge and validate configurations [5]. When updating provider settings, the system typically preserves unrelated configuration keys rather than overwriting the entire file, by applying structured schema validation (using Zod) and partial updates [5]. 2. Atomic Writes and Provider Setup: To prevent configuration corruption during updates or provider setup—specifically when using commands like gjc setup provider—the system utilizes atomic write patterns. This ensures that when a configuration file is updated, the operation either completes fully or leaves the existing configuration intact, preventing partial or malformed writes that could inadvertently strip out unrelated settings [4][5]. 3. Validation: Recent improvements have focused on ensuring that unknown or unsupported provider keys in models.yml (such as legacy requestTransform or wireModelId fields) are either handled or result in clear validation warnings rather than silent failures [1][5]. If you are encountering issues where unrelated settings are being removed, it is likely related to an older version of the configuration logic or a failure to strictly adhere to the updated ProviderConfigSchema, which defines the expected structure for provider-level overrides [5]. Using the most recent version of the CLI is recommended to ensure robust configuration management [2].

Citations:


🌐 Web query:

farion1231 cc-switch provider remove_provider_from_live_config Hermes OpenClaw additive config

💡 Result:

In the context of the CC Switch application (maintained by farion1231), the function remove_provider_from_live_config is a specific API command used to remove a provider from the live configuration file for supported AI tools that utilize an additive configuration mode [1]. Additive Mode and Configuration Management: CC Switch supports an additive mode for tools such as OpenCode, OpenClaw, and Hermes Agent [1]. In this mode, multiple providers can remain active simultaneously within the same configuration file [1]. When managing these providers, the application tracks their presence in the live configuration using an isInConfig property [1]. The remove_provider_from_live_config command is specifically designed to perform this removal at the live file level without deleting the provider data from the underlying database [2][1]. Technical Context: - CC Switch is an all-in-one assistant for tools including Claude Code, Codex, OpenCode, OpenClaw, and Hermes Agent [3][4]. - The remove_provider_from_live_config API was implemented to ensure that when a user removes a provider in the UI, the live configuration file is updated correctly, while maintaining the integrity of the provider records in the app's SQLite database [3][2]. - This command is part of the broader provider management system that abstracts the configuration differences between the various native formats of the supported AI tools [1]. Note on Migration: While OpenClaw and Hermes share configuration concepts, migrating between them (using tools like openclaw-to-hermes or bundled migration providers) involves distinct processes—such as mapping custom providers and migrating API keys to a.env file—which are separate from the live config management performed by CC Switch [5][6][7][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

curl -fsSL \
  https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670/packages/agent-core/src/rpc/core-impl.ts \
  -o "$tmp/kimi-core-impl.ts"
curl -fsSL \
  https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670/packages/agent-core/src/config/toml.ts \
  -o "$tmp/kimi-toml.ts"
curl -fsSL \
  https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/5f2e7cd05e8ea344991566f9ed96f1f9c66226bd/packages/coding-agent/src/cli/setup-cli.ts \
  -o "$tmp/gajae-setup-cli.ts"
curl -fsSL \
  https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/5f2e7cd05e8ea344991566f9ed96f1f9c66226bd/packages/coding-agent/src/config/models-config-schema.ts \
  -o "$tmp/gajae-schema.ts"
curl -fsSL \
  https://raw.githubusercontent.com/farion1231/cc-switch/main/src-tauri/src/services/provider/mod.rs \
  -o "$tmp/ccswitch-provider.rs"

printf '%s\n' '--- Kimi provider removal ---'
rg -n -A 38 -B 12 'provider.*remove|remove.*provider|default_model|default_provider' "$tmp/kimi-core-impl.ts" | head -n 180

printf '%s\n' '--- Kimi TOML mutation/write path ---'
rg -n -A 35 -B 12 'write|serialize|smol|provider|models' "$tmp/kimi-toml.ts" | head -n 220

printf '%s\n' '--- Gajae setup mutation path ---'
rg -n -A 45 -B 15 'setup provider|models\.yml|write|atomic|rename|provider' "$tmp/gajae-setup-cli.ts" | head -n 240

printf '%s\n' '--- Gajae schema ---'
rg -n -A 35 -B 12 'ProviderConfig|providers|strict|apiKeyEnv|models' "$tmp/gajae-schema.ts" | head -n 220

printf '%s\n' '--- cc-switch additive removal implementation ---'
rg -n -A 45 -B 15 'remove_provider_from_live_config|remove.*live|live_config_managed' "$tmp/ccswitch-provider.rs" | head -n 260

Repository: lidge-jun/opencodex

Length of output: 36263


🏁 Script executed:

#!/bin/bash
set -eu

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

curl -fsSL \
  https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670/packages/agent-core/src/rpc/core-impl.ts \
  -o "$tmp/kimi-core-impl.ts"
curl -fsSL \
  https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670/packages/agent-core/src/config/toml.ts \
  -o "$tmp/kimi-toml.ts"
curl -fsSL \
  https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/5f2e7cd05e8ea344991566f9ed96f1f9c66226bd/packages/coding-agent/src/setup/provider-onboarding.ts \
  -o "$tmp/gajae-provider-onboarding.ts"
curl -fsSL \
  https://raw.githubusercontent.com/Yeachan-Heo/gajae-code/5f2e7cd05e8ea344991566f9ed96f1f9c66226bd/packages/coding-agent/src/config/models-config.ts \
  -o "$tmp/gajae-models-config.ts"
curl -fsSL \
  https://raw.githubusercontent.com/farion1231/cc-switch/main/src-tauri/src/services/provider/live.rs \
  -o "$tmp/ccswitch-live.rs"

printf '%s\n' '--- Kimi removal and TOML write ranges ---'
rg -n 'removeProvider|remove_provider|default_model|default_provider|atomicWrite|stringifyToml|writeConfig|writeFile' \
  "$tmp/kimi-core-impl.ts" "$tmp/kimi-toml.ts"
sed -n '700,780p' "$tmp/kimi-core-impl.ts"
sed -n '450,535p' "$tmp/kimi-toml.ts"

printf '%s\n' '--- Gajae provider onboarding and write ranges ---'
rg -n 'addApiCompatibleProvider|models\.yml|writeFile|rename|atomic|remove|delete|providers' \
  "$tmp/gajae-provider-onboarding.ts" "$tmp/gajae-models-config.ts"
sed -n '1,260p' "$tmp/gajae-provider-onboarding.ts"

printf '%s\n' '--- cc-switch Hermes/OpenClaw removal ranges ---'
rg -n 'remove_hermes_provider_from_live|remove_openclaw_provider_from_live|remove_provider_from_live_config|remove.*provider' \
  "$tmp/ccswitch-live.rs"

Repository: lidge-jun/opencodex

Length of output: 208


🏁 Script executed:

#!/bin/bash
set -u

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

fetch() {
  url=$1
  out=$2
  if curl -fsSL "$url" -o "$out"; then
    printf 'fetched %s\n' "$url"
  else
    printf 'failed %s\n' "$url" >&2
  fi
}

fetch \
  https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba967/packages/agent-core/src/rpc/core-impl.ts \
  "$tmp/kimi-core-impl.ts"
fetch \
  https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba967/packages/agent-core/src/config/toml.ts \
  "$tmp/kimi-toml.ts"
fetch \
  https://raw.githubusercontent.com/farion1231/cc-switch/main/src-tauri/src/services/provider/live.rs \
  "$tmp/ccswitch-live.rs"

printf '%s\n' '--- Gajae repository paths at the cited commit ---'
curl -fsSL \
  'https://api.github.com/repos/Yeachan-Heo/gajae-code/git/trees/5f2e7cd05e8ea344991566f9ed96f1f9c66226bd?recursive=1' |
  jq -r '.tree[] | select(.path|test("provider|models-config|config.*(write|file)|setup-cli"; "i")) | .path' |
  head -n 240

printf '%s\n' '--- Kimi removal and TOML write ranges ---'
rg -n 'removeProvider|remove_provider|default_model|default_provider|atomicWrite|stringifyToml|writeConfig|writeFile' \
  "$tmp/kimi-core-impl.ts" "$tmp/kimi-toml.ts" || true
sed -n '700,780p' "$tmp/kimi-core-impl.ts"
sed -n '450,535p' "$tmp/kimi-toml.ts"

printf '%s\n' '--- cc-switch removal implementation ---'
rg -n -A 35 -B 12 'remove_hermes_provider_from_live|remove_openclaw_provider_from_live|remove_provider_from_live_config|remove.*provider' \
  "$tmp/ccswitch-live.rs" || true

Repository: lidge-jun/opencodex

Length of output: 13708


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path

analysis = Path("devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md").read_text()
matrix = Path("devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md").read_text()

claim = "All four of this unit's target clients"
assert claim in analysis
assert "Kimi provider remove" in matrix
assert "cascades" in matrix
assert "no remove command exists" in matrix
assert "file edit required" in matrix

print("001 classification claim: present")
print("Kimi: vendor removal exists and cascades dependent aliases/defaults")
print("Gajae: vendor add/replace exists; vendor removal is absent")
print("Conclusion: the four clients cannot share an unqualified one-entry removal contract")
PY

Repository: lidge-jun/opencodex

Length of output: 245


Split the additive classification by client. cc-switch proves entry-scoped removal for Hermes and OpenClaw. Kimi Code removes the provider, dependent model aliases, and matching defaults before rewriting TOML; it is not a one-entry removal contract. Gajae Code provides add/replace only and has no removal command. Mark Gajae Code candidate — unverified, and require a fixture that proves disable preserves unrelated models.yml configuration before applying the additive writer contract. Update devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md:32-34.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md` around
lines 32 - 34, The client classification at the referenced statement incorrectly
groups all four clients as additive-class. Keep Hermes and OpenClaw classified
as additive, classify Kimi Code separately because disabling removes related
provider, alias, and default entries, and mark Gajae Code as
candidate—unverified; require a fixture proving disable preserves unrelated
models.yml configuration before applying the additive writer contract.


## 2. Write discipline

- Provider-specific writers project into native files: Claude ->
`~/.claude/settings.json`, Codex -> `~/.codex/auth.json` + `config.toml`,
OpenClaw -> `~/.openclaw/openclaw.json` (JSON5), Hermes ->
`~/.hermes/config.yaml` (YAML).
- JSON/TOML/text writes go through a temporary sibling file + rename (atomic on
Unix; Windows removes the destination first).
[provider/live.rs#L1015-L1060](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/provider/live.rs#L1015-L1060),
[config.rs#L273-L351](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/config.rs#L273-L351)
- Writer modules preserve unknown fields (`serde(flatten)` extra maps in
`hermes_config.rs` / `openclaw_config.rs`) and honor the client's own
config-dir overrides (`HERMES_HOME`, Windows `%LOCALAPPDATA%\hermes`).
- Health warnings are returned on write (`OpenClawWriteOutcome.warnings`).

## 3. Proxy takeover: the *other* kind of toggle

cc-switch has a second, coarser switch — "proxy takeover" — which is
backup-and-restore, not provider-entry deletion:

1. Enable: start the local proxy if needed, snapshot the app's current live
config into the DB's live-backup storage, then write proxy
endpoint/placeholder fields into the client config.
2. Disable/stop-with-restore: write the saved snapshot back and delete the
backup rows. Missing backup -> attempt SSOT/provider reconstruction and
placeholder cleanup.
3. Switching providers *while* takeover is active is a hot switch: the client
keeps pointing at the local proxy and only the proxy's upstream target
changes.

[services/proxy.rs#L730-L820](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/proxy.rs#L730-L820),
[services/proxy.rs#L1292-L1323](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/proxy.rs#L1292-L1323),
[services/proxy.rs#L1822-L1865](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/proxy.rs#L1822-L1865),
[provider/mod.rs#L3004-L3054](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/services/provider/mod.rs#L3004-L3054)

For opencodex this pattern is mostly redundant — opencodex *is* the proxy, so
the client config only ever needs the additive provider entry; there is no
upstream target to hot-switch inside the client.

## 4. Universal endpoint (relay), not a management API

cc-switch embeds an Axum server (default `http://127.0.0.1:15721`) exposing
Claude / OpenAI Responses / Gemini routes plus health and status; tools point
their base URL at it. It is a data-plane relay, **not** a control API — the
toggle operations live in Tauri commands, and the only URL scheme is
`ccswitch://v1/import?...` for config import.
[proxy/server.rs#L100-L145](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/proxy/server.rs#L100-L145),
[proxy/server.rs#L291-L366](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/proxy/server.rs#L291-L366),
[deeplink/parser.rs#L11-L67](https://github.com/farion1231/cc-switch/blob/main/src-tauri/src/deeplink/parser.rs#L11-L67)

Main-agent spot-check: `proxy/server.rs` binds
`config.listen_address:listen_port` via tokio TcpListener (lines 101-145).

## 5. What this means for an opencodex toggle

- The additive per-client writer is the pattern to copy: insert/remove exactly
one provider entry, preserve everything else, atomic write, health warnings
on read-back.
- cc-switch's "is it on?" state is *its own DB record* plus a
`live_config_managed` marker — it does not re-derive state from the client
file. An opencodex toggle has no such DB; state must be read back from the
client config itself (does our entry exist, and does it match what we would
generate now?). This is a real design divergence, explored in 003.
- Its exclusive-app machinery (backfill/overwrite) and proxy-takeover
snapshot/restore solve problems opencodex does not have; neither needs
porting.
Loading
Loading