@blackbelt-technology/pi-agent-dashboard 0.2.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 (212) hide show
  1. package/AGENTS.md +342 -0
  2. package/README.md +619 -0
  3. package/docs/architecture.md +646 -0
  4. package/package.json +92 -0
  5. package/packages/extension/package.json +33 -0
  6. package/packages/extension/src/__tests__/ask-user-tool.test.ts +85 -0
  7. package/packages/extension/src/__tests__/command-handler.test.ts +712 -0
  8. package/packages/extension/src/__tests__/connection.test.ts +344 -0
  9. package/packages/extension/src/__tests__/credentials-updated.test.ts +26 -0
  10. package/packages/extension/src/__tests__/dev-build.test.ts +79 -0
  11. package/packages/extension/src/__tests__/event-forwarder.test.ts +89 -0
  12. package/packages/extension/src/__tests__/git-info.test.ts +112 -0
  13. package/packages/extension/src/__tests__/git-link-builder.test.ts +102 -0
  14. package/packages/extension/src/__tests__/openspec-activity-detector.test.ts +232 -0
  15. package/packages/extension/src/__tests__/openspec-poller.test.ts +119 -0
  16. package/packages/extension/src/__tests__/process-metrics.test.ts +47 -0
  17. package/packages/extension/src/__tests__/process-scanner.test.ts +202 -0
  18. package/packages/extension/src/__tests__/prompt-expander.test.ts +54 -0
  19. package/packages/extension/src/__tests__/server-auto-start.test.ts +167 -0
  20. package/packages/extension/src/__tests__/server-launcher.test.ts +44 -0
  21. package/packages/extension/src/__tests__/server-probe.test.ts +25 -0
  22. package/packages/extension/src/__tests__/session-switch.test.ts +139 -0
  23. package/packages/extension/src/__tests__/session-sync.test.ts +55 -0
  24. package/packages/extension/src/__tests__/source-detector.test.ts +73 -0
  25. package/packages/extension/src/__tests__/stats-extractor.test.ts +92 -0
  26. package/packages/extension/src/__tests__/ui-proxy.test.ts +583 -0
  27. package/packages/extension/src/__tests__/watchdog.test.ts +161 -0
  28. package/packages/extension/src/ask-user-tool.ts +63 -0
  29. package/packages/extension/src/bridge-context.ts +64 -0
  30. package/packages/extension/src/bridge.ts +926 -0
  31. package/packages/extension/src/command-handler.ts +538 -0
  32. package/packages/extension/src/connection.ts +204 -0
  33. package/packages/extension/src/dev-build.ts +39 -0
  34. package/packages/extension/src/event-forwarder.ts +40 -0
  35. package/packages/extension/src/flow-event-wiring.ts +102 -0
  36. package/packages/extension/src/git-info.ts +65 -0
  37. package/packages/extension/src/git-link-builder.ts +112 -0
  38. package/packages/extension/src/model-tracker.ts +56 -0
  39. package/packages/extension/src/pi-env.d.ts +23 -0
  40. package/packages/extension/src/process-metrics.ts +70 -0
  41. package/packages/extension/src/process-scanner.ts +396 -0
  42. package/packages/extension/src/prompt-expander.ts +87 -0
  43. package/packages/extension/src/provider-register.ts +276 -0
  44. package/packages/extension/src/server-auto-start.ts +87 -0
  45. package/packages/extension/src/server-launcher.ts +82 -0
  46. package/packages/extension/src/server-probe.ts +33 -0
  47. package/packages/extension/src/session-sync.ts +154 -0
  48. package/packages/extension/src/source-detector.ts +26 -0
  49. package/packages/extension/src/ui-proxy.ts +269 -0
  50. package/packages/extension/tsconfig.json +11 -0
  51. package/packages/server/package.json +37 -0
  52. package/packages/server/src/__tests__/auth-plugin.test.ts +117 -0
  53. package/packages/server/src/__tests__/auth.test.ts +224 -0
  54. package/packages/server/src/__tests__/auto-attach.test.ts +246 -0
  55. package/packages/server/src/__tests__/auto-resume.test.ts +135 -0
  56. package/packages/server/src/__tests__/auto-shutdown.test.ts +136 -0
  57. package/packages/server/src/__tests__/browse-endpoint.test.ts +104 -0
  58. package/packages/server/src/__tests__/bulk-archive-handler.test.ts +15 -0
  59. package/packages/server/src/__tests__/cli-parse.test.ts +73 -0
  60. package/packages/server/src/__tests__/client-discovery.test.ts +39 -0
  61. package/packages/server/src/__tests__/config-api.test.ts +104 -0
  62. package/packages/server/src/__tests__/cors.test.ts +48 -0
  63. package/packages/server/src/__tests__/directory-service.test.ts +240 -0
  64. package/packages/server/src/__tests__/editor-detection.test.ts +60 -0
  65. package/packages/server/src/__tests__/editor-endpoints.test.ts +26 -0
  66. package/packages/server/src/__tests__/editor-manager.test.ts +73 -0
  67. package/packages/server/src/__tests__/editor-registry.test.ts +151 -0
  68. package/packages/server/src/__tests__/event-status-extraction-flow.test.ts +55 -0
  69. package/packages/server/src/__tests__/event-status-extraction.test.ts +58 -0
  70. package/packages/server/src/__tests__/extension-register.test.ts +61 -0
  71. package/packages/server/src/__tests__/file-endpoint.test.ts +49 -0
  72. package/packages/server/src/__tests__/force-kill-handler.test.ts +109 -0
  73. package/packages/server/src/__tests__/git-operations.test.ts +251 -0
  74. package/packages/server/src/__tests__/headless-pid-registry.test.ts +233 -0
  75. package/packages/server/src/__tests__/headless-shutdown-fallback.test.ts +109 -0
  76. package/packages/server/src/__tests__/health-endpoint.test.ts +35 -0
  77. package/packages/server/src/__tests__/heartbeat-ack.test.ts +63 -0
  78. package/packages/server/src/__tests__/json-store.test.ts +70 -0
  79. package/packages/server/src/__tests__/localhost-guard.test.ts +149 -0
  80. package/packages/server/src/__tests__/memory-event-store.test.ts +260 -0
  81. package/packages/server/src/__tests__/memory-session-manager.test.ts +80 -0
  82. package/packages/server/src/__tests__/meta-persistence.test.ts +107 -0
  83. package/packages/server/src/__tests__/migrate-persistence.test.ts +180 -0
  84. package/packages/server/src/__tests__/npm-search-proxy.test.ts +153 -0
  85. package/packages/server/src/__tests__/oauth-callback-server.test.ts +165 -0
  86. package/packages/server/src/__tests__/openspec-archive.test.ts +87 -0
  87. package/packages/server/src/__tests__/package-manager-wrapper.test.ts +163 -0
  88. package/packages/server/src/__tests__/package-routes.test.ts +172 -0
  89. package/packages/server/src/__tests__/pending-fork-registry.test.ts +69 -0
  90. package/packages/server/src/__tests__/pending-load-manager.test.ts +144 -0
  91. package/packages/server/src/__tests__/pending-resume-registry.test.ts +130 -0
  92. package/packages/server/src/__tests__/pi-resource-scanner.test.ts +235 -0
  93. package/packages/server/src/__tests__/preferences-store.test.ts +108 -0
  94. package/packages/server/src/__tests__/process-manager.test.ts +184 -0
  95. package/packages/server/src/__tests__/provider-auth-handlers.test.ts +93 -0
  96. package/packages/server/src/__tests__/provider-auth-routes.test.ts +143 -0
  97. package/packages/server/src/__tests__/provider-auth-storage.test.ts +114 -0
  98. package/packages/server/src/__tests__/resolve-path.test.ts +38 -0
  99. package/packages/server/src/__tests__/ring-buffer.test.ts +45 -0
  100. package/packages/server/src/__tests__/server-pid.test.ts +89 -0
  101. package/packages/server/src/__tests__/session-api.test.ts +244 -0
  102. package/packages/server/src/__tests__/session-diff.test.ts +138 -0
  103. package/packages/server/src/__tests__/session-file-dedup.test.ts +102 -0
  104. package/packages/server/src/__tests__/session-file-reader.test.ts +85 -0
  105. package/packages/server/src/__tests__/session-lifecycle-logging.test.ts +138 -0
  106. package/packages/server/src/__tests__/session-order-manager.test.ts +135 -0
  107. package/packages/server/src/__tests__/session-ordering-integration.test.ts +102 -0
  108. package/packages/server/src/__tests__/session-scanner.test.ts +199 -0
  109. package/packages/server/src/__tests__/shutdown-endpoint.test.ts +42 -0
  110. package/packages/server/src/__tests__/skip-wipe.test.ts +123 -0
  111. package/packages/server/src/__tests__/sleep-aware-heartbeat.test.ts +126 -0
  112. package/packages/server/src/__tests__/smoke-integration.test.ts +175 -0
  113. package/packages/server/src/__tests__/spa-fallback.test.ts +68 -0
  114. package/packages/server/src/__tests__/subscription-handler.test.ts +155 -0
  115. package/packages/server/src/__tests__/terminal-gateway.test.ts +61 -0
  116. package/packages/server/src/__tests__/terminal-manager.test.ts +257 -0
  117. package/packages/server/src/__tests__/trusted-networks-config.test.ts +84 -0
  118. package/packages/server/src/__tests__/tunnel.test.ts +206 -0
  119. package/packages/server/src/__tests__/ws-ping-pong.test.ts +112 -0
  120. package/packages/server/src/auth-plugin.ts +302 -0
  121. package/packages/server/src/auth.ts +323 -0
  122. package/packages/server/src/browse.ts +55 -0
  123. package/packages/server/src/browser-gateway.ts +495 -0
  124. package/packages/server/src/browser-handlers/directory-handler.ts +137 -0
  125. package/packages/server/src/browser-handlers/handler-context.ts +45 -0
  126. package/packages/server/src/browser-handlers/session-action-handler.ts +271 -0
  127. package/packages/server/src/browser-handlers/session-meta-handler.ts +95 -0
  128. package/packages/server/src/browser-handlers/subscription-handler.ts +154 -0
  129. package/packages/server/src/browser-handlers/terminal-handler.ts +37 -0
  130. package/packages/server/src/cli.ts +347 -0
  131. package/packages/server/src/config-api.ts +130 -0
  132. package/packages/server/src/directory-service.ts +162 -0
  133. package/packages/server/src/editor-detection.ts +60 -0
  134. package/packages/server/src/editor-manager.ts +352 -0
  135. package/packages/server/src/editor-proxy.ts +134 -0
  136. package/packages/server/src/editor-registry.ts +108 -0
  137. package/packages/server/src/event-status-extraction.ts +131 -0
  138. package/packages/server/src/event-wiring.ts +589 -0
  139. package/packages/server/src/extension-register.ts +92 -0
  140. package/packages/server/src/git-operations.ts +200 -0
  141. package/packages/server/src/headless-pid-registry.ts +207 -0
  142. package/packages/server/src/idle-timer.ts +61 -0
  143. package/packages/server/src/json-store.ts +32 -0
  144. package/packages/server/src/localhost-guard.ts +117 -0
  145. package/packages/server/src/memory-event-store.ts +193 -0
  146. package/packages/server/src/memory-session-manager.ts +123 -0
  147. package/packages/server/src/meta-persistence.ts +64 -0
  148. package/packages/server/src/migrate-persistence.ts +195 -0
  149. package/packages/server/src/npm-search-proxy.ts +143 -0
  150. package/packages/server/src/oauth-callback-server.ts +177 -0
  151. package/packages/server/src/openspec-archive.ts +60 -0
  152. package/packages/server/src/package-manager-wrapper.ts +200 -0
  153. package/packages/server/src/pending-fork-registry.ts +53 -0
  154. package/packages/server/src/pending-load-manager.ts +110 -0
  155. package/packages/server/src/pending-resume-registry.ts +69 -0
  156. package/packages/server/src/pi-gateway.ts +419 -0
  157. package/packages/server/src/pi-resource-scanner.ts +369 -0
  158. package/packages/server/src/preferences-store.ts +116 -0
  159. package/packages/server/src/process-manager.ts +311 -0
  160. package/packages/server/src/provider-auth-handlers.ts +438 -0
  161. package/packages/server/src/provider-auth-storage.ts +200 -0
  162. package/packages/server/src/resolve-path.ts +12 -0
  163. package/packages/server/src/routes/editor-routes.ts +86 -0
  164. package/packages/server/src/routes/file-routes.ts +116 -0
  165. package/packages/server/src/routes/git-routes.ts +89 -0
  166. package/packages/server/src/routes/openspec-routes.ts +99 -0
  167. package/packages/server/src/routes/package-routes.ts +172 -0
  168. package/packages/server/src/routes/provider-auth-routes.ts +244 -0
  169. package/packages/server/src/routes/provider-routes.ts +101 -0
  170. package/packages/server/src/routes/route-deps.ts +23 -0
  171. package/packages/server/src/routes/session-routes.ts +91 -0
  172. package/packages/server/src/routes/system-routes.ts +271 -0
  173. package/packages/server/src/server-pid.ts +84 -0
  174. package/packages/server/src/server.ts +554 -0
  175. package/packages/server/src/session-api.ts +330 -0
  176. package/packages/server/src/session-bootstrap.ts +80 -0
  177. package/packages/server/src/session-diff.ts +178 -0
  178. package/packages/server/src/session-discovery.ts +134 -0
  179. package/packages/server/src/session-file-reader.ts +135 -0
  180. package/packages/server/src/session-order-manager.ts +73 -0
  181. package/packages/server/src/session-scanner.ts +233 -0
  182. package/packages/server/src/session-stats-reader.ts +99 -0
  183. package/packages/server/src/terminal-gateway.ts +51 -0
  184. package/packages/server/src/terminal-manager.ts +241 -0
  185. package/packages/server/src/tunnel.ts +329 -0
  186. package/packages/server/tsconfig.json +11 -0
  187. package/packages/shared/package.json +15 -0
  188. package/packages/shared/src/__tests__/config.test.ts +358 -0
  189. package/packages/shared/src/__tests__/deriveChangeState.test.ts +95 -0
  190. package/packages/shared/src/__tests__/mdns-discovery.test.ts +80 -0
  191. package/packages/shared/src/__tests__/protocol.test.ts +243 -0
  192. package/packages/shared/src/__tests__/resolve-jiti.test.ts +17 -0
  193. package/packages/shared/src/__tests__/server-identity.test.ts +73 -0
  194. package/packages/shared/src/__tests__/session-meta.test.ts +125 -0
  195. package/packages/shared/src/archive-types.ts +11 -0
  196. package/packages/shared/src/browser-protocol.ts +534 -0
  197. package/packages/shared/src/config.ts +245 -0
  198. package/packages/shared/src/diff-types.ts +41 -0
  199. package/packages/shared/src/editor-types.ts +18 -0
  200. package/packages/shared/src/mdns-discovery.ts +248 -0
  201. package/packages/shared/src/openspec-activity-detector.ts +109 -0
  202. package/packages/shared/src/openspec-poller.ts +96 -0
  203. package/packages/shared/src/protocol.ts +369 -0
  204. package/packages/shared/src/resolve-jiti.ts +43 -0
  205. package/packages/shared/src/rest-api.ts +255 -0
  206. package/packages/shared/src/server-identity.ts +51 -0
  207. package/packages/shared/src/session-meta.ts +86 -0
  208. package/packages/shared/src/state-replay.ts +174 -0
  209. package/packages/shared/src/stats-extractor.ts +54 -0
  210. package/packages/shared/src/terminal-types.ts +18 -0
  211. package/packages/shared/src/types.ts +351 -0
  212. package/packages/shared/tsconfig.json +8 -0
