agent: default unset scope to global (safe by default)
Only tools that explicitly declare scope="write" get path-based parallelism. Unset scope is now global (full barrier), avoiding the "path is a lie" problem where a tool has a path arg but modifies other files (e.g., lsp_rename). Tools must opt in to parallelism: - scope=read: never conflicts - scope=write: conflicts on same path only - scope=global (or unset): serialization barrier Added scope=write to file_edit.meta and file_write.meta.
This commit is contained in:
parent
586edc1332
commit
2870da593d
|
|
@ -417,22 +417,21 @@ func execToolCalls(rt *Runtime, ctx TurnCtx, toolCalls []backend.ToolCall, resul
|
|||
const conflictKeyGlobal = "\x00GLOBAL"
|
||||
|
||||
// toolConflictKeys returns the set of resource keys a tool call touches.
|
||||
// Scope "read" tools return nil (never conflict). Scope "global" tools
|
||||
// return the global sentinel. Scope "write" (or empty) tools return their
|
||||
// file path, or global if no path is detectable.
|
||||
// Scope "read" tools return nil (never conflict). Scope "write" tools
|
||||
// return their file path (or global if no path detectable). Everything
|
||||
// else (scope "global" or unset) is a full serialization barrier.
|
||||
func toolConflictKeys(rt *Runtime, call backend.ToolCall) []string {
|
||||
scope := rt.ToolMeta[call.Name].Scope
|
||||
if scope == "read" {
|
||||
return nil // reads never conflict
|
||||
}
|
||||
if scope == "global" {
|
||||
if scope == "write" {
|
||||
if p := extractFilePath(call.Arguments); p != "" {
|
||||
return []string{p}
|
||||
}
|
||||
return []string{conflictKeyGlobal}
|
||||
}
|
||||
// "write" or unset: infer from path argument
|
||||
if p := extractFilePath(call.Arguments); p != "" {
|
||||
return []string{p}
|
||||
}
|
||||
// No detectable path: global barrier
|
||||
// "global", empty, or anything else: full barrier
|
||||
return []string{conflictKeyGlobal}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"description": "Replace text in a file. Has fuzzy matching (whitespace/indent flexible).",
|
||||
"outputFormat": "diff",
|
||||
"resetsCounter": true,
|
||||
"prompt": "## file_edit\n\nReplace text in a file. Has fuzzy matching (whitespace/indent flexible).\n\n**Args**: `path`, `old_string`, `new_string` (required); `replace_all` (optional)\n\n```\nfile_edit(path=\"/abs/path\", old_string=\"old text\", new_string=\"new text\")\nfile_edit(path=\"/abs/path\", old_string=\"old text\", new_string=\"new text\", replace_all=\"true\")\n```\n\n**Constraints**: Absolute paths only. Errors if `old_string` matches multiple locations — add surrounding context to disambiguate, or use `replace_all=true`.",
|
||||
"prompt": "## file_edit\n\nReplace text in a file. Has fuzzy matching (whitespace/indent flexible).\n\n**Args**: `path`, `old_string`, `new_string` (required); `replace_all` (optional)\n\n```\nfile_edit(path=\"/abs/path\", old_string=\"old text\", new_string=\"new text\")\nfile_edit(path=\"/abs/path\", old_string=\"old text\", new_string=\"new text\", replace_all=\"true\")\n```\n\n**Constraints**: Absolute paths only. Errors if `old_string` matches multiple locations \u2014 add surrounding context to disambiguate, or use `replace_all=true`.",
|
||||
"args": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
@ -28,5 +28,6 @@
|
|||
"description": "Set to true/1/yes to replace all occurrences"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scope": "write"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,5 +19,6 @@
|
|||
"description": "File content to write"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scope": "write"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue