subharness 0.0.3 → 0.0.5

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 (241) hide show
  1. package/README.md +39 -9
  2. package/dist/adapters/approval-context.d.ts +2 -0
  3. package/dist/adapters/approval-context.js +9 -0
  4. package/dist/adapters/approval-context.js.map +1 -0
  5. package/dist/adapters/approval-lifetime.d.ts +8 -0
  6. package/dist/adapters/approval-lifetime.js +50 -0
  7. package/dist/adapters/approval-lifetime.js.map +1 -0
  8. package/dist/adapters/claude-approvals.d.ts +4 -0
  9. package/dist/adapters/claude-approvals.js +32 -0
  10. package/dist/adapters/claude-approvals.js.map +1 -0
  11. package/dist/adapters/claude-permissions.js +1 -1
  12. package/dist/adapters/claude-permissions.js.map +1 -1
  13. package/dist/adapters/claude-process.d.ts +3 -0
  14. package/dist/adapters/claude-process.js +11 -1
  15. package/dist/adapters/claude-process.js.map +1 -1
  16. package/dist/adapters/claude-result.d.ts +2 -0
  17. package/dist/adapters/claude-result.js +22 -0
  18. package/dist/adapters/claude-result.js.map +1 -0
  19. package/dist/adapters/claude.js +60 -12
  20. package/dist/adapters/claude.js.map +1 -1
  21. package/dist/adapters/codex-approvals.d.ts +6 -0
  22. package/dist/adapters/codex-approvals.js +111 -0
  23. package/dist/adapters/codex-approvals.js.map +1 -0
  24. package/dist/adapters/codex.js +36 -3
  25. package/dist/adapters/codex.js.map +1 -1
  26. package/dist/adapters/copilot-permissions.d.ts +26 -0
  27. package/dist/adapters/copilot-permissions.js +121 -0
  28. package/dist/adapters/copilot-permissions.js.map +1 -0
  29. package/dist/adapters/copilot-tools.d.ts +12 -0
  30. package/dist/adapters/copilot-tools.js +61 -0
  31. package/dist/adapters/copilot-tools.js.map +1 -0
  32. package/dist/adapters/copilot.d.ts +108 -0
  33. package/dist/adapters/copilot.js +819 -0
  34. package/dist/adapters/copilot.js.map +1 -0
  35. package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
  36. package/dist/adapters/cursor-cli-approvals.js +83 -0
  37. package/dist/adapters/cursor-cli-approvals.js.map +1 -0
  38. package/dist/adapters/cursor-cli-model.d.ts +4 -0
  39. package/dist/adapters/cursor-cli-model.js +64 -0
  40. package/dist/adapters/cursor-cli-model.js.map +1 -0
  41. package/dist/adapters/cursor-cli-session.d.ts +32 -0
  42. package/dist/adapters/cursor-cli-session.js +316 -0
  43. package/dist/adapters/cursor-cli-session.js.map +1 -0
  44. package/dist/adapters/cursor-cli-tools.d.ts +20 -0
  45. package/dist/adapters/cursor-cli-tools.js +181 -0
  46. package/dist/adapters/cursor-cli-tools.js.map +1 -0
  47. package/dist/adapters/cursor-cli.d.ts +2 -0
  48. package/dist/adapters/cursor-cli.js +306 -0
  49. package/dist/adapters/cursor-cli.js.map +1 -0
  50. package/dist/adapters/cursor-model.d.ts +7 -0
  51. package/dist/adapters/cursor-model.js +67 -0
  52. package/dist/adapters/cursor-model.js.map +1 -0
  53. package/dist/adapters/cursor-rpc.d.ts +42 -0
  54. package/dist/adapters/cursor-rpc.js +271 -0
  55. package/dist/adapters/cursor-rpc.js.map +1 -0
  56. package/dist/adapters/cursor-session.d.ts +4 -0
  57. package/dist/adapters/cursor-session.js +327 -0
  58. package/dist/adapters/cursor-session.js.map +1 -0
  59. package/dist/adapters/cursor-startup.d.ts +16 -0
  60. package/dist/adapters/cursor-startup.js +74 -0
  61. package/dist/adapters/cursor-startup.js.map +1 -0
  62. package/dist/adapters/cursor-tools.d.ts +11 -0
  63. package/dist/adapters/cursor-tools.js +49 -0
  64. package/dist/adapters/cursor-tools.js.map +1 -0
  65. package/dist/adapters/cursor.d.ts +6 -0
  66. package/dist/adapters/cursor.js +17 -0
  67. package/dist/adapters/cursor.js.map +1 -0
  68. package/dist/adapters/fx-approvals.d.ts +2 -0
  69. package/dist/adapters/fx-approvals.js +14 -0
  70. package/dist/adapters/fx-approvals.js.map +1 -0
  71. package/dist/adapters/fx-auth.d.ts +12 -2
  72. package/dist/adapters/fx-auth.js +51 -61
  73. package/dist/adapters/fx-auth.js.map +1 -1
  74. package/dist/adapters/fx-profile.d.ts +8 -0
  75. package/dist/adapters/fx-profile.js +101 -0
  76. package/dist/adapters/fx-profile.js.map +1 -0
  77. package/dist/adapters/fx-rpc.d.ts +2 -0
  78. package/dist/adapters/fx-rpc.js +30 -4
  79. package/dist/adapters/fx-rpc.js.map +1 -1
  80. package/dist/adapters/fx-status.d.ts +2 -0
  81. package/dist/adapters/fx-status.js +96 -0
  82. package/dist/adapters/fx-status.js.map +1 -0
  83. package/dist/adapters/fx.js +77 -30
  84. package/dist/adapters/fx.js.map +1 -1
  85. package/dist/adapters/opencode-access.d.ts +13 -0
  86. package/dist/adapters/opencode-access.js +76 -0
  87. package/dist/adapters/opencode-access.js.map +1 -0
  88. package/dist/adapters/opencode-config.d.ts +11 -0
  89. package/dist/adapters/opencode-config.js +238 -0
  90. package/dist/adapters/opencode-config.js.map +1 -0
  91. package/dist/adapters/opencode-http.d.ts +28 -0
  92. package/dist/adapters/opencode-http.js +297 -0
  93. package/dist/adapters/opencode-http.js.map +1 -0
  94. package/dist/adapters/opencode-tools.d.ts +19 -0
  95. package/dist/adapters/opencode-tools.js +127 -0
  96. package/dist/adapters/opencode-tools.js.map +1 -0
  97. package/dist/adapters/opencode.d.ts +2 -0
  98. package/dist/adapters/opencode.js +569 -0
  99. package/dist/adapters/opencode.js.map +1 -0
  100. package/dist/adapters/rpc.d.ts +3 -0
  101. package/dist/adapters/rpc.js +45 -4
  102. package/dist/adapters/rpc.js.map +1 -1
  103. package/dist/adapters/types.d.ts +7 -0
  104. package/dist/adapters/types.js.map +1 -1
  105. package/dist/approvals/schema.d.ts +4 -0
  106. package/dist/approvals/schema.js +70 -0
  107. package/dist/approvals/schema.js.map +1 -0
  108. package/dist/approvals/types.d.ts +47 -0
  109. package/dist/approvals/types.js +2 -0
  110. package/dist/approvals/types.js.map +1 -0
  111. package/dist/cli/args.d.ts +3 -2
  112. package/dist/cli/args.js +24 -3
  113. package/dist/cli/args.js.map +1 -1
  114. package/dist/cli/dashboard-client.d.ts +5 -1
  115. package/dist/cli/dashboard-client.js +162 -15
  116. package/dist/cli/dashboard-client.js.map +1 -1
  117. package/dist/cli/dashboard-controller.d.ts +7 -0
  118. package/dist/cli/dashboard-controller.js +27 -0
  119. package/dist/cli/dashboard-controller.js.map +1 -0
  120. package/dist/cli/dashboard-detail-view.d.ts +16 -0
  121. package/dist/cli/dashboard-detail-view.js +162 -0
  122. package/dist/cli/dashboard-detail-view.js.map +1 -0
  123. package/dist/cli/dashboard-history-view.d.ts +29 -0
  124. package/dist/cli/dashboard-history-view.js +211 -0
  125. package/dist/cli/dashboard-history-view.js.map +1 -0
  126. package/dist/cli/dashboard-input.d.ts +14 -0
  127. package/dist/cli/dashboard-input.js +128 -0
  128. package/dist/cli/dashboard-input.js.map +1 -0
  129. package/dist/cli/dashboard-layout.d.ts +12 -1
  130. package/dist/cli/dashboard-layout.js +32 -30
  131. package/dist/cli/dashboard-layout.js.map +1 -1
  132. package/dist/cli/dashboard-renderer.d.ts +30 -2
  133. package/dist/cli/dashboard-renderer.js +125 -7
  134. package/dist/cli/dashboard-renderer.js.map +1 -1
  135. package/dist/cli/dashboard-style.d.ts +9 -0
  136. package/dist/cli/dashboard-style.js +36 -0
  137. package/dist/cli/dashboard-style.js.map +1 -0
  138. package/dist/cli/dashboard.d.ts +5 -3
  139. package/dist/cli/dashboard.js +102 -12
  140. package/dist/cli/dashboard.js.map +1 -1
  141. package/dist/cli/help.d.ts +1 -1
  142. package/dist/cli/help.js +37 -12
  143. package/dist/cli/help.js.map +1 -1
  144. package/dist/cli/input.d.ts +1 -0
  145. package/dist/cli/input.js +23 -17
  146. package/dist/cli/input.js.map +1 -1
  147. package/dist/cli/main.js +6 -2
  148. package/dist/cli/main.js.map +1 -1
  149. package/dist/cli/output.js +17 -1
  150. package/dist/cli/output.js.map +1 -1
  151. package/dist/config/access-provenance.d.ts +10 -0
  152. package/dist/config/access-provenance.js +31 -0
  153. package/dist/config/access-provenance.js.map +1 -0
  154. package/dist/config/access.d.ts +12 -2
  155. package/dist/config/access.js +125 -27
  156. package/dist/config/access.js.map +1 -1
  157. package/dist/config/loader.js +1 -0
  158. package/dist/config/loader.js.map +1 -1
  159. package/dist/config/oidc.js +4 -2
  160. package/dist/config/oidc.js.map +1 -1
  161. package/dist/config/project.js +2 -1
  162. package/dist/config/project.js.map +1 -1
  163. package/dist/config/resolve-access.js +24 -10
  164. package/dist/config/resolve-access.js.map +1 -1
  165. package/dist/index.d.ts +2 -2
  166. package/dist/index.js +1 -1
  167. package/dist/index.js.map +1 -1
  168. package/dist/process-diagnostics.d.ts +2 -0
  169. package/dist/process-diagnostics.js +11 -0
  170. package/dist/process-diagnostics.js.map +1 -0
  171. package/dist/runtime/access-errors.d.ts +4 -0
  172. package/dist/runtime/access-errors.js +25 -0
  173. package/dist/runtime/access-errors.js.map +1 -0
  174. package/dist/runtime/approval-registry.d.ts +27 -0
  175. package/dist/runtime/approval-registry.js +126 -0
  176. package/dist/runtime/approval-registry.js.map +1 -0
  177. package/dist/runtime/client.js +54 -14
  178. package/dist/runtime/client.js.map +1 -1
  179. package/dist/runtime/coordinator.d.ts +17 -1
  180. package/dist/runtime/coordinator.js +124 -6
  181. package/dist/runtime/coordinator.js.map +1 -1
  182. package/dist/runtime/dashboard-sanitize.d.ts +2 -0
  183. package/dist/runtime/dashboard-sanitize.js +7 -0
  184. package/dist/runtime/dashboard-sanitize.js.map +1 -0
  185. package/dist/runtime/dashboard.d.ts +43 -1
  186. package/dist/runtime/dashboard.js +2 -3
  187. package/dist/runtime/dashboard.js.map +1 -1
  188. package/dist/runtime/definition.js +11 -1
  189. package/dist/runtime/definition.js.map +1 -1
  190. package/dist/runtime/select-native.d.ts +2 -1
  191. package/dist/runtime/select-native.js +28 -10
  192. package/dist/runtime/select-native.js.map +1 -1
  193. package/dist/runtime/service.js +24 -1
  194. package/dist/runtime/service.js.map +1 -1
  195. package/dist/runtime/types.d.ts +9 -1
  196. package/dist/runtime/types.js.map +1 -1
  197. package/dist/runtime/worker-approvals.d.ts +21 -0
  198. package/dist/runtime/worker-approvals.js +51 -0
  199. package/dist/runtime/worker-approvals.js.map +1 -0
  200. package/dist/runtime/worker-client.js +73 -20
  201. package/dist/runtime/worker-client.js.map +1 -1
  202. package/dist/runtime/worker-server.d.ts +2 -0
  203. package/dist/runtime/worker-server.js +39 -10
  204. package/dist/runtime/worker-server.js.map +1 -1
  205. package/dist/sdk/definitions.d.ts +4 -1
  206. package/dist/sdk/definitions.js +16 -2
  207. package/dist/sdk/definitions.js.map +1 -1
  208. package/dist/sdk/permission-validation.js +10 -1
  209. package/dist/sdk/permission-validation.js.map +1 -1
  210. package/dist/sdk/types.d.ts +29 -1
  211. package/dist/sdk/types.js.map +1 -1
  212. package/package.json +4 -1
  213. package/sdk/access-config.md +86 -4
  214. package/sdk/adapter-contract.md +7 -13
  215. package/sdk/additional-harnesses.md +43 -0
  216. package/sdk/agent-skill.md +7 -4
  217. package/sdk/agent.md +5 -3
  218. package/sdk/approvals.md +120 -0
  219. package/sdk/authentication.md +1 -1
  220. package/sdk/cli/dashboard-design.md +44 -4
  221. package/sdk/cli/dashboard-inspection.md +25 -0
  222. package/sdk/cli/index.md +20 -11
  223. package/sdk/cli/output.md +10 -4
  224. package/sdk/completion-notifications.md +2 -0
  225. package/sdk/config.md +1 -1
  226. package/sdk/copilot.md +53 -0
  227. package/sdk/cursor.md +68 -0
  228. package/sdk/diagnostics.md +29 -0
  229. package/sdk/distribution.md +53 -1
  230. package/sdk/evals.md +10 -4
  231. package/sdk/fx.md +51 -6
  232. package/sdk/harnesses.md +13 -13
  233. package/sdk/index.md +4 -2
  234. package/sdk/message-delivery.md +1 -1
  235. package/sdk/opencode.md +57 -0
  236. package/sdk/permissions.md +7 -5
  237. package/sdk/pr-integration.md +1 -1
  238. package/sdk/project-team.md +2 -0
  239. package/sdk/sessions.md +4 -1
  240. package/sdk/tools.md +1 -1
  241. package/sdk/v1-runtime.md +6 -6
