Commit Graph

55 Commits

Author SHA1 Message Date
Levi Neely 9ba68ff35e agent: remove rebuildToolsIdx and rebuildSkillsIdx
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>
2026-04-14 14:47:30 +02:00
Levi Neely 87f056e221 prompt: idx-based tool/skill discovery; session self-awareness
- 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>
2026-04-14 14:27:39 +02:00
Levi Neely e5e94e57fe agent: inject shell tool descriptions into system prompt at build time
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>
2026-04-14 13:00:03 +02:00
Levi Neely ae3fc3be78 refactor: split memory and task out of reasoning into own packages
- 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>
2026-04-14 08:14:41 +02:00
Levi Neely 809ad3472d rename workdir to cwd
Go API: WorkDir→CWD, SetWorkDir→SetCWD, WorkDirSetter→CWDSetter.
Unexported: workdir→cwd in fields, params, locals.
Docs: README.md updated.
2026-04-13 18:03:20 +02:00
Levi Neely bebe3014fc rename workdir to cwd
Rename the user-facing 9P file and session key from "workdir" to
"cwd" for consistency with the /cwd command.
2026-04-13 15:51:48 +02:00
Levi Neely 2273f0e867 agent: drop empty input in Submit 2026-04-13 15:46:25 +02:00
Levi Neely 269c3eacd5 agent: add SetSessionID for session rename support
- 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
2026-04-13 14:25:47 +02:00
Levi Neely dace96ce5b core: guard /model writes while agent is running 2026-04-13 13:34:13 +02:00
Levi Neely adb4b3c46f core: guard state-mutating commands while agent is running
/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.
2026-04-13 13:29:46 +02:00
Levi Neely 47133b1ccd flat planning repo built-in 2026-04-13 11:41:01 +02:00
Levi Neely e5a3c8cfc9 usage: drop TotalResponses, accumulate requests only 2026-04-13 11:06:29 +02:00
Levi Neely cf604bd38f usage: track responses, estimate tokens when backend doesn't report 2026-04-13 11:03:39 +02:00
Levi Neely 0429c590af usage/ctxsz: separate billed tokens from context size
- Rename Core.Usage() to Core.CtxSz() (estimated context size)
- New Core.Usage() returns billed token counts (in, out, requests)
- 9P: add ctxsz file, usage file now returns billed tokens
- /context command now shows context size summary
- Emacs mode-line reads ctxsz instead of usage
2026-04-13 10:45:45 +02:00
Levi Neely 6b8cd7afee add /skills and /tools slash commands 2026-04-13 09:53:27 +02:00
Levi Neely 7ebd809817 improve /backend and command handling: show current backend with no args, emit blank info events before command output 2026-04-13 09:50:32 +02:00
Levi Neely c47c1ea84c agent: /model with no args shows current model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 20:20:43 +02:00
Levi Neely 345ec744d0 remove dead code 2026-04-12 20:05:52 +02:00
Levi Neely 41fa92f544 agent: add pluggable memory backend with flat-dir fallback
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>
2026-04-11 23:26:26 +02:00
Levi Neely ae82e0eff0 agent: inject mid-turn prompts at tool boundaries instead of queueing
- 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>
2026-04-11 22:25:37 +02:00
Levi Neely b194968851 agent: fix reply never being set — commit on run completion not newline event
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>
2026-04-11 18:06:08 +02:00
Levi Neely edad6472c8 agent: initialize state to "idle" in NewAgentCore
Zero value was "" causing IsIdle() to return false on fresh sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 18:05:21 +02:00
Levi Neely 9990ceb444 agent: expose State, Reply, AgentName, BackendName, ModelName on Core
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>
2026-04-11 17:55:25 +02:00
Levi Neely 93e49e9f6f emit user event from Submit; strengthen no-echo-tool rule
- 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>
2026-04-11 17:37:10 +02:00
Levi Neely 8142f5096e agent: yield after queue-based planning; extend PlanBackend interface
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>
2026-04-11 17:24:12 +02:00
Levi Neely 1ac68c2ac1 agent: queue-based reasoning_plan fallback; add default-nobeads agent
- BuildAgentEnv accepts WithFallbackPlanBackend option; wired when
  task_create is absent (task backend takes priority when present)
- reasoning_plan description updated to reflect queue fallback
- agents/default-nobeads.json: copy of default without 9beads MCP

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 16:58:34 +02:00
Levi Neely e2d973fe3f Remove agent stalled feature
Remove MaxSteps limit and associated stalled event emission.
The agent will now run indefinitely until completion or interruption,
eliminating the annoying 'agent stalled' messages.
2026-04-11 15:51:39 +02:00
Levi Neely 5e08beb9bf agent: add dynamic workdir support via /cwd command and SetWorkDir API
- 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>
2026-04-11 14:21:34 +02:00
Levi Neely 7fa952c59f agent: add reasoning_plan, task backend integration, and /mcp command
- 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>
2026-04-11 11:31:41 +02:00
Levi Neely b77aefbe1e agent: add PromptFIFO unit tests
Tests cover FIFO ordering, empty-pop behaviour, and concurrent push safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 10:03:47 +02:00
Levi Neely 7b90a459d1 agent: add /usage command, Usage() and ListModels() on Core 2026-04-10 20:37:21 +02:00
Levi Neely 2f68cc1f31 agent: add usage tracking and per-turn token reporting
Emit usage events from the loop when backends report token counts.
Session tracks cumulative input/output tokens and request count.
2026-04-10 20:29:49 +02:00
Levi Neely c49e117489 agent: richer hooks with JSON payloads, blocking, and context injection
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.
2026-04-10 20:19:38 +02:00
Levi Neely a54e5946bb agent: add interrupt injection, prompt FIFO, and queue drain
- 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
2026-04-10 20:11:12 +02:00
Levi Neely 105b56d746 agent: replace rollback with proper interruption recording
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
2026-04-10 19:39:02 +02:00
Levi Neely 45a094f46e backend: add ContextLength, Models, and auto-compaction
- 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.
2026-04-10 19:35:44 +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 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 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 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 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