fix(kernel): de-dup tool events, restore approval diff, break read-before-write deadlock
Three tool-path correctness fixes: - Tool-execution events were emitted twice — SandboxedToolExecutor and SessionOrchestrator.recordToolExecution both emitted Completed/Failed/ FileWritten per call (double TUI rows, double-counted metrics). Split ownership: orchestrator is the sole recorder of Completed/Failed (truncates output, drives the read-before-write gate); executor owns Started + FileWritten (pre/post-image hashes it alone can capture). - computeToolPreview still gated on operation == "write", but file_write lost its operation param when delete was split out, so it bailed to raw-args for every write (approval card showed raw JSON). Drop the dead guard. - A file_read blocked by REFERENCE_EXISTS can never complete, so it could never lift read-only mode: a stage writing a NEW file deadlocked (writes filtered, every read of the not-yet-created target blocked) until MAX_TOOL_ROUNDS. Lift read-only on a REFERENCE_EXISTS block, and reword the block message to tell the model to file_write directly. Regression test added.
This commit is contained in:
+4
-2
@@ -45,8 +45,10 @@ class ReferenceExistsRule : ToolCallRule {
|
||||
if (inWorkspace && !exists) {
|
||||
issues += ValidationIssue(
|
||||
code = RULE_CODE,
|
||||
message = "Tool '${input.request.toolName}' references '$raw', which does not " +
|
||||
"exist — list the directory or fix the path; do not assume its contents.",
|
||||
message = "Tool '${input.request.toolName}' references '$raw', which does not exist. " +
|
||||
"Do NOT keep retrying the read. If you intend to create this file, call file_write " +
|
||||
"directly — you cannot read a file that does not exist yet. Otherwise list the " +
|
||||
"directory or fix the path; do not assume its contents.",
|
||||
severity = ValidationSeverity.ERROR,
|
||||
)
|
||||
disposition = maxAction(disposition, RiskAction.BLOCK)
|
||||
|
||||
Reference in New Issue
Block a user