feat(tools): standardize executor approval and strict arg contract#191
feat(tools): standardize executor approval and strict arg contract#191Nomikfk1215 merged 2 commits intomainfrom
Conversation
- add executor-level write approval gate for destructive tools - default strict args to reject unknown fields and normalize schema defaults - add stable ToolExecError mapping contract tests - update architecture doc with real runner/registry/executor mapping Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: 520wheat <142715831+520wheat@users.noreply.github.com>
| Reason: reason, | ||
| }) | ||
| if err != nil { | ||
| return err |
There was a problem hiding this comment.
Approval callback errors are returned directly here, which bypasses the ToolExecError contract used elsewhere by Executor. That can leak inconsistent error shapes/codes to callers for pre-run failures. Consider wrapping callback errors as ToolErrorPermissionDenied (or ToolErrorInternal as appropriate) with the callback error as cause, similar to other permission-path failures.
| Reason: reason, | ||
| }) | ||
| if err != nil { | ||
| return err |
There was a problem hiding this comment.
promptForWriteApproval returns callback errors directly (return err), which bypasses executor error normalization and can leak untyped errors out of ExecuteRequest. Since this new path is part of the unified executor pipeline, consider wrapping callback failures as ToolExecError (likely permission_denied or internal) so downstream handling remains consistent.
| Reason: reason, | ||
| }) | ||
| if err != nil { | ||
| return err |
There was a problem hiding this comment.
promptForWriteApproval returns the raw callback error directly. Because this happens before Tool.Run, ExecuteRequest returns a non-ToolExecError on this path, which breaks the otherwise consistent executor error contract (Code/Retryable) and can leak backend-specific error text to the model. Consider wrapping callback failures as ToolErrorPermissionDenied (or ToolErrorInternal) with the original error as cause.
| } | ||
|
|
||
| func schemaRejectsUnknownFields(parameters map[string]any) bool { | ||
| func schemaAllowsUnknownFields(parameters map[string]any) bool { |
There was a problem hiding this comment.
schemaAllowsUnknownFields only treats additionalProperties: true as allowing unknown keys. In JSON Schema, additionalProperties may also be an object schema; that should still allow unknown keys (with validation constraints). This implementation now rejects all unknown keys in that valid schema form, which is a behavior regression for tools that rely on schema-object additionalProperties.
There was a problem hiding this comment.
No blocking issues found across code quality, performance, security, and documentation accuracy in the changed files.
Validation run:
go test ./internal/tools ./internal/agent -run 'Test(Executor|Registry|DefaultRegistry|NormalizeToolError|RunPromptStopsOnRepeatedToolPlan)' -v
| "testing" | ||
| ) | ||
|
|
||
| func TestNormalizeToolErrorContractTimeout(t *testing.T) { |
There was a problem hiding this comment.
Good contract coverage here: these tests assert Code, Retryable, and wrapped cause behavior, which protects error mapping stability.
| Reason: reason, | ||
| }) | ||
| if err != nil { | ||
| return err |
There was a problem hiding this comment.
execCtx.Approval(...) errors are returned directly, which bypasses ToolExecError normalization used elsewhere in the executor pipeline. This can leak untyped errors from ExecuteRequest and break callers that rely on Code/Retryable consistency. Consider wrapping callback failures with NewToolExecError(...) before returning.
|
@xgopilot 这个issue里最大的不能不修复的问题是什么 |
结论这个 PR 里最大且必须修复的问题是: 为什么这是最高优先级
建议修复在 说明已完成代码与测试上下文核对;本次仅做分析答复,未提交代码改动。 |
简单解释刚刚说的问题问题是这条路径: 这会带来两点影响:
这个是不是 #162 总体架构文档里的内容结论:算是架构流程的一部分,但你问的这个具体问题本身是实现细节/缺陷,不是总体架构层面的新模块设计。 对应关系是:
所以可以理解为:文档覆盖了“应该怎么工作”的架构层描述;当前问题是代码没有完全对齐该架构约束。 |
|
@xgopilot 修复这个bug 的最小改动方案是什么 |
最小改动方案针对你说的这个 bug(
return NewToolExecError(
ToolErrorPermissionDenied,
err.Error(),
false,
err,
)这样
说明
|
|
@xgopilot 进行修复 |
完成情况已按 修复内容
验证
提交
未执行内容:无。 |
…ionalProperties object Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: Nomikfk1215 <229251720+Nomikfk1215@users.noreply.github.com>
Requested by @520wheat
This PR implements the four requested follow-ups for issue #187 based on current
runner.go / registry.go / executor.goreality.Summary
internal/agent/runner.gointernal/tools/registry.gointernal/tools/executor.goExecutorfor destructive tools (write_file,replace_in_file,apply_patchclass), instead of relying only onrun_shell.additionalProperties: falseadditionalProperties: trueremains opt-in to allow unknown argsinternal/tools/errors_test.goforCode/Retryable/Causemapping.Validation
go test ./internal/tools ./internal/agent -run 'Test(Executor|Registry|DefaultRegistry|NormalizeToolError|RunPromptStopsOnRepeatedToolPlan)' -v