@gotgenes/pi-session-tools3.0.1
Pi extension providing session metadata tools (naming, context) for multi-session workflows
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@gotgenes/pi-session-tools
Pi extension providing session metadata tools for multi-session workflows.
Tools
set_session_name
Set the current session's display name (shown in the session selector).
set_session_name({ name: "#42 Planning — Extract ExtensionPaths" })
Use a stage-encoded format to identify both the issue and workflow stage:
| Stage | Format |
|---|---|
| Planning | #N Planning — <title> |
| TDD | #N TDD — <title> |
| Build | #N Build — <title> |
| Retrospective | #N Retrospective — <title> |
get_session_name
Get the current session's display name, if one has been set.
get_session_name({})
read_session
Read the current session's entries as a structured transcript. Useful for retro lenses and cross-session context.
read_session({ types?: string[], offset?: number, limit?: number, elide_user_text?: boolean, branches?: "live" | "all" })
Parameters:
types— filter to specific entry types (e.g.["message", "compaction"]). Omit for all.offset— skip the most recent N entries after filtering, beforelimitapplies. Defaults to 0.limit— return only the most recent N entries after filtering andoffset.0returns none; a negative value is clamped to0.elide_user_text— replace each user turn's body with a[text elided: N chars]placeholder. Defaults tofalse.branches— which branches of the session's entry tree to render. Defaults to"live"; any value other than"all"is read as"live".
The output is a human-readable transcript: numbered user/assistant turns, one-line tool call summaries with correlated result status, and metadata events (compaction, model changes, session renames, context edits, system prompt changes).
Tool result bodies, thinking content, and image data are omitted.
A [model change] line renders only when the switch actually took effect — a marker followed by an assistant turn before the next switch or the end of entries.
A phantom switch (e.g. cycling the TUI model picker with no turn run after it) is omitted from both the transcript and the model changes count, and does not consume a slot against limit.
A [session] line marks each point where the session was renamed, which is how a multi-stage session's stage boundaries stay visible even when user bodies are elided.
A [context edit] line marks where an earlier message was omitted from (or replaced in) the model's context, naming it in transcript terms, as in [context edit] turn 2 (assistant) omitted from context or bash result from turn 2.
Turns between the target and the edit still saw the original message.
When the target falls outside the rendered entries (because of types, offset, or limit), the line names it by entry id instead.
A [system] line marks each system message: the first one in the transcript is the prompt and renders as counts ([system] prompt: 8 sections, 21 tools), and later ones name the sections and tools they change.
Rewound sessions
A session file is a tree, not a list: every entry records its parent, and rewinding the conversation makes the next entry a second child of an earlier one. The transcript follows the live path — the walk from the session's newest entry back to the root, which is the same path Pi resumes into — and replaces each stretch it leaves out with a marker:
[abandoned branch] 89 entries omitted (branches: "all" to include)
Pass branches: "all" to render those entries too, bracketed so they stay distinguishable from the live path:
[abandoned branch begins] 89 entries
…the abandoned turns…
[abandoned branch ends]
A marker survives an explicit types filter, so a filtered call can never hide the fact that a branch was dropped.
It does occupy a slot against offset and limit, like any other rendered entry.
The summary counts session entries only, so a live-path render reports the turns that are actually on it.
A session with no rewind renders identically either way, and an entry array with no parent links — a pre-v3 file — is left exactly as it is read.
Reading a long session without re-reading its tail
limit alone bounds a transcript only from the end, so reaching an early turn means asking for a larger window and re-rendering everything already read.
offset and elide_user_text address that from opposite directions, and compose:
offsetpages backward —{ limit: 40 }, then{ offset: 40, limit: 40 }, and so on. Each call renders only the entries it adds.elide_user_textshrinks every turn instead of dropping entries. On a measured 246-entry session, user bodies were 46.7% of the rendered transcript, and the elided render of the whole session cost less than three partial unelided calls did.
Neither is a filter: both leave phantom-switch suppression and the [provider/model] label on every assistant turn intact, so an attribution pass can use them freely.
Branch resolution is a filter — it is the one knob that changes which turns an attribution pass sees, which is the point of following the live path by default.
In the TUI the tool row shows a compact summary by default (e.g. ✓ 42 entries — 38 messages, 18 tool calls, 2 compactions).
Press Ctrl-O to expand to the full transcript.
The model always receives the full transcript regardless of the TUI state.
1. user
How do I fix the login bug?
---
2. assistant [anthropic/claude-sonnet-4-20250514]
Let me check the auth flow.
[tool] Read — path: src/auth/login.ts → completed
[tool] Bash — command: pnpm vitest login → error
The test is failing because...
---
[compaction] Context compacted (48000 tokens before)
---
[model change] → anthropic/claude-opus-4-20250514
---
[session] → #42 TDD — Fix the login bug
---
3. assistant [anthropic/claude-opus-4-20250514]
Looking at the recent commits...
read_parent_session
Read the parent session's entries as a structured transcript when running inside a subagent. Derives the parent session file from the subagent directory layout. Returns an error if not running in a subagent context.
read_parent_session({ types?: string[], offset?: number, limit?: number, elide_user_text?: boolean, branches?: "live" | "all" })
Parameters and output format are the same as read_session.
read_session_file
Read an arbitrary session file as a structured transcript, given its path.
Useful for reading a sibling session that neither read_session (current session only) nor read_parent_session (parent-via-subagent only) can reach — for example, a peer worktree session in the parallel-worktree ship flow.
read_session_file({ path: string, types?: string[], offset?: number, limit?: number, elide_user_text?: boolean, branches?: "live" | "all" })
Parameters:
path— absolute path to a session.jsonlfile.types/offset/limit/elide_user_text— same asread_session.
Output format is the same as read_session.
Returns a status message (not an error) when the file does not exist.
list_session_files
List a working directory's session files, newest first.
Encodes the given cwd to Pi's session-directory naming convention (--<cwd with slashes replaced by dashes>-- under the sessions root) and lists the .jsonl files found there, so a caller does not have to hand-roll the encoding.
Pass a listed path to read_session_file to render it as a transcript.
list_session_files({ cwd: string, limit?: number })
Parameters:
cwd— the working directory whose session files to list (e.g. a peer worktree path). Required — there is no default, since the sibling-session use case always targets a directory other than the current session's own.limit— maximum number of paths to list, newest first. Defaults to 10. Pass a large number (e.g.1000) to list every file; there is no sentinel for "all", and0lists none.
Session directory: /Users/chris/.pi/agent/sessions/--Users-chris-worktrees-issue-546--
2 session files, newest first:
/Users/chris/.pi/agent/sessions/--Users-chris-worktrees-issue-546--/2026-07-06T10-00-00Z_.jsonl
/Users/chris/.pi/agent/sessions/--Users-chris-worktrees-issue-546--/2026-07-05T09-00-00Z_.jsonl
The count line always reports the directory's true total, and names how many paths follow when the listing is bounded:
Session directory: /Users/chris/.pi/agent/sessions/--Users-chris-pi-packages--
608 session files, newest first (showing 10):
…ten paths…
list_subagent_sessions
List a session's subagent transcripts, newest first.
Pi stores each subagent session beneath the parent session file's basename (<session>/tasks/*.jsonl), so list_session_files — which reads one directory and does not recurse — never reports them.
Pass a listed path to read_session_file to render it.
list_subagent_sessions({ path: string, limit?: number })
Parameters:
path— absolute path to the session.jsonlfile whose subagent transcripts to list. Required — there is no default, so a mistyped path can never silently answer about the current session.limit— maximum number of paths to list, newest first. Defaults to 10, with the same large-number escape hatch aslist_session_files.
Session directory: /Users/chris/.pi/agent/sessions/--project--/2026-09-06T04-26-34-471Z_01a074f7/tasks
2 session files, newest first:
/Users/chris/.pi/agent/sessions/--project--/2026-09-06T04-26-34-471Z_01a074f7/tasks/2026-09-06T17-26-06-793Z_01a077c1.jsonl
/Users/chris/.pi/agent/sessions/--project--/2026-09-06T04-26-34-471Z_01a074f7/tasks/2026-09-06T10-02-11-004Z_01a07612.jsonl
The listing reports one generation, mirroring read_parent_session's single step up — call the tool again on a returned path to reach a subagent's own subagents.
A session that spawned none renders No session files found. under the directory that would hold them; a path with no session file behind it returns a Session file not found: status instead, so the two cases stay distinguishable.
Install
pi install npm:@gotgenes/pi-session-tools
Or add it to your Pi settings (.pi/settings.json):
{
"packages": ["npm:@gotgenes/pi-session-tools"]
}
Scope and non-goals
Purpose. A multi-session workflow needs two things Pi does not otherwise expose to an extension: a way to label the current session, and a way to read a session's transcript — including a parent's or a sibling's.
In scope. Rendering a transcript more usefully for whoever reads it, and reaching a session the existing tools cannot reach. A new capability arrives as a new tool rather than as another parameter on an existing one.
Non-goals.
- Mutating transcript content. Session metadata is writable; transcript entries are read-only — no editing, redacting, replaying, or pruning.
- A raw or JSON passthrough mode.
Anything needing unrendered entries can read the session
.jsonldirectly. - Owning Pi's session storage format. The directory encoding and entry schema belong to Pi; this package matches them rather than improving them.
- Path allowlisting for
read_session_file. The agent already hasReadandBashon any file, so restricting one tool adds friction without a security boundary. - Analytics and roll-ups. Per-turn attribution and flat counts are emitted; interpreting them is the calling prompt's job.
License
MIT