@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.
Files changed (56) hide show
  1. package/CHANGELOG.md +37 -37
  2. package/README.md +9 -1052
  3. package/package.json +7 -7
  4. package/src/cli/args.ts +1 -0
  5. package/src/cli/update-cli.ts +49 -35
  6. package/src/cli/web-search-cli.ts +3 -2
  7. package/src/commands/web-search.ts +1 -0
  8. package/src/config/model-registry.ts +6 -0
  9. package/src/config/settings-schema.ts +25 -3
  10. package/src/config/settings.ts +1 -0
  11. package/src/extensibility/extensions/wrapper.ts +20 -13
  12. package/src/extensibility/slash-commands.ts +12 -91
  13. package/src/lsp/client.ts +24 -27
  14. package/src/lsp/index.ts +92 -42
  15. package/src/mcp/config-writer.ts +33 -0
  16. package/src/mcp/config.ts +6 -1
  17. package/src/mcp/types.ts +1 -0
  18. package/src/modes/components/custom-editor.ts +8 -5
  19. package/src/modes/components/settings-defs.ts +2 -1
  20. package/src/modes/controllers/command-controller.ts +12 -6
  21. package/src/modes/controllers/input-controller.ts +21 -186
  22. package/src/modes/controllers/mcp-command-controller.ts +60 -3
  23. package/src/modes/interactive-mode.ts +2 -2
  24. package/src/modes/types.ts +1 -1
  25. package/src/sdk.ts +23 -1
  26. package/src/secrets/index.ts +116 -0
  27. package/src/secrets/obfuscator.ts +269 -0
  28. package/src/secrets/regex.ts +21 -0
  29. package/src/session/agent-session.ts +138 -21
  30. package/src/session/compaction/branch-summarization.ts +2 -2
  31. package/src/session/compaction/compaction.ts +10 -3
  32. package/src/session/compaction/utils.ts +25 -1
  33. package/src/slash-commands/builtin-registry.ts +419 -0
  34. package/src/web/scrapers/github.ts +50 -12
  35. package/src/web/search/index.ts +5 -5
  36. package/src/web/search/provider.ts +13 -2
  37. package/src/web/search/providers/brave.ts +165 -0
  38. package/src/web/search/types.ts +1 -1
  39. package/docs/compaction.md +0 -436
  40. package/docs/config-usage.md +0 -176
  41. package/docs/custom-tools.md +0 -585
  42. package/docs/environment-variables.md +0 -257
  43. package/docs/extension-loading.md +0 -106
  44. package/docs/extensions.md +0 -1342
  45. package/docs/fs-scan-cache-architecture.md +0 -50
  46. package/docs/hooks.md +0 -906
  47. package/docs/models.md +0 -234
  48. package/docs/python-repl.md +0 -110
  49. package/docs/rpc.md +0 -1173
  50. package/docs/sdk.md +0 -1039
  51. package/docs/session-tree-plan.md +0 -84
  52. package/docs/session.md +0 -368
  53. package/docs/skills.md +0 -254
  54. package/docs/theme.md +0 -696
  55. package/docs/tree.md +0 -206
  56. 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