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>
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>
- Revised system prompt with concrete prohibitions against verbosity and
premature stopping
- Added GenerationParams (max_tokens, temperature, frequency/presence penalty)
threading from agent config through backend ChatStream calls
- Added stall detection: emits "stalled" role on max-steps hit or zero tool
calls with content, surfaces as "stalled" in status bar
- Added per-session file read range tracking: warns on overlapping re-reads,
blocks file_write unless the target range was previously read
- Added general tool-call dedup: warns on exact (name, args) repeats for
non-file tools
- Both caches invalidated on /compact and /clear; file read cache invalidated
per-path on file_write
- Updated all agent configs with documenting defaults for new generation params
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- /compact: summarizes evicted context messages via LLM call, replaces
them with a single summary system message
- /clear: resets session and display
- ContextBuilder.EvictedMessages(): returns messages outside bounded window
- Session.Compact(): drives the summarization and history replacement
- Pass caller context into Executor.Execute so cancellation reaches
the subprocess immediately
- Use Setpgid + SIGKILL on process group to kill grandchild processes
- Add context to ToolExecutor signature and thread it through dispatch
- Reset agent state to idle after drainAgent
- Rollback incomplete session turn on interrupt
Add agent/context.go with a ContextBuilder that enforces a character budget
(soft + hard) over the assembled history, evicting oldest non-system messages
first and replacing large tool outputs with truncated summaries.
Update agent/session.go to use ContextBuilder in History(), so the loop
always sees a bounded context window regardless of how many steps have run.
The budget defaults are:
- SoftLimit: 24000 chars (~6k tokens at 4 chars/token)
- HardLimit: 96000 chars (~24k tokens)
- MaxToolOutputChars: 2000
- TailMessages: 6 (always keep last N messages verbatim)
All limits are configurable via ContextConfig.
main.go now uses backend.New() for LLM selection (OLLIE_BACKEND env),
agent.Loop for observe/decide/act/update/terminate, and ollie/exec for
built-in execute_code. Multi-turn sessions persist across user inputs.
MCP tools route through mcpExecutor; unknown tools fall back to built-in.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the core agent loop with two state backends:
- Session: ephemeral in-memory, lives for one process lifetime
- BeadState: bead-backed via 9beads tool scripts (claim/read/complete)
Loop is backend-agnostic; takes a ToolExecutor and emits OutputMsgs.
MaxSteps defaults to 1 for simple prompts; callers set higher for tasks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>