refactor(coding-agent): add runtime host for session switching closes #2024

This commit is contained in:
Mario Zechner 2026-03-31 13:49:57 +02:00
parent a3bf1eb399
commit d86122cbd3
32 changed files with 1328 additions and 692 deletions

View file

@ -37,6 +37,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
- [Extension Styles](#extension-styles)
- [Events](#events)
- [Lifecycle Overview](#lifecycle-overview)
- [Resource Events](#resource-events)
- [Session Events](#session-events)
- [Agent Events](#agent-events)
- [Tool Events](#tool-events)
@ -228,7 +229,8 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
pi starts (CLI only)
├─► session_directory (CLI startup only, no ctx)
└─► session_start
├─► session_start { reason: "startup" }
└─► resources_discover { reason: "startup" }
user sends prompt ─────────────────────────────────────────┐
@ -261,11 +263,15 @@ user sends another prompt ◄─────────────────
/new (new session) or /resume (switch session)
├─► session_before_switch (can cancel)
└─► session_switch
├─► session_shutdown
├─► session_start { reason: "new" | "resume", previousSessionFile? }
└─► resources_discover { reason: "startup" }
/fork
├─► session_before_fork (can cancel)
└─► session_fork
├─► session_shutdown
├─► session_start { reason: "fork", previousSessionFile }
└─► resources_discover { reason: "startup" }
/compact or auto-compaction
├─► session_before_compact (can cancel or customize)
@ -282,6 +288,25 @@ exit (Ctrl+C, Ctrl+D)
└─► session_shutdown
```
### Resource Events
#### resources_discover
Fired after `session_start` so extensions can contribute additional skill, prompt, and theme paths.
The startup path uses `reason: "startup"`. Reload uses `reason: "reload"`.
```typescript
pi.on("resources_discover", async (event, _ctx) => {
// event.cwd - current working directory
// event.reason - "startup" | "reload"
return {
skillPaths: ["/path/to/skills"],
promptPaths: ["/path/to/prompts"],
themePaths: ["/path/to/themes"],
};
});
```
### Session Events
See [session.md](session.md) for session storage internals and the SessionManager API.
@ -309,17 +334,19 @@ pi.on("session_directory", async (event) => {
#### session_start
Fired on initial session load.
Fired when a session is started, loaded, or reloaded.
```typescript
pi.on("session_start", async (_event, ctx) => {
pi.on("session_start", async (event, ctx) => {
// event.reason - "startup" | "reload" | "new" | "resume" | "fork"
// event.previousSessionFile - present for "new", "resume", and "fork"
ctx.ui.notify(`Session: ${ctx.sessionManager.getSessionFile() ?? "ephemeral"}`, "info");
});
```
#### session_before_switch / session_switch
#### session_before_switch
Fired when starting a new session (`/new`) or switching sessions (`/resume`).
Fired before starting a new session (`/new`) or switching sessions (`/resume`).
```typescript
pi.on("session_before_switch", async (event, ctx) => {
@ -331,14 +358,12 @@ pi.on("session_before_switch", async (event, ctx) => {
if (!ok) return { cancel: true };
}
});
pi.on("session_switch", async (event, ctx) => {
// event.reason - "new" or "resume"
// event.previousSessionFile - session we came from
});
```
#### session_before_fork / session_fork
After a successful switch or new-session action, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "new" | "resume"` and `previousSessionFile`.
Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
#### session_before_fork
Fired when forking via `/fork`.
@ -349,12 +374,11 @@ pi.on("session_before_fork", async (event, ctx) => {
// OR
return { skipConversationRestore: true }; // Fork but don't rewind messages
});
pi.on("session_fork", async (event, ctx) => {
// event.previousSessionFile - previous session file
});
```
After a successful fork, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
#### session_before_compact / session_compact
Fired on compaction. See [compaction.md](compaction.md) for details.
@ -936,7 +960,7 @@ pi.registerCommand("reload-runtime", {
Important behavior:
- `await ctx.reload()` emits `session_shutdown` for the current extension runtime
- It then reloads resources and emits `session_start` (and `resources_discover` with reason `"reload"`) for the new runtime
- It then reloads resources and emits `session_start` with `reason: "reload"` and `resources_discover` with reason `"reload"`
- The currently running command handler still continues in the old call frame
- Code after `await ctx.reload()` still runs from the pre-reload version
- Code after `await ctx.reload()` must not assume old in-memory extension state is still valid
@ -2187,7 +2211,7 @@ All examples in [examples/extensions/](../examples/extensions/).
| **Compaction & Sessions** |||
| `custom-compaction.ts` | Custom compaction summary | `on("session_before_compact")` |
| `trigger-compact.ts` | Trigger compaction manually | `compact()` |
| `git-checkpoint.ts` | Git stash on turns | `on("turn_end")`, `on("session_fork")`, `exec` |
| `git-checkpoint.ts` | Git stash on turns | `on("turn_start")`, `on("session_before_fork")`, `exec` |
| `auto-commit-on-exit.ts` | Commit on shutdown | `on("session_shutdown")`, `exec` |
| **UI Components** |||
| `status-line.ts` | Footer status indicator | `setStatus`, session events |