Commit Graph

1594 Commits

Author SHA1 Message Date
Ollie Agent eaf0a83a57 move LSP .meta files to live alongside their Go code
Each lsp_*.meta now lives in tools/lsp/cmd/<name>/ alongside
main.go, rather than in data/tools/. The lsp-tools just target
was updated to install both the binary and its .meta sidecar.

AGENTS.md updated to document the pattern for compiled tools.
2026-08-03 01:03:27 +02:00
Ollie Agent 60fafd5381 remove tools/builtin/ — zero built-in tools, all tools are external scripts
With the zero-tools transition complete, the tools/builtin/ package
served no purpose — Builtins() returned nil. Removed:

- tools/builtin/builtins.go and the directory
- builtins field, WithBuiltins, Dispatch from toolsrv.Server
- Handler type from toolsrv (only used by builtins)
- OnPreDispatch/WithOnPreDispatch (dead code)
- Strict field and WithStrict (dead code, never read)
- CallTool simplified — no fallback to Dispatch
- builtin import and WithBuiltins/WithStrict calls from ollie-remote
- tools/builtin from justfile build/test targets

Updated AGENTS.md, ARCHITECTURE.md, README.md, EVOLUTION.md
2026-08-03 00:55:22 +02:00
Ollie Agent 73eb4dea63 kde submodule: revert all system-user-interruption changes 2026-08-03 00:42:05 +02:00
Ollie Agent d89259d4f5 Revert "kde submodule: render system-user-interruption as styled yellow box"
This reverts commit 10cd007d90.
2026-08-03 00:39:39 +02:00
Ollie Agent 4dfa6a55d5 Revert "wrap system-user-interruption in its own fence block"
This reverts commit d5dea4446d.
2026-08-03 00:39:29 +02:00
Ollie Agent 10cd007d90 kde submodule: render system-user-interruption as styled yellow box 2026-08-03 00:38:08 +02:00
Ollie Agent d5dea4446d wrap system-user-interruption in its own fence block 2026-08-03 00:37:55 +02:00
Ollie Agent 28cfd40771 kde submodule: fix build on Qt < 6.5 2026-08-03 00:32:31 +02:00
Ollie Agent ff84c0b4c6 kde submodule: render system-user-interruption as styled box 2026-08-03 00:31:15 +02:00
Ollie Agent 6994388ffd remove plan 2026-08-03 00:29:17 +02:00
Ollie Agent 04d191fe68 kde submodule: fix fence close regex 2026-08-03 00:27:32 +02:00
Ollie Agent d0607f363e kde submodule: preserve newlines in plaintext blocks 2026-08-03 00:25:03 +02:00
Ollie Agent 6509bdb5ae streaming tool blocks: always close source fence and emit [[[end]]]
When a streaming tool completes, emit a final empty-Content event so the
log handler always closes the markdown source fence. In the handler,
emit [[[end]]] directly instead of a blank line, so the GUI block parser
transitions back to BlockStart regardless of what follows.
2026-08-03 00:21:48 +02:00
Ollie Agent 7beef106e0 tool_load: add dynamic tool loader with bash script
Add a tool_load tool that writes a tool name to the session's 9P
tools file via ollie-9p, enabling on-demand tool loading.

Reduce autoLoad lists in default.json and theo.json to bare
essentials — everything else is loaded via tool_load.
2026-08-03 00:15:36 +02:00
Ollie Agent 0a418c20f4 bump kde 2026-08-02 23:59:36 +02:00
Ollie Agent 53ecbe249b fix tool output format; bump kde 2026-08-02 23:48:15 +02:00
Ollie Agent b2fe5f1b4e connection statuses 2026-08-02 23:35:30 +02:00
Ollie Agent d3860304af Add formatted tool output fences 2026-08-02 22:57:17 +02:00
Ollie Agent 2d621d26b9 Update KDE frontend 2026-08-02 22:34:41 +02:00
Ollie Agent 8fc47622c8 Persist renamed sessions and profiles 2026-08-02 22:34:35 +02:00
Ollie Agent 39ce817f51 Remove hard-coded tool autoloads 2026-08-02 22:33:54 +02:00
Ollie Agent 1f2870dd20 fix rename session and agent 2026-08-02 22:16:22 +02:00
Ollie Agent aa6f444fa5 bump kde 2026-08-02 22:10:45 +02:00
Ollie Agent 119e673299 fix naming problems 2026-08-02 22:10:00 +02:00
Ollie Agent 088aa38f1c data/agents/default.json: remove hard-coded shell and reasoning_think from autoLoad
These two are built-in tools always available regardless of autoload.
No need to explicitly load them.
2026-08-02 21:16:43 +02:00
Ollie Agent 9af742acd6 fs/lifecycle: fix nil MkdirAll panic blocking agent creation and autoload
The panic at fs/lifecycle.go:463 was a nil function pointer: fs.Config.MkdirAll
is a func field, not a method, and the struct literal in cmd/olliesrv/main.go
omitted it, defaulting to nil. When CreateAgent called
rs.cfg.MkdirAll(...), it panicked before ever reaching the autoload loop.

