fix(server,config): extension CORS origin identity + symlink-safe atomic writes - #886
fix(server,config): extension CORS origin identity + symlink-safe atomic writes#886lidge-jun wants to merge 15 commits into
Conversation
…dence Fourteen documents: the ask decomposed, four read-only research lanes against upstream 2b5bdcf67 and dev f9b9440, a UX design, seven implementation phases, and a deferred Theme stub. Two findings reshape the build. model_instructions_file replaces the entire base prompt rather than adding to it, so custom layers compose into developer_instructions instead. And the Logs tab the ask names as the model uses exclusive hash-persisted tabpanels, not the SectionTabs scroll-spy strip that shares the name. The hard constraint - layers that must never be disableable - is now a proven set rather than a guess: base/model instructions, model-switch, AGENTS.md, realtime, plugins, and non-Skills extension layers have no include_ key anywhere in the schema.
…eference) Survey unit for applying client integrations as an on/off switch from the management API: cc-switch additive/exclusive toggle mechanics, per-client requirements for Hermes/OpenClaw/Kimi Code/Gajae Code, and design options with a five-state read-back model and risk register. Research only; no production code.
An independent audit returned FAIL on twelve counts. The central architecture survived - model_instructions_file replaces the base prompt, so custom layers belong in developer_instructions - but the persistence protocol would have corrupted user config. Fencing layer bodies inside a TOML multiline string was the worst of it. A body containing triple quotes terminates the value and a backslash is an escape, so arbitrary prose could produce a file Codex cannot parse at all. The sidecar JSON made it worse by requiring two-way reconciliation that no rule actually defined. Storage is now one owned JSON file as the single source of truth, with developer_instructions as a generated projection written through a real TOML serializer and verified by a reparse. No fences, so no text can collide with a delimiter. Revision hashes and a write-order contract close the concurrency hole that atomic rename never covered. The layer inventory was also wrong: it listed Plugins as impossible to disable while session/mod.rs checks Feature::Plugins. A single cannot-be-turned-off list is replaced by five explicit classes, exported once from WP1 and consumed by route and GUI alike, with a partition test. The API derives its refusals from that inventory rather than a hand-maintained deny-list. Two overclaims are retracted. effective becomes defaultedUserValue because opencodex reads one config layer out of eight. The linter's size rule drops its citation of the 32 KiB AGENTS.md budget, which governs project-doc loading and never constrained developer_instructions. Phases re-sliced to remove four forward dependencies, and the missing-config state no longer disables the switch that was supposed to create the file.
Round 2 of the audit rejected the smol-toml plan on six counts. A live probe on this machine settled it harder than the audit could: Bun 1.3.14's TOML.parse transposes \t and \f, rejects \u0007, and does not trim the newline after an opening triple quote. Codex parses with Rust toml_edit. So a verify-by-reparse design would have checked our encoding against a parser with known-transposed escapes and reported success on a file Codex might read differently. The fix is to stop needing a parser. Bodies accept printable Unicode, spaces and newlines; tabs normalize to four spaces and CRLF to LF; control characters are refused. Within that set the escaping is three unambiguous rules, and verification becomes a byte comparison. No production dependency is added, which also retires the unreviewed dependency the audit flagged - it was BSD-3-Clause rather than MIT, absent from the lockfile, and its parse() never exposed the source spans the plan claimed. The transaction is rebuilt around a journal. config.toml is written first now, so a failed request leaves the source of truth untouched rather than committing JSON and returning an error. Reads never write: drift is reported as state and resolved by an explicit POST, because an HTTP GET must not modify a user's config. A missing store with a live owned projection is no longer treated as an empty store - that would have erased the active prompt on the next write. Refusing an externally authored developer_instructions is no longer a dead end: an adopt flow shows the raw line, offers a copy, and imports it on confirmation. Also corrected: the assembly table now uses the five-class vocabulary instead of contradicting section 4, two citations the audit judged overstated are narrowed to what the source actually shows, and 003 no longer mandates an effective field that 010 and 005 deliberately defer.
Round 3 confirmed the Bun.TOML measurements independently and accepted the restricted encoder, then found the recovery algorithm destructive. The rule was 'if either target differs from the post-image, rewrite both'. Crash after the first write, let the user or Codex edit config.toml, and recovery overwrites their edit with a stale post-image. Recovery now classifies each target against both recorded hashes and refuses to write any file matching neither - that file belongs to someone else. One unrecognised target aborts the whole recovery with recovery_required rather than rolling forward into a state nobody intended. The commit point was also mixing two models: journal-rename-is-commit alongside a rollback that claimed a failed request changes nothing. The journal is now prepared intent, commit is both targets matching the post-image, and rollback is the only failure path. Durability adds parent-directory fsync with a documented Windows fallback. Stale-lock breaking could delete a successor's lock and admit two writers. Takeover is now an atomic rename to a token-quarantined name that exactly one contender can win, and release deletes only a lock whose token still matches. Adoption imported the raw TOML source line as the body, so a value containing an escaped newline would have become twelve literal characters. It now decodes through the inverse of our own encoder, accepting only the three escapes we emit, and previews both the source line and the decoded body. Malformed marker-owned lines get the same flow instead of being a permanent lockout, and store-missing repair is described as what it is: salvage of one concatenated string, with the losses listed and a backup written first. Character validation is defined over Unicode scalar values, rejects unpaired surrogates and C1 controls, measures caps in UTF-8 bytes after normalization, and reports code-point positions. One golden fixture parsed by real toml_edit now backs the claim that byte equality means Rust reads what we intended.
Campaign preparation (docs-only): five units under devlog/_plan/260802_wtN_* with 000 research + 010 implementation roadmaps, claim ledgers verified by a lunasearch fan-out (Anthropic 1M windows, Copilot mixed-wire, DeepSeek service_tier, WHATWG extension origins, POSIX rename-over-symlink). wt1 update-path: PR #871, issue #879 (star-prompt deferral leakage), #557 optional wt2 zero-leak: PRs #840 #841 #843 #844 #845 #847 (tracker #820) wt3 provider-wire: PRs #746 #860 #839/#854, issue #875 triage, #616/#837 optional wt4 server-config: PRs #850 (CORS origin confusion), #869 (symlink destruction) wt5 windows-service: PRs #868, #861 (issue #848)
…tract Round 4 found the transaction still holding both models at once: journal existence was called the commit point while rollback claimed a failed request changes nothing. The journal is now unambiguously prepared intent - commit is deleting it after both targets verify against the post-image - so a journal found on disk always means the transaction never committed, and recovery rolls back. The one exception is not a roll-forward: when both targets already match the post-image the writes had finished and only the commit step was missing. An unparseable journal previously implied restoring a pre-image that lives inside the same damaged document. The journal is now a checksummed envelope and any failure of that checksum is recovery_required with nothing read and nothing written. The pre-rename guard only rehashed config.toml, leaving the store overwritable by a third party between compare and write; each target now re-verifies its own bytes immediately before its own rename, and rollback applies the same pre/post/neither classification. Post-write verification compares complete bytes instead of looking for our two lines, so another writer changing an unrelated key can no longer be reported as success. WP1's exported contract was missing drift, adopt, repair, salvage, and recovery entirely, so WP2 could not have been built from it. All are declared now, with read-only preview DTOs, and every filesystem mutation stays in WP1. Modes were specified for config.toml alone. The store, journal, salvage backup, and lock all carry prompt bodies or metadata and are now 0600 at creation rather than by later chmod. Salvage backups use exclusive creation with a random suffix and abort the operation if they cannot be made durable. Also: the 001 assembly table fix from the previous round never landed on that block - the audit was right that the committed file still carried ALWAYS-ON. It is corrected here, along with the last locked/features array references.
GO-WITH-FIXES (5 blockers) all folded: CORS fix retargeted to src/server/auth-cors.ts isExtraAllowedOrigin with full origin_rejected coverage instruction; ANTHROPIC_MODEL_CONTEXT_WINDOWS correctly assigned to registry.ts:217; wt2/wt3 shared-file coordination named in both units; atomicWriteFile caller audit switched to grep-at-P with oauth/store.ts named explicitly.
…ds on
Round 5 opened core/src/mcp.rs and disproved two of my drafts at once:
let plugins_available =
selected_plugin_available || !loaded_plugins.capability_summaries().is_empty();
Feature::Plugins feeds plugins_config_input and therefore the right operand,
but selected_plugin_available is an independent OR path that can make the
section emit regardless. The flag influences emission; it does not gate it. The
citation both earlier drafts leaned on, session/mod.rs:3422-3430, gates
recommended plugin candidates rather than this section. Plugins is now
runtime-conditional in the table, in class D, and out of the feature list, with
the residual question recorded as UNKNOWN rather than asserted.
Three other corrections. A stale paragraph still told recovery to restore from
the pre-image of a journal that had failed its own checksum - exactly the
impossible path the envelope section rejects - and it is deleted. Adoption now
states its five ordered steps and previews the post-normalization body, because
previewing decoded text while committing normalized text would show one string
and save another. WP2's repair contract gains owned-malformed, which it omitted
entirely, and stops calling store recovery reconstruction when 010 defines it as
lossy salvage.
Also enumerated the lock interleaving the audit asked for - A quarantines, B
acquires, A's wx fails and A retries without touching B's lock - and made
Windows write-through an explicit WP1 acceptance gate rather than an assumption:
establish what Bun exposes, or fail closed as recovery_required with a test on
that branch.
The stale UX paragraph promised a built-in dialog showing rendered prompt text and an effective value. Neither exists: Codex exposes no API for rendered layer bodies, and opencodex reads one config layer out of eight. 005 now says what the dialog actually shows and names the two things it does not. SalvagePreview returned a backupPath, which either reserves a name during a read-only preview or promises one that exclusive creation may refuse at commit. It returns backupDir now; the real path is created during the confirmed mutation and comes back in the WriteResult. Adoption tests 26a-26g enumerate the pipeline the prose already required: normalization, control rejection with position, post-normalization overflow, composed overflow, and the property that matters most - the previewed body is byte-identical to the committed one. The Windows durability gate stays open by design. It is an acceptance criterion for WP1, not something a document can settle.
…ollback UX design spec for merging the API/Claude/Grok pages into one Integrations tab: hash-routed sub-tabs, ops-hero with install detection and capability- aware switches, per-client settings, and a two-level rollback contract (operation journal + restore preflight). Design only; no components.
First slice of WP1: the canonical layer inventory plus the encoding contract. No file writes yet. The encoder exists because Bun.TOML cannot verify our work. Measured on 1.3.14, its parser transposes \t and \f, rejects \u0007, and does not trim the newline after an opening triple quote. Codex parses with Rust toml_edit, so a verify-by-reparse loop could report success on a file Codex reads differently. So bodies are restricted instead: tabs become four spaces, CRLF becomes LF, and control characters, DEL, C1, and unpaired surrogates are refused with a code-point position. Within that set the escaping is three total rules and the emitted line is checked against a hand-written grammar that shares no code with the encoder. The decoder is deliberately narrow - it accepts only the three escapes we emit and refuses \t, \f, \b, \r and \uXXXX rather than guessing. The inventory classifies every layer into exactly one of five classes, and the tests assert the partition is total and disjoint so the API can derive switchability from it instead of a hand-maintained deny-list. Plugins is runtime-conditional: core/src/mcp.rs:200 ORs selected_plugin_available with the loaded summaries, so [features] plugins feeds only the right operand. The surrogate guard was driven red once to prove the test is not vacuous.
The scanner flags concrete home paths, and three example snippets carried one. Nothing sensitive was in them, but the gate is the gate.
Applies the maintainer-reviewed implementation from PR #850 (eachann1024) onto current dev. URL.origin serializes every extension scheme as "null" per WHATWG URL 4.7, so the old origin-equality check in isExtraAllowedOrigin admitted ANY browser extension whenever one was allowlisted. comparableOrigin now keeps WHATWG origins for normal schemes and compares protocol//host for authority-based opaque origins; hostless opaque origins keep exact-string fallback. Covers both planes through the shared predicate: data-plane /v1/* and management /api/* (preflight + GUI session issuance). Beyond the PR: management-plane preflight assertions (/api/settings accept + cross-extension reject) folded from the wt4 audit; locale docs re-based onto the configuration/server.md subpages (docs split 7fdb2cb) with the Firefox/Safari UUID-rotation caveat (per-install / per-launch regeneration — MDN, Mozilla bug 1717671, WebKit bug 244330). Tests: 69/69 server-auth + server-loopback-host-gate; typecheck green.
Applies PR #869 (nicosuave) onto current dev plus one audit-folded amendment. atomicWriteFile/atomicWriteFileAsync wrote the temp beside the literal destination and renamed over it; rename(2) replaces the directory entry, so a symlinked destination (dotfiles-managed ~/.codex/config.toml) was silently converted to a plain file and the tracked repo stopped receiving writes (POSIX.1-2024 rename; Linux rename(2): the link will be overwritten). resolveWriteTarget realpaths the destination so temp and rename land beside the real file and the link survives; the test-only real-home guard is re-applied to the resolved target so a symlink escaping a fixture home into the protected home is still refused; the responses-state load sweeps stale temps in both the literal and the resolved directory. Audit amendment (wt4 wp2): a realpath failure no longer falls back to the literal path blindly. A genuinely absent destination keeps the literal first-write path, but an EXISTING unresolvable symlink (dangling target, unmounted volume, ELOOP, EACCES) is now refused and preserved instead of silently replaced; snapshot loading sweeps the literal dir only in that case. Tests: 202/202 config + responses-state + test-home-guard (sync+async link survival, no-temp-left, plain destination, first-write creation, dangling-link refusal, resolved-dir sweep, guard escape probe); typecheck green.
📝 WalkthroughWalkthroughThis PR adds research and implementation plans for client integrations and Codex prompt composition. It also implements prompt-layer utilities, symlink-safe atomic writes, response-state cleanup, browser-extension CORS matching, documentation updates, and focused regression tests. ChangesClient toggle research
Codex Set prompt composer
Worktree issue planning
Server and filesystem security
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 44
🤖 Prompt for all review comments with 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.
Inline comments:
In `@devlog/_plan/260802_client_toggle_api/000_plan.md`:
- Around line 91-93: Revise the invariant list in the plan to separate global
guarantees from client-format-specific behavior: retain secret exclusion,
additive merging, atomic write with backup, and avoiding untouched blocks
globally; move unknown-field preservation into the applicable format rules,
documenting Kimi’s loss of comments/formatting and Gajae’s schema-only,
unknown-field-rejecting serialization in alignment with the client toggle
matrix.
In `@devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md`:
- Around line 32-34: The classification of Kimi Code and Gajae Code as
additive-provider clients is unsupported and should be marked as a hypothesis.
Update the related plan guidance to require documenting each client’s complete
add and remove contract, including Kimi’s model aliases, default_model, and
default_provider cleanup and Gajae Code’s schema fields and models.yml
references.
In `@devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md`:
- Around line 19-21: Update the Hermes entries in the client-toggle matrix to
distinguish dedicated non-interactive provider CRUD from authenticated raw
configuration editing: replace the “No” add-path wording with language
indicating no dedicated provider CRUD while acknowledging authenticated raw
YAML/dashboard save, and keep the dashboard API as a valid toggle mechanism.
- Around line 177-179: Update the Hermes entries in 002_client_toggle_matrix.md
and 004_ux_design.md to distinguish CLI sessions, which read configuration at
session start, from the gateway, which performs partial live reads. State that
provider changes require a new session or gateway restart, while avoiding
language that claims Hermes has no live reads; replace the blanket “새 세션부터
적용됩니다” wording with this qualified behavior.
- Around line 107-119: Keep the registry-toggle proposal explicitly provisional
until its wire contract is defined. Update the loopback command example to
include --api-key (or KIMI_REGISTRY_API_KEY), and document the required contract
details: api.json payload, provider ID, refresh matching, credential handling,
and removal behavior; do not assume the registry creates the opencodex ID.
In `@devlog/_plan/260802_client_toggle_api/003_api_design_options.md`:
- Around line 9-42: Replace every machine-local absolute link in the documented
references with repository-relative paths and anchors, such as
src/server/management/model-routes.ts#L236. Update all affected references in
the listed API precedents, including config-export.ts, client-config-clients.ts,
and agent-settings-routes.ts, while preserving the referenced symbols and line
context.
- Around line 112-114: Update the atomicWriteFile source link in the surrounding
documentation to use the current src/config.ts definition at lines 158-209,
preferably with a repository-relative path or a revision-pinned URL, and remove
the stale local absolute path and range.
In `@devlog/_plan/260802_client_toggle_api/004_ux_design.md`:
- Around line 319-324: Update the restore refusal-path design to follow the
shared atomic writer’s symlink behavior: resolve existing symlink destinations
and allow verified targets that are regular and writable, while refusing
dangling, unresolvable, escaped, or non-regular targets. Keep refusal notices
naming the snapshot path and reason, and do not reject a symlink solely because
it is a symlink.
- Around line 252-263: Update the credential control in the Settings design to
be capability-aware rather than universal, using the client capability
definitions from client toggle matrix 002. For Kimi Code, replace the
environment-variable reference/export affordance with a loopback-only
placeholder or an explicit manual-secret path, and ensure the UI does not imply
serialized-secret support for that client.
- Around line 122-126: Update the narrow-window tab-count statement from 11 to
12 to match the route list, and revise the surrounding overflow and
accessibility assumptions where they depend on the incorrect count. Preserve the
conclusion that tabs wrap rather than scroll and that wrapped tabs remain
keyboard-accessible.
- Around line 283-285: Update the UX design’s `해제` cleanup description and
confirmation text to specify client-specific side effects rather than promising
only surgical block removal: include Kimi’s model alias/default deletions and
Gajae’s related `config.yml` reference reverts, while preserving the defined
meanings of `적용`, `해제`, `되돌리기`, and `복원`.
In `@devlog/_plan/260802_codex_set_prompt_composer/000_plan.md`:
- Around line 36-39: Update
devlog/_plan/260802_codex_set_prompt_composer/000_plan.md lines 36-39 to
describe layers without upstream off-switches as rendering without a user-facing
switch, rather than permanently on. Update
devlog/_plan/260802_codex_set_prompt_composer/005_ux_design.md lines 30-40 to
expose each runtime-conditional layer’s current availability and avoid labeling
it “always on” when its content may be absent.
- Around line 3-8: Remove the machine-local checkout path from the upstream
provenance in devlog/_plan/260802_codex_set_prompt_composer/000_plan.md lines
3-8, retaining only the portable repository identity and commit SHA. Apply the
same cleanup to
devlog/_plan/260802_codex_set_prompt_composer/001_prompt_layer_inventory.md
lines 3-4 by removing the repeated absolute path and preserving the repository
identity.
In `@devlog/_plan/260802_codex_set_prompt_composer/001_prompt_layer_inventory.md`:
- Around line 18-48: The Skills layer entry in the inventory must not claim a
fixed assembly position. Update the Skills descriptor to use order null via
LayerDescriptor.order, and ensure the UI does not display or promise a fixed
index for the skills_instructions extension.
- Around line 183-197: Extend the prompt-layer DTO and snapshot contract used by
LAYER_INVENTORY to include runtime availability/state for AGENTS.md, realtime,
plugins, and model-switch rows. Update the serialization and consuming tests in
cases 5 and 2 to populate and validate this status; if runtime data cannot be
provided, explicitly redefine those rows as static descriptions rather than
implying current availability.
In `@devlog/_plan/260802_codex_set_prompt_composer/005_ux_design.md`:
- Around line 30-54: Update the UX mockup to include every inventory layer
defined in `001`, specifically model-switch, context-window-guidance, realtime,
environments-instructions, and tools, or explicitly label the mockup as
illustrative rather than exhaustive. Correct the “Three row kinds” wording to
reflect the four listed kinds, while preserving the documented affordances for
each kind.
In `@devlog/_plan/260802_codex_set_prompt_composer/010_wp1_prompt_layers_core.md`:
- Around line 310-325: Revise the prompt-layer storage section so the final
contract presents only developer_instructions. Move the model_instructions_file
example and its explanatory rejection under a clearly labeled “Rejected
alternative — do not implement” heading, or remove that design entirely,
ensuring it is not positioned near the final storage requirements.
- Around line 472-483: The revision construction must use unambiguous framing
instead of the in-band “\0absent” sentinel and delimiter-only concatenation.
Update the revision hashing specification to encode each file’s existence flag
and byte length explicitly, or hash each file separately with its existence flag
before combining the digests, while continuing to cover the complete config and
store bytes.
- Around line 501-510: The transaction write flow must preserve existing
symlinks and reject dangling links. Update the advisory-lock workflow and
atomic-write steps for config.toml and opencodex-prompt.json to resolve
destinations through the shared symlink-safe resolver before writing, ensuring
renames target the resolved files rather than replacing links; add regression
coverage for symlink preservation and dangling symlink rejection.
- Around line 565-573: Update the journal envelope format so each complete file
image uses an explicit byte-safe encoding such as base64 or hex, includes its
original byte length, and is decoded only after checksum validation. Ensure the
checksum covers the serialized envelope containing these encoded values,
preserving exact bytes including invalid UTF-8 and control bytes during
recovery.
In `@devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md`:
- Around line 26-27: Update the plugins descriptor example to match the
runtime-conditional entry defined by LAYER_INVENTORY in prompt-layers.ts: use
class runtime-conditional, set key to null, and set default to null while
preserving its id and ordering.
- Around line 89-95: Define a shared normalized UTF-8 byte-size helper and use
it for the 64 KiB body and 128 KiB composed limits, measuring the enabled
composed projection including separators; update
devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md lines
89-95 accordingly. In
devlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.md lines
67-85, call the same helper instead of duplicating client-side counting logic.
Add boundary tests covering non-ASCII and non-BMP text at both per-layer and
composed limits.
In `@devlog/_plan/260802_codex_set_prompt_composer/060_wp6_presets_and_linter.md`:
- Around line 64-72: Update the linter rules represented by identity,
foreign-tool, approval-vocab, and environment to perform case-insensitive
matching, while preserving their existing detection behavior. Add positive
coverage for mixed-case identity, tool, approval, and environment inputs,
including variants such as “You are Claude,” “READ tool,” and “Ask Mode.”
In
`@devlog/_plan/260802_codex_set_prompt_composer/070_wp7_docs_and_verification.md`:
- Line 76: Update acceptance item 5 in the verification plan to state that
config-toggle layers are switchable, replacing the broader “built-in layers
switchable” wording. Leave item 10 unchanged as the proof that other layer
classes cannot be disabled through the API or UI.
In `@devlog/_plan/260802_wt1_update_path_star_prompt/000_plan.md`:
- Around line 8-9: Resolve the conflicting priority language in the plan: either
make Bug C mandatory throughout, or, if it remains optional, update the opening
scope statement and the Bug C section so only Bugs A and B are described as
must-fix. Keep the policy consistent across the scope summary and Bug C details.
- Line 3: Replace the absolute developer-specific path in the Worktree entry
with a repository-relative worktree name, while preserving the branch and
base-branch information.
In `@devlog/_plan/260802_wt1_update_path_star_prompt/010_implementation.md`:
- Around line 23-32: The deferral cooldown policy is unresolved, so choose and
document one before implementing the record flow. Use a once-per-version policy:
have printAgentDeferral() persist the current version, suppress only
matching-version records, and show the deferral for missing, malformed, or
different-version records. Update the activation tests and AGENTS.md wording to
reflect this policy while preserving the interactive prompt and existing marker
semantics.
In `@devlog/_plan/260802_wt2_zero_leak_bounds/000_plan.md`:
- Around line 27-33: The Claim ledger entry for Claim 1 needs evidence
supporting its “code-verified (prior unit)” status. Update the Source field to
include the prior-unit path or exact test paths that performed the verification,
while retaining the existing tracker and PR-body references.
- Line 18: Update the stale atomicWriteFileAsync reference in
devlog/_plan/260802_wt2_zero_leak_bounds/000_plan.md:18-18 and
devlog/_plan/260802_wt2_zero_leak_bounds/010_implementation.md:11-11 to use its
current location at src/config.ts:240-297 or identify the symbol by name,
preserving the existing roadmap guidance.
In `@devlog/_plan/260802_wt2_zero_leak_bounds/010_implementation.md`:
- Around line 5-11: Clarify the “Landing order (dependency-ordered, not
effort-bucketed)” heading to match the stated relationships: either rename it to
a verification/landing-order heading if `#841` is only preferred first, or
document the actual dependency edges among the listed items. Keep the existing
ordering and rationale unless dependencies are explicitly added.
- Around line 13-18: The acceptance criteria section should be replaced with a
PR-specific matrix that maps each criterion only to the behavior covered by that
PR, separating `#847` stream semantics, `#845` pinning/eviction, `#843` mapping
preservation, and `#841` chain preservation. Add explicit `#840` checks verifying
timeout memo release and cleanup, using the relevant symbols or test targets
from each implementation, while retaining oversized-input, boundary, and
retention coverage where applicable.
In `@devlog/_plan/260802_wt3_provider_wire/000_plan.md`:
- Around line 8-12: Update Bug A’s claim ledger so gpt-5.4 remains the verified
model, while gpt-5.6-sol is described as conditional or lead-only rather than a
confirmed Responses-only failure. Specify that model-specific routing for
gpt-5.6-sol activates only when the request-shape probe identifies the relevant
Responses API requirements.
- Around line 14-18: Revise the plan’s DeepSeek capability wording to
distinguish implementation policy from verified upstream behavior: state that
DeepSeek is treated as unsupported and service_tier is stripped, without
claiming DeepSeek explicitly rejects the field. Keep the upstream rejection
status and whether issue `#875` shares this root cause unresolved pending
verification.
- Around line 3-4: Remove the developer-specific absolute filesystem path from
the Worktree entry in the roadmap, while preserving the branch name
`codex/wt3-provider-wire`; replace it with a repository-relative or generic
worktree description.
In `@devlog/_plan/260802_wt3_provider_wire/010_implementation.md`:
- Around line 27-31: Extend the serialized-payload tests for the DeepSeek
Responses request to cover fastMode disabled with a caller-supplied
service_tier, asserting the field is stripped rather than forwarded. Preserve
the existing DeepSeek fastMode case, canonical OpenAI injection case, and
unclassified-provider caller-value preservation case.
- Around line 7-17: Define explicit routing for unknown or newly released models
in the github-copilot provider flow instead of silently using the provider-wide
openai-chat default. Update the relevant registry routing symbol to use a
capability source or fail-safe behavior that prevents potentially Responses-only
models from reaching /chat/completions, and add a regression test covering an
unlisted model while preserving chat routing for explicitly chat-served models.
- Around line 37-39: Expand the tests for generated profiles and picker rows to
cover every rule in the model-info change: add fixtures for a sub-1M effective
window incorrectly containing `[1m]`, mixed-case marker spellings, and provider
caps below 1M. Verify markers are removed or ignored case-insensitively and
remain absent whenever the provider cap keeps the effective window below 1M,
while preserving the existing positive cases for the three 1M models.
In `@devlog/_plan/260802_wt4_server_config_security/020_wp2_execution_notes.md`:
- Around line 12-16: The resolveWriteTarget flow must reject existing unresolved
symlinks instead of replacing them. After realpathSync fails, use lstatSync or
equivalent to distinguish an absent path from an existing symbolic link: allow
the literal path only when absent, and refuse unresolved symlinks; apply the
same correction to the repeated decision in the plan. Update the
dangling-symlink test expectation from replacement to refusal.
In `@devlog/_plan/260802_wt5_windows_service_doctor/000_plan.md`:
- Line 25: Update the Windows scheduler verification entry in the plan table to
use reported or pending re-verification instead of code-verified, since its
evidence is only the PR body and an author claim. Keep the existing claim and
source unchanged.
- Line 3: Update the Worktree entry in the plan document to remove the personal
absolute filesystem path while preserving the branch relationship; replace it
with repository-relative wording that is valid for all contributors.
In `@devlog/_plan/260802_wt5_windows_service_doctor/010_implementation.md`:
- Around line 36-38: Update the Verification gate to require a named Windows CI
job or command in addition to typecheck and the existing tests. Require Windows
scheduler and WinSW focused tests, including
tests/windows-scheduler-install-verification.test.ts and equivalent launcher
tests, plus the live startup-protection validation referenced in line 18.
- Around line 9-17: Expand the service implementation plan around scheduler
registration and post-create verification with a state matrix that explicitly
classifies retryable versus terminal visibility, XML health, conflict, asset,
and SCM states. Define a finite maximum-attempt or deadline budget and backoff,
require ownership validation immediately before every late reconciliation write,
and stop when ownership changes. Map each acceptance test to one specific
scripted state or interleaving scenario, including transient recovery, conflict,
missing asset, unknown SCM, and ownership-change cases.
- Around line 24-28: Define a persisted optional bunSource field with only
override, bundled, or process values across the five launcher artifacts and
management response, reusing DurableBunRuntime.source’s allowlist. Update
launcher generation and src/server/management/system-routes.ts to record and
expose it, and update src/cli/status.ts to report the recorded value without
calling durableBunRuntime(); missing or invalid values must be reported as
unknown. Add fixtures and tests covering each launcher, invalid markers,
management output, and legacy payloads.
In `@src/codex/prompt-layers.ts`:
- Around line 202-220: Update decodeBasicString to reject raw control
characters, including LF, tab, DEL, and C1 controls, and reject unpaired UTF-16
surrogates while processing non-escaped characters. Continue permitting newlines
only when represented by the existing \n escape, and add regression tests
covering raw LF, tab, and surrogate inputs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5db48c02-76f3-4bad-8d7e-37f80bff407c
📒 Files selected for processing (47)
devlog/_plan/260802_client_toggle_api/000_plan.mddevlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.mddevlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.mddevlog/_plan/260802_client_toggle_api/003_api_design_options.mddevlog/_plan/260802_client_toggle_api/004_ux_design.mddevlog/_plan/260802_codex_set_prompt_composer/000_plan.mddevlog/_plan/260802_codex_set_prompt_composer/001_prompt_layer_inventory.mddevlog/_plan/260802_codex_set_prompt_composer/002_injection_paths_and_presets.mddevlog/_plan/260802_codex_set_prompt_composer/003_config_write_semantics.mddevlog/_plan/260802_codex_set_prompt_composer/004_surface_inventory.mddevlog/_plan/260802_codex_set_prompt_composer/005_ux_design.mddevlog/_plan/260802_codex_set_prompt_composer/010_wp1_prompt_layers_core.mddevlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.mddevlog/_plan/260802_codex_set_prompt_composer/030_wp3_page_shell.mddevlog/_plan/260802_codex_set_prompt_composer/040_wp4_layer_rows.mddevlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.mddevlog/_plan/260802_codex_set_prompt_composer/060_wp6_presets_and_linter.mddevlog/_plan/260802_codex_set_prompt_composer/070_wp7_docs_and_verification.mddevlog/_plan/260802_codex_set_prompt_composer/090_theme_deferred.mddevlog/_plan/260802_wt1_update_path_star_prompt/000_plan.mddevlog/_plan/260802_wt1_update_path_star_prompt/010_implementation.mddevlog/_plan/260802_wt2_zero_leak_bounds/000_plan.mddevlog/_plan/260802_wt2_zero_leak_bounds/010_implementation.mddevlog/_plan/260802_wt3_provider_wire/000_plan.mddevlog/_plan/260802_wt3_provider_wire/010_implementation.mddevlog/_plan/260802_wt4_server_config_security/000_plan.mddevlog/_plan/260802_wt4_server_config_security/010_implementation.mddevlog/_plan/260802_wt4_server_config_security/011_wp1_execution_notes.mddevlog/_plan/260802_wt4_server_config_security/020_wp2_execution_notes.mddevlog/_plan/260802_wt5_windows_service_doctor/000_plan.mddevlog/_plan/260802_wt5_windows_service_doctor/010_implementation.mddocs-site/src/content/docs/ja/reference/configuration/server.mddocs-site/src/content/docs/ko/reference/configuration/server.mddocs-site/src/content/docs/reference/configuration/server.mddocs-site/src/content/docs/ru/reference/configuration/server.mddocs-site/src/content/docs/zh-cn/reference/configuration/server.mdsrc/codex/prompt-layers.tssrc/config.tssrc/responses/state.tssrc/server/auth-cors.tssrc/types.tstests/codex-prompt-layers.test.tstests/config.test.tstests/responses-state.test.tstests/server-auth.test.tstests/server-loopback-host-gate.test.tstests/test-home-guard.test.ts
| - Invariants to carry over: no secret serialized, additive merge only, | ||
| preserve unknown fields, atomic write + backup, never touch blocks we did | ||
| not write. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Scope the write invariants per client format.
The unconditional preserve unknown fields rule conflicts with devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md Lines 120-124 and 138-144. Kimi loses comments and formatting during serialization, while Gajae requires schema-known fields and rejects unknown fields.
Split these into global invariants and format-specific rules. Otherwise the implementation cycle may generate invalid Gajae blocks or promise preservation that the serializer cannot provide.
🤖 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 91 - 93,
Revise the invariant list in the plan to separate global guarantees from
client-format-specific behavior: retain secret exclusion, additive merging,
atomic write with backup, and avoiding untouched blocks globally; move
unknown-field preservation into the applicable format rules, documenting Kimi’s
loss of comments/formatting and Gajae’s schema-only, unknown-field-rejecting
serialization in alignment with the client toggle matrix.
| 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. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
git ls-files 'devlog/_plan/260802_client_toggle_api/*'
printf '%s\n' '--- source map ---'
ast-grep outline devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md
ast-grep outline devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md
printf '%s\n' '--- analysis excerpt ---'
sed -n '1,95p' devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md
printf '%s\n' '--- client matrix excerpt ---'
sed -n '1,130p' devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.mdRepository: lidge-jun/opencodex
Length of output: 15357
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- exact disputed lines ---'
nl -ba devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md | sed -n '26,78p'
printf '%s\n' '--- remaining client matrix ---'
nl -ba devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md | sed -n '90,230p'
printf '%s\n' '--- cross-document references ---'
rg -n -C 3 'Kimi|Gajae|additive|model alias|default_model|default_provider|strict|schema|cleanup|provider entry' devlog/_plan/260802_client_toggle_apiRepository: lidge-jun/opencodex
Length of output: 41762
Separate Kimi Code and Gajae Code from the additive-provider pattern.
The cc-switch evidence covers OpenClaw and Hermes Agent, not Kimi Code or Gajae Code. Kimi provider removal also removes referencing model aliases, default_model, and default_provider. Gajae Code requires schema-known fields and cleanup of related models.yml references. Mark the classification at lines 32–34 as a hypothesis, and revise lines 71–73 to require each client’s complete add and remove contract.
🤖 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 classification of Kimi Code and Gajae Code as
additive-provider clients is unsupported and should be marked as a hypothesis.
Update the related plan guidance to require documenting each client’s complete
add and remove contract, including Kimi’s model aliases, default_model, and
default_provider cleanup and Gajae Code’s schema fields and models.yml
references.
| | Non-interactive add | No (wizard/dashboard only) | `openclaw config set/patch` | `kimi provider add <registry-url>` (registry import only) | `gjc setup provider` | | ||
| | Non-interactive remove | No | `openclaw config unset` | `kimi provider remove` (cascades) | **No** (file edit required) | | ||
| | Toggle channel of choice | file writer (or dashboard API) | **client CLI** | **registry endpoint + client CLI** | CLI add + file-writer remove | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Distinguish Hermes raw config save from provider CRUD.
The matrix says Hermes has no non-interactive add path, but Lines 38-43 describe an authenticated dashboard with raw YAML save, and Line 21 lists the dashboard API as a toggle option.
Use wording such as no dedicated provider CRUD; authenticated raw config save exists if that is the intended distinction.
🤖 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/002_client_toggle_matrix.md` around
lines 19 - 21, Update the Hermes entries in the client-toggle matrix to
distinguish dedicated non-interactive provider CRUD from authenticated raw
configuration editing: replace the “No” add-path wording with language
indicating no dedicated provider CRUD while acknowledging authenticated raw
YAML/dashboard save, and keep the dashboard API as a valid toggle mechanism.
| - **Non-interactive surface**: `kimi provider add <registry-url> | ||
| --api-key <key>` imports a **custom `api.json` registry** — the CLI then | ||
| creates the provider/model entries itself and refreshes them from the same | ||
| URL on later startups. `kimi provider remove <id>` cascades: provider + | ||
| referencing model aliases + `default_model`/`default_provider` | ||
| ([core-impl.ts#L734-L762](https://github.com/MoonshotAI/kimi-code/blob/e22479a62eed9c3b78a67b313f4332c2c0ba9670/packages/agent-core/src/rpc/core-impl.ts#L734-L762)). | ||
| There is **no** generic `kimi provider add --type openai ...`; a local proxy | ||
| must be hand-written to TOML *or* served as a registry. **Design | ||
| consequence: opencodex could serve an `api.json` registry endpoint, making | ||
| the toggle `kimi provider add http://127.0.0.1:10100/...` / `kimi provider | ||
| remove opencodex` — vendor-owned writes, atomic by construction, with | ||
| cascade cleanup and catalog refresh for free.** The registry schema itself | ||
| is an open question for the implementation cycle. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- target plan section ---'
sed -n '90,130p' devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md
printf '%s\n' '--- candidate provider command references ---'
rg -n -C 4 'provider (add|remove)|api\.json|api-key|default_model|default_provider' \
packages devlog 2>/dev/null | head -n 500
printf '%s\n' '--- relevant source files ---'
git ls-files | rg '(^|/)(core-impl\.ts|.*provider.*|.*registry.*|.*config.*)$' | head -n 300Repository: lidge-jun/opencodex
Length of output: 50377
🏁 Script executed:
#!/bin/bash
set -eu
base='https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670'
printf '%s\n' '--- complete target section ---'
sed -n '100,145p' devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md
printf '%s\n' '--- referenced upstream mutation code ---'
curl -fsSL "$base/packages/agent-core/src/rpc/core-impl.ts" |
sed -n '700,790p'
printf '%s\n' '--- upstream command/schema references ---'
for path in \
packages/agent-core/src/config/toml.ts \
packages/agent-core/src/rpc/core.ts \
packages/agent-core/src/cli.ts \
packages/agent-core/src/commands/provider.ts \
packages/agent-core/src/commands/providers.ts
do
printf '\n--- %s ---\n' "$path"
curl -fsSL "$base/$path" | rg -n -C 5 \
'provider add|provider remove|api\.json|api-key|registry|default_model|default_provider' || true
doneRepository: lidge-jun/opencodex
Length of output: 6875
🏁 Script executed:
#!/bin/bash
set -eu
base='https://api.github.com/repos/MoonshotAI/kimi-code/git/trees/e22479a62eed9c3b78a67b313f4332c2c0ba9670?recursive=1'
printf '%s\n' '--- upstream paths containing provider, registry, or command ---'
curl -fsSL "$base" |
jq -r '.tree[] | select(.path | test("provider|registry|command|cli"; "i")) | .path' |
head -n 400
printf '%s\n' '--- exact config mutation implementation ---'
curl -fsSL 'https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670/packages/agent-core/src/config/toml.ts' |
sed -n '430,535p'
printf '%s\n' '--- repository-wide provider command text from GitHub code archive paths ---'
curl -fsSL "$base" |
jq -r '.tree[] | select(.type == "blob" and (.path | test("\\.(ts|tsx|js|json|md)$"))) | .path' |
while read -r path; do
content=$(curl -fsSL "https://raw.githubusercontent.com/MoonshotAI/kimi-code/e22479a62eed9c3b78a67b313f4332c2c0ba9670/$path" || true)
if printf '%s' "$content" | rg -q 'api\.json|provider add|RemoveKimiProvider|addKimiProvider|registry-url'; then
printf '%s\n' "$path"
printf '%s\n' "$content" | rg -n -C 3 'api\.json|provider add|RemoveKimiProvider|addKimiProvider|registry-url' | head -n 80
fi
doneRepository: lidge-jun/opencodex
Length of output: 4315
🌐 Web query:
MoonshotAI kimi-code "provider add" "api.json"
💡 Result:
To add a custom registry in the form of an api.json file to the Kimi Code CLI, you use the kimi provider add command [1][2]. This command bulk-imports providers from your specified registry URL, automatically configuring the necessary [providers.] and [models.] entries in your configuration [1][2]. You can execute the command by providing the registry URL and an API key: kimi provider add https://your-registry-url/path/to/api.json --api-key YOUR_KEY Alternatively, you can set the API key using an environment variable if you prefer not to pass it as an argument: KIMI_REGISTRY_API_KEY=YOUR_KEY kimi provider add https://your-registry-url/path/to/api.json When this command is run, the CLI fetches the registry and writes source metadata to your configuration. This ensures that on subsequent startups, the Kimi Code CLI automatically refreshes providers and models from that same URL, keeping additions, removals, and metadata updates in sync [3][1][2]. If a provider ID already exists in your configuration, it will be removed and re-created using the information from the registry [2].
Citations:
- 1: https://www.kimi.com/code/docs/en/kimi-code-cli/reference/kimi-command.html
- 2: https://moonshotai.github.io/kimi-code/en/reference/kimi-command
- 3: https://moonshotai.github.io/kimi-code/en/configuration/providers.html
🌐 Web query:
site:github.com/MoonshotAI/kimi-code "api-key" "provider add"
💡 Result:
The phrase "provider add" typically refers to a command-line interface (CLI) operation used to configure an AI service provider within a software environment, most notably in the Kimi Code CLI [1][2]. In the context of the Kimi Code CLI, the command kimi provider add is used to manually register an AI service provider [1]. This process allows the tool to use various LLM providers (such as Anthropic or OpenAI) by supplying the necessary metadata and credentials [1]. Key details regarding this command include: - Functionality: It allows users to bulk-import providers from a registry (e.g., a JSON file) or add specific providers individually [1]. - Required Arguments: The command typically requires the provider's identifier (e.g., anthropic) and an API key for authentication [1]. - Configuration: It often allows for optional flags like --base-url to override endpoints or --default-model to set a preferred model for that provider [1]. - Alternatives: In addition to the CLI command, interactive management is often available through a /provider slash command within the tool's terminal user interface (TUI) to view, add, or remove providers [3]. If you are encountering this phrase outside of the Kimi Code ecosystem, it likely follows a similar pattern in other CLI tools designed to manage API integrations or cloud service configurations, where "provider" represents an external service (like a cloud platform or LLM API) and "add" is the action to register its credentials for use within the local application [1][4][5].
Citations:
- 1: https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/reference/kimi-command.md
- 2: https://github.com/MoonshotAI/kimi-code/releases/tag/%40moonshot-ai%2Fkimi-code%400.7.0
- 3: MoonshotAI/kimi-code@7bffa2d
- 4: feat(kimi-code): rework /logout into a provider picker and add /disconnect alias MoonshotAI/kimi-code#76
- 5: feat(kimi-code): add /connect command with bundled model catalog MoonshotAI/kimi-code#30
🌐 Web query:
site:github.com/MoonshotAI/kimi-code "registry-url" provider
💡 Result:
In the context of software development, registry-url is a configuration setting used to specify the location of a remote package registry or service catalog [1]. Its function and usage depend on the specific tool or environment: 1. Package Managers (e.g., npm, pnpm): The registry-url property is used in CI/CD configurations (such as GitHub Actions) to define the base URL for the package registry (e.g., https://registry.npmjs.org) from which dependencies are fetched [1]. 2. Kimi Code CLI: In the Kimi Code project, a registry-url refers to the address of a remote JSON-based catalog (e.g., api.json) [2]. Users can utilize the kimi provider add command followed by this URL to bulk-import multiple AI model providers [2]. The system periodically refreshes providers and models from this same registry URL to keep metadata, additions, and removals synchronized with the upstream source [3][2]. Outside of these specific contexts, the term registry-url is generally descriptive, denoting a URL that points to a centralized repository, registry, or service discovery endpoint [2][1].
Citations:
- 1: https://github.com/MoonshotAI/kimi-code/blob/5cc194956f6f9752d172aa4994385d2d2e7a066f/.github/workflows/release.yml
- 2: https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/reference/kimi-command.md
- 3: https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/configuration/providers.md
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- Kimi command reference ---'
curl -fsSL 'https://moonshotai.github.io/kimi-code/en/reference/kimi-command.html' |
sed 's/<[^>]*>/ /g' |
tr -s ' ' |
rg -n -C 8 'provider add|provider remove|api-key|registry|KIMI_REGISTRY_API_KEY' |
head -n 160
printf '%s\n' '--- Kimi provider configuration reference ---'
curl -fsSL 'https://moonshotai.github.io/kimi-code/en/configuration/providers.html' |
sed 's/<[^>]*>/ /g' |
tr -s ' ' |
rg -n -C 12 'api\.json|registry|provider add|api-key|KIMI_REGISTRY_API_KEY|refresh|remove' |
head -n 240
printf '%s\n' '--- current source reference links and registry wording ---'
for url in \
'https://raw.githubusercontent.com/MoonshotAI/kimi-code/main/docs/en/reference/kimi-command.md' \
'https://raw.githubusercontent.com/MoonshotAI/kimi-code/main/docs/en/configuration/providers.md'
do
printf '\n--- %s ---\n' "$url"
curl -fsSL "$url" |
rg -n -C 10 'api\.json|registry|provider add|provider remove|api-key|KIMI_REGISTRY_API_KEY|refresh|remove'
doneRepository: lidge-jun/opencodex
Length of output: 43133
🌐 Web query:
site:moonshotai.github.io/kimi-code api.json registry schema providers models
💡 Result:
In the context of the Kimi Code CLI, api.json refers to a custom registry file format used to bulk-import and manage API providers and their associated models [1][2]. Key aspects of the api.json registry schema include: Registry Functionality: Users can import providers and models by providing a URL to an api.json file, typically via the kimi provider add command [1][2]. The CLI fetches this registry, automatically creates the corresponding [providers] and [models] entries in the configuration file, and maintains a source link to automatically refresh these providers and models upon subsequent startups [3][1][2]. Integration with CLI Configuration: Providers: Each entry in the providers table of the configuration defines an API provider (e.g., Kimi, OpenAI, Anthropic, Google GenAI), including its type, base URL, and credentials [3][4]. Models: Each entry in the models table defines a model alias, which maps to a provider defined in the providers table and specifies the model identifier, maximum context size, and other capabilities [3][4]. Registry Maintenance: The registry mechanism ensures that upstream additions, removals, and metadata changes—such as new models or updated configurations—are synced automatically [3][5]. The client includes a User-Agent in its requests to these registries, allowing providers to identify the client version [5]. For programmatic processing, the CLI can output the current provider and model configurations in JSON format using the --json flag with the kimi provider list command [1].
Citations:
- 1: https://moonshotai.github.io/kimi-code/en/reference/kimi-command
- 2: https://moonshotai.github.io/kimi-code/en/reference/kimi-command.html
- 3: https://moonshotai.github.io/kimi-code/en/configuration/providers.html
- 4: https://moonshotai.github.io/kimi-code/en/configuration/config-files.html
- 5: https://moonshotai.github.io/kimi-code/en/release-notes/changelog.html
🌐 Web query:
site:github.com/MoonshotAI/kimi-code "CustomProviderRegistry" OR "ProviderRegistry" "api.json"
💡 Result:
In the context of the Kimi Code project, a Custom Provider Registry—often represented by an api.json file—is a mechanism used to bulk-import and manage third-party AI model providers and their associated models [1][2]. Key aspects of the Custom Provider Registry include: Usage and Management: Users can import providers from a custom api.json registry URL using the Kimi Code CLI via the command: kimi provider add <URL> --api-key <KEY> [1]. This process fetches the registry and configures providers and models within the environment [1]. The system supports automatic synchronization, where providers imported from a custom registry are refreshed on startup or via scheduled background tasks to update model information and handle added or removed providers [3][4]. Technical Implementation: The api.json file defines the providers and models that the client should import. When models are imported, the system maps specific fields to internal configurations [5]. For example, fields such as support_efforts and default_effort are parsed from the api.json model entries to support thinking-effort features [5]. The registry fetch mechanism is designed to be secure and identifiable, sending a product User-Agent header (e.g., kimi-code-cli/) during requests to ensure compatibility with WAFs and proper identification by the server [6][7]. The logic for handling these registries is centralized within the @moonshot-ai/kimi-code-oauth package, which provides utilities for fetching, parsing, and refreshing registry data across both the CLI and server environments [4][6].
Citations:
- 1: https://github.com/MoonshotAI/kimi-code/blob/main/docs/en/reference/kimi-command.md
- 2: https://github.com/MoonshotAI/kimi-code/releases/tag/%40moonshot-ai%2Fkimi-code%400.8.0
- 3: MoonshotAI/kimi-code@d1ba145
- 4: MoonshotAI/kimi-code@14d9e98
- 5: feat(oauth): parse support_efforts/default_effort in custom registry import MoonshotAI/kimi-code#1564
- 6: MoonshotAI/kimi-code@d601847
- 7: fix: send the product User-Agent on provider registry and catalog fetches MoonshotAI/kimi-code#1597
🌐 Web query:
site:github.com/MoonshotAI/kimi-code "KIMI_REGISTRY_API_KEY" "source"
💡 Result:
KIMI_REGISTRY_API_KEY is an environment variable used by the Kimi Code CLI to provide the necessary authentication token (Bearer token) when interacting with a custom model registry [1]. In Kimi Code, when a user imports providers using a custom registry (typically provided via an api.json file), the CLI may require an API key to access that registry [1]. Instead of passing the key explicitly via the --api-key command-line option every time, users can set the KIMI_REGISTRY_API_KEY environment variable [1]. This is particularly useful for automated environments such as CI/CD pipelines or when using shell configuration files like.envrc [1]. Example usage: KIMI_REGISTRY_API_KEY=YOUR_KEY kimi provider add https://registry.example.com/v1/models/api.json [1] When using commands like kimi provider add, the CLI falls back to the value of KIMI_REGISTRY_API_KEY if the --api-key flag is not provided [1].
Citations:
Keep the registry toggle as a candidate until its wire contract is specified.
kimi provider add <url> requires --api-key <key> or KIMI_REGISTRY_API_KEY; include one in the loopback example, even if the local registry accepts a placeholder. Define the api.json payload, provider ID, refresh matching, credential handling, and removal behavior before using this command as an implementation contract. The upstream removal code confirms cascade cleanup, but it does not confirm that the registry will create the provider ID opencodex.
🤖 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/002_client_toggle_matrix.md` around
lines 107 - 119, Keep the registry-toggle proposal explicitly provisional until
its wire contract is defined. Update the loopback command example to include
--api-key (or KIMI_REGISTRY_API_KEY), and document the required contract
details: api.json payload, provider ID, refresh matching, credential handling,
and removal behavior; do not assume the registry creates the opencodex ID.
| 2. **Only OpenClaw hot-reloads.** Every other client applies the toggle on | ||
| the next session (Hermes, Gajae) or explicit `/reload` (Kimi v1). The GUI | ||
| copy must say "applies to new sessions" everywhere except OpenClaw. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
file="devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md"
printf '%s\n' '--- target section ---'
sed -n '25,45p;165,185p' "$file"
printf '%s\n' '--- Hermes references ---'
rg -n -i -C 3 'Hermes|hot.?reload|live read|new session|/reload|GUI copy' devlog/_planRepository: lidge-jun/opencodex
Length of output: 41343
🏁 Script executed:
#!/bin/bash
set -eu
file="devlog/_plan/260802_client_toggle_api/002_client_toggle_matrix.md"
ux="devlog/_plan/260802_client_toggle_api/004_ux_design.md"
analysis="devlog/_plan/260802_client_toggle_api/001_ccswitch_toggle_analysis.md"
printf '%s\n' '--- matrix and Hermes context ---'
sed -n '10,21p;23,52p;170,186p' "$file"
printf '%s\n' '--- GUI apply-semantics context ---'
sed -n '235,250p' "$ux"
printf '%s\n' '--- Hermes source/document links ---'
rg -n -i -C 3 'hermes_cli/config.py|partial live|gateway|config hot|session start|configuring-models' \
"$file" "$ux" "$analysis"Repository: lidge-jun/opencodex
Length of output: 11590
Qualify Hermes reload semantics. In 002_client_toggle_matrix.md:17,34-37,177-179, distinguish Hermes CLI sessions, which read configuration at session start, from the gateway, which performs partial live reads. State that provider changes require a new session or restart, but do not describe Hermes as having no live reads. Update 004_ux_design.md:243-245 with the same distinction instead of saying only 새 세션부터 적용됩니다.
🤖 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/002_client_toggle_matrix.md` around
lines 177 - 179, Update the Hermes entries in 002_client_toggle_matrix.md and
004_ux_design.md to distinguish CLI sessions, which read configuration at
session start, from the gateway, which performs partial live reads. State that
provider changes require a new session or gateway restart, while avoiding
language that claims Hermes has no live reads; replace the blanket “새 세션부터
적용됩니다” wording with this qualified behavior.
| @@ -0,0 +1,32 @@ | |||
| # wt5 — Windows scheduler settle + Bun provenance diagnostics (research) | |||
|
|
|||
| Worktree: `/Users/jun/.codex/worktrees/260802-wt5-windows-service` (branch `codex/wt5-windows-service`, off `dev`). | |||
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Remove the personal worktree path from the committed plan.
Line 3 exposes a local username and filesystem layout. It also makes the document stale for other contributors. Keep the branch relationship and replace the path with repository-relative text.
Proposed documentation change
-Worktree: `/Users/jun/.codex/worktrees/260802-wt5-windows-service` (branch `codex/wt5-windows-service`, off `dev`).
+Branch `codex/wt5-windows-service` is based on `dev`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Worktree: `/Users/jun/.codex/worktrees/260802-wt5-windows-service` (branch `codex/wt5-windows-service`, off `dev`). | |
| Branch `codex/wt5-windows-service` is based on `dev`. |
🤖 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_wt5_windows_service_doctor/000_plan.md` at line 3, Update
the Worktree entry in the plan document to remove the personal absolute
filesystem path while preserving the branch relationship; replace it with
repository-relative wording that is valid for all contributors.
|
|
||
| | # | Claim | Source | Status | | ||
| |---|-------|--------|--------| | ||
| | 1 | Windows scheduler verification can fail to settle on transient states | PR #868 body | code-verified (PR author claims live Windows validation) | |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Align the claim status with its evidence.
Line 25 labels the claim code-verified, but the source is the PR body and the live validation is described as a PR-author claim. Use reported or pending re-verification until direct code and test evidence is recorded.
🤖 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_wt5_windows_service_doctor/000_plan.md` at line 25,
Update the Windows scheduler verification entry in the plan table to use
reported or pending re-verification instead of code-verified, since its evidence
is only the PR body and an author claim. Keep the existing claim and source
unchanged.
| - MODIFY `src/service.ts` (scheduler registration + post-create verification) — retry ONLY transient post-create Task Scheduler visibility/XML health states. | ||
| - PRESERVE fail-closed for: conflicts, missing assets, unknown SCM status. Stop late reconciliation when attempt ownership changes. | ||
| - Tests: scheduler/startup/service/install-verification contracts (PR claims 136 focused; re-verify on rebase). | ||
|
|
||
| Acceptance + activation: | ||
|
|
||
| 1. Transient post-create invisibility settles to installed/viable/running within the retry budget. Activation: fault-injection test with scripted transient states. | ||
| 2. Conflict / missing asset / unknown SCM each still fail closed with no retry storm. Activation: three adversarial tests. | ||
| 3. Ownership change mid-reconcile stops late writes. Activation: interleaving test. |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Specify the scheduler state matrix and finite retry bound.
The roadmap does not name the exact retryable states, terminal states, attempt or deadline budget, backoff, or ordering of ownership checks versus late writes. Without these details, an implementation can retry a conflict, missing asset, or unknown SCM result and continue after ownership changes.
Add a table for retryable and terminal states. Define the maximum attempts or deadline. Require an ownership check before every late reconciliation write. Bind each acceptance test to one state.
🤖 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_wt5_windows_service_doctor/010_implementation.md` around
lines 9 - 17, Expand the service implementation plan around scheduler
registration and post-create verification with a state matrix that explicitly
classifies retryable versus terminal visibility, XML health, conflict, asset,
and SCM states. Define a finite maximum-attempt or deadline budget and backoff,
require ownership validation immediately before every late reconciliation write,
and stop when ownership changes. Map each acceptance test to one specific
scripted state or interleaving scenario, including transient recovery, conflict,
missing asset, unknown SCM, and ownership-change cases.
| - MODIFY all five launcher paths to stamp one allowlisted `override | bundled | process` marker: npm Node launcher, Windows scheduler, native WinSW (`src/lib/winsw.ts`), launchd, systemd (`src/service.ts`, `src/lib/bun-runtime.ts`). | ||
| - MODIFY `src/server/management/system-routes.ts` — expose the recorded provenance scalar alongside Bun version/revision. | ||
| - MODIFY doctor/status (`src/cli/status.ts`) — report recorded provenance; legacy payload without the field = unknown/absent. NEVER call `durableBunRuntime()` at report time to guess from the current shell (mislabels the running process). | ||
| - KEEP `bunRevision` informational; conservative `auto-known-bad` for canaries unchanged; eager-relay capability policy untouched. | ||
| - DOCS: `structure/05_gui-and-management-api.md` — provenance trust + backward-compat rule. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 6 \
'bunRevision|durableBunRuntime|durableBunPath|OPENCODEX_BUN_PATH|source' \
src tests
rg -n -C 4 -i \
'launchd|systemd|task scheduler|winsw|npm.*launcher|scheduler' \
src testsRepository: lidge-jun/opencodex
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files \
'src/lib/bun-runtime.ts' \
'src/lib/winsw.ts' \
'src/server/management/system-routes.ts' \
'src/cli/status.ts' \
'src/service.ts' \
'tests/*' \
'devlog/_plan/260802_wt5_windows_service_doctor/010_implementation.md'
printf '%s\n' '--- runtime provenance definitions and uses ---'
rg -n -C 8 \
'DurableBunRuntime|durableBunRuntime|bunRevision|OPENCODEX_BUN_PATH|source' \
src/lib/bun-runtime.ts src/server/management/system-routes.ts src/cli/status.ts src/service.ts
printf '%s\n' '--- launcher construction and environment propagation ---'
rg -n -C 8 \
'build.*(Plist|Unit|Script|Xml)|Environment=|process\\.env|env:|launchd|systemd|schtasks|WinSW|winsw' \
src/lib/winsw.ts src/service.ts src/cli src/serverRepository: lidge-jun/opencodex
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- management route imports and response ---'
sed -n '1,115p' src/server/management/system-routes.ts
printf '%s\n' '--- status types and report assembly ---'
sed -n '1,75p' src/cli/status.ts
sed -n '130,325p' src/cli/status.ts
printf '%s\n' '--- service launcher builders ---'
sed -n '1260,1355p' src/service.ts
sed -n '1355,1490p' src/service.ts
printf '%s\n' '--- WinSW XML builder ---'
sed -n '65,128p' src/lib/winsw.ts
printf '%s\n' '--- launcher-related files and source markers ---'
git ls-files | rg '(^|/)(bin|scripts|src)/|launcher|launchd|systemd|winsw|status|system-routes'
rg -n -C 5 \
'bun_source|BUN_SOURCE|runtime.*source|source.*bundled|source.*override|source.*process|OPENCODEX_BUN_PATH' \
--glob '!devlog/**' --glob '!*.lock' .Repository: lidge-jun/opencodex
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- targeted launcher files ---'
git ls-files | rg '(^|/)(bin|scripts)/|launcher|launchd|systemd|winsw|bun-runtime|system-routes|status'
printf '%s\n' '--- exact source-marker assertions and fixtures ---'
rg -n -C 8 \
'bun_source|BUN_SOURCE|runtime.*source|source.*(bundled|override|process)|OPENCODEX_BUN_PATH|bunVersion|bunRevision' \
tests src/bin bin scripts src 2>/dev/null || true
printf '%s\n' '--- runtime and status tests ---'
sed -n '1,260p' tests/bun-runtime.test.ts
sed -n '1,260p' tests/ocx-launcher-source.test.ts
sed -n '1,260p' tests/cli-status-json.test.tsRepository: lidge-jun/opencodex
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- npm launcher runtime resolution ---'
sed -n '280,390p' bin/ocx.mjs
printf '%s\n' '--- service artifact builders ---'
rg -n '^export function (buildPlist|buildSystemd|buildService|buildWindows|buildWinsw)|durableBunRuntime|Environment=|envLines|OCX_SERVICE' src/service.ts src/lib/winsw.ts
sed -n '560,760p' src/service.ts
sed -n '1180,1325p' src/service.ts
printf '%s\n' '--- launcher tests for artifact contents ---'
rg -n -C 10 \
'buildPlist|buildSystemd|buildWindowsServiceScript|buildWinswXml|bun_source|Environment|OCX_SERVICE|source' \
tests/service.test.ts tests/winsw.test.ts tests/bun-runtime.test.ts tests/ocx-launcher-source.test.tsRepository: lidge-jun/opencodex
Length of output: 50375
Define one persisted bunSource field and validation rule.
DurableBunRuntime.source already defines the allowlist in src/lib/bun-runtime.ts:23-70, but the five launchers do not share a serialized marker. The Windows wrapper only logs an install-time value, while src/cli/status.ts:170 still calls durableBunRuntime() and can report the current shell instead of the running service. Define bunSource?: "override" | "bundled" | "process" for launcher artifacts and the management response. Treat missing or invalid values as unknown. Do not infer the value during reporting.
Add fixtures for bin/ocx.mjs, the Windows scheduler, src/lib/winsw.ts:65-125, launchd, and systemd. Add an invalid-marker fixture and test the management response and legacy payload behavior.
🧰 Tools
🪛 LanguageTool
[style] ~26-~26: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...calar alongside Bun version/revision. - MODIFY doctor/status (src/cli/status.ts) — r...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🤖 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_wt5_windows_service_doctor/010_implementation.md` around
lines 24 - 28, Define a persisted optional bunSource field with only override,
bundled, or process values across the five launcher artifacts and management
response, reusing DurableBunRuntime.source’s allowlist. Update launcher
generation and src/server/management/system-routes.ts to record and expose it,
and update src/cli/status.ts to report the recorded value without calling
durableBunRuntime(); missing or invalid values must be reported as unknown. Add
fixtures and tests covering each launcher, invalid markers, management output,
and legacy payloads.
| ## Verification gate | ||
|
|
||
| `bun run typecheck` + focused doctor/runtime/service/watchdog tests (baseline was 99/99 on the issue thread) + `bun run test`. |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Make Windows execution a required verification gate.
The commands in line 38 can pass on macOS or Linux without exercising Task Scheduler or WinSW. Require a named Windows CI job or command, the scheduler and WinSW focused tests, and the live startup-protection validation listed in line 18.
Include tests/windows-scheduler-install-verification.test.ts and the equivalent launcher tests in the required matrix.
🤖 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_wt5_windows_service_doctor/010_implementation.md` around
lines 36 - 38, Update the Verification gate to require a named Windows CI job or
command in addition to typecheck and the existing tests. Require Windows
scheduler and WinSW focused tests, including
tests/windows-scheduler-install-verification.test.ts and equivalent launcher
tests, plus the live startup-protection validation referenced in line 18.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8261a286b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| ### Bug A — PR #850: browser-extension CORS origins all collapse to `null` | ||
|
|
||
| - Root cause: `URL.origin` serializes `chrome-extension://...` (and other non-special schemes) as the string `"null"` per the WHATWG URL Standard. Comparing that value makes every browser extension origin look identical — the allowlist cannot distinguish the configured extension from any other, while rejecting `*` leaves users no safe path. |
There was a problem hiding this comment.
Move pre-disclosure security notes out of devlog
Before these fixes have shipped, this tracked _plan document identifies the CORS vulnerability's root cause, affected boundary, severity, and remediation, so pushing the commit publishes security triage that repository policy requires keeping in ignored scratch space. Move the wt4 planning and execution notes to .tmp/, and publish only the completed outcome under _fin/ after the fix ships.
AGENTS.md reference: AGENTS.md:L61-L68
Useful? React with 👍 / 👎.
| function assertResolvedTargetAllowed(path: string, target: string): void { | ||
| if (target === path) return; | ||
| assertNotRealHomeUnderTest(dirname(target)); |
There was a problem hiding this comment.
Guard the entire resolved real-home subtree
When an armed test uses a temporary OPENCODEX_HOME whose config symlink targets a nested live path such as ~/.opencodex/backups/config.json, dirname(target) is the nested directory and the guard's exact equality check does not reject it; the subsequent rename therefore overwrites the user's real file. This new symlink-following path should reject resolved targets anywhere inside the protected home, not only files whose immediate parent is the home root, and needs a nested-target regression test.
Useful? React with 👍 / 👎.
| for (const dir of new Set([dirname(path), resolvedDir])) { | ||
| try { | ||
| recoverStaleResponseStateTemps(dir); |
There was a problem hiding this comment.
Match stale temps using the resolved target basename
When responses-state.json is symlinked to a target with a different filename, such as ~/dotfiles/ocx-state.json, atomic writes now leave interrupted temps named ocx-state.json.ocx.<pid>.<seq>.tmp in the resolved directory. This loop scans that directory but recoverStaleResponseStateTemps still hard-codes the responses-state.json.ocx... pattern, so those private state remnants are never reclaimed; pass the resolved basename into recovery or otherwise match the exact target-derived temp name.
Useful? React with 👍 / 👎.
| hash.update("cfg:"); | ||
| hash.update(configBytes ?? "\0absent"); | ||
| hash.update("\nstore:"); | ||
| hash.update(storeBytes ?? "\0absent"); |
There was a problem hiding this comment.
Frame both files before hashing the revision
When file contents contain the separator text, different file pairs can produce the same hash input: for example, config="a\nstore:b", store="c" and config="a", store="b\nstore:c" both hash cfg:a\nstore:b\nstore:c. Once this revision gates prompt-layer mutations, that deterministic ambiguity can make changed files appear current and defeat stale-write detection; length-prefix each value or hash independently before combining them.
Useful? React with 👍 / 👎.
| | > 32 layers | `400 too_many_layers` | | ||
| | id not `[a-z0-9]{6}` | `400 invalid_layer_id` | | ||
| | duplicate id | `400 duplicate_layer_id` | | ||
| | title empty, > 80 chars, or contains a newline | `400 invalid_title` | | ||
| | body > 64 KiB | `400 body_too_large` | | ||
| | composed total > 128 KiB | `400 composed_too_large` | | ||
| | control character in body | `400 invalid_characters` with position | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Define and share normalized UTF-8 byte limits.
The plans specify 64 KiB and 128 KiB limits but do not define how size is measured. A JavaScript .length check counts UTF-16 code units, so "😀".repeat(16385) is 65,540 UTF-8 bytes but only 32,770 UTF-16 units. It bypasses a 64 KiB limit implemented with .length.
devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md#L89-L95: enforce limits after normalization with UTF-8 byte length. Measure the enabled composed projection, including its separators.devlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.md#L67-L85: call the same shared size helper as the route. Do not duplicate client-side counting logic.
Add boundary tests with non-ASCII and non-BMP text at both the per-layer and composed limits.
📍 Affects 2 files
devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md#L89-L95(this comment)devlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.md#L67-L85
🤖 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_codex_set_prompt_composer/020_wp2_management_route.md`
around lines 89 - 95, Define a shared normalized UTF-8 byte-size helper and use
it for the 64 KiB body and 128 KiB composed limits, measuring the enabled
composed projection including separators; update
devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md lines
89-95 accordingly. In
devlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.md lines
67-85, call the same helper instead of duplicating client-side counting logic.
Add boundary tests covering non-ASCII and non-BMP text at both per-layer and
composed limits.
| | Rule | Pattern | Level | Why | | ||
| |---|---|---|---| | ||
| | `identity` | `you are (claude\|grok\|gemini\|gpt-\|chatgpt)` | warn | contradicts base identity — `02_cc_prompt.md:44` | | ||
| | `foreign-tool` | `\b(Read\|Edit\|Write\|Bash\|Glob\|Grep)\s+tool\b` | warn | registry defines tools — `model_info.rs:151` | | ||
| | `placeholder` | `\$\{\{.*?\}\}` | warn | no MiniJinja over instructions — `context.rs:252` | | ||
| | `apply-patch` | `apply_patch` with redefining verbs | warn | same registry argument | | ||
| | `approval-vocab` | `always-approve`, `ask mode`, `acceptEdits` | warn | Codex injects its own — `world_state.rs:114` | | ||
| | `environment` | claims about cwd, date, network, OS | warn | generated later — `world_state.rs:149` | | ||
| | `size` | body > 8 KB | info | **opencodex policy** — see below | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Match compatibility patterns without case sensitivity.
The specified patterns miss common variants such as You are Claude, READ tool, and Ask Mode. These inputs bypass warnings even though they have the same compatibility risk.
Use case-insensitive matching for natural-language rules. Add positive tests for mixed-case identity, tool, approval, and environment text.
🤖 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_codex_set_prompt_composer/060_wp6_presets_and_linter.md`
around lines 64 - 72, Update the linter rules represented by identity,
foreign-tool, approval-vocab, and environment to perform case-insensitive
matching, while preserving their existing detection behavior. Add positive
coverage for mixed-case identity, tool, approval, and environment inputs,
including variants such as “You are Claude,” “READ tool,” and “Ask Mode.”
| | 2 | current window → Multi-auth | `030` test 1 | | ||
| | 3 | Prompt section beside it | `030` test 2 | | ||
| | 4 | Logs-style left/right panels | `030` tests 4-6 | | ||
| | 5 | built-in layers switchable | `030` tests 8-9 | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Limit acceptance item 5 to config-toggle layers.
The phrase “built-in layers switchable” contradicts WP4. base, runtime-conditional, and feature-gated layers are built-in layers but do not have switches on this page.
Change the acceptance text to “config-toggle layers are switchable.” Keep item 10 as the proof that all other classes cannot be disabled through this API or UI.
🤖 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_codex_set_prompt_composer/070_wp7_docs_and_verification.md`
at line 76, Update acceptance item 5 in the verification plan to state that
config-toggle layers are switchable, replacing the broader “built-in layers
switchable” wording. Leave item 10 unchanged as the proof that other layer
classes cannot be disabled through the API or UI.
| export function decodeBasicString(literal: string): string | null { | ||
| if (literal.length < 2 || !literal.startsWith('"') || !literal.endsWith('"')) return null; | ||
| const inner = literal.slice(1, -1); | ||
| let out = ""; | ||
| for (let i = 0; i < inner.length; i += 1) { | ||
| const ch = inner[i]!; | ||
| if (ch !== "\\") { | ||
| if (ch === '"') return null; // unescaped quote: not a single literal | ||
| out += ch; | ||
| continue; | ||
| } | ||
| const next = inner[i + 1]; | ||
| if (next === "\\") out += "\\"; | ||
| else if (next === '"') out += '"'; | ||
| else if (next === "n") out += "\n"; | ||
| else return null; // any other escape is outside what we will decode | ||
| i += 1; | ||
| } | ||
| return out; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Reject raw characters outside the emitted basic-string subset.
decodeBasicString accepts raw LF, tab, DEL, C1 controls, and unpaired surrogates in its non-escape branch. For example, decodeBasicString('"a\nb"') returns a value even though the adoption contract accepts only a single-line basic string.
Reject raw control characters and unpaired surrogates in this decoder. Permit a newline only through the emitted \\n escape. Add regression tests for raw LF, tab, and surrogate input.
🤖 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 `@src/codex/prompt-layers.ts` around lines 202 - 220, Update decodeBasicString
to reject raw control characters, including LF, tab, DEL, and C1 controls, and
reject unpaired UTF-16 surrogates while processing non-escaped characters.
Continue permitting newlines only when represented by the existing \n escape,
and add regression tests covering raw LF, tab, and surrogate inputs.
|
Superseded by #892, which lands both fixes natively: extension CORS origins are matched by scheme+authority (configured extension ID only, wildcard rejected, all ten data-plane sites covered) and atomic writes resolve symlinked destinations before the temp+rename dance. The unified branch additionally closes two guard gaps the stacked review caught: first writes beneath a symlinked parent are refused, and pid/port writers guard before any mkdir/chmod. Thank you — the original reports and patches drove the fix. |
Summary
Two must-fix bugs on the server/config boundary, implemented in the wt4 worktree lane (unit:
devlog/_plan/260802_wt4_server_config_security/). Supersedes and closes #850 and #869 — both contributor implementations are applied here with authorship credited, plus two amendments folded in from independent audits.Bug A — browser-extension CORS origins all collapse to
null(supersedes #850, @eachann1024)URL.originserializes every extension scheme (chrome-extension://,moz-extension://,safari-web-extension://) as"null"per WHATWG URL §4.7, soisExtraAllowedOrigin's origin-equality check admitted any browser extension whenever one was allowlisted.comparableOrigin()keeps WHATWG origins for normal schemes and comparesprotocol//hostfor authority-based opaque origins; hostless opaque origins keep the exact-string fallback (literalOrigin: nullbehavior unchanged)./v1/*and management/api/*(preflight + GUI session issuance)./api/settingsaccept + cross-extension reject) folded from the wt4 audit; locale docs re-based onto theconfiguration/server.mdsubpages (docs split in 7fdb2cb) with the Firefox/Safari UUID-rotation caveat (per-install / per-launch regeneration — MDN, Mozilla bug 1717671, WebKit bug 244330).Bug B — atomic config writes destroy symlinked destinations (supersedes #869, @nicosuave)
atomicWriteFile/atomicWriteFileAsyncwrote the temp beside the literal path and renamed over it; rename(2) replaces the directory entry, so a symlinked destination (dotfiles-managed~/.codex/config.toml) was silently converted to a plain file and the tracked repo stopped receiving writes (POSIX.1-2024; Linuxrename(2): "the link will be overwritten").resolveWriteTarget()realpaths the destination so temp and rename land beside the real file; the test-only real-home guard is re-applied to the resolved target; the responses-state load sweeps stale temps in both the literal and the resolved directory.Test plan
bun run typecheck— greenbun run test— 7013 pass, 0 fail (macOS arm64, Ran 7023)server-auth+server-loopback-host-gate+config— 172/172 (win32, real node)EPERMsymlink-privilege of the test node (no admin), zero functional failuresbun run privacy:scan— passed*rejected; symlink survival (lstat/readlink/target-updated, sync+async), no temp left, plain destination byte-identical, first-write creation, dangling-link refusal, resolved-dir temp sweep, real-home escape refusalSecurity note
Touches the CORS origin admission boundary (
src/server/auth-cors.ts) and credential-adjacent file writes (src/config.ts,src/oauth/store.tscallers) — per MAINTAINERS.md this wants explicit security review. No secrets are logged; the allowlist model stays exact-match (no wildcard relaxation).Summary by CodeRabbit
Bug Fixes
Documentation
Tests