@geohar/unbien-cli 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.
Files changed (50) hide show
  1. package/README.md +72 -0
  2. package/dist/client.d.ts +89 -0
  3. package/dist/client.js +210 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/commands.d.ts +59 -0
  6. package/dist/commands.js +273 -0
  7. package/dist/commands.js.map +1 -0
  8. package/dist/connect.d.ts +2 -0
  9. package/dist/connect.js +475 -0
  10. package/dist/connect.js.map +1 -0
  11. package/dist/identity.d.ts +2 -0
  12. package/dist/identity.js +30 -0
  13. package/dist/identity.js.map +1 -0
  14. package/dist/invite.d.ts +10 -0
  15. package/dist/invite.js +28 -0
  16. package/dist/invite.js.map +1 -0
  17. package/dist/main.d.ts +3 -0
  18. package/dist/main.js +20 -0
  19. package/dist/main.js.map +1 -0
  20. package/dist/panels.d.ts +40 -0
  21. package/dist/panels.js +155 -0
  22. package/dist/panels.js.map +1 -0
  23. package/dist/picker.d.ts +6 -0
  24. package/dist/picker.js +95 -0
  25. package/dist/picker.js.map +1 -0
  26. package/dist/plan.d.ts +30 -0
  27. package/dist/plan.js +132 -0
  28. package/dist/plan.js.map +1 -0
  29. package/dist/reduce.d.ts +45 -0
  30. package/dist/reduce.js +123 -0
  31. package/dist/reduce.js.map +1 -0
  32. package/dist/render.d.ts +13 -0
  33. package/dist/render.js +91 -0
  34. package/dist/render.js.map +1 -0
  35. package/dist/replay.d.ts +2 -0
  36. package/dist/replay.js +57 -0
  37. package/dist/replay.js.map +1 -0
  38. package/dist/settings.d.ts +27 -0
  39. package/dist/settings.js +31 -0
  40. package/dist/settings.js.map +1 -0
  41. package/dist/store.d.ts +13 -0
  42. package/dist/store.js +20 -0
  43. package/dist/store.js.map +1 -0
  44. package/dist/theme.d.ts +7 -0
  45. package/dist/theme.js +110 -0
  46. package/dist/theme.js.map +1 -0
  47. package/dist/tui.d.ts +78 -0
  48. package/dist/tui.js +265 -0
  49. package/dist/tui.js.map +1 -0
  50. package/package.json +58 -0
