Commit Graph

138 Commits

Author SHA1 Message Date
Levi Neely 57a92281cf skills: add pkg/skills for skill discovery from OLLIE_SKILLS_PATH
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.
2026-04-10 19:10:46 +02:00
Levi Neely bb6d25aab0 agents: add coding agent prompt to all agent configs
Replace the minimal "read before write" prompt with structured behavioral
guidance covering code modification discipline, over-engineering avoidance,
security awareness, and code reference format. Adapted from goq's
CODING_AGENT_PROMPT.md.
2026-04-10 19:03:32 +02:00
Levi Neely e4c379ab94 agent: replace naive compaction with structured LLM-driven summarization
- 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
2026-04-10 18:56:17 +02:00
Levi Neely c147c7a7da agent: remove buildFirstPrompt file listing and README injection
The agent discovers files via tools now. No need to stuff git ls-files
and README.md into the first user message.
2026-04-10 18:51:20 +02:00
Levi Neely ade69b0e16 agent: remove character-based context window eviction
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.
2026-04-10 18:49:38 +02:00
Levi Neely c0436a88d1 agent: replace hardcoded system prompt with runtime-loaded template
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/
2026-04-10 18:45:01 +02:00
Levi Neely d585125303 agent: fix buildFirstPrompt using process cwd instead of session workdir
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>
2026-04-10 09:21:12 +02:00
Levi Neely de2365187a docs: update Decl and workdir in README and ARCHITECTURE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:06:44 +02:00
Levi Neely 23913d0d76 execute, agent: thread workdir through Decl and AgentCoreConfig
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>
2026-04-10 09:04:19 +02:00
Levi Neely 5afe380437 agent: use Unix nanosecond timestamps in NewSessionID
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>
2026-04-10 08:50:48 +02:00
Levi Neely 12b4dbac73 README, ARCHITECTURE: document Decl pattern and tool extension model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 08:30:37 +02:00
Levi Neely 8496742f14 tools: add Decl vars, NewDispatcherFunc; strip execute logging
- 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>
2026-04-10 08:27:56 +02:00
Levi Neely 3941742fed README: update package listing and tools section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 08:04:58 +02:00
Levi Neely 6ce6e2514d agent/core: decouple from execute and file packages
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>
2026-04-10 00:03:53 +02:00
Levi Neely f87699ac7d Unify tool servers: dissolve builtin/MCP distinction
All tool servers now implement tools.Server equally. tools.MCPExecutor →
tools.Dispatcher (unexported dispatcher), tools.Executor → tools.Dispatcher
interface, tools.NewMCPServer → tools.NewServer, execute.Executor →
execute.Server, file.Server added. BuildAgentEnv takes a pre-configured
Dispatcher; caller registers execute and file servers before calling.
builtinServer collapsed — no special "builtin" concept remains.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:51:10 +02:00
Levi Neely 2e82bb2306 tools: unexport builtinServer, add NewBuiltinServer constructor
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>
2026-04-09 23:19:02 +02:00
Levi Neely 9d1021f129 tools: consolidate builtin server in pkg/tools/
- 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>
2026-04-09 23:16:29 +02:00
Levi Neely 01c9ac534e tools: move file_read/file_write into pkg/tools/file
- 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>
2026-04-09 23:03:50 +02:00
Levi Neely fa6ac6d1cd docs: update ARCHITECTURE.md for unified tool dispatch
Reflect that execute.Executor implements tools.Server, all tool calls
route through one MCPExecutor path, and document all five built-in tools.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:01:02 +02:00
Levi Neely 8875bc8377 tools: unify builtin and MCP dispatch through tools.Server
- 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>
2026-04-09 23:00:23 +02:00
Levi Neely f70f84e0c8 docs: rewrite ARCHITECTURE.md for extensible core design
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:42:47 +02:00
Levi Neely 7068d1832a mkfile: install sandbox configs to ~/.config/ollie/sandbox
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:39:00 +02:00
Levi Neely 480c3c1f37 docs: update README for current state
- Remove hard anvillm dependency language
- Document OLLIE_TOOLS_PATH
- Document ~/.config/ollie/sandbox.yaml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:37:11 +02:00
Levi Neely 8e2b8083bb execute_tool: replace 9P mount with filesystem read; drop 9fans dep
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>
2026-04-09 22:33:30 +02:00
Levi Neely c7d2684cfc sandbox: copy from anvillm, break dependency
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>
2026-04-09 22:19:46 +02:00
Levi Neely edee1a5889 mkfile: remove build target; ollie is now a library
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:08:21 +02:00
Levi Neely 97b85e5163 refactor: promote backend/config to pkg/, extract TUI to ollie-tui
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>
2026-04-09 22:05:35 +02:00
Levi Neely a3e1a078c4 refactor: move mcp/tools/execute to pkg/ for external consumability
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>
2026-04-09 21:59:01 +02:00
Levi Neely ce329bfcc7 backend: internalize model state; remove model param from ChatStream
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>
2026-04-09 21:42:30 +02:00
Levi Neely f280ef17db Make pkg/agent internals private; move app-level utils to cmd/ollie
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>
2026-04-09 21:31:02 +02:00
Levi Neely c8d2a7a907 Merge agentcore into agent; decouple pkg/core from internal/agent
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>
2026-04-09 21:13:00 +02:00
Levi Neely 992c47dd5b Fix .gitignore: anchor /ollie to repo root only
Bare 'ollie' matched cmd/ollie/ directory. /ollie only ignores the
root-level binary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:07:03 +02:00
Levi Neely c31436c8bf Move hook mechanism to pkg/core
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>
2026-04-09 21:06:43 +02:00
Levi Neely dffce36dd4 Move signal handling from internal/tui to pkg/core
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>
2026-04-09 21:03:46 +02:00
Levi Neely c5b16e739e Restructure: pkg/core interface, internal/ packages, cmd/ollie TUI entry point
- pkg/core: public Core interface (Submit/Prompt/Interrupt) + Event/EventHandler types
- internal/agentcore: AgentCore implementation of Core (session, tools, commands)
- internal/tui: TUI package (readline loop, splitInput, signals, bracketed paste)
- internal/agent,backend,config,exec,mcp,tools: moved from root to internal/
- cmd/ollie: minimal main() that wires agentcore + tui together (~130 lines)
- mkfile: update build target to ./cmd/ollie

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:00:04 +02:00
Levi Neely 868ae09364 Add CodeWhisperer/Kiro backend + backend-aware model defaults
- backend/codewhisperer{,_internal}.go: full Amazon CodeWhisperer
  backend implementation — binary AWS event stream decoding, SQLite
  auth for both enterprise OIDC and personal social/GitHub flows, OIDC
  token refresh, and message encoding to the Kiro wire format
