Commit Graph

1752 Commits

Author SHA1 Message Date
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
Ollie Agent 8d89ac31eb fs/session: fix agent files incorrectly reported as directories in rootOpen
The rootOpen function in root.go had a bug where paths like
'session/<id>/agent/<aid>/state' were split into 4 parts by
strings.SplitN(name, "/", 4). An if len(parts) == 4 guard
incorrectly returned 'state: is a directory', turning every agent
file into a directory error.

Removed the erroneous guard so the code falls through to
NewAgentTree(...).Open(parts[3]), which correctly opens the agent's
synthetic file.
2026-08-02 10:09:07 +02:00
Ollie Agent d9c2c4c7a3 olliesrv: classify /session descendants before routeDir
The generic routeDir(path) check ran before session-specific path handling.
Because /session is a mounted tree, every descendant under /session/... was
being classified as a directory, including regular files like state,
statewait, cfg, prompt, etc.

Fix pathType() to handle /session/... first, and only fall back to the
generic routeDir check afterward.
2026-08-02 09:58:22 +02:00
Ollie Agent a01829991f olliesrv: route agent paths by FindAgent, not active agent
The 9P server path classifier still validated /session/{id}/agent/{aid}
against sess.Core.Agent().Name(), which leaked single-agent semantics into
multi-agent routing. This broke per-agent paths even when the per-agent
file trees were otherwise correct.

Fix route/type validation to resolve the requested agent with FindAgent(),
and use that specific agent for /proc PID checks.
2026-08-01 22:03:41 +02:00
Ollie Agent beadf8463a update kde submodule (statewait no longer lists agent dir) 2026-08-01 21:53:03 +02:00
Ollie Agent bf4bf87810 update kde submodule (stop tree rebuild on selection changes) 2026-08-01 21:48:06 +02:00
Ollie Agent c5285ac259 fs/session: restore normal blocking semantics for statewait
statewait is a blocking wait file with a 5-second timeout, not an
indefinite long-block stream like chat. Remove LongBlock=true so the
9P server uses the normal blocking path (WithTimeout 5s) instead of
WithCancel-only behavior.

