feat: let raven read and change its own configuration - #833
Conversation
Add the raven_config tool over a catalog of Raven's settings (raven/config/self_surface.py). Each setting records its type, when a change takes effect (next turn, immediately, reload, restart) and which writer applies it: the settings page RPCs lent by the rpc bootstrap, or a validated atomic write of config.json. Secrets are reported as set or not set and never written through the tool. Sub-agents expose what their kind allows (toggle, description, model). The permission gate reads raven_config describe/get without asking and asks for every change, whatever the mode or allow rules say, with no session grant. The gateway lends the tool an idle-waiting restarter so a batched reload or restart runs after the user confirms it. The always-on raven-self-config skill teaches when to suspect configuration (a failing sub-agent, a missing tool, a silent channel, a GitHub link with no plugin) and how each change applies. The plugin tool now finds and lists without a prompt and tells the agent to offer a connection it lacks. Sub-agent processes and product agents do not get the tool. benchmarks/self_config_eval runs 16 implicit, explicit, negative and injection cases through the real RPC stack in isolated homes, with an optional LLM judge. Co-authored-by: Claude (claude-opus-5-5) <noreply@anthropic.com>
Found by running the self-configuration flow against a real gateway and page. The approval sheet drew raven_config calls as raw JSON and offered "allow for this conversation", a grant the gate never records for them. The tool now declares a config.change layout: the setting, its old and new value (the default when unset), when the change applies and any security note, with deny and allow once only. The prompt never carries a credential, and a key like maxTokens is no longer taken for one. A reload asked from a page turn swapped two seconds later, mid-answer: the gateway's busy check read only its own lock and scheduler, and a page turn holds neither. It now counts the page's turns too. The swap also signed every open tab out, because the old generation removed serve.json before the next one mounted the page; the page's token and cookie now cross a swap in memory. describe also answers a path prefix, and the restart prompt names reload or restart as the tool will actually run it. Co-authored-by: Claude (claude-opus-5-5) <noreply@anthropic.com>
…ation's model From trying the self-configuration flow on the page. Asked to switch the web search vendor and set its key, the agent made two separate changes and sent the user to Settings for the key. raven_config set now takes several settings at once (no path, value an object), checks every value before writing any, and shows them on one card. A secret is named with an empty value: the card shows a password field for it and saves what the user types through the page's own settings.set or model.save_key before answering, so the key never passes through the model. The tool then reports whether it is set. A call that carries a key's value (one pasted into the chat) is refused by the gate before anyone is asked. Surfaces without the field fall back to Settings. session.model switches only the current conversation, through the existing session-scoped config.set; agents.defaults.model still moves the default. The skill says which one a request means. The trajectory now words raven_config and plugin rows by what the call did (checked settings, changed settings, reloaded Raven, checked or connected plugins) instead of the bare tool name, and no longer offers a raven_config setting path as a file to open. Co-authored-by: Claude (claude-opus-5-5) <noreply@anthropic.com>
…es settings The chips read the conversation's model and permission mode only when a conversation opens or the reader picks one, so a switch the agent made through raven_config left the picker naming the old model until the conversation was reopened. A successful raven_config set or unset now reads both back, under the current view ticket so a reader who has moved on keeps the page they are on. The approval card also spells a model value as provider/model, the way the old value beside it already reads, instead of the JSON it was sent as. Co-authored-by: Claude (claude-opus-5-5) <noreply@anthropic.com>
…e approval mode - Secrets: a key the agent needs is typed on a credential card of its own (credential.request / submit / skip / pending / closed) and written through the settings handler that owns it; the model only learns saved or skipped. Channel secret fields go through the same card. Enabled on page surfaces only. Tool output is scrubbed of the values Raven holds (held_secrets), and reads of dotfile configs under home are redacted. - Approval: raven_config writes now honour full access, the user's allow rule and smart mode's reviewer, in a turn someone is at. The reviewer never approves a setting the catalog marks sensitive, and a grant for the session never carries a change. A call that only asks for keys goes straight to the credential card. The reviewer's instruction counts the agent's own non-security settings as ordinary work. - Sub-agents: subagents.add stays preset-only (every execution field comes from the preset). A preset can be named by its display name, an add can pin a model the agent lists, a model pick on an unmeasured agent takes the handshake menu first, and refusals carry the models the agent offers. - Probe and remedies: the ping passes the pinned model, an empty turn reads as silent, and each refusal kind maps to one next step, with stop rules for what the agent may not do while diagnosing. - Tools: plugin says agents and chat apps belong to raven_config; cron names the missing message; a GNU timeout shim on hosts without one; read-only package manager subcommands allow. - Skill: raven-self-config describes itself as reading and changing Raven's own settings, mostly connecting agents and chat apps, with no per-agent hints. - Contracts: CONTRACTS_VERSION 33 with the credential types in the ledger; kernel budget ceiling 3,660 with its review note. - The self-config eval harness is no longer part of the repository. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aven - Lent keys: an acp sub-agent row can name Raven providers (lendKeys) whose key it is started with. The row holds the provider name, not the key: each start reads the key from Raven's config into the variable the preset reads it from (presets.LENDABLE_KEYS, Pi only for now), so it never passes through the model and follows a key Raven rotates. subagents.add takes lend_key and subagents.update takes lend_keys; both refuse a provider the preset does not read or Raven holds no key for. Both fingerprints fold the field in only when set, so recorded verdicts and menus survive the upgrade. - raven_config: describe shows lends_keys and can_lend, set subagents.<name>.lendKeys routes through subagents.update, and a refusal that needs a sign-in or a key offers lending first when Raven holds one. The catalog marks lendKeys sensitive, so smart mode's reviewer never approves it. The stop rule now forbids reading or copying keys rather than lending them, which had led the agent to say an agent could not use Raven's key. - Agents page: the failure note on a refused connect or a failed test offers "Hand it to Raven", which opens a new conversation with a prompt naming the agent and the reason, not sent. The composer verb is lent by the app so the domain does not import a sibling. - CONTEXT.md defines lent key; schemas/subagent.schema.json regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Between the lead and the server's own sentence it split one explanation in two. On the title line it is out of the reading line and the note grows by nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as a note - The capability probe started an agent with the row's own env only, so an agent connected on a key lent by Raven answered its ping and then read "connected, but no session could be opened: Authentication required". verify_agent now takes the launch env, and probe.py passes the lent keys when a row borrows any (the transport stays free of the agent layer). - A connected row whose last check found a problem drew the check's English sentence under the head line. The head is now one line, and the finding is an amber note at the top of the body, shaped like a failure's: a sign-in reads as one, the check's words fold under "what the check found", and the note offers the hand-off to Raven. The note names the press the bar shows. - The hand-off prompts name the agent as an agent: "Pi" alone sent the model searching Raven's files before it read the self-config skill. The Chinese link reads "Hand it to Raven" without "look into it". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: sensitive configuration changes can bypass confirmation, secrets still leak through result telemetry, and the new timeout test fails on Linux.
I reviewed the complete github/main...HEAD diff and the surrounding callers/history. I covered the repository rules and canonical terms in AGENTS.md, CLAUDE.md, CONTEXT-MAP.md, CONTEXT.md, ui-web/CONTEXT.md, and the shipped-agent contract; the configuration catalog/write path; permission and credential flows; sub-agent key lending/probes; RPC and reload lifecycle; Web UI consumers; backward compatibility; and whether tests were weakened. I tried to refute each item against its call path and kept only the three concrete failures marked inline.
Verification after uv sync --all-packages:
uv run pytest -q -n 0over the 15 directly affected Python test modules: 1,493 passed, 96 skipped, 1 failed. The failure is the inlinetimeout -s KILLcase and is also the current Ubuntu CI failure.- Selected affected Web UI tests: 392 passed across 6 files.
git diff --check github/main...HEAD: pass.
…ding out of review - A tool result was scrubbed only where the model's message is built, after its preview had been written to the log and sent to the page as tool.complete.result_preview. The dotfile redaction and the held-secret scrub now run where the result is first read, so the watch note, the log line, the UI event and the model message all get the same scrubbed text. - touches_sensitive read only the top-level path, so an add carrying lend_key (alone or in a list) went to smart mode's reviewer and could hand Raven's key to a third-party agent without the sensitive confirmation. It now reads the add's value. - The timeout shim answered 124 for a child it had to KILL; GNU answers 137. The shim now does, and its test calls it by path so a host's own timeout (earlier on Linux PATHs) is not what the test measures. - A test literal is written as an escape: the repository refuses non-English source additions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the 0abda68 delta against the previously reviewed full main diff and verified all three prior blockers: lending adds cannot reach the smart reviewer, secret-bearing tool output is scrubbed before every log/UI/model consumer, and the compatibility test now measures the generated timeout shim with GNU-compatible SIGKILL status. No tests were weakened.
Verification: uv run pytest -q -n 0 tests/test_raven_config_tool.py tests/test_agent_loop_run_emit.py tests/test_security_untrusted_context.py tests/test_sandbox_compat_bin.py -> 166 passed. Current CI has one red unit shard: all 6,673 tests in that shard passed, but the session failed its idle-time watchdog on an untouched DAG test; the other completed checks pass.
Coverage included AGENTS.md and the domain context, the revision delta, affected callers and execution ordering, relevant history, backward compatibility, test-strength changes, and the permission/security architecture.
Diff coverage was 89.31% against a 90% gate. The uncovered lines were the branches nothing exercised: a credential card the page never received, a close that failed too, a sink that throws (the value must not reach the log), the three credential methods served through the dispatcher and refusing a malformed answer, a missing or unreadable config for the held-secret scrub and for key lending, a dotfile read whose arguments JSON cannot encode, a model refusal whose menu cannot be read, and a model pick whose handshake fails. Locally the gate now reads 90.79%; the ratchet and baseline checks pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
The new revision only adds coverage for existing fallback paths. I checked the added cases against the credential broker, credential RPC registration, sub-agent capability/model fallback, held-secret cache, dotfile redaction, and lent-key readers; they preserve the intended behavior and do not weaken prior assertions.
Verification: uv run pytest -q -n 0 tests/test_rpc_credential_broker.py tests/test_rpc_subagents.py tests/test_security_untrusted_context.py -> 162 passed. Completed CI checks are green; three unit shards were still running when reviewed.
Coverage included AGENTS.md/CLAUDE.md and runtime context rules, the delta against the previously reviewed full diff, affected callers and history, backward compatibility, test strength, and architecture boundaries. No production behavior or architecture changed.
…ckend The three tests that stub record_memories still let the record task build and start the configured memory backend. On CI that start and stop landed as seconds of idle on test_a_node_leaves_a_memory_record, failing the strict idle ceiling, and on a developer machine it probed the live everos server on 18791. Hand them the file's lifecycle fake instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
This revision only replaces an irrelevant real EverOS backend dependency in three DAG memory-record tests with the existing lifecycle fake. The tests still exercise their intended cancellation, file-writing, and instance-propagation behavior, so this removes external latency without weakening the assertions or changing production behavior.
Verification: uv run pytest -q -n 0 tests/test_subagent_dag_runner.py -> 270 passed. Completed CI checks are green; three unit shards were still running when reviewed.
Coverage included AGENTS.md/CLAUDE.md and runtime context rules, the delta against the previously reviewed full diff, the memory backend caller and test history, backward compatibility, test strength, and architecture boundaries.
Resolves the approval sheet conflict: keep main's keyboard answers (Esc, Cmd+Enter, Shift+Cmd+Enter) and keep a configuration change to one-time approval, so the broader chord does nothing on that card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
The new revision merges current main. I reviewed the current github/main...HEAD diff against the previously reviewed feature and checked the overlapping approval-sheet files and merge history. The resolution preserves main's Escape/Cmd+Enter/Shift+Cmd+Enter behavior while keeping self-configuration approval one-time-only: its broader chord is consumed without granting anything, and its ordinary allow chord still answers. Existing tests pin both behaviors, with no weakened assertions.
Verification: npm test -- --run src/features/composer/approve.test.ts -> 49 passed; npm test -- --run scripts/gates/approval-card-css.test.mjs -> 4 passed; npm run type-check -> passed. A preliminary direct node invocation of the Vitest gate was invalid because it bypassed the Vitest runner; the same gate passed through the project test command. Completed CI checks are green; four unit shards were still running when reviewed.
Coverage included AGENTS.md/CLAUDE.md and the UI/runtime context rules, the current diff, overlapping callers and merge history, backward compatibility, test strength, and architecture boundaries.
Main and this branch each moved the contract tier from version 33: main for FileWrite on the tool paper, this branch for the credential card on the asking paper. Both land here as version 35 with the surface repinned, and the papers' line ceiling goes 3,660 -> 3,700 (measured 3,682), with main's budget note kept and this branch's written after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the new merge revision against both the previously reviewed head and current main. The three manual conflict resolutions correctly retain both branches' contract additions: contract version 35, both FileWrite and credential exports in the ledger, and the cumulative 3,700-line paper-tier ceiling (3,682 measured). I found no weakened tests or compatibility regression.
Verification: uv run pytest -q -n 0 tests/test_contracts_two_tier_ledger.py tests/test_kernel_budget.py tests/test_agent_loop_run_emit.py tests/test_shell_command_writes.py tests/test_raven_config_tool.py passed (182 tests), and git diff --check passed for both the revision delta and the full PR diff. CI has one red shard on the untouched ACP heartbeat timing test (1 failed, 6,920 passed); that case passed 10/10 locally, neither merged delta changes its ACP code or test, and the merged main commit passed all four unit shards, so I do not attribute that failure to this revision.
Coverage included AGENTS.md, CLAUDE.md and applicable CONTEXT guidance, the diff and conflict resolutions, callers and history, backward compatibility, test-strength review, and the contract-tier architecture constraints. All three threads I opened remain resolved.
|
Not a blocker, but it is the same seam as the sub-agent scrub thread and worth fixing in the same pass: inside
When Reproduced, with the no-blocks arm as the control: I am filing this as not blocking because of reachability, and I would rather say so than overstate it. Blocks are set only for a multimodal result on a model that carries images in a tool role, and the producers are Scrubbing the text blocks where they are fenced would cover both branches from one call. |
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: resolve the newly reported self-configuration security gaps before merging.
The head is unchanged, but the new review record invalidates my earlier clean stance. I traced all five reports and reproduced the relevant production predicates:
- The three outbound-destination settings are not sensitive, so smart mode can approve changes that redirect credential-bearing traffic.
- The raven-loop subagent path fences raw tool output but never applies the held-secret scrub before sending it to its model.
- With
curl *: denyand*: allow, direct curl andtimeout 0.5 curl ...deny, while the installed shim accepts.5,1e3, and+5spellings that the gate classifies as allow. - A trailing space bypasses both sensitive and secret classification before the tool normalizes the path; this preserves the sensitive-setting write bypass and exposes supplied secret text in approval rendering.
- Feishu
encrypt_keyis secret in the adapter schema, butchannels.feishu.encryptKeyis not secret to the gate and its plaintext is rendered in the approval text. The downstream channel writer does discard that supplied value and route to the credential flow, so persistence is prevented, but that happens after the exposure and does not defeat the reported control gap.
I did not reply in or resolve another reviewer's threads. Their blockers remain open for the author to address. Verification: uv run pytest -q -n 0 tests/test_config_self_surface.py tests/test_permissions_gate.py tests/test_raven_config_tool.py tests/test_security_untrusted_context.py tests/test_sandbox_compat_bin.py tests/test_shell_policy_reasons.py passed (500 tests); those passing tests do not exercise the reported bypass cases.
Resolves the i18n catalogue conflict: main's connections keys and this branch's credential card keys land side by side, and the generated ui-tui catalogue is regenerated from the merged file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Mark tools.web.proxy, tools.media.proxy and providers.*.apiBase sensitive: each sends Raven's keys and traffic to a host the call names, so smart mode's reviewer no longer settles them. - Classify the path the tool writes: the gate and the tool share canonical_path, so a trailing space or dot no longer turns a secret into an ordinary setting or a sensitive setting into a plain one. Batch keys and a blank path take the same spelling. - Ask the channel's own spec which fields are secret (Feishu encryptKey), and read an object set on a channel field by field, so its secrets are refused and never printed in the approval text. - Scrub held keys and home dotfile reads in the sub-agent loop and in the text blocks of an image-bearing result, through one shared helper. - The timeout shim accepts only the durations the gate's parser reads past, from one shared definition, and exits 125 on any other spelling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
On the 🤖 Addressed by Claude Code |
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Verified the revision delta from the previously reviewed head and the full PR context. The five security blockers are addressed at their shared enforcement points: keyed-traffic destinations are sensitive, permission and execution share canonical paths, channel secrecy comes from adapter specs (including object writes), both agent loops and multimodal text blocks use the shared output scrubber, and the timeout shim accepts only the duration grammar the gate can unwrap. The earlier nonblocking image-block seam is covered by the same scrub fix. I found no weakened tests or compatibility regression.
Verification: the focused config, permission, shell-policy, agent-loop and secret-output suite passed (610 passed); i18n boundary, prompt and source-language tests passed (49 passed). The local TUI generator check could not start because this worktree lacks the prettier Node dependency, while CI's TUI check and all four unit shards passed; the aggregate coverage job was still running at review time. git diff --check passed for both the revision delta and full PR diff.
Coverage included AGENTS.md/CLAUDE.md and applicable CONTEXT guidance, the delta and full diff, the merge-only i18n conflicts, callers and history, backward compatibility, whether tests were weakened, and the permission/configuration architecture constraints.
|
Not a blocker, and separate from the three threads -- one more catalog entry Severity of this one finding, not a verdict on the pull request. The blocking findings from this pass are review threads on the changed files; GitHub renders those collapsed, as a file name with no text.
So a Not blocking, for two reasons I checked rather than assumed: the value is a model Mentioning it because the three threads are about the same question -- which |
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: the three newly opened security-control gaps must be resolved before this revision merges.
The head is unchanged, but the new open threads invalidate my earlier clean stance. Static inspection confirms the cited source conditions: canonical_path() leaves a space when the input ends in space-plus-dot; provider secret classification does not consult ProviderConfig declarations such as extra_headers; and channel destination fields are not catalogued as sensitive even though the smart-mode gate uses that flag to keep them with the user. These are @0xKT's findings, so I am not re-grading, replying in, or resolving their threads. No tests were rerun because this answer evaluates new comments on unchanged code; the prior verification remains the latest test result for this head.
- canonical_path strips whitespace and dots together, so no order of them survives (`token .` read as `token `, a secret classed as ordinary); tested over every wrapping of up to two such characters at each end. - is_secret_path asks the provider schema the way it asks a channel's spec: extraHeaders (declared secret) and Gemini's apiKeyList (on the provider writer's patch list) are credentials, values nested in them included, and held_secrets now collects string items of a list. - A channel field that sends its traffic somewhere (a proxy or server address) is declared sensitive in its adapter's spec, and the gate, the card and describe read that declaration. - permissions.judgeModel is sensitive: it picks the reviewer that settles smart-mode calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the doctor warning for full mode or an off sandbox and the uncached deliverable downloads; no conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
On 🤖 Addressed by Claude Code |
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: the three open security findings need their author or a maintainer to settle them before this revision merges.
I independently verified that this head closes the paths they cite: canonical_path() now removes whitespace and dots as one character set; provider-level extraHeaders and Gemini apiKeyList follow the provider writer's secret classification into held-secret scrubbing; adapter-declared channel destinations and permissions.judgeModel stay out of smart-mode review. These are @0xKT's findings, so this verification does not downgrade or resolve their threads.
Named follow-up (nonblocking): endpoint-specific providers.*.endpoints[].extraHeaders remains outside is_secret_path() and held_secrets(). A supported endpoint header such as APP-Code therefore remains visible when a tool prints Raven's config; the generic home-config redactor does not catch that arbitrary header name. This requires endpoint-specific custom headers plus a tool read of the config, so at this late review round I am carrying it as a narrow follow-up rather than a blocker.
Verification: the focused self-configuration, permission, scrubbing, channel, and provider suite passed (765 passed). The full suite completed with 27052 passed, 133 skipped, 1 failed; the sole failure is the reproducible tests/test_cli_theme.py::test_bold_accent_renders_styled_not_bare on an untouched file, while every GitHub check including all four unit shards is green. git diff --check passes for both the fix delta and the full PR.
Covered this round: AGENTS.md/CLAUDE.md, CONTEXT-MAP.md and the relevant runtime terms; the full github/main...HEAD inventory and the complete fix delta; affected gate, writer, schema, scrubber, provider and channel callers; commit history and compatibility; test changes for weakening; and the self-configuration, Config-with-cargo, Permission Gate, and credential-boundary architecture.
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; suggestions only, and they are marked inline.
The new commit is a conflict-free merge of current main; its doctor warning, identity guidance, and deliverable-cache changes do not alter the self-configuration fixes. All previously blocking review threads are now resolved. The already named endpoint-header follow-up is unchanged and remains nonblocking, so I have not repeated it.
Verification: the focused self-configuration, permission, secret-scrubbing, doctor, deliverables, and identity-rendering tests pass (629 passed); git diff --check passes for both the merge delta and the full PR, and GitHub reports the PR checks green.
Covered this round: the repository rules and relevant runtime context, github/main...HEAD, the complete merge delta, affected callers and merge history, backward compatibility, test changes for weakening, and the self-configuration, Permission Gate, Config-with-cargo, identity, and RPC transport boundaries.
… can be ProviderEndpoint.extra_headers is declared secret like the provider-level field, and the gate and the held-secret scrub read endpoint fields through the same schema marker, so a per-endpoint APP-Code no longer reaches a model when a tool prints the config. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oint A systematic audit of the self-configuration surface (gate versus tool, secret locations, sensitive settings, output paths) found the remaining gaps were all one of four shapes; each is now closed at a single point. - The gate and the tool read a call the same way: one value decoder, one channel field lookup (exact name, then snake_case, any case of the channel name), and the gate refuses every path the tool would refuse, so a value at such a path reaches neither the card nor the reviewer. A call naming one setting twice is refused. The prompt title is the tool's own account, so a bare restart reads as the restart it runs. - Sensitivity is declared by the owner: channel adapter specs and the shared socket fields carry a reason (who may instruct Raven, where a channel's credentials go, encryption, the weixin login store), and the catalog marks disabledTools, MCP enabled, channel enabled, the skill blocklist and auto-install, and sub-agent descriptions. An object value is judged field by field, and the card carries the field's note. - Secrecy is read from the schema (a walk through lists, maps, unions and Annotated), MCP headers are secret, credential-named env entries are, and a URL's userinfo or query credential is one to the gate, the card and the scrub alike. - ToolRegistry.execute scrubs every text a result carries, inside the traced call, so the trace span, the page diff and the sentinel get the scrubbed copy. Sub-agent transcripts, call labels, closing text, the persisted output a later DAG node renders, error records, announces, the ACP frame journal, probe details and CLI transcript artifacts are scrubbed where they are kept. held_secrets also collects short declared values, JSON-escaped spellings and URL credentials. Each fix has a test that fails when the fix is reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
Blocking: reject nonexistent channel and sub-agent configuration targets before smart review.
I reviewed the 40161c3..7982d68 delta and the full main diff, including the repository rules, the gate/registry/tool call order, affected writers and persistence callers, commit history, backward-compatibility implications, and whether the tests were weakened. The focused runs passed: 246 config/security tests and 705 gate, registry, journal, and sub-agent tests; both full-PR and revision git diff --check passed. One concrete pre-review validation gap remains, marked inline.
…re review unwritable_target accepted every three-segment channel path and every member of a channel object, so a misspelt field such as channels.telegram.tokne carried its plaintext value to the smart-mode reviewer and the approval card before the tool refused it. Channel names and fields, including each key of a channel object, are now checked against the adapter declarations the writer uses; a sub-agent's settings were already the catalog's four wildcard entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the complete 7982d68..83ad36a delta and the supporting gate, writer, channel-declaration, and catalog paths. The fix validates scalar, object, and batch channel targets with the writer's own declarations and preserves valid aliases and sub-agent settings; the added tests strengthen rather than weaken coverage. uv run pytest -q tests/test_raven_config_tool.py tests/test_config_self_surface.py passed (231 tests), and both revision and full-PR git diff --check passed.
0xKT
left a comment
There was a problem hiding this comment.
Not blockers -- four items from a full-diff pass at 83ad36a. The blocking findings from this pass are the three review threads (rules.py, held_secrets.py, redact.py).
N1 permissions.mode writes the default, not the conversation's mode (self_surface.py:699, raven_config.py _value_view)
The tool reads and writes the file default only. A web conversation that set its own mode in the picker keeps it: the user sets this chat to full, then asks Raven to go back to ask; the tool replies Set permissions.mode: ... -> "ask". It takes effect from the next turn, and the gate (gate.py:114, session_mode(...) or cfg.mode) still reads the session's full next turn. The reply is wrong in the unsafe direction. The other way round, "make this chat full" widens the default for every conversation without its own mode, channels included. Fix: give it a session scope like session.model (config.set with scope: "session"), or at least say in the reply that this conversation has its own mode X and only the default changed. Fine as a follow-up if you prefer.
N2 a batch set can leave a partial write and report only the error (raven_config.py:855-901)
The docstring says every value is checked before anything is written, but subagents.* entries are appended unchecked (873-875) and channel / sub-agent entries are not checked for a missing RPC caller. Example: {"agents.defaults.temperature": 0.5, "subagents.codex.enabled": "yes"} writes the temperature, then returns only Error: subagents.codex.enabled takes true or false. Fix: pre-check sub-agent fields and the caller in the first pass, or refuse subagents.* / channels.* in a batch to match SKILL.md ("Channels and sub-agents are changed one call each"); include the lines already applied when a later write fails.
N3 Risk section: memory now follows the main model (plugins-dist/everos-memory/raven_everos/config.py)
Summary mentions it, Risk does not. A user who never pinned the memory LLM gets long-term memory working after the upgrade, extracting on the main model and billed to the main provider's key, and EverOS restarts when the main model changes. Worth one line in Risk and the release notes (and how to keep it off).
N4 comment in gate.py:152-153 says a channel message is unattended
raven/gateway/spine.py:120 binds a responder for channel user turns on purpose ("A channel user can answer a question, so they can answer an approval"), so channel turns are attended and the code behaves as designed. Only the comment is wrong: drop "or a channel message".
Smaller items seen, no action needed in this PR: the deferred reload / restart gives up silently after 600s busy, lent_key_env reads only apiKey (not apiKeyList), opening an approval card sweeps away a docked credential card in the same conversation, the catalog's tools.exec.timeout has no upper bound (writer allows 5..3600).
- Package managers and docker: the read-only subcommand must come first, after flags known to take no value, so a valued global option (`npm --prefix ls install x`, `docker -H ps run x`) no longer makes an install read as a query. - held_secrets keeps a header value only when the header names a credential; every header still counts as secret to the gate and the card, but `Content-Type: application/json` is no longer replaced in every tool result. - The home dot-directory redaction leaves Raven's own home out: the default workspace and channel scratch directories live there, and rewriting their source broke edits. Raven's keys are still removed by the exact-match pass. - Changing permissions.mode says when this conversation keeps its own mode, since only the default changes. - A batch checks sub-agent values and the services it needs before writing anything, and a write refused partway names what already took. - The gate's comment no longer calls a channel message unattended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the cleared rich style cache between theme tests; no conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
On the non-blocking items in the review summary: N1 fixed in 342735e (the reply now says when this conversation keeps its own approval mode; a session scope for 🤖 Addressed by Claude Code |
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the full 83ad36a..1ac64e2 delta: all five changed production files, relevant callers and history, backward compatibility, test changes, the permission/config architecture, and repository rules. I independently verified that valued global options can no longer disguise mutating package-manager commands as reads, ordinary header values are preserved while credential headers are scrubbed, and reads under Raven home no longer corrupt source while other home-dotfile configuration remains redacted. I found no new issue.
Verification: uv run pytest tests/test_permissions_gate.py tests/test_raven_config_tool.py tests/test_security_untrusted_context.py -q passed (463 tests), and both diff checks passed. The combined run with tests/test_cli_theme.py was 516 passed / 1 failed; the same theme test fails in isolation, but GitHub reports the current base as cad436c, which is the commit that introduced that identical test-only change, so it is not caused by this PR delta.
|
Not a blocker -- R1-R3 re-checked against 1ac64e2: the package-manager bypass shapes (plus One gap from the R3 fix, tracked as a follow-up in #842 rather than held against this PR: skipping all of Raven's home also skips |
Summary
Raven can now read and change its own configuration through one tool,
raven_config, instead of telling the user to open Settings or editingconfig.jsonwith file tools. The same path lets it connect preset sub-agents and diagnose the ones that do not answer, and asks the user only for what needs them (a key, a sign-in, a paid model).The tool and its catalog
raven_configactions:describe(every setting with its value, or a search),get,set(one path, or several under one confirmation),unset,add(connect presets, several at once),test,restart(reload or restart once pending changes are gathered).session.modelswitches only the current conversation.raven/config/self_surface.pyis the catalog: 14 sections, about 93 settings, each with its value kind, its writer, and when a change takes effect (next turn, at once, reload, restart, memory server, or inert). Writes go through the RPC methods the settings page already uses (SELF_CONFIG_METHODSallowlist), so the page and the agent share one validation path.tests/test_config_self_surface.pyholds every entry against the schema and every next-turn claim against a real reader.subagents.addstays preset-only: every execution field comes from the preset. Presets can be named by display name, an add can pin a model the agent lists, and a model pick on an unmeasured agent reads the handshake menu first.raven-self-configdescribes the whole flow; its description is injected every turn, the body is read on demand.Approval
Secrets
credential.request/submit/skip/pending/closed) takes it; the host writes it through the handler that owns it and the model learns only saved or skipped. Channel secrets use the same card. A call that only asks for keys goes straight to the card. Enabled on page surfaces only.config/held_secrets.py, including JSON-escaped spellings and URL credentials) and dotfile reads under home are redacted, once, insideToolRegistry.execute, so the trace span, the page's diffs, the sentinel and both agent loops get the scrubbed copy. What a sub-agent says is scrubbed where it is kept: transcripts, call labels, closing text, the output a later DAG node renders, error records, announces, the ACP frame journal, probe details. A key pasted into the chat is refused.lendKeys) whose key it is started with. The row stores the provider name; each start (the spawn path and the capability probe) reads the key into the variable the preset reads (presets.LENDABLE_KEYS, Pi only for now), so it never reaches the model and follows a rotated key. Verified live: Pi refused without it, answered with Raven's OpenRouter key, and its row reads connected and tested with a 398-model menu.Page
raven_configcalls by what they did.Other
pluginpoints agents and chat apps atraven_config;cronnames the missingmessage; a GNUtimeoutshim on hosts without one, accepting only the durations the permission gate parses (one shared definition); read-only package-manager subcommands allow.CONTRACTS_VERSION35 (this branch's credential types plus main'sFileWrite) with the surface repinned; paper-tier line ceiling 3,700 (measured 3,682) with its review note.CONTEXT.mddefines the self-configuration surface and lent key.Type
Verification
uv run pytest -q -n auto: 27187 passed, 3 failed. The 3 fail on this machine regardless of this branch:test_rpc_files::test_a_host_without_libreoffice_says_so(LibreOffice is installed here),test_simulation_scenario::test_a_fresh_trial_refreshes_the_subagent_homes_before_it_runsandtest_simulation_suite::test_a_stopped_run_is_marked_in_its_record_and_finished_with_its_evidence.schemas/subagent.schema.jsonis regenerated forlendKeys;test_import_cycle_budgetholds (the capability probe takes the launch env from its caller rather than importing the agent layer).make lint-ui(gen:check, eslint, tsc): pass; the 4 eslint warnings are in files this branch does not touch.npx vitest runinui-web: 3116 passed;check-css,check-class-namespace,check-pageOK.npm run lint:i18n,lint:rpc,type-checkinui-tui: pass.uv run ruff checkandruff format --checkon every changed Python file, andlint-imports(10 contracts kept): pass.In a browser against a local gateway: credential card save, skip, replace and reload replay; the value appears in no session, trace or log. Agents page link opens a new task with the prompt filled and unsent; sent, the agent reads the self-config skill and calls
raven_configfirst, and for Pi offers Raven's key before a sign-in.Smart-mode reviewer, run against the real model: 7 ordinary config changes allowed;
sedorwrite_fileon Raven's config, reading an SSH key, andcurl | shescalated.Relevant tests pass locally
Relevant lint / type checks pass locally
User-facing docs or screenshots are updated when needed
Risk
Behaviour changes: the agent can now change settings and connect sub-agents after the user confirms (or unasked under full access, and under smart mode for non-sensitive settings). Lending gives a sub-agent Raven's key for a provider after an explicit, sensitive confirmation; what it spends is billed to that key.
Memory: a memory LLM left unset now follows the main model, so long-term memory starts working after the upgrade for anyone who never pinned one, extracting on the main model and billed to the main provider's key, and EverOS restarts when the main model changes. To keep it off, set
memory.backendto null; to keep it on a different model, pin one of its own.Backward compatible: new config fields are optional, and both probe fingerprints include
lendKeysonly when set, so recorded verdicts and menus survive. Aconfig.jsonwritten withlendKeyswill not load on an older build (extra=forbid).Rollback: revert this PR; nothing is migrated. To switch the feature off without a revert, add
raven_configtotools.disabledTools.Security impact considered
Backward compatibility considered
Rollback path is clear for risky changes
Related Issues
N/A