- Add quirks package for stupid model behavior workarounds
- ShellInvokesNativeTool blocks shell(cmd="tool_name") patterns
- Add client_9p tool: native wrapper for ollie-9p operations
- Block ollie-9p in shell — use client_9p instead
- Update all prompts to use client_9p, not shell+ollie-9p
- Clarify 9P namespace is complete (tools are NOT in 9P)
- Registry.All() lists all available tools for validation
The step budget mechanism is gone. Agents run until they finish,
are interrupted by the user, or (for sub-agents) hit the timeout.
No replacement. The human is the kill switch.
- Remove per-step stripCold from run(); call once per turn in executeTurn
after accumulator reset and before preamble injection.
- Replace expensive deep-clone snapshot with pre-turn length truncation.
- Delete unused cloneMessages() helper.
- Remove per-step preamble prepending in loop.go; inject once at turn start
in turn.go instead of re-sending thousands of tokens every tool step.
- Re-inject preamble after overflow retry compaction where it was lost.
- Replace stripCold() N sequential LLM calls with one batched call that
summarizes all cold-zone tool results via a single JSON response.
tool_load was a built-in intercept in the agent loop — the only
'tool' that didn't run in toolsrv. Removed entirely:
- Intercept in loop.go (25 lines)
- Script + .meta in data/tools/
- autoLoad references in agent configs
Loading tools is now exclusively via ctl (which already existed):
echo 'tool_load X' | ollie-9p write .../ctl
System prompt updated to show the ctl pattern.
- Delete TurnCtx struct (was progressively populated with optional nil fields)
- Convert run(), streamResponse(), execToolCalls(), execBatch(), execOne(),
trackErrors(), retryCountdown() to Agent methods
- Extract autoCompact() and popInject() as Agent methods
- turn.go now installs a turn-scoped output handler wrapper instead of
building TurnCtx closures
- context.Context is now passed as plain context.Context, not smuggled
with unrelated state
Add history compaction tests:
- TestBuildCompactedHistory_OrphanedToolWalkback: verifies walk-back logic
prevents orphaned tool messages at hot zone boundary
- TestBuildCompactedHistory_NoOrphanAtBoundary: verifies tool messages
always have preceding assistant with tool_calls
- toolsrv pushes <system-proc-complete> to agent prompt when bg proc exits
- Remove bgTracker and CollectInterrupts from olliesrv
- Add Cmd, AgentID, SessionID fields to Proc
- Add OnProcExit callback to State
- Add command field to Proc.Stat() output
- Timeout: timeout=0 means no deadline (was defaulting to 30s)
- Signal: send to process group (-pgid) not just process; SIGTERM no longer
cancels context (only SIGKILL does); cmd.Cancel sends SIGTERM with 5s WaitDelay
- Streaming: background procs stream output in real-time via procWriter;
shell tool no longer buffers all output into a bash variable
- Proc tree: olliesrv exposes proc/{id}/out, proc/{id}/ctl, proc/{id}/status
as proper 9P directory (was broken flat file)
- Connection: proc handlers dial fresh toolsrv conn per request via
Session.DialToolServer() to avoid deadlocking the agent's blocked conn
- Stat format: key=value (exited=true, exit_code=N, id=N) matching client parser
- GC: procs auto-removed 10min after LastRead (exited procs only)
- Rename: PID -> ID throughout (synthetic, not OS PID)
- Ctl commands: term (SIGTERM), kill (SIGKILL), signal <n>, dismiss
- System prompt: correct ollie-9p commands for proc management
When a tool call includes "background": true, it executes via
proc/new.bg and returns immediately with a PID in a
<system-proc-background> tag. The agent tracks active background
processes and injects their output as <system-proc-interrupt> blocks
alongside subsequent tool results.
The model sees background updates without polling. It can react to
build failures, log events, etc. naturally. Kill via PID when done.
Implementation:
- toolsrv client: CallToolBackground (uses proc/new.bg as rdwr)
- toolsrv: proc/new.bg upgraded from write-only to request-response
- agent: bgTracker collects last 20 lines of output per proc
- agent loop: injects interrupts after execToolCalls, before h.update
- system prompt: documents the background mechanism and rules
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.
Rename ToolInfo.ReadOnly bool → ToolInfo.Scope string with three values:
- "read" — path-scoped read, never conflicts (always parallel)
- "write" — path-scoped write, conflicts on same file path only
- "global" — full serialization barrier, runs alone
Tools declare scope in their .meta file. If unset, inferred from path
arg presence (write if path exists, global otherwise).
This correctly classifies lsp_rename as global (cross-file workspace
edits) despite having a path argument. The path arg in lsp_rename is
a symbol coordinate, not a resource scope.
All .meta files updated: readOnly:true → scope:read,
readOnly:false → scope:global, lsp_rename gets scope:global.
Replace binary ReadOnly/not batching with path-based conflict detection.
Tool calls that operate on different file paths now execute in parallel,
while same-path writes and shell (global barrier) serialize.
Conflict rules:
- ReadOnly tools: never conflict (unchanged from before)
- Write tools with path arg: conflict only on same path
- Shell / unknown (no path): global barrier, runs alone
This parallelizes the common refactoring case where the model edits
multiple files in one turn (e.g., 5 file_edits on different files
complete in 1 round-trip instead of 5).
Safety argument:
- LLM API is turn-based: model reads files first, plans edits based
on known content, emits writes in a subsequent turn
- file_edit carries old_string context: self-contained per-file
- Same-path conflicts serialize (detected via extractFilePath)
- Shell is conservative: always a full serialization barrier
tool_load must be handled specially by the agent runtime because tools
run inside toolsrv's sandbox and cannot load other tools into
themselves. The runtime now intercepts tool_load calls and directly
invokes ToolServer.LoadTool().
This is the only built-in tool - all others are external scripts.