feat(core): add ConfigTool for programmatic config read/write#2911
feat(core): add ConfigTool for programmatic config read/write#2911
Conversation
Allow the Agent to read and write configuration settings via a new ConfigTool, eliminating the need for users to manually run /model or /settings commands during multi-phase tasks. Phase 1 supports the "model" setting only. GET is auto-allowed (read-only), SET requires user confirmation. A curated allowlist in supported-config-settings.ts ensures only approved settings are accessible. - Add ConfigSettingDescriptor allowlist with "model" descriptor - Add ConfigTool + ConfigToolInvocation (GET/SET with permission control) - Register CONFIG in tool-names.ts and config.ts - 12 unit tests covering validation, GET/SET, permissions, errors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📋 Review SummaryThis PR introduces a 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
- Wrap descriptor.read() in try-catch in getConfirmationDetails - Sanitize non-Error exceptions in write function - Add debug logging for getAvailableModels failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for the review. Addressed the actionable items in d30de21:
Other notes:
|
…ull assertion - Use Object.hasOwn() instead of `in` operator in isSupported() to prevent prototype chain keys (toString, __proto__) from bypassing the allowlist - Reject empty/whitespace-only values in SET validation - Replace value! non-null assertion with defensive null check in execute() - Add 4 new tests: empty string, whitespace, toString, __proto__ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new core ConfigTool that lets agents programmatically read/write selected configuration settings (Phase 1: model) with a whitelist-based safety boundary and confirmation for writes.
Changes:
- Add
configas a registered core tool (ToolNames.CONFIG/ToolDisplayNames.CONFIG) and wire it into core tool registration. - Implement
ConfigTool+ an allowlisted settings registry (SUPPORTED_CONFIG_SETTINGS) with GET (auto-allow) and SET (ask). - Add unit tests covering validation, GET/SET behavior, permissions, confirmation details, and error handling.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/src/tools/tool-names.ts | Adds the new config tool name and display name constants. |
| packages/core/src/tools/supported-config-settings.ts | Introduces a whitelist/descriptor registry for settings accessible via ConfigTool (Phase 1: model). |
| packages/core/src/tools/config-tool.ts | Implements ConfigTool and its invocation logic (GET/SET, confirmation details, model options listing). |
| packages/core/src/tools/config-tool.test.ts | Adds vitest coverage for validation, permissions, execution results, and confirmation prompts. |
| packages/core/src/config/config.ts | Registers ConfigTool in the core tool registry during config initialization. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The Config(action:setting) rule format would not be matched by PermissionManager since it has no alias/specifier mapping registered. Remove permissionRules and set hideAlwaysAllow=true so config changes always require per-invocation confirmation in Phase 1. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
- getDescriptor() now uses Object.hasOwn() to match isSupported(), preventing prototype chain keys from returning non-descriptor values - Add 'config' to PermissionManager.CORE_TOOLS so it follows the same registry-level enable/disable semantics as other core tools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Return error with ToolErrorType.EXECUTION_FAILED on SET failure and unknown setting, so coreToolScheduler correctly classifies failures - Restrict ConfigSettingDescriptor.type to 'string' for Phase 1 since the descriptor API is string-only (boolean coercion deferred to Phase 2) - Add error assertion in SET failure test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… validation, and more settings - Extend ConfigSettingDescriptor to support string, boolean, and number types with automatic coercion - Add options/getOptions and validateOnWrite for pre-write validation - Return structured JSON output (ConfigToolOutput) instead of plain text - Add source field (global/project) to distinguish config origins - Add new writable settings: checkpointing, respectGitIgnore, enableFuzzySearch - Add new read-only settings: targetDir, outputFormat - Add Config setters: setCheckpointingEnabled, setFileFilteringRespectGitIgnore, setFileFilteringEnableFuzzySearch - Add design doc, developer tool doc, and update user configuration doc
- ToolResult.llmContent is PartListUnion, not string — relax test helper parameter type and cast at the JSON.parse call site. - supported-config-settings.ts top-level read of APPROVAL_MODES caused a TDZ error due to a config.ts ↔ config-tool.ts ↔ supported-config-settings.ts import cycle. Switch to lazy getOptions() to defer the access.
Make approvalMode, checkpointing, respectGitIgnore, and enableFuzzySearch read-only via ConfigTool. GET still works for all of them. Rationale: - approvalMode: letting the agent escalate its own permissions (even with 'ask' confirmation) is a prompt injection risk. Users must change approval mode via slash command. - checkpointing / respectGitIgnore / enableFuzzySearch: user preferences without a clear agent-driven use case; overlap with sub-agents and skill model overrides (#2949) for task-scoped behavior changes. Only `model` remains writable because it's the one setting the agent may legitimately need to change durably at runtime (e.g., multi-stage tasks that want to switch the session default model rather than just override one call).
Update the three ConfigTool docs (user guide, developer reference, and design doc) to match the code change: only `model` is writable, all other settings are read-only. Each doc now calls out why the other settings are intentionally read-only (approvalMode = prompt injection risk; the three file-filtering booleans = no clear agent use case; debugMode/targetDir/outputFormat = runtime state that can't meaningfully change).
|
Closing this PR. After narrowing the writable scope to Reasons
The branch 中文版本关闭本 PR。 在把可写范围缩窄到只有 原因
分支 |
Summary
Agents cannot programmatically read or write configuration. When a complex task needs to switch models across phases (large model for analysis → small model for templated generation → large model for review), the user has to manually run
/modelto switch, which interrupts the workflow.This PR introduces ConfigTool, letting agents read and write a curated allowlist of settings via
config GET/SET.Before
/modelAfter
config SET model qwen3-coderto switch on its own'ask'permission); GET is auto-allowedSUPPORTED_CONFIG_SETTINGSSupported settings
modelapprovalModecheckpointingrespectGitIgnoreenableFuzzySearchdebugModetargetDiroutputFormatOnly
modelis writable. All other settings are read-only by design:approvalModeis intentionally read-only — allowing the agent to escalate its own permissions (even with'ask'confirmation) is a prompt injection risk. Users must change approval mode via slash command.checkpointing,respectGitIgnore,enableFuzzySearchare user preferences without a clear agent-driven use case. For task-scoped behavior changes, prefer sub-agents or skill-level overrides.Behavior matrix
config GET <setting>config SET model <id>config GET <unknown>config SET <read-only>config SET model <invalid>Key changes
tools/supported-config-settings.tstools/config-tool.tsConfigTool+ConfigToolInvocation. Multi-type aware, returns structured JSON output, validates options before writingtools/config-tool.test.tstools/tool-names.tsCONFIGconstantconfig/config.tsConfigToolOutput format
ConfigToolreturns a structured JSON payload (parsed by callers fromllmContent):{ "success": true, "operation": "set", "setting": "model", "source": "project", "previousValue": "qwen-coder-plus", "newValue": "qwen3-coder", "options": ["qwen-coder-plus", "qwen3-coder"] }Relationship to #2949 (skill model override)
#2949 (merged) added a
model:field to skill YAML frontmatter so that invoking a skill can switch the model for the rest of the agentic loop. It is complementary to this PR, not duplicate — they target different scenarios with different mechanisms:config SET model Xat runtimemodel:in frontmatter ahead of timeconfig.setModel(); persists until changed again or session endsSendMessageOptions; expires when the agentic loop ends'ask')ToolResult.modelOverrideplumbingmodel(write) + 7 read-only settings for introspectionmodeltools/config-tool.ts,tools/supported-config-settings.ts,tools/tool-names.ts,config/config.tsskills/*,tools/skill.ts,coreToolScheduler.ts,turn.ts,client.ts,nonInteractiveCli.ts,useGeminiStream.tsThere is no file overlap between the two PRs. They can coexist:
Layering: when both exist,
SendMessageOptions.modelOverride(from #2949) takes precedence overconfig.getModel()for that specific call. So even if ConfigTool has changed the session model, a skill's per-call override still wins inside the skill — they live at different layers and don't conflict.Test plan
modelsuccess/error paths, read-only enforcement for the other 7 settings, options validation, permission control, confirmation details, error handling中文版本
概要
Agent 无法程序化读写配置。当复杂任务需要在不同阶段使用不同模型时(大模型分析 → 小模型生成模板 → 大模型审查),用户必须手动执行
/model切换,打断工作流。本 PR 新增 ConfigTool,让 Agent 通过
config GET/SET自主读写白名单内的配置设置。Before
/model命令After
config SET model qwen3-coder自动切换模型'ask'权限),GET 自动放行SUPPORTED_CONFIG_SETTINGS添加条目已支持的设置
modelapprovalModecheckpointingrespectGitIgnoreenableFuzzySearchdebugModetargetDiroutputFormat只有
model可写,其余设置按设计均为只读:approvalMode有意设为只读 —— 允许 agent 自行提权(即便带'ask'确认)是 prompt injection 风险;用户必须通过斜杠命令改 approval mode。checkpointing、respectGitIgnore、enableFuzzySearch属于用户偏好,缺少明确的 agent 驱动用例;任务内的行为切换应走 sub-agent 或 skill 级 override。行为矩阵
config GET <setting>config SET model <id>config GET <unknown>config SET <只读>config SET model <非法值>关键变更
tools/supported-config-settings.tstools/config-tool.tsConfigTool+ConfigToolInvocation,多类型支持,结构化 JSON 输出,写入前校验 optionstools/config-tool.test.tstools/tool-names.tsCONFIG常量config/config.tsConfigTool与 #2949 (skill model override) 的关系
#2949 (已合并) 在 skill YAML frontmatter 中新增
model:字段,让调用 skill 时能切换后续 agentic loop 内的模型。它与本 PR 互补,而非重复 —— 两者目标场景与机制都不同:config SET model Xmodel:config.setModel()写入 session 配置,持久到再次修改或会话结束SendMessageOptions上的 per-call override,agentic loop 结束后失效'ask')ToolResult.modelOverride链路打通model(可写) + 另外 7 个只读自省设置modeltools/config-tool.ts、tools/supported-config-settings.ts、tools/tool-names.ts、config/config.tsskills/*、tools/skill.ts、coreToolScheduler.ts、turn.ts、client.ts、nonInteractiveCli.ts、useGeminiStream.ts两个 PR 文件无任何交集,可以共存:
层级关系: 当两者都生效时,
SendMessageOptions.modelOverride(来自 #2949)会优先于config.getModel()用于该次调用。所以即使 ConfigTool 已经改了 session model,skill 内部的 per-call override 仍然胜出 —— 两者位于不同层级,不冲突。测试
model的 SET 成功与错误路径、其余 7 个只读设置的写入拒绝、options 校验、权限控制、确认详情、错误处理🤖 Generated with Claude Code