@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,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The family a tree belongs to: walk up parentSessionPath to the original,
|
|
3
|
+
* then collect every descendant depth-first. Sibling order is by session
|
|
4
|
+
* path — a stable key, so merge order (and with it node ownership) never
|
|
5
|
+
* shuffles just because activity bumped a file's mtime.
|
|
6
|
+
*/
|
|
7
|
+
export function familyOf(treeId, summaryOf, childrenOf, parentOf) {
|
|
8
|
+
const start = summaryOf(treeId);
|
|
9
|
+
if (!start)
|
|
10
|
+
return [];
|
|
11
|
+
let root = start;
|
|
12
|
+
const up = new Set([root.treeId]);
|
|
13
|
+
for (let guard = 0; guard < 64; guard++) {
|
|
14
|
+
const parent = root.parentSessionPath ? parentOf(root.parentSessionPath) : undefined;
|
|
15
|
+
if (!parent || up.has(parent.treeId))
|
|
16
|
+
break;
|
|
17
|
+
up.add(parent.treeId);
|
|
18
|
+
root = parent;
|
|
19
|
+
}
|
|
20
|
+
const out = [];
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
const walk = (t) => {
|
|
23
|
+
if (seen.has(t.treeId))
|
|
24
|
+
return;
|
|
25
|
+
seen.add(t.treeId);
|
|
26
|
+
out.push(t);
|
|
27
|
+
for (const kid of [...childrenOf(t.sessionPath)].sort((a, b) => a.sessionPath < b.sessionPath ? -1 : a.sessionPath > b.sessionPath ? 1 : 0)) {
|
|
28
|
+
walk(kid);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
walk(root);
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Union the family's session trees into one. First writer wins per node id
|
|
36
|
+
* (the original is merged first, so shared prefixes keep their canonical
|
|
37
|
+
* content); children lists union, then sort by the child message's own
|
|
38
|
+
* timestamp — branches sit in the order they were CREATED, an order nothing
|
|
39
|
+
* (attaching, labeling, agent activity) can later disturb.
|
|
40
|
+
*
|
|
41
|
+
* The merged leafId — the path the ascii tree treats as "the story so far" —
|
|
42
|
+
* is the tip of the most recently active loaded session.
|
|
43
|
+
*/
|
|
44
|
+
export function mergeConversation(members) {
|
|
45
|
+
const loaded = members.filter((m) => m.detail);
|
|
46
|
+
const nodes = {};
|
|
47
|
+
const childOrder = new Map();
|
|
48
|
+
const rootIds = [];
|
|
49
|
+
for (const m of loaded) {
|
|
50
|
+
const d = m.detail;
|
|
51
|
+
for (const r of d.rootIds)
|
|
52
|
+
if (!rootIds.includes(r))
|
|
53
|
+
rootIds.push(r);
|
|
54
|
+
for (const [id, n] of Object.entries(d.nodes)) {
|
|
55
|
+
const existing = nodes[id];
|
|
56
|
+
if (!existing) {
|
|
57
|
+
nodes[id] = { ...n, children: [...n.children] };
|
|
58
|
+
childOrder.set(id, [...n.children]);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const order = childOrder.get(id);
|
|
62
|
+
for (const c of n.children)
|
|
63
|
+
if (!order.includes(c))
|
|
64
|
+
order.push(c);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const [id, order] of childOrder) {
|
|
69
|
+
// Drop dangling child ids (a member's detail may still be in flight);
|
|
70
|
+
// creation order among siblings (stable sort — union order breaks ties).
|
|
71
|
+
nodes[id].children = order
|
|
72
|
+
.filter((c) => nodes[c])
|
|
73
|
+
.sort((a, b) => {
|
|
74
|
+
const ta = nodes[a].timestamp;
|
|
75
|
+
const tb = nodes[b].timestamp;
|
|
76
|
+
return ta < tb ? -1 : ta > tb ? 1 : 0;
|
|
77
|
+
});
|
|
78
|
+
for (const c of nodes[id].children) {
|
|
79
|
+
if (nodes[c] && nodes[c].parentId == null && !rootIds.includes(c)) {
|
|
80
|
+
nodes[c] = { ...nodes[c], parentId: id };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const tips = [];
|
|
85
|
+
const tipsAt = new Map();
|
|
86
|
+
for (const m of loaded) {
|
|
87
|
+
const leaf = m.detail.leafId;
|
|
88
|
+
if (!leaf || !nodes[leaf])
|
|
89
|
+
continue;
|
|
90
|
+
tips.push({ nodeId: leaf, summary: m.summary, parked: nodes[leaf].children.length > 0 });
|
|
91
|
+
const at = tipsAt.get(leaf);
|
|
92
|
+
if (at)
|
|
93
|
+
at.push(m.summary);
|
|
94
|
+
else
|
|
95
|
+
tipsAt.set(leaf, [m.summary]);
|
|
96
|
+
}
|
|
97
|
+
// Story ends at the busiest recent branch: running beats waiting beats
|
|
98
|
+
// dormant; recency breaks ties. Parked tips sit mid-tree and can never be
|
|
99
|
+
// "the end of the story" — a label append touching an old file must not
|
|
100
|
+
// yank the green ● into the middle of the conversation.
|
|
101
|
+
const rank = (t) => t.parked
|
|
102
|
+
? -1
|
|
103
|
+
: t.summary.status === "running"
|
|
104
|
+
? 3
|
|
105
|
+
: !t.summary.seen
|
|
106
|
+
? 2
|
|
107
|
+
: t.summary.live
|
|
108
|
+
? 1
|
|
109
|
+
: 0;
|
|
110
|
+
const activeTip = [...tips].sort((a, b) => rank(b) - rank(a) || b.summary.mtime - a.summary.mtime)[0];
|
|
111
|
+
return {
|
|
112
|
+
detail: {
|
|
113
|
+
treeId: members[0]?.summary.treeId ?? "",
|
|
114
|
+
leafId: activeTip?.nodeId ?? loaded[0]?.detail.leafId ?? null,
|
|
115
|
+
rootIds,
|
|
116
|
+
nodes,
|
|
117
|
+
},
|
|
118
|
+
tips,
|
|
119
|
+
tipsAt,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Aggregate a family into one forest-facing summary (the root's identity,
|
|
124
|
+
* the family's most attention-worthy state). The forest shows conversations,
|
|
125
|
+
* not session files.
|
|
126
|
+
*/
|
|
127
|
+
export function aggregateFamily(members) {
|
|
128
|
+
const root = members[0];
|
|
129
|
+
const pick = (pred) => members.filter(pred).sort((a, b) => b.mtime - a.mtime)[0];
|
|
130
|
+
const crashed = pick((t) => t.status === "crashed");
|
|
131
|
+
const needsInput = pick((t) => t.status === "waiting" && !t.seen);
|
|
132
|
+
const working = pick((t) => t.status === "running");
|
|
133
|
+
const waiting = pick((t) => t.status === "waiting");
|
|
134
|
+
const lead = crashed ?? needsInput ?? working ?? waiting ?? pick(() => true);
|
|
135
|
+
return {
|
|
136
|
+
...root,
|
|
137
|
+
status: lead.status,
|
|
138
|
+
seen: lead.seen,
|
|
139
|
+
live: members.some((t) => t.live),
|
|
140
|
+
mtime: Math.max(...members.map((t) => t.mtime)),
|
|
141
|
+
nodeCount: members.reduce((n, t) => n + t.nodeCount, 0),
|
|
142
|
+
lastExitCode: lead.lastExitCode,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The member to attach to when the user targets the conversation as a whole:
|
|
147
|
+
* whoever needs attention, else the most recently active.
|
|
148
|
+
*/
|
|
149
|
+
export function attentionMemberOf(members) {
|
|
150
|
+
const rank = (t) => t.status === "crashed"
|
|
151
|
+
? 0
|
|
152
|
+
: t.status === "waiting" && !t.seen
|
|
153
|
+
? 1
|
|
154
|
+
: t.status === "running"
|
|
155
|
+
? 2
|
|
156
|
+
: t.status === "waiting"
|
|
157
|
+
? 3
|
|
158
|
+
: 4;
|
|
159
|
+
return [...members].sort((a, b) => rank(a) - rank(b) || b.mtime - a.mtime)[0];
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../src/client/tree/merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { aContainerOf, aRowIndexOf, buildAsciiTree, } from "./asciitree.js";
|
|
2
|
+
import { familyOf, mergeConversation } from "./merge.js";
|
|
3
|
+
import { buildSkeleton } from "./convmap.js";
|
|
4
|
+
/**
|
|
5
|
+
* The first node unique to a forked session: the child of its fork point on
|
|
6
|
+
* its own path — the user message that began the branch. Null for parked
|
|
7
|
+
* forks (nothing unique yet) and for the original session.
|
|
8
|
+
*/
|
|
9
|
+
export function branchHeadOf(m) {
|
|
10
|
+
const forkPoint = m.summary.parentEntryId;
|
|
11
|
+
const detail = m.detail;
|
|
12
|
+
if (!forkPoint || !detail?.leafId)
|
|
13
|
+
return null;
|
|
14
|
+
let cur = detail.leafId;
|
|
15
|
+
for (let guard = 0; cur && guard < 10_000; guard++) {
|
|
16
|
+
const node = detail.nodes[cur];
|
|
17
|
+
if (!node)
|
|
18
|
+
return null;
|
|
19
|
+
if (node.parentId === forkPoint)
|
|
20
|
+
return cur;
|
|
21
|
+
cur = node.parentId;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Follow the current exchange to its end: from `nodeId`, walk the original
|
|
27
|
+
* continuation (first child) through tool calls and replies, stopping before
|
|
28
|
+
* the next user turn. Branching from a question then lands AFTER its answer —
|
|
29
|
+
* a user message begins the new branch; it never displaces the reply.
|
|
30
|
+
*/
|
|
31
|
+
export function exchangeEndOf(detail, nodeId) {
|
|
32
|
+
let cur = nodeId;
|
|
33
|
+
for (let guard = 0; guard < 10_000; guard++) {
|
|
34
|
+
const first = detail.nodes[cur]?.children?.[0];
|
|
35
|
+
const next = first ? detail.nodes[first] : undefined;
|
|
36
|
+
if (!next)
|
|
37
|
+
return cur;
|
|
38
|
+
if (next.kind === "user" || next.kind === "compaction" || next.kind === "branch_summary") {
|
|
39
|
+
return cur;
|
|
40
|
+
}
|
|
41
|
+
cur = first;
|
|
42
|
+
}
|
|
43
|
+
return cur;
|
|
44
|
+
}
|
|
45
|
+
export function buildConvView(input) {
|
|
46
|
+
const family = familyOf(input.rootTreeId, input.summaryOf, input.childrenOf, input.parentOf);
|
|
47
|
+
const members = family.map((summary) => {
|
|
48
|
+
const detail = input.detailOf(summary.treeId);
|
|
49
|
+
if (!detail)
|
|
50
|
+
input.wantDetail?.(summary.treeId);
|
|
51
|
+
return { summary, detail };
|
|
52
|
+
});
|
|
53
|
+
const pendingMembers = members.filter((m) => !m.detail).length;
|
|
54
|
+
const merged = mergeConversation(members);
|
|
55
|
+
// A dormant fork parked mid-tree adds no content — it is bookkeeping, not
|
|
56
|
+
// conversation. It leaves the transcript, the panel and the mini entirely;
|
|
57
|
+
// Enter on its node quietly RESUMES it instead of minting another file.
|
|
58
|
+
const shown = merged.tips.filter((t) => !t.parked || t.summary.live || !t.summary.seen);
|
|
59
|
+
const parkedAt = new Map();
|
|
60
|
+
let parkedCount = 0;
|
|
61
|
+
for (const t of merged.tips) {
|
|
62
|
+
if (shown.includes(t))
|
|
63
|
+
continue;
|
|
64
|
+
parkedCount++;
|
|
65
|
+
const at = parkedAt.get(t.nodeId);
|
|
66
|
+
if (at)
|
|
67
|
+
at.push(t.summary);
|
|
68
|
+
else
|
|
69
|
+
parkedAt.set(t.nodeId, [t.summary]);
|
|
70
|
+
}
|
|
71
|
+
const shownAt = new Map();
|
|
72
|
+
for (const t of shown) {
|
|
73
|
+
const at = shownAt.get(t.nodeId);
|
|
74
|
+
if (at)
|
|
75
|
+
at.push(t.summary);
|
|
76
|
+
else
|
|
77
|
+
shownAt.set(t.nodeId, [t.summary]);
|
|
78
|
+
}
|
|
79
|
+
const atree = buildAsciiTree(merged.detail, {
|
|
80
|
+
expandedRuns: input.expandedRuns,
|
|
81
|
+
unfolded: input.unfolded,
|
|
82
|
+
viewportH: input.viewportH,
|
|
83
|
+
now: input.now,
|
|
84
|
+
keep: new Set(shownAt.keys()),
|
|
85
|
+
});
|
|
86
|
+
const rows = atree.rows.map((a) => ({
|
|
87
|
+
a,
|
|
88
|
+
tips: a.nodeId ? shownAt.get(a.nodeId) : undefined,
|
|
89
|
+
}));
|
|
90
|
+
// Panel order = transcript order: row k in the panel is the k-th agent
|
|
91
|
+
// you meet reading the tree top to bottom (creation order is history,
|
|
92
|
+
// not geography).
|
|
93
|
+
const rowIndexOf = new Map();
|
|
94
|
+
atree.rows.forEach((r, i) => {
|
|
95
|
+
if (r.nodeId && !rowIndexOf.has(r.nodeId))
|
|
96
|
+
rowIndexOf.set(r.nodeId, i);
|
|
97
|
+
});
|
|
98
|
+
const headByTree = new Map();
|
|
99
|
+
for (const m of members) {
|
|
100
|
+
const head = branchHeadOf(m);
|
|
101
|
+
if (head)
|
|
102
|
+
headByTree.set(m.summary.treeId, head);
|
|
103
|
+
}
|
|
104
|
+
const tips = [...shown]
|
|
105
|
+
.sort((a, b) => (rowIndexOf.get(a.nodeId) ?? Number.MAX_SAFE_INTEGER) -
|
|
106
|
+
(rowIndexOf.get(b.nodeId) ?? Number.MAX_SAFE_INTEGER))
|
|
107
|
+
.map((t, i) => {
|
|
108
|
+
const headNodeId = headByTree.get(t.summary.treeId);
|
|
109
|
+
const head = headNodeId ? merged.detail.nodes[headNodeId] : undefined;
|
|
110
|
+
// A resumed-but-untyped branch has no head yet: name it by where it
|
|
111
|
+
// sits, not by the root's title (which would read as a duplicate).
|
|
112
|
+
const atNode = t.summary.parentEntryId
|
|
113
|
+
? merged.detail.nodes[t.summary.parentEntryId]
|
|
114
|
+
: undefined;
|
|
115
|
+
return {
|
|
116
|
+
...t,
|
|
117
|
+
num: i + 1,
|
|
118
|
+
headNodeId,
|
|
119
|
+
headExcerpt: head
|
|
120
|
+
? head.excerpt || head.kind
|
|
121
|
+
: atNode
|
|
122
|
+
? `at: ${atNode.excerpt || atNode.kind}`
|
|
123
|
+
: undefined,
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
// Branch heads of the agents on display: each splits a map segment so its
|
|
127
|
+
// agent's dot hangs off its own branch, nested under its parent's.
|
|
128
|
+
const shownTreeIds = new Set(shown.map((t) => t.summary.treeId));
|
|
129
|
+
const heads = new Set([...headByTree].filter(([id]) => shownTreeIds.has(id)).map(([, h]) => h));
|
|
130
|
+
const skel = buildSkeleton(merged.detail, shownAt, heads);
|
|
131
|
+
return {
|
|
132
|
+
rows,
|
|
133
|
+
leafId: merged.detail.leafId,
|
|
134
|
+
tips,
|
|
135
|
+
parkedAt,
|
|
136
|
+
parkedCount,
|
|
137
|
+
atree,
|
|
138
|
+
detail: merged.detail,
|
|
139
|
+
skel,
|
|
140
|
+
memberOrder: family.map((t) => t.treeId),
|
|
141
|
+
pendingMembers,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Row index of a node id (cursor placement, tip jumps, search). */
|
|
145
|
+
export function cRowIndexOf(view, nodeId) {
|
|
146
|
+
if (!nodeId)
|
|
147
|
+
return -1;
|
|
148
|
+
return view.rows.findIndex((r) => r.a.nodeId === nodeId);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Which family session's file contains `nodeId` (labels/branches target it).
|
|
152
|
+
* Walks MERGE order (root first): the first writer's copy is the one the
|
|
153
|
+
* merged view displays, so writes must land there to be visible.
|
|
154
|
+
*/
|
|
155
|
+
export function ownerOf(view, nodeId, detailOf) {
|
|
156
|
+
for (const id of view.memberOrder) {
|
|
157
|
+
if (detailOf(id)?.nodes[nodeId])
|
|
158
|
+
return id;
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
export { aContainerOf, aRowIndexOf };
|
|
163
|
+
//# sourceMappingURL=sessionview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionview.js","sourceRoot":"","sources":["../../../src/client/tree/sessionview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation rendering: one ascii message tree with agent tips, plus the
|
|
3
|
+
* agents panel (metro mini + one row per branch tip).
|
|
4
|
+
*
|
|
5
|
+
* ○ build me a parser for the pines forest
|
|
6
|
+
* └─ · here's a first pass…
|
|
7
|
+
* ├─ ○ make it incremental — · 2m
|
|
8
|
+
* └─ ○ use embeddings instead — ● needs input · 40s
|
|
9
|
+
*/
|
|
10
|
+
import { clipAnsi, visibleLength } from "../ansi.js";
|
|
11
|
+
import { FAINT, MUTED } from "../theme.js";
|
|
12
|
+
import { exchangeEndOf } from "./sessionview.js";
|
|
13
|
+
import { statusGlyph, statusSgr } from "../forest/view.js";
|
|
14
|
+
const RESET = "\x1b[0m";
|
|
15
|
+
import { renderConvMini, skelNodeFor } from "./convmap.js";
|
|
16
|
+
import { treeTitle, humanAge } from "../forest/sidebar.js";
|
|
17
|
+
function stateWord(t) {
|
|
18
|
+
return t.status === "running"
|
|
19
|
+
? "working"
|
|
20
|
+
: t.status === "waiting"
|
|
21
|
+
? t.seen
|
|
22
|
+
? "waiting"
|
|
23
|
+
: "needs input"
|
|
24
|
+
: t.status;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Where the agents panel sits. `panelW` is the app's shared sidebar width
|
|
28
|
+
* (same setting, same divider as the forest sidebar) — 0 hides the panel;
|
|
29
|
+
* it also stays hidden while the tree has no forks worth mapping.
|
|
30
|
+
*/
|
|
31
|
+
export function convLayout(view, width, height, panelW) {
|
|
32
|
+
// Narrow terminals cede more of the row to the transcript: the panel
|
|
33
|
+
// duplicates status the tip suffixes already carry, the words do not.
|
|
34
|
+
const cap = Math.max(24, Math.floor(width / (width < 100 ? 3 : 2)));
|
|
35
|
+
const sideW = view.tips.length > 1 && panelW > 0 ? Math.min(panelW, cap) : 0;
|
|
36
|
+
// The floating mini sits between the header and the agent rows; it only
|
|
37
|
+
// draws when there's honest room for both.
|
|
38
|
+
const wanted = Math.min(16, Math.max(6, view.tips.length * 2 + 2));
|
|
39
|
+
const spare = height - (view.tips.length + 6);
|
|
40
|
+
const mapH = sideW > 0 && spare >= 6 ? Math.min(wanted, spare) : 0;
|
|
41
|
+
const mapTop = 2;
|
|
42
|
+
const agentsTop = mapH > 0 ? mapTop + mapH + 1 : 2;
|
|
43
|
+
return { sideW, textH: height, mapTop, mapH, agentsTop };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Render the conversation: one ascii message tree; nodes where a session's
|
|
47
|
+
* branch currently ends ("tips") carry that agent's status glyph and state.
|
|
48
|
+
* The optional left panel lists the tips — agents, not messages.
|
|
49
|
+
*/
|
|
50
|
+
export function renderConversation(view, opts) {
|
|
51
|
+
const lay = convLayout(view, opts.width, opts.height, opts.panelW);
|
|
52
|
+
const withMap = view.rows.length > lay.textH;
|
|
53
|
+
const textW = opts.width - (lay.sideW ? lay.sideW + 2 : 0) - (withMap ? 2 : 0);
|
|
54
|
+
const lines = [];
|
|
55
|
+
const end = Math.min(view.rows.length, opts.scroll + lay.textH);
|
|
56
|
+
for (let i = opts.scroll; i < end; i++) {
|
|
57
|
+
const { a: row, tips } = view.rows[i];
|
|
58
|
+
const isSel = i === opts.selected;
|
|
59
|
+
const prefix = `\x1b[${FAINT}m${row.prefix}${RESET}`;
|
|
60
|
+
let body = "";
|
|
61
|
+
if (row.kind === "node" && row.node) {
|
|
62
|
+
const n = row.node;
|
|
63
|
+
const tip = tips?.[0];
|
|
64
|
+
// Messages keep their role glyph ALWAYS — a user turn stays ○ no
|
|
65
|
+
// matter what agents are parked on it. Agent status is a suffix.
|
|
66
|
+
const glyph = row.isLeaf
|
|
67
|
+
? "\x1b[32m●" + RESET
|
|
68
|
+
: n.label
|
|
69
|
+
? "\x1b[33m◆" + RESET
|
|
70
|
+
: n.kind === "user"
|
|
71
|
+
? "\x1b[36m○" + RESET
|
|
72
|
+
: `\x1b[${MUTED}m·${RESET}`;
|
|
73
|
+
const excerpt = n.excerpt || n.kind;
|
|
74
|
+
const textSgr = isSel
|
|
75
|
+
? "7"
|
|
76
|
+
: row.onActive
|
|
77
|
+
? n.kind === "user" || row.isLeaf
|
|
78
|
+
? "1"
|
|
79
|
+
: "0"
|
|
80
|
+
: tip && !tip.seen
|
|
81
|
+
? "1"
|
|
82
|
+
: n.kind === "user"
|
|
83
|
+
? "36"
|
|
84
|
+
: MUTED;
|
|
85
|
+
body = `${glyph} \x1b[${textSgr}m${excerpt}${RESET}`;
|
|
86
|
+
if (n.label)
|
|
87
|
+
body += ` \x1b[33m[${n.label}]${RESET}`;
|
|
88
|
+
if (tips?.length) {
|
|
89
|
+
const t = tips[0];
|
|
90
|
+
const g = `\x1b[${statusSgr(t)}m${statusGlyph(t, opts.spinnerFrame)}${RESET}`;
|
|
91
|
+
// The default state stays quiet: a dormant, seen agent is just a
|
|
92
|
+
// glyph and an age. Only states that want something (working,
|
|
93
|
+
// needs input, crashed) earn a word — so the words mean something.
|
|
94
|
+
const quiet = t.status === "dormant" && t.seen;
|
|
95
|
+
const boldSt = !t.seen ? "\x1b[1m" : "";
|
|
96
|
+
const word = quiet
|
|
97
|
+
? ""
|
|
98
|
+
: `${boldSt}\x1b[${!t.seen ? "0" : MUTED}m${stateWord(t)}${RESET}\x1b[${MUTED}m · `;
|
|
99
|
+
body += ` \x1b[${FAINT}m—${RESET} ${g} ${word}\x1b[${MUTED}m${humanAge(t.mtime, opts.now)}${RESET}`;
|
|
100
|
+
if (tips.length > 1)
|
|
101
|
+
body += ` \x1b[${MUTED}m(+${tips.length - 1})${RESET}`;
|
|
102
|
+
if (isSel)
|
|
103
|
+
body += ` \x1b[${MUTED}m(⏎ ${t.live ? "attach" : "resume"})${RESET}`;
|
|
104
|
+
}
|
|
105
|
+
else if (isSel && row.nodeId) {
|
|
106
|
+
const snapped = exchangeEndOf(view.detail, row.nodeId);
|
|
107
|
+
const parkedHere = view.parkedAt.get(snapped)?.length ?? view.parkedAt.get(row.nodeId)?.length;
|
|
108
|
+
body += parkedHere
|
|
109
|
+
? ` \x1b[${MUTED}m(⏎ continue from here — reuses a parked agent)${RESET}`
|
|
110
|
+
: ` \x1b[${MUTED}m(⏎ new branch + agent from here)${RESET}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (row.kind === "elision") {
|
|
114
|
+
const sgr = isSel ? "7" : MUTED;
|
|
115
|
+
const tools = row.toolCount ?? 0;
|
|
116
|
+
const replies = (row.count ?? 0) - tools;
|
|
117
|
+
const what = tools && replies
|
|
118
|
+
? `${tools} tool ${tools === 1 ? "call" : "calls"} · ${replies} ${replies === 1 ? "reply" : "replies"}`
|
|
119
|
+
: tools
|
|
120
|
+
? `${tools} tool ${tools === 1 ? "call" : "calls"}`
|
|
121
|
+
: `${replies} ${replies === 1 ? "reply" : "replies"}`;
|
|
122
|
+
body = `\x1b[${sgr}m⚙ ⋯ ${what}${RESET}`;
|
|
123
|
+
if (isSel)
|
|
124
|
+
body += ` \x1b[${MUTED}m(⏎ expands)${RESET}`;
|
|
125
|
+
}
|
|
126
|
+
else if (row.kind === "fold") {
|
|
127
|
+
const sgr = isSel ? "7" : MUTED;
|
|
128
|
+
const age = row.foldAge ? ` · ${row.foldAge}` : "";
|
|
129
|
+
body =
|
|
130
|
+
`\x1b[${sgr}m○ ${row.foldExcerpt}${RESET} ` +
|
|
131
|
+
`\x1b[${MUTED}m· ${row.foldMsgs} msgs${age}${RESET} \x1b[${FAINT}m[+]${RESET}`;
|
|
132
|
+
}
|
|
133
|
+
lines.push(clipAnsi(` ${prefix}${body}`, textW));
|
|
134
|
+
}
|
|
135
|
+
while (lines.length < lay.textH)
|
|
136
|
+
lines.push("");
|
|
137
|
+
if (withMap) {
|
|
138
|
+
const total = view.rows.length;
|
|
139
|
+
for (let j = 0; j < lay.textH; j++) {
|
|
140
|
+
const lo = Math.floor((j / lay.textH) * total);
|
|
141
|
+
const hi = Math.max(lo + 1, Math.floor(((j + 1) / lay.textH) * total));
|
|
142
|
+
let ch = "│";
|
|
143
|
+
let sgr = FAINT;
|
|
144
|
+
for (let d = lo; d < hi; d++) {
|
|
145
|
+
const r = view.rows[d];
|
|
146
|
+
if (r.tips?.length) {
|
|
147
|
+
ch = "●";
|
|
148
|
+
sgr = statusSgr(r.tips[0]);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
if (r.a.kind === "fold" || r.a.prefix.includes("├─")) {
|
|
152
|
+
ch = "┿";
|
|
153
|
+
sgr = MUTED;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const inView = hi > opts.scroll && lo < opts.scroll + lay.textH;
|
|
157
|
+
if (inView && ch === "│") {
|
|
158
|
+
ch = "█";
|
|
159
|
+
sgr = "48;5;238;" + MUTED;
|
|
160
|
+
}
|
|
161
|
+
const line = lines[j];
|
|
162
|
+
const pad = Math.max(0, textW - visibleLength(line));
|
|
163
|
+
lines[j] = line + " ".repeat(pad) + ` \x1b[${sgr}m${ch}${RESET}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Agents panel: the floating mini of the tree (skeleton: root, branch
|
|
167
|
+
// points, agent tips — the forest's visual language) above one row per
|
|
168
|
+
// branch tip. Agents, not messages: selecting jumps to that exact node.
|
|
169
|
+
let miniHits;
|
|
170
|
+
if (lay.sideW > 0) {
|
|
171
|
+
const side = [];
|
|
172
|
+
const n = view.tips.length;
|
|
173
|
+
side.push(clipAnsi(`\x1b[${MUTED}m agents on this tree — ${n}${RESET}`, lay.sideW));
|
|
174
|
+
side.push("");
|
|
175
|
+
const selRow = view.rows[opts.selected]?.a;
|
|
176
|
+
const selNode = selRow?.nodeId;
|
|
177
|
+
const selIdx = view.tips.findIndex((t) => t.nodeId === selNode);
|
|
178
|
+
if (lay.mapH > 0) {
|
|
179
|
+
const cursorSkelId = skelNodeFor(view.detail, view.skel, selRow?.nodeId ?? selRow?.runId ?? selRow?.foldId);
|
|
180
|
+
const mini = renderConvMini(view.skel, {
|
|
181
|
+
width: lay.sideW,
|
|
182
|
+
height: lay.mapH,
|
|
183
|
+
spinnerFrame: opts.spinnerFrame,
|
|
184
|
+
cursorSkelId,
|
|
185
|
+
});
|
|
186
|
+
miniHits = mini.hits;
|
|
187
|
+
for (const l of mini.lines)
|
|
188
|
+
side.push(l);
|
|
189
|
+
side.push("");
|
|
190
|
+
}
|
|
191
|
+
view.tips.forEach((t, i) => {
|
|
192
|
+
const s = t.summary;
|
|
193
|
+
const here = i === selIdx;
|
|
194
|
+
const glyph = `\x1b[${statusSgr(s)}m${statusGlyph(s, opts.spinnerFrame)}${RESET}`;
|
|
195
|
+
// A branch is named by the user message that began it, not by its
|
|
196
|
+
// latest reply — divergence is identity.
|
|
197
|
+
const label = t.headExcerpt ?? treeTitle(s).title;
|
|
198
|
+
const quiet = s.status === "dormant" && s.seen;
|
|
199
|
+
const info = quiet
|
|
200
|
+
? `\x1b[${MUTED}m· ${humanAge(s.mtime, opts.now)}${RESET}`
|
|
201
|
+
: `\x1b[${MUTED}m· ${stateWord(s)} · ${humanAge(s.mtime, opts.now)}${RESET}`;
|
|
202
|
+
const nameSgr = !s.seen ? "1" : here ? "1" : s.live ? "0" : MUTED;
|
|
203
|
+
const line = ` ${glyph} \x1b[${nameSgr}m${label}${RESET} ${info}`;
|
|
204
|
+
if (here) {
|
|
205
|
+
const clipped = clipAnsi(line, lay.sideW);
|
|
206
|
+
const fill = Math.max(0, lay.sideW - visibleLength(clipped));
|
|
207
|
+
side.push(`\x1b[48;5;236m${clipped}${" ".repeat(fill)}${RESET}`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
side.push(clipAnsi(line, lay.sideW));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
if (view.parkedCount > 0) {
|
|
214
|
+
side.push(clipAnsi(`\x1b[${FAINT}m ⊘ ${view.parkedCount} parked agent${view.parkedCount === 1 ? "" : "s"} mid-tree${RESET}`, lay.sideW));
|
|
215
|
+
}
|
|
216
|
+
if (view.pendingMembers > 0) {
|
|
217
|
+
side.push("");
|
|
218
|
+
side.push(clipAnsi(`\x1b[${FAINT}m loading ${view.pendingMembers} branch…${RESET}`, lay.sideW));
|
|
219
|
+
}
|
|
220
|
+
side.push("");
|
|
221
|
+
side.push(clipAnsi(`\x1b[${FAINT}m click a branch to go there${RESET}`, lay.sideW));
|
|
222
|
+
for (let j = 0; j < lay.textH; j++) {
|
|
223
|
+
const sd = side[j] ?? "";
|
|
224
|
+
const fill = Math.max(0, lay.sideW - visibleLength(sd));
|
|
225
|
+
lines[j] = `${sd}${" ".repeat(fill)}\x1b[${FAINT}m│${RESET} ${lines[j] ?? ""}`;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { lines, miniHits };
|
|
229
|
+
}
|
|
230
|
+
/** Clamp scroll so `selected` stays inside the viewport. */
|
|
231
|
+
export function scrollTo(selected, scroll, height) {
|
|
232
|
+
if (selected < scroll)
|
|
233
|
+
return selected;
|
|
234
|
+
if (selected >= scroll + height)
|
|
235
|
+
return selected - height + 1;
|
|
236
|
+
return scroll;
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/client/tree/view.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Persisted client UI state (~/.pines/ui.json): tolerant load, atomic-ish save. */
|
|
2
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { ensurePinesHome } from "../shared/paths.js";
|
|
4
|
+
import { uiStatePath } from "../shared/paths.js";
|
|
5
|
+
import { DEFAULT_STYLE_ID, FOREST_STYLES } from "./forest/styles.js";
|
|
6
|
+
export const DEFAULT_UI_STATE = {
|
|
7
|
+
sidebarVisible: true,
|
|
8
|
+
sidebarWidth: 32,
|
|
9
|
+
forestStyle: DEFAULT_STYLE_ID,
|
|
10
|
+
};
|
|
11
|
+
export function loadUiState() {
|
|
12
|
+
try {
|
|
13
|
+
// On disk the style is whatever an older version wrote, not today's union.
|
|
14
|
+
const raw = JSON.parse(readFileSync(uiStatePath(), "utf8"));
|
|
15
|
+
return {
|
|
16
|
+
sidebarVisible: typeof raw.sidebarVisible === "boolean"
|
|
17
|
+
? raw.sidebarVisible
|
|
18
|
+
: DEFAULT_UI_STATE.sidebarVisible,
|
|
19
|
+
sidebarWidth: typeof raw.sidebarWidth === "number" && Number.isFinite(raw.sidebarWidth)
|
|
20
|
+
? raw.sidebarWidth
|
|
21
|
+
: DEFAULT_UI_STATE.sidebarWidth,
|
|
22
|
+
// An unknown style (hand-edited file, or one dropped in a later
|
|
23
|
+
// version) falls back rather than rendering nothing. blueprint was
|
|
24
|
+
// absorbed into canopy — saved picks follow it there.
|
|
25
|
+
forestStyle: raw.forestStyle === "blueprint"
|
|
26
|
+
? "canopy"
|
|
27
|
+
: FOREST_STYLES.some((s) => s.id === raw.forestStyle)
|
|
28
|
+
? raw.forestStyle
|
|
29
|
+
: DEFAULT_UI_STATE.forestStyle,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { ...DEFAULT_UI_STATE };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function saveUiState(state) {
|
|
37
|
+
try {
|
|
38
|
+
ensurePinesHome();
|
|
39
|
+
writeFileSync(uiStatePath(), JSON.stringify(state, null, 2) + "\n");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Persistence is best-effort; the session keeps its in-memory state.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=uistate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uistate.js","sourceRoot":"","sources":["../../src/client/uistate.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUrE,MAAM,CAAC,MAAM,gBAAgB,GAAY;IACvC,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,EAAE;IAChB,WAAW,EAAE,gBAAgB;CAC9B,CAAC;AAEF,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,2EAA2E;QAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAEzD,CAAC;QACF,OAAO;YACL,cAAc,EACZ,OAAO,GAAG,CAAC,cAAc,KAAK,SAAS;gBACrC,CAAC,CAAC,GAAG,CAAC,cAAc;gBACpB,CAAC,CAAC,gBAAgB,CAAC,cAAc;YACrC,YAAY,EACV,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC;gBACvE,CAAC,CAAC,GAAG,CAAC,YAAY;gBAClB,CAAC,CAAC,gBAAgB,CAAC,YAAY;YACnC,gEAAgE;YAChE,mEAAmE;YACnE,sDAAsD;YACtD,WAAW,EACT,GAAG,CAAC,WAAW,KAAK,WAAW;gBAC7B,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,WAAW,CAAC;oBACnD,CAAC,CAAE,GAAG,CAAC,WAA6B;oBACpC,CAAC,CAAC,gBAAgB,CAAC,WAAW;SACrC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,CAAC;QACH,eAAe,EAAE,CAAC;QAClB,aAAa,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;AACH,CAAC"}
|