When a long-running subagent finishes (success/fail/abort), the main agent should:
- Notify the user in the interactive session when idle (push-style, no polling), and
- Persist a concise, searchable record even if the interactive session is disconnected/terminated.
Status (PR #323): PR #323 introduces pi.events so hooks can notify the user while the agent is idle. It also mentions PI_ASYNC_RESULT for writing an async result artifact. This proposal adds a durable session-history record by appending subagent lifecycle events to the existing session .jsonl via SessionManager.
PR #323 already enables (1) via pi.events + hooks. This adds (2) using the existing SessionManager JSONL session file.
We should append lifecycle entries to the same session .jsonl file managed by SessionManager:
- Keeps a single source of truth for “what happened in this session”
- Avoids introducing a second log file to manage/rotate/sync
- Remains searchable via
jq,rg, etc. - Prevents log corruption by keeping a single-writer model (the main process writes session files)
- Main agent starts async subagent/chain
- A lifecycle event is recorded to the session JSONL (
subagent:start) - Subagent runs in background
- On completion, record
subagent:completewith timing + usage + model + requestedBy - Hook listening for
subagent:completecallspi.send(...)to notify user while idle
Add a new SessionEntry variant, e.g. type: "agent_event" (name flexible), appended as JSONL lines.
type:"agent_event"timestamp: ISO8601 string (when the entry is written)eventType:"subagent:start"|"subagent:complete"|"subagent:error"|"subagent:aborted"jobId: string (correlates start/complete)requestedBy: string (use current user login when available, e.g.grahama1970; otherwise"assistant")
startedAt: ISO8601 string (on start; may also be repeated on complete)completedAt: ISO8601 string (on complete/error/aborted)durationMs: number (on complete/error/aborted)
model: string (provider/model id; whichever representation is already used by the subagent result)usage: object (token counts and cost, if available from the subagent run) usage should reuse the existing usage shape already produced by pi-agent (don’t invent a new token schema).
agentName: string (e.g."scout","planner","worker")pid: number (subprocess pid if applicable)mode:"single"|"parallel"|"chain"
status:"completed"|"failed"|"aborted"summary: short text suitable for display + log search
{
"type": "agent_event",
"timestamp": "2025-12-27T12:00:00.000Z",
"eventType": "subagent:start",
"jobId": "job_01...",
"requestedBy": "grahama1970",
"agentName": "worker",
"mode": "chain",
"pid": 12345,
"startedAt": "2025-12-27T12:00:00.000Z"
}{
"type": "agent_event",
"timestamp": "2025-12-27T12:00:12.345Z",
"eventType": "subagent:complete",
"jobId": "job_01...",
"requestedBy": "grahama1970",
"agentName": "worker",
"mode": "chain",
"pid": 12345,
"startedAt": "2025-12-27T12:00:00.000Z",
"completedAt": "2025-12-27T12:00:12.345Z",
"durationMs": 12345,
"model": "anthropic/claude-sonnet-4-5",
"usage": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0, "cost": { "total": 0 } },
"summary": "Subagent finished: <short summary>"
}- Only log lifecycle events (start/complete/error/aborted). Do not log progress ticks into the session file.
- Keep payloads small. If a subagent produces large output, store it elsewhere (e.g. subagent session file / artifact) and log only a pointer + summary.
- User notification remains handled by hooks (pi.events.on("subagent:complete", ...) -> pi.send(...)).