- backend/anthropic.go, copilot.go: new backends wired into New()
- backend/new.go: register anthropic, copilot, kiro/codewhisperer cases
- backend/openai.go: add extraHeaders hook for future use
- agent/loop.go: surface non-standard stop reasons as errors instead of
  silently dropping them
- main.go: defaultModelForBackend() sets a sensible default per backend
  (ollama→qwen3.5:9b, openrouter→deepseek/deepseek-v3.2,
   anthropic→claude-sonnet-4-5, kiro→auto); /backend switch now also
  resets the model to avoid stale foreign model IDs causing
  ValidationException; add -prompt flag for non-interactive batch mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 20:02:23 +02:00
Levi Neely 4d4c1708e6 main: replace bubbletea TUI with goq readline + splitInput
Drop charmbracelet/bubbletea, bubbles, and lipgloss. Replace the full-screen
Elm-architecture TUI with goq's readline-based approach:

- go-multiline-ny for readline input with history and bracketed paste
- splitInput persistent bottom band during agent turns (pulse separator,
  queue display, Ctrl-U/W/Backspace, /queued pop/clear)
- Signal handling: Ctrl-C cancels the current turn; double-Ctrl-C exits
- Plain text agent output (no ANSI formatting yet)
- All existing slash commands preserved; /queued integrated

New files: splitinput.go, splitinput_windows.go, bracketed_paste.go,
rerender_input.go, queued_commands.go, signals.go, signals_unix.go,
signals_windows.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 23:42:47 +02:00
Levi Neely 124e4d4e0e main: cap file listing (16KB) and README.md (8KB) in first prompt
Prevents context explosion on new sessions in repos with large READMEs
or many files. Both are truncated at a clean line boundary with a
trailing "...(truncated)" marker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:23:15 +02:00
Levi Neely 3f98be93d8 main: simplify file_read — full-file read with path caching
Removes start_line/end_line range support from file_read. The tool now
always reads the full file and caches by path; re-reading the same path
is a hard error. file_write's precondition check is simplified to
"path was read this session" rather than range coverage. Removes the
lineRange, fileReadResult, rangesOverlap, and rangesCover helpers.

