Skip to content

feat(agent): add monitor supervisor for long-running processes - #774

Open
lu-zero wants to merge 8 commits into
tontinton:mainfrom
lu-zero:feat/monitor-supervisor
Open

feat(agent): add monitor supervisor for long-running processes#774
lu-zero wants to merge 8 commits into
tontinton:mainfrom
lu-zero:feat/monitor-supervisor

Conversation

@lu-zero

@lu-zero lu-zero commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #799. Tony asked whether monitor can live as a standalone Lua plugin. It can, once those host hooks land: owner = "session" jobs, SessionEnd on every teardown path, and jobinfo/joblist/jobwait/jobstop that see exited session jobs.

This PR sits on #799 and replaces the native maki-agent::monitor supervisor with plugins/monitor/init.lua.

Contract

monitor(command)     start it, get log paths back
                     keep working

exit observation     mailbox message with exit, paths, short tail
                     TUI starts a turn if the session is idle

monitor_wait         only when you have nothing else to do
monitor_peek         look now, do not wait
read stdout/stderr   post-mortem, not cat-through-bash
monitor_stop         kill the process group (safe after exit)

Do not sleep or poll. notify_on_success defaults on; set it false to hear only about failures.

Commits on top of #799

  1. plugin — five tools (monitor, monitor_stop, monitor_list, monitor_peek, monitor_wait) on maki.fn.jobstart / jobinfo / joblist / jobwait / jobstop plus maki.fs.append. Same log layout as the native supervisor: logs_dir()/{session}/monitor-{id}/{stdout,stderr,meta}.
  2. sub-tools + calling session--allowed-tools accepts monitor_stop/list/peek/wait. Handlers take ctx:session_id() so headless calls do not round-trip through the UI.
  3. keep logs — SessionEnd no longer deletes the per-session log directory, so --print and /new can still collect stdout/stderr/meta.

Test plan

  • cargo fmt --all -- --check and stylua on plugins/monitor/
  • cargo clippy on maki-lua, maki-config, maki-agent (-D warnings)
  • cargo nextest run -p maki-lua -p maki-config -p maki-agent
  • just gen-docs-check

@lu-zero
lu-zero force-pushed the feat/monitor-supervisor branch 2 times, most recently from 6c237be to cd63cae Compare August 15, 2026 19:04
@lu-zero
lu-zero marked this pull request as ready for review August 15, 2026 19:09
@lu-zero

lu-zero commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

@tontinton this is a bit bigger than the minimal proof of concept I built initially but after using it all day I ended up with a richer tool. It seems working well enough, if you prefer I can try to slice it in smaller commits.

@tontinton

Copy link
Copy Markdown
Owner

Super cool, but any reason for the plugin to not remain yours, outside the repo?

@lu-zero

lu-zero commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

The plugin layer doesn't have all the hooks needed, if you prefer I can send a PR to expand the plugin surface, but I'd consider this kind of feature useful enough to be in the standard set.

@tontinton

Copy link
Copy Markdown
Owner

Yeah we should add the needed lua APIs instead, what's missing?

@lu-zero

lu-zero commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Few hooks to cleanup and some uniformity across the frontends, I'll prepare something later.

Shall we consider making somehow easier to find and install lua plugins though?

@tontinton

tontinton commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Yes for sure, we have #721 to track the issue, for now there's #452

@lu-zero
lu-zero force-pushed the feat/monitor-supervisor branch 5 times, most recently from 3567390 to f79c88d Compare August 24, 2026 06:41
@tontinton
tontinton force-pushed the main branch 3 times, most recently from 54f00ba to df0a069 Compare August 25, 2026 13:51
SessionReset is TUI-only (`/new`). Plugins that need to clean up when
a session goes away on tab delete, load, ACP replace, or headless
completion had no hook.

EventHandle::end_session fires SessionEnd with the session being
left behind. Wired on TUI reset/load/delete/shutdown, ACP session
replace/EOF, and headless completion.
Plugins can start jobs but could not inspect them: no pid, no
command, no trailing output unless they kept their own buffers.