@@ -0,0 +1,29 @@
1
+ import type { DashboardDetail, DashboardSession } from '../runtime/dashboard.js';
2
+ type Navigation = 'up' | 'down' | 'page-up' | 'page-down' | 'home' | 'end';
3
+ /** Owns session selection, the one expanded request and its bounded content cache. */
4
+ export declare class DashboardHistoryView {
5
+ private session;
6
+ private selectedId;
7
+ private expandedId?;
8
+ private detail?;
9
+ private offset;
10
+ private followsEnd;
11
+ private revealSelection;
12
+ private now;
13
+ constructor(session: DashboardSession, anchor: string);
14
+ get selectedTaskId(): string;
15
+ get expandedTaskId(): string | undefined;
16
+ get detailLoaded(): boolean;
17
+ get responseId(): string | undefined;
18
+ get workspace(): import("../runtime/dashboard.js").DashboardWorkspace;
19
+ update(session: DashboardSession): void;
20
+ toggleExpansion(): string | undefined;
21
+ private collapse;
22
+ updateDetail(detail: DashboardDetail): boolean;
23
+ navigate(key: Navigation, width: number, height: number): void;
24
+ private geometry;
25
+ private body;
26
+ lines(label: string, width: number, height: number, now: number, color?: boolean): string[];
27
+ private footer;
28
+ }
29
+ export {};
@@ -0,0 +1,211 @@
1
+ import stringWidth from 'string-width';
2
+ import { DashboardDetailView } from './dashboard-detail-view.js';
3
+ import { clipDashboardText, dashboardElapsed } from './dashboard-layout.js';
4
+ import { cleanDashboardLine, stateStyle, styledLine, workspaceLine } from './dashboard-style.js';
5
+ function pad(text, width) {
6
+ const clipped = clipDashboardText(text, width);
7
+ return `${clipped}${' '.repeat(Math.max(0, width - stringWidth(clipped)))}`;
8
+ }
9
+ /** Owns session selection, the one expanded request and its bounded content cache. */
10
+ export class DashboardHistoryView {
11
+ session;
12
+ selectedId;
13
+ expandedId;
14
+ detail;
15
+ offset = 0;
16
+ followsEnd = false;
17
+ revealSelection = true;
18
+ now = 0;
19
+ constructor(session, anchor) {
20
+ this.session = session;
21
+ this.selectedId = anchor;
22
+ }
23
+ get selectedTaskId() { return this.selectedId; }
24
+ get expandedTaskId() { return this.expandedId; }
25
+ get detailLoaded() { return this.detail !== undefined; }
26
+ get responseId() { return this.detail?.responseId; }
27
+ get workspace() { return this.session.workspace; }
28
+ update(session) {
29
+ const previous = this.session.requests.findIndex(row => row.taskId === this.selectedId);
30
+ this.session = session;
31
+ const current = session.requests.findIndex(row => row.taskId === this.selectedId);
32
+ if (this.expandedId && current >= 0)
33
+ this.offset += current - previous;
34
+ if (current < 0) {
35
+ this.selectedId = session.requests[Math.max(0, Math.min(previous, session.requests.length - 1))].taskId;
36
+ this.collapse();
37
+ this.revealSelection = true;
38
+ }
39
+ }
40
+ toggleExpansion() {
41
+ if (this.expandedId) {
42
+ this.collapse();
43
+ this.revealSelection = true;
44
+ return undefined;
45
+ }
46
+ this.expandedId = this.selectedId;
47
+ this.detail = undefined;
48
+ this.followsEnd = false;
49
+ this.revealSelection = true;
50
+ return this.expandedId;
51
+ }
52
+ collapse() {
53
+ this.expandedId = undefined;
54
+ this.detail = undefined;
55
+ this.followsEnd = false;
56
+ }
57
+ updateDetail(detail) {
58
+ if (detail.task.taskId !== this.expandedId)
59
+ return false;
60
+ if (this.detail)
61
+ this.detail.update(detail);
62
+ else
63
+ this.detail = new DashboardDetailView(detail);
64
+ return true;
65
+ }
66
+ navigate(key, width, height) {
67
+ const geometry = this.geometry(height);
68
+ if (key === 'up' || key === 'down' || !this.expandedId) {
69
+ const index = this.session.requests.findIndex(row => row.taskId === this.selectedId);
70
+ const distance = key === 'up' ? -1 : key === 'down' ? 1 : key === 'page-up' ? -geometry.body : geometry.body;
71
+ const next = key === 'home' ? 0 : key === 'end' ? this.session.requests.length - 1
72
+ : Math.max(0, Math.min(this.session.requests.length - 1, index + distance));
73
+ const id = this.session.requests[next]?.taskId;
74
+ if (id && id !== this.selectedId) {
75
+ this.selectedId = id;
76
+ this.collapse();
77
+ this.revealSelection = true;
78
+ }
79
+ return;
80
+ }
81
+ const body = this.body(width, this.now, false);
82
+ const start = body.positions.get(this.expandedId) ?? 0;
83
+ const end = Math.max(start, body.expansionEnd - geometry.body + 1);
84
+ const previous = this.offset;
85
+ if (key === 'home')
86
+ this.offset = start;
87
+ else if (key === 'end')
88
+ this.offset = end;
89
+ else if (key === 'page-up')
90
+ this.offset = Math.max(0, this.offset - geometry.body);
91
+ else
92
+ this.offset = Math.min(Math.max(0, body.lines.length - geometry.body), this.offset + geometry.body);
93
+ this.followsEnd = key === 'end';
94
+ if (previous !== this.offset || key === 'home' || key === 'end')
95
+ this.revealSelection = false;
96
+ }
97
+ geometry(height) {
98
+ if (height <= 1)
99
+ return { header: 0, footer: 0, body: Math.max(0, height) };
100
+ const header = height < 8 ? 1 : 6;
101
+ const footer = height < 8 || !this.session.subagents.length ? 0 : Math.min(Math.floor(height / 3), height - header - 1, 6);
102
+ return { header, footer, body: Math.max(1, height - header - footer) };
103
+ }
104
+ body(width, now, color) {
105
+ const lines = [];
106
+ const positions = new Map();
107
+ let expansionEnd = 0;
108
+ const requests = this.session.requests;
109
+ const ordinalWidth = requests.reduce((width, row) => Math.max(width, String(row.ordinal).length), 1);
110
+ const states = requests.map(row => `${row.state}${row.stopUnconfirmed ? ' · stop unconfirmed' : ''}`);
111
+ const stateWidth = states.reduce((width, text) => Math.max(width, stringWidth(text)), 0);
112
+ const elapsed = requests.map(row => row.dispatchedAt === undefined ? '—' : dashboardElapsed(row, now));
113
+ const elapsedWidth = elapsed.reduce((width, text) => Math.max(width, stringWidth(text)), 1);
114
+ const titleWidth = Math.max(0, width - 2 - ordinalWidth - 2 - 2 - stateWidth - 2 - elapsedWidth);
115
+ for (const [index, row] of requests.entries()) {
116
+ positions.set(row.taskId, lines.length);
117
+ const selected = row.taskId === this.selectedId;
118
+ lines.push(styledLine([
119
+ { text: selected ? '› ' : ' ', style: '1' },
120
+ { text: `${pad(String(row.ordinal), ordinalWidth)} `, style: '2' },
121
+ { text: pad(cleanDashboardLine(row.title), titleWidth), style: selected ? '1' : row.state === 'completed' ? '2' : undefined },
122
+ { text: ` ${pad(states[index], stateWidth)}`, style: stateStyle(row.state) },
123
+ { text: ` ${pad(elapsed[index], elapsedWidth)}`, style: '2' },
124
+ ], width, color, selected));
125
+ if (row.taskId === this.expandedId) {
126
+ const indent = ' '.repeat(Math.min(4, Math.max(0, width - 1)));
127
+ if (this.detail) {
128
+ for (const line of this.detail.contentLines(Math.max(1, width - indent.length))) {
129
+ const heading = ['Request', 'Response', 'Error'].includes(line);
130
+ lines.push(heading ? styledLine([{ text: indent + line, style: '1' }], width, color) : indent + line);
131
+ }
132
+ }
133
+ expansionEnd = lines.length - 1;
134
+ lines.push('');
135
+ }
136
+ }
137
+ return { lines, positions, expansionEnd };
138
+ }
139
+ lines(label, width, height, now, color = false) {
140
+ if (height <= 0)
141
+ return [];
142
+ if (width <= 0)
143
+ return Array.from({ length: height }, () => '');
144
+ this.now = now;
145
+ const geometry = this.geometry(height);
146
+ const body = this.body(width, now, color);
147
+ const selected = body.positions.get(this.selectedId) ?? 0;
148
+ if (height === 1)
149
+ this.offset = selected;
150
+ else if (this.followsEnd && this.expandedId)
151
+ this.offset = Math.max(0, body.expansionEnd - geometry.body + 1);
152
+ else if (this.revealSelection || !this.expandedId) {
153
+ if (selected < this.offset)
154
+ this.offset = selected;
155
+ else if (selected >= this.offset + geometry.body)
156
+ this.offset = selected - geometry.body + 1;
157
+ }
158
+ this.revealSelection = false;
159
+ this.offset = Math.max(0, Math.min(this.offset, Math.max(0, body.lines.length - geometry.body)));
160
+ const header = [];
161
+ if (geometry.header === 1) {
162
+ const children = this.session.subagents.length ? ` · ${this.session.subagents.length} sub-agents` : '';
163
+ header.push(styledLine([
164
+ { text: `${cleanDashboardLine(this.session.title)} · `, style: '1' },
165
+ { text: this.session.state, style: stateStyle(this.session.state) },
166
+ { text: `${this.session.queuePaused ? ' · queue paused' : ''}${children}`, style: '2' },
167
+ ], width, color));
168
+ }
169
+ else if (geometry.header) {
170
+ header.push(workspaceLine(label, width, color));
171
+ header.push(styledLine([{ text: cleanDashboardLine(this.session.title), style: '1' }], width, color));
172
+ header.push(styledLine([
173
+ { text: `${cleanDashboardLine(this.session.harness)} `, style: '2' },
174
+ { text: this.session.state, style: stateStyle(this.session.state) },
175
+ ], width, color));
176
+ header.push(styledLine([{ text: '─'.repeat(width), style: '2' }], width, color));
177
+ header.push(styledLine([{ text: `Requests${this.session.queuePaused ? ' · queue paused' : ''}`, style: '1' }], width, color));
178
+ header.push('');
179
+ }
180
+ const visible = body.lines.slice(this.offset, this.offset + geometry.body);
181
+ while (visible.length < geometry.body)
182
+ visible.push('');
183
+ return [...header, ...visible, ...this.footer(width, geometry.footer, color)];
184
+ }
185
+ footer(width, height, color) {
186
+ if (!height)
187
+ return [];
188
+ const children = this.session.subagents;
189
+ const label = styledLine([{ text: `Sub-agents · ${children.length}`, style: '1' }], width, color);
190
+ if (height === 1)
191
+ return [label];
192
+ const lines = [styledLine([{ text: '─'.repeat(width), style: '2' }], width, color), label];
193
+ const available = height - 2;
194
+ const preview = Math.max(0, Math.min(3, children.length, available - (children.length > available || children.length > 3 ? 1 : 0)));
195
+ for (const child of children.slice(0, preview)) {
196
+ const harness = cleanDashboardLine(child.harness), state = cleanDashboardLine(child.state);
197
+ const title = pad(cleanDashboardLine(child.title), Math.max(0, width - 2 - harness.length - 2 - state.length - 2));
198
+ lines.push(styledLine([
199
+ { text: ` ${title}` },
200
+ { text: ` ${harness} `, style: '2' },
201
+ { text: state, style: stateStyle(state) },
202
+ ], width, color));
203
+ }
204
+ if (preview < children.length && available > 0)
205
+ lines.push(styledLine([{ text: ` … ${children.length - preview} more`, style: '2' }], width, color));
206
+ while (lines.length < height)
207
+ lines.push('');
208
+ return lines;
209
+ }
210
+ }
211
+ //# sourceMappingURL=dashboard-history-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-history-view.js","sourceRoot":"","sources":["../../src/cli/dashboard-history-view.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAQjG,SAAS,GAAG,CAAC,IAAY,EAAE,KAAa;IACtC,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,sFAAsF;AACtF,MAAM,OAAO,oBAAoB;IAQX,OAAO;IAPnB,UAAU,CAAS;IACnB,UAAU,CAAU;IACpB,MAAM,CAAuB;IAC7B,MAAM,GAAG,CAAC,CAAC;IACX,UAAU,GAAG,KAAK,CAAC;IACnB,eAAe,GAAG,IAAI,CAAC;IACvB,GAAG,GAAG,CAAC,CAAC;IAChB,YAAoB,OAAyB,EAAE,MAAc;uBAAzC,OAAO;QACzB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;IAC3B,CAAC;IAED,IAAI,cAAc,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,IAAI,cAAc,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,IAAI,YAAY,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC;IACxD,IAAI,UAAU,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,IAAI,SAAS,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,OAAyB;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;QACxF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;QAClF,IAAI,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,CAAC;YAAE,IAAI,CAAC,MAAM,IAAI,OAAO,GAAG,QAAQ,CAAC;QACvE,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC;YACzG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC5B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEO,QAAQ;QACd,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,YAAY,CAAC,MAAuB;QAClC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU;YACxC,OAAO,KAAK,CAAC;QACf,IAAI,IAAI,CAAC,MAAM;YACb,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;;YAE3B,IAAI,CAAC,MAAM,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,GAAe,EAAE,KAAa,EAAE,MAAc;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;YACrF,MAAM,QAAQ,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC7G,MAAM,IAAI,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAChF,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC;YAC9E,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAC/C,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;gBACjC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;gBACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC9B,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7B,IAAI,GAAG,KAAK,MAAM;YAChB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;aACjB,IAAI,GAAG,KAAK,KAAK;YACpB,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;aACf,IAAI,GAAG,KAAK,SAAS;YACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;;YAEvD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtG,IAAI,CAAC,UAAU,GAAG,GAAG,KAAK,KAAK,CAAC;QAChC,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,KAAK;YAC7D,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;IACjC,CAAC;IAEO,QAAQ,CAAC,MAAc;QAC7B,IAAI,MAAM,IAAI,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;QAC7D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3H,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACzE,CAAC;IAEO,IAAI,CAAC,KAAa,EAAE,GAAW,EAAE,KAAc;QACrD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC5C,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvC,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACrG,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtG,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACvG,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,YAAY,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;QACjG,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9C,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;gBACpB,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC5C,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE;gBACnE,EAAE,IAAI,EAAE,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE;gBAC7H,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,CAAE,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;gBAC9E,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,CAAE,EAAE,YAAY,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;aAChE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;gBACnC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC/D,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;wBAChF,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;wBAChE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;oBACxG,CAAC;gBACH,CAAC;gBACD,YAAY,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;gBAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,KAAa,EAAE,KAAa,EAAE,MAAc,EAAE,GAAW,EAAE,KAAK,GAAG,KAAK;QAC5E,IAAI,MAAM,IAAI,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,IAAI,KAAK,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,MAAM,EAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,MAAM,KAAK,CAAC;YAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;aACpC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU;YACzC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;aAC9D,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAClD,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM;gBACxB,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;iBACpB,IAAI,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI;gBAC9C,IAAI,CAAC,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjG,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;YACvG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;gBACrB,EAAC,IAAI,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAC;gBAClE,EAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAC;gBACjE,EAAC,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAC;aACtF,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACpB,CAAC;aACI,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACtG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;gBACrB,EAAC,IAAI,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAC;gBACnE,EAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAC;aAClE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACjF,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YAC9H,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3E,OAAO,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI;YACnC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAChF,CAAC;IAEO,MAAM,CAAC,KAAa,EAAE,MAAc,EAAE,KAAc;QAC1D,IAAI,CAAC,MAAM;YACT,OAAO,EAAE,CAAC;QACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,QAAQ,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAClG,IAAI,MAAM,KAAK,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,MAAM,KAAK,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;QAC3F,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpI,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;YAC/C,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3F,MAAM,KAAK,GAAG,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YACnH,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;gBACpB,EAAC,IAAI,EAAE,KAAK,KAAK,EAAE,EAAC;gBACpB,EAAC,IAAI,EAAE,KAAK,OAAO,IAAI,EAAE,KAAK,EAAE,GAAG,EAAC;gBACpC,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,EAAC;aACxC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,QAAQ,CAAC,MAAM,GAAG,OAAO,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACxG,OAAO,KAAK,CAAC,MAAM,GAAG,MAAM;YAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;CACF","sourcesContent":["import stringWidth from 'string-width';\nimport type { DashboardDetail, DashboardSession } from '../runtime/dashboard.js';\nimport { DashboardDetailView } from './dashboard-detail-view.ts';\nimport { clipDashboardText, dashboardElapsed } from './dashboard-layout.ts';\nimport { cleanDashboardLine, stateStyle, styledLine, workspaceLine } from './dashboard-style.ts';\n\ntype Navigation = 'up' | 'down' | 'page-up' | 'page-down' | 'home' | 'end';\ninterface Body {\n lines: string[];\n positions: Map<string, number>;\n expansionEnd: number;\n}\nfunction pad(text: string, width: number): string {\n const clipped = clipDashboardText(text, width);\n return `${clipped}${' '.repeat(Math.max(0, width - stringWidth(clipped)))}`;\n}\n\n/** Owns session selection, the one expanded request and its bounded content cache. */\nexport class DashboardHistoryView {\n private selectedId: string;\n private expandedId?: string;\n private detail?: DashboardDetailView;\n private offset = 0;\n private followsEnd = false;\n private revealSelection = true;\n private now = 0;\n constructor(private session: DashboardSession, anchor: string) {\n this.selectedId = anchor;\n }\n\n get selectedTaskId() { return this.selectedId; }\n get expandedTaskId() { return this.expandedId; }\n get detailLoaded() { return this.detail !== undefined; }\n get responseId() { return this.detail?.responseId; }\n get workspace() { return this.session.workspace; }\n update(session: DashboardSession) {\n const previous = this.session.requests.findIndex(row => row.taskId === this.selectedId);\n this.session = session;\n const current = session.requests.findIndex(row => row.taskId === this.selectedId);\n if (this.expandedId && current >= 0) this.offset += current - previous;\n if (current < 0) {\n this.selectedId = session.requests[Math.max(0, Math.min(previous, session.requests.length - 1))]!.taskId;\n this.collapse();\n this.revealSelection = true;\n }\n }\n\n toggleExpansion(): string | undefined {\n if (this.expandedId) {\n this.collapse();\n this.revealSelection = true;\n return undefined;\n }\n this.expandedId = this.selectedId;\n this.detail = undefined;\n this.followsEnd = false;\n this.revealSelection = true;\n return this.expandedId;\n }\n\n private collapse() {\n this.expandedId = undefined;\n this.detail = undefined;\n this.followsEnd = false;\n }\n\n updateDetail(detail: DashboardDetail): boolean {\n if (detail.task.taskId !== this.expandedId)\n return false;\n if (this.detail)\n this.detail.update(detail);\n else\n this.detail = new DashboardDetailView(detail);\n return true;\n }\n\n navigate(key: Navigation, width: number, height: number) {\n const geometry = this.geometry(height);\n if (key === 'up' || key === 'down' || !this.expandedId) {\n const index = this.session.requests.findIndex(row => row.taskId === this.selectedId);\n const distance = key === 'up' ? -1 : key === 'down' ? 1 : key === 'page-up' ? -geometry.body : geometry.body;\n const next = key === 'home' ? 0 : key === 'end' ? this.session.requests.length - 1\n : Math.max(0, Math.min(this.session.requests.length - 1, index + distance));\n const id = this.session.requests[next]?.taskId;\n if (id && id !== this.selectedId) {\n this.selectedId = id;\n this.collapse();\n this.revealSelection = true;\n }\n return;\n }\n const body = this.body(width, this.now, false);\n const start = body.positions.get(this.expandedId) ?? 0;\n const end = Math.max(start, body.expansionEnd - geometry.body + 1);\n const previous = this.offset;\n if (key === 'home')\n this.offset = start;\n else if (key === 'end')\n this.offset = end;\n else if (key === 'page-up')\n this.offset = Math.max(0, this.offset - geometry.body);\n else\n this.offset = Math.min(Math.max(0, body.lines.length - geometry.body), this.offset + geometry.body);\n this.followsEnd = key === 'end';\n if (previous !== this.offset || key === 'home' || key === 'end')\n this.revealSelection = false;\n }\n\n private geometry(height: number) {\n if (height <= 1)\n return { header: 0, footer: 0, body: Math.max(0, height) };\n const header = height < 8 ? 1 : 6;\n const footer = height < 8 || !this.session.subagents.length ? 0 : Math.min(Math.floor(height / 3), height - header - 1, 6);\n return { header, footer, body: Math.max(1, height - header - footer) };\n }\n\n private body(width: number, now: number, color: boolean): Body {\n const lines: string[] = [];\n const positions = new Map<string, number>();\n let expansionEnd = 0;\n const requests = this.session.requests;\n const ordinalWidth = requests.reduce((width, row) => Math.max(width, String(row.ordinal).length), 1);\n const states = requests.map(row => `${row.state}${row.stopUnconfirmed ? ' · stop unconfirmed' : ''}`);\n const stateWidth = states.reduce((width, text) => Math.max(width, stringWidth(text)), 0);\n const elapsed = requests.map(row => row.dispatchedAt === undefined ? '—' : dashboardElapsed(row, now));\n const elapsedWidth = elapsed.reduce((width, text) => Math.max(width, stringWidth(text)), 1);\n const titleWidth = Math.max(0, width - 2 - ordinalWidth - 2 - 2 - stateWidth - 2 - elapsedWidth);\n for (const [index, row] of requests.entries()) {\n positions.set(row.taskId, lines.length);\n const selected = row.taskId === this.selectedId;\n lines.push(styledLine([\n { text: selected ? '› ' : ' ', style: '1' },\n { text: `${pad(String(row.ordinal), ordinalWidth)} `, style: '2' },\n { text: pad(cleanDashboardLine(row.title), titleWidth), style: selected ? '1' : row.state === 'completed' ? '2' : undefined },\n { text: ` ${pad(states[index]!, stateWidth)}`, style: stateStyle(row.state) },\n { text: ` ${pad(elapsed[index]!, elapsedWidth)}`, style: '2' },\n ], width, color, selected));\n if (row.taskId === this.expandedId) {\n const indent = ' '.repeat(Math.min(4, Math.max(0, width - 1)));\n if (this.detail) {\n for (const line of this.detail.contentLines(Math.max(1, width - indent.length))) {\n const heading = ['Request', 'Response', 'Error'].includes(line);\n lines.push(heading ? styledLine([{ text: indent + line, style: '1' }], width, color) : indent + line);\n }\n }\n expansionEnd = lines.length - 1;\n lines.push('');\n }\n }\n return { lines, positions, expansionEnd };\n }\n\n lines(label: string, width: number, height: number, now: number, color = false): string[] {\n if (height <= 0)\n return [];\n if (width <= 0) return Array.from({length: height}, () => '');\n this.now = now;\n const geometry = this.geometry(height);\n const body = this.body(width, now, color);\n const selected = body.positions.get(this.selectedId) ?? 0;\n if (height === 1) this.offset = selected;\n else if (this.followsEnd && this.expandedId)\n this.offset = Math.max(0, body.expansionEnd - geometry.body + 1);\n else if (this.revealSelection || !this.expandedId) {\n if (selected < this.offset)\n this.offset = selected;\n else if (selected >= this.offset + geometry.body)\n this.offset = selected - geometry.body + 1;\n }\n this.revealSelection = false;\n this.offset = Math.max(0, Math.min(this.offset, Math.max(0, body.lines.length - geometry.body)));\n const header: string[] = [];\n if (geometry.header === 1) {\n const children = this.session.subagents.length ? ` · ${this.session.subagents.length} sub-agents` : '';\n header.push(styledLine([\n {text: `${cleanDashboardLine(this.session.title)} · `, style: '1'},\n {text: this.session.state, style: stateStyle(this.session.state)},\n {text: `${this.session.queuePaused ? ' · queue paused' : ''}${children}`, style: '2'},\n ], width, color));\n }\n else if (geometry.header) {\n header.push(workspaceLine(label, width, color));\n header.push(styledLine([{ text: cleanDashboardLine(this.session.title), style: '1' }], width, color));\n header.push(styledLine([\n {text: `${cleanDashboardLine(this.session.harness)} `, style: '2'},\n {text: this.session.state, style: stateStyle(this.session.state)},\n ], width, color));\n header.push(styledLine([{ text: '─'.repeat(width), style: '2' }], width, color));\n header.push(styledLine([{ text: `Requests${this.session.queuePaused ? ' · queue paused' : ''}`, style: '1' }], width, color));\n header.push('');\n }\n const visible = body.lines.slice(this.offset, this.offset + geometry.body);\n while (visible.length < geometry.body)\n visible.push('');\n return [...header, ...visible, ...this.footer(width, geometry.footer, color)];\n }\n\n private footer(width: number, height: number, color: boolean): string[] {\n if (!height)\n return [];\n const children = this.session.subagents;\n const label = styledLine([{ text: `Sub-agents · ${children.length}`, style: '1' }], width, color);\n if (height === 1)\n return [label];\n const lines = [styledLine([{ text: '─'.repeat(width), style: '2' }], width, color), label];\n const available = height - 2;\n const preview = Math.max(0, Math.min(3, children.length, available - (children.length > available || children.length > 3 ? 1 : 0)));\n for (const child of children.slice(0, preview)) {\n const harness = cleanDashboardLine(child.harness), state = cleanDashboardLine(child.state);\n const title = pad(cleanDashboardLine(child.title), Math.max(0, width - 2 - harness.length - 2 - state.length - 2));\n lines.push(styledLine([\n {text: ` ${title}`},\n {text: ` ${harness} `, style: '2'},\n {text: state, style: stateStyle(state)},\n ], width, color));\n }\n if (preview < children.length && available > 0)\n lines.push(styledLine([{ text: ` … ${children.length - preview} more`, style: '2' }], width, color));\n while (lines.length < height)\n lines.push('');\n return lines;\n }\n}\n"]}
@@ -0,0 +1,14 @@
1
+ export type DashboardKey = 'up' | 'down' | 'page-up' | 'page-down' | 'home' | 'end' | 'enter' | 'escape' | 'interrupt';
2
+ /** Decodes raw terminal keys while retaining incomplete escape sequences across chunks. */
3
+ export declare class DashboardKeyDecoder {
4
+ private readonly emit;
5
+ private readonly escapeDelayMs;
6
+ private pending;
7
+ private timer?;
8
+ constructor(emit: (key: DashboardKey) => void, escapeDelayMs?: number);
9
+ push(chunk: Uint8Array | string): void;
10
+ private decode;
11
+ private flushIncompleteEscape;
12
+ private clearTimer;
13
+ close(): void;
14
+ }
@@ -0,0 +1,128 @@
1
+ const sequences = new Map([
2
+ ['\u001b[A', 'up'],
3
+ ['\u001b[B', 'down'],
4
+ ['\u001bOA', 'up'],
5
+ ['\u001bOB', 'down'],
6
+ ['\u001b[5~', 'page-up'],
7
+ ['\u001b[6~', 'page-down'],
8
+ ['\u001b[H', 'home'],
9
+ ['\u001b[F', 'end'],
10
+ ['\u001bOH', 'home'],
11
+ ['\u001bOF', 'end'],
12
+ ['\u001b[1~', 'home'],
13
+ ['\u001b[4~', 'end'],
14
+ ['\u001b[7~', 'home'],
15
+ ['\u001b[8~', 'end'],
16
+ ]);
17
+ /** Decodes raw terminal keys while retaining incomplete escape sequences across chunks. */
18
+ export class DashboardKeyDecoder {
19
+ emit;
20
+ escapeDelayMs;
21
+ pending = '';
22
+ timer;
23
+ constructor(emit, escapeDelayMs = 30) {
24
+ this.emit = emit;
25
+ this.escapeDelayMs = escapeDelayMs;
26
+ }
27
+ push(chunk) {
28
+ this.clearTimer();
29
+ let input;
30
+ if (typeof chunk === 'string') {
31
+ input = chunk;
32
+ }
33
+ else {
34
+ const characters = new Array(chunk.length);
35
+ for (let index = 0; index < chunk.length; index++) {
36
+ characters[index] = String.fromCharCode(chunk[index]);
37
+ }
38
+ input = characters.join('');
39
+ }
40
+ if (input.includes('\u0003')) {
41
+ this.pending = '';
42
+ this.emit('interrupt');
43
+ return;
44
+ }
45
+ this.pending += input;
46
+ this.decode();
47
+ }
48
+ decode() {
49
+ while (this.pending) {
50
+ const first = this.pending[0];
51
+ if (first === '\u0003') {
52
+ this.pending = this.pending.slice(1);
53
+ this.emit('interrupt');
54
+ continue;
55
+ }
56
+ if (first === '\r' || first === '\n') {
57
+ this.pending = this.pending.slice(1);
58
+ this.emit('enter');
59
+ continue;
60
+ }
61
+ if (first !== '\u001b') {
62
+ this.pending = this.pending.slice(1);
63
+ continue;
64
+ }
65
+ if (this.pending.length >= 2) {
66
+ const next = this.pending.charCodeAt(1);
67
+ if (next === 0x1b || next < 0x20 || next === 0x7f) {
68
+ this.pending = this.pending.slice(1);
69
+ this.emit('escape');
70
+ continue;
71
+ }
72
+ }
73
+ const sequence = [...sequences.keys()].find(candidate => this.pending.startsWith(candidate));
74
+ if (sequence) {
75
+ this.pending = this.pending.slice(sequence.length);
76
+ this.emit(sequences.get(sequence));
77
+ continue;
78
+ }
79
+ if ([...sequences.keys()].some(candidate => candidate.startsWith(this.pending))) {
80
+ this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);
81
+ return;
82
+ }
83
+ if (this.pending.startsWith('\u001b[')) {
84
+ const complete = /^\u001b\[[0-?]*[ -/]*[@-~]/u.exec(this.pending);
85
+ if (complete) {
86
+ this.pending = this.pending.slice(complete[0].length);
87
+ continue;
88
+ }
89
+ this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);
90
+ return;
91
+ }
92
+ if (this.pending.startsWith('\u001bO')) {
93
+ if (this.pending.length >= 3) {
94
+ this.pending = this.pending.slice(3);
95
+ continue;
96
+ }
97
+ this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);
98
+ return;
99
+ }
100
+ if (this.pending.length < 2) {
101
+ this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);
102
+ return;
103
+ }
104
+ // An Escape combined with another byte is an unsupported key, not a standalone Escape.
105
+ this.pending = this.pending.slice(2);
106
+ continue;
107
+ }
108
+ }
109
+ flushIncompleteEscape() {
110
+ this.timer = undefined;
111
+ if (!this.pending.startsWith('\u001b'))
112
+ return;
113
+ const standalone = this.pending === '\u001b';
114
+ this.pending = '';
115
+ if (standalone)
116
+ this.emit('escape');
117
+ }
118
+ clearTimer() {
119
+ if (this.timer)
120
+ clearTimeout(this.timer);
121
+ this.timer = undefined;
122
+ }
123
+ close() {
124
+ this.clearTimer();
125
+ this.pending = '';
126
+ }
127
+ }
128
+ //# sourceMappingURL=dashboard-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-input.js","sourceRoot":"","sources":["../../src/cli/dashboard-input.ts"],"names":[],"mappings":"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","sourcesContent":["export type DashboardKey = 'up' | 'down' | 'page-up' | 'page-down' | 'home' | 'end'\n | 'enter' | 'escape' | 'interrupt';\n\nconst sequences = new Map<string, DashboardKey>([\n ['\\u001b[A', 'up'],\n ['\\u001b[B', 'down'],\n ['\\u001bOA', 'up'],\n ['\\u001bOB', 'down'],\n ['\\u001b[5~', 'page-up'],\n ['\\u001b[6~', 'page-down'],\n ['\\u001b[H', 'home'],\n ['\\u001b[F', 'end'],\n ['\\u001bOH', 'home'],\n ['\\u001bOF', 'end'],\n ['\\u001b[1~', 'home'],\n ['\\u001b[4~', 'end'],\n ['\\u001b[7~', 'home'],\n ['\\u001b[8~', 'end'],\n]);\n\n/** Decodes raw terminal keys while retaining incomplete escape sequences across chunks. */\nexport class DashboardKeyDecoder {\n private pending = '';\n private timer?: ReturnType<typeof setTimeout>;\n\n constructor(\n private readonly emit: (key: DashboardKey) => void,\n private readonly escapeDelayMs = 30,\n ) {}\n\n push(chunk: Uint8Array | string) {\n this.clearTimer();\n let input: string;\n if (typeof chunk === 'string') {\n input = chunk;\n } else {\n const characters = new Array<string>(chunk.length);\n for (let index = 0; index < chunk.length; index++) {\n characters[index] = String.fromCharCode(chunk[index]!);\n }\n input = characters.join('');\n }\n if (input.includes('\\u0003')) {\n this.pending = '';\n this.emit('interrupt');\n return;\n }\n this.pending += input;\n this.decode();\n }\n\n private decode() {\n while (this.pending) {\n const first = this.pending[0]!;\n if (first === '\\u0003') {\n this.pending = this.pending.slice(1);\n this.emit('interrupt');\n continue;\n }\n if (first === '\\r' || first === '\\n') {\n this.pending = this.pending.slice(1);\n this.emit('enter');\n continue;\n }\n if (first !== '\\u001b') {\n this.pending = this.pending.slice(1);\n continue;\n }\n if (this.pending.length >= 2) {\n const next = this.pending.charCodeAt(1);\n if (next === 0x1b || next < 0x20 || next === 0x7f) {\n this.pending = this.pending.slice(1);\n this.emit('escape');\n continue;\n }\n }\n const sequence = [...sequences.keys()].find(candidate => this.pending.startsWith(candidate));\n if (sequence) {\n this.pending = this.pending.slice(sequence.length);\n this.emit(sequences.get(sequence)!);\n continue;\n }\n if ([...sequences.keys()].some(candidate => candidate.startsWith(this.pending))) {\n this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);\n return;\n }\n if (this.pending.startsWith('\\u001b[')) {\n const complete = /^\\u001b\\[[0-?]*[ -/]*[@-~]/u.exec(this.pending);\n if (complete) {\n this.pending = this.pending.slice(complete[0].length);\n continue;\n }\n this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);\n return;\n }\n if (this.pending.startsWith('\\u001bO')) {\n if (this.pending.length >= 3) {\n this.pending = this.pending.slice(3);\n continue;\n }\n this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);\n return;\n }\n if (this.pending.length < 2) {\n this.timer = setTimeout(() => this.flushIncompleteEscape(), this.escapeDelayMs);\n return;\n }\n // An Escape combined with another byte is an unsupported key, not a standalone Escape.\n this.pending = this.pending.slice(2);\n continue;\n }\n }\n\n private flushIncompleteEscape() {\n this.timer = undefined;\n if (!this.pending.startsWith('\\u001b')) return;\n const standalone = this.pending === '\\u001b';\n this.pending = '';\n if (standalone) this.emit('escape');\n }\n\n private clearTimer() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n close() {\n this.clearTimer();\n this.pending = '';\n }\n}\n"]}
@@ -17,5 +17,16 @@ export declare function groupDashboardRows(rows: DashboardRow[], launchWorkspace
17
17
  export declare function labelDashboardGroups(groups: DashboardGroup[]): LabelledDashboardGroup[];
18
18
  export declare function clipDashboardText(text: string, columns: number): string;
19
19
  export declare function dashboardElapsed(row: DashboardRow, now: number): string;
20
- export declare function formatDashboardRows(rows: DashboardRow[], columns: number, now: number): string[];
20
+ export declare function formatDashboardRows(rows: DashboardRow[], columns: number, now: number, selectedTaskId?: string, color?: boolean): string[];
21
+ export type DashboardOverviewSlot = {
22
+ kind: 'blank';
23
+ } | {
24
+ kind: 'label';
25
+ text: string;
26
+ } | {
27
+ kind: 'row';
28
+ row: DashboardRow;
29
+ };
30
+ export declare function dashboardOverviewSlots(groups: LabelledDashboardGroup[]): DashboardOverviewSlot[];
31
+ export declare function layoutDashboardSlots(slots: DashboardOverviewSlot[], columns: number, now: number, selectedTaskId?: string, color?: boolean): string[];
21
32
  export declare function layoutDashboardGroups(groups: LabelledDashboardGroup[], columns: number, now: number, maxLines?: number): string[];
@@ -1,5 +1,6 @@
1
- import { stripVTControlCharacters } from 'node:util';
2
1
  import stringWidth from 'string-width';
2
+ import { styledLine, stateStyle, workspaceLine } from './dashboard-style.js';
3
+ import { sanitizeDashboardTerminalText } from '../runtime/dashboard-sanitize.js';
3
4
  import { dashboardTitle } from '../runtime/dashboard.js';
4
5
  /** Groups a snapshot without changing coordinator-owned row order. */
5
6
  export function groupDashboardRows(rows, launchWorkspace, previousFirstObserved = new Map()) {
@@ -31,8 +32,8 @@ export function groupDashboardRows(rows, launchWorkspace, previousFirstObserved
31
32
  return { groups, firstObserved };
32
33
  }
33
34
  function sanitize(text) {
34
- return stripVTControlCharacters(text)
35
- .replace(/[\u0000-\u001f\u007f-\u009f]/gu, '')
35
+ return sanitizeDashboardTerminalText(text)
36
+ .replace(/[\t\n\r]/gu, '')
36
37
  .replace(/\s+/gu, ' ')
37
38
  .trim();
38
39
  }
@@ -150,13 +151,13 @@ function completionAge(row, now) {
150
151
  function dashboardMarker(row) {
151
152
  if (row.state === 'running')
152
153
  return '●';
153
- if (row.state === 'queued' || row.state === 'waiting')
154
+ if (row.state === 'queued' || row.state === 'waiting' || row.state === 'awaiting_approval')
154
155
  return '◌';
155
156
  if (row.state === 'failed' || row.state === 'interrupted')
156
157
  return '!';
157
158
  return ' ';
158
159
  }
159
- export function formatDashboardRows(rows, columns, now) {
160
+ export function formatDashboardRows(rows, columns, now, selectedTaskId, color = false) {
160
161
  const harnesses = rows.map(row => sanitize(row.harness));
161
162
  const titles = rows.map(row => dashboardTitle(row.title));
162
163
  const states = rows.map(row => sanitize(row.state));
@@ -173,39 +174,40 @@ export function formatDashboardRows(rows, columns, now) {
173
174
  const titleWidth = Math.min(titleNaturalWidth, Math.max(0, columns - fixedWidth - ageSpace));
174
175
  return rows.map((row, index) => {
175
176
  const age = showAge && ages[index] ? ` ${ages[index]}` : '';
176
- const line = ` ${dashboardMarker(row)} ${pad(harnesses[index], harnessWidth)} `
177
- + `${pad(titles[index], titleWidth)} ${pad(states[index], stateWidth)} `
178
- + `${pad(elapsed[index], elapsedWidth)}${age}`;
179
- return clipDashboardText(line, columns);
177
+ return styledLine([
178
+ { text: `${row.taskId === selectedTaskId ? '› ' : ' '}${dashboardMarker(row)} `, style: stateStyle(row.state) },
179
+ { text: pad(titles[index], titleWidth), style: row.taskId === selectedTaskId ? '1' : row.state === 'completed' ? '2' : undefined },
180
+ { text: ` ${pad(harnesses[index], harnessWidth)} `, style: '2' },
181
+ { text: pad(states[index], stateWidth), style: stateStyle(row.state) },
182
+ { text: ` ${pad(elapsed[index], elapsedWidth)}${age}`, style: '2' },
183
+ ], columns, color, row.taskId === selectedTaskId);
180
184
  });
181
185
  }
182
- export function layoutDashboardGroups(groups, columns, now, maxLines = Number.POSITIVE_INFINITY) {
183
- if (maxLines <= 0)
184
- return [];
185
- const visible = [];
186
- groupLoop: for (const [groupIndex, group] of groups.entries()) {
187
- if (groupIndex > 0) {
188
- visible.push({ kind: 'blank' });
189
- if (visible.length >= maxLines)
190
- break;
191
- }
192
- visible.push({ kind: 'label', text: group.label });
193
- if (visible.length >= maxLines)
194
- break;
195
- for (const row of group.rows) {
196
- visible.push({ kind: 'row', row });
197
- if (visible.length >= maxLines)
198
- break groupLoop;
199
- }
186
+ export function dashboardOverviewSlots(groups) {
187
+ const slots = [];
188
+ for (const [groupIndex, group] of groups.entries()) {
189
+ if (groupIndex > 0)
190
+ slots.push({ kind: 'blank' });
191
+ slots.push({ kind: 'label', text: group.label }, { kind: 'blank' });
192
+ for (const row of group.rows)
193
+ slots.push({ kind: 'row', row });
200
194
  }
201
- const formattedRows = formatDashboardRows(visible.flatMap(slot => slot.kind === 'row' ? [slot.row] : []), columns, now);
195
+ return slots;
196
+ }
197
+ export function layoutDashboardSlots(slots, columns, now, selectedTaskId, color = false) {
198
+ const formattedRows = formatDashboardRows(slots.flatMap(slot => slot.kind === 'row' ? [slot.row] : []), columns, now, selectedTaskId, color);
202
199
  let rowIndex = 0;
203
- return visible.map(slot => {
200
+ return slots.map(slot => {
204
201
  if (slot.kind === 'blank')
205
202
  return '';
206
203
  if (slot.kind === 'label')
207
- return clipDashboardText(sanitize(slot.text), columns);
204
+ return workspaceLine(slot.text, columns, color);
208
205
  return formattedRows[rowIndex++];
209
206
  });
210
207
  }
208
+ export function layoutDashboardGroups(groups, columns, now, maxLines = Number.POSITIVE_INFINITY) {
209
+ if (maxLines <= 0)
210
+ return [];
211
+ return layoutDashboardSlots(dashboardOverviewSlots(groups).slice(0, maxLines), columns, now);
212
+ }
211
213
  //# sourceMappingURL=dashboard-layout.js.map