perf(kernel): hoist read-only check out of per-tool filter (#51)
runInference filtered the offered tool list with isReadOnlyMode(sessionId)
evaluated inside the per-tool .filter{} — a full log read+fold per offered
tool, per inference round (the audit's dominant O(events^2) hot path). The
flag is tool-independent, so read it once before the filter.
The per-tool-CALL re-check in dispatchToolCalls is left as-is: it is
semantically load-bearing (a read completing earlier in the same batch lifts
read-only mode for a later write), not redundant.
This commit is contained in:
+4
-1
@@ -2906,11 +2906,14 @@ abstract class SessionOrchestrator(
|
|||||||
tools = if (!withTools) {
|
tools = if (!withTools) {
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
|
// Read the log ONCE for the read-only check instead of once per tool inside the filter
|
||||||
|
// (the flag is tool-independent) — this filter runs per tool per inference round.
|
||||||
|
val readOnlyMode = isReadOnlyMode(sessionId)
|
||||||
stageConfig.effectiveAllowedTools
|
stageConfig.effectiveAllowedTools
|
||||||
.mapNotNull { effectives.registry?.resolve(it) }
|
.mapNotNull { effectives.registry?.resolve(it) }
|
||||||
.filter { tool ->
|
.filter { tool ->
|
||||||
// ponytail: filter write tools while read-before-write block is active; restored once a read completes
|
// ponytail: filter write tools while read-before-write block is active; restored once a read completes
|
||||||
!isReadOnlyMode(sessionId) || ToolCapability.FILE_WRITE !in tool.requiredCapabilities
|
!readOnlyMode || ToolCapability.FILE_WRITE !in tool.requiredCapabilities
|
||||||
}
|
}
|
||||||
.filter { tool ->
|
.filter { tool ->
|
||||||
// Read-loop break: keep only write tools (drop file_read/list_dir/shell) so
|
// Read-loop break: keep only write tools (drop file_read/list_dir/shell) so
|
||||||
|
|||||||
Reference in New Issue
Block a user