pi-fabric 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 (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +635 -0
  3. package/THIRD_PARTY_NOTICES.md +9 -0
  4. package/dist/activity/store.d.ts +27 -0
  5. package/dist/activity/store.d.ts.map +1 -0
  6. package/dist/activity/store.js +474 -0
  7. package/dist/activity/store.js.map +1 -0
  8. package/dist/activity/types.d.ts +101 -0
  9. package/dist/activity/types.d.ts.map +1 -0
  10. package/dist/activity/types.js +2 -0
  11. package/dist/activity/types.js.map +1 -0
  12. package/dist/actors/context.d.ts +13 -0
  13. package/dist/actors/context.d.ts.map +1 -0
  14. package/dist/actors/context.js +155 -0
  15. package/dist/actors/context.js.map +1 -0
  16. package/dist/actors/manager.d.ts +33 -0
  17. package/dist/actors/manager.d.ts.map +1 -0
  18. package/dist/actors/manager.js +751 -0
  19. package/dist/actors/manager.js.map +1 -0
  20. package/dist/actors/types.d.ts +67 -0
  21. package/dist/actors/types.d.ts.map +1 -0
  22. package/dist/actors/types.js +2 -0
  23. package/dist/actors/types.js.map +1 -0
  24. package/dist/capture/catalog.d.ts +23 -0
  25. package/dist/capture/catalog.d.ts.map +1 -0
  26. package/dist/capture/catalog.js +53 -0
  27. package/dist/capture/catalog.js.map +1 -0
  28. package/dist/capture/interceptor.d.ts +13 -0
  29. package/dist/capture/interceptor.d.ts.map +1 -0
  30. package/dist/capture/interceptor.js +130 -0
  31. package/dist/capture/interceptor.js.map +1 -0
  32. package/dist/commands/fabric.d.ts +14 -0
  33. package/dist/commands/fabric.d.ts.map +1 -0
  34. package/dist/commands/fabric.js +237 -0
  35. package/dist/commands/fabric.js.map +1 -0
  36. package/dist/config.d.ts +89 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +255 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/core/action-registry.d.ts +71 -0
  41. package/dist/core/action-registry.d.ts.map +1 -0
  42. package/dist/core/action-registry.js +273 -0
  43. package/dist/core/action-registry.js.map +1 -0
  44. package/dist/core/approval-controller.d.ts +11 -0
  45. package/dist/core/approval-controller.d.ts.map +1 -0
  46. package/dist/core/approval-controller.js +45 -0
  47. package/dist/core/approval-controller.js.map +1 -0
  48. package/dist/core/pi-tools.d.ts +4 -0
  49. package/dist/core/pi-tools.d.ts.map +1 -0
  50. package/dist/core/pi-tools.js +11 -0
  51. package/dist/core/pi-tools.js.map +1 -0
  52. package/dist/core/tool-ownership.d.ts +12 -0
  53. package/dist/core/tool-ownership.d.ts.map +1 -0
  54. package/dist/core/tool-ownership.js +41 -0
  55. package/dist/core/tool-ownership.js.map +1 -0
  56. package/dist/execution-service.d.ts +41 -0
  57. package/dist/execution-service.d.ts.map +1 -0
  58. package/dist/execution-service.js +210 -0
  59. package/dist/execution-service.js.map +1 -0
  60. package/dist/fabric-state.d.ts +38 -0
  61. package/dist/fabric-state.d.ts.map +1 -0
  62. package/dist/fabric-state.js +275 -0
  63. package/dist/fabric-state.js.map +1 -0
  64. package/dist/index.d.ts +4 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +491 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/mesh/store.d.ts +68 -0
  69. package/dist/mesh/store.d.ts.map +1 -0
  70. package/dist/mesh/store.js +387 -0
  71. package/dist/mesh/store.js.map +1 -0
  72. package/dist/protocol.d.ts +62 -0
  73. package/dist/protocol.d.ts.map +1 -0
  74. package/dist/protocol.js +3 -0
  75. package/dist/protocol.js.map +1 -0
  76. package/dist/providers/agents-provider.d.ts +15 -0
  77. package/dist/providers/agents-provider.d.ts.map +1 -0
  78. package/dist/providers/agents-provider.js +393 -0
  79. package/dist/providers/agents-provider.js.map +1 -0
  80. package/dist/providers/captured-tools-provider.d.ts +23 -0
  81. package/dist/providers/captured-tools-provider.d.ts.map +1 -0
  82. package/dist/providers/captured-tools-provider.js +170 -0
  83. package/dist/providers/captured-tools-provider.js.map +1 -0
  84. package/dist/providers/mcp-provider.d.ts +15 -0
  85. package/dist/providers/mcp-provider.d.ts.map +1 -0
  86. package/dist/providers/mcp-provider.js +289 -0
  87. package/dist/providers/mcp-provider.js.map +1 -0
  88. package/dist/providers/mesh-provider.d.ts +13 -0
  89. package/dist/providers/mesh-provider.d.ts.map +1 -0
  90. package/dist/providers/mesh-provider.js +175 -0
  91. package/dist/providers/mesh-provider.js.map +1 -0
  92. package/dist/providers/pi-tools-provider.d.ts +14 -0
  93. package/dist/providers/pi-tools-provider.d.ts.map +1 -0
  94. package/dist/providers/pi-tools-provider.js +200 -0
  95. package/dist/providers/pi-tools-provider.js.map +1 -0
  96. package/dist/runtime/guest-types.d.ts +3 -0
  97. package/dist/runtime/guest-types.d.ts.map +1 -0
  98. package/dist/runtime/guest-types.js +313 -0
  99. package/dist/runtime/guest-types.js.map +1 -0
  100. package/dist/runtime/quickjs-runtime.d.ts +18 -0
  101. package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
  102. package/dist/runtime/quickjs-runtime.js +538 -0
  103. package/dist/runtime/quickjs-runtime.js.map +1 -0
  104. package/dist/runtime/type-checker.d.ts +10 -0
  105. package/dist/runtime/type-checker.d.ts.map +1 -0
  106. package/dist/runtime/type-checker.js +71 -0
  107. package/dist/runtime/type-checker.js.map +1 -0
  108. package/dist/subagents/budget-ledger.d.ts +61 -0
  109. package/dist/subagents/budget-ledger.d.ts.map +1 -0
  110. package/dist/subagents/budget-ledger.js +92 -0
  111. package/dist/subagents/budget-ledger.js.map +1 -0
  112. package/dist/subagents/manager.d.ts +27 -0
  113. package/dist/subagents/manager.d.ts.map +1 -0
  114. package/dist/subagents/manager.js +497 -0
  115. package/dist/subagents/manager.js.map +1 -0
  116. package/dist/subagents/semaphore.d.ts +7 -0
  117. package/dist/subagents/semaphore.d.ts.map +1 -0
  118. package/dist/subagents/semaphore.js +54 -0
  119. package/dist/subagents/semaphore.js.map +1 -0
  120. package/dist/subagents/transports/localterm-transport.d.ts +7 -0
  121. package/dist/subagents/transports/localterm-transport.d.ts.map +1 -0
  122. package/dist/subagents/transports/localterm-transport.js +54 -0
  123. package/dist/subagents/transports/localterm-transport.js.map +1 -0
  124. package/dist/subagents/transports/process-transport.d.ts +7 -0
  125. package/dist/subagents/transports/process-transport.d.ts.map +1 -0
  126. package/dist/subagents/transports/process-transport.js +17 -0
  127. package/dist/subagents/transports/process-transport.js.map +1 -0
  128. package/dist/subagents/transports/process-utils.d.ts +16 -0
  129. package/dist/subagents/transports/process-utils.d.ts.map +1 -0
  130. package/dist/subagents/transports/process-utils.js +57 -0
  131. package/dist/subagents/transports/process-utils.js.map +1 -0
  132. package/dist/subagents/transports/screen-transport.d.ts +7 -0
  133. package/dist/subagents/transports/screen-transport.d.ts.map +1 -0
  134. package/dist/subagents/transports/screen-transport.js +33 -0
  135. package/dist/subagents/transports/screen-transport.js.map +1 -0
  136. package/dist/subagents/transports/tmux-transport.d.ts +7 -0
  137. package/dist/subagents/transports/tmux-transport.d.ts.map +1 -0
  138. package/dist/subagents/transports/tmux-transport.js +41 -0
  139. package/dist/subagents/transports/tmux-transport.js.map +1 -0
  140. package/dist/subagents/types.d.ts +132 -0
  141. package/dist/subagents/types.d.ts.map +1 -0
  142. package/dist/subagents/types.js +2 -0
  143. package/dist/subagents/types.js.map +1 -0
  144. package/dist/subagents/worktree-manager.d.ts +12 -0
  145. package/dist/subagents/worktree-manager.d.ts.map +1 -0
  146. package/dist/subagents/worktree-manager.js +53 -0
  147. package/dist/subagents/worktree-manager.js.map +1 -0
  148. package/dist/ui/controller.d.ts +14 -0
  149. package/dist/ui/controller.d.ts.map +1 -0
  150. package/dist/ui/controller.js +153 -0
  151. package/dist/ui/controller.js.map +1 -0
  152. package/dist/ui/dashboard.d.ts +36 -0
  153. package/dist/ui/dashboard.d.ts.map +1 -0
  154. package/dist/ui/dashboard.js +620 -0
  155. package/dist/ui/dashboard.js.map +1 -0
  156. package/dist/ui/fabric-render.d.ts +28 -0
  157. package/dist/ui/fabric-render.d.ts.map +1 -0
  158. package/dist/ui/fabric-render.js +280 -0
  159. package/dist/ui/fabric-render.js.map +1 -0
  160. package/dist/ui/format.d.ts +7 -0
  161. package/dist/ui/format.d.ts.map +1 -0
  162. package/dist/ui/format.js +57 -0
  163. package/dist/ui/format.js.map +1 -0
  164. package/dist/ui/highlight.d.ts +13 -0
  165. package/dist/ui/highlight.d.ts.map +1 -0
  166. package/dist/ui/highlight.js +337 -0
  167. package/dist/ui/highlight.js.map +1 -0
  168. package/dist/ui/settings.d.ts +21 -0
  169. package/dist/ui/settings.d.ts.map +1 -0
  170. package/dist/ui/settings.js +480 -0
  171. package/dist/ui/settings.js.map +1 -0
  172. package/dist/ui/snapshot.d.ts +5 -0
  173. package/dist/ui/snapshot.d.ts.map +1 -0
  174. package/dist/ui/snapshot.js +120 -0
  175. package/dist/ui/snapshot.js.map +1 -0
  176. package/dist/ui/types.d.ts +57 -0
  177. package/dist/ui/types.d.ts.map +1 -0
  178. package/dist/ui/types.js +11 -0
  179. package/dist/ui/types.js.map +1 -0
  180. package/dist/ui/widget.d.ts +16 -0
  181. package/dist/ui/widget.d.ts.map +1 -0
  182. package/dist/ui/widget.js +199 -0
  183. package/dist/ui/widget.js.map +1 -0
  184. package/dist/util.d.ts +2 -0
  185. package/dist/util.d.ts.map +1 -0
  186. package/dist/util.js +10 -0
  187. package/dist/util.js.map +1 -0
  188. package/dist/worker.d.ts +3 -0
  189. package/dist/worker.d.ts.map +1 -0
  190. package/dist/worker.js +478 -0
  191. package/dist/worker.js.map +1 -0
  192. package/package.json +87 -0
  193. package/skills/fabric-advisor/SKILL.md +53 -0
  194. package/skills/fabric-ambient/SKILL.md +84 -0
  195. package/skills/fabric-council/SKILL.md +37 -0
  196. package/skills/fabric-exec/SKILL.md +39 -0
  197. package/skills/fabric-exec/references/agents.md +78 -0
  198. package/skills/fabric-exec/references/mcp.md +46 -0
  199. package/skills/fabric-exec/references/mesh.md +42 -0
  200. package/skills/fabric-rlm/SKILL.md +84 -0
  201. package/skills/fabric-supervisor/SKILL.md +63 -0
  202. package/skills/fabric-swarm/SKILL.md +85 -0
  203. package/skills/fabric-workflow/SKILL.md +69 -0
@@ -0,0 +1,620 @@
1
+ import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { formatClock, formatDuration, formatTokens, padToWidth, safeText, wrapPlainText } from "./format.js";
3
+ import { isActiveStatus } from "./types.js";
4
+ const filters = ["all", "active", "completed", "failed"];
5
+ const spinnerFrames = ["◐", "◓", "◑", "◒"];
6
+ const statusGlyph = (status) => {
7
+ if (status === "completed" || status === "done")
8
+ return "✓";
9
+ if (status === "failed" || status === "timed_out")
10
+ return "✗";
11
+ if (status === "blocked")
12
+ return "!";
13
+ if (status === "stopped" || status === "cancelled")
14
+ return "■";
15
+ if (status === "queued" || status === "pending" || status === "ready")
16
+ return "○";
17
+ if (status === "idle" || status === "state")
18
+ return "·";
19
+ return spinnerFrames[Math.floor(Date.now() / 250) % spinnerFrames.length] ?? "●";
20
+ };
21
+ const colorStatus = (theme, status, value) => {
22
+ if (status === "completed" || status === "done")
23
+ return theme.fg("success", value);
24
+ if (status === "failed" || status === "timed_out" || status === "error") {
25
+ return theme.fg("error", value);
26
+ }
27
+ if (status === "blocked" || status === "warning")
28
+ return theme.fg("warning", value);
29
+ if (status === "running" || status === "in_progress")
30
+ return theme.fg("accent", value);
31
+ return theme.fg("dim", value);
32
+ };
33
+ const phaseWork = (run, phaseId) => [
34
+ ...run.calls.filter((call) => call.phaseId === phaseId),
35
+ ...run.items.filter((item) => item.phaseId === phaseId),
36
+ ];
37
+ const phasePanels = (snapshot, run) => {
38
+ const panels = run?.phases.map((phase) => {
39
+ const work = phaseWork(run, phase.id);
40
+ return {
41
+ id: phase.id,
42
+ name: phase.name,
43
+ status: phase.status,
44
+ completed: work.filter((entry) => entry.status === "completed").length,
45
+ total: Math.max(phase.total ?? 0, work.length),
46
+ phase,
47
+ session: false,
48
+ };
49
+ }) ?? [];
50
+ const sessionTotal = snapshot.actors.length + snapshot.state.length;
51
+ if (sessionTotal > 0 || panels.length === 0) {
52
+ panels.push({
53
+ id: "__fabric_session",
54
+ name: "Actors & shared state",
55
+ status: snapshot.actors.some((actor) => actor.lastError)
56
+ ? "failed"
57
+ : snapshot.actors.some((actor) => isActiveStatus(actor.status)) ||
58
+ snapshot.state.some((entry) => isActiveStatus(entry.status))
59
+ ? "running"
60
+ : "idle",
61
+ completed: snapshot.actors.filter((actor) => actor.status === "stopped").length,
62
+ total: sessionTotal,
63
+ session: true,
64
+ });
65
+ }
66
+ return panels;
67
+ };
68
+ const linkedAgent = (call, agent) => Boolean(call.entityId &&
69
+ (agent.id.startsWith(call.entityId) || call.entityId.startsWith(agent.id)));
70
+ const entitiesFor = (snapshot, run, panel) => {
71
+ if (!panel || panel.session) {
72
+ const actors = snapshot.actors.map((actor) => ({
73
+ id: `actor:${actor.id}`,
74
+ kind: "actor",
75
+ label: actor.name,
76
+ status: actor.lastError ? "failed" : actor.status,
77
+ value: actor,
78
+ }));
79
+ const state = snapshot.state.map((entry) => ({
80
+ id: `state:${entry.key}`,
81
+ kind: "state",
82
+ label: entry.label,
83
+ status: entry.status,
84
+ value: entry,
85
+ }));
86
+ const unlinkedAgents = snapshot.agents
87
+ .filter((agent) => agent.runId !== run?.id && isActiveStatus(agent.status))
88
+ .map((agent) => ({
89
+ id: `agent:${agent.id}`,
90
+ kind: "agent",
91
+ label: agent.name,
92
+ status: agent.status,
93
+ value: agent,
94
+ }));
95
+ return [...unlinkedAgents, ...actors, ...state];
96
+ }
97
+ const calls = run?.calls.filter((call) => call.phaseId === panel.id) ?? [];
98
+ const linkedAgents = snapshot.agents
99
+ .filter((agent) => (agent.runId === run?.id && agent.phaseId === panel.id) ||
100
+ calls.some((call) => linkedAgent(call, agent)))
101
+ .map((agent) => ({
102
+ id: `agent:${agent.id}`,
103
+ kind: "agent",
104
+ label: agent.name,
105
+ status: agent.status,
106
+ value: agent,
107
+ }));
108
+ const visibleCalls = calls
109
+ .filter((call) => (call.kind !== "agent" && call.kind !== "actor") ||
110
+ !snapshot.agents.some((agent) => linkedAgent(call, agent)))
111
+ .map((call) => ({
112
+ id: `call:${call.id}`,
113
+ kind: "call",
114
+ label: call.label,
115
+ status: call.status,
116
+ value: call,
117
+ }));
118
+ const items = run?.items
119
+ .filter((item) => item.phaseId === panel.id)
120
+ .map((item) => ({
121
+ id: `item:${item.id}`,
122
+ kind: "item",
123
+ label: item.label,
124
+ status: item.status,
125
+ value: item,
126
+ })) ?? [];
127
+ return [...linkedAgents, ...visibleCalls, ...items];
128
+ };
129
+ const matchesFilter = (status, filter) => {
130
+ if (filter === "all")
131
+ return true;
132
+ if (filter === "active")
133
+ return isActiveStatus(status);
134
+ if (filter === "completed")
135
+ return status === "completed" || status === "done";
136
+ return status === "failed" || status === "timed_out" || status === "blocked";
137
+ };
138
+ const tokensFor = (snapshot, run) => snapshot.agents
139
+ .filter((agent) => !run || agent.runId === run.id)
140
+ .reduce((sum, agent) => sum + (agent.usage ? agent.usage.input + agent.usage.output : 0), 0);
141
+ const entityTail = (entity, now) => {
142
+ if (entity.kind === "agent") {
143
+ const agent = entity.value;
144
+ const parts = [
145
+ agent.model,
146
+ agent.currentTool ?? (agent.status === "running" ? "thinking" : undefined),
147
+ agent.usage ? `${formatTokens(agent.usage.input + agent.usage.output)} tok` : undefined,
148
+ agent.toolCalls !== undefined ? `${agent.toolCalls} tools` : undefined,
149
+ agent.startedAt ? formatDuration((agent.finishedAt ?? now) - agent.startedAt) : undefined,
150
+ ];
151
+ return parts.filter((value) => Boolean(value)).join(" · ");
152
+ }
153
+ if (entity.kind === "actor") {
154
+ const actor = entity.value;
155
+ return [
156
+ actor.model ?? actor.worker?.model,
157
+ actor.worker?.currentTool,
158
+ actor.worker?.usage
159
+ ? `${formatTokens(actor.worker.usage.input + actor.worker.usage.output)} tok`
160
+ : undefined,
161
+ `${actor.messages} msg`,
162
+ actor.queued > 0 ? `q:${actor.queued}` : undefined,
163
+ ]
164
+ .filter((value) => Boolean(value))
165
+ .join(" · ");
166
+ }
167
+ if (entity.kind === "call") {
168
+ const call = entity.value;
169
+ return [
170
+ call.ref,
171
+ call.progress,
172
+ call.metrics?.tokens !== undefined ? `${formatTokens(call.metrics.tokens)} tok` : undefined,
173
+ call.metrics?.toolCalls !== undefined ? `${call.metrics.toolCalls} tools` : undefined,
174
+ formatDuration((call.finishedAt ?? now) - call.startedAt),
175
+ ]
176
+ .filter((value) => Boolean(value))
177
+ .join(" · ");
178
+ }
179
+ if (entity.kind === "item") {
180
+ const item = entity.value;
181
+ return [
182
+ item.current ?? item.detail,
183
+ item.total !== undefined ? `${item.completed ?? 0}/${item.total}` : undefined,
184
+ ]
185
+ .filter((value) => Boolean(value))
186
+ .join(" · ");
187
+ }
188
+ return [entity.value.owner, entity.value.detail, `v${entity.value.version}`]
189
+ .filter((value) => Boolean(value))
190
+ .join(" · ");
191
+ };
192
+ export class FabricDashboard {
193
+ tui;
194
+ theme;
195
+ snapshot;
196
+ done;
197
+ focused = false;
198
+ pane = "phases";
199
+ phaseIndex = 0;
200
+ entityIndex = 0;
201
+ runIndex = 0;
202
+ filter = "all";
203
+ phaseSelectionTouched = false;
204
+ detailId;
205
+ detailScroll = 0;
206
+ refreshTimer;
207
+ constructor(tui, theme, snapshot, done) {
208
+ this.tui = tui;
209
+ this.theme = theme;
210
+ this.snapshot = snapshot;
211
+ this.done = done;
212
+ this.focused = true;
213
+ this.refreshTimer = setInterval(() => this.tui.requestRender(), 500);
214
+ this.refreshTimer.unref();
215
+ }
216
+ handleInput(data) {
217
+ const snapshot = this.snapshot();
218
+ const run = snapshot.runs[this.runIndex];
219
+ const panels = phasePanels(snapshot, run);
220
+ this.syncPhase(run, panels);
221
+ const panel = panels[this.phaseIndex];
222
+ const entities = entitiesFor(snapshot, run, panel).filter((entity) => matchesFilter(entity.status, this.filter));
223
+ if (this.detailId) {
224
+ if (matchesKey(data, Key.escape) ||
225
+ matchesKey(data, Key.ctrl("c")) ||
226
+ matchesKey(data, Key.left) ||
227
+ data === "h") {
228
+ this.detailId = undefined;
229
+ this.detailScroll = 0;
230
+ }
231
+ else if (matchesKey(data, Key.up) || data === "k") {
232
+ this.detailScroll = Math.max(0, this.detailScroll - 1);
233
+ }
234
+ else if (matchesKey(data, Key.down) || data === "j") {
235
+ this.detailScroll++;
236
+ }
237
+ else if (matchesKey(data, Key.home) || data === "g") {
238
+ this.detailScroll = 0;
239
+ }
240
+ this.tui.requestRender();
241
+ return;
242
+ }
243
+ if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
244
+ if (this.pane === "entities") {
245
+ this.pane = "phases";
246
+ }
247
+ else {
248
+ this.done();
249
+ return;
250
+ }
251
+ }
252
+ else if (matchesKey(data, Key.tab)) {
253
+ this.pane = this.pane === "phases" ? "entities" : "phases";
254
+ }
255
+ else if (matchesKey(data, Key.left) || data === "h") {
256
+ this.pane = "phases";
257
+ }
258
+ else if (matchesKey(data, Key.right) || data === "l") {
259
+ this.pane = "entities";
260
+ }
261
+ else if (matchesKey(data, Key.up) || data === "k") {
262
+ if (this.pane === "phases") {
263
+ this.phaseIndex = Math.max(0, this.phaseIndex - 1);
264
+ this.phaseSelectionTouched = true;
265
+ }
266
+ else
267
+ this.entityIndex = Math.max(0, this.entityIndex - 1);
268
+ }
269
+ else if (matchesKey(data, Key.down) || data === "j") {
270
+ if (this.pane === "phases") {
271
+ this.phaseIndex = Math.min(Math.max(0, panels.length - 1), this.phaseIndex + 1);
272
+ this.phaseSelectionTouched = true;
273
+ this.entityIndex = 0;
274
+ }
275
+ else {
276
+ this.entityIndex = Math.min(Math.max(0, entities.length - 1), this.entityIndex + 1);
277
+ }
278
+ }
279
+ else if (matchesKey(data, Key.enter)) {
280
+ if (this.pane === "phases") {
281
+ this.pane = "entities";
282
+ }
283
+ else if (entities[this.entityIndex]) {
284
+ this.detailId = entities[this.entityIndex]?.id;
285
+ this.detailScroll = 0;
286
+ }
287
+ }
288
+ else if (data === "f") {
289
+ const next = (filters.indexOf(this.filter) + 1) % filters.length;
290
+ this.filter = filters[next] ?? "all";
291
+ this.entityIndex = 0;
292
+ }
293
+ else if (data === "[") {
294
+ this.runIndex = Math.min(Math.max(0, snapshot.runs.length - 1), this.runIndex + 1);
295
+ this.resetSelection();
296
+ }
297
+ else if (data === "]") {
298
+ this.runIndex = Math.max(0, this.runIndex - 1);
299
+ this.resetSelection();
300
+ }
301
+ else if (data === "G") {
302
+ if (this.pane === "phases") {
303
+ this.phaseIndex = Math.max(0, panels.length - 1);
304
+ this.phaseSelectionTouched = true;
305
+ }
306
+ else
307
+ this.entityIndex = Math.max(0, entities.length - 1);
308
+ }
309
+ else if (data === "g") {
310
+ if (this.pane === "phases") {
311
+ this.phaseIndex = 0;
312
+ this.phaseSelectionTouched = true;
313
+ }
314
+ else
315
+ this.entityIndex = 0;
316
+ }
317
+ this.tui.requestRender();
318
+ }
319
+ render(width) {
320
+ if (width <= 0)
321
+ return [];
322
+ const snapshot = this.snapshot();
323
+ this.runIndex = Math.max(0, Math.min(this.runIndex, Math.max(0, snapshot.runs.length - 1)));
324
+ const run = snapshot.runs[this.runIndex];
325
+ const panels = phasePanels(snapshot, run);
326
+ this.syncPhase(run, panels);
327
+ const panel = panels[this.phaseIndex];
328
+ const entities = entitiesFor(snapshot, run, panel).filter((entity) => matchesFilter(entity.status, this.filter));
329
+ this.entityIndex = Math.max(0, Math.min(this.entityIndex, Math.max(0, entities.length - 1)));
330
+ if (this.detailId) {
331
+ const detail = entities.find((entity) => entity.id === this.detailId);
332
+ if (detail)
333
+ return this.renderDetail(width, snapshot, detail);
334
+ this.detailId = undefined;
335
+ }
336
+ return this.renderOverview(width, snapshot, run, panels, panel, entities);
337
+ }
338
+ invalidate() { }
339
+ dispose() {
340
+ clearInterval(this.refreshTimer);
341
+ }
342
+ renderOverview(width, snapshot, run, panels, selectedPanel, entities) {
343
+ if (width < 24) {
344
+ return [truncateToWidth(`Fabric · ${run?.name ?? "session"}`, width)];
345
+ }
346
+ const innerWidth = width - 2;
347
+ const lines = [];
348
+ lines.push(this.topBorder(width, `Fabric · ${run?.name ?? "session"}`));
349
+ const activeAgents = snapshot.agents.filter((agent) => isActiveStatus(agent.status)).length;
350
+ const elapsed = run ? formatDuration((run.finishedAt ?? snapshot.now) - run.startedAt) : undefined;
351
+ const summary = [
352
+ run?.status,
353
+ `${activeAgents}/${snapshot.agents.length} agents active`,
354
+ `${snapshot.actors.length} actors`,
355
+ tokensFor(snapshot, run) > 0
356
+ ? `${formatTokens(tokensFor(snapshot, run))} tok`
357
+ : undefined,
358
+ elapsed,
359
+ snapshot.runs.length > 1 ? `run ${this.runIndex + 1}/${snapshot.runs.length}` : undefined,
360
+ ]
361
+ .filter((value) => Boolean(value))
362
+ .join(" · ");
363
+ const summaryText = safeText(summary);
364
+ let headerLine = summaryText;
365
+ if (run?.description) {
366
+ const gap = " ";
367
+ const availableDescription = innerWidth - visibleWidth(summaryText) - gap.length;
368
+ headerLine =
369
+ availableDescription >= 12
370
+ ? `${padToWidth(this.theme.fg("muted", safeText(run.description)), availableDescription)}${gap}${this.theme.fg("dim", summaryText)}`
371
+ : this.theme.fg("dim", summaryText);
372
+ }
373
+ else if (summaryText) {
374
+ headerLine = this.theme.fg("dim", summaryText);
375
+ }
376
+ lines.push(this.row(width, headerLine || this.theme.fg("muted", "No Fabric activity yet")));
377
+ lines.push(this.middleBorder(width));
378
+ const maxBody = Math.max(8, Math.min(22, (process.stdout.rows ?? 28) - 10));
379
+ if (innerWidth >= 88) {
380
+ const leftWidth = Math.min(38, Math.max(28, Math.floor((innerWidth - 1) * 0.34)));
381
+ const rightWidth = innerWidth - leftWidth - 1;
382
+ const leftLines = this.renderPhasePanel(panels, leftWidth, maxBody);
383
+ const rightLines = this.renderEntityPanel(entities, rightWidth, maxBody, snapshot.now, selectedPanel);
384
+ for (let index = 0; index < maxBody; index++) {
385
+ const left = leftLines[index] ?? "";
386
+ const right = rightLines[index] ?? "";
387
+ lines.push(this.row(width, `${padToWidth(left, leftWidth)}${this.theme.fg("borderMuted", "│")}${padToWidth(right, rightWidth)}`));
388
+ }
389
+ }
390
+ else {
391
+ const phaseHeight = Math.min(Math.max(3, panels.length + 1), Math.floor(maxBody * 0.45));
392
+ const entityHeight = Math.max(3, maxBody - phaseHeight - 1);
393
+ for (const line of this.renderPhasePanel(panels, innerWidth, phaseHeight)) {
394
+ lines.push(this.row(width, line));
395
+ }
396
+ lines.push(this.row(width, this.theme.fg("borderMuted", "─".repeat(innerWidth))));
397
+ for (const line of this.renderEntityPanel(entities, innerWidth, entityHeight, snapshot.now, selectedPanel)) {
398
+ lines.push(this.row(width, line));
399
+ }
400
+ }
401
+ const runEvents = run?.events.slice(-2) ?? [];
402
+ const meshEventCount = Math.max(0, 2 - runEvents.length);
403
+ const meshEvents = meshEventCount > 0 ? snapshot.events.slice(-meshEventCount) : [];
404
+ if (runEvents.length > 0 || meshEvents.length > 0) {
405
+ lines.push(this.middleBorder(width));
406
+ for (const event of runEvents) {
407
+ lines.push(this.row(width, colorStatus(this.theme, event.level === "success" ? "completed" : event.level, `[${formatClock(event.createdAt)}] ${safeText(event.message)}`)));
408
+ }
409
+ for (const event of meshEvents) {
410
+ const target = event.to ? ` → ${event.to}` : "";
411
+ const text = event.text ? ` · ${safeText(event.text)}` : "";
412
+ lines.push(this.row(width, this.theme.fg("dim", `[${formatClock(event.createdAt)}] ${event.topic} · ${event.from.name}${target}${text}`)));
413
+ }
414
+ }
415
+ lines.push(this.middleBorder(width));
416
+ lines.push(this.row(width, this.theme.fg("dim", `↑↓/jk select · ←→/tab pane · enter inspect · f filter:${this.filter} · [ ] runs · esc close`)));
417
+ lines.push(this.bottomBorder(width));
418
+ return lines.map((line) => truncateToWidth(line, width, ""));
419
+ }
420
+ renderPhasePanel(panels, width, height) {
421
+ const lines = [
422
+ truncateToWidth(`${this.pane === "phases" ? this.theme.fg("accent", "▸ ") : " "}${this.theme.fg("accent", "Phases")}`, width),
423
+ ];
424
+ const available = Math.max(0, height - 1);
425
+ const start = Math.max(0, Math.min(this.phaseIndex - Math.floor(available / 2), Math.max(0, panels.length - available)));
426
+ for (let index = start; index < Math.min(panels.length, start + available); index++) {
427
+ const panel = panels[index];
428
+ if (!panel)
429
+ continue;
430
+ const selected = index === this.phaseIndex;
431
+ const prefix = selected ? "› " : " ";
432
+ const count = panel.total > 0 ? `${panel.completed}/${panel.total}` : "";
433
+ const raw = `${prefix}${colorStatus(this.theme, panel.status, statusGlyph(panel.status))} ${safeText(panel.name)}`;
434
+ const countWidth = visibleWidth(count);
435
+ const contentWidth = Math.max(0, width - countWidth - (count ? 1 : 0));
436
+ let line = `${padToWidth(raw, contentWidth)}${count ? ` ${this.theme.fg("dim", count)}` : ""}`;
437
+ if (selected && this.pane === "phases")
438
+ line = this.theme.bg("selectedBg", padToWidth(line, width));
439
+ lines.push(truncateToWidth(line, width, ""));
440
+ }
441
+ while (lines.length < height)
442
+ lines.push("");
443
+ return lines.slice(0, height);
444
+ }
445
+ renderEntityPanel(entities, width, height, now, panel) {
446
+ const heading = panel?.name ?? "Activity";
447
+ const headingDetail = panel?.phase?.description
448
+ ? this.theme.fg("dim", ` · ${safeText(panel.phase.description)}`)
449
+ : "";
450
+ const lines = [
451
+ truncateToWidth(`${this.pane === "entities" ? this.theme.fg("accent", "▸ ") : " "}${this.theme.fg("accent", safeText(heading))}${headingDetail}${this.filter !== "all" ? this.theme.fg("dim", ` · ${this.filter}`) : ""}`, width),
452
+ ];
453
+ const available = Math.max(0, height - 1);
454
+ const start = Math.max(0, Math.min(this.entityIndex - Math.floor(available / 2), Math.max(0, entities.length - available)));
455
+ for (let index = start; index < Math.min(entities.length, start + available); index++) {
456
+ const entity = entities[index];
457
+ if (!entity)
458
+ continue;
459
+ const selected = index === this.entityIndex;
460
+ const prefix = selected ? "› " : " ";
461
+ const lead = `${prefix}${colorStatus(this.theme, entity.status, statusGlyph(entity.status))} ${safeText(entity.label)}`;
462
+ const tail = safeText(entityTail(entity, now));
463
+ let line = tail ? `${lead} ${this.theme.fg("dim", tail)}` : lead;
464
+ if (selected && this.pane === "entities")
465
+ line = this.theme.bg("selectedBg", padToWidth(line, width));
466
+ lines.push(truncateToWidth(line, width, ""));
467
+ }
468
+ if (entities.length === 0 && available > 0) {
469
+ lines.push(this.theme.fg("dim", " (no matching activity)"));
470
+ }
471
+ while (lines.length < height)
472
+ lines.push("");
473
+ return lines.slice(0, height);
474
+ }
475
+ renderDetail(width, snapshot, entity) {
476
+ if (width < 24)
477
+ return [truncateToWidth(entity.label, width)];
478
+ const innerWidth = width - 2;
479
+ const lines = [this.topBorder(width, `${entity.kind} · ${entity.label}`)];
480
+ const content = this.detailLines(entity, innerWidth, snapshot.now);
481
+ const maxBody = Math.max(8, Math.min(24, (process.stdout.rows ?? 28) - 7));
482
+ const maxScroll = Math.max(0, content.length - maxBody);
483
+ this.detailScroll = Math.max(0, Math.min(this.detailScroll, maxScroll));
484
+ const visible = content.slice(this.detailScroll, this.detailScroll + maxBody);
485
+ for (const line of visible)
486
+ lines.push(this.row(width, line));
487
+ while (lines.length < maxBody + 1)
488
+ lines.push(this.row(width, ""));
489
+ lines.push(this.middleBorder(width));
490
+ lines.push(this.row(width, this.theme.fg("dim", `j/k scroll · esc back${content.length > maxBody ? ` · ${this.detailScroll + 1}-${Math.min(content.length, this.detailScroll + maxBody)}/${content.length}` : ""}`)));
491
+ lines.push(this.bottomBorder(width));
492
+ return lines.map((line) => truncateToWidth(line, width, ""));
493
+ }
494
+ detailLines(entity, width, now) {
495
+ const lines = [];
496
+ const field = (label, value) => {
497
+ const text = safeText(value);
498
+ if (!text)
499
+ return;
500
+ const prefix = `${this.theme.fg("dim", `${label}:`)} `;
501
+ const wrapped = wrapPlainText(text, Math.max(1, width - visibleWidth(prefix)), 12);
502
+ if (wrapped[0])
503
+ lines.push(truncateToWidth(prefix + wrapped[0], width));
504
+ for (const continuation of wrapped.slice(1)) {
505
+ lines.push(truncateToWidth(" ".repeat(visibleWidth(prefix)) + continuation, width));
506
+ }
507
+ };
508
+ field("Status", entity.status);
509
+ if (entity.kind === "agent") {
510
+ const agent = entity.value;
511
+ field("ID", agent.id);
512
+ field("Model", agent.model);
513
+ field("Transport", agent.transport);
514
+ field("Activity", agent.currentTool);
515
+ field("Elapsed", agent.startedAt ? formatDuration((agent.finishedAt ?? now) - agent.startedAt) : undefined);
516
+ field("Usage", agent.usage ? `${formatTokens(agent.usage.input + agent.usage.output)} tokens · ${agent.toolCalls ?? 0} tools · ${agent.turns ?? 0} turns · $${agent.usage.cost.toFixed(4)}` : undefined);
517
+ field("Task", agent.task);
518
+ field("Branch", agent.branch);
519
+ field("Worktree", agent.worktree);
520
+ field("Attach", agent.attachCommand);
521
+ field("Error", agent.error);
522
+ field("Result", agent.text);
523
+ }
524
+ else if (entity.kind === "actor") {
525
+ const actor = entity.value;
526
+ field("ID", actor.id);
527
+ field("Model", actor.model ?? actor.worker?.model);
528
+ field("Delivery", `${actor.delivery} · ${actor.responseMode}`);
529
+ field("Activity", actor.worker?.currentTool);
530
+ field("Transport", actor.worker?.transport);
531
+ field("Usage", actor.worker?.usage
532
+ ? `${formatTokens(actor.worker.usage.input + actor.worker.usage.output)} tokens · ${actor.worker.toolCalls ?? 0} tools`
533
+ : undefined);
534
+ field("Host events", actor.events.join(", "));
535
+ field("Topics", actor.topics.join(", "));
536
+ field("Queue", actor.queued);
537
+ field("Last error", actor.lastError);
538
+ if (actor.recentMessages.length > 0) {
539
+ lines.push("");
540
+ lines.push(this.theme.fg("accent", "Recent mailbox"));
541
+ for (const message of actor.recentMessages) {
542
+ const text = message.text ?? message.action ?? message.error ?? "data";
543
+ field(`${message.direction === "in" ? "→" : "←"} ${formatClock(message.createdAt)} ${message.source}`, text);
544
+ }
545
+ }
546
+ }
547
+ else if (entity.kind === "call") {
548
+ const call = entity.value;
549
+ field("Reference", call.ref);
550
+ field("ID", call.id);
551
+ field("Kind", call.kind);
552
+ field("Progress", call.progress);
553
+ field("Elapsed", formatDuration((call.finishedAt ?? now) - call.startedAt));
554
+ field("Tokens", call.metrics?.tokens);
555
+ field("Tool calls", call.metrics?.toolCalls);
556
+ field("Cost", call.metrics?.cost);
557
+ field("Entity", call.entityId);
558
+ field("Error", call.error);
559
+ }
560
+ else if (entity.kind === "item") {
561
+ const item = entity.value;
562
+ field("ID", item.id);
563
+ field("Kind", item.kind);
564
+ field("Progress", item.total !== undefined ? `${item.completed ?? 0}/${item.total}` : undefined);
565
+ field("Current", item.current);
566
+ field("Detail", item.detail);
567
+ field("Data", item.data === undefined ? undefined : JSON.stringify(item.data));
568
+ }
569
+ else {
570
+ const entry = entity.value;
571
+ field("Key", entry.key);
572
+ field("Owner", entry.owner);
573
+ field("Version", entry.version);
574
+ field("Updated", new Date(entry.updatedAt).toLocaleString());
575
+ field("Detail", entry.detail);
576
+ field("Value", JSON.stringify(entry.value));
577
+ }
578
+ return lines.length > 0 ? lines : [this.theme.fg("dim", "No details")];
579
+ }
580
+ syncPhase(run, panels) {
581
+ if (panels.length === 0) {
582
+ this.phaseIndex = 0;
583
+ return;
584
+ }
585
+ if (!this.phaseSelectionTouched && run?.currentPhaseId) {
586
+ const current = panels.findIndex((panel) => panel.id === run.currentPhaseId);
587
+ if (current >= 0)
588
+ this.phaseIndex = current;
589
+ }
590
+ this.phaseIndex = Math.max(0, Math.min(this.phaseIndex, panels.length - 1));
591
+ }
592
+ resetSelection() {
593
+ this.phaseIndex = 0;
594
+ this.entityIndex = 0;
595
+ this.phaseSelectionTouched = false;
596
+ this.detailId = undefined;
597
+ this.detailScroll = 0;
598
+ this.pane = "phases";
599
+ }
600
+ topBorder(width, title) {
601
+ const border = (value) => this.theme.fg("borderMuted", value);
602
+ const safeTitle = truncateToWidth(safeText(title), Math.max(0, width - 6));
603
+ const styledTitle = ` ${this.theme.fg("accent", safeTitle)} `;
604
+ const remaining = Math.max(0, width - 2 - visibleWidth(styledTitle));
605
+ const left = Math.floor(remaining / 2);
606
+ const right = remaining - left;
607
+ return `${border(`╭${"─".repeat(left)}`)}${styledTitle}${border(`${"─".repeat(right)}╮`)}`;
608
+ }
609
+ middleBorder(width) {
610
+ return this.theme.fg("borderMuted", `├${"─".repeat(Math.max(0, width - 2))}┤`);
611
+ }
612
+ bottomBorder(width) {
613
+ return this.theme.fg("borderMuted", `╰${"─".repeat(Math.max(0, width - 2))}╯`);
614
+ }
615
+ row(width, content) {
616
+ const innerWidth = Math.max(0, width - 2);
617
+ return `${this.theme.fg("borderMuted", "│")}${padToWidth(content, innerWidth)}${this.theme.fg("borderMuted", "│")}`;
618
+ }
619
+ }
620
+ //# sourceMappingURL=dashboard.js.map