package/dist/panels.js ADDED
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Plan and subagent panels ride the `{evt}` plane as `panel_update` frames the
3
+ * extension already produces from the in-process plan/subagents buses. They are
4
+ * ephemeral view state, never transcript, so they are held here rather than
5
+ * folded into the reducer.
6
+ */
7
+ import { agentItems, agentType, waveOrder, waveSections } from "./plan.js";
8
+ export class PanelStore {
9
+ panels = new Map();
10
+ /** Returns true when the envelope was a panel update. */
11
+ apply(env) {
12
+ const evt = env.evt;
13
+ if (!evt || evt.channel !== "panel")
14
+ return false;
15
+ const frame = evt.data;
16
+ if (frame?.type !== "panel_update" || !frame.key)
17
+ return false;
18
+ this.panels.set(frame.key, {
19
+ key: frame.key,
20
+ title: frame.title ?? frame.key,
21
+ items: frame.data?.items ?? [],
22
+ });
23
+ return true;
24
+ }
25
+ get(key) {
26
+ return this.panels.get(key);
27
+ }
28
+ keys() {
29
+ return [...this.panels.keys()];
30
+ }
31
+ }
32
+ /** Mirrors the app's status glyphs: done / running / everything else. */
33
+ function statusMark(status) {
34
+ switch (status) {
35
+ case "done":
36
+ case "completed":
37
+ return "✓";
38
+ case "failed":
39
+ case "error":
40
+ return "✗";
41
+ case "in_progress":
42
+ case "in-progress":
43
+ case "started":
44
+ case "running":
45
+ return "▶";
46
+ default:
47
+ return "○";
48
+ }
49
+ }
50
+ const isRunning = (status) => status === "in_progress" ||
51
+ status === "in-progress" ||
52
+ status === "started" ||
53
+ status === "running";
54
+ export function renderPlanPanel(panel, options = {}) {
55
+ const { showDone = false, showContext = false, maxRows = Number.POSITIVE_INFINITY, } = options;
56
+ const items = panel?.items ?? [];
57
+ if (items.length === 0)
58
+ return [" no plan items yet"];
59
+ const done = items.filter((i) => i.status === "done").length;
60
+ const active = items.filter((i) => isRunning(i.status)).length;
61
+ const hidden = [];
62
+ if (!showDone && done > 0)
63
+ hidden.push(`${done} done`);
64
+ const contextCount = items.filter((i) => i.kind !== "plan").length;
65
+ if (!showContext && contextCount > 0)
66
+ hidden.push(`${contextCount} context`);
67
+ const header = ` Plan — ${items.length} items, ${done} done${active > 0 ? `, ${active} active` : ""}${hidden.length > 0 ? ` (hiding ${hidden.join(", ")})` : ""}`;
68
+ const lines = [header];
69
+ // Filter as pi-plan does, then cap: the point of the panel is what to pick up
70
+ // next, so history and context are opt-in and long plans trail off.
71
+ let budget = maxRows;
72
+ let dropped = 0;
73
+ for (const section of waveSections(items)) {
74
+ const rows = section.rows.filter((row) => (showDone || row.item.status !== "done") &&
75
+ (showContext || row.item.kind === "plan"));
76
+ if (rows.length === 0)
77
+ continue;
78
+ const shown = budget > 0 ? rows.slice(0, budget) : [];
79
+ dropped += rows.length - shown.length;
80
+ budget -= shown.length;
81
+ if (shown.length === 0)
82
+ continue;
83
+ lines.push("", ` ${section.title}`);
84
+ for (const row of shown) {
85
+ const kind = row.item.kind && row.item.kind !== "plan" ? ` (${row.item.kind})` : "";
86
+ const taint = row.item.tainted ? " ⚠ tainted" : "";
87
+ const blocked = row.blockedCount > 0 && !row.actionable
88
+ ? ` · blocked by ${row.blockedCount}`
89
+ : "";
90
+ lines.push(` ${statusMark(row.item.status)} ${row.item.title}${kind}${blocked}${taint}`);
91
+ }
92
+ }
93
+ if (dropped > 0)
94
+ lines.push(` …${dropped} more`);
95
+ if (lines.length === 1)
96
+ lines.push(" (nothing to show with these filters)");
97
+ return lines;
98
+ }
99
+ /** pi-plan's collapsed form: one line of counts, and how to expand. */
100
+ export function summarisePlan(panel) {
101
+ const items = panel?.items ?? [];
102
+ if (items.length === 0)
103
+ return [];
104
+ const rows = waveOrder(items);
105
+ const ready = rows.filter((r) => r.actionable).length;
106
+ const active = items.filter((i) => isRunning(i.status)).length;
107
+ const blocked = rows.filter((r) => !r.circular && (r.wave ?? 0) > 0 && r.item.status !== "done").length;
108
+ const circular = rows.filter((r) => r.circular).length;
109
+ const parts = [];
110
+ if (ready > 0)
111
+ parts.push(`${ready} ready`);
112
+ if (active > 0)
113
+ parts.push(`${active} active`);
114
+ if (blocked > 0)
115
+ parts.push(`${blocked} blocked`);
116
+ if (circular > 0)
117
+ parts.push(`${circular} circular`);
118
+ const body = parts.length > 0 ? parts.join(" · ") : "(empty)";
119
+ return [` ▸ Plan ${body} /plan to expand`];
120
+ }
121
+ export function summariseAgents(panel) {
122
+ const items = panel?.items ?? [];
123
+ if (items.length === 0)
124
+ return [];
125
+ const running = items.filter((i) => isRunning(i.status)).length;
126
+ const parts = [`${items.length} agent${items.length === 1 ? "" : "s"}`];
127
+ if (running > 0)
128
+ parts.push(`${running} running`);
129
+ return [` ▸ Agents ${parts.join(" · ")} /subagents to expand`];
130
+ }
131
+ /** Subagents chronologically, with type badge — the app's agents panel. */
132
+ export function renderAgentsPanel(panel) {
133
+ const items = agentItems(panel?.items ?? []);
134
+ if (items.length === 0)
135
+ return [" no subagents yet"];
136
+ const running = items.filter((i) => isRunning(i.status)).length;
137
+ const done = items.filter((i) => i.status === "done").length;
138
+ const failed = items.filter((i) => i.status === "failed" || i.status === "error").length;
139
+ const summary = [`${items.length} agents`];
140
+ if (running > 0)
141
+ summary.push(`${running} running`);
142
+ if (done > 0)
143
+ summary.push(`${done} done`);
144
+ if (failed > 0)
145
+ summary.push(`${failed} failed`);
146
+ return [
147
+ ` Agents — ${summary.join(", ")}`,
148
+ "",
149
+ ...items.map((item) => {
150
+ const type = agentType(item);
151
+ return ` ${statusMark(item.status)} ${item.title}${type ? ` · ${type}` : ""}`;
152
+ }),
153
+ ];
154
+ }
155
+ //# sourceMappingURL=panels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panels.js","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { RoomInfo } from "./client.js";
2
+ /**
3
+ * Renders on stderr in raw mode so stdout stays clean for the transcript.
4
+ * Resolves to the chosen session, or null if cancelled.
5
+ */
6
+ export declare function pickSession(rooms: readonly RoomInfo[]): Promise<RoomInfo | null>;
package/dist/picker.js ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Session picker built on pi's own `SelectList` — arrow-key highlight,
3
+ * type-to-filter, and pi's list theme, rather than "enter a number".
4
+ */
5
+ import { getSelectListTheme } from "@earendil-works/pi-coding-agent";
6
+ import { SelectList } from "@earendil-works/pi-tui";
7
+ const MAX_VISIBLE = 12;
8
+ /**
9
+ * `SelectList.setFilter` prefix-matches on `value`, so the value must be what a
10
+ * person would type — the session name, not its routing id. Names are not
11
+ * unique, so collisions get a short session-id suffix to stay addressable.
12
+ */
13
+ function buildItems(rooms) {
14
+ const seen = new Map();
15
+ const items = [];
16
+ const byValue = new Map();
17
+ for (const room of rooms) {
18
+ const base = room.name ?? room.sessionId?.slice(0, 8) ?? room.room_id;
19
+ const count = (seen.get(base) ?? 0) + 1;
20
+ seen.set(base, count);
21
+ const id = room.sessionId?.slice(0, 8) ?? room.room_id;
22
+ const value = count === 1 ? base : `${base} (${id})`;
23
+ items.push({
24
+ value,
25
+ label: room.parent ? `${value} (subagent)` : value,
26
+ description: room.cwd ?? "",
27
+ });
28
+ byValue.set(value, room);
29
+ }
30
+ return { items, byValue };
31
+ }
32
+ /**
33
+ * Renders on stderr in raw mode so stdout stays clean for the transcript.
34
+ * Resolves to the chosen session, or null if cancelled.
35
+ */
36
+ export function pickSession(rooms) {
37
+ const { items, byValue } = buildItems(rooms);
38
+ const list = new SelectList(items, MAX_VISIBLE, getSelectListTheme());
39
+ const input = process.stdin;
40
+ const output = process.stderr;
41
+ const width = output.columns ?? 100;
42
+ let filter = "";
43
+ let lastHeight = 0;
44
+ const paint = () => {
45
+ if (lastHeight > 0)
46
+ output.write(`\u001b[${lastHeight}A`);
47
+ const lines = [
48
+ ` filter: ${filter}\u001b[K`,
49
+ ...list.render(width).map((line) => `${line}\u001b[K`),
50
+ ];
51
+ for (const line of lines)
52
+ output.write(`${line}\n`);
53
+ lastHeight = lines.length;
54
+ };
55
+ return new Promise((resolve) => {
56
+ const finish = (room) => {
57
+ input.off("data", onData);
58
+ if (input.isTTY)
59
+ input.setRawMode(false);
60
+ // Deliberately NOT paused: the shell takes stdin over next, and a paused
61
+ // stream there is a prompt box that renders but never sees a keystroke.
62
+ output.write("\u001b[?25h");
63
+ resolve(room);
64
+ };
65
+ list.onSelect = (item) => finish(byValue.get(item.value) ?? null);
66
+ list.onCancel = () => finish(null);
67
+ const onData = (chunk) => {
68
+ const data = chunk.toString("utf8");
69
+ if (data === "\u0003") {
70
+ finish(null);
71
+ return;
72
+ }
73
+ if (data === "\u007f" || data === "\b") {
74
+ filter = filter.slice(0, -1);
75
+ list.setFilter(filter);
76
+ }
77
+ else if (/^[\x20-\x7e]+$/.test(data)) {
78
+ filter += data;
79
+ list.setFilter(filter);
80
+ }
81
+ else {
82
+ list.handleInput(data);
83
+ }
84
+ paint();
85
+ };
86
+ output.write("Select a session (↑/↓ move · type to filter · enter · esc)\n\n");
87
+ output.write("\u001b[?25l");
88
+ if (input.isTTY)
89
+ input.setRawMode(true);
90
+ input.resume();
91
+ input.on("data", onData);
92
+ paint();
93
+ });
94
+ }
95
+ //# sourceMappingURL=picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker.js","sourceRoot":"","sources":["../src/picker.ts"],"names":[],"mappings":"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"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Plan + subagents presentation, mirroring the app's `PlanModel` — itself a
3
+ * port of pi-plan's `waveOrder`. Kept faithful on purpose: a plan should read
4
+ * the same in the CLI, the app, and pi-plan's own widget.
5
+ */
6
+ import type { PanelItem } from "./panels.js";
7
+ export interface PlanRow {
8
+ item: PanelItem;
9
+ /** 0 = free now; N = behind N waves of unsatisfied deps; null = circular. */
10
+ wave: number | null;
11
+ blockedCount: number;
12
+ circular: boolean;
13
+ /** A plan item, not done, wave 0 — the pick-up-now set. */
14
+ actionable: boolean;
15
+ }
16
+ export interface WaveSection {
17
+ title: string;
18
+ rows: PlanRow[];
19
+ }
20
+ /**
21
+ * Kahn-style layering: each pass places items whose unsatisfied deps are all
22
+ * placed; whatever never places is a cycle. Ordered by (done, wave, kind, id).
23
+ */
24
+ export declare function waveOrder(items: readonly PanelItem[]): PlanRow[];
25
+ /** Group into `Available now` → `Wave N` → `Cycle` → `Done`. */
26
+ export declare function waveSections(items: readonly PanelItem[]): WaveSection[];
27
+ /** Subagents read chronologically (pi-plan's `sortAgents`), by `meta.startedAt`. */
28
+ export declare function agentItems(items: readonly PanelItem[]): PanelItem[];
29
+ export declare function startedAt(item: PanelItem): number | undefined;
30
+ export declare function agentType(item: PanelItem): string | undefined;
package/dist/plan.js ADDED
@@ -0,0 +1,132 @@
1
+ const isDone = (item) => item.status === "done";
2
+ /**
3
+ * Crib dep semantics: a note never blocks; a design blocks only while tainted;
4
+ * a plan blocks until it is done.
5
+ */
6
+ function isSatisfied(item) {
7
+ switch (item.kind) {
8
+ case "note":
9
+ return true;
10
+ case "design":
11
+ return item.tainted !== true;
12
+ default:
13
+ return isDone(item);
14
+ }
15
+ }
16
+ function kindRank(kind) {
17
+ switch (kind) {
18
+ case "plan":
19
+ return 0;
20
+ case "design":
21
+ return 1;
22
+ case "note":
23
+ return 2;
24
+ default:
25
+ return 3;
26
+ }
27
+ }
28
+ /**
29
+ * Kahn-style layering: each pass places items whose unsatisfied deps are all
30
+ * placed; whatever never places is a cycle. Ordered by (done, wave, kind, id).
31
+ */
32
+ export function waveOrder(items) {
33
+ const byId = new Map(items.map((item) => [item.id, item]));
34
+ const blockers = new Map(items.map((item) => [
35
+ item.id,
36
+ (item.deps ?? [])
37
+ .map((dep) => byId.get(dep))
38
+ .filter((dep) => !!dep)
39
+ .filter((dep) => dep.id !== item.id && !isSatisfied(dep)),
40
+ ]));
41
+ const wave = new Map();
42
+ const placed = new Set();
43
+ let remaining = [...items];
44
+ let waveNum = 0;
45
+ while (remaining.length > 0) {
46
+ const ready = remaining.filter((item) => (blockers.get(item.id) ?? []).every((dep) => placed.has(dep.id)));
47
+ if (ready.length === 0)
48
+ break; // the rest form a cycle
49
+ for (const item of ready) {
50
+ wave.set(item.id, waveNum);
51
+ placed.add(item.id);
52
+ }
53
+ remaining = remaining.filter((item) => !placed.has(item.id));
54
+ waveNum += 1;
55
+ }
56
+ const rows = items.map((item) => {
57
+ const circular = !placed.has(item.id);
58
+ const waveValue = circular ? null : (wave.get(item.id) ?? null);
59
+ return {
60
+ item,
61
+ wave: waveValue,
62
+ blockedCount: (blockers.get(item.id) ?? []).length,
63
+ circular,
64
+ actionable: item.kind === "plan" && !isDone(item) && waveValue === 0,
65
+ };
66
+ });
67
+ return rows.sort((a, b) => {
68
+ const ad = isDone(a.item) ? 1 : 0;
69
+ const bd = isDone(b.item) ? 1 : 0;
70
+ if (ad !== bd)
71
+ return ad - bd;
72
+ if (a.wave !== null && b.wave !== null && a.wave !== b.wave) {
73
+ return a.wave - b.wave;
74
+ }
75
+ if (a.wave === null && b.wave !== null)
76
+ return 1;
77
+ if (a.wave !== null && b.wave === null)
78
+ return -1;
79
+ const ak = kindRank(a.item.kind);
80
+ const bk = kindRank(b.item.kind);
81
+ if (ak !== bk)
82
+ return ak - bk;
83
+ return a.item.id.localeCompare(b.item.id);
84
+ });
85
+ }
86
+ /** Group into `Available now` → `Wave N` → `Cycle` → `Done`. */
87
+ export function waveSections(items) {
88
+ const bucket = (row) => {
89
+ if (isDone(row.item))
90
+ return "Done";
91
+ if (row.circular || row.wave === null)
92
+ return "Cycle";
93
+ return row.wave === 0 ? "Available now" : `Wave ${row.wave}`;
94
+ };
95
+ const order = [];
96
+ const grouped = new Map();
97
+ for (const row of waveOrder(items)) {
98
+ const key = bucket(row);
99
+ if (!grouped.has(key)) {
100
+ order.push(key);
101
+ grouped.set(key, []);
102
+ }
103
+ grouped.get(key).push(row);
104
+ }
105
+ return order.map((title) => ({ title, rows: grouped.get(title) ?? [] }));
106
+ }
107
+ /** Subagents read chronologically (pi-plan's `sortAgents`), by `meta.startedAt`. */
108
+ export function agentItems(items) {
109
+ return items
110
+ .map((item, index) => ({ item, index }))
111
+ .sort((a, b) => {
112
+ const at = startedAt(a.item) ?? a.index;
113
+ const bt = startedAt(b.item) ?? b.index;
114
+ return at - bt;
115
+ })
116
+ .map((entry) => entry.item);
117
+ }
118
+ function meta(item) {
119
+ const value = item.meta;
120
+ return typeof value === "object" && value !== null
121
+ ? value
122
+ : {};
123
+ }
124
+ export function startedAt(item) {
125
+ const value = meta(item)["startedAt"];
126
+ return typeof value === "number" ? value : undefined;
127
+ }
128
+ export function agentType(item) {
129
+ const value = meta(item)["agentType"];
130
+ return typeof value === "string" ? value : undefined;
131
+ }
132
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,GAAG,CAAC,IAAe,EAAW,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAA;AAEnE;;;GAGG;AACH,SAAS,WAAW,CAAC,IAAe;IAClC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,IAAI,CAAA;QACb,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAA;QAC9B;YACE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,CAAC,CAAA;QACV,KAAK,QAAQ;YACX,OAAO,CAAC,CAAA;QACV,KAAK,MAAM;YACT,OAAO,CAAC,CAAA;QACV;YACE,OAAO,CAAC,CAAA;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAA2B;IACnD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,EAAE;QACP,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;aACd,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;aAC3B,MAAM,CAAC,CAAC,GAAG,EAAoB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;aACxC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;KAC5D,CAAC,CACH,CAAA;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAA;IACtC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAA;IAChC,IAAI,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;IAC1B,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACtC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CACjE,CAAA;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAK,CAAC,wBAAwB;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;YAC1B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACrB,CAAC;QACD,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5D,OAAO,IAAI,CAAC,CAAA;IACd,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAW,EAAE;QACvC,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACrC,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAA;QAC/D,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,SAAS;YACf,YAAY,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM;YAClD,QAAQ;YACR,UAAU,EAAE,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,KAAK,CAAC;SACrE,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACjC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACjC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAA;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5D,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,CAAC,CAAA;QAChD,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC,CAAA;QACjD,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAA;QAC7B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,KAA2B;IACtD,MAAM,MAAM,GAAG,CAAC,GAAY,EAAU,EAAE;QACtC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAA;QACnC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,OAAO,CAAA;QACrD,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAA;IAC9D,CAAC,CAAA;IACD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAA;IAC5C,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACf,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;QACtB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;AAC1E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,KAA2B;IACpD,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;SACvC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACb,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;QACvC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;QACvC,OAAO,EAAE,GAAG,EAAE,CAAA;IAChB,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AAC/B,CAAC;AAED,SAAS,IAAI,CAAC,IAAe;IAC3B,MAAM,KAAK,GAAI,IAA2B,CAAC,IAAI,CAAA;IAC/C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAChD,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,EAAE,CAAA;AACR,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAe;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,CAAA;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAe;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,CAAA;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACtD,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The consumer-side reducer the harness is missing: an rpc-envelope stream in,
3
+ * an ordered transcript out. Mirrors the Swift `SessionState.applyRPC` role, so
4
+ * the same fixture corpus can be replayed through both.
5
+ */
6
+ import type { EnvelopeMessage } from "@geohar/un-bien/client";
7
+ export type { EnvelopeMessage };
8
+ export interface ContentBlock {
9
+ type: string;
10
+ text?: string | null;
11
+ name?: string | null;
12
+ id?: string | null;
13
+ }
14
+ export interface WireMessage {
15
+ role: "user" | "assistant" | "toolResult" | string;
16
+ content: ContentBlock[];
17
+ }
18
+ export type TranscriptItem = {
19
+ kind: "message";
20
+ message: WireMessage;
21
+ } | {
22
+ kind: "tool";
23
+ toolCallId: string;
24
+ toolName: string;
25
+ args: unknown;
26
+ result?: {
27
+ content: ContentBlock[];
28
+ details?: unknown;
29
+ isError: boolean;
30
+ };
31
+ } | {
32
+ kind: "notice";
33
+ level: string;
34
+ text: string;
35
+ };
36
+ /**
37
+ * Replay `get_entries` history as the SAME synthetic frames the live stream
38
+ * produces, so history and live fold through one reducer rather than two.
39
+ * Mirrors the Swift `applyEntries`, which emits only message_end /
40
+ * tool_execution_* for exactly this reason.
41
+ */
42
+ export declare function entriesToEnvelopes(entries: readonly unknown[]): EnvelopeMessage[];
43
+ /** Normalize a raw fixture line: bare rpc frames and wrapped envelopes both occur. */
44
+ export declare function toEnvelope(line: unknown): EnvelopeMessage | null;
45
+ export declare function reduce(envelopes: readonly EnvelopeMessage[]): TranscriptItem[];
package/dist/reduce.js ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The consumer-side reducer the harness is missing: an rpc-envelope stream in,
3
+ * an ordered transcript out. Mirrors the Swift `SessionState.applyRPC` role, so
4
+ * the same fixture corpus can be replayed through both.
5
+ */
6
+ function asFrame(value) {
7
+ return typeof value === "object" && value !== null ? value : null;
8
+ }
9
+ /**
10
+ * Replay `get_entries` history as the SAME synthetic frames the live stream
11
+ * produces, so history and live fold through one reducer rather than two.
12
+ * Mirrors the Swift `applyEntries`, which emits only message_end /
13
+ * tool_execution_* for exactly this reason.
14
+ */
15
+ export function entriesToEnvelopes(entries) {
16
+ const out = [];
17
+ for (const raw of entries) {
18
+ const entry = asFrame(raw);
19
+ if (!entry || entry.type !== "message")
20
+ continue;
21
+ const message = asFrame(entry.message);
22
+ if (!message)
23
+ continue;
24
+ if (message.role === "toolResult") {
25
+ out.push({
26
+ rpc: {
27
+ type: "tool_execution_end",
28
+ toolCallId: message.toolCallId,
29
+ toolName: message.toolName,
30
+ result: { content: message.content, details: message.details },
31
+ isError: message.isError === true,
32
+ },
33
+ });
34
+ continue;
35
+ }
36
+ out.push({ rpc: { type: "message_end", message } });
37
+ // An assistant turn carries its tool calls inline; the card is born from
38
+ // the start frame, exactly as in the live stream.
39
+ if (message.role !== "assistant" || !Array.isArray(message.content))
40
+ continue;
41
+ for (const block of message.content) {
42
+ const call = asFrame(block);
43
+ if (!call || call.type !== "toolCall")
44
+ continue;
45
+ out.push({
46
+ rpc: {
47
+ type: "tool_execution_start",
48
+ toolCallId: call.id,
49
+ toolName: call.name,
50
+ args: call.arguments,
51
+ },
52
+ });
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+ /** Normalize a raw fixture line: bare rpc frames and wrapped envelopes both occur. */
58
+ export function toEnvelope(line) {
59
+ const obj = asFrame(line);
60
+ if (!obj)
61
+ return null;
62
+ if ("rpc" in obj || "evt" in obj || "ub" in obj)
63
+ return obj;
64
+ if (typeof obj.type === "string")
65
+ return { rpc: obj };
66
+ return null;
67
+ }
68
+ export function reduce(envelopes) {
69
+ const items = [];
70
+ const toolsById = new Map();
71
+ for (const env of envelopes) {
72
+ const rpc = asFrame(env.rpc);
73
+ if (!rpc)
74
+ continue;
75
+ switch (rpc.type) {
76
+ case "message_end": {
77
+ const message = rpc.message;
78
+ // A toolResult message duplicates the tool card's own result frame.
79
+ if (!message || message.role === "toolResult")
80
+ break;
81
+ items.push({ kind: "message", message });
82
+ break;
83
+ }
84
+ case "tool_execution_start": {
85
+ const card = {
86
+ kind: "tool",
87
+ toolCallId: String(rpc.toolCallId),
88
+ toolName: String(rpc.toolName),
89
+ args: rpc.args,
90
+ };
91
+ toolsById.set(card.toolCallId, card);
92
+ items.push(card);
93
+ break;
94
+ }
95
+ case "tool_execution_end": {
96
+ const card = toolsById.get(String(rpc.toolCallId));
97
+ if (!card)
98
+ break;
99
+ // `details` carries each tool's typed payload — the edit diff among
100
+ // them — so pi's own result renderers draw natively from the wire.
101
+ const result = rpc.result;
102
+ card.result = {
103
+ content: result?.content ?? [],
104
+ details: result?.details,
105
+ isError: rpc.isError === true,
106
+ };
107
+ break;
108
+ }
109
+ case "extension_ui_request": {
110
+ if (rpc.method !== "notify")
111
+ break;
112
+ items.push({
113
+ kind: "notice",
114
+ level: String(rpc.notifyType ?? "info"),
115
+ text: String(rpc.message ?? ""),
116
+ });
117
+ break;
118
+ }
119
+ }
120
+ }
121
+ return items;
122
+ }
123
+ //# sourceMappingURL=reduce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reduce.js","sourceRoot":"","sources":["../src/reduce.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { TranscriptItem } from "./reduce.js";
2
+ /**
3
+ * Draw an in-flight assistant turn with the SAME component that draws the
4
+ * settled one, so the text doesn't visibly reflow when the message lands — it
5
+ * is simply replaced by its finished self.
6
+ */
7
+ /**
8
+ * In-flight reasoning, drawn as a thinking block by the same component that
9
+ * renders it once settled.
10
+ */
11
+ export declare function renderThinking(text: string, width: number): string[];
12
+ export declare function renderStreaming(text: string, width: number): string[];
13
+ export declare function renderTranscript(items: readonly TranscriptItem[], width: number, cwd: string, hideThinking?: boolean): string[];