Commit Graph

134 Commits

Author SHA1 Message Date
Levi Neely cfdaf18150 agent: replace handleCommand switch with dispatch map
Each slash command is now a closure in a map[string]cmdFn. Adding or
removing a command is a single map entry with no fall-through risk.
The ! shell handler is unchanged. handler(infoEvent("")) now fires only
for recognized commands, not unknown /foo inputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:19:09 +02:00
Levi Neely c3d749d2d3 agent: sweep stale tmpdirs at startup
Crash-left /tmp/ollie/{id}/ directories are now cleared once at process
start via sync.Once before the first session tmpdir is created.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:15:02 +02:00
Levi Neely d6fa9458e5 agent: hard-fail on loadSystemPrompt error and auto-compact error
Both failures now panic with a descriptive message. Submit recovers,
logs the panic and stack trace via clog, cancels any in-progress action,
resets state to idle, and emits an error event to the handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:12:58 +02:00
Levi Neely 5ffda77972 agent: convert Submit recursion to explicit loop
Post-turn hook continue, unconsumed pendingInject, and FIFO drain all
previously re-entered Submit recursively, growing the stack with each
continuation. Replace with an explicit loop in Submit and extract the
single-turn logic into executeTurn, which returns the next prompt or "".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:07:14 +02:00
Levi Neely 5569b3917b agent: load SYSTEM_PROMPT.md as base system prompt, append agentPrompt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 09:14:07 +02:00
Levi Neely 93e5fe9f43 agent: remove Role:"newline" event
setState("idle") was called before handler(Event{Role:"newline"}),
meaning the final appendChat("\n") raced against statewait watchers
and clients that poll state. Remove the event entirely; clients no
longer see it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:01:04 +02:00
Levi Neely 5a3f76d035 agent: fix AfterFunc broadcast missing lock; add WaitChange tests
context.AfterFunc callback was broadcasting without holding changeMu,
risking a missed wakeup if ctx cancels between the consumer's check
and cond.Wait(). Add unit tests covering the full state-change cycle
and the missed-wakeup race under -race.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:55:57 +02:00
Levi Neely f03ccadd43 agent: fix missed-wakeup race in WaitChange
notifyChange must hold changeMu when broadcasting. Without the lock,
the broadcast can fire between the consumer's value check and its
cond.Wait call — the signal is lost and the consumer sleeps until the
next change, causing an apparent off-by-one lag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:25:43 +02:00
Levi Neely fdc77618ab agent: add WaitChange for blocking reads on state/usage/ctxsz/cwd
Adds a sync.Cond-based change notification mechanism. setState,
SetCWD, and usage updates all broadcast. WaitChange blocks until the
named field changes from the given current value, with context
cancellation support via context.AfterFunc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:09:03 +02:00
Levi Neely 524dccdc81 agent: rename userPromptSubmit→preTurn, stop→postTurn
Names now reflect the turn lifecycle rather than implementation details.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 15:47:42 +02:00
Levi Neely 800dfa526e agent: remove Go-based systemPrompt composition; priming now via hooks
systemPrompt() loaded and templated files from DefaultPromptsDir() at
startup and on cwd change. This is now handled entirely by the
agentSpawn hook using the prime script, eliminating the parallel
prompt-injection path.

