New method on the Core interface that appends a lightweight user
message to session history without triggering a model turn. The
agent sees the reaction as context on its next turn.
Used by the 9P react file: echo emoji > s/{id}/react
When tool results are streamed, suffixes appended after execution
(truncation hints, user-interruptions, PostTool context) were only
written to the stored message history but never emitted to the chat
stream. This meant they never appeared in D-Bus signals or the chat
file.
Accumulate suffixes explicitly and emit them as a final event when
streaming was active, guaranteeing they appear at the end of the
tool output in all output paths.
The tool result truncation had two bypasses:
1. Error results were exempt (!isErr condition) — a command returning
output in its error message could flood the context with megabytes
of untruncated data.
2. The streaming callback emitted chunks to the event handler (and
thus D-Bus signals) with no size check, flooding the bus even
when the final stored result would be truncated.
Fix:
- Remove the configurable ToolResultMaxBytes field; use the constant
defaultToolResultMaxBytes (128KB) unconditionally for all results
regardless of success/error status.
- Cap the streaming callback at 128KB so chunks stop being emitted
once the ceiling is reached.
- Cap output embedded in execute server error messages to 8KB as
defense in depth.
Proxies SSH agent requests to the real agent, intercepting
Sign/SignWithFlags for approval via the same broker flow
(notification + persist). Key fingerprints can be persisted
to auto-approve future sign requests.
The operational model was only rendered for new sessions, not for
restored sessions or /agent reloads. Store baseLayers on the agent
struct and pass them through on rebuild.
Also fix pre-existing test failures caused by DiscoverTools reading
from the live OLLIE_TOOLS_PATH during tests.
DiscoverTools() results have no InputSchema and are invoked via
call_tool, not directly. Sending them to Bedrock as tool definitions
caused ValidationException (inputSchema.json.type must be object).
Now only built-in executors (execute_code, call_tool, pipe) with proper
schemas are registered as backend tools. Discovered scripts remain in
allToolInfos for the preamble surface listing only.
BuildRuntime now appends a compact surface listing (name + one-liner)
to the preamble instead of full tool prompts. Full prompts are
available on-demand via ToolPrompt(name) for the /net/dns pattern.
- DiscoverTools: extracts short description from first content line
- BuildRuntime: appends "# Available Tools" with name + description
- ToolPrompt(name): returns full ollie:prompt for a specific tool
- Add Prompt field to tools.ToolInfo
- Add ExtractPrompt parser for ollie:prompt...ollie:end blocks
- Add DiscoverTools to scan tool scripts directory
- BuildRuntime appends discovered tool prompts to preamble
Tool scripts can now embed their usage documentation directly in
the script header. This replaces the separate tools-*.md prompt
files and ensures documentation stays co-located with implementation.
BuildRuntime now accepts optional baseLayers (variadic strings) that
are prepended to the agent-resolved preamble. This enables the server
to compose: system_prompt + operational_model + environment + agent_prompts.
- config.Config: add SystemPrompt field (path override)
- AgentCoreConfig: add OperationalModel and SystemPrompt fields
- BuildRuntime: prepend baseLayers before agent prompt content
- Remove debug fprintf statements from prompt resolution
Move the read-safe tool result cache from a per-turn local variable
to the agent struct, persisting it across turns. This eliminates
redundant calls to file_read, lsp_*, and memory_recall when the
underlying data has not changed.
The cache is invalidated (cleared entirely) when file_write or
file_edit executes, ensuring stale results are never returned
after mutations.
saveSession() now sets a dirty flag and starts a 2-second timer
instead of immediately serializing and writing the full message
history. For a 50-step turn, this reduces 50 JSON marshals + disk
writes down to ~25 (one every 2s) plus a guaranteed flush at
turn end and on close.
flushSave() is called:
- At end of turn (after setState idle)
- On interrupt
- On Close()
- When the 2s timer fires (if still dirty)
The elevation adapter is now managed by the system service manager
(openrc/systemd), not started on-demand from inside the sandbox.
The on-demand approach was unreliable (needed master token access,
exec chain failures on Gentoo, stale instances on socket).
When the elevated process exits with a non-zero code but produces no
stdout/stderr output, report a cleaner error message instead of the
confusing "Output: " with empty string.
Added PromptEnvExtra to AgentCoreConfig and agent struct. The /agent
command now uses stored PRIME_* vars instead of recomputing locally.
This ensures remote sessions get the correct remote host info on
every prompt rebuild.
Added PromptEnv(cwd) helper that returns PRIME_CWD, PRIME_PLATFORM,
PRIME_IS_GIT_REPO. All callers of BuildRuntime now pass these.
prime script is now a pure template renderer with no detection logic.
Removed ad-hoc PRIME_CWD injection from prompt_resolver.
prompt_resolver: when cmd.Dir cannot be set (remote CWD does not exist
locally), inject PWD=cwd into subprocess env so envsubst in prime sees
the correct working directory.
remote.Server: fetch host_info RPC after connect, expose HostInfo struct
with platform, arch, is_git_repo for the session to pass to prompts.
The /agent command rebuilds the runtime but only passed OLLIE_SESSION_ID.
For remote sessions where cmd.Dir falls through, the prompt scripts need
PWD explicitly set to the session CWD for envsubst in prime.
Dial() now bootstraps ollie-remote on the remote host automatically:
- Computes SHA-256 of the local ollie-remote binary
- Sends a bootstrap script over SSH stdin
- If the remote has a matching cached binary, skips transfer
- Otherwise transfers gzip+base64 encoded binary
- Monitors stderr for OLLIE_LOADER_START/READY signals
- After bootstrap, stdin/stdout switch to JSON-RPC
The local binary is found at ~/bin/ollie-remote (just build output)
or ~/.local/bin/ollie-remote or PATH.
remote.Server.ListTools() now calls the remote via a list_tools RPC
method instead of returning nil. This ensures the model receives
proper tool definitions through the API.
remote.Server.CallTool() now passes the actual tool name (execute_code,
call_tool, pipe) as the RPC method instead of hardcoding execute.
Simplified result handling since resp.Result is already json.RawMessage.
prompt_resolver: guard cmd.Dir with stat check so remote CWDs that
do not exist locally do not cause prompt commands to fail with ENOENT.
server.go: add cancel on all early-return paths to fix go vet
cancel-not-used-on-all-paths warnings.
If the session CWD does not exist locally (remote execution), do not
set cmd.Dir. This prevents fork/exec failures when the CWD only
exists on the remote host.
Previously loadSandboxConfig was called unconditionally, causing
failures on hosts without ~/.config/ollie/sandbox/ even when yolo
mode was enabled. Move the load inside the !Yolo branch.
Package remote provides a tools.Server implementation that forwards
tool calls to an ollie-remote process over SSH via JSON-RPC.
Key components:
- Dial(): opens SSH, launches ollie-remote, verifies with ping
- Server: implements tools.Server, forwards CallTool over RPC
- Decl(): factory compatible with tools.NewDispatcherFunc
- Config: SSH target, CWD, tools path, yolo flag
Integration: swap execute.Decl() with remote.Decl() in session
creation when remote= config is set.