Both idx files are now served as live synthetic files by the 9P server.
No physical files are written to disk.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace {{.Tools}} injection with ollie/t/idx (generated at session
start by rebuildToolsIdx); prompt now instructs grep-based discovery
- Replace {{.Skills}} injection with ollie/sk/idx (rebuildSkillsIdx);
same grep pattern
- Add {{.SessionID}} to system prompt identity line
- Expand filesystem section: full ollie/s/ layout, all session files,
ctl commands, s/idx cooperation pattern
- Fix execute_tool description: use ollie/t instead of physical ToolsPath()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
toolsSummary() scans OLLIE_TOOLS_PATH for # description: and # Args:
headers and injects the result as {{.Tools}} in the system prompt —
same effect as MCP schema injection but for shell/python tools.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pkg/tools/memory: memory_remember + memory_recall with FlatDirBackend
baked in (no more injectable MemoryBackend abstraction)
- pkg/tools/task: task_plan + task_complete with file-based plan
persistence baked in (no more injectable PlanBackend abstraction)
- pkg/tools/reasoning: stripped to reasoning_think only
- pkg/tools/tools: remove PlanStep/PlanBackend/MemoryBackend interfaces
- pkg/agent: remove dispatchMemoryBackend, dispatchPlanBackend,
WithFallbackPlanBackend; BuildAgentEnv takes no options
Fixes infinite recursion crash: previously dispatchMemoryBackend routed
memory_recall back to the reasoning server that owned it, causing
unbounded mutual recursion and a goroutine stack overflow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add SetSessionID to Core interface and agentCore implementation
- Renames persisted .json and .compaction.jsonl files on disk
- Propagates OLLIE_SESSION_ID env var to execute server
- Add /kill and /rn to /help output
/backend, /agent, /compact, and /clear now return an error if
the agent loop is active. These commands replace pointers or
mutate session state that the running loop holds by copy or
reference, leading to silent failures or races.
Introduces memory_remember and memory_recall built-in tools on the
reasoning server. A MemoryBackend interface allows swapping backends:
dispatchMemoryBackend routes through a registered MCP server (e.g.
denote-mcp) when available; FlatDirBackend writes dated markdown files
to ~/.local/share/ollie/memory/ as a zero-config fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Submit routes to Inject when a turn is already running, keeping the
routing decision in core rather than the frontend or 9p layer
- Inject no longer cancels the current action; the pending prompt is
stored and consumed at the next tool result boundary (success or
failure), matching goq interrupt semantics
- If an inject is already pending, fall back to the FIFO so nothing is
lost; /irw (interrupt-rewrite) explicitly overwrites the pending inject
- PopInject is now called after every tool result, not just the last one
- Injected prompts are echoed to the chat via the running turn's output
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The newline event is emitted via handler() directly, not through
loopcfg.Output, so the Output-based newline case never fired.
Commit replyBuf to s.reply alongside the state="idle" transition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontends no longer need to shadow these in their own structs; they can
read authoritative values directly through the Core interface.
State and reply tracking moves into agentCore: state transitions on
call/tool events, reply resets and accumulates per-turn in Submit's
Output func. This also fixes a bug where reply accumulated across all
queue-drained turns instead of reflecting only the most recent one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Submit now emits Event{Role:"user"} before each turn so queued prompts
appear in the chat log via the normal event path, not just direct
appendChat calls from the 9p prompt handler
- Strengthen system prompt rule against using execute_code/echo as a
communication mechanism
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PlanBackend.CreatePlan now returns an optional message alongside IDs.
When non-empty, the reasoning server uses it as the tool response
instead of the default plan summary — allowing queue backends to
instruct the agent to stop after planning rather than execute directly.
- tools.PlanBackend: CreatePlan returns ([]string, string, error)
- dispatchPlanBackend: returns "" (agent continues as before)
- reasoning/server: uses backend message when non-empty
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove MaxSteps limit and associated stalled event emission.
The agent will now run indefinitely until completion or interruption,
eliminating the annoying 'agent stalled' messages.
- Add WorkDirSetter interface to pkg/tools so servers can receive workdir updates
- Add SetWorkDir on execute.Server with RWMutex-guarded workdir field
- Add WorkDir()/SetWorkDir() to the Core interface and agentCore
- Store agentPrompt in AgentEnv/agentCore so system prompt can be rebuilt on workdir change
- Add /cwd [path] slash command (no args shows current dir, arg changes it)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add reasoning_plan built-in tool: decomposes goals into ordered steps
with dependency edges; persists to task backend when available, falls
back to in-context plan otherwise.
- Add PlanBackend/PlanBackendSetter interfaces and PlanStep type to
pkg/tools; add GetServer to Dispatcher.
- Add dispatchPlanBackend in pkg/agent: routes task_create calls through
the dispatcher to any registered task MCP server.
- BuildAgentEnv auto-wires the task backend to the reasoning server after
connecting MCP servers; re-wires on every /agent switch.
- Add ListServers() to Core interface: groups all registered tools by
server, returns formatted text.
- Add /mcp command to handleCommand(); update /help.
- Add doc/PLANNING.md and update ARCHITECTURE.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hooks now receive JSON on stdin, can block actions (exit 2), and inject
stdout into the conversation. Stop hooks can continue the agent loop
with stderr as the next prompt. 60s timeout, context-cancellable.
Config format unchanged — existing hooks still work.
- Inject(prompt) cancels the running tool and injects the message into
the conversation as <system-user-interruption>
- Queue/PopQueue FIFO for deferred prompt execution
- Submit() drains FIFO after each turn
- IsRunning() for frontends to choose Submit vs Inject
- TUI: typing during a turn calls Inject; /q queues
- 9P: prompt interrupts if busy; prompt.fifo is push/pop FIFO
Instead of discarding history on interruption, record what happened:
- Partial assistant content and tool calls are preserved
- Skipped tool calls get synthetic cancelled results
- Phase-specific notes (stream/tools/request) tell the model what occurred
- Side effects from already-executed tools are no longer hidden from history
- Add ContextLength(ctx) int to Backend interface; each backend fetches
from its API (OpenAI /v1/models, Ollama /api/show, CodeWhisperer
ListAvailableModels) with caching. Falls back to 128k default.
- Add Models(ctx) []string to Backend interface for model discovery.
- Add /models command to list available models.
- Auto-compact before each turn when estimated tokens >= 75% of context length.
- Structured compaction prompt asking for progress, decisions, next steps
- Flatten tool call/result sequences to plain text before compaction
- Preserve recent user messages (up to ~20k tokens) in compacted history
- Inject summary as handoff message for the resuming LLM
- Persist pre-compaction snapshots as append-only JSONL for long-term memory
Delete context.go and its entire eviction system (contextBuilder,
SoftLimit/HardLimit/TailMessages, buildBounded, sanitizeHistory).
Session now holds a plain []backend.Message with no silent dropping.
/compact still works via LLM-driven summarization. /context still
shows message count and token estimates.
Move the system prompt from an inline Go string to prompts/SYSTEM_PROMPT.md,
loaded at runtime from ~/.config/ollie/prompts/ via text/template. The prompt
is now editable without recompilation and focuses on behavioral guidance
rather than token-saving directives.
- Remove systemPromptBase constant and token-efficiency instructions
- Add DefaultPromptsDir() for prompt directory discovery
- Load and render SYSTEM_PROMPT.md with {{.WorkDir}}, {{.Platform}}, etc.
- Update mkfile to install prompts/ to ~/.config/ollie/prompts/
buildFirstPrompt was calling os.Getwd() unconditionally, injecting the
server process's working directory into the first turn's file listing
regardless of the session's configured workdir. Now accepts workdir and
falls back to os.Getwd() only when empty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
execute.Decl is now a function (func(workdir string) func() tools.Server)
rather than a var, making workdir an explicit dependency rather than
an implicit os.Getwd() call. execute.Server stores the workdir and
sets cmd.Dir accordingly; falls back to os.Getwd() if empty.
AgentCoreConfig gains WorkDir string, threaded into BuildAgentEnv and
the system prompt. The /agent switch uses s.workdir so the correct
directory is preserved across agent switches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Session IDs are now <unix-nanoseconds>-<random-hex> instead of a
human-readable timestamp. Lexicographic sort = creation order, with
nanosecond granularity ensuring correct ordering even for sessions
created in rapid succession. The random suffix guarantees uniqueness
in the (extremely unlikely) event of a nanosecond collision.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- execute.Decl and file.Decl: package-level func() tools.Server factories;
consumers reference them without calling directly
- tools.NewDispatcherFunc: builds a fresh Dispatcher per call by invoking
each registered Decl — used as AgentCoreConfig.NewDispatcher
- execute.Server: remove LogDir, WorkspaceBase, MaxOutputChars, and all
file-based logging (metrics.go deleted); New() takes no args
- Export AgentConfigPath, NewSessionID, DefaultAgentsDir, DefaultSessionsDir
from pkg/agent so frontends don't duplicate them
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace ExecServer/FileServer fields on AgentCoreConfig with a
NewDispatcher factory func. The /agent handler calls it to rebuild
a fresh dispatcher on agent switches, without core needing to import
execute or file.
Drop ToolsPath from the system prompt template — the path is already
embedded in the execute_tool and execute_pipe tool descriptions by
the execute package itself. Removes the text/template dependency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Matches the pattern of mcpServer/NewMCPServer — both are unexported
Server implementations created via constructors. No conceptual
difference between the two from the caller's perspective.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pkg/tools/builtin.go: BuiltinServer implements tools.Server for all
built-in tools; ExecuteDefs/FileDefs live here (subpackages can't
hold them without reintroducing the import cycle)
- pkg/tools/execute/dispatch.go: drop tools.Server implementation;
export Dispatch method for BuiltinServer to call
- pkg/tools/file/: export Read(path) and Write(path,content,start,end);
remove server.go
- pkg/agent/core.go: register one tools.BuiltinServer instead of two
separate servers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New pkg/tools/file package with read.go, write.go, server.go
- file.Server implements tools.Server; registered as "file" in MCPExecutor
- Remove file_read/file_write from execute/dispatch.go
- Confirm closure shared between builtin and file servers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add tools.Server interface; MCPExecutor now holds map[string]Server
- execute.Executor implements tools.Server (ListTools/CallTool/Close)
- Built-in tools register as "builtin" server in MCPExecutor
- Remove duplicate dispatch functions from core.go (moved to dispatch.go)
- Remove builtinTools() from core.go (now in dispatch.go)
- Remove invalidateCaches and trustedTools dead code
- All tool calls now route through one mcpExec.Execute() path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>