- Add Prompt field to tools.ToolInfo
- Add ExtractPrompt parser for ollie:prompt...ollie:end blocks
- Add DiscoverTools to scan tool scripts directory
- BuildRuntime appends discovered tool prompts to preamble
Tool scripts can now embed their usage documentation directly in
the script header. This replaces the separate tools-*.md prompt
files and ensures documentation stays co-located with implementation.
BuildRuntime now accepts optional baseLayers (variadic strings) that
are prepended to the agent-resolved preamble. This enables the server
to compose: system_prompt + operational_model + environment + agent_prompts.
- config.Config: add SystemPrompt field (path override)
- AgentCoreConfig: add OperationalModel and SystemPrompt fields
- BuildRuntime: prepend baseLayers before agent prompt content
- Remove debug fprintf statements from prompt resolution
Move the read-safe tool result cache from a per-turn local variable
to the agent struct, persisting it across turns. This eliminates
redundant calls to file_read, lsp_*, and memory_recall when the
underlying data has not changed.
The cache is invalidated (cleared entirely) when file_write or
file_edit executes, ensuring stale results are never returned
after mutations.
saveSession() now sets a dirty flag and starts a 2-second timer
instead of immediately serializing and writing the full message
history. For a 50-step turn, this reduces 50 JSON marshals + disk
writes down to ~25 (one every 2s) plus a guaranteed flush at
turn end and on close.
flushSave() is called:
- At end of turn (after setState idle)
- On interrupt
- On Close()
- When the 2s timer fires (if still dirty)
Added PromptEnvExtra to AgentCoreConfig and agent struct. The /agent
command now uses stored PRIME_* vars instead of recomputing locally.
This ensures remote sessions get the correct remote host info on
every prompt rebuild.
Track Total, Succeeded, Failed counts and FailedCmds in HookResult.
Add Summary() method producing "(N of X hooks run) (K of X failed: cmd)".
Emit hook summary as info event at all lifecycle hook call sites and
on preTool/postTool failure.
When a detached process exits, its output (last 2KB of ring buffer)
is queued as a prompt wrapped in <detached-process-result> tags.
The agent sees it on its next turn (or immediately if idle via Queue).
- InjectSystemEvent(content) added to Core interface
- SetOnExit(fn) added to execute.Server for external hook wiring
Adds Detach, ListDetached, SignalDetached, GetDetachedOutput,
DismissDetached to the Core interface. Implemented on agent struct
via interface type assertions to the execute server.
Also adds ListDetachedRaw() to execute.Server for cross-package
consumption without import cycles.
- Add Backend/Model fields to PersistedSession for full restore metadata
- Add saveToFull() that includes backend/model in persisted state
- Add exported LoadPersistedSession(path) helper
- Add SaveSession(path) to Core interface, implemented on agent struct
- Existing saveTo() preserved as backward-compatible wrapper
SetCWD now replaces old cwd references in the system prompt,
matching the pattern used by SetSessionID. Without this, the
model continued to see the original cwd in its environment
section after a directory change.
- Add ListHandlers map to AgentCoreConfig/agent struct; /skills and /tools
dispatch through it instead of reading local paths directly
- Add ReadPlanStep to AgentCoreConfig; wired by 9p/ directly from sess.plan
- Export NextUncheckedStep([]byte) as pure helper
- Fix prependOlliePath to use OLLIE_CFG_PATH/scripts/x/ instead of $OLLIE/x/
- Add OLLIE_CFG_PATH to EnsureDefaults and managed env var list
The ! prefix allowed arbitrary command execution without sandbox
protection. Since agents share the same Submit path as users, this
was a sandbox bypass vector. Remove it entirely; users who need
shell access can use execute_code (sandboxed) or run commands
directly in their terminal.
- Add CWD field to PersistedSession for per-directory filtering
- /sessions now shows only sessions matching current cwd (-a for all)
- /save <name> creates a named checkpoint
- /resume <id> restores a saved session (messages + task state)
- Autosave (already existed) now persists cwd with each save
- Add pubsub.Bus to agent; all events published on "event" topic
- Remove EventHandler param from Submit (and internal methods)
- Queue() self-drains via goroutine when agent is idle
- /i submits directly when idle, injects when running
Session identity is now a numeric UID (starting at 10000) assigned at
creation and never changed. Session names can be renamed freely without
affecting the 9P identity.
- AgentCoreConfig.Uname carries the principal
- pushSessionEnv sets OLLIE_UNAME on the execute server
- startMount gated by WithMount() option (not env var)
- Close() uses context timeout to avoid hanging on fusermount
The execute server now starts ollie-9p-mount when OLLIE_SESSION_ID is
set, giving each agent session its own FUSE proxy that attaches to
olliesrv with Uname=session_id. Tools see the full namespace through
$OLLIE but all 9P operations carry the session's identity.
Close() tears down the mount on session destroy.
resolveExecPrompt spawns x/prime which uses envsubst to expand
${OLLIE_SESSION_ID} in the system prompt template. The variable was
never in the process environment at resolution time — it was only
set on the execute server after the prompt was already rendered.
Add env []string parameter to resolvePrompt/resolveExecPrompt and
BuildAgentEnv so callers can inject session-scoped vars into the
subprocess environment. Generate session ID before BuildAgentEnv in
the agent-switch path so it is available at prompt resolution time.
- Wire PlanFile from $OLLIE/s/{id}/plan into agentConfig so plan
re-injection is no longer dead code
- Add missing failureWindow/maxFailures/blockDuration constants
- Replace hand-rolled contains() with strings.Contains
- Fix rm -rf .. regex to match end-of-string and whitespace
- Add TestPlanReinjection covering periodic plan re-surfacing
- Add loop_maxsteps_helpers_test.go with mockResponse/newState/sequentialStream
- AgentCoreConfig.MaxSteps: when non-zero, overrides the value loaded
from the agent JSON (cfg.Env.maxSteps). Zero means "use the agent
JSON value, or unlimited if absent". Callers (9p frontend, httpgw,
CLI) can now inject a per-session limit at construction time without
touching any agent config file.
- /maxsteps [n] command: shows the current limit (0 = unlimited) or
sets it live during a session. Follows the same pattern as /model.
Added to /help listing.
Adds a monotonically increasing tool-call counter (tcct) to the agent.
- atomic.Int64 toolCallCount on the agent struct
- ToolCallCount() int64 on the Core interface
- IncrToolCallCount func() int64 on agentConfig, wired in executeTurn
- Incremented in execOne after the pre-tool hook returns (blocked calls
don't count) and before cfg.Exec is called (timeout/error still counts)
The counter never resets. Consumers use modulo arithmetic:
tcct % 10 == 0 -> fires every 10 actual executions
The primary consumer is ollie-9p, which exposes tcct as a read-only
9P file that pre_tool / post_tool hooks can inspect via cat.
- On interrupt or error, keep user message, assistant response, and
completed tool results (success or error)
- Only remove cancelled/interrupted tool results
- Use deep copy for message snapshots to prevent aliasing bugs
- Snapshot now only used for context overflow retry
On user interrupt (stop/CTRL+C), the session history now rolls back
to the pre-turn snapshot instead of keeping partial/cancelled tool
results in the model's context. This prevents raw errors like
'context canceled' from poisoning subsequent turns.
Tool results interrupted by cancellation now report 'tool execution
interrupted by user' instead of the raw Go error chain.
When tools is false in agent config, BuildAgentEnv skips ListTools
entirely so no tool schemas are sent to the backend. Models without
tool support now work cleanly.
currentAction was stored after setState("thinking"), creating a window
where Interrupt() would see a nil action handle and return false despite
the agent being mid-turn.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes the current context window (messages sent to the backend) as a
read-only JSONL file. Each line is a serialized backend.Message with the
system prompt prepended and stale reads pruned, matching what run() sends
to ChatStream. Useful for debugging agent forgetfulness.
- backend: add HTTP 404 and "no endpoints found" to isToolUnsupported
so OpenRouter 404s are correctly classified as tool_unsupported
- core: drain FIFO on error return from executeTurn so freeloader's
resubmitted prompt (queued via fifo.in while running) is picked up
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /model command no longer rejects switches while agent is running;
needed for turnError hook recovery (freeloader switches model mid-error)
- TurnError hook uses context.Background()-derived context so hook can
safely issue ctl commands without cancelling its own execution
- update TestCommand_Model_WhileRunning to expect success
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>