@oh-my-pi/pi-coding-agent 12.7.6 → 12.8.1
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.
- package/CHANGELOG.md +37 -37
- package/README.md +9 -1052
- package/package.json +7 -7
- package/src/cli/args.ts +1 -0
- package/src/cli/update-cli.ts +49 -35
- package/src/cli/web-search-cli.ts +3 -2
- package/src/commands/web-search.ts +1 -0
- package/src/config/model-registry.ts +6 -0
- package/src/config/settings-schema.ts +25 -3
- package/src/config/settings.ts +1 -0
- package/src/extensibility/extensions/wrapper.ts +20 -13
- package/src/extensibility/slash-commands.ts +12 -91
- package/src/lsp/client.ts +24 -27
- package/src/lsp/index.ts +92 -42
- package/src/mcp/config-writer.ts +33 -0
- package/src/mcp/config.ts +6 -1
- package/src/mcp/types.ts +1 -0
- package/src/modes/components/custom-editor.ts +8 -5
- package/src/modes/components/settings-defs.ts +2 -1
- package/src/modes/controllers/command-controller.ts +12 -6
- package/src/modes/controllers/input-controller.ts +21 -186
- package/src/modes/controllers/mcp-command-controller.ts +60 -3
- package/src/modes/interactive-mode.ts +2 -2
- package/src/modes/types.ts +1 -1
- package/src/sdk.ts +23 -1
- package/src/secrets/index.ts +116 -0
- package/src/secrets/obfuscator.ts +269 -0
- package/src/secrets/regex.ts +21 -0
- package/src/session/agent-session.ts +138 -21
- package/src/session/compaction/branch-summarization.ts +2 -2
- package/src/session/compaction/compaction.ts +10 -3
- package/src/session/compaction/utils.ts +25 -1
- package/src/slash-commands/builtin-registry.ts +419 -0
- package/src/web/scrapers/github.ts +50 -12
- package/src/web/search/index.ts +5 -5
- package/src/web/search/provider.ts +13 -2
- package/src/web/search/providers/brave.ts +165 -0
- package/src/web/search/types.ts +1 -1
- package/docs/compaction.md +0 -436
- package/docs/config-usage.md +0 -176
- package/docs/custom-tools.md +0 -585
- package/docs/environment-variables.md +0 -257
- package/docs/extension-loading.md +0 -106
- package/docs/extensions.md +0 -1342
- package/docs/fs-scan-cache-architecture.md +0 -50
- package/docs/hooks.md +0 -906
- package/docs/models.md +0 -234
- package/docs/python-repl.md +0 -110
- package/docs/rpc.md +0 -1173
- package/docs/sdk.md +0 -1039
- package/docs/session-tree-plan.md +0 -84
- package/docs/session.md +0 -368
- package/docs/skills.md +0 -254
- package/docs/theme.md +0 -696
- package/docs/tree.md +0 -206
- package/docs/tui.md +0 -487
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# Session Tree Architecture (Current)
|
|
2
|
-
|
|
3
|
-
Reference: [session.md](./session.md), [tree.md](./tree.md)
|
|
4
|
-
|
|
5
|
-
This document summarizes the current session tree implementation and extension touchpoints. It replaces the historical rollout checklist.
|
|
6
|
-
|
|
7
|
-
## Session file format (v3)
|
|
8
|
-
|
|
9
|
-
- JSONL file with a SessionHeader (version 3). Header is metadata only and does not participate in the tree.
|
|
10
|
-
- Every SessionEntry derives from SessionEntryBase: `id`, `parentId`, `timestamp`.
|
|
11
|
-
- Entries are append-only; branching only moves the leaf pointer.
|
|
12
|
-
- Entry types: `message`, `compaction`, `branch_summary`, `custom`, `custom_message`, `label`, `model_change`, `thinking_level_change`, `ttsr_injection`, `session_init`.
|
|
13
|
-
|
|
14
|
-
## SessionManager core
|
|
15
|
-
|
|
16
|
-
- Tracks `byId`, `labelsById`, `leafId`, and usage statistics.
|
|
17
|
-
- Tree APIs:
|
|
18
|
-
- `getLeafId()`, `getLeafEntry()`, `getEntry(id)`, `getChildren(id)`
|
|
19
|
-
- `getBranch(fromId?)` → root-to-leaf path
|
|
20
|
-
- `getTree()` → `SessionTreeNode { entry, children, label }`
|
|
21
|
-
- `getLabel(id)`
|
|
22
|
-
- `buildSessionContext()` walks from the current leaf and resolves compaction. `custom_message` and `branch_summary` entries are converted to AgentMessage roles and later to user-role LLM messages via `convertToLlm()`.
|
|
23
|
-
- Appenders (all return entry id and advance the leaf): `appendMessage`, `appendCompaction`, `appendCustomEntry`, `appendCustomMessageEntry`, `appendLabelChange`, `appendModelChange`, `appendThinkingLevelChange`, `appendSessionInit`, `appendTtsrInjection`.
|
|
24
|
-
- `getSessionFile()` returns `string | undefined` for in-memory sessions. `flush()` persists pending writes.
|
|
25
|
-
|
|
26
|
-
## Migration
|
|
27
|
-
|
|
28
|
-
- `CURRENT_SESSION_VERSION = 3`.
|
|
29
|
-
- v1 → v2: assigns `id`/`parentId` and converts compaction `firstKeptEntryIndex` to `firstKeptEntryId`.
|
|
30
|
-
- v2 → v3: renames message role `hookMessage` → `custom`.
|
|
31
|
-
- `SessionManager.open()` / `setSessionFile()` rewrite the file after migration.
|
|
32
|
-
|
|
33
|
-
## Branching
|
|
34
|
-
|
|
35
|
-
- `branch(entryId)` moves the leaf pointer to a prior entry.
|
|
36
|
-
- `resetLeaf()` sets the leaf to `null` so the next append creates a new root entry.
|
|
37
|
-
- `branchWithSummary(branchFromId, summary, details?, fromExtension?)` appends `branch_summary` and switches the leaf.
|
|
38
|
-
- `createBranchedSession(leafId)` writes a new session file containing the selected path; `LabelEntry` values are rebuilt from resolved labels. In-memory sessions replace their entries and return `undefined`.
|
|
39
|
-
|
|
40
|
-
## Compaction integration
|
|
41
|
-
|
|
42
|
-
- `CompactionEntry` / `CompactionResult` are generic with optional `details` and `preserveData`; `firstKeptEntryId` is the compaction anchor.
|
|
43
|
-
- `session_before_compact` provides `CompactionPreparation`, `branchEntries`, `customInstructions`, and `signal`.
|
|
44
|
-
- `session.compacting` allows overriding the compaction prompt/context.
|
|
45
|
-
- `session_compact` emits the final `CompactionEntry` and `fromExtension` flag.
|
|
46
|
-
|
|
47
|
-
## Labels
|
|
48
|
-
|
|
49
|
-
- `LabelEntry` stores `targetId` + `label`; `labelsById` maps targetId → label.
|
|
50
|
-
- `appendLabelChange(targetId, label?)` sets or clears labels.
|
|
51
|
-
- Tree selector shows labels and supports the "labeled-only" filter. Press Shift+L in `/tree` to edit the selected label.
|
|
52
|
-
|
|
53
|
-
## Custom messages
|
|
54
|
-
|
|
55
|
-
- `CustomMessageEntry` stores `customType`, `content`, `display`, `details`; converted to AgentMessage role `custom`.
|
|
56
|
-
- `buildSessionContext()` includes `custom_message` entries; `convertToLlm()` maps them to user-role LLM messages.
|
|
57
|
-
- TUI rendering: `display=false` hides the entry; `display=true` uses `customMessageBg`/`customMessageText`/`customMessageLabel` theme tokens.
|
|
58
|
-
- Extensions can override rendering via `registerMessageRenderer(customType, renderer)`.
|
|
59
|
-
|
|
60
|
-
## Extension API touchpoints
|
|
61
|
-
|
|
62
|
-
- `sendMessage(...)` appends a `CustomMessageEntry`. options: `triggerTurn`, `deliverAs` ("steer" | "followUp" | "nextTurn").
|
|
63
|
-
- `sendUserMessage(...)` always triggers a turn with a real user message.
|
|
64
|
-
- `appendEntry(customType, data)` persists extension state (`CustomEntry`, not sent to the LLM).
|
|
65
|
-
- `registerCommand(name, { description?, handler })` registers `/commands`. Handlers return `void`; trigger turns explicitly with `sendMessage`/`sendUserMessage`.
|
|
66
|
-
- `ExtensionContext` exposes `sessionManager` (read-only), `modelRegistry`, `model`, `getContextUsage()`, `compact()`, and abort/idle helpers.
|
|
67
|
-
- `ExtensionCommandContext` adds `waitForIdle()`, `newSession()`, `branch()`, `navigateTree()`.
|
|
68
|
-
|
|
69
|
-
## Agent context events
|
|
70
|
-
|
|
71
|
-
- `context`: called before each LLM call with `AgentMessage[]`; returning `{ messages }` replaces the prompt messages for this call (not persisted).
|
|
72
|
-
- `before_agent_start`: fired after the user prompt but before the agent loop; event includes `prompt`, `images`, and `systemPrompt`.
|
|
73
|
-
- Result can add a `CustomMessage` and/or replace the `systemPrompt` for the turn. Multiple extensions can contribute messages; `systemPrompt` updates chain in order.
|
|
74
|
-
|
|
75
|
-
## Tree UI + commands
|
|
76
|
-
|
|
77
|
-
- `/tree`: in-place navigation with search, filter modes (default/no-tools/user-only/labeled-only/all), labels, and active-path highlighting.
|
|
78
|
-
- `/branch`: creates a new session file from the current path.
|
|
79
|
-
- Tree navigation emits `session_before_tree` with `TreePreparation` (`targetId`, `oldLeafId`, `commonAncestorId`, `entriesToSummarize`, `userWantsSummary`) and `session_tree` with `SessionTreeEvent` (`newLeafId`, `oldLeafId`, `summaryEntry?`, `fromExtension?`).
|
|
80
|
-
|
|
81
|
-
## HTML export
|
|
82
|
-
|
|
83
|
-
- Session HTML export includes a sidebar tree with search, the same filter modes as `/tree`, and a responsive hamburger toggle.
|
|
84
|
-
- URL parameters `leafId`/`targetId` allow deep-linking to a branch and specific entry.
|
package/docs/session.md
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
# Session File Format
|
|
2
|
-
|
|
3
|
-
Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with a `type` field. Session entries form a tree structure via `id`/`parentId` fields, enabling in-place branching without creating new files.
|
|
4
|
-
|
|
5
|
-
## File Location
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
~/.omp/agent/sessions/--<cwd>--/<timestamp>_<sessionId>.jsonl
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Default base directory comes from `getAgentDir()` (overridable via `PI_CODING_AGENT_DIR`).
|
|
12
|
-
`<cwd>` is the working directory with the leading slash removed and `/`, `\`, `:` replaced by `-`.
|
|
13
|
-
`<timestamp>` is ISO-8601 with `:`/`.` replaced by `-`. `sessionId` is a snowflake hex string.
|
|
14
|
-
|
|
15
|
-
## Session Version
|
|
16
|
-
|
|
17
|
-
Sessions have a version field in the header:
|
|
18
|
-
|
|
19
|
-
- **Version 1**: Linear entry sequence (legacy, auto-migrated on load)
|
|
20
|
-
- **Version 2**: Tree structure with `id`/`parentId` linking
|
|
21
|
-
- **Version 3**: Renamed legacy `hookMessage` role to `custom`
|
|
22
|
-
|
|
23
|
-
Existing sessions are automatically migrated to the latest version when loaded.
|
|
24
|
-
|
|
25
|
-
## Type Definitions
|
|
26
|
-
|
|
27
|
-
- [`src/session/session-manager.ts`](../src/session/session-manager.ts) - Session entry types and `SessionManager`
|
|
28
|
-
- [`src/session/messages.ts`](../src/session/messages.ts) - Custom message roles and LLM conversion
|
|
29
|
-
- [`packages/agent/src/types.ts`](../../agent/src/types.ts) - `AgentMessage`, `ThinkingLevel`
|
|
30
|
-
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `Message`, content blocks, `Usage`, `ToolCall`
|
|
31
|
-
|
|
32
|
-
## Entry Base
|
|
33
|
-
|
|
34
|
-
All entries (except `SessionHeader`) extend `SessionEntryBase`:
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
interface SessionEntryBase {
|
|
38
|
-
type: string;
|
|
39
|
-
id: string; // Short snowflake suffix (8 hex chars)
|
|
40
|
-
parentId: string | null; // Parent entry ID (null for first entry)
|
|
41
|
-
timestamp: string; // ISO timestamp
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Entry Types
|
|
46
|
-
|
|
47
|
-
### SessionHeader
|
|
48
|
-
|
|
49
|
-
First line of the file. Metadata only, not part of the tree (no `id`/`parentId`). `version` is absent in v1 sessions.
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{
|
|
53
|
-
"type": "session",
|
|
54
|
-
"version": 3,
|
|
55
|
-
"id": "a1b2c3d4e5f60001",
|
|
56
|
-
"timestamp": "2024-12-03T14:00:00.000Z",
|
|
57
|
-
"cwd": "/path/to/project",
|
|
58
|
-
"title": "Optional title"
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
For sessions with a parent (created via `/branch`, `newSession({ parentSession })`, or fork operations):
|
|
63
|
-
|
|
64
|
-
```json
|
|
65
|
-
{
|
|
66
|
-
"type": "session",
|
|
67
|
-
"version": 3,
|
|
68
|
-
"id": "a1b2c3d4e5f60001",
|
|
69
|
-
"timestamp": "2024-12-03T14:00:00.000Z",
|
|
70
|
-
"cwd": "/path/to/project",
|
|
71
|
-
"parentSession": "/path/to/original/session.jsonl"
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
`parentSession` is an opaque string used for lineage tracking (typically a session file path).
|
|
76
|
-
|
|
77
|
-
### SessionMessageEntry
|
|
78
|
-
|
|
79
|
-
A message in the conversation. The `message` field contains an `AgentMessage`,
|
|
80
|
-
including base LLM messages plus coding-agent custom roles (bash/python execution,
|
|
81
|
-
custom/legacy `hookMessage` messages from v2 sessions, file mentions, etc.).
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{"type":"message","id":"a1b2c3d4","parentId":"prev1234","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"}}
|
|
85
|
-
{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
|
|
86
|
-
{"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false}}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### ModelChangeEntry
|
|
90
|
-
|
|
91
|
-
Emitted when the user switches models mid-session. `model` is stored as `provider/modelId`.
|
|
92
|
-
|
|
93
|
-
```json
|
|
94
|
-
{
|
|
95
|
-
"type": "model_change",
|
|
96
|
-
"id": "d4e5f6g7",
|
|
97
|
-
"parentId": "c3d4e5f6",
|
|
98
|
-
"timestamp": "2024-12-03T14:05:00.000Z",
|
|
99
|
-
"model": "openai/gpt-4o",
|
|
100
|
-
"role": "default"
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### ThinkingLevelChangeEntry
|
|
105
|
-
|
|
106
|
-
Emitted when the user changes the thinking/reasoning level.
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"type": "thinking_level_change",
|
|
111
|
-
"id": "e5f6g7h8",
|
|
112
|
-
"parentId": "d4e5f6g7",
|
|
113
|
-
"timestamp": "2024-12-03T14:06:00.000Z",
|
|
114
|
-
"thinkingLevel": "high"
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
`thinkingLevel` matches `ThinkingLevel` from `packages/agent` (e.g., `off`, `minimal`, `low`, `medium`, `high`, `xhigh`).
|
|
119
|
-
|
|
120
|
-
### CompactionEntry
|
|
121
|
-
|
|
122
|
-
Created when context is compacted. Stores a summary of earlier messages.
|
|
123
|
-
|
|
124
|
-
```json
|
|
125
|
-
{
|
|
126
|
-
"type": "compaction",
|
|
127
|
-
"id": "f6g7h8i9",
|
|
128
|
-
"parentId": "e5f6g7h8",
|
|
129
|
-
"timestamp": "2024-12-03T14:10:00.000Z",
|
|
130
|
-
"summary": "User discussed X, Y, Z...",
|
|
131
|
-
"shortSummary": "Quick recap...",
|
|
132
|
-
"firstKeptEntryId": "c3d4e5f6",
|
|
133
|
-
"tokensBefore": 50000,
|
|
134
|
-
"fromExtension": false
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Optional fields:
|
|
139
|
-
|
|
140
|
-
- `details`: Compaction-implementation specific data (extension data, version markers, etc.)
|
|
141
|
-
- `shortSummary`: Short-form summary for UI contexts
|
|
142
|
-
- `preserveData`: Hook/extension data to persist across compaction
|
|
143
|
-
- `fromExtension`: `true` if generated by an extension, `false`/`undefined` if pi-generated
|
|
144
|
-
|
|
145
|
-
### BranchSummaryEntry
|
|
146
|
-
|
|
147
|
-
Created when switching branches with an LLM-generated summary of the abandoned path. Captures context from the previous branch.
|
|
148
|
-
|
|
149
|
-
```json
|
|
150
|
-
{
|
|
151
|
-
"type": "branch_summary",
|
|
152
|
-
"id": "g7h8i9j0",
|
|
153
|
-
"parentId": "a1b2c3d4",
|
|
154
|
-
"timestamp": "2024-12-03T14:15:00.000Z",
|
|
155
|
-
"fromId": "f6g7h8i9",
|
|
156
|
-
"summary": "Branch explored approach A..."
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
`fromId` is the branch point entry id; when branching from the root it is `"root"`.
|
|
161
|
-
|
|
162
|
-
Optional fields:
|
|
163
|
-
|
|
164
|
-
- `details`: Extension-specific data (not sent to LLM)
|
|
165
|
-
- `fromExtension`: `true` if generated by an extension
|
|
166
|
-
|
|
167
|
-
### CustomEntry
|
|
168
|
-
|
|
169
|
-
Extension state persistence. Does NOT participate in LLM context.
|
|
170
|
-
|
|
171
|
-
```json
|
|
172
|
-
{
|
|
173
|
-
"type": "custom",
|
|
174
|
-
"id": "h8i9j0k1",
|
|
175
|
-
"parentId": "g7h8i9j0",
|
|
176
|
-
"timestamp": "2024-12-03T14:20:00.000Z",
|
|
177
|
-
"customType": "my-extension",
|
|
178
|
-
"data": { "count": 42 }
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Use `customType` to identify your extension's entries on reload.
|
|
183
|
-
|
|
184
|
-
### CustomMessageEntry
|
|
185
|
-
|
|
186
|
-
Extension-injected messages that DO participate in LLM context.
|
|
187
|
-
|
|
188
|
-
```json
|
|
189
|
-
{
|
|
190
|
-
"type": "custom_message",
|
|
191
|
-
"id": "i9j0k1l2",
|
|
192
|
-
"parentId": "h8i9j0k1",
|
|
193
|
-
"timestamp": "2024-12-03T14:25:00.000Z",
|
|
194
|
-
"customType": "my-extension",
|
|
195
|
-
"content": "Injected context...",
|
|
196
|
-
"display": true
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Fields:
|
|
201
|
-
|
|
202
|
-
- `content`: String or `(TextContent | ImageContent)[]` (same as UserMessage)
|
|
203
|
-
- `display`: `true` = show in TUI with distinct styling, `false` = hidden
|
|
204
|
-
- `details`: Optional extension-specific metadata (not sent to LLM)
|
|
205
|
-
|
|
206
|
-
### LabelEntry
|
|
207
|
-
|
|
208
|
-
User-defined bookmark/marker on an entry.
|
|
209
|
-
|
|
210
|
-
```json
|
|
211
|
-
{
|
|
212
|
-
"type": "label",
|
|
213
|
-
"id": "j0k1l2m3",
|
|
214
|
-
"parentId": "i9j0k1l2",
|
|
215
|
-
"timestamp": "2024-12-03T14:30:00.000Z",
|
|
216
|
-
"targetId": "a1b2c3d4",
|
|
217
|
-
"label": "checkpoint-1"
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Set `label` to `undefined` to clear a label.
|
|
222
|
-
|
|
223
|
-
### TtsrInjectionEntry
|
|
224
|
-
|
|
225
|
-
Tracks which time-traveling stream rules were injected during the session.
|
|
226
|
-
|
|
227
|
-
```json
|
|
228
|
-
{
|
|
229
|
-
"type": "ttsr_injection",
|
|
230
|
-
"id": "k1l2m3n4",
|
|
231
|
-
"parentId": "j0k1l2m3",
|
|
232
|
-
"timestamp": "2024-12-03T14:31:00.000Z",
|
|
233
|
-
"injectedRules": ["rule-a", "rule-b"]
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### SessionInitEntry
|
|
238
|
-
|
|
239
|
-
Captures initial context for subagent sessions (debugging/replay). Not used in LLM context building.
|
|
240
|
-
|
|
241
|
-
```json
|
|
242
|
-
{
|
|
243
|
-
"type": "session_init",
|
|
244
|
-
"id": "l2m3n4o5",
|
|
245
|
-
"parentId": "k1l2m3n4",
|
|
246
|
-
"timestamp": "2024-12-03T14:32:00.000Z",
|
|
247
|
-
"systemPrompt": "...",
|
|
248
|
-
"task": "Initial task...",
|
|
249
|
-
"tools": ["bash", "read"],
|
|
250
|
-
"outputSchema": { "type": "object" }
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Tree Structure
|
|
255
|
-
|
|
256
|
-
Entries form a tree:
|
|
257
|
-
|
|
258
|
-
- First entry has `parentId: null`
|
|
259
|
-
- Each subsequent entry points to its parent via `parentId`
|
|
260
|
-
- Branching creates new children from an earlier entry
|
|
261
|
-
- The "leaf" is the current position in the tree
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
[user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← current leaf
|
|
265
|
-
│
|
|
266
|
-
└─ [branch_summary] ─── [user msg] ← alternate branch
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## Context Building
|
|
270
|
-
|
|
271
|
-
`buildSessionContext()` walks from the current leaf to the root, producing the message list for the LLM:
|
|
272
|
-
|
|
273
|
-
1. Collects all entries on the path
|
|
274
|
-
2. Extracts current model map, thinking level, and injected TTSR rules
|
|
275
|
-
3. If a `CompactionEntry` is on the path:
|
|
276
|
-
- Emits the summary first
|
|
277
|
-
- Then messages from `firstKeptEntryId` to compaction
|
|
278
|
-
- Then messages after compaction
|
|
279
|
-
4. Converts `BranchSummaryEntry` and `CustomMessageEntry` to appropriate message formats
|
|
280
|
-
|
|
281
|
-
Return value is a `SessionContext` containing `messages`, `models`, `thinkingLevel`, and `injectedTtsrRules`.
|
|
282
|
-
|
|
283
|
-
## Parsing Example
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
const text = await Bun.file("session.jsonl").text();
|
|
287
|
-
const entries = Bun.JSONL.parse(text);
|
|
288
|
-
|
|
289
|
-
for (const entry of entries) {
|
|
290
|
-
switch (entry.type) {
|
|
291
|
-
case "session":
|
|
292
|
-
console.log(`Session v${entry.version ?? 1}: ${entry.id}`);
|
|
293
|
-
break;
|
|
294
|
-
case "message":
|
|
295
|
-
console.log(`[${entry.id}] ${entry.message.role}: ${JSON.stringify(entry.message.content)}`);
|
|
296
|
-
break;
|
|
297
|
-
case "compaction":
|
|
298
|
-
console.log(`[${entry.id}] Compaction: ${entry.tokensBefore} tokens summarized`);
|
|
299
|
-
break;
|
|
300
|
-
case "branch_summary":
|
|
301
|
-
console.log(`[${entry.id}] Branch from ${entry.fromId}`);
|
|
302
|
-
break;
|
|
303
|
-
case "custom":
|
|
304
|
-
console.log(`[${entry.id}] Custom (${entry.customType}): ${JSON.stringify(entry.data)}`);
|
|
305
|
-
break;
|
|
306
|
-
case "custom_message":
|
|
307
|
-
console.log(`[${entry.id}] Custom message (${entry.customType}): ${entry.content}`);
|
|
308
|
-
break;
|
|
309
|
-
case "ttsr_injection":
|
|
310
|
-
console.log(`[${entry.id}] TTSR rules: ${entry.injectedRules.join(", ")}`);
|
|
311
|
-
break;
|
|
312
|
-
case "session_init":
|
|
313
|
-
console.log(`[${entry.id}] Init: ${entry.tools.join(", ")}`);
|
|
314
|
-
break;
|
|
315
|
-
case "label":
|
|
316
|
-
console.log(`[${entry.id}] Label "${entry.label}" on ${entry.targetId}`);
|
|
317
|
-
break;
|
|
318
|
-
case "model_change":
|
|
319
|
-
console.log(`[${entry.id}] Model: ${entry.model} (${entry.role ?? "default"})`);
|
|
320
|
-
break;
|
|
321
|
-
case "thinking_level_change":
|
|
322
|
-
console.log(`[${entry.id}] Thinking: ${entry.thinkingLevel}`);
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
## SessionManager API
|
|
329
|
-
|
|
330
|
-
Key methods for working with sessions programmatically:
|
|
331
|
-
|
|
332
|
-
### Creation
|
|
333
|
-
|
|
334
|
-
- `SessionManager.create(cwd, sessionDir?)` - New session
|
|
335
|
-
- `SessionManager.open(path, sessionDir?)` - Open existing
|
|
336
|
-
- `SessionManager.continueRecent(cwd, sessionDir?)` - Continue most recent or create new
|
|
337
|
-
- `SessionManager.inMemory(cwd?)` - No file persistence
|
|
338
|
-
|
|
339
|
-
### Appending (all return entry ID)
|
|
340
|
-
|
|
341
|
-
- `appendMessage(message)` - Add message
|
|
342
|
-
- `appendThinkingLevelChange(level)` - Record thinking change
|
|
343
|
-
- `appendModelChange(model, role?)` - Record model change (`model` = `provider/modelId`)
|
|
344
|
-
- `appendSessionInit(init)` - Record initial subagent context
|
|
345
|
-
- `appendCompaction(summary, shortSummary, firstKeptEntryId, tokensBefore, details?, fromExtension?, preserveData?)`
|
|
346
|
-
- `appendCustomEntry(customType, data?)` - Extension state (not in context)
|
|
347
|
-
- `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
|
|
348
|
-
- `appendTtsrInjection(ruleNames)` - Record injected TTSR rules
|
|
349
|
-
- `appendLabelChange(targetId, label)` - Set/clear label
|
|
350
|
-
|
|
351
|
-
### Tree Navigation
|
|
352
|
-
|
|
353
|
-
- `getLeafId()` - Current position
|
|
354
|
-
- `getLeafEntry()` - Current leaf entry
|
|
355
|
-
- `getEntry(id)` - Get entry by ID
|
|
356
|
-
- `getBranch(fromId?)` - Walk from entry to root
|
|
357
|
-
- `getTree()` - Get full tree structure
|
|
358
|
-
- `getChildren(parentId)` - Get direct children
|
|
359
|
-
- `getLabel(id)` - Get label for entry
|
|
360
|
-
- `branch(entryId)` - Move leaf to earlier entry
|
|
361
|
-
- `branchWithSummary(entryId | null, summary, details?, fromExtension?)` - Branch with context summary
|
|
362
|
-
- `resetLeaf()` - Move leaf to before first entry
|
|
363
|
-
|
|
364
|
-
### Context
|
|
365
|
-
|
|
366
|
-
- `buildSessionContext()` - Get messages for LLM
|
|
367
|
-
- `getEntries()` - All entries (excluding header)
|
|
368
|
-
- `getHeader()` - Session metadata
|