DefaultPromptsDir() is kept for the 9P server's p/ directory exposure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 12:10:07 +02:00
Levi Neely be42fbd739 hooks: support array of commands per hook event
Hook values can now be a string or array of strings in agent JSON config.
Commands run in order; exit 2 from any command blocks immediately.
Context from all successful commands is concatenated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 11:53:57 +02:00
Levi Neely aeeb0a3e22 execute: prepend \$OLLIE/x/ to PATH for tool subprocesses
Allows wrapper scripts in ~/.config/ollie/scripts/x/ to shadow system
binaries transparently without modifying the user's shell environment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 11:07:23 +02:00
Levi Neely b7c1b317cb agent: fix first-turn chat display; setState thinking before agentSpawn hook
setState("thinking") was called after fireAgentSpawn, so if the agentSpawn
hook (e.g. bd prime) took longer than the TUI's 150ms grace period,
tailUntilIdle would see state=idle and exit before the LLM turn began.
Move setState("thinking") to before the session-nil block so the TUI
immediately sees a non-idle state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 10:41:21 +02:00
Levi Neely 031dc349e2 agent: add hook observability; log state changes only on transition
- Debug-log all hook firings unconditionally (name, cmd, exit, context_len)
- Emit "N hook(s) run" to chat for lifecycle hooks (agentSpawn, pre/postCompact,
  pre/postClear); suppress per-turn hooks (userPromptSubmit, stop) from chat
- Store env.Messages (MCP connections) on agentCore; emit to chat on first turn,
  debug-log unconditionally
- Replace inline s.state assignments with setState() so debug logs emit only
  on state transitions, not on every State() poll

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 07:24:44 +02:00
Levi Neely d66b8f473f agent: fix agentSpawn hook; add pre/post compact and clear hooks
agentSpawn previously fired on every turn with its result discarded.
It now fires once on the first turn of a session, injecting stdout into
the opening message. It re-fires naturally after /agent switch since
that resets the session.

Add preCompact/postCompact hooks at both compact sites (auto and manual).
Add preClear/postClear hooks around /clear. Pre-hooks can block the
operation (exit 2); post-hooks are fire-and-forget notifications.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 06:54:44 +02:00
Levi Neely 56c299d29b elevation: replace Elevator interface with x/elevate plugin script
Remove the elevation package and Elevator interface entirely. The
execute server now invokes x/elevate directly, with detection reduced
to a stat check. Decl() loses the elevator parameter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:50:20 +02:00
Levi Neely e09a6c1250 elevation: add Elevator interface + superpowers backend; add elevated:true to execute_code steps
Remove session-scoped env injection from frontend (s/{id}/env, -e/-env flags) — superseded
by per-invocation superpowers run-session in the elevator. Keep agentCore env map and
OLLIE_SESSION_ID propagation via pushSessionEnv.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:06:43 +02:00
Levi Neely fc1d1c6f83 agent: include session env in !cmd subprocess
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:19:55 +02:00
Levi Neely 97e453d453 env: session-scoped env injection via session spec; no daemon-global env
- BuildAgentEnv accepts sessionEnv map for MCP ${VAR} expansion without
  touching os.Setenv — session vars stay isolated from other sessions
