@edeng23/pines 0.1.0
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/LICENSE +202 -0
- package/README.md +37 -0
- package/dist/cli.js +83 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/ansi.js +37 -0
- package/dist/client/ansi.js.map +1 -0
- package/dist/client/app.js +1868 -0
- package/dist/client/app.js.map +1 -0
- package/dist/client/boot.js +221 -0
- package/dist/client/boot.js.map +1 -0
- package/dist/client/commands.js +133 -0
- package/dist/client/commands.js.map +1 -0
- package/dist/client/daemon-client.js +99 -0
- package/dist/client/daemon-client.js.map +1 -0
- package/dist/client/forest/camera.js +88 -0
- package/dist/client/forest/camera.js.map +1 -0
- package/dist/client/forest/canvas.js +175 -0
- package/dist/client/forest/canvas.js.map +1 -0
- package/dist/client/forest/sidebar.js +182 -0
- package/dist/client/forest/sidebar.js.map +1 -0
- package/dist/client/forest/status.js +39 -0
- package/dist/client/forest/status.js.map +1 -0
- package/dist/client/forest/style.js +70 -0
- package/dist/client/forest/style.js.map +1 -0
- package/dist/client/forest/styles.js +429 -0
- package/dist/client/forest/styles.js.map +1 -0
- package/dist/client/forest/view.js +181 -0
- package/dist/client/forest/view.js.map +1 -0
- package/dist/client/forest/wordmark.js +26 -0
- package/dist/client/forest/wordmark.js.map +1 -0
- package/dist/client/input.js +89 -0
- package/dist/client/input.js.map +1 -0
- package/dist/client/theme.js +27 -0
- package/dist/client/theme.js.map +1 -0
- package/dist/client/tree/asciitree.js +277 -0
- package/dist/client/tree/asciitree.js.map +1 -0
- package/dist/client/tree/convmap.js +260 -0
- package/dist/client/tree/convmap.js.map +1 -0
- package/dist/client/tree/lanegraph.js +260 -0
- package/dist/client/tree/lanegraph.js.map +1 -0
- package/dist/client/tree/merge.js +161 -0
- package/dist/client/tree/merge.js.map +1 -0
- package/dist/client/tree/sessionview.js +163 -0
- package/dist/client/tree/sessionview.js.map +1 -0
- package/dist/client/tree/view.js +238 -0
- package/dist/client/tree/view.js.map +1 -0
- package/dist/client/uistate.js +45 -0
- package/dist/client/uistate.js.map +1 -0
- package/dist/daemon/agents.js +102 -0
- package/dist/daemon/agents.js.map +1 -0
- package/dist/daemon/branch.js +149 -0
- package/dist/daemon/branch.js.map +1 -0
- package/dist/daemon/embed-worker.js +74 -0
- package/dist/daemon/embed-worker.js.map +1 -0
- package/dist/daemon/essence.js +194 -0
- package/dist/daemon/essence.js.map +1 -0
- package/dist/daemon/extbridge.js +138 -0
- package/dist/daemon/extbridge.js.map +1 -0
- package/dist/daemon/lifecycle.js +134 -0
- package/dist/daemon/lifecycle.js.map +1 -0
- package/dist/daemon/pty-compat.js +53 -0
- package/dist/daemon/pty-compat.js.map +1 -0
- package/dist/daemon/semantic.js +337 -0
- package/dist/daemon/semantic.js.map +1 -0
- package/dist/daemon/server.js +808 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/supervisor.js +311 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/extension/pines-extension.js +191 -0
- package/dist/extension/pines-extension.js.map +1 -0
- package/dist/layout/lexical.js +104 -0
- package/dist/layout/lexical.js.map +1 -0
- package/dist/layout/pca.js +91 -0
- package/dist/layout/pca.js.map +1 -0
- package/dist/layout/relax.js +47 -0
- package/dist/layout/relax.js.map +1 -0
- package/dist/session/ingest.js +95 -0
- package/dist/session/ingest.js.map +1 -0
- package/dist/session/parser.js +254 -0
- package/dist/session/parser.js.map +1 -0
- package/dist/shared/config.js +30 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/paths.js +47 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/protocol.js +9 -0
- package/dist/shared/protocol.js.map +1 -0
- package/dist/shared/types.js +3 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/shared/wire.js +51 -0
- package/dist/shared/wire.js.map +1 -0
- package/dist/store/db.js +203 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/watcher.js +69 -0
- package/dist/store/watcher.js.map +1 -0
- package/package.json +80 -0
- package/scripts/fix-node-pty.mjs +34 -0
- package/src/extension/pines-extension.ts +192 -0
- package/src/store/schema.sql +60 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cold-start forest layout: no embeddings needed, deterministic, instant.
|
|
3
|
+
*
|
|
4
|
+
* Trees cluster by cwd (shared project → shared neighborhood); cluster
|
|
5
|
+
* anchors sit on a phyllotaxis spiral (evenly spread, no grid artifacts),
|
|
6
|
+
* and trees within a cluster ring around the anchor by recency.
|
|
7
|
+
*
|
|
8
|
+
* Only unpositioned trees (x=0, y=0) are placed — semantic layout (M5) and
|
|
9
|
+
* user relayouts overwrite positions later; existing trees never move here.
|
|
10
|
+
*/
|
|
11
|
+
const GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
|
|
12
|
+
const CLUSTER_SPACING = 14;
|
|
13
|
+
const RING_SPACING = 3.2;
|
|
14
|
+
function hashString(s) {
|
|
15
|
+
let h = 2166136261;
|
|
16
|
+
for (let i = 0; i < s.length; i++) {
|
|
17
|
+
h ^= s.charCodeAt(i);
|
|
18
|
+
h = Math.imul(h, 16777619);
|
|
19
|
+
}
|
|
20
|
+
return h >>> 0;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Sanity bound for persisted world coordinates. Real layouts live within
|
|
24
|
+
* ~±100; anything beyond this is corruption (e.g. the pre-fix relax()
|
|
25
|
+
* divide-by-zero wrote positions around ±1.5e9 into people's databases) and
|
|
26
|
+
* should be re-placed rather than trusted — one such tree makes zoom-to-fit
|
|
27
|
+
* frame a viewport a billion units wide.
|
|
28
|
+
*/
|
|
29
|
+
export const MAX_SANE_COORD = 10_000;
|
|
30
|
+
export function isSanePosition(x, y) {
|
|
31
|
+
return (Number.isFinite(x) && Number.isFinite(y) && Math.abs(x) <= MAX_SANE_COORD && Math.abs(y) <= MAX_SANE_COORD);
|
|
32
|
+
}
|
|
33
|
+
export function assignLexicalPositions(trees) {
|
|
34
|
+
const unplaced = trees.filter((t) => t.x === 0 && t.y === 0);
|
|
35
|
+
if (unplaced.length === 0)
|
|
36
|
+
return [];
|
|
37
|
+
// Cluster key: cwd (or a bucket for unknown).
|
|
38
|
+
const clusters = new Map();
|
|
39
|
+
for (const t of unplaced) {
|
|
40
|
+
const key = t.cwd ?? "~unknown";
|
|
41
|
+
let arr = clusters.get(key);
|
|
42
|
+
if (!arr)
|
|
43
|
+
clusters.set(key, (arr = []));
|
|
44
|
+
arr.push(t);
|
|
45
|
+
}
|
|
46
|
+
// Anchor per cluster on a deterministic phyllotaxis spiral, ordered by key
|
|
47
|
+
// hash so anchors are stable regardless of discovery order. Anchors of
|
|
48
|
+
// clusters that already have placed trees reuse their centroid.
|
|
49
|
+
const placedByCwd = new Map();
|
|
50
|
+
for (const t of trees) {
|
|
51
|
+
if (t.x === 0 && t.y === 0)
|
|
52
|
+
continue;
|
|
53
|
+
const key = t.cwd ?? "~unknown";
|
|
54
|
+
const c = placedByCwd.get(key) ?? { x: 0, y: 0, n: 0 };
|
|
55
|
+
c.x += t.x;
|
|
56
|
+
c.y += t.y;
|
|
57
|
+
c.n++;
|
|
58
|
+
placedByCwd.set(key, c);
|
|
59
|
+
}
|
|
60
|
+
const keys = [...clusters.keys()].sort((a, b) => hashString(a) - hashString(b));
|
|
61
|
+
const changed = [];
|
|
62
|
+
keys.forEach((key) => {
|
|
63
|
+
const members = clusters.get(key);
|
|
64
|
+
let ax;
|
|
65
|
+
let ay;
|
|
66
|
+
const placed = placedByCwd.get(key);
|
|
67
|
+
if (placed && placed.n > 0) {
|
|
68
|
+
ax = placed.x / placed.n;
|
|
69
|
+
ay = placed.y / placed.n;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Spiral slot derived purely from the key hash: stable regardless of
|
|
73
|
+
// discovery order, and a wide slot range keeps distinct clusters from
|
|
74
|
+
// landing on the same anchor (exact collisions are still safe — the
|
|
75
|
+
// overlap relaxation separates them).
|
|
76
|
+
const idx = 1 + (hashString(key) % 97);
|
|
77
|
+
const r = CLUSTER_SPACING * Math.sqrt(1 + (idx % 12));
|
|
78
|
+
const theta = idx * GOLDEN_ANGLE;
|
|
79
|
+
ax = r * Math.cos(theta);
|
|
80
|
+
ay = r * Math.sin(theta);
|
|
81
|
+
}
|
|
82
|
+
// Members ring around the anchor by recency (newest closest).
|
|
83
|
+
members.sort((a, b) => b.mtime - a.mtime);
|
|
84
|
+
members.forEach((t, mi) => {
|
|
85
|
+
if (mi === 0 && !placed) {
|
|
86
|
+
t.x = ax;
|
|
87
|
+
t.y = ay;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const ringIdx = placed ? mi + placed.n : mi;
|
|
91
|
+
const r = RING_SPACING * Math.sqrt(ringIdx + 1);
|
|
92
|
+
const theta = ringIdx * GOLDEN_ANGLE + hashString(t.treeId) % 7;
|
|
93
|
+
t.x = ax + r * Math.cos(theta);
|
|
94
|
+
t.y = ay + r * Math.sin(theta);
|
|
95
|
+
}
|
|
96
|
+
// (0,0) is the "unpositioned" sentinel — nudge exact zeros off it.
|
|
97
|
+
if (t.x === 0 && t.y === 0)
|
|
98
|
+
t.x = 0.001;
|
|
99
|
+
changed.push(t);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
return changed;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=lexical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lexical.js","sourceRoot":"","sources":["../../src/layout/lexical.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PCA projection: 384-d embeddings → 2-d forest coordinates.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic power iteration for the top-2 principal components. The basis
|
|
5
|
+
* (mean + two components) is cached; new trees project through the cached
|
|
6
|
+
* basis so existing trees never move when a tree is added.
|
|
7
|
+
*/
|
|
8
|
+
function dot(a, b) {
|
|
9
|
+
let s = 0;
|
|
10
|
+
for (let i = 0; i < a.length; i++)
|
|
11
|
+
s += a[i] * b[i];
|
|
12
|
+
return s;
|
|
13
|
+
}
|
|
14
|
+
function norm(a) {
|
|
15
|
+
return Math.sqrt(dot(a, a));
|
|
16
|
+
}
|
|
17
|
+
/** Deterministic unit start vector (no Math.random — reproducible layouts). */
|
|
18
|
+
function seedVector(d, seed) {
|
|
19
|
+
const v = new Array(d);
|
|
20
|
+
let state = seed >>> 0 || 1;
|
|
21
|
+
for (let i = 0; i < d; i++) {
|
|
22
|
+
// xorshift32
|
|
23
|
+
state ^= state << 13;
|
|
24
|
+
state ^= state >>> 17;
|
|
25
|
+
state ^= state << 5;
|
|
26
|
+
state >>>= 0;
|
|
27
|
+
v[i] = state / 0xffffffff - 0.5;
|
|
28
|
+
}
|
|
29
|
+
const n = norm(v);
|
|
30
|
+
for (let i = 0; i < d; i++)
|
|
31
|
+
v[i] /= n;
|
|
32
|
+
return v;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Power-iterate the covariance of `vectors` (n×d, centered internally) for the
|
|
36
|
+
* top component orthogonal to `deflate` (if given).
|
|
37
|
+
*/
|
|
38
|
+
function topComponent(centered, d, deflate, seed) {
|
|
39
|
+
let v = seedVector(d, seed);
|
|
40
|
+
for (let iter = 0; iter < 60; iter++) {
|
|
41
|
+
// w = Cov · v computed as Σ x (x·v) without materializing the d×d matrix.
|
|
42
|
+
const w = new Array(d).fill(0);
|
|
43
|
+
for (const x of centered) {
|
|
44
|
+
const p = dot(x, v);
|
|
45
|
+
for (let i = 0; i < d; i++)
|
|
46
|
+
w[i] += x[i] * p;
|
|
47
|
+
}
|
|
48
|
+
if (deflate) {
|
|
49
|
+
const p = dot(w, deflate);
|
|
50
|
+
for (let i = 0; i < d; i++)
|
|
51
|
+
w[i] -= deflate[i] * p;
|
|
52
|
+
}
|
|
53
|
+
const n = norm(w);
|
|
54
|
+
if (n < 1e-12)
|
|
55
|
+
break;
|
|
56
|
+
for (let i = 0; i < d; i++)
|
|
57
|
+
w[i] /= n;
|
|
58
|
+
v = w;
|
|
59
|
+
}
|
|
60
|
+
return v;
|
|
61
|
+
}
|
|
62
|
+
export function fitPca(vectors, targetSpan = 30) {
|
|
63
|
+
const n = vectors.length;
|
|
64
|
+
const d = vectors[0]?.length ?? 0;
|
|
65
|
+
const mean = new Array(d).fill(0);
|
|
66
|
+
for (const v of vectors)
|
|
67
|
+
for (let i = 0; i < d; i++)
|
|
68
|
+
mean[i] += v[i] / n;
|
|
69
|
+
const centered = vectors.map((v) => {
|
|
70
|
+
const c = new Array(d);
|
|
71
|
+
for (let i = 0; i < d; i++)
|
|
72
|
+
c[i] = v[i] - mean[i];
|
|
73
|
+
return c;
|
|
74
|
+
});
|
|
75
|
+
const c1 = topComponent(centered, d, null, 42);
|
|
76
|
+
const c2 = topComponent(centered, d, c1, 1337);
|
|
77
|
+
// Scale so the projected point cloud spans roughly `targetSpan` world units.
|
|
78
|
+
let maxAbs = 1e-9;
|
|
79
|
+
for (const x of centered) {
|
|
80
|
+
maxAbs = Math.max(maxAbs, Math.abs(dot(x, c1)), Math.abs(dot(x, c2)));
|
|
81
|
+
}
|
|
82
|
+
return { mean, c1, c2, scale: targetSpan / (2 * maxAbs) };
|
|
83
|
+
}
|
|
84
|
+
export function projectPca(basis, v) {
|
|
85
|
+
const d = basis.mean.length;
|
|
86
|
+
const c = new Array(d);
|
|
87
|
+
for (let i = 0; i < d; i++)
|
|
88
|
+
c[i] = v[i] - basis.mean[i];
|
|
89
|
+
return { x: dot(c, basis.c1) * basis.scale, y: dot(c, basis.c2) * basis.scale };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=pca.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pca.js","sourceRoot":"","sources":["../../src/layout/pca.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlap removal: repulsion-only local relaxation.
|
|
3
|
+
*
|
|
4
|
+
* Movable points are pushed apart until no pair sits closer than `minDist`;
|
|
5
|
+
* pinned points never move (existing trees stay put when new ones arrive).
|
|
6
|
+
*/
|
|
7
|
+
export function relax(points, minDist = 2.4, iterations = 30) {
|
|
8
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
9
|
+
let moved = false;
|
|
10
|
+
for (let i = 0; i < points.length; i++) {
|
|
11
|
+
const p = points[i];
|
|
12
|
+
if (p.pinned)
|
|
13
|
+
continue;
|
|
14
|
+
let pushX = 0;
|
|
15
|
+
let pushY = 0;
|
|
16
|
+
for (let j = 0; j < points.length; j++) {
|
|
17
|
+
if (i === j)
|
|
18
|
+
continue;
|
|
19
|
+
const q = points[j];
|
|
20
|
+
let dx = p.x - q.x;
|
|
21
|
+
let dy = p.y - q.y;
|
|
22
|
+
const dist = Math.hypot(dx, dy);
|
|
23
|
+
if (dist >= minDist)
|
|
24
|
+
continue;
|
|
25
|
+
if (dist < 1e-9) {
|
|
26
|
+
// Coincident: push a half-separation in a deterministic direction.
|
|
27
|
+
// (dx/dy are unit-length here — do NOT divide by the zero distance.)
|
|
28
|
+
const angle = (i * 2.399963) % (Math.PI * 2);
|
|
29
|
+
pushX += Math.cos(angle) * minDist * 0.5;
|
|
30
|
+
pushY += Math.sin(angle) * minDist * 0.5;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const overlap = (minDist - dist) / dist;
|
|
34
|
+
pushX += dx * overlap * 0.5;
|
|
35
|
+
pushY += dy * overlap * 0.5;
|
|
36
|
+
}
|
|
37
|
+
if (pushX !== 0 || pushY !== 0) {
|
|
38
|
+
p.x += pushX;
|
|
39
|
+
p.y += pushY;
|
|
40
|
+
moved = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!moved)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=relax.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relax.js","sourceRoot":"","sources":["../../src/layout/relax.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,UAAU,KAAK,CAAC,MAAoB,EAAE,OAAO,GAAG,GAAG,EAAE,UAAU,GAAG,EAAE;IACxE,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;QAC7C,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;YACrB,IAAI,CAAC,CAAC,MAAM;gBAAE,SAAS;YACvB,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACvC,IAAI,CAAC,KAAK,CAAC;oBAAE,SAAS;gBACtB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;gBACrB,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACnB,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACnB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,IAAI,IAAI,OAAO;oBAAE,SAAS;gBAC9B,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;oBAChB,mEAAmE;oBACnE,qEAAqE;oBACrE,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;oBAC7C,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,GAAG,CAAC;oBACzC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,GAAG,CAAC;oBACzC,SAAS;gBACX,CAAC;gBACD,MAAM,OAAO,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;gBACxC,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,GAAG,CAAC;gBAC5B,KAAK,IAAI,EAAE,GAAG,OAAO,GAAG,GAAG,CAAC;YAC9B,CAAC;YACD,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAC/B,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;gBACb,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;gBACb,KAAK,GAAG,IAAI,CAAC;YACf,CAAC;QACH,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,MAAM;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental ingest: session JSONL → trees row + FTS index.
|
|
3
|
+
*
|
|
4
|
+
* JSONL is append-only, so we index only entries that appeared after the
|
|
5
|
+
* stored ingest_offset. A file that shrank (e.g. replaced by a fork flow)
|
|
6
|
+
* triggers a full re-ingest for that tree.
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { readFileSync, statSync } from "node:fs";
|
|
10
|
+
import { deleteFtsForTree, insertFts, treeBySessionPath, upsertTree, } from "../store/db.js";
|
|
11
|
+
import { parseSessionContent, parseSessionFile } from "./parser.js";
|
|
12
|
+
function ftsRowsFrom(parsed, treeId, sinceByte) {
|
|
13
|
+
// We re-derive which entries are "new" by parsing only the appended slice in
|
|
14
|
+
// the caller; here parsed is already just that slice (or the whole file).
|
|
15
|
+
void sinceByte;
|
|
16
|
+
const rows = [];
|
|
17
|
+
for (const e of parsed.entries) {
|
|
18
|
+
if (e.type === "message" && e.role === "user" && e.text) {
|
|
19
|
+
rows.push({ text: e.text, treeId, entryId: e.id, kind: "user_msg" });
|
|
20
|
+
}
|
|
21
|
+
else if ((e.type === "compaction" || e.type === "branch_summary") && e.text) {
|
|
22
|
+
rows.push({ text: e.text, treeId, entryId: e.id, kind: "summary" });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const [entryId, label] of parsed.labels) {
|
|
26
|
+
rows.push({ text: label, treeId, entryId, kind: "label" });
|
|
27
|
+
}
|
|
28
|
+
return rows;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Ingest one session file. Returns the treeId (existing or newly assigned)
|
|
32
|
+
* and the full parse of the file. `preferredTreeId` is used only when the
|
|
33
|
+
* session has no row yet (lets the daemon keep a spawn-time provisional id).
|
|
34
|
+
*/
|
|
35
|
+
export async function ingestSessionFile(db, sessionPath, opts) {
|
|
36
|
+
const st = statSync(sessionPath);
|
|
37
|
+
const existing = treeBySessionPath(db, sessionPath);
|
|
38
|
+
const full = await parseSessionFile(sessionPath);
|
|
39
|
+
let treeId;
|
|
40
|
+
let isNew = false;
|
|
41
|
+
let newRows;
|
|
42
|
+
if (!existing) {
|
|
43
|
+
treeId = opts?.preferredTreeId ?? `t_${randomUUID().slice(0, 8)}`;
|
|
44
|
+
isNew = true;
|
|
45
|
+
newRows = ftsRowsFrom(full, treeId, 0);
|
|
46
|
+
if (full.name)
|
|
47
|
+
newRows.push({ text: full.name, treeId, entryId: null, kind: "name" });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
treeId = existing.tree_id;
|
|
51
|
+
if (st.size < existing.ingest_offset) {
|
|
52
|
+
// File shrank: full re-index.
|
|
53
|
+
deleteFtsForTree(db, treeId);
|
|
54
|
+
newRows = ftsRowsFrom(full, treeId, 0);
|
|
55
|
+
if (full.name)
|
|
56
|
+
newRows.push({ text: full.name, treeId, entryId: null, kind: "name" });
|
|
57
|
+
}
|
|
58
|
+
else if (full.completeBytes > existing.ingest_offset) {
|
|
59
|
+
// Parse only the appended slice for FTS deltas (labels in the slice are
|
|
60
|
+
// additive; full.labels remains authoritative for display).
|
|
61
|
+
const slice = readFileSync(sessionPath).subarray(existing.ingest_offset).toString("utf8");
|
|
62
|
+
const appended = await parseSessionContent(slice);
|
|
63
|
+
newRows = ftsRowsFrom(appended, treeId, existing.ingest_offset);
|
|
64
|
+
const knownName = existing.name ?? null;
|
|
65
|
+
if (full.name && full.name !== knownName) {
|
|
66
|
+
newRows.push({ text: full.name, treeId, entryId: null, kind: "name" });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
newRows = [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
upsertTree(db, {
|
|
74
|
+
tree_id: treeId,
|
|
75
|
+
session_path: sessionPath,
|
|
76
|
+
session_id: full.header.sessionId,
|
|
77
|
+
name: full.name,
|
|
78
|
+
cwd: full.header.cwd,
|
|
79
|
+
parent_session_path: full.header.parentSession,
|
|
80
|
+
leaf_id: full.leafId,
|
|
81
|
+
node_count: full.entries.length,
|
|
82
|
+
mtime: Math.floor(st.mtimeMs),
|
|
83
|
+
ingest_offset: full.completeBytes,
|
|
84
|
+
});
|
|
85
|
+
if (isNew) {
|
|
86
|
+
// Lineage row (ordinal 0) — graft (v2) adds more ordinals.
|
|
87
|
+
if (full.header.parentSession) {
|
|
88
|
+
db.prepare("INSERT OR IGNORE INTO graft_parents (tree_id, parent_session_path, parent_entry_id, ordinal) VALUES (?, ?, NULL, 0)").run(treeId, full.header.parentSession);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (newRows.length > 0)
|
|
92
|
+
insertFts(db, newRows);
|
|
93
|
+
return { treeId, parsed: full, isNew };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/session/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session JSONL → tree. This is the isolation layer around pi's session format:
|
|
3
|
+
* all @earendil-works imports for *reading* sessions live here (R3), and the
|
|
4
|
+
* parser tolerates a torn trailing line (a live pi may be mid-append, R8).
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, statSync } from "node:fs";
|
|
7
|
+
let piModule;
|
|
8
|
+
function pi() {
|
|
9
|
+
piModule ??= import("@earendil-works/pi-coding-agent");
|
|
10
|
+
return piModule;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extract display text from a pi message-ish object. Content is a string or
|
|
14
|
+
* an array of blocks — text, thinking {thinking}, toolCall {name}, image.
|
|
15
|
+
* An assistant turn that only thinks and calls tools has no text block, so
|
|
16
|
+
* fall through: text, else the tool calls, else the thinking, else images.
|
|
17
|
+
*/
|
|
18
|
+
function textOf(obj) {
|
|
19
|
+
if (typeof obj === "string")
|
|
20
|
+
return obj;
|
|
21
|
+
if (!Array.isArray(obj))
|
|
22
|
+
return "";
|
|
23
|
+
const texts = [];
|
|
24
|
+
const calls = [];
|
|
25
|
+
const thinking = [];
|
|
26
|
+
let images = 0;
|
|
27
|
+
for (const part of obj) {
|
|
28
|
+
if (typeof part !== "object" || part === null)
|
|
29
|
+
continue;
|
|
30
|
+
const p = part;
|
|
31
|
+
if (typeof p.text === "string" && p.text.trim())
|
|
32
|
+
texts.push(p.text);
|
|
33
|
+
else if (p.type === "toolCall")
|
|
34
|
+
calls.push(`${String(p.name ?? "tool")}(…)`);
|
|
35
|
+
else if (typeof p.thinking === "string" && p.thinking.trim())
|
|
36
|
+
thinking.push(p.thinking);
|
|
37
|
+
else if (p.type === "image")
|
|
38
|
+
images += 1;
|
|
39
|
+
}
|
|
40
|
+
if (texts.length > 0)
|
|
41
|
+
return texts.join(" ");
|
|
42
|
+
if (calls.length > 0)
|
|
43
|
+
return calls.join(", ");
|
|
44
|
+
if (thinking.length > 0)
|
|
45
|
+
return thinking.join(" ");
|
|
46
|
+
if (images > 0)
|
|
47
|
+
return images === 1 ? "[image]" : `[${images} images]`;
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
function entryText(raw) {
|
|
51
|
+
const type = raw.type;
|
|
52
|
+
if (type === "message") {
|
|
53
|
+
const msg = raw.message;
|
|
54
|
+
const role = msg?.role ?? null;
|
|
55
|
+
return { role, text: textOf(msg?.content).trim() };
|
|
56
|
+
}
|
|
57
|
+
if (type === "compaction" || type === "branch_summary") {
|
|
58
|
+
return { role: null, text: String(raw.summary ?? "").trim() };
|
|
59
|
+
}
|
|
60
|
+
if (type === "custom_message") {
|
|
61
|
+
return { role: "custom", text: textOf(raw.content).trim() };
|
|
62
|
+
}
|
|
63
|
+
if (type === "session_info") {
|
|
64
|
+
return { role: null, text: String(raw.name ?? "").trim() };
|
|
65
|
+
}
|
|
66
|
+
// Settings bookkeeping that pi records as tree entries. Normally spliced
|
|
67
|
+
// out of the lane graph; readable when surfaced (e.g. labeled).
|
|
68
|
+
if (type === "model_change") {
|
|
69
|
+
return { role: null, text: `model → ${String(raw.modelId ?? "?")}` };
|
|
70
|
+
}
|
|
71
|
+
if (type === "thinking_level_change") {
|
|
72
|
+
return { role: null, text: `thinking → ${String(raw.thinkingLevel ?? "?")}` };
|
|
73
|
+
}
|
|
74
|
+
if (type === "active_tools_change") {
|
|
75
|
+
return { role: null, text: "active tools changed" };
|
|
76
|
+
}
|
|
77
|
+
return { role: null, text: "" };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse raw JSONL content. Lines are split on LF only; an incomplete trailing
|
|
81
|
+
* line is ignored and excluded from `completeBytes`.
|
|
82
|
+
*/
|
|
83
|
+
export async function parseSessionContent(content) {
|
|
84
|
+
const { migrateSessionEntries } = await pi();
|
|
85
|
+
const header = { sessionId: null, cwd: null, parentSession: null };
|
|
86
|
+
let headerRaw;
|
|
87
|
+
const rawEntries = [];
|
|
88
|
+
let completeBytes = 0;
|
|
89
|
+
let cursor = 0;
|
|
90
|
+
while (cursor < content.length) {
|
|
91
|
+
const nl = content.indexOf("\n", cursor);
|
|
92
|
+
if (nl < 0)
|
|
93
|
+
break; // torn tail — a writer is mid-append
|
|
94
|
+
const line = content.slice(cursor, nl);
|
|
95
|
+
const lineEnd = nl + 1;
|
|
96
|
+
cursor = lineEnd;
|
|
97
|
+
const trimmed = line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
98
|
+
if (trimmed.length === 0) {
|
|
99
|
+
completeBytes = Buffer.byteLength(content.slice(0, lineEnd), "utf8");
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
let obj;
|
|
103
|
+
try {
|
|
104
|
+
obj = JSON.parse(trimmed);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// A malformed complete line should not appear in an append-only file;
|
|
108
|
+
// stop advancing the cursor so a future re-read can retry.
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
completeBytes = Buffer.byteLength(content.slice(0, lineEnd), "utf8");
|
|
112
|
+
if (obj.type === "session") {
|
|
113
|
+
headerRaw = obj;
|
|
114
|
+
header.sessionId = obj.id ?? null;
|
|
115
|
+
header.cwd = obj.cwd ?? null;
|
|
116
|
+
header.parentSession = obj.parentSession ?? null;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
rawEntries.push(obj);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Let pi migrate v1/v2 shapes in place. The header MUST be in the array —
|
|
123
|
+
// version detection reads it (headerless input would be "migrated" as v1,
|
|
124
|
+
// rewriting every id). Unknown future entry types (e.g. the newer "leaf"
|
|
125
|
+
// pointer) pass through untouched.
|
|
126
|
+
try {
|
|
127
|
+
const fileEntries = headerRaw ? [headerRaw, ...rawEntries] : rawEntries;
|
|
128
|
+
migrateSessionEntries(fileEntries);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Migration is best-effort; raw v3 files don't need it.
|
|
132
|
+
}
|
|
133
|
+
const entries = [];
|
|
134
|
+
const order = [];
|
|
135
|
+
const labels = new Map();
|
|
136
|
+
let name = null;
|
|
137
|
+
let leafFromLeafEntry;
|
|
138
|
+
let firstUserText = null;
|
|
139
|
+
const summaries = [];
|
|
140
|
+
for (const raw of rawEntries) {
|
|
141
|
+
const type = raw.type;
|
|
142
|
+
if (type === "leaf") {
|
|
143
|
+
// Newer agent-core persists the active leaf pointer as an entry.
|
|
144
|
+
leafFromLeafEntry = raw.targetId ?? null;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (type === "label") {
|
|
148
|
+
const target = raw.targetId;
|
|
149
|
+
const label = raw.label;
|
|
150
|
+
if (label)
|
|
151
|
+
labels.set(target, label);
|
|
152
|
+
else
|
|
153
|
+
labels.delete(target);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (type === "session_info") {
|
|
157
|
+
name = raw.name ?? name;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const id = raw.id;
|
|
161
|
+
if (!id)
|
|
162
|
+
continue;
|
|
163
|
+
const { role, text } = entryText(raw);
|
|
164
|
+
if (type === "compaction" || type === "branch_summary") {
|
|
165
|
+
if (text)
|
|
166
|
+
summaries.push(text);
|
|
167
|
+
}
|
|
168
|
+
if (role === "user" && firstUserText === null && text)
|
|
169
|
+
firstUserText = text;
|
|
170
|
+
entries.push({
|
|
171
|
+
id,
|
|
172
|
+
parentId: raw.parentId ?? null,
|
|
173
|
+
type,
|
|
174
|
+
timestamp: raw.timestamp ?? "",
|
|
175
|
+
role,
|
|
176
|
+
text,
|
|
177
|
+
raw,
|
|
178
|
+
});
|
|
179
|
+
order.push(id);
|
|
180
|
+
}
|
|
181
|
+
// Leaf: an explicit leaf entry wins; otherwise the last appended tree entry
|
|
182
|
+
// (pi reconstructs the leaf the same way on load).
|
|
183
|
+
const leafId = leafFromLeafEntry !== undefined ? leafFromLeafEntry : (order[order.length - 1] ?? null);
|
|
184
|
+
return { header, entries, order, name, labels, leafId, completeBytes, firstUserText, summaries };
|
|
185
|
+
}
|
|
186
|
+
const cache = new Map();
|
|
187
|
+
const CACHE_MAX = 64;
|
|
188
|
+
export async function parseSessionFile(path) {
|
|
189
|
+
const st = statSync(path);
|
|
190
|
+
const hit = cache.get(path);
|
|
191
|
+
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
|
|
192
|
+
return hit.parsed;
|
|
193
|
+
const parsed = await parseSessionContent(readFileSync(path, "utf8"));
|
|
194
|
+
cache.set(path, { mtimeMs: st.mtimeMs, size: st.size, parsed });
|
|
195
|
+
if (cache.size > CACHE_MAX) {
|
|
196
|
+
const oldest = cache.keys().next().value;
|
|
197
|
+
if (oldest !== undefined)
|
|
198
|
+
cache.delete(oldest);
|
|
199
|
+
}
|
|
200
|
+
return parsed;
|
|
201
|
+
}
|
|
202
|
+
/** Which entry types appear as nodes in tree views. */
|
|
203
|
+
function nodeKind(e) {
|
|
204
|
+
if (e.type === "message") {
|
|
205
|
+
if (e.role === "user")
|
|
206
|
+
return "user";
|
|
207
|
+
if (e.role === "assistant")
|
|
208
|
+
return "assistant";
|
|
209
|
+
if (e.role === "toolResult")
|
|
210
|
+
return "tool";
|
|
211
|
+
return "other";
|
|
212
|
+
}
|
|
213
|
+
if (e.type === "compaction")
|
|
214
|
+
return "compaction";
|
|
215
|
+
if (e.type === "branch_summary")
|
|
216
|
+
return "branch_summary";
|
|
217
|
+
if (e.type === "custom" || e.type === "custom_message")
|
|
218
|
+
return "custom";
|
|
219
|
+
// Settings changes are plumbing, not conversation: give them the same
|
|
220
|
+
// spliceable kind as pines' own markers so trees don't open with a stack
|
|
221
|
+
// of unreadable bookkeeping rows.
|
|
222
|
+
if (e.type === "model_change" ||
|
|
223
|
+
e.type === "thinking_level_change" ||
|
|
224
|
+
e.type === "active_tools_change") {
|
|
225
|
+
return "custom";
|
|
226
|
+
}
|
|
227
|
+
return "other";
|
|
228
|
+
}
|
|
229
|
+
const EXCERPT_LEN = 80;
|
|
230
|
+
export function toTreeDetail(treeId, parsed) {
|
|
231
|
+
const nodes = {};
|
|
232
|
+
const rootIds = [];
|
|
233
|
+
for (const e of parsed.entries) {
|
|
234
|
+
nodes[e.id] = {
|
|
235
|
+
id: e.id,
|
|
236
|
+
parentId: e.parentId,
|
|
237
|
+
kind: nodeKind(e),
|
|
238
|
+
excerpt: e.text.replace(/\s+/g, " ").slice(0, EXCERPT_LEN),
|
|
239
|
+
label: parsed.labels.get(e.id) ?? null,
|
|
240
|
+
timestamp: e.timestamp,
|
|
241
|
+
children: [],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
for (const e of parsed.entries) {
|
|
245
|
+
if (e.parentId && nodes[e.parentId]) {
|
|
246
|
+
nodes[e.parentId].children.push(e.id);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
rootIds.push(e.id);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return { treeId, rootIds, nodes, leafId: parsed.leafId };
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/session/parser.ts"],"names":[],"mappings":"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"}
|