@fleetagent/pi-coding-agent 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/core/agent-session.d.ts +5 -5
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +8 -11
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  7. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  8. package/dist/core/compaction/branch-summarization.js.map +1 -1
  9. package/dist/core/compaction/compaction.d.ts +2 -2
  10. package/dist/core/compaction/compaction.d.ts.map +1 -1
  11. package/dist/core/compaction/compaction.js +1 -1
  12. package/dist/core/compaction/compaction.js.map +1 -1
  13. package/dist/core/extensions/runner.d.ts +3 -3
  14. package/dist/core/extensions/runner.d.ts.map +1 -1
  15. package/dist/core/extensions/runner.js +5 -5
  16. package/dist/core/extensions/runner.js.map +1 -1
  17. package/dist/core/extensions/types.d.ts +15 -8
  18. package/dist/core/extensions/types.d.ts.map +1 -1
  19. package/dist/core/extensions/types.js.map +1 -1
  20. package/dist/core/footer-data-provider.d.ts +1 -1
  21. package/dist/core/footer-data-provider.d.ts.map +1 -1
  22. package/dist/core/footer-data-provider.js +1 -1
  23. package/dist/core/footer-data-provider.js.map +1 -1
  24. package/dist/core/pi-agent.d.ts +5 -3
  25. package/dist/core/pi-agent.d.ts.map +1 -1
  26. package/dist/core/pi-agent.js +64 -18
  27. package/dist/core/pi-agent.js.map +1 -1
  28. package/dist/core/session/in-memory-session-manager.d.ts.map +1 -1
  29. package/dist/core/session/in-memory-session-manager.js +5 -7
  30. package/dist/core/session/in-memory-session-manager.js.map +1 -1
  31. package/dist/core/session/in-memory-session.d.ts +3 -1
  32. package/dist/core/session/in-memory-session.d.ts.map +1 -1
  33. package/dist/core/session/in-memory-session.js +5 -2
  34. package/dist/core/session/in-memory-session.js.map +1 -1
  35. package/dist/core/session/index.d.ts +2 -2
  36. package/dist/core/session/index.d.ts.map +1 -1
  37. package/dist/core/session/index.js.map +1 -1
  38. package/dist/core/session/jsonl-helpers.d.ts.map +1 -1
  39. package/dist/core/session/jsonl-helpers.js +4 -4
  40. package/dist/core/session/jsonl-helpers.js.map +1 -1
  41. package/dist/core/session/local-session-manager.d.ts.map +1 -1
  42. package/dist/core/session/local-session-manager.js +12 -11
  43. package/dist/core/session/local-session-manager.js.map +1 -1
  44. package/dist/core/session/local-session.d.ts +3 -1
  45. package/dist/core/session/local-session.d.ts.map +1 -1
  46. package/dist/core/session/local-session.js +7 -2
  47. package/dist/core/session/local-session.js.map +1 -1
  48. package/dist/core/session/remote-session-client.d.ts +6 -1
  49. package/dist/core/session/remote-session-client.d.ts.map +1 -1
  50. package/dist/core/session/remote-session-client.js.map +1 -1
  51. package/dist/core/session/remote-session-manager.d.ts.map +1 -1
  52. package/dist/core/session/remote-session-manager.js +28 -7
  53. package/dist/core/session/remote-session-manager.js.map +1 -1
  54. package/dist/core/session/remote-session.d.ts +3 -0
  55. package/dist/core/session/remote-session.d.ts.map +1 -1
  56. package/dist/core/session/remote-session.js +4 -1
  57. package/dist/core/session/remote-session.js.map +1 -1
  58. package/dist/core/session/session.d.ts +9 -3
  59. package/dist/core/session/session.d.ts.map +1 -1
  60. package/dist/core/session/session.js +64 -10
  61. package/dist/core/session/session.js.map +1 -1
  62. package/dist/core/session/stores/in-memory-session-store.d.ts +6 -14
  63. package/dist/core/session/stores/in-memory-session-store.d.ts.map +1 -1
  64. package/dist/core/session/stores/in-memory-session-store.js +8 -34
  65. package/dist/core/session/stores/in-memory-session-store.js.map +1 -1
  66. package/dist/core/session/stores/jsonl-session-store.d.ts +14 -14
  67. package/dist/core/session/stores/jsonl-session-store.d.ts.map +1 -1
  68. package/dist/core/session/stores/jsonl-session-store.js +153 -162
  69. package/dist/core/session/stores/jsonl-session-store.js.map +1 -1
  70. package/dist/core/session/stores/remote-session-store.d.ts +4 -6
  71. package/dist/core/session/stores/remote-session-store.d.ts.map +1 -1
  72. package/dist/core/session/stores/remote-session-store.js +18 -30
  73. package/dist/core/session/stores/remote-session-store.js.map +1 -1
  74. package/dist/core/session/stores/session-store.d.ts +1 -15
  75. package/dist/core/session/stores/session-store.d.ts.map +1 -1
  76. package/dist/core/session/stores/session-store.js.map +1 -1
  77. package/dist/core/session-cwd.d.ts +2 -2
  78. package/dist/core/session-cwd.d.ts.map +1 -1
  79. package/dist/core/session-cwd.js +5 -5
  80. package/dist/core/session-cwd.js.map +1 -1
  81. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  82. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  83. package/dist/modes/interactive/interactive-mode.js +39 -37
  84. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  85. package/docs/extensions.md +35 -32
  86. package/docs/index.md +1 -1
  87. package/docs/sdk.md +2 -0
  88. package/docs/session-format.md +21 -21
  89. package/docs/sessions.md +2 -2
  90. package/docs/tui.md +1 -1
  91. package/examples/README.md +3 -0
  92. package/examples/extensions/README.md +1 -1
  93. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  94. package/examples/extensions/bookmark.ts +3 -3
  95. package/examples/extensions/confirm-destructive.ts +1 -1
  96. package/examples/extensions/custom-compaction.ts +1 -1
  97. package/examples/extensions/custom-footer.ts +2 -2
  98. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  99. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  100. package/examples/extensions/git-checkpoint.ts +1 -1
  101. package/examples/extensions/handoff.ts +2 -2
  102. package/examples/extensions/plan-mode/index.ts +1 -1
  103. package/examples/extensions/preset.ts +1 -1
  104. package/examples/extensions/qna.ts +1 -1
  105. package/examples/extensions/sandbox/package.json +1 -1
  106. package/examples/extensions/snake.ts +1 -1
  107. package/examples/extensions/space-invaders.ts +1 -1
  108. package/examples/extensions/summarize.ts +1 -1
  109. package/examples/extensions/tic-tac-toe.ts +1 -1
  110. package/examples/extensions/todo.ts +1 -1
  111. package/examples/extensions/tools.ts +1 -1
  112. package/examples/extensions/with-deps/package.json +1 -1
  113. package/examples/remote-session-server/README.md +66 -0
  114. package/examples/remote-session-server/server.ts +359 -0
  115. package/examples/sdk/11-sessions.ts +3 -3
  116. package/examples/sdk/13-session-runtime.ts +6 -6
  117. package/npm-shrinkwrap.json +12 -12
  118. package/package.json +4 -4