- Add MkdirAll: os.MkdirAll to fs.Config in main.go
- Spawn tool server in Create() so empty session/new sessions are
  operational, then reuse it in CreateAgent (else if sess.proc != nil)
- Wrap create agent error with fmt.Errorf for clearer messages
- Add panic stack trace to stderr for debugging
2026-08-02 21:16:12 +02:00
Ollie Agent 8fba664f11 cleanup / unify toolsrv bootstrap 2026-08-02 20:08:45 +02:00
Ollie Agent e25157ffcd multiplex toolsrv, wip zero-tool server 2026-08-02 19:18:39 +02:00
Ollie Agent 7b5573ee89 docs: fix design philosophy — Plan 9/Acme, not Emacs
Ollie's extensibility is the opposite of Emacs: instead of building
capabilities on top of an interpreter, the core provides integration
surfaces (9P, shell) and pushes everything else to the surrounding
system. This is the Acme/Plan 9 model.
2026-08-02 16:45:54 +02:00
Ollie Agent 6c6c133d76 docs: update architecture docs for Aug 1-2 refactors
Covers the major changes from the past two days:

- 9P Declarative EDSL (fs/spec.go + fs/builder.go, fs/session/ → fs/ flattening)
- 9P Server streamlining (fid map, GroupTable, method extraction)
- Multi-Agent Deepening (ID/Name split, UUIDv4, two-step creation, empty sessions)
- Eventwait redesign (global /eventwait ring buffer, structured delta events)
- Chat log [[[type]]]/[[[end]]] block format
- KDE submodule evolution (StreamFsm, responsive tree, emoji states, eventwait)

Updates: AGENTS.md, ARCHITECTURE.md, EVOLUTION.md, MULTI_AGENT.md,
         CORE.md, edsl.md
2026-08-02 16:43:13 +02:00
Ollie Agent bb152f401d create: set f.entry after creating files
After Tcreate on a file, the fid is open and should be usable for
read/write without a separate Topen. Set f.entry via root.Open()
after creation so subsequent reads/writes on the same fid work.
2026-08-02 16:23:08 +02:00
Ollie Agent b23785a4d9 Fix directory read: entry not required for dirs
Tree.Open() returns an error for directories (is a directory), so
f.entry stays nil after Topen on a dir. The entry == nil guard in
read() was firing before the isDir branch, breaking directory reads.
Moved the guard after the isDir check — directories use buildDirData
directly and never need entry.
2026-08-02 16:22:13 +02:00
Ollie Agent e1e03e50ae Inline handle into Start; rename Serve→Start, Shutdown→Kill
- handle() is gone — the dispatch + panic recovery is inlined into
  the per-request goroutine in Start(). Panic recovery is lifecycle,
  not protocol handling.
- Serve → Start: accepts a connection and runs the 9P loop
- Shutdown → Kill: stops accepting, cancels sessions, waits for
  goroutines to drain.
- 2 methods remain on *Server: Start, Kill. Server is purely lifecycle.
- All 9P protocol handlers are package-level functions.
2026-08-02 16:18:32 +02:00
Ollie Agent 447156ce13 Extract attach and open from *Server; pass groups and log explicitly
- attach() is now a function taking (*olog.Logger, *GroupTable, ...)
- open() is now a function taking (*fs.Tree, *GroupTable, ...)
- Both captured in the handler map via closures over s.sink, s.groups
- 3 methods remain on *Server: Serve, handle, Shutdown
2026-08-02 16:13:23 +02:00
Ollie Agent 3242f108f9 Store opened file on fid; read/write use f.entry instead of path re-resolution
- Added entry (fs.File) field to fid struct
- open() stores the result of rootTree.Open() on f.entry after
  permission check succeeds
- read() uses f.entry instead of calling root.Open(path[1:]) —
  fids survive renames because the File object references the
  tree entry by pointer, not by path
- write() uses f.entry for RequestRespMode writes — no longer
  needs root parameter at all; it's a pure connState+fid function