jobinfo snapshots a job this plugin can see. joblist lists live
ones. jobstart tail=N keeps a ring of stdout/stderr lines for that
snapshot (default 20).

Task and plugin jobs still disappear on exit, so this is a live
view. After-exit peek and session filters can grow on top.
Plugin jobs die on /reload, so a monitor implemented only in Lua
cannot outlive its own code. owner = "session" keeps the process
until that session ends; the starting plugin can still inspect and
stop it after a reload.

notify posts a mailbox observation from the host, so exit notify
does not depend on a Lua on_exit callback. SessionEnd fires first
so handlers can inspect or stop those jobs, then the host reaps
them.

Folds the first review round: kill_job skips already-exited pids,
jobwait reports truncated=true for the captured tail, elapsed_secs
freezes at exit, and joblist includes exited session jobs.
A Lua monitor needs to grow a log from job and autocmd callbacks.
Those callbacks used to resume inline, so the first maki.fs await
died with a yield-across-C-call error.

Callbacks now run in their own coroutine under a detached task
scope, so they can suspend. SessionEnd wait is bounded on
process-exit paths so a suspending handler cannot hang quit.
maki.fs.append grows a file without replacing it, through
smol::unblock so writes stay ordered.

A failing job callback no longer aborts jobwait: the wait still
collects the exit code, matching the detached dispatch pump.

Also folds later review: jobforget, joblist without cloned tails,
imported MakiId, and the per-callback / delivery-scope pinning.
ui_roundtrip waited forever if the event loop had already stopped
draining, so a SessionEnd handler that touched winsaveview on quit
deadlocked the Lua thread against teardown. It now times out.

ACP called the blocking end_session wait on the smol executor, which
paused stdin for the whole grace period. Session replace and EOF now
poll end_session_async instead.

The maki.fn module example passed the job id to on_exit as if it were
the exit code.
The previous monitor was a native supervisor (maki-agent::monitor) with
its own registry, log-writing threads, and reap calls hand-wired into
every session-teardown path. feat/plugin-session-jobs built exactly the
host hooks that made the native version unnecessary: owner = "session"
jobs that survive plugin reload, a generic SessionEnd fired on every
teardown path already, and jobinfo/joblist/jobwait/jobstop that see
exited session jobs.

plugins/monitor/init.lua rebuilds the same five tools (monitor,
monitor_stop, monitor_list, monitor_peek, monitor_wait) entirely on
maki.fn.jobstart/jobinfo/joblist/jobwait/jobstop plus maki.fs.append,
writing the same logs_dir()/{session}/monitor-{id}/{stdout,stderr,meta}
layout the native version used. A SessionEnd autocmd removes the
session's log directory; process teardown is already handled by the
host's kill_session after SessionEnd dispatches.

Registered as a bundled plugin (maki-config, maki-lua/loader), added to
the generated tool docs (maki-docgen), and ported the long-running
commands concept page.
…t UI

--allowed-tools validated against DEFAULT_BUILTINS plus edit sub-tools, so
monitor_stop/list/peek/wait were rejected at CLI parse time. In headless
mode, parse_session fell back to maki.session.current(), a UI roundtrip
nobody drains outside the TUI; tool calls then failed with 'ui request
timed out'. Both now use the paths designed for this: the sub-tool name
list mirrors EDIT_SUB_TOOLS, and handlers take ctx:session_id() which
carries the calling session on every frontend.
The SessionEnd autocmd wiped the whole per-session logs dir, so headless
callers could never collect stdout/stderr/meta after --print returned,
and /new erased failure logs of finished runs in the TUI. Jobs still get
reaped by the host; the files stay.
@lu-zero
lu-zero force-pushed the feat/monitor-supervisor branch from f79c88d to c76416c Compare August 27, 2026 07:13
@lu-zero

lu-zero commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Restacked on current #799 (the five-commit session-jobs series on latest main).

The native supervisor is gone. What remains on top of #799:

  • plugins/monitor/init.lua on session-owned jobs
  • --allowed-tools accepts the monitor sub-tools, and handlers use ctx:session_id() so headless does not hang on a UI roundtrip
  • SessionEnd keeps the log files so --print / /new can still collect stdout/stderr/meta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants