Interrupting a long turn previously cleared the session (first-turn case)
or rolled back messages (subsequent turns). An interrupt is intentional —
keep whatever state was built so the user can continue.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DeepSeek's native tool-call format uses <|DSML|invoke> tokens. When
OpenRouter fails to convert them to standard tool_calls chunks, they
arrive as raw content text. Detect the marker, buffer the content, and
parse it into ToolCall structs at [DONE] if no structured tool calls
accumulated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Session.plan field and SessionFile handler for 9p endpoint s/{id}/plan.
Agents write markdown checklists here via task_add/task_check/task_clear;
peers and parent sessions can read it directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Snapshot session state before each turn. On any error from run(),
restore to the pre-turn snapshot so the session is clean for the next
attempt. Remove recordInterruption — those messages were appended to
state that gets rolled back anyway, making them dead code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
preTool fires before each tool execution; exit 2 blocks the call and
returns the hook's stderr as the tool error result. postTool fires after;
exit 0 appends context to the result, exit 2 replaces it entirely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Non-blocked postTurn hook output is now appended to session history so
the model sees it at the start of the next turn without triggering
autonomous continuation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract runCompact() to handle the full compact cycle (pre-hook, compact,
spawn-context re-injection, post-hook) consistently across all three call
sites. Wire AutoCompact into agentConfig so loop.go triggers it after each
tool round, not just before turns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename loopConfig→agentConfig, agentCore→agent, loopcfg→cfg, systemPrompt→preamble
for clarity: the struct represents agent runtime config, the field is its config,
and preamble accurately describes the compiled system+agent prompt sent as system role
- Separate preamble from agent context: preamble now contains only SYSTEM_PROMPT.md;
agent prompt and agentSpawn hook output are injected into session history at
context refresh points instead of being bolted onto the system role
- Replace fireAgentSpawn (mutated preamble once, guarded by agentSpawnFired flag)
with spawnContext (assembles agent prompt + hook output, returns string for caller
to inject); remove agentSpawnFired entirely — session==nil is the sufficient guard
- Three refresh points inject spawn context into session history:
session start, post-clear (session=nil triggers same path), post-compaction
(auto and manual)
- Move all preTurn tool primes (tools-file, tools-reasoning, tools-memory,
tools-subagent, tools-elevate) to agentSpawn in all agent configs so they are
injected at session refresh points alongside bd prime, not repeated every turn
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Default to $XDG_RUNTIME_DIR/ollie/elevate.sock (falling back to
/run/user/<uid>/ollie/elevate.sock) so the daemon sets and propagates
the value without requiring explicit configuration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add paths.ExpandHome() helper for tilde expansion
- Apply in NewAgentCore, SetCWD (agent + execute server), and hook runner
- Fixes 'chdir ~/path: no such file or directory' errors
- Report non-zero size for wait files so FUSE clients read them
- Return current state on timeout instead of nil
- Set ChatOffset after user message so offset points past it
- Comment out hooksRan info events (TODO: route to debug file)
Batch jobs were a separate concept (one-and-done lifecycle, distinct
dirspec) that turned out to be a front-end concern. s/b, s/bfg, and
s/bbg now compose session primitives directly: create session, submit
prompt, loop statewait until idle, extract chat[offset:], kill.
Also renames spec -> cfg throughout (session config file) and removes
the parallel/output fields that previously lived in the batch spec;
parallel is now a shell loop, output=json is a prompt suffix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove state, backend, model, agent, cwd, params, cwdwait, usagewait,
ctxszwait as individual files. Add spec (0666) which on read returns a
KV snapshot of all config and current state; on write, dispatches
partial KV pairs to the appropriate handlers (backend/model/agent via
Submit, cwd via SetCWD, generation params via SetGenerationParams).
Read-only fields (state, usage, ctxsz, offset) are silently ignored on
write. Only statewait survives from the *wait set — it has genuine
control-flow semantics; usagewait/ctxszwait/cwdwait are better served
by post-turn hooks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SessionStore now embeds a *BatchStore and presents a unified
interface. s/new detects batch format (--- delimiter) and
delegates accordingly. list/stat/del/OpenStore/index all check
both session and batch maps. Shutdown/InterruptAll delegate to
both. Batch scripts (job, q, sched, cleanup) added to session
store file table.
This is the store-side half of Option C: single namespace with
composable file tables. The 9p server changes are in the parent
repo.
Introduce FileSpec and RunFileStore as a shared, table-driven
implementation of RunnableStore. Both SessionFileStore and
BatchJobStore are now type aliases for RunFileStore, constructed
from a list of FileSpecs that define per-file read/write/wait
behavior via function pointers.
This eliminates ~94 lines of duplicated store plumbing (stat/list/
open/entryFor methods) while preserving the existing filesystem
layout and all public API signatures.
No behavioral changes. Tests pass unchanged.
assistant events are streamed without trailing newlines, causing
line-buffered 9P reads to lag one turn behind. Close the assistant
block with \n on role transitions and after Submit returns. Set
ChatOffset at EOF before the user event so offset spans the full
exchange. Remove 500-char truncation of tool call args in FormatEvent.
Adds a synthetic `tail` file (mode 0555) to each session directory
that runs `tail -f chat`, allowing users to follow a session's chat
log from any working directory.
expandSystemPrompt fell through to os.Getenv("PWD") which always
returned the process startup directory. Map "PWD" explicitly to the
cwd parameter so the system prompt reflects the session's working
directory.
- Test all expandPath branches (HOME, XDG_*, OLLIE_CFG/DATA_PATH)
- Test checkPath symlink resolution and ROX read/write
- Test LoadSandbox reader error
- Test WrapCommand: landrun unavailable, ROX/RWX entries, sort tiebreaker, superpowerd wrapping
- Test isSuperpowerdRunning: all socket dir paths, successful connection
- Make isAvailableFn/isSuperpowerdRunningFn overridable for testing
- Add Warning field to HookResult
- runHookCmd sets Warning on timeout and start failure
- Hooks.Run collects warnings across commands
- All hook call sites emit warnings via infoEvent
- Test verifies timeout warning is surfaced
- Store = BlobStore + Open(name) (Store, error)
- BlobStore = ReadWriteStore + Delete, Create, Rename
- NewFlatDir/NewSkillStore return BlobStore (flat, no subdirectories)
- SessionStore/BatchStore satisfy Store via Open for child stores
- SessionFileStore/BatchJobStore embed *storeConfig, satisfy Store
- Replace SessionStore.SessionFileStore/BatchStore.JobStore with Open
- Add StoreEntry type alias for os.DirEntry
- SessionStore now owns construction of its child SessionFileStore
- Add SaveTranscript to SessionStoreConfig
- Server delegates to SessionStore instead of assembling the child
Move ReadableStore, WritableStore, ReadWriteStore, BlobStore, Store
interfaces and FlatDir (née FlatDirStore) implementation from 9p into
core so the agent runtime and other consumers can use them directly.
- Extract expandSystemPrompt(content, cwd) from loadSystemPrompt for
testability without file I/O
- Add tests: ListTools error, tools populated, TrustedTools injection,
agent prompt without system prompt, exec dispatch success/error/isError
- Add mockDispatcher and mockTrustedSetter test helpers
- Test all buildAnthropicMessages branches: system concat, assistant
tool-calls-only, nil arguments default, tool batching, unknown role,
full multi-turn conversation
- Test streamAnthropicSSE done re-entry: events after message_stop and
error are ignored
- Remove unreachable done guard in process closure