Also tightens the system prompt: bots should use grep/execute_code for
exploration and reserve file_read for pre-write context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:21:11 +02:00
Levi Neely cc8b252ee1 main: seed first prompt with file listing and README.md
Augments the initial user message with git ls-files output (falling back
to a recursive walk that skips hidden files when not in a git repo) and
the contents of README.md if present, so the agent has real workspace
context from the start instead of guessing which files exist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:45:38 +02:00
Levi Neely a959102e6e main: forbid whole-file overwrites of existing files
Whole-file writes on existing files are the primary source of
corruption: a bot that assembled file content from multiple partial
reads can silently duplicate or mangle sections. The blast radius is
unbounded.

New rule:
- Existing files: start_line/end_line required; whole-file write
  returns a hard error directing the bot to use range writes
- New files (not on disk): whole-file write is the only option and
  remains allowed

Updated file_write tool description to state the rule up front.
Simplified the guard block: the coverage/read check for existing files
now only applies to range writes (the only permitted case).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:40:26 +02:00
Levi Neely 069d88ba79 main: change default model to qwen3.5:9b
qwen3:8b is too obtuse for agentic use; qwen3.5:9b is better at
instruction following and tool use at roughly the same size.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:55:23 +02:00
Levi Neely 939f41013e agent: fix stall state always firing on normal text responses
The "no tools" stall condition (totalToolCalls==0 && hadContent) fired
whenever the bot replied in plain text without calling any tools, which
is the normal completion path. The UI would then see agentStalled before
the done message and never transition back to agentIdle.

Removed the "no tools" stall; only the max-steps limit case is a real
stall worth surfacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:51:10 +02:00
Levi Neely b45f81908e agent: fix compact to proactively summarize older messages
Compact was gated on EvictedMessages(), which only returns messages
that have spilled past the 120k char soft limit. Normal sessions never
hit that threshold, making /compact a permanent no-op.

New approach: compact ALL messages older than the tail window (i.e.
everything computeTailStart puts before the protected tail), regardless
of budget. This makes /compact always useful.

Added OlderMessages(), TailWindow(), and SystemMessages() to
ContextBuilder to support the rewrite cleanly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:49:39 +02:00
Levi Neely bc8c6b75b5 session,dedup,compact: persistence, hard errors, summary display, write-then-write
Session persistence:
- Sessions saved to ~/.config/ollie/sessions/{id}.json after each
  completed agent turn and after /compact
- --session <id> flag resumes a saved session, auto-loading its agent
- /sessions command lists saved sessions with agent and goal preview
- /clear and /agent switch generate a new session ID
- Session ID shown at startup

Dedup:
- file_read: overlap check now returns a hard error (was a warning
  prepended to the result); check uses requested range, not actual
  read range, to avoid redundant dispatchFileRead on overlap
- tool calls: duplicate (name, args) now returns a hard error; key
  is only recorded on successful execution

/compact: Compact() now returns the summary text; displayed in the
  UI so the user can verify quality; session saved after compact

Write-then-write:
- After a successful file_write, repopulate fileRanges with the
  written range so a follow-up write to the same region does not
  require a re-read
- Whole-file writes record exact new line count from content;
  range writes record the written [start, end] with totalLines=0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:46:01 +02:00
Levi Neely 864d13381a main: fix write guard for new files, rangesCover off-by-one, drop 500-line cap, add /agents
- file_write: allow creating files that don't exist on disk without a
  prior read; the guard only applies to existing files
- rangesCover: initialize cur = ws-1 so coverage check starts at the
  target range, not line 0 (range writes to e.g. lines 50-100 were
  incorrectly blocked even when exactly that range had been read)
- remove fileReadMaxLines (500-line pagination cap); range tracking
  makes the truncation unnecessary
- add /agents command: lists .json configs in agentsDir, marks active
  agent with *
- add main_test.go: table-driven tests for rangesOverlap and rangesCover

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:25:02 +02:00
Levi Neely f6f3f60428 clear: reset ctxStats and lastUsage on /clear 2026-04-06 21:46:43 +02:00
Levi Neely 7714243291 prompt,agents: move proactive exploration to system prompt; agent prompts use-case only
"Be proactive / explore first" is generic behavior — moved to systemPromptBase.
Agent prompts now contain only use-case-specific rules (code exploration,
context reads before writing, skill discovery for anvillm-agent).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-06 21:38:15 +02:00
Levi Neely 4ccfcdec4f agents: add code exploration and context rules; scope skill discovery to anvillm
- All four agents: require full source enumeration before explaining code,
  and full context reads before writing or modifying code
- default, default-sp, yolo: remove skill discovery references (anvillm only)
- anvillm-agent: retains skill discovery, adds the same code rules

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-06 21:37:04 +02:00
Levi Neely 0a2983b590 prompt: revert code exploration rules from systemPromptBase (moved to agents)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-06 21:35:43 +02:00