@@ -0,0 +1,161 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { ConnectionManager } from "../connection.js";
3
+
4
+ // Mock WebSocket (same pattern as connection.test.ts)
5
+ class MockWebSocket {
6
+ static instances: MockWebSocket[] = [];
7
+ readyState = 0;
8
+ onopen: (() => void) | null = null;
9
+ onclose: (() => void) | null = null;
10
+ onmessage: ((ev: { data: string }) => void) | null = null;
11
+ onerror: ((ev: unknown) => void) | null = null;
12
+ sentMessages: string[] = [];
13
+
14
+ constructor(public url: string) {
15
+ MockWebSocket.instances.push(this);
16
+ }
17
+
18
+ send(data: string) {
19
+ this.sentMessages.push(data);
20
+ }
21
+
22
+ close() {
23
+ this.readyState = 3;
24
+ this.onclose?.();
25
+ }
26
+
27
+ simulateOpen() {
28
+ this.readyState = 1;
29
+ this.onopen?.();
30
+ }
31
+
32
+ simulateClose() {
33
+ this.readyState = 3;
34
+ this.onclose?.();
35
+ }
36
+
37
+ simulateMessage(data: string) {
38
+ this.onmessage?.({ data });
39
+ }
40
+ }
41
+
42
+ describe("ConnectionManager watchdog", () => {
43
+ beforeEach(() => {
44
+ MockWebSocket.instances = [];
45
+ vi.useFakeTimers();
46
+ });
47
+
48
+ it("should force-close when no messages received for watchdogTimeout", () => {
49
+ const cm = new ConnectionManager({
50
+ url: "ws://localhost:9999",
51
+ WebSocketImpl: MockWebSocket as any,
52
+ watchdogTimeout: 60_000,
53
+ });
54
+ cm.connect();
55
+
56
+ const ws = MockWebSocket.instances[0];
57
+ ws.simulateOpen();
58
+
59
+ // Advance past watchdog timeout (checked every 15s)
60
+ vi.advanceTimersByTime(60_000);
61
+
62
+ // Watchdog should have triggered — ws should be closed and reconnect scheduled
63
+ expect(MockWebSocket.instances.length).toBeGreaterThanOrEqual(1);
64
+ // The connection should have been torn down
65
+ expect(cm.isConnected).toBe(false);
66
+
67
+ cm.disconnect();
68
+ });
69
+
70
+ it("should NOT force-close when messages are received regularly", () => {
71
+ const cm = new ConnectionManager({
72
+ url: "ws://localhost:9999",
73
+ WebSocketImpl: MockWebSocket as any,
74
+ watchdogTimeout: 60_000,
75
+ });
76
+ cm.connect();
77
+
78
+ const ws = MockWebSocket.instances[0];
79
+ ws.simulateOpen();
80
+
81
+ // Send messages every 20s to keep watchdog happy
82
+ for (let i = 0; i < 5; i++) {
83
+ vi.advanceTimersByTime(20_000);
84
+ ws.simulateMessage(JSON.stringify({ type: "heartbeat_ack" }));
85
+ }
86
+
87
+ // Should still be connected (100s elapsed, but messages kept coming)
88
+ expect(cm.isConnected).toBe(true);
89
+ expect(MockWebSocket.instances).toHaveLength(1);
90
+
91
+ cm.disconnect();
92
+ });
93
+
94
+ it("should stop watchdog on disconnect", () => {
95
+ const cm = new ConnectionManager({
96
+ url: "ws://localhost:9999",
97
+ WebSocketImpl: MockWebSocket as any,
98
+ watchdogTimeout: 60_000,
99
+ });
100
+ cm.connect();
101
+
102
+ const ws = MockWebSocket.instances[0];
103
+ ws.simulateOpen();
104
+
105
+ // Disconnect before watchdog fires
106
+ cm.disconnect();
107
+
108
+ // Advance past timeout — should not create new connections
109
+ vi.advanceTimersByTime(120_000);
110
+ expect(MockWebSocket.instances).toHaveLength(1);
111
+ });
112
+
113
+ it("should be disabled when watchdogTimeout is 0", () => {
114
+ const cm = new ConnectionManager({
115
+ url: "ws://localhost:9999",
116
+ WebSocketImpl: MockWebSocket as any,
117
+ watchdogTimeout: 0,
118
+ });
119
+ cm.connect();
120
+
121
+ const ws = MockWebSocket.instances[0];
122
+ ws.simulateOpen();
123
+
124
+ // Advance way past any timeout — should stay connected
125
+ vi.advanceTimersByTime(300_000);
126
+ expect(cm.isConnected).toBe(true);
127
+ expect(MockWebSocket.instances).toHaveLength(1);
128
+
129
+ cm.disconnect();
130
+ });
131
+
132
+ it("should reconnect after watchdog triggers", () => {
133
+ const onReconnect = vi.fn();
134
+ const cm = new ConnectionManager({
135
+ url: "ws://localhost:9999",
136
+ WebSocketImpl: MockWebSocket as any,
137
+ watchdogTimeout: 60_000,
138
+ onReconnect,
139
+ });
140
+ cm.connect();
141
+
142
+ const ws1 = MockWebSocket.instances[0];
143
+ ws1.simulateOpen();
144
+
145
+ // Let watchdog trigger
146
+ vi.advanceTimersByTime(60_000);
147
+ expect(cm.isConnected).toBe(false);
148
+
149
+ // Reconnect timer fires (1s backoff)
150
+ vi.advanceTimersByTime(1000);
151
+ expect(MockWebSocket.instances.length).toBeGreaterThanOrEqual(2);
152
+
153
+ // Simulate successful reconnect
154
+ const ws2 = MockWebSocket.instances[MockWebSocket.instances.length - 1];
155
+ ws2.simulateOpen();
156
+ expect(cm.isConnected).toBe(true);
157
+ expect(onReconnect).toHaveBeenCalled();
158
+
159
+ cm.disconnect();
160
+ });
161
+ });
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Registers the ask_user tool in the bridge extension.
3
+ *
4
+ * Called at runtime (session_start) rather than extension load time to avoid
5
+ * static tool-name conflicts with other extensions (e.g. pi-flows) that also
6
+ * register ask_user. Runtime registration bypasses detectExtensionConflicts.
7
+ */
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
+ import { Type } from "@sinclair/typebox";
10
+ import { StringEnum } from "@mariozechner/pi-ai";
11
+
12
+ export function registerAskUserTool(pi: ExtensionAPI): void {
13
+ pi.registerTool({
14
+ name: "ask_user",
15
+ label: "Ask User",
16
+ description:
17
+ "Ask the user a question interactively. Use this when you need clarification, confirmation, or a choice from the user before proceeding.",
18
+ promptSnippet:
19
+ "Ask the user interactive questions (confirm, select, multiselect, or free text input)",
20
+ promptGuidelines: [
21
+ "When you need to ask the user a question, ALWAYS use the ask_user tool instead of writing the question as plain text.",
22
+ "Use method 'confirm' for yes/no questions, 'select' when offering specific choices, 'multiselect' when the user should pick multiple items from a list, and 'input' for open-ended questions.",
23
+ "This applies to all workflows including OpenSpec, planning, and any situation where you need user input before proceeding.",
24
+ ],
25
+ parameters: Type.Object({
26
+ method: StringEnum(["confirm", "select", "multiselect", "input"] as const, {
27
+ description:
28
+ "Type of question: confirm (yes/no), select (pick from options), multiselect (pick multiple), input (free text)",
29
+ }),
30
+ title: Type.String({ description: "The question to ask" }),
31
+ message: Type.Optional(Type.String({ description: "Additional context or detailed question body (all methods)" })),
32
+ options: Type.Optional(
33
+ Type.Array(Type.String(), { description: "Options to choose from (for select)" }),
34
+ ),
35
+ placeholder: Type.Optional(Type.String({ description: "Placeholder text (for input)" })),
36
+ }),
37
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
38
+ let result: unknown;
39
+
40
+ const msgOpts = params.message ? { message: params.message } : undefined;
41
+
42
+ switch (params.method) {
43
+ case "confirm":
44
+ result = await ctx.ui.confirm(params.title, params.message ?? "");
45
+ break;
46
+ case "select":
47
+ result = await ctx.ui.select(params.title, params.options ?? [], msgOpts);
48
+ break;
49
+ case "multiselect":
50
+ result = await (ctx.ui as any).multiselect(params.title, params.options ?? [], msgOpts);
51
+ break;
52
+ case "input":
53
+ result = await ctx.ui.input(params.title, params.placeholder, msgOpts);
54
+ break;
55
+ }
56
+
57
+ return {
58
+ content: [{ type: "text", text: `User responded: ${JSON.stringify(result)}` }],
59
+ details: { method: params.method, result },
60
+ };
61
+ },
62
+ });
63
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Shared mutable state for bridge modules.
3
+ * Avoids passing 14+ closure variables to every extracted function.
4
+ */
5
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
+ import type { ConnectionManager } from "./connection.js";
7
+
8
+ export interface BridgeContext {
9
+ pi: ExtensionAPI;
10
+ connection: ConnectionManager;
11
+ /** Current session ID (mutated on session_switch/fork) */
12
+ sessionId: string;
13
+ cachedCtx: any;
14
+ cachedModelRegistry: any;
15
+ cachedHasUI: boolean | undefined;
16
+ lastModel: string | undefined;
17
+ lastThinkingLevel: string | undefined;
18
+ lastSessionFile: string | undefined;
19
+ lastSessionDir: string | undefined;
20
+ lastFirstMessage: string | undefined;
21
+ lastGitBranch: string | undefined;
22
+ lastGitPrNumber: number | undefined;
23
+ lastSessionName: string | undefined;
24
+ }
25
+
26
+ // Commands that the dashboard handles natively with superior UX.
27
+ // These are filtered from the command list sent to dashboard clients.
28
+ const DASHBOARD_NATIVE_COMMANDS = new Set(["roles"]);
29
+
30
+ /** Filter out hidden commands (names starting with __) and dashboard-native commands from commands list */
31
+ export function filterHiddenCommands(commands: any[]): any[] {
32
+ return commands.filter((cmd) =>
33
+ !cmd.name.startsWith("__") &&
34
+ !DASHBOARD_NATIVE_COMMANDS.has(cmd.name)
35
+ );
36
+ }
37
+
38
+ /** Extract first user message text from session entries */
39
+ export function extractFirstMessage(ctx: any): string | undefined {
40
+ try {
41
+ const entries = ctx?.sessionManager?.getEntries?.();
42
+ if (!entries || !Array.isArray(entries)) return undefined;
43
+ for (const entry of entries) {
44
+ if (entry.role === "user" && typeof entry.content === "string") {
45
+ return entry.content.slice(0, 200);
46
+ }
47
+ if (entry.role === "user" && Array.isArray(entry.content)) {
48
+ for (const part of entry.content) {
49
+ if (part.type === "text" && typeof part.text === "string") {
50
+ return part.text.slice(0, 200);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ } catch { /* ignore */ }
56
+ return undefined;
57
+ }
58
+
59
+ /** Get current model string (provider/id) from cached context */
60
+ export function getCurrentModelString(bc: BridgeContext): string | undefined {
61
+ const model = bc.cachedCtx?.model;
62
+ if (!model) return undefined;
63
+ return `${model.provider}/${model.id}`;
64
+ }