@@ -306,13 +306,13 @@ user sends another prompt ◄─────────────────
306
306
  /new (new session) or /resume (switch session)
307
307
  ├─► session_before_switch (can cancel)
308
308
  ├─► session_shutdown
309
- ├─► session_start { reason: "new" | "resume", previousSessionFile? }
309
+ ├─► session_start { reason: "new" | "resume", previousSessionReference? }
310
310
  └─► resources_discover { reason: "startup" }
311
311
 
312
312
  /fork or /clone
313
313
  ├─► session_before_fork (can cancel)
314
314
  ├─► session_shutdown
315
- ├─► session_start { reason: "fork", previousSessionFile }
315
+ ├─► session_start { reason: "fork", previousSessionReference }
316
316
  └─► resources_discover { reason: "startup" }
317
317
 
318
318
  /compact or auto-compaction
@@ -355,7 +355,7 @@ pi.on("resources_discover", async (event, _ctx) => {
355
355
 
356
356
  ### Session Events
357
357
 
358
- See [Session Format](session-format.md) for session storage internals and the SessionManager API.
358
+ See [Session Format](session-format.md) for session storage internals, active `Session` APIs, and `SessionManager` lifecycle APIs.
359
359
 
360
360
  #### session_start
361
361
 
@@ -364,8 +364,9 @@ Fired when a session is started, loaded, or reloaded.
364
364
  ```typescript
365
365
  pi.on("session_start", async (event, ctx) => {
366
366
  // event.reason - "startup" | "reload" | "new" | "resume" | "fork"
367
- // event.previousSessionFile - present for "new", "resume", and "fork"
368
- ctx.ui.notify(`Session: ${ctx.sessionManager.getSessionFile() ?? "ephemeral"}`, "info");
367
+ // event.previousSessionReference - present for "new", "resume", and "fork"
368
+ // event.previousSessionFile - deprecated alias for local-file-oriented compatibility
369
+ ctx.ui.notify(`Session: ${ctx.session.getSessionReference() ?? "ephemeral"}`, "info");
369
370
  });
370
371
  ```
371
372
 
@@ -376,7 +377,8 @@ Fired before starting a new session (`/new`) or switching sessions (`/resume`).
376
377
  ```typescript
377
378
  pi.on("session_before_switch", async (event, ctx) => {
378
379
  // event.reason - "new" or "resume"
379
- // event.targetSessionFile - session we're switching to (only for "resume")
380
+ // event.targetSessionReference - session we're switching to (only for "resume")
381
+ // event.targetSessionFile - deprecated alias for local-file-oriented compatibility
380
382
 
381
383
  if (event.reason === "new") {
382
384
  const ok = await ctx.ui.confirm("Clear?", "Delete all messages?");
@@ -385,7 +387,7 @@ pi.on("session_before_switch", async (event, ctx) => {
385
387
  });
386
388
  ```
387
389
 
388
- 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`.
390
+ 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 `previousSessionReference`.
389
391
  Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
390
392
 
391
393
  #### session_before_fork
@@ -402,7 +404,7 @@ pi.on("session_before_fork", async (event, ctx) => {
402
404
  });
403
405
  ```
404
406
 
405
- 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`.
407
+ 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 `previousSessionReference`.
406
408
  Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
407
409
 
408
410
  #### session_before_compact / session_compact
@@ -456,7 +458,8 @@ Fired before an extension runtime is torn down.
456
458
  ```typescript
457
459
  pi.on("session_shutdown", async (event, ctx) => {
458
460
  // event.reason - "quit" | "reload" | "new" | "resume" | "fork"
459
- // event.targetSessionFile - destination session for session replacement flows
461
+ // event.targetSessionReference - destination session for session replacement flows
462
+ // event.targetSessionFile - deprecated alias for local-file-oriented compatibility
460
463
  // Cleanup, save state, etc.
461
464
  });
462
465
  ```
@@ -675,9 +678,9 @@ Use this to update extension UI when `pi.setThinkingLevel()`, model changes, or
675
678
 
676
679
  Fired after `tool_execution_start`, before the tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
677
680
 
678
- Before `tool_call` runs, pi waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.sessionManager` is up to date through the current assistant tool-calling message.
681
+ Before `tool_call` runs, pi waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.session` is up to date through the current assistant tool-calling message.
679
682
 
680
- In the default parallel tool execution mode, sibling tool calls from the same assistant message are preflighted sequentially, then executed concurrently. `tool_call` is not guaranteed to see sibling tool results from that same assistant message in `ctx.sessionManager`.
683
+ In the default parallel tool execution mode, sibling tool calls from the same assistant message are preflighted sequentially, then executed concurrently. `tool_call` is not guaranteed to see sibling tool results from that same assistant message in `ctx.session`.
681
684
 
682
685
  `event.input` is mutable. Mutate it in place to patch tool arguments before execution.
683
686
 
@@ -865,16 +868,16 @@ UI methods for user interaction. See [Custom UI](#custom-ui) for full details.
865
868
 
866
869
  Current working directory.
867
870
 
868
- ### ctx.sessionManager
871
+ ### ctx.session
869
872
 
870
- Read-only access to session state. See [Session Format](session-format.md) for the full SessionManager API and entry types.
873
+ Read-only access to active session state. See [Session Format](session-format.md) for the full `Session` API and entry types.
871
874
 
872
875
  For `tool_call`, this state is synchronized through the current assistant message before handlers run. In parallel tool execution mode it is still not guaranteed to include sibling tool results from the same assistant message.
873
876
 
874
877
  ```typescript
875
- ctx.sessionManager.getEntries() // All entries
876
- ctx.sessionManager.getBranch() // Current branch
877
- ctx.sessionManager.getLeafId() // Current leaf entry ID
878
+ ctx.session.getEntries() // All entries
879
+ ctx.session.getBranch() // Current branch
880
+ ctx.session.getLeafId() // Current leaf entry ID
878
881
  ```
879
882
 
880
883
  ### ctx.modelRegistry / ctx.model
@@ -993,13 +996,13 @@ pi.registerCommand("my-cmd", {
993
996
  Create a new session:
994
997
 
995
998
  ```typescript
996
- const parentSession = ctx.sessionManager.getSessionFile();
999
+ const parentSession = ctx.session.getSessionReference();
997
1000
  const kickoff = "Continue in the replacement session";
998
1001
 
999
1002
  const result = await ctx.newSession({
1000
1003
  parentSession,
1001
- setup: async (sm) => {
1002
- sm.appendMessage({
1004
+ setup: async (session) => {
1005
+ session.appendMessage({
1003
1006
  role: "user",
1004
1007
  content: [{ type: "text", text: "Context from previous session..." }],
1005
1008
  timestamp: Date.now(),
@@ -1018,7 +1021,7 @@ if (result.cancelled) {
1018
1021
 
1019
1022
  Options:
1020
1023
  - `parentSession`: parent session file to record in the new session header
1021
- - `setup`: mutate the new session's `SessionManager` before `withSession` runs
1024
+ - `setup`: mutate the new active `Session` before `withSession` runs
1022
1025
  - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1023
1026
 
1024
1027
  ### ctx.fork(entryId, options?)
@@ -1084,19 +1087,19 @@ if (result.cancelled) {
1084
1087
  Options:
1085
1088
  - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1086
1089
 
1087
- To discover available sessions, use the static `LocalSessionManager.list()` or `LocalSessionManager.listAll()` methods:
1090
+ To discover available sessions, create a `LocalSessionManager` and call `list()` or `listAll()`:
1088
1091
 
1089
1092
  ```typescript
1090
- import { SessionManager } from "@fleetagent/pi-coding-agent";
1093
+ import { LocalSessionManager } from "@fleetagent/pi-coding-agent";
1091
1094
 
1092
1095
  pi.registerCommand("switch", {
1093
1096
  description: "Switch to another session",
1094
1097
  handler: async (args, ctx) => {
1095
- const sessions = await LocalSessionManager.list(ctx.cwd);
1098
+ const sessions = await new LocalSessionManager({ cwd: ctx.cwd }).list();
1096
1099
  if (sessions.length === 0) return;
1097
1100
  const choice = await ctx.ui.select(
1098
1101
  "Pick session:",
1099
- sessions.map(s => s.file),
1102
+ sessions.map(s => s.reference),
1100
1103
  );
1101
1104
  if (choice) {
1102
1105
  await ctx.switchSession(choice, {
@@ -1117,7 +1120,7 @@ Lifecycle and footguns:
1117
1120
  - `withSession` runs only after the old session has emitted `session_shutdown`, the old runtime has been torn down, the replacement session has been rebound, and the new extension instance has already received `session_start`.
1118
1121
  - The callback still executes in the original closure, not inside the new extension instance. That means your old extension instance may already have run its shutdown cleanup before `withSession` starts.
1119
1122
  - Captured old `pi` / old command `ctx` session-bound objects are stale after replacement and will throw if used. Use only the `ctx` passed to `withSession` for session-bound work.
1120
- - Previously extracted raw objects are still your responsibility. For example, if you capture `const sm = ctx.sessionManager` before replacement, `sm` is still the old `SessionManager` object. Do not reuse it after replacement.
1123
+ - Previously extracted raw objects are still your responsibility. For example, if you capture `const oldSession = ctx.session` before replacement, `oldSession` is still the old `Session` object. Do not reuse it after replacement.
1121
1124
  - Code in `withSession` should assume any state invalidated by your `session_shutdown` handler is already gone. Only capture plain data that survives shutdown cleanly, such as strings, ids, and serialized config.
1122
1125
 
1123
1126
  Safe pattern:
@@ -1140,11 +1143,11 @@ Unsafe pattern:
1140
1143
  ```typescript
1141
1144
  pi.registerCommand("handoff", {
1142
1145
  handler: async (_args, ctx) => {
1143
- const oldSessionManager = ctx.sessionManager;
1146
+ const oldSession = ctx.session;
1144
1147
  await ctx.newSession({
1145
1148
  withSession: async (_ctx) => {
1146
1149
  // stale old objects: do not do this
1147
- oldSessionManager.getSessionFile();
1150
+ oldSession.getSessionReference();
1148
1151
  pi.sendUserMessage("wrong");
1149
1152
  },
1150
1153
  });
@@ -1325,7 +1328,7 @@ pi.appendEntry("my-state", { count: 42 });
1325
1328
 
1326
1329
  // Restore on reload
1327
1330
  pi.on("session_start", async (_event, ctx) => {
1328
- for (const entry of ctx.sessionManager.getEntries()) {
1331
+ for (const entry of ctx.session.getEntries()) {
1329
1332
  if (entry.type === "custom" && entry.customType === "my-state") {
1330
1333
  // Reconstruct from entry.data
1331
1334
  }
@@ -1363,8 +1366,8 @@ pi.setLabel(entryId, "checkpoint-before-refactor");
1363
1366
  // Clear a label
1364
1367
  pi.setLabel(entryId, undefined);
1365
1368
 
1366
- // Read labels via sessionManager
1367
- const label = ctx.sessionManager.getLabel(entryId);
1369
+ // Read labels via the active session
1370
+ const label = ctx.session.getLabel(entryId);
1368
1371
  ```
1369
1372
 
1370
1373
  Labels persist in the session and survive restarts. Use them to mark important points (turns, checkpoints) in the conversation tree.
@@ -1379,7 +1382,7 @@ If multiple extensions register the same command name, pi keeps them all and ass
1379
1382
  pi.registerCommand("stats", {
1380
1383
  description: "Show session statistics",
1381
1384
  handler: async (args, ctx) => {
1382
- const count = ctx.sessionManager.getEntries().length;
1385
+ const count = ctx.session.getEntries().length;
1383
1386
  ctx.ui.notify(`${count} entries`, "info");
1384
1387
  }
1385
1388
  });
@@ -1634,7 +1637,7 @@ export default function (pi: ExtensionAPI) {
1634
1637
  // Reconstruct state from session
1635
1638
  pi.on("session_start", async (_event, ctx) => {
1636
1639
  items = [];
1637
- for (const entry of ctx.sessionManager.getBranch()) {
1640
+ for (const entry of ctx.session.getBranch()) {
1638
1641
  if (entry.type === "message" && entry.message.role === "toolResult") {
1639
1642
  if (entry.message.toolName === "my_tool") {
1640
1643
  items = entry.message.details?.items ?? [];
package/docs/index.md CHANGED
@@ -65,7 +65,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
65
65
 
66
66
  ## Reference
67
67
 
68
- - [Session format](session-format.md) - JSONL session file format, entry types, and SessionManager API.
68
+ - [Session format](session-format.md) - JSONL session file format, entry types, and session APIs.
69
69
 
70
70
  ## Platform setup
71
71
 
package/docs/sdk.md CHANGED
@@ -69,6 +69,8 @@ Use `pi.createAgentSession({ session })` when you already have an opened or crea
69
69
 
70
70
  `AgentSession` owns active conversation behavior: prompts, message history, model state, compaction, event streaming, and in-place tree navigation.
71
71
 
72
+ Use `session.sessionReference` when you need the backend-neutral active session reference. For local JSONL sessions this is the session file path; for in-memory sessions it is `undefined`. `session.sessionFile` remains available as a local-file-oriented alias.
73
+
72
74
  Session replacement APIs such as new-session, resume, fork, clone, and import live on `PiAgent`, not on `AgentSession`.
73
75
 
74
76
  ### SessionManager and Session
@@ -29,7 +29,7 @@ Existing sessions are automatically migrated to the current version (v3) when lo
29
29
  ## Source Files
30
30
 
31
31
  Source on GitHub ([pi-mono](https://github.com/earendil-works/pi-mono)):
32
- - [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/session-manager.ts) - Session entry types and SessionManager
32
+ - [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/session-manager.ts) - Session entry types, `Session`, and `SessionManager`
33
33
  - [`packages/coding-agent/src/core/messages.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/src/core/messages.ts) - Extended message types (BashExecutionMessage, CustomMessage, etc.)
34
34
  - [`packages/ai/src/types.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/ai/src/types.ts) - Base message types (UserMessage, AssistantMessage, ToolResultMessage)
35
35
  - [`packages/agent/src/types.ts`](https://github.com/earendil-works/pi-mono/blob/main/packages/agent/src/types.ts) - AgentMessage union type
@@ -358,27 +358,27 @@ for (const line of lines) {
358
358
  }
359
359
  ```
360
360
 
361
- ## SessionManager API
361
+ ## Programmatic APIs
362
362
 
363
- Key methods for working with sessions programmatically.
363
+ `SessionManager` handles lifecycle and discovery. It creates, opens, forks, imports, and lists `Session` objects. `Session` represents one active conversation tree and owns entry append/read/navigation operations.
364
364
 
365
- ### Static Creation Methods
366
- - `LocalSessionManager.create(cwd, sessionDir?)` - New session
367
- - `LocalSessionManager.openReference(path, sessionDir?)` - Open existing session file
368
- - `LocalSessionManager.continueRecent(cwd, sessionDir?)` - Continue most recent or create new
369
- - `InMemorySessionManager.create(cwd?)` - No file persistence
370
- - `LocalSessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)` - Fork session from another project
365
+ ### SessionManager Methods
366
+ - `create(options?)` - Create a new session
367
+ - `openReference(reference, options?)` - Open an existing session reference
368
+ - `continueRecent()` - Continue the most recent session or create a new one
369
+ - `forkFrom(reference)` - Fork from an existing session reference
370
+ - `forkSession(source, targetLeafId)` - Fork from an active `Session`
371
+ - `importJsonl(inputPath, options?)` - Import a JSONL session file
372
+ - `list(onProgress?)` - List sessions for the manager's scope
373
+ - `listAll(onProgress?)` - List all sessions across scopes supported by the backend
371
374
 
372
- ### Static Listing Methods
373
- - `LocalSessionManager.list(cwd, sessionDir?, onProgress?)` - List sessions for a directory
374
- - `LocalSessionManager.listAll(onProgress?)` - List all sessions across all projects
375
+ ### Session Methods - Lifecycle Helpers
376
+ - `createSubSession(options?)` - Create a child session via the session's private manager
377
+ - `forkSubSession(targetLeafId)` - Fork the active session via the session's private manager
378
+ - `copyBranchFrom(source, leafId, parentSession?)` - Copy one branch into this session
379
+ - `createBranchedSession(leafId)` - Extract branch to a new session reference; this mutates the current `Session` to point at the new reference
375
380
 
376
- ### Instance Methods - Session Management
377
- - `newSession(options?)` - Start a new session (options: `{ parentSession?: string }`)
378
- - `setSessionFile(path)` - Switch to a different session file
379
- - `createBranchedSession(leafId)` - Extract branch to new session file
380
-
381
- ### Instance Methods - Appending (all return entry ID)
381
+ ### Session Methods - Appending (all return entry ID)
382
382
  - `appendMessage(message)` - Add message
383
383
  - `appendThinkingLevelChange(level)` - Record thinking change
384
384
  - `appendModelChange(provider, modelId)` - Record model change
@@ -388,7 +388,7 @@ Key methods for working with sessions programmatically.
388
388
  - `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
389
389
  - `appendLabelChange(targetId, label)` - Set/clear label
390
390
 
391
- ### Instance Methods - Tree Navigation
391
+ ### Session Methods - Tree Navigation
392
392
  - `getLeafId()` - Current position
393
393
  - `getLeafEntry()` - Get current leaf entry
394
394
  - `getEntry(id)` - Get entry by ID
@@ -400,7 +400,7 @@ Key methods for working with sessions programmatically.
400
400
  - `resetLeaf()` - Reset leaf to null (before any entries)
401
401
  - `branchWithSummary(entryId, summary, details?, fromHook?)` - Branch with context summary
402
402
 
403
- ### Instance Methods - Context & Info
403
+ ### Session Methods - Context & Info
404
404
  - `buildSessionContext()` - Get messages, thinkingLevel, and model for LLM
405
405
  - `getEntries()` - All entries (excluding header)
406
406
  - `getHeader()` - Session header metadata
@@ -408,5 +408,5 @@ Key methods for working with sessions programmatically.
408
408
  - `getCwd()` - Working directory
409
409
  - `getSessionDir()` - Session storage directory
410
410
  - `getSessionId()` - Session UUID
411
- - `getSessionFile()` - Session file path (undefined for in-memory)
411
+ - `getSessionReference()` - Backend session reference (local file path for JSONL, remote reference for remote sessions, undefined for in-memory)
412
412
  - `isPersisted()` - Whether session is saved to disk
package/docs/sessions.md CHANGED
@@ -16,7 +16,7 @@ pi --fork <path|id> # Fork a session file or partial session ID into a new se
16
16
 
17
17
  Use `/session` in interactive mode to see the current session file, session ID, message count, tokens, and cost.
18
18
 
19
- For the JSONL file format and SessionManager API, see [Session Format](session-format.md).
19
+ For the JSONL file format, active `Session` API, and `SessionManager` lifecycle API, see [Session Format](session-format.md).
20
20
 
21
21
  ## Session Commands
22
22
 
@@ -134,4 +134,4 @@ See [Compaction](compaction.md) for branch summarization internals and extension
134
134
 
135
135
  Session files are JSONL and contain message entries, model changes, thinking-level changes, labels, compactions, branch summaries, and extension entries.
136
136
 
137
- For parsers, extensions, SDK usage, and the full SessionManager API, see [Session Format](session-format.md).
137
+ For parsers, extensions, SDK usage, and the full session APIs, see [Session Format](session-format.md).
package/docs/tui.md CHANGED
@@ -813,7 +813,7 @@ ctx.ui.setFooter((tui, theme, footerData) => ({
813
813
  ctx.ui.setFooter(undefined); // restore default
814
814
  ```
815
815
 
816
- Token stats available via `ctx.sessionManager.getBranch()` and `ctx.model`.
816
+ Token stats available via `ctx.session.getBranch()` and `ctx.model`.
817
817
 
818
818
  **Examples:** [custom-footer.ts](../examples/extensions/custom-footer.ts)
819
819
 
@@ -18,6 +18,9 @@ Example extensions demonstrating:
18
18
  - External integrations (SSH, file watchers, system theme sync)
19
19
  - Custom providers (Anthropic with custom streaming, GitLab Duo)
20
20
 
21
+ ### [remote-session-server/](remote-session-server/)
22
+ Hono-based remote session storage server. It uses static bearer auth and stores sessions as JSONL files for debugging the remote session protocol end-to-end.
23
+
21
24
  ## Documentation
22
25
 
23
26
  - [SDK Reference](sdk/README.md)
@@ -198,7 +198,7 @@ return {
198
198
 
199
199
  // Reconstruct on session events
200
200
  pi.on("session_start", async (_event, ctx) => {
201
- for (const entry of ctx.sessionManager.getBranch()) {
201
+ for (const entry of ctx.session.getBranch()) {
202
202
  if (entry.type === "message" && entry.message.toolName === "my_tool") {
203
203
  const details = entry.message.details;
204
204
  // Reconstruct state from details
@@ -18,7 +18,7 @@ export default function (pi: ExtensionAPI) {
18
18
  }
19
19
 
20
20
  // Find the last assistant message for commit context
21
- const entries = ctx.sessionManager.getEntries();
21
+ const entries = ctx.session.getEntries();
22
22
  let lastAssistantText = "";
23
23
  for (let i = entries.length - 1; i >= 0; i--) {
24
24
  const entry = entries[i];
@@ -16,7 +16,7 @@ export default function (pi: ExtensionAPI) {
16
16
  const label = args.trim() || `bookmark-${Date.now()}`;
17
17
 
18
18
  // Find the last assistant message entry
19
- const entries = ctx.sessionManager.getEntries();
19
+ const entries = ctx.session.getEntries();
20
20
  for (let i = entries.length - 1; i >= 0; i--) {
21
21
  const entry = entries[i];
22
22
  if (entry.type === "message" && entry.message.role === "assistant") {
@@ -34,10 +34,10 @@ export default function (pi: ExtensionAPI) {
34
34
  pi.registerCommand("unbookmark", {
35
35
  description: "Remove bookmark from last labeled entry",
36
36
  handler: async (_args, ctx) => {
37
- const entries = ctx.sessionManager.getEntries();
37
+ const entries = ctx.session.getEntries();
38
38
  for (let i = entries.length - 1; i >= 0; i--) {
39
39
  const entry = entries[i];
40
- const label = ctx.sessionManager.getLabel(entry.id);
40
+ const label = ctx.session.getLabel(entry.id);
41
41
  if (label) {
42
42
  pi.setLabel(entry.id, undefined);
43
43
  ctx.ui.notify(`Removed bookmark: ${label}`, "info");
@@ -25,7 +25,7 @@ export default function (pi: ExtensionAPI) {
25
25
  }
26
26
 
27
27
  // reason === "resume" - check if there are unsaved changes (messages since last assistant response)
28
- const entries = ctx.sessionManager.getEntries();
28
+ const entries = ctx.session.getEntries();
29
29
  const hasUnsavedWork = entries.some(
30
30
  (e): e is SessionMessageEntry => e.type === "message" && e.message.role === "user",
31
31
  );
@@ -108,7 +108,7 @@ ${conversationText}
108
108
  return;
109
109
  }
110
110
 
111
- // Return compaction content - SessionManager adds id/parentId
111
+ // Return compaction content - Session adds id/parentId
112
112
  // Use firstKeptEntryId from preparation to keep recent messages
113
113
  return {
114
114
  compaction: {
@@ -5,7 +5,7 @@
5
5
  * - getGitBranch(): current git branch
6
6
  * - getExtensionStatuses(): texts from ctx.ui.setStatus()
7
7
  *
8
- * Token stats come from ctx.sessionManager/ctx.model (already accessible).
8
+ * Token stats come from ctx.session/ctx.model (already accessible).
9
9
  */
10
10
 
11
11
  import type { AssistantMessage } from "@fleetagent/pi-ai";
@@ -32,7 +32,7 @@ export default function (pi: ExtensionAPI) {
32
32
  let input = 0,
33
33
  output = 0,
34
34
  cost = 0;
35
- for (const e of ctx.sessionManager.getBranch()) {
35
+ for (const e of ctx.session.getBranch()) {
36
36
  if (e.type === "message" && e.message.role === "assistant") {
37
37
  const m = e.message as AssistantMessage;
38
38
  input += m.usage.input;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -13,7 +13,7 @@ export default function (pi: ExtensionAPI) {
13
13
 
14
14
  // Track the current entry ID when user messages are saved
15
15
  pi.on("tool_result", async (_event, ctx) => {
16
- const leaf = ctx.sessionManager.getLeafEntry();
16
+ const leaf = ctx.session.getLeafEntry();
17
17
  if (leaf) currentEntryId = leaf.id;
18
18
  });
19
19
 
@@ -99,7 +99,7 @@ export default function (pi: ExtensionAPI) {
99
99
 
100
100
  // Gather conversation context from current branch. If the branch was compacted,
101
101
  // include the compaction summary plus entries from firstKeptEntryId onward.
102
- const messages = getHandoffMessages(ctx.sessionManager.getBranch());
102
+ const messages = getHandoffMessages(ctx.session.getBranch());
103
103
 
104
104
  if (messages.length === 0) {
105
105
  ctx.ui.notify("No conversation to hand off", "error");
@@ -109,7 +109,7 @@ export default function (pi: ExtensionAPI) {
109
109
  // Convert to LLM format and serialize
110
110
  const llmMessages = convertToLlm(messages);
111
111
  const conversationText = serializeConversation(llmMessages);
112
- const currentSessionFile = ctx.sessionManager.getSessionReference();
112
+ const currentSessionFile = ctx.session.getSessionReference();
113
113
 
114
114
  // Generate the handoff prompt with loader UI
115
115
  const result = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
@@ -293,7 +293,7 @@ After completing a step, include a [DONE:n] tag in your response.`,
293
293
  planModeEnabled = true;
294
294
  }
295
295
 
296
- const entries = ctx.sessionManager.getEntries();
296
+ const entries = ctx.session.getEntries();
297
297
 
298
298
  // Restore persisted state
299
299
  const planModeEntry = entries
@@ -404,7 +404,7 @@ export default function presetExtension(pi: ExtensionAPI) {
404
404
  }
405
405
 
406
406
  // Restore preset from session state
407
- const entries = ctx.sessionManager.getEntries();
407
+ const entries = ctx.session.getEntries();
408
408
  const presetEntry = entries
409
409
  .filter((e: { type: string; customType?: string }) => e.type === "custom" && e.customType === "preset-state")
410
410
  .pop() as { data?: { name: string } } | undefined;
@@ -42,7 +42,7 @@ export default function (pi: ExtensionAPI) {
42
42
  }
43
43
 
44
44
  // Find the last assistant message on the current branch
45
- const branch = ctx.sessionManager.getBranch();
45
+ const branch = ctx.session.getBranch();
46
46
  let lastAssistantText: string | undefined;
47
47
 
48
48
  for (let i = branch.length - 1; i >= 0; i--) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -317,7 +317,7 @@ export default function (pi: ExtensionAPI) {
317
317
  }
318
318
 
319
319
  // Load saved state from session
320
- const entries = ctx.sessionManager.getEntries();
320
+ const entries = ctx.session.getEntries();
321
321
  let savedState: GameState | undefined;
322
322
  for (let i = entries.length - 1; i >= 0; i--) {
323
323
  const entry = entries[i];
@@ -535,7 +535,7 @@ export default function (pi: ExtensionAPI) {
535
535
  }
536
536
 
537
537
  // Load saved state from session
538
- const entries = ctx.sessionManager.getEntries();
538
+ const entries = ctx.session.getEntries();
539
539
  let savedState: GameState | undefined;
540
540
  for (let i = entries.length - 1; i >= 0; i--) {
541
541
  const entry = entries[i];
@@ -146,7 +146,7 @@ export default function (pi: ExtensionAPI) {
146
146
  pi.registerCommand("summarize", {
147
147
  description: "Summarize the current conversation in a custom UI",
148
148
  handler: async (_args, ctx) => {
149
- const branch = ctx.sessionManager.getBranch();
149
+ const branch = ctx.session.getBranch();
150
150
  const conversationText = buildConversationText(branch);
151
151
 
152
152
  if (!conversationText.trim()) {
@@ -628,7 +628,7 @@ function reconstructState(ctx: ExtensionContext): void {
628
628
  gameState = createInitialState();
629
629
  gameActive = false;
630
630
 
631
- for (const entry of ctx.sessionManager.getBranch()) {
631
+ for (const entry of ctx.session.getBranch()) {
632
632
  if (entry.type !== "message") continue;
633
633
  const msg = entry.message;
634
634
  if (msg.role !== "toolResult") continue;
@@ -115,7 +115,7 @@ export default function (pi: ExtensionAPI) {
115
115
  todos = [];
116
116
  nextId = 1;
117
117
 
118
- for (const entry of ctx.sessionManager.getBranch()) {
118
+ for (const entry of ctx.session.getBranch()) {
119
119
  if (entry.type !== "message") continue;
120
120
  const msg = entry.message;
121
121
  if (msg.role !== "toolResult" || msg.toolName !== "todo") continue;
@@ -40,7 +40,7 @@ export default function toolsExtension(pi: ExtensionAPI) {
40
40
  allTools = pi.getAllTools();
41
41
 
42
42
  // Get entries in current branch only
43
- const branchEntries = ctx.sessionManager.getBranch();
43
+ const branchEntries = ctx.session.getBranch();
44
44
  let savedTools: string[] | undefined;
45
45
 
46
46
  for (const entry of branchEntries) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",