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.
Scans colon-separated OLLIE_SKILLS_PATH (default ~/.config/ollie/skills)
for directories containing SKILL.md with name/description front-matter.
Reads from disk on every call, no caching. Deduplicates by name.
- 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>
ReadTool now reads from OLLIE_TOOLS_PATH (default ~/.local/share/ollie/tools)
as a plain filesystem path, compatible with 9pfuse mounts or symlinks.
ToolsPath() is exported and used to expand tool descriptions and the
system prompt template at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vendor anvillm/pkg/sandbox into internal/sandbox, updating config
paths from ~/.config/anvillm to ~/.config/ollie. Remove anvillm
from go.mod.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move internal/backend and internal/config to pkg/ so external consumers
can import them. Remove cmd/ollie and internal/tui — they now live in
the separate ollie-tui repo which imports ollie as a library.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
internal/mcp → pkg/mcp (MCP client, concrete)
internal/tools → pkg/tools (Executor interface + MCPExecutor)
internal/exec → pkg/tools/execute (builtin sandbox executor, split by tool)
Split exec.go into executor.go (sandbox runner), pipe.go (BuildPipeline/PipeStep),
tool.go (ReadTool), and code.go (execute_code is Executor.Execute with trusted=false).
All three packages are now importable by consumers outside this module.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each backend now owns its active model via Model()/SetModel(). ChatStream
no longer takes a model string — callers use SetModel to configure it.
AgentCoreConfig.ModelName sets the backend model at construction time via
SetModel, then the field can be dropped from the live agentCore struct.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Everything not part of the agent<->frontend contract is now unexported:
- contextConfig/Builder/Stats, loopConfig, toolExecutor, confirmFn,
run, state, toolResult — internal loop/session machinery
- buildFirstPrompt, systemPrompt, builtinTools — impl details
- agentCore struct (NewAgentCore returns Core)
- Session methods (saveTo, history, rollback, compact, etc.)
- AgentEnv internal fields (mcpExec, exec, confirm, tools, etc.)
App-level utilities moved from pkg/agent to cmd/ollie:
- resolveBackendName, defaultModelForBackend, newSessionID,
agentConfigPath
RestoreSession now takes int (ctxOverhead) directly, eliminating
the need for contextConfig to be public.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AgentCore (the Core interface impl) belongs in internal/agent alongside
the loop, session, and state it orchestrates — not in a separate one-file
package.
To break the resulting import cycle (agent → pkg/core → agent), define
Event and EventHandler natively in pkg/core instead of aliasing from
internal/agent. The agent loop now imports pkg/core for these types.
Usage token tracking is internal to the loop and no longer emitted as
an event (no consumer used it).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hooks are a generic lifecycle concept — not specific to any frontend.
core.Hooks, core.HookAgentSpawn/UserPromptSubmit/Stop constants, and
Hooks.Run() now live in pkg/core and are usable by any implementation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WatchSignals and CtrlCExitWindow are generic — any frontend can use them.
Removing them from the TUI package keeps tui focused on terminal I/O only.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>