Also remove temporary statewait debug instrumentation.
2026-08-01 21:45:39 +02:00
Ollie Agent d12cebe40e update kde submodule (selection/statewait debug logs) 2026-08-01 21:24:40 +02:00
Ollie Agent 0e04449612 update kde submodule (session click clears agent selection) 2026-08-01 21:22:34 +02:00
Ollie Agent 7d530c162b update kde submodule (only switchAgent starts per-agent streams) 2026-08-01 21:20:57 +02:00
Ollie Agent c6d959e6b0 update kde submodule (explicit agent selection for statewait/chat) 2026-08-01 21:18:21 +02:00
Ollie Agent 1a600fb9eb fs/session: add debug logging to statewait handler
Logs the agent name and base value each time statewait is
read, and whether WaitChange returned ok or !ok.
2026-08-01 21:10:55 +02:00
Ollie Agent a7359fffcf fs/session: statewait must use h.ag.WaitChange, not Core.WaitChange
Session.WaitChange delegates to a.Agent() (the active agent),
so statewait was blocking on the WRONG agent's state changes.
Agent.WaitChange uses per-agent changeCond, so it properly
blocks until this specific agent's state actually changes.
2026-08-01 21:06:26 +02:00
Ollie Agent 3f61a8929a fs/session: wire agentHelper to the correct agent, not the active one
agentHelper (serving a specific agent's 9P directory) was using
h.sess.Core.Agent() everywhere — which looks up by activeAgentName.
For multi-agent, this is wrong: reading session/{sess}/agent/{aid}/state
should return the state of that specific agent, not the active one.

Fix:
- Added ag *agent.Agent field to agentHelper
- NewAgentTree now takes *agent.Agent parameter
- All 33 references to h.sess.Core.Agent() changed to h.ag
- Updated all callers in root.go to pass the resolved agent
- Agent rename handler uses FindAgent instead of active agent
2026-08-01 21:03:34 +02:00
Ollie Agent 38c81fb5cd fs/session: make statewait LongBlock=true so it blocks until state change
Without LongBlock, statewait is treated as one-shot: first read
returns current state, second read returns EOF. The GUI had to
restart the connection in a tight loop to keep polling.

With LongBlock=true, the 9P server blocks the read indefinitely
until the agent's state actually changes. The per-fid waitBase
tracks the previous state value so the next read blocks until a
real change. The GUI opens statewait once and reads from it
continuously — no reconnect loop needed.
2026-08-01 20:59:25 +02:00
Ollie Agent 2cfaff923a update kde submodule (gui: verify agent dir before blocking reads) 2026-08-01 20:56:46 +02:00
Ollie Agent 4208dd9b14 update kde submodule (gui: never auto-restart streams on finish) 2026-08-01 20:55:47 +02:00
Ollie Agent 210638a88c update kde submodule (gui: dedup sessions, fix field mapping) 2026-08-01 20:53:14 +02:00
Ollie Agent 8176daec45 session/idx: emit one line per agent in buildIndex
buildIndex now iterates all agents in each session and emits
one line per agent, so the GUI can enumerate all existing
session/agent directories.
2026-08-01 20:49:46 +02:00
Ollie Agent 9ef397045c session: add multi-agent support by name, not index
- Session.Agent() now looks up activeAgentName in the agents slice
  instead of returning agents[0]. Falls back to first agent if unset.
- Added SetActiveAgent(), FindAgent(), SaveFuncs() methods.
- New() and AddAgent() set activeAgentName automatically.
- CreateAgent: refactored to ADD to existing core session instead
  of overwriting it. If sess.Core exists, builds the new agent via
  agent.NewAgent() and adds it via Core.AddAgent().
- agent/new now lists all agents in the session directory.
- 9P path handlers (rootReaddir/Stat/Open) use FindAgent() and
  AgentLogFor() to resolve agents by name/ID instead of using
  the first agent exclusively.
- Added AgentLogFor(id) to fs Session for per-agent log lookup.
2026-08-01 20:44:47 +02:00
Ollie Agent b1cbb9ca7c session/fs: handle empty sessions during persistence
persistSession: check sess.Core.Agent() != nil before saving.
silently skip sessions with no agent instead of logging
'no active session' error on shutdown.

SaveSession: return nil (no-op) when there are no agents,
rather than returning an error that gets logged.
2026-08-01 20:35:02 +02:00
Ollie Agent cb13f6523c fs/session: fix deadlock in agent/new via setAgentLogLocked
SetAgentLog acquires sess.mu, but CreateAgent in create.go
already holds sess.mu when calling it — causing a deadlock on
agent/new writes. Split into SetAgentLog (locks) and
setAgentLogLocked (no lock) so callers that already hold the
lock can use the locked variant directly.
2026-08-01 20:32:18 +02:00
Ollie Agent 951fdc8133 session: support multiple agents in data structures
- session.Session: r *agent.Agent → agents []*agent.Agent
  - Agent() returns agents[0] for backward compat
  - Added Agents(), AgentAt(), AgentCount(), AddAgent()
  - Close/SetEnv/SetSessionID iterate all agents
  - Other methods use Agent() (primary agent)
- fs/session: AgentLog *AgentLog → AgentLogs map[string]*AgentLog
  - Added AgentLog() (returns first) and SetAgentLog(id, al) methods
  - Updated create.go, persist.go, root.go, olliesrv/server.go
2026-08-01 20:29:23 +02:00
Ollie Agent 107a157a18 update kde submodule: no line trimming before tab split 2026-08-01 20:20:12 +02:00
Ollie Agent df15faf7bc update kde submodule: fix empty sessions dropped by trimmed() 2026-08-01 20:18:31 +02:00
Ollie Agent 456e68c389 update kde submodule: debug logging 2026-08-01 20:16:53 +02:00
Ollie Agent 5a32ce5577 update kde submodule: prevent sidebar focus stealing 2026-08-01 20:14:46 +02:00
Ollie Agent 85353d2aaf update kde submodule: use eventwait data, no timer 2026-08-01 20:13:59 +02:00
Ollie Agent 0ec7efada6 update kde submodule: robust session loading on startup 2026-08-01 20:10:25 +02:00
Ollie Agent 98d83965a4 update kde submodule: load sessions on startup, (no sessions) placeholder 2026-08-01 20:05:37 +02:00
Ollie Agent b038e82931 fix: rdwr on agent/new now returns agent name
The rdwr pattern needs per-fid state to work correctly (like session/new).
- Added agentNewResult field to fid struct
- Intercept writes to /session/{id}/agent/new and store result on fid
- Intercept reads from /session/{id}/agent/new and return stored result
- Added CreateAgentFromRoot public API
- Clearer error message when cwd is missing
2026-08-01 20:02:30 +02:00
Ollie Agent 6fe212a6fc fix: add name= field to agent/new template, support in CreateAgent
- Added name= as the first field in agent/new read template
- CreateAgent now accepts name= as alias for agent_alias=
- gofmt formatting fixes
2026-08-01 19:58:00 +02:00
Ollie Agent 8b795ad7e6 fix: allow access to agent/new on empty sessions
pathType in server.go blocked walks to agent/new when Core is nil.
Added early return for parts[2]=="new" before the nil-guard so the
agent creation file is always accessible.

Also made agent/new rdwr return the agent name instead of just "ok".
2026-08-01 19:56:14 +02:00
Ollie Agent a01338b850 update kde submodule for empty session gui support 2026-08-01 19:51:12 +02:00
Ollie Agent 94d11f9121 feat: support empty sessions (Core=nil) for multi-agent prep
Create() now registers a Session with Core=nil instead of just reserving
a name. All code paths that accessed sess.Core are nil-guarded:

- root.go: buildIndex, InterruptAll, rootOpen (proc read), rootDel (proc dismiss)
- persist.go: Kill, Rename, Shutdown, waitIdle, persistSession
- files.go: handleCtl save command
- create.go: Create() creates and registers the empty session

Also fixed pathType in server.go which panicked on empty sessions
(calling sess.Core.Agent().Name() when Core is nil). The panic was
caught by the recovery handler and returned as 'internal error'.

AGENTS.md: document cmd/ollie-remote test/build quirk
2026-08-01 19:47:02 +02:00
Ollie Agent daf535308f server: handle session/new write+read in server (rdwr pattern), bypass FileConfig 2026-08-01 19:31:39 +02:00
Ollie Agent 7e731d4624 update kde submodule: rdwr for session/new and agent/new 2026-08-01 18:50:28 +02:00
Ollie Agent 349d0fc470 server+kde: session/new and agent/new use rdwr pattern (write request, read response) 2026-08-01 18:50:10 +02:00
Ollie Agent 4bfbf70d45 fix: CreateAgent creates session (with Core) atomically, never registers nil Core in map 2026-08-01 18:46:06 +02:00
Ollie Agent c58a1d839b update kde submodule: two-step create (session/new then agent/new) 2026-08-01 18:41:23 +02:00
Ollie Agent 90ffca8622 server: split session/new (name only) from agent/new (cwd, backend, model, agent, alias, remote) 2026-08-01 18:41:10 +02:00
Ollie Agent 3144bcea3c update kde submodule: fix session/agent group split 2026-08-01 18:35:38 +02:00
Ollie Agent e32453ffa4 update kde submodule: group session/agent params in new session dialog 2026-08-01 18:33:11 +02:00