@henryqw/pi-session-recall 2.0.0 → 2.1.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/README.md +70 -6
- package/docs/session-search-routing.html +138 -0
- package/docs/session-search-routing.svg +121 -0
- package/extensions/hydrate.ts +16 -3
- package/extensions/repository-inventory.ts +515 -0
- package/extensions/search-core.ts +71 -2
- package/extensions/session-recall.ts +220 -4
- package/extensions/types.ts +6 -0
- package/package.json +2 -1
- package/skills/pi-session-pattern-miner/SKILL.md +58 -16
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Find decisions and context in past Pi sessions through a local FTS5 index.
|
|
|
4
4
|
|
|
5
5
|
Saved transcripts are not injected on every turn. The active tool registration still adds standing prompt cost through its schema, descriptions, and guideline. Returned content enters active model context.
|
|
6
6
|
|
|
7
|
-
The bundled `pi-session-pattern-miner` skill finds repeated work that may deserve automation.
|
|
7
|
+
The bundled `pi-session-pattern-miner` skill prepares one bounded sample. The model then finds repeated work that may deserve automation.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -22,7 +22,19 @@ Start discovery with a distinctive query:
|
|
|
22
22
|
|
|
23
23
|
`session_search` returns ranked sessions. The top result includes nearby messages and session bookends.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Prepare a repository-scoped pattern-mining sample with one call:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"operation": "prepare-pattern-miner",
|
|
30
|
+
"scope": "repository",
|
|
31
|
+
"limit": 10
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `scope:"all"` for cross-repository work. It still returns the corpus when repository inventory is unavailable.
|
|
36
|
+
|
|
37
|
+
Use IDs from a discovery result to ask for more context:
|
|
26
38
|
|
|
27
39
|
```json
|
|
28
40
|
{
|
|
@@ -36,13 +48,14 @@ The follow-up returns up to ten messages before and after that anchor on the sel
|
|
|
36
48
|
|
|
37
49
|
| Surface | Type | Purpose |
|
|
38
50
|
| --- | --- | --- |
|
|
39
|
-
| `session_search` | tool | Search
|
|
51
|
+
| `session_search` | tool | Search, inspect, or prepare a bounded mining corpus from past sessions. |
|
|
40
52
|
| `pi-session-pattern-miner` | skill | Find repeated work and choose the smallest useful automation. |
|
|
41
53
|
|
|
42
54
|
BM25 is a text-ranking method. Hydrated results include messages read from saved session files.
|
|
43
55
|
|
|
44
56
|
| Mode | Call | Result |
|
|
45
57
|
| --- | --- | --- |
|
|
58
|
+
| Pattern preparation | `operation:"prepare-pattern-miner"` + `scope:"repository"` or `scope:"all"` | Up to ten recent lineage-unique sessions plus repository inventory. The default limit is 10. Repository scope includes exact and descendant `cwd` values, filters before the limit, and excludes the current session file. |
|
|
46
59
|
| Discovery | `query` | BM25-ranked top sessions. Adaptive retrieval uses user and assistant text for windows, bookends, anchors, and counts. It omits tool-result messages and sets `toolResultsOmitted:true` when it removes one. Lower hits still include their indexed anchor. Use `detail:"full"` to hydrate every hit with tool-result messages included. |
|
|
47
60
|
| Scroll | `sessionId` + `aroundMessageId` | Raw message roles, including tool results, within ±`window` ([1,20]) of the anchor. Re-anchor on the last or first message ID to scroll. Across forks, pass the previous response's `branchTip`; `aroundMessageId` only centers the window and must lie on that branch. |
|
|
48
61
|
| Read | `sessionId` | Raw message roles, including tool results, from the session. Large sessions return head 20 + tail 10. Oversized content is bounded to 50k characters and marked with `contentTruncated`. |
|
|
@@ -52,10 +65,41 @@ In the interactive TUI, the collapsed tool block shows the last five visual line
|
|
|
52
65
|
|
|
53
66
|
### Skills
|
|
54
67
|
|
|
55
|
-
Run `/skill:pi-session-pattern-miner` to find repeated workflows in past sessions.
|
|
68
|
+
Run `/skill:pi-session-pattern-miner` to find repeated workflows in past sessions. The skill makes one preparation call before interpretation.
|
|
69
|
+
|
|
70
|
+
It treats one lineage as one source. It requires two independent examples before recommending automation. A requested topic gets a focused confirmation search even when the prepared sample does not contain it.
|
|
71
|
+
|
|
72
|
+
After clustering, the skill always checks current candidate-relevant package manifests, scripts, skills, and instructions. It abstains if it cannot check them safely.
|
|
73
|
+
|
|
74
|
+
### Repository inventory
|
|
75
|
+
|
|
76
|
+
Repository inventory contains discovery hints. It is never proof that a file owns a workflow. It does not verify the current worktree.
|
|
77
|
+
|
|
78
|
+
Package scripts, executable paths, and instruction paths come from stage-0 entries in the Git index. Package content comes from indexed blobs. Git reads the objects locally in one check batch and one content batch. Lazy object fetching and replacement refs are disabled. Inventory never opens working-tree package paths.
|
|
79
|
+
|
|
80
|
+
Executables need index mode `100755`. Instructions need a recognized name and a regular-file index mode.
|
|
81
|
+
|
|
82
|
+
Skills come from Pi's effective command registry. Their canonical source paths must stay inside the repository.
|
|
83
|
+
|
|
84
|
+
Available inventory includes this provenance:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"packageScripts": "git-index",
|
|
89
|
+
"executableScripts": "git-index",
|
|
90
|
+
"agentInstructions": "git-index",
|
|
91
|
+
"skills": "pi-effective-registry"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
It also sets `worktreeVerified:false`. Staged adds, changes, and deletes affect the snapshot. Unstaged changes, deletions, mode changes, symlinks, and untracked files do not.
|
|
96
|
+
|
|
97
|
+
Inspect the current candidate files before assigning ownership. Abstain if targeted current-file checks cannot be done safely.
|
|
56
98
|
|
|
57
99
|
## Flow
|
|
58
100
|
|
|
101
|
+

|
|
102
|
+
|
|
59
103
|
Search makes no model calls.
|
|
60
104
|
|
|
61
105
|
### Query and index
|
|
@@ -70,12 +114,16 @@ Search makes no model calls.
|
|
|
70
114
|
|
|
71
115
|
Hits inside the current session's live context are suppressed. Compacted-away or inactive-branch history stays discoverable. Forked sessions collapse into their parent when both match.
|
|
72
116
|
|
|
73
|
-
Before browse or
|
|
117
|
+
Before browse, discovery, or pattern preparation, the extension lazily syncs the index from the session tree.
|
|
118
|
+
|
|
119
|
+
Pattern preparation reports `sync.walkComplete`, `sync.backlogRemaining`, and `sync.complete`. `sync.complete` is true only after a complete walk with no backlog.
|
|
74
120
|
|
|
75
121
|
### Retrieval safety
|
|
76
122
|
|
|
77
123
|
Adaptive discovery leaves tool-result messages out of returned context. Use `detail:"full"`, READ, or SCROLL when you explicitly need them.
|
|
78
124
|
|
|
125
|
+
Pattern preparation returns only non-empty user and assistant text. It never returns thinking blocks or tool-result content. Each session keeps citation and lineage metadata even when its hydration fails.
|
|
126
|
+
|
|
79
127
|
Historical tool output may contain secrets or other sensitive data. Raw retrieval places that output in active model context.
|
|
80
128
|
|
|
81
129
|
## State and storage
|
|
@@ -91,7 +139,7 @@ The index and transcript reads stay local. Transcripts are read in place. Return
|
|
|
91
139
|
Pin the previous release:
|
|
92
140
|
|
|
93
141
|
```bash
|
|
94
|
-
pi install npm:@henryqw/pi-session-recall@
|
|
142
|
+
pi install npm:@henryqw/pi-session-recall@2.0.0
|
|
95
143
|
```
|
|
96
144
|
|
|
97
145
|
No index migration or cleanup is needed.
|
|
@@ -109,3 +157,19 @@ Session directories whose encoded path starts with `--tmp-` or `--private-tmp-`
|
|
|
109
157
|
Session files over 32 MiB are excluded from indexing and hydration. Discovery cannot newly find them.
|
|
110
158
|
|
|
111
159
|
READ and SCROLL return an explicit size error. A stale discovery hit from before a file grew returns metadata with empty messages and that error.
|
|
160
|
+
|
|
161
|
+
Pattern preparation runs one sync pass. A positive backlog or incomplete walk limits the sample and sets `sync.complete:false`. A total sync or required repository-inventory failure returns an explicit tool error.
|
|
162
|
+
|
|
163
|
+
Repository scope fails outside Git or when repository inventory fails. All scope still returns its corpus in both cases.
|
|
164
|
+
|
|
165
|
+
Outside Git, all scope sets `inventory.available:false` with `reason:"not-a-git-repository"`. On a repository inventory error, it uses `reason:"inventory-failed"`. Cancellation always aborts the call instead of returning unavailable inventory.
|
|
166
|
+
|
|
167
|
+
Preparation rejects `query`, session cursors, `window`, or `detail` in the same call. It also rejects `scope` without the operation.
|
|
168
|
+
|
|
169
|
+
Preparation output stays within 50,000 serialized characters. Inventory uses at most 10,000 characters. Its `omittedCounts` report only omitted collection entries, and `inventory.truncated` reports those omissions.
|
|
170
|
+
|
|
171
|
+
Inventory fails if the Git root output exceeds 4 KiB or the raw index listing exceeds 8 MiB. It also fails on malformed index data, invalid UTF-8, conflict entries, unsupported package modes, Git errors, unexpected Git stderr, or bounded stream overflow.
|
|
172
|
+
|
|
173
|
+
Inventory accepts at most 512 package manifests. Each indexed manifest can be at most 1 MiB, and their declared sizes can total at most 16 MiB. One bounded batch checks all blob sizes before one bounded batch reads their content. Blob order, type, size, UTF-8, and exact output framing are checked before manifest data is parsed.
|
|
174
|
+
|
|
175
|
+
Session and top-level `contentTruncated` report transcript budget trimming only. Session `truncated` reports omitted middle messages.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>session_search routing</title>
|
|
7
|
+
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
8
|
+
<style>
|
|
9
|
+
html, body { min-height: 100%; margin: 0; background: #f0eee9; }
|
|
10
|
+
body { display: grid; place-items: center; }
|
|
11
|
+
figure { width: min(100%, 1280px); margin: 0; }
|
|
12
|
+
svg { display: block; width: 100%; height: auto; }
|
|
13
|
+
</style>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<figure>
|
|
17
|
+
<svg width="1280" height="720" viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="session-search-routing-title session-search-routing-desc">
|
|
18
|
+
<title id="session-search-routing-title">session_search routing</title>
|
|
19
|
+
<desc id="session-search-routing-desc">Flowchart routing session_search requests between indexed preparation, discovery, and browse calls and direct transcript scroll and read calls.</desc>
|
|
20
|
+
<defs>
|
|
21
|
+
<style>
|
|
22
|
+
.eyebrow { fill: #6a7282; font: 500 8px 'Geist Mono', monospace; letter-spacing: .16em; }
|
|
23
|
+
.diagram-title { fill: #101828; font: 400 28px 'Instrument Serif', serif; }
|
|
24
|
+
.zone-label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; letter-spacing: .12em; }
|
|
25
|
+
.zone-note { fill: #6a7282; font: 400 8px 'Geist Mono', monospace; letter-spacing: .04em; }
|
|
26
|
+
.node-name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
|
|
27
|
+
.technical-name { fill: #101828; font: 500 12px 'Geist Mono', monospace; text-anchor: middle; }
|
|
28
|
+
.node-sub { fill: #4c5665; font: 400 8px 'Geist Mono', monospace; text-anchor: middle; }
|
|
29
|
+
.edge { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#session-search-routing-arrow); }
|
|
30
|
+
.edge-label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .08em; }
|
|
31
|
+
</style>
|
|
32
|
+
<marker id="session-search-routing-arrow" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
33
|
+
<polygon points="0 0,8 4,0 8" fill="#4c5665"/>
|
|
34
|
+
</marker>
|
|
35
|
+
<marker id="session-search-routing-arrow-accent" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
36
|
+
<polygon points="0 0,8 4,0 8" fill="#1d4ed8"/>
|
|
37
|
+
</marker>
|
|
38
|
+
<marker id="session-search-routing-arrow-link" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
39
|
+
<polygon points="0 0,8 4,0 8" fill="#1d4ed8"/>
|
|
40
|
+
</marker>
|
|
41
|
+
</defs>
|
|
42
|
+
|
|
43
|
+
<rect width="1280" height="720" fill="#f0eee9"/>
|
|
44
|
+
|
|
45
|
+
<text class="eyebrow" x="40" y="48">PI-SESSION-RECALL · FLOWCHART</text>
|
|
46
|
+
<text class="diagram-title" x="40" y="88">Five routes through session_search</text>
|
|
47
|
+
|
|
48
|
+
<!-- Routing zones are backgrounds, not primary nodes. -->
|
|
49
|
+
<rect x="40" y="352" width="752" height="320" rx="8" fill="#e6e4de" stroke="rgba(16,24,40,0.14)" stroke-width="1"/>
|
|
50
|
+
<text class="zone-label" x="56" y="376">INDEX-BACKED CALLS · LAZY SYNC</text>
|
|
51
|
+
<text class="zone-note" x="56" y="392">PREPARE · DISCOVERY · BROWSE</text>
|
|
52
|
+
|
|
53
|
+
<rect x="816" y="352" width="424" height="320" rx="8" fill="#e6e4de" stroke="rgba(16,24,40,0.14)" stroke-width="1"/>
|
|
54
|
+
<text class="zone-label" x="832" y="376">DIRECT TRANSCRIPT READS</text>
|
|
55
|
+
<text class="zone-note" x="832" y="392">RAW ROLES · TOOL RESULTS</text>
|
|
56
|
+
|
|
57
|
+
<!-- Connectors: all routes are painted before their nodes. -->
|
|
58
|
+
<path class="edge" d="M640 180 V212"/>
|
|
59
|
+
<path class="edge" d="M532 264 H320 Q312 264 312 272 V388 Q312 396 304 396 H184 Q176 396 176 404 V412"/>
|
|
60
|
+
<path class="edge" d="M640 316 V336 Q640 344 632 344 H432 Q424 344 424 352 V392"/>
|
|
61
|
+
<path class="edge" d="M748 264 H792 Q800 264 800 272 V328 Q800 336 808 336 H1016 Q1024 336 1024 344 V392"/>
|
|
62
|
+
<path class="edge" d="M536 448 H576"/>
|
|
63
|
+
<path class="edge" d="M424 504 V544"/>
|
|
64
|
+
<path class="edge" d="M1136 448 V536"/>
|
|
65
|
+
<path class="edge" d="M912 448 H896 Q888 448 888 456 V520 Q888 528 896 528 H912 Q920 528 920 536"/>
|
|
66
|
+
|
|
67
|
+
<!-- Branch labels stay 8px clear of their connectors. -->
|
|
68
|
+
<rect x="384" y="244" width="72" height="12" rx="2" fill="#f0eee9"/>
|
|
69
|
+
<text class="edge-label" x="420" y="252">PREPARE</text>
|
|
70
|
+
|
|
71
|
+
<rect x="488" y="324" width="112" height="12" rx="2" fill="#f0eee9"/>
|
|
72
|
+
<text class="edge-label" x="544" y="332">SEARCH / BROWSE</text>
|
|
73
|
+
|
|
74
|
+
<rect x="808" y="288" width="56" height="12" rx="2" fill="#f0eee9"/>
|
|
75
|
+
<text class="edge-label" x="836" y="296">INSPECT</text>
|
|
76
|
+
|
|
77
|
+
<rect x="544" y="428" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
78
|
+
<text class="edge-label" x="556" y="436">YES</text>
|
|
79
|
+
|
|
80
|
+
<rect x="432" y="516" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
81
|
+
<text class="edge-label" x="444" y="524">NO</text>
|
|
82
|
+
|
|
83
|
+
<rect x="1144" y="480" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
84
|
+
<text class="edge-label" x="1156" y="488">YES</text>
|
|
85
|
+
|
|
86
|
+
<rect x="896" y="480" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
87
|
+
<text class="edge-label" x="908" y="488">NO</text>
|
|
88
|
+
|
|
89
|
+
<!-- Primary nodes: one start, three decisions, and five terminal modes. -->
|
|
90
|
+
<rect x="540" y="132" width="200" height="48" rx="20" fill="rgba(16,24,40,0.03)" stroke="#4c5665" stroke-width="1"/>
|
|
91
|
+
<text class="technical-name" x="640" y="156">session_search</text>
|
|
92
|
+
<text class="node-sub" x="640" y="172">ONE LOCAL TOOL</text>
|
|
93
|
+
|
|
94
|
+
<polygon points="640,212 748,264 640,316 532,264" fill="rgba(29,78,216,0.08)" stroke="#1d4ed8" stroke-width="1.2"/>
|
|
95
|
+
<text class="node-name" x="640" y="260">Call family?</text>
|
|
96
|
+
<text class="node-sub" x="640" y="280">ROUTE BY ARGUMENTS</text>
|
|
97
|
+
|
|
98
|
+
<rect x="48" y="412" width="240" height="128" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
99
|
+
<text class="node-name" x="168" y="444">Pattern preparation</text>
|
|
100
|
+
<text class="node-sub" x="168" y="472">operation + scope · lazy sync</text>
|
|
101
|
+
<text class="node-sub" x="168" y="488">bounded user/assistant corpus</text>
|
|
102
|
+
<text class="node-sub" x="168" y="504">+ repository inventory · never tool results</text>
|
|
103
|
+
|
|
104
|
+
<polygon points="424,392 536,448 424,504 312,448" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
105
|
+
<text class="technical-name" x="424" y="444">query?</text>
|
|
106
|
+
<text class="node-sub" x="424" y="464">PRESENT</text>
|
|
107
|
+
|
|
108
|
+
<rect x="576" y="400" width="200" height="96" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
109
|
+
<text class="node-name" x="676" y="428">Discovery</text>
|
|
110
|
+
<text class="node-sub" x="676" y="452">query · lazy sync + FTS5</text>
|
|
111
|
+
<text class="node-sub" x="676" y="468">adaptive output omits tool results</text>
|
|
112
|
+
<text class="node-sub" x="676" y="484">detail:"full" includes tool results</text>
|
|
113
|
+
|
|
114
|
+
<rect x="328" y="544" width="192" height="96" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
115
|
+
<text class="node-name" x="424" y="572">Browse</text>
|
|
116
|
+
<text class="node-sub" x="424" y="596">no args · lazy sync</text>
|
|
117
|
+
<text class="node-sub" x="424" y="612">recent session metadata</text>
|
|
118
|
+
|
|
119
|
+
<polygon points="1024,392 1136,448 1024,504 912,448" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
120
|
+
<text class="technical-name" x="1024" y="444">aroundMessageId?</text>
|
|
121
|
+
<text class="node-sub" x="1024" y="464">WITH sessionId</text>
|
|
122
|
+
|
|
123
|
+
<rect x="1040" y="536" width="192" height="112" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
124
|
+
<text class="node-name" x="1136" y="564">Scroll</text>
|
|
125
|
+
<text class="node-sub" x="1136" y="588">sessionId + aroundMessageId</text>
|
|
126
|
+
<text class="node-sub" x="1136" y="604">direct saved JSONL</text>
|
|
127
|
+
<text class="node-sub" x="1136" y="620">raw roles including tool results</text>
|
|
128
|
+
<text class="node-sub" x="1136" y="636">±window with branchTip</text>
|
|
129
|
+
|
|
130
|
+
<rect x="824" y="536" width="192" height="112" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
131
|
+
<text class="node-name" x="920" y="564">Read</text>
|
|
132
|
+
<text class="node-sub" x="920" y="588">sessionId · direct saved JSONL</text>
|
|
133
|
+
<text class="node-sub" x="920" y="604">raw roles including tool results</text>
|
|
134
|
+
<text class="node-sub" x="920" y="620">head 20 + tail 10</text>
|
|
135
|
+
</svg>
|
|
136
|
+
</figure>
|
|
137
|
+
</body>
|
|
138
|
+
</html>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="1280" height="720" viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="session-search-routing-title session-search-routing-desc">
|
|
3
|
+
<title id="session-search-routing-title">session_search routing</title>
|
|
4
|
+
<desc id="session-search-routing-desc">Flowchart routing session_search requests between indexed preparation, discovery, and browse calls and direct transcript scroll and read calls.</desc>
|
|
5
|
+
<defs>
|
|
6
|
+
<style>@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap');</style>
|
|
7
|
+
<style>
|
|
8
|
+
.eyebrow { fill: #6a7282; font: 500 8px 'Geist Mono', monospace; letter-spacing: .16em; }
|
|
9
|
+
.diagram-title { fill: #101828; font: 400 28px 'Instrument Serif', serif; }
|
|
10
|
+
.zone-label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; letter-spacing: .12em; }
|
|
11
|
+
.zone-note { fill: #6a7282; font: 400 8px 'Geist Mono', monospace; letter-spacing: .04em; }
|
|
12
|
+
.node-name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
|
|
13
|
+
.technical-name { fill: #101828; font: 500 12px 'Geist Mono', monospace; text-anchor: middle; }
|
|
14
|
+
.node-sub { fill: #4c5665; font: 400 8px 'Geist Mono', monospace; text-anchor: middle; }
|
|
15
|
+
.edge { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#session-search-routing-arrow); }
|
|
16
|
+
.edge-label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .08em; }
|
|
17
|
+
</style>
|
|
18
|
+
<marker id="session-search-routing-arrow" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
19
|
+
<polygon points="0 0,8 4,0 8" fill="#4c5665"/>
|
|
20
|
+
</marker>
|
|
21
|
+
<marker id="session-search-routing-arrow-accent" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
22
|
+
<polygon points="0 0,8 4,0 8" fill="#1d4ed8"/>
|
|
23
|
+
</marker>
|
|
24
|
+
<marker id="session-search-routing-arrow-link" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
|
|
25
|
+
<polygon points="0 0,8 4,0 8" fill="#1d4ed8"/>
|
|
26
|
+
</marker>
|
|
27
|
+
</defs>
|
|
28
|
+
|
|
29
|
+
<rect width="1280" height="720" fill="#f0eee9"/>
|
|
30
|
+
|
|
31
|
+
<text class="eyebrow" x="40" y="48">PI-SESSION-RECALL · FLOWCHART</text>
|
|
32
|
+
<text class="diagram-title" x="40" y="88">Five routes through session_search</text>
|
|
33
|
+
|
|
34
|
+
<!-- Routing zones are backgrounds, not primary nodes. -->
|
|
35
|
+
<rect x="40" y="352" width="752" height="320" rx="8" fill="#e6e4de" stroke="#101828" stroke-opacity="0.14" stroke-width="1"/>
|
|
36
|
+
<text class="zone-label" x="56" y="376">INDEX-BACKED CALLS · LAZY SYNC</text>
|
|
37
|
+
<text class="zone-note" x="56" y="392">PREPARE · DISCOVERY · BROWSE</text>
|
|
38
|
+
|
|
39
|
+
<rect x="816" y="352" width="424" height="320" rx="8" fill="#e6e4de" stroke="#101828" stroke-opacity="0.14" stroke-width="1"/>
|
|
40
|
+
<text class="zone-label" x="832" y="376">DIRECT TRANSCRIPT READS</text>
|
|
41
|
+
<text class="zone-note" x="832" y="392">RAW ROLES · TOOL RESULTS</text>
|
|
42
|
+
|
|
43
|
+
<!-- Connectors: all routes are painted before their nodes. -->
|
|
44
|
+
<path class="edge" d="M640 180 V212"/>
|
|
45
|
+
<path class="edge" d="M532 264 H320 Q312 264 312 272 V388 Q312 396 304 396 H184 Q176 396 176 404 V412"/>
|
|
46
|
+
<path class="edge" d="M640 316 V336 Q640 344 632 344 H432 Q424 344 424 352 V392"/>
|
|
47
|
+
<path class="edge" d="M748 264 H792 Q800 264 800 272 V328 Q800 336 808 336 H1016 Q1024 336 1024 344 V392"/>
|
|
48
|
+
<path class="edge" d="M536 448 H576"/>
|
|
49
|
+
<path class="edge" d="M424 504 V544"/>
|
|
50
|
+
<path class="edge" d="M1136 448 V536"/>
|
|
51
|
+
<path class="edge" d="M912 448 H896 Q888 448 888 456 V520 Q888 528 896 528 H912 Q920 528 920 536"/>
|
|
52
|
+
|
|
53
|
+
<!-- Branch labels stay 8px clear of their connectors. -->
|
|
54
|
+
<rect x="384" y="244" width="72" height="12" rx="2" fill="#f0eee9"/>
|
|
55
|
+
<text class="edge-label" x="420" y="252">PREPARE</text>
|
|
56
|
+
|
|
57
|
+
<rect x="488" y="324" width="112" height="12" rx="2" fill="#f0eee9"/>
|
|
58
|
+
<text class="edge-label" x="544" y="332">SEARCH / BROWSE</text>
|
|
59
|
+
|
|
60
|
+
<rect x="808" y="288" width="56" height="12" rx="2" fill="#f0eee9"/>
|
|
61
|
+
<text class="edge-label" x="836" y="296">INSPECT</text>
|
|
62
|
+
|
|
63
|
+
<rect x="544" y="428" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
64
|
+
<text class="edge-label" x="556" y="436">YES</text>
|
|
65
|
+
|
|
66
|
+
<rect x="432" y="516" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
67
|
+
<text class="edge-label" x="444" y="524">NO</text>
|
|
68
|
+
|
|
69
|
+
<rect x="1144" y="480" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
70
|
+
<text class="edge-label" x="1156" y="488">YES</text>
|
|
71
|
+
|
|
72
|
+
<rect x="896" y="480" width="24" height="12" rx="2" fill="#f0eee9"/>
|
|
73
|
+
<text class="edge-label" x="908" y="488">NO</text>
|
|
74
|
+
|
|
75
|
+
<!-- Primary nodes: one start, three decisions, and five terminal modes. -->
|
|
76
|
+
<rect x="540" y="132" width="200" height="48" rx="20" fill="#101828" fill-opacity="0.03" stroke="#4c5665" stroke-width="1"/>
|
|
77
|
+
<text class="technical-name" x="640" y="156">session_search</text>
|
|
78
|
+
<text class="node-sub" x="640" y="172">ONE LOCAL TOOL</text>
|
|
79
|
+
|
|
80
|
+
<polygon points="640,212 748,264 640,316 532,264" fill="#1d4ed8" fill-opacity="0.08" stroke="#1d4ed8" stroke-width="1.2"/>
|
|
81
|
+
<text class="node-name" x="640" y="260">Call family?</text>
|
|
82
|
+
<text class="node-sub" x="640" y="280">ROUTE BY ARGUMENTS</text>
|
|
83
|
+
|
|
84
|
+
<rect x="48" y="412" width="240" height="128" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
85
|
+
<text class="node-name" x="168" y="444">Pattern preparation</text>
|
|
86
|
+
<text class="node-sub" x="168" y="472">operation + scope · lazy sync</text>
|
|
87
|
+
<text class="node-sub" x="168" y="488">bounded user/assistant corpus</text>
|
|
88
|
+
<text class="node-sub" x="168" y="504">+ repository inventory · never tool results</text>
|
|
89
|
+
|
|
90
|
+
<polygon points="424,392 536,448 424,504 312,448" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
91
|
+
<text class="technical-name" x="424" y="444">query?</text>
|
|
92
|
+
<text class="node-sub" x="424" y="464">PRESENT</text>
|
|
93
|
+
|
|
94
|
+
<rect x="576" y="400" width="200" height="96" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
95
|
+
<text class="node-name" x="676" y="428">Discovery</text>
|
|
96
|
+
<text class="node-sub" x="676" y="452">query · lazy sync + FTS5</text>
|
|
97
|
+
<text class="node-sub" x="676" y="468">adaptive output omits tool results</text>
|
|
98
|
+
<text class="node-sub" x="676" y="484">detail:"full" includes tool results</text>
|
|
99
|
+
|
|
100
|
+
<rect x="328" y="544" width="192" height="96" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
101
|
+
<text class="node-name" x="424" y="572">Browse</text>
|
|
102
|
+
<text class="node-sub" x="424" y="596">no args · lazy sync</text>
|
|
103
|
+
<text class="node-sub" x="424" y="612">recent session metadata</text>
|
|
104
|
+
|
|
105
|
+
<polygon points="1024,392 1136,448 1024,504 912,448" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
106
|
+
<text class="technical-name" x="1024" y="444">aroundMessageId?</text>
|
|
107
|
+
<text class="node-sub" x="1024" y="464">WITH sessionId</text>
|
|
108
|
+
|
|
109
|
+
<rect x="1040" y="536" width="192" height="112" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
110
|
+
<text class="node-name" x="1136" y="564">Scroll</text>
|
|
111
|
+
<text class="node-sub" x="1136" y="588">sessionId + aroundMessageId</text>
|
|
112
|
+
<text class="node-sub" x="1136" y="604">direct saved JSONL</text>
|
|
113
|
+
<text class="node-sub" x="1136" y="620">raw roles including tool results</text>
|
|
114
|
+
<text class="node-sub" x="1136" y="636">±window with branchTip</text>
|
|
115
|
+
|
|
116
|
+
<rect x="824" y="536" width="192" height="112" rx="20" fill="#f0eee9" stroke="#101828" stroke-width="1"/>
|
|
117
|
+
<text class="node-name" x="920" y="564">Read</text>
|
|
118
|
+
<text class="node-sub" x="920" y="588">sessionId · direct saved JSONL</text>
|
|
119
|
+
<text class="node-sub" x="920" y="604">raw roles including tool results</text>
|
|
120
|
+
<text class="node-sub" x="920" y="620">head 20 + tail 10</text>
|
|
121
|
+
</svg>
|
package/extensions/hydrate.ts
CHANGED
|
@@ -25,6 +25,13 @@ export interface ReadResult {
|
|
|
25
25
|
messages: WindowMessage[];
|
|
26
26
|
totalMessages: number;
|
|
27
27
|
truncated: boolean;
|
|
28
|
+
/** Resolved leaf branch, or null when no selected message can be hydrated. */
|
|
29
|
+
branchTip: string | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ReadOptions {
|
|
33
|
+
/** Preparation view: retain only non-empty user/assistant text. */
|
|
34
|
+
userAssistantTextOnly?: boolean;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
interface Entry {
|
|
@@ -249,18 +256,24 @@ export function readSession(
|
|
|
249
256
|
sessionPath: string,
|
|
250
257
|
head = 20,
|
|
251
258
|
tail = 10,
|
|
259
|
+
opts?: ReadOptions,
|
|
252
260
|
): ReadResult {
|
|
253
261
|
const entries = parseSessionEntries(sessionPath);
|
|
254
262
|
const entriesById = new Map(entries.map((e) => [e.id, e]));
|
|
255
263
|
const leaf = leafId(entriesById, entries);
|
|
256
|
-
if (!leaf) return { messages: [], totalMessages: 0, truncated: false };
|
|
257
|
-
const
|
|
264
|
+
if (!leaf) return { messages: [], totalMessages: 0, truncated: false, branchTip: null };
|
|
265
|
+
const rawMessages = branchMessages(entriesById, leaf);
|
|
266
|
+
const msgs = opts?.userAssistantTextOnly
|
|
267
|
+
? rawMessages.filter((m) => (m.role === "user" || m.role === "assistant") && m.content.trim().length > 0)
|
|
268
|
+
: rawMessages;
|
|
269
|
+
const branchTip = msgs.length > 0 ? leaf : null;
|
|
258
270
|
if (msgs.length > head + tail) {
|
|
259
271
|
return {
|
|
260
272
|
messages: [...msgs.slice(0, head), ...msgs.slice(-tail)],
|
|
261
273
|
totalMessages: msgs.length,
|
|
262
274
|
truncated: true,
|
|
275
|
+
branchTip,
|
|
263
276
|
};
|
|
264
277
|
}
|
|
265
|
-
return { messages: msgs, totalMessages: msgs.length, truncated: false };
|
|
278
|
+
return { messages: msgs, totalMessages: msgs.length, truncated: false, branchTip };
|
|
266
279
|
}
|