Logseq 2.0.1 has bug #12413 where POST /mcp always returns 400.
Switched to the REST API at POST /api which works correctly.
Requires OLLIE_LOGSEQ_TOKEN env var.
Commands: listPages, getPage, searchBlocks, listTags, listProperties,
upsertNodes, getCurrentGraph
- Embed the full sandbox/default.yaml instead of sandbox-remote.yaml
so local ollie-remote subprocesses get proper sandbox permissions
(DNS, git config, etc.)
- justfile copies sandbox/default.yaml temporarily during build
- Register builtins + ToolRegistry + SkillsRegistry on ollie-remote
- Pdeathsig: kernel auto-SIGTERMs children when parent dies
- Cleanup: SIGTERM → 3s grace → SIGKILL (guaranteed kill)
- Atomic counter for unique socket paths (no collisions)
Execution is now identical for local and remote:
- Session calls Spawn()/SpawnRemote() → owns *Process with socket
- Clients call Dial(LocalAddr(socket)) → *Conn over Unix socket
- ollie-remote serves --listen mode with multi-client accept loop
- Builtins (shell, tool_load, skill_load) run in subprocess with registries
Lifecycle:
- Pdeathsig ensures children die with parent (no orphans)
- Cleanup: SIGTERM → 3s grace → SIGKILL (guaranteed)
- Socket paths use atomic counter for uniqueness
- Session Kill() closes Process
Protocol:
- Extended RPC: set_env, set_cwd, detach, list_detached, etc.
- Shell goes through builtin dispatch (parses 'cmd' field correctly)
- Streaming output via JSON-RPC notifications
Shell results were returned as raw strings but the agent expects
structured content blocks ({content:[{type:text,text:...}]}). Fixed
shell handler to return the same format as the local Server.CallTool.
Shell errors now return as isError content blocks rather than RPC
errors, matching local behavior (output is preserved in errors).
The default RPC handler now dispatches to srv.CallTool() for any
unknown method, enabling named tools (file_read, file_grep, etc.)
from the embedded tools to be called remotely.
Before each CallTool, check if the local ollie-remote binary hash
differs from what was deployed. If so, close the SSH connection,
re-dial (triggering bootstrap to upload the new binary), and swap
the connection transparently.
Emits info events when upgrade is detected and when it completes.
ExecuteInSandbox uses StreamFunc(ctx) to emit partial output. The
context passed to it lacked a WithOutputStream callback, so no output
was ever streamed back to the client.
Fix: wrap ctx with WithOutputStream that encodes output as JSON-RPC
notifications (id=0, result={data:...}). The client side already
handles these via the outputNotification path in CallTool.
The bootstrap script now also checks for and deploys the landrun
sandbox binary to ~/.cache/ollie/bin/landrun on the remote host.
Without this, ollie-remote couldn't sandbox tool execution.
Protocol extended: OLLIE_LOADER_START now includes need_landrun field.
Transfers are sequential: ollie-remote first, then landrun if needed.
Each binary has independent hash-based caching.
- Skip cwd existence check when remote is specified (cwd is on remote)
- Add BatchMode=yes to SSH: pubkey-only, no password fallback
- Add StrictHostKeyChecking=accept-new: auto-accept new hosts, reject
changed keys
- Deploy failure already propagates as session creation error via
RemoteDial returning error
When a session is created with a cwd that doesn't exist, all tool
execution fails with a confusing 'fork/exec landrun: no such file or
directory' because exec.Command sets cmd.Dir to the non-existent path.
Validate that cwd exists and is a directory at session creation time,
returning a clear error instead of allowing a broken session.
Also includes the earlier USER/NAMESPACE env export fix (agent.go,
shell.go) for the ollie-9p namespace path issue.
ollie-9p computes its namespace path from $USER and $DISPLAY via
client.Namespace(). When run inside the sandboxed shell, USER was not
guaranteed to be in the environment, producing /tmp/ns..:0/ollie.
Fix: explicitly inject USER into envExtra in SetSessionEnv (alongside
OLLIE_SESSION_ID and OLLIE_UNAME). Also export the computed NAMESPACE
to cmd.Env so ollie-9p can use it directly without recomputing.
Adds system_logs with two variants:
- journalctl (systemd hosts): unit, since, lines, grep
- dmesg (any Linux): source, lines, grep
Both declare sudo: true — privilege escalation is handled by dispatch.
Also adds OR support in match conditions: array values mean any must
pass. Combined with AND across keys:
{"binary": ["journalctl", "systemd-cat"], "os": "linux"}
= (journalctl OR systemd-cat) AND linux
A .meta file can now declare multiple variants, each gated by match
conditions. The first variant where all conditions pass determines the
tool's schema, documentation, and executable. If no variant matches,
the tool is hidden from the registry on that host.
Match conditions:
binary — exec.LookPath or absolute path stat
file — os.Stat (file or dir exists)
os — runtime.GOOS
arch — runtime.GOARCH
env — environment variable is non-empty
nenv — environment variable is empty
This enables network-transparent tool declarations: one .meta file
works across heterogeneous hosts (systemd vs syslog, apt vs pacman,
GUI vs headless). When ollie-remote deploys to a different machine,
tools adapt their contract to what's actually available.
No variants field = current behavior, zero breaking changes.
Tools no longer need to be co-located in $OLLIE_TOOLS_PATH. The .meta
sidecar can specify a cmd field pointing to any executable:
- Absolute path: use directly
- Bare name: resolve via $PATH
Resolution order: OLLIE_TOOLS_PATH/<name> → meta.cmd → PATH lookup.
Local wrappers always win, enabling simplified interfaces over complex
system binaries.
Rewrites doc/WRITING_TOOLS.md to document the plugin model: any
executable + .meta = tool, no source tree integration required.
The Registry had its own discover() method that still required
'ollie:prompt' in file content — the old header-based check that was
removed from DiscoverTools(). This caused tool_load to fail for ALL
tools (not just Go binaries) since .meta is now the sole metadata source.
Fix: delegate to DiscoverTools() which correctly scans .meta files.
Binaries now read path/line/col (matching the .meta schema the model
sees) instead of file/line/character. Also fix col to be 1-indexed
(matching the schema description) with proper -1 conversion to LSP
0-indexed protocol.
- Fix for loop: just doesn't need $$ escaping (that produces PID)
- Remove empty install-scripts and install-contrib targets
- Fold script installs into install-data