2026-08-02 16:11:57 +02:00
Ollie Agent 05e0aba155 Server owns the fid map: validate newfids, GC stale fids
- attach: reject if fc.Fid already in use (was silently overwriting)
- walk: reject if fc.Newfid already in use (both clone and full walk)
- open: validate fid path exists; delete stale fid if path is gone
- stat: validate fid path exists; delete stale fid if path is gone
- wstat: after rename, GC all fids on this connection that reference
  the old path tree (was previously updating paths — now just deletes)

The server fully owns the fid map. The client proposes fid numbers,
the server validates and accepts or rejects. Stale fids (pointing to
renamed/deleted paths) are garbage collected when detected.
2026-08-02 16:08:23 +02:00
Ollie Agent 8601562962 Extract GroupTable from Server; replace permission boolean dance with bitmask OR
- GroupTable is a standalone type with Add/Remove/IsMember, owns its
  own mutex and map. Server now holds groups *GroupTable.
- Removed AddGroup/RemoveGroup/InGroup from *Server (3 fewer methods).
- Permission check in open() now computes effective bits as a mask:
    bits := perm & 7                      // world bits
    if in group { bits |= (perm >> 3) & 7 }  // group bits
    if is owner { bits |= (perm >> 6) & 7 }  // owner bits
    if !hasPermBits(mode, bits) → denied
- No boolean accumulator, no tiered if/else, just bitmask OR.
- 6 remaining Server methods (down from 23).
2026-08-02 15:59:50 +02:00
Ollie Agent 0b4daf016a Replace checkPermBits error-return with hasPermBits bool
- hasPermBits uses a 4-entry mask array indexed by mode&3 to select
  the required permission bits: OREAD→4, OWRITE→2, ORDWR→6, OEXEC→1
- Zero branches, zero error paths — returns bits&mask == mask
- open() uses OR semantics: checks owner, group, world independently;
  any tier granting access is sufficient
- checkPerm removed entirely; permission logic is pure boolean math
2026-08-02 15:55:25 +02:00
Ollie Agent 325ad224be Extract checkPerm to package-level function, resolve perms in open
- checkPerm no longer needs *Server: it takes uid, perm, and inGroup
  directly. The caller (open) resolves uid/gid via fileOwnerGroup,
  perm via makeStat, and group membership via InGroup.
- checkPerm is now a pure function: (uname, mode, uid, perm, inGroup)
  → returns only on permissions, needs nothing from Server state.
- open is the remaining method that resolves these values.

server.go: 10 remaining Server methods.
2026-08-02 15:50:31 +02:00
Ollie Agent 8a13174b4f Extract 9 rootTree-only methods to package-level functions; replace switch with handler map
Converted walk, create, read, write, stat, clunk, remove, makeStat,
and buildDirData from *Server methods to functions taking *fs.Tree
as first parameter. Exactly the same pattern as fileOwnerGroup.

Also:
- Replaced the switch in handle() with a s.handler map, built once
  in New(). All 11 9P message types (Tversion through Tremove) now
  dispatch through the map.
- Extracted flush() as a package-level function (was inlined in switch)
- handle() is now: recover + map lookup. No inline cases, no switch.
- server.go: 802 lines, 10 remaining Server methods (down from 23)
- 20 package-level functions handle the actual protocol logic
2026-08-02 15:44:19 +02:00
Ollie Agent 54403a1ce2 Inline readFile/writeFile; remove unused FileTree alias
- readFile was dead code (defined but never called)
- writeFile was a 7-line helper called only once in clunk;
  inlined as direct s.rootTree.Open + e.Write
- FileTree type alias now unused after both deletions
2026-08-02 15:33:06 +02:00
Ollie Agent 082ba1c511 fileOwnerGroup: convert from method to package-level function
fileOwnerGroup only needed s.rootTree, not the full *Server. Making
it a function taking (*fs.Tree, string) removes one more method from
Server, makes the dependency explicit, and makes it unit-testable.
2026-08-02 15:31:33 +02:00
Ollie Agent 3279725128 Inline three thin wrapper methods in server.go
- openEntry (one-liner delegating to rootTree.Open): inlined into
  the two call sites in read() and write()
- statPath (one-liner delegating to rootTree.Stat): inlined into
  three call sites (fileOwnerGroup, walk, makeStat)
- InterruptAll (one-liner delegating to fs.InterruptAll): deleted,
  it was dead code with no callers anywhere

Also cleaned up: removed unused File type alias and unused 'os' import.

server.go: 825 -> 810 lines. Server method count drops by 3, but more
importantly, zero forwarding boilerplate remains.
2026-08-02 15:27:50 +02:00
Ollie Agent 1e126ce76a Replace imperative 9P filesystem with declarative EDSL
The old fs/session/ package (14 files) and cmd/olliesrv/elevate_tree.go
(imperative FileTree implementations) are gone. Replaced by a single
declarative EDSL in fs/spec.go that declares the entire 9P namespace.

