@@ -269,7 +269,7 @@ user sends another prompt ◄─────────────────
|
||||
├─► session_start { reason: "new" | "resume", previousSessionFile? }
|
||||
└─► resources_discover { reason: "startup" }
|
||||
|
||||
/fork
|
||||
/fork or /clone
|
||||
├─► session_before_fork (can cancel)
|
||||
├─► session_shutdown
|
||||
├─► session_start { reason: "fork", previousSessionFile }
|
||||
@@ -346,18 +346,19 @@ Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `
|
||||
|
||||
#### session_before_fork
|
||||
|
||||
Fired when forking via `/fork`.
|
||||
Fired when forking via `/fork` or cloning via `/clone`.
|
||||
|
||||
```typescript
|
||||
pi.on("session_before_fork", async (event, ctx) => {
|
||||
// event.entryId - ID of the entry being forked from
|
||||
return { cancel: true }; // Cancel fork
|
||||
// event.entryId - ID of the selected entry
|
||||
// event.position - "before" for /fork, "at" for /clone
|
||||
return { cancel: true }; // Cancel fork/clone
|
||||
// OR
|
||||
return { skipConversationRestore: true }; // Fork but don't rewind messages
|
||||
return { skipConversationRestore: true }; // Reserved for future conversation restore control
|
||||
});
|
||||
```
|
||||
|
||||
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`.
|
||||
After a successful fork or clone, 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
|
||||
@@ -909,7 +910,7 @@ if (result.cancelled) {
|
||||
}
|
||||
```
|
||||
|
||||
### ctx.fork(entryId)
|
||||
### ctx.fork(entryId, options?)
|
||||
|
||||
Fork from a specific entry, creating a new session file:
|
||||
|
||||
@@ -918,8 +919,17 @@ const result = await ctx.fork("entry-id-123");
|
||||
if (!result.cancelled) {
|
||||
// Now in the forked session
|
||||
}
|
||||
|
||||
const cloneResult = await ctx.fork("entry-id-456", { position: "at" });
|
||||
if (!cloneResult.cancelled) {
|
||||
// New session contains the active path through entry-id-456
|
||||
}
|
||||
```
|
||||
|
||||
Options:
|
||||
- `position`: `"before"` (default) forks before the selected user message, restoring that prompt into the editor
|
||||
- `position`: `"at"` duplicates the active path through the selected entry without restoring editor text
|
||||
|
||||
### ctx.navigateTree(targetId, options?)
|
||||
|
||||
Navigate to a different point in the session tree:
|
||||
|
||||
@@ -580,7 +580,7 @@ If an extension cancelled the switch:
|
||||
|
||||
#### fork
|
||||
|
||||
Create a new fork from a previous user message. Can be cancelled by a `session_before_fork` extension event handler. Returns the text of the message being forked from.
|
||||
Create a new fork from a previous user message on the active branch. Can be cancelled by a `session_before_fork` extension event handler. Returns the text of the message being forked from.
|
||||
|
||||
```json
|
||||
{"type": "fork", "entryId": "abc123"}
|
||||
@@ -606,6 +606,34 @@ If an extension cancelled the fork:
|
||||
}
|
||||
```
|
||||
|
||||
#### clone
|
||||
|
||||
Duplicate the current active branch into a new session at the current position. Can be cancelled by a `session_before_fork` extension event handler.
|
||||
|
||||
```json
|
||||
{"type": "clone"}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"type": "response",
|
||||
"command": "clone",
|
||||
"success": true,
|
||||
"data": {"cancelled": false}
|
||||
}
|
||||
```
|
||||
|
||||
If an extension cancelled the clone:
|
||||
```json
|
||||
{
|
||||
"type": "response",
|
||||
"command": "clone",
|
||||
"success": true,
|
||||
"data": {"cancelled": true}
|
||||
}
|
||||
```
|
||||
|
||||
#### get_fork_messages
|
||||
|
||||
Get user messages available for forking.
|
||||
|
||||
@@ -159,6 +159,7 @@ const runtime = await createAgentSessionRuntime(createRuntime, {
|
||||
- `newSession()`
|
||||
- `switchSession()`
|
||||
- `fork()`
|
||||
- clone flows via `fork(entryId, { position: "at" })`
|
||||
- `importFromJsonl()`
|
||||
|
||||
Important behavior:
|
||||
@@ -718,7 +719,7 @@ const { session: opened } = await createAgentSession({
|
||||
const currentProjectSessions = await SessionManager.list(process.cwd());
|
||||
const allSessions = await SessionManager.listAll(process.cwd());
|
||||
|
||||
// Session replacement API for /new, /resume, /fork, and import flows.
|
||||
// Session replacement API for /new, /resume, /fork, /clone, and import flows.
|
||||
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
||||
const services = await createAgentSessionServices({ cwd });
|
||||
return {
|
||||
@@ -744,8 +745,11 @@ await runtime.newSession();
|
||||
// Replace the active session with another saved session
|
||||
await runtime.switchSession("/path/to/session.jsonl");
|
||||
|
||||
// Replace the active session with a fork from a specific entry
|
||||
// Replace the active session with a fork from a specific user entry
|
||||
await runtime.fork("entry-id");
|
||||
|
||||
// Clone the active path through a specific entry
|
||||
await runtime.fork("entry-id", { position: "at" });
|
||||
```
|
||||
|
||||
**SessionManager tree API:**
|
||||
|
||||
@@ -191,7 +191,7 @@ First line of the file. Metadata only, not part of the tree (no `id`/`parentId`)
|
||||
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}
|
||||
```
|
||||
|
||||
For sessions with a parent (created via `/fork` or `newSession({ parentSession })`):
|
||||
For sessions with a parent (created via `/fork`, `/clone`, or `newSession({ parentSession })`):
|
||||
|
||||
```json
|
||||
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}
|
||||
|
||||
@@ -15,6 +15,8 @@ Sessions are stored as trees where each entry has an `id` and `parentId`. The "l
|
||||
| Summary | Never | Optional (user prompted) |
|
||||
| Events | `session_before_fork` / `session_start` (`reason: "fork"`) | `session_before_tree` / `session_tree` |
|
||||
|
||||
Use `/clone` when you want a new session file containing the full current active branch without rewinding to an earlier user message.
|
||||
|
||||
## Tree UI
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user