- STDIOTransport uses os.Expand with session lookup instead of os.ExpandEnv
- Core.SetEnv injects into execute server envExtra only (not process env)
- Remove global env exposure; env is injected at session creation time

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 15:59:51 +02:00
Levi Neely 44bff71a65 env: centralize daemon env in pkg/env; clean up session env propagation
- New pkg/env package owns EnsureDefaults, Set, Get, All, Format
- BuildAgentEnv reverted to 3-arg signature (no initialEnv)
- agentCore.setEnv removed; pushSessionEnv handles OLLIE_SESSION_ID only
- execute.Server.SetEnv no longer calls os.Setenv (session-scoped only)
- !cmd handler sets OLLIE_SESSION_ID explicitly in subprocess env
- mcp/transport: seed cmd.Env from os.Environ() so MCP servers inherit PATH etc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 15:43:01 +02:00
Levi Neely f6aa68b80e agent: expose GenerationParams on Core interface
Add GenerationParams() and SetGenerationParams() to the Core interface
and implement them on agentCore, allowing frontends to read and update
sampling parameters at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 09:49:42 +02:00
Levi Neely 95fdc90c18 sandbox: allow write access to OLLIE_TMP_PATH
Add {OLLIE_TMP_PATH} to DefaultConfig filesystem RW list so landrun
permits writes to the tmp directory. Also mkdir the path at session
init so the pathExists check in WrapCommand doesn't skip it.
2026-04-17 16:09:44 +02:00
Levi Neely 2703e754b8 propagate OLLIE_TMP_PATH to sandbox
Add OLLIE_TMP_PATH to DefaultConfig env list and inject it via SetEnv
in NewAgentCore and SetSessionID, matching the pattern used for OLLIE
and OLLIE_SESSION_ID.
2026-04-17 16:07:33 +02:00
Levi Neely 92034b5342 add OLLIE_CFG_PATH and OLLIE_DATA_PATH env vars
Replace all hard-coded ~/.config/ollie and ~/.local/share/ollie paths
with a new paths package that reads OLLIE_CFG_PATH and OLLIE_DATA_PATH,
falling back to the previous defaults when unset.
2026-04-17 15:31:19 +02:00
Levi Neely c2ba1728e4 execute: unify execute_pipe into execute_code as pipeline primitive
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 21:36:22 +02:00
Levi Neely ab96de2771 execute: remove execute_tool; use execute_code {tool} steps instead
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 21:18:12 +02:00
Levi Neely cf25268d80 execute: refactor execute_code to parallel steps; add parallel pipe stages
- execute_code now takes steps:[{code,language}|{tool,args}] instead of
  a single code/language pair. Single-step calls return raw output;
  multi-step calls run in parallel and return results under === step N ===
  headers in submission order.
- execute_pipe stages may now include {parallel:[...]} to fan out
  concurrently within a pipeline. Parallel outputs are concatenated in
  submission order and fed as stdin to the next stage.
- Stage execution is now Go-level (stdin chaining) rather than a single
  bash pipeline string, enabling parallel stages and tool/code mixing.
- CodeStep and resolveCodeStep are the shared primitives for both
  execute_code and execute_pipe parallel stages.
- executeWithStdin added to Server for stdin chaining between stages.
- Tool steps use shebang detection; language field applies only to
  inline code steps (default: bash).
- ListTools schemas and descriptions updated throughout.
2026-04-16 20:06:05 +02:00
Levi Neely 54f20bce02 log: async writes via buffered channel 2026-04-15 18:19:57 +02:00
Levi Neely da094e9803 Revert "execute_tool: resolve tools from $OLLIE/t"
This reverts commit 42b7b5b5c2.
2026-04-15 11:13:37 +02:00
Levi Neely 42b7b5b5c2 execute_tool: resolve tools from $OLLIE/t
ToolsPath() fell back to ~/.config/ollie/tools when OLLIE_TOOLS_PATH
was unset, ignoring the 9P mount entirely. Add /home/lkn/mnt/ollie/t as the
preferred fallback so execute_tool finds tools on the mounted
filesystem regardless of cwd.
2026-04-15 11:12:53 +02:00
Levi Neely 5582f3c8de add shared logger package; replace ad-hoc debug logging
New ollie/pkg/log with per-component log levels:
  OLLIE_LOG=debug        (global default, fallback: warn)
  OLLIE_CORE_LOG=debug   (component override)
  OLLIE_9P_LOG=info      (component override)

info/debug → stdout, warn/error → stderr.
2026-04-15 10:24:25 +02:00
Levi Neely 313a5d85fe add debug logging to all core agent methods
Gated behind OLLIE_CORE_DEBUG=1. Logs tagged with "core:" prefix
to distinguish from 9p server logs.
2026-04-15 10:13:32 +02:00
Levi Neely 655b9e7060 fix: surface tool script errors to model; inject session env at startup
- extractMCPResult: parse isError from MCP response so tool failures
  are returned as Go errors and marked isErr=true in the loop
- CallTool: emit isError:true content block instead of bare {"error":...}
  so the model sees tool failures as error results, not success
- SetEnv: call os.Setenv so OLLIE_SESSION_ID/OLLIE are in the process
  env and inherited by sandboxed subprocesses