cmd/olliesrv/server.go simplified from 1430 to 825 lines:
- Deleted pathType() (110 lines) — tree.Stat() now handles all path resolution
- Deleted isSessionFile(), dirMode(), filePerm() — all from tree's info
- Deleted fsRoute/routes/route/routeDir — single rootTree, no route table
- Deleted elevateTree — EDSL handles /elevate/ in spec.go
- Deleted handleWrite(), readDNS(), helpPath() — dead code
- Deleted all 5 per-fid result fields — EDSL Request handlers manage state
- Deleted all path-based intercepts in read()/write() — entry.RequestRespMode()
- Deleted all path-depth hacks in makeStat() — custom Stat() closures in EDSL
- Deleted all content-length compute logic — info.Size() from tree
- Removed imports: agent, backend, elevate, paths, toolsrv, json

New fs/ package:
- spec.go — single EDSL declaration of the entire namespace
- builder.go — BuildTree() validates and compiles spec into *Tree
- tree.go — configurable FileTree implementation
- fsnode.go — Dir(), Leaf(), TemplateDir(), NodeOption constructors
- agentfiles.go / sessionfiles.go / rootfiles.go / elevatefiles.go
  — handler implementations wired by the EDSL
- lifecycle.go — Create, Kill, Rename, Shutdown, InterruptAll
- persist.go, procfiles.go
- edsl.md — documentation of the EDSL
2026-08-02 15:22:17 +02:00
Ollie Agent 1fd11f62af update kde submodule: fix fence eating surrounding content 2026-08-02 11:13:31 +02:00
Ollie Agent d5fbd86def update kde submodule: StreamFsm refactor + chatblock FSM fix 2026-08-02 11:11:07 +02:00
Ollie Agent 7b7ccfe056 fix: suppress spurious [[[end]]] on reasoning blocks
Don't set streamingRole='reasoning' when processing reasoning_think
events. Since no [[[reasoning]]] header is written to the log, the
role-transition logic would emit a double [[[end]]] when the next
role (assistant) fires. Also suppress streamingRole for the tool
role's reasoning_think name check.
2026-08-02 11:10:55 +02:00
Ollie Agent e2a37cba3c fs/session: fix dangling pointer in eventRing cond initialization
The closure returned eventRing by value, copying the struct.
e.cond.L was set to &e.mu (the local variable's mutex), but
after the copy evRing.cond.L still pointed to the ORIGINAL e.mu
while evRing.mu was a different mutex. This caused
sync.Cond.Wait to unlock the wrong (already-unlocked) mutex,
panicking every consumer connecting to /eventwait.

Fixed by using *sync.Cond with sync.NewCond, making evRing a
pointer to prevent any copying of the struct with its cond.
2026-08-02 10:29:37 +02:00
Ollie Agent e254f4b942 fs/session: fix double-unlock panic in WaitEvent
sync.Cond.Wait() unlocks the mutex internally during the wait and
re-locks it on return. Using defer Unlock caused a double-unlock
panic when the GUI connected to /eventwait.

Also added context.AfterFunc to broadcast the cond on cancellation,
so blocked readers don't hang indefinitely if the connection drops.
2026-08-02 10:27:52 +02:00
Ollie Agent fd5b215268 eventwait: redesign as global /eventwait with structured delta events
BREAKING CHANGE: /session/eventwait is removed. Use /eventwait instead.

The old event system had several problems:
1. eventwait lived inside /session/ (per-session) but events are global
2. It returned the entire session index on every event, growing unboundedly
3. No structured event data — consumers couldn't tell WHAT happened

New design:
- Global /eventwait at the root of the 9P namespace
- Events are structured delta lines: '<scope> <action> <path>'
  e.g. 'S new session/foo', 'A kill session/foo/agent/bar'
- Ring buffer capped at 100 events (bounded memory)
- Offset-based blocking reads (consumers track position)
- Per-event granularity: S=new/kill/rename, A=new/kill/rename

Call sites updated:
- Session create → 'S new session/{name}'
- Agent create → 'A new session/{name}/agent/{aid}'
- Session kill → 'A kill ...' per agent + 'S kill session/{name}'
- Session rename → 'S rename session/{old} session/{new}'
- Agent rename → 'A rename session/{name}/agent/{old} ...'

KDE GUI updated to use ollie-9p read /eventwait instead of
plan9port 9p read session/eventwait.
2026-08-02 10:25:33 +02:00