- NewAgentCore: inject session env into execute server at creation time,
  not only on rename, so tools have OLLIE_SESSION_ID from the first call

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 22:49:00 +02:00
Levi Neely 82ceaebbe6 agent: add OllieMountPath to system prompt template data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:41:27 +02:00
Levi Neely ee82ac7181 agent: run ! shell commands in session cwd
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:35:13 +02:00
Levi Neely 8b3b6bd281 agent: add Core.Close() to manage session tmpdir lifecycle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:29:50 +02:00
Levi Neely c03f9386c1 agent: create /tmp/ollie/{session-id} on session start
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:27:35 +02:00
Levi Neely 72f7a95329 agent: add /sp command to display rendered system prompt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:18:43 +02:00
Levi Neely fd2a534943 agent: add SystemPrompt() to Core interface
Returns the fully rendered system prompt for the session, so
the 9P server can expose it via s/{id}/systemprompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:16:58 +02:00
Levi Neely fe00c607a1 agent: load system prompt from ordered p/ parts, not a single file
systemPrompt() now reads all *.md files from DefaultPromptsDir() in
lexical order (00_base.md, 01_foo.md, ...), applies template rendering
to each, and concatenates them with --- separators.

Removes hardcoded eager-loading of skills (reasoning, memory, task,
edit-text) and the skills package dependency. Those now live as numbered
prompt parts alongside the base prompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:15:17 +02:00
Levi Neely 0762a37d1c eager-load edit-text skill; document eager-loading rationale
Skills are loaded into the system prompt from the first turn as an
alternative to encoding guidance in MCP schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:58:13 +02:00
Levi Neely 13590a84e7 implement TrustedTools: explicit trust replaces implicit allow-all
- Add TrustedToolsSetter interface and SetTrustedTools to execute.Server
- Add allowed(): trusted tools return true; untrusted with no Confirm
  return false (deny by default). Confirm returns true=allow, consistent
  with allowed() semantics throughout.
- BuildAgentEnv wires cfg.TrustedTools into the execute server

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:52:37 +02:00
Levi Neely d8421f2c24 rename OLLIE_9MOUNT → OLLIE; always inject OLLIE into execute env
$OLLIE is the ollie root (FUSE mount). Renaming makes it shorter and
cleaner for use in sandboxed code. SetSessionID now resolves and
injects OLLIE into the execute server env so it is always available
regardless of whether the parent process has it set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:39:31 +02:00
Levi Neely da6e8d46c4 eager-load task skill into system prompt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:30:55 +02:00
Levi Neely 4e230d6f0c fix execute_tool: detect language for #!/usr/bin/env shebangs
detectLanguage only checked filepath.Base(fields[0]), which returns
"env" for #!/usr/bin/env python3. Every such script was run as bash,
producing syntax errors. Now checks fields[1] when fields[0] is "env".

Also add OLLIE_MEMORY_PATH to default env passthrough.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:29:19 +02:00
Levi Neely 37bb6aa6c2 agent: read eager-loaded skills from physical path, not 9P mount
Reading from the 9P mount during session creation deadlocked the
server. Use skills.Read() to access the physical files directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:55:06 +02:00
Levi Neely aff2bbc65f agent: embed reasoning and memory skills into system prompt at render time
Read ollie/sk/reasoning.md and ollie/sk/memory.md from the 9P mount
and append them to the rendered system prompt. Removes dependency on
the model following a startup instruction.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:52:19 +02:00
Levi Neely abe8ab76cc tools: replace file and reasoning built-ins with shell scripts
Remove pkg/tools/file and pkg/tools/reasoning. Both are now implemented
as execute_tool scripts in ollie/t/ with feature parity:
- file_read, file_write, file_edit, file_grep, file_glob
- reasoning_think

isread guard delegated to /tmp/ollie/$OLLIE_SESSION_ID/<hash> markers.
SetSessionID renames the tmpdir on session rename.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:26:12 +02:00
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