@opengsd/gsd-pi 1.0.2-dev.50223bc → 1.0.2-dev.5961fbf

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 (251) hide show
  1. package/dist/resource-loader.d.ts +5 -0
  2. package/dist/resource-loader.js +24 -8
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/gsd/auto/loop.js +19 -0
  5. package/dist/resources/extensions/gsd/auto/phases.js +1 -1
  6. package/dist/resources/extensions/gsd/auto-worktree.js +2 -54
  7. package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
  8. package/dist/web/standalone/.next/BUILD_ID +1 -1
  9. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  10. package/dist/web/standalone/.next/build-manifest.json +2 -2
  11. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  12. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  29. package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
  30. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  31. package/dist/web/standalone/.next/server/app/index.html +1 -1
  32. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  39. package/dist/web/standalone/.next/server/chunks/1834.js +1 -1
  40. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  42. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  43. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  44. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  45. package/dist/web/standalone/package.json +0 -1
  46. package/dist/worktree-cli.d.ts +0 -2
  47. package/dist/worktree-cli.js +21 -9
  48. package/package.json +9 -4
  49. package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
  50. package/packages/cloud-mcp-gateway/package.json +5 -4
  51. package/packages/contracts/package.json +2 -2
  52. package/packages/daemon/bin/gsd-daemon.js +14 -0
  53. package/packages/daemon/bin/gsd-mcp-runtime.js +14 -0
  54. package/packages/daemon/bin/gsd-mcp.js +14 -0
  55. package/packages/daemon/dist/channel-manager.d.ts +53 -0
  56. package/packages/daemon/dist/channel-manager.d.ts.map +1 -0
  57. package/packages/daemon/dist/channel-manager.js +167 -0
  58. package/packages/daemon/dist/channel-manager.js.map +1 -0
  59. package/packages/daemon/dist/cli.d.ts +3 -0
  60. package/packages/daemon/dist/cli.d.ts.map +1 -0
  61. package/packages/daemon/dist/cli.js +94 -0
  62. package/packages/daemon/dist/cli.js.map +1 -0
  63. package/packages/daemon/dist/cloud-cli.d.ts +7 -0
  64. package/packages/daemon/dist/cloud-cli.d.ts.map +1 -0
  65. package/packages/daemon/dist/cloud-cli.js +96 -0
  66. package/packages/daemon/dist/cloud-cli.js.map +1 -0
  67. package/packages/daemon/dist/cloud-config.d.ts +18 -0
  68. package/packages/daemon/dist/cloud-config.d.ts.map +1 -0
  69. package/packages/daemon/dist/cloud-config.js +209 -0
  70. package/packages/daemon/dist/cloud-config.js.map +1 -0
  71. package/packages/daemon/dist/cloud-config.test.d.ts +2 -0
  72. package/packages/daemon/dist/cloud-config.test.d.ts.map +1 -0
  73. package/packages/daemon/dist/cloud-config.test.js +132 -0
  74. package/packages/daemon/dist/cloud-config.test.js.map +1 -0
  75. package/packages/daemon/dist/cloud-runtime.d.ts +26 -0
  76. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -0
  77. package/packages/daemon/dist/cloud-runtime.js +180 -0
  78. package/packages/daemon/dist/cloud-runtime.js.map +1 -0
  79. package/packages/daemon/dist/cloud-runtime.test.d.ts +2 -0
  80. package/packages/daemon/dist/cloud-runtime.test.d.ts.map +1 -0
  81. package/packages/daemon/dist/cloud-runtime.test.js +28 -0
  82. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -0
  83. package/packages/daemon/dist/cloud-token.d.ts +3 -0
  84. package/packages/daemon/dist/cloud-token.d.ts.map +1 -0
  85. package/packages/daemon/dist/cloud-token.js +37 -0
  86. package/packages/daemon/dist/cloud-token.js.map +1 -0
  87. package/packages/daemon/dist/commands.d.ts +25 -0
  88. package/packages/daemon/dist/commands.d.ts.map +1 -0
  89. package/packages/daemon/dist/commands.js +81 -0
  90. package/packages/daemon/dist/commands.js.map +1 -0
  91. package/packages/daemon/dist/config.d.ts +17 -0
  92. package/packages/daemon/dist/config.d.ts.map +1 -0
  93. package/packages/daemon/dist/config.js +146 -0
  94. package/packages/daemon/dist/config.js.map +1 -0
  95. package/packages/daemon/dist/daemon.d.ts +38 -0
  96. package/packages/daemon/dist/daemon.d.ts.map +1 -0
  97. package/packages/daemon/dist/daemon.js +194 -0
  98. package/packages/daemon/dist/daemon.js.map +1 -0
  99. package/packages/daemon/dist/daemon.test.d.ts +2 -0
  100. package/packages/daemon/dist/daemon.test.d.ts.map +1 -0
  101. package/packages/daemon/dist/daemon.test.js +692 -0
  102. package/packages/daemon/dist/daemon.test.js.map +1 -0
  103. package/packages/daemon/dist/discord-bot.d.ts +70 -0
  104. package/packages/daemon/dist/discord-bot.d.ts.map +1 -0
  105. package/packages/daemon/dist/discord-bot.js +433 -0
  106. package/packages/daemon/dist/discord-bot.js.map +1 -0
  107. package/packages/daemon/dist/discord-bot.test.d.ts +2 -0
  108. package/packages/daemon/dist/discord-bot.test.d.ts.map +1 -0
  109. package/packages/daemon/dist/discord-bot.test.js +667 -0
  110. package/packages/daemon/dist/discord-bot.test.js.map +1 -0
  111. package/packages/daemon/dist/event-bridge.d.ts +72 -0
  112. package/packages/daemon/dist/event-bridge.d.ts.map +1 -0
  113. package/packages/daemon/dist/event-bridge.js +366 -0
  114. package/packages/daemon/dist/event-bridge.js.map +1 -0
  115. package/packages/daemon/dist/event-bridge.test.d.ts +9 -0
  116. package/packages/daemon/dist/event-bridge.test.d.ts.map +1 -0
  117. package/packages/daemon/dist/event-bridge.test.js +528 -0
  118. package/packages/daemon/dist/event-bridge.test.js.map +1 -0
  119. package/packages/daemon/dist/event-formatter.d.ts +34 -0
  120. package/packages/daemon/dist/event-formatter.d.ts.map +1 -0
  121. package/packages/daemon/dist/event-formatter.js +355 -0
  122. package/packages/daemon/dist/event-formatter.js.map +1 -0
  123. package/packages/daemon/dist/event-formatter.test.d.ts +2 -0
  124. package/packages/daemon/dist/event-formatter.test.d.ts.map +1 -0
  125. package/packages/daemon/dist/event-formatter.test.js +333 -0
  126. package/packages/daemon/dist/event-formatter.test.js.map +1 -0
  127. package/packages/daemon/dist/index.d.ts +25 -0
  128. package/packages/daemon/dist/index.d.ts.map +1 -0
  129. package/packages/daemon/dist/index.js +17 -0
  130. package/packages/daemon/dist/index.js.map +1 -0
  131. package/packages/daemon/dist/launchd.d.ts +49 -0
  132. package/packages/daemon/dist/launchd.d.ts.map +1 -0
  133. package/packages/daemon/dist/launchd.js +188 -0
  134. package/packages/daemon/dist/launchd.js.map +1 -0
  135. package/packages/daemon/dist/launchd.test.d.ts +2 -0
  136. package/packages/daemon/dist/launchd.test.d.ts.map +1 -0
  137. package/packages/daemon/dist/launchd.test.js +296 -0
  138. package/packages/daemon/dist/launchd.test.js.map +1 -0
  139. package/packages/daemon/dist/local-tool-executor.d.ts +22 -0
  140. package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -0
  141. package/packages/daemon/dist/local-tool-executor.js +307 -0
  142. package/packages/daemon/dist/local-tool-executor.js.map +1 -0
  143. package/packages/daemon/dist/local-tool-executor.test.d.ts +2 -0
  144. package/packages/daemon/dist/local-tool-executor.test.d.ts.map +1 -0
  145. package/packages/daemon/dist/local-tool-executor.test.js +111 -0
  146. package/packages/daemon/dist/local-tool-executor.test.js.map +1 -0
  147. package/packages/daemon/dist/logger.d.ts +25 -0
  148. package/packages/daemon/dist/logger.d.ts.map +1 -0
  149. package/packages/daemon/dist/logger.js +72 -0
  150. package/packages/daemon/dist/logger.js.map +1 -0
  151. package/packages/daemon/dist/mcp-cli.d.ts +3 -0
  152. package/packages/daemon/dist/mcp-cli.d.ts.map +1 -0
  153. package/packages/daemon/dist/mcp-cli.js +8 -0
  154. package/packages/daemon/dist/mcp-cli.js.map +1 -0
  155. package/packages/daemon/dist/mcp-cli.test.d.ts +2 -0
  156. package/packages/daemon/dist/mcp-cli.test.d.ts.map +1 -0
  157. package/packages/daemon/dist/mcp-cli.test.js +13 -0
  158. package/packages/daemon/dist/mcp-cli.test.js.map +1 -0
  159. package/packages/daemon/dist/mcp-runtime-cli.d.ts +3 -0
  160. package/packages/daemon/dist/mcp-runtime-cli.d.ts.map +1 -0
  161. package/packages/daemon/dist/mcp-runtime-cli.js +8 -0
  162. package/packages/daemon/dist/mcp-runtime-cli.js.map +1 -0
  163. package/packages/daemon/dist/message-batcher.d.ts +78 -0
  164. package/packages/daemon/dist/message-batcher.d.ts.map +1 -0
  165. package/packages/daemon/dist/message-batcher.js +173 -0
  166. package/packages/daemon/dist/message-batcher.js.map +1 -0
  167. package/packages/daemon/dist/message-batcher.test.d.ts +2 -0
  168. package/packages/daemon/dist/message-batcher.test.d.ts.map +1 -0
  169. package/packages/daemon/dist/message-batcher.test.js +242 -0
  170. package/packages/daemon/dist/message-batcher.test.js.map +1 -0
  171. package/packages/daemon/dist/orchestrator.d.ts +98 -0
  172. package/packages/daemon/dist/orchestrator.d.ts.map +1 -0
  173. package/packages/daemon/dist/orchestrator.js +359 -0
  174. package/packages/daemon/dist/orchestrator.js.map +1 -0
  175. package/packages/daemon/dist/orchestrator.test.d.ts +8 -0
  176. package/packages/daemon/dist/orchestrator.test.d.ts.map +1 -0
  177. package/packages/daemon/dist/orchestrator.test.js +425 -0
  178. package/packages/daemon/dist/orchestrator.test.js.map +1 -0
  179. package/packages/daemon/dist/project-scanner.d.ts +18 -0
  180. package/packages/daemon/dist/project-scanner.d.ts.map +1 -0
  181. package/packages/daemon/dist/project-scanner.js +90 -0
  182. package/packages/daemon/dist/project-scanner.js.map +1 -0
  183. package/packages/daemon/dist/project-scanner.test.d.ts +5 -0
  184. package/packages/daemon/dist/project-scanner.test.d.ts.map +1 -0
  185. package/packages/daemon/dist/project-scanner.test.js +183 -0
  186. package/packages/daemon/dist/project-scanner.test.js.map +1 -0
  187. package/packages/daemon/dist/session-manager.d.ts +70 -0
  188. package/packages/daemon/dist/session-manager.d.ts.map +1 -0
  189. package/packages/daemon/dist/session-manager.js +358 -0
  190. package/packages/daemon/dist/session-manager.js.map +1 -0
  191. package/packages/daemon/dist/session-manager.test.d.ts +9 -0
  192. package/packages/daemon/dist/session-manager.test.d.ts.map +1 -0
  193. package/packages/daemon/dist/session-manager.test.js +616 -0
  194. package/packages/daemon/dist/session-manager.test.js.map +1 -0
  195. package/packages/daemon/dist/types.d.ts +133 -0
  196. package/packages/daemon/dist/types.d.ts.map +1 -0
  197. package/packages/daemon/dist/types.js +8 -0
  198. package/packages/daemon/dist/types.js.map +1 -0
  199. package/packages/daemon/dist/verbosity.d.ts +27 -0
  200. package/packages/daemon/dist/verbosity.d.ts.map +1 -0
  201. package/packages/daemon/dist/verbosity.js +86 -0
  202. package/packages/daemon/dist/verbosity.js.map +1 -0
  203. package/packages/daemon/dist/verbosity.test.d.ts +2 -0
  204. package/packages/daemon/dist/verbosity.test.d.ts.map +1 -0
  205. package/packages/daemon/dist/verbosity.test.js +136 -0
  206. package/packages/daemon/dist/verbosity.test.js.map +1 -0
  207. package/packages/daemon/package.json +9 -8
  208. package/packages/gsd-agent-core/package.json +6 -6
  209. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  210. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
  211. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  212. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  213. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  214. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  215. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  222. package/packages/gsd-agent-modes/package.json +8 -8
  223. package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
  224. package/packages/mcp-server/package.json +6 -5
  225. package/packages/native/package.json +3 -3
  226. package/packages/pi-agent-core/package.json +4 -4
  227. package/packages/pi-ai/bin/pi-ai.js +14 -0
  228. package/packages/pi-ai/dist/models.generated.d.ts +0 -17
  229. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  230. package/packages/pi-ai/dist/models.generated.js +18 -35
  231. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  232. package/packages/pi-ai/package.json +5 -4
  233. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
  234. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/core/tools/read.js +5 -3
  236. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  237. package/packages/pi-coding-agent/package.json +9 -9
  238. package/packages/pi-tui/package.json +2 -2
  239. package/packages/rpc-client/package.json +3 -3
  240. package/pkg/package.json +1 -1
  241. package/scripts/ensure-workspace-builds.cjs +4 -4
  242. package/scripts/install/deps.js +10 -0
  243. package/src/resources/extensions/gsd/auto/loop.ts +22 -0
  244. package/src/resources/extensions/gsd/auto/phases.ts +1 -1
  245. package/src/resources/extensions/gsd/auto-worktree.ts +2 -56
  246. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
  247. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
  248. package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
  249. package/dist/tsconfig.extensions.tsbuildinfo +0 -1
  250. /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_buildManifest.js +0 -0
  251. /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_ssgManifest.js +0 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * event-bridge.test.ts — Tests for EventBridge orchestrator.
3
+ *
4
+ * Uses mock SessionManager (EventEmitter), mock ChannelManager,
5
+ * mock Discord Client, and mock Logger to test event wiring,
6
+ * blocker handling, conversation relay, and cleanup.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=event-bridge.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bridge.test.d.ts","sourceRoot":"","sources":["../src/event-bridge.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,528 @@
1
+ /**
2
+ * event-bridge.test.ts — Tests for EventBridge orchestrator.
3
+ *
4
+ * Uses mock SessionManager (EventEmitter), mock ChannelManager,
5
+ * mock Discord Client, and mock Logger to test event wiring,
6
+ * blocker handling, conversation relay, and cleanup.
7
+ */
8
+ import { describe, it, mock } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { EventEmitter } from 'node:events';
11
+ import { EventBridge } from './event-bridge.js';
12
+ // ---------------------------------------------------------------------------
13
+ // Mock factories
14
+ // ---------------------------------------------------------------------------
15
+ function createMockLogger() {
16
+ return {
17
+ debug: mock.fn(() => { }),
18
+ info: mock.fn(() => { }),
19
+ warn: mock.fn(() => { }),
20
+ error: mock.fn(() => { }),
21
+ };
22
+ }
23
+ function createMockChannelManager() {
24
+ const sentMessages = [];
25
+ const mockChannel = {
26
+ id: 'ch-123',
27
+ send: mock.fn(async (_payload) => {
28
+ sentMessages.push(_payload);
29
+ return { id: 'msg-1' };
30
+ }),
31
+ createMessageComponentCollector: mock.fn((_opts) => {
32
+ const collector = new EventEmitter();
33
+ collector.stop = (reason) => collector.emit('end', [], reason ?? 'manual');
34
+ return collector;
35
+ }),
36
+ };
37
+ return {
38
+ createProjectChannel: mock.fn(async (_dir) => mockChannel),
39
+ _channel: mockChannel,
40
+ _sentMessages: sentMessages,
41
+ };
42
+ }
43
+ function createMockClient() {
44
+ const emitter = new EventEmitter();
45
+ const dmSendFn = mock.fn(async () => ({}));
46
+ const fetchFn = mock.fn(async (_id) => ({ send: dmSendFn }));
47
+ emitter.users = { fetch: fetchFn };
48
+ return Object.assign(emitter, {
49
+ users: { fetch: fetchFn },
50
+ _dmSend: dmSendFn,
51
+ });
52
+ }
53
+ function createMockSessionManager() {
54
+ const sm = new EventEmitter();
55
+ sm.getSession = mock.fn((_id) => undefined);
56
+ sm.resolveBlocker = mock.fn(async (_sid, _resp) => { });
57
+ return sm;
58
+ }
59
+ function createMockSession(overrides) {
60
+ return {
61
+ sessionId: 'sess-1',
62
+ projectDir: '/test/project',
63
+ projectName: 'project',
64
+ status: 'running',
65
+ client: {
66
+ steer: mock.fn(async (_msg) => { }),
67
+ prompt: mock.fn(async () => ({})),
68
+ },
69
+ events: [],
70
+ pendingBlocker: null,
71
+ cost: { totalCost: 0, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } },
72
+ startTime: Date.now(),
73
+ ...overrides,
74
+ };
75
+ }
76
+ const DEFAULT_CONFIG = {
77
+ discord: {
78
+ token: 'dummy',
79
+ guild_id: 'guild-1',
80
+ owner_id: 'owner-1',
81
+ dm_on_blocker: false,
82
+ },
83
+ projects: { scan_roots: [] },
84
+ log: { file: '/tmp/test.log', level: 'debug', max_size_mb: 10 },
85
+ };
86
+ function buildBridge(overrides) {
87
+ const sessionManager = createMockSessionManager();
88
+ const channelManager = createMockChannelManager();
89
+ const client = createMockClient();
90
+ const logger = createMockLogger();
91
+ const opts = {
92
+ sessionManager: sessionManager,
93
+ channelManager: channelManager,
94
+ client,
95
+ config: DEFAULT_CONFIG,
96
+ logger: logger,
97
+ ownerId: 'owner-1',
98
+ ...overrides,
99
+ };
100
+ const bridge = new EventBridge(opts);
101
+ return { bridge, sessionManager, channelManager, client, logger };
102
+ }
103
+ // ---------------------------------------------------------------------------
104
+ // Helpers
105
+ // ---------------------------------------------------------------------------
106
+ const tick = () => new Promise((r) => setTimeout(r, 30));
107
+ function mockFn(obj) {
108
+ return obj;
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Tests
112
+ // ---------------------------------------------------------------------------
113
+ describe('EventBridge', () => {
114
+ describe('lifecycle', () => {
115
+ it('start() subscribes to session manager events and messageCreate', () => {
116
+ const { bridge, sessionManager, client } = buildBridge();
117
+ bridge.start();
118
+ assert.ok(sessionManager.listenerCount('session:started') > 0);
119
+ assert.ok(sessionManager.listenerCount('session:event') > 0);
120
+ assert.ok(sessionManager.listenerCount('session:blocked') > 0);
121
+ assert.ok(sessionManager.listenerCount('session:completed') > 0);
122
+ assert.ok(sessionManager.listenerCount('session:error') > 0);
123
+ assert.ok(client.listenerCount('messageCreate') > 0);
124
+ });
125
+ it('stop() unsubscribes from all events and clears mappings', async () => {
126
+ const { bridge, sessionManager, client } = buildBridge();
127
+ bridge.start();
128
+ await bridge.stop();
129
+ assert.equal(sessionManager.listenerCount('session:started'), 0);
130
+ assert.equal(sessionManager.listenerCount('session:event'), 0);
131
+ assert.equal(sessionManager.listenerCount('session:blocked'), 0);
132
+ assert.equal(sessionManager.listenerCount('session:completed'), 0);
133
+ assert.equal(sessionManager.listenerCount('session:error'), 0);
134
+ assert.equal(client.listenerCount('messageCreate'), 0);
135
+ });
136
+ it('start() is idempotent', () => {
137
+ const { bridge, sessionManager } = buildBridge();
138
+ bridge.start();
139
+ bridge.start();
140
+ assert.equal(sessionManager.listenerCount('session:started'), 1);
141
+ });
142
+ it('getVerbosityManager() returns a VerbosityManager', () => {
143
+ const { bridge } = buildBridge();
144
+ const vm = bridge.getVerbosityManager();
145
+ assert.ok(vm);
146
+ assert.equal(typeof vm.shouldShow, 'function');
147
+ });
148
+ });
149
+ describe('session:started → channel creation + welcome embed', () => {
150
+ it('creates channel and batcher', async () => {
151
+ const { bridge, sessionManager, channelManager } = buildBridge();
152
+ bridge.start();
153
+ sessionManager.emit('session:started', {
154
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
155
+ });
156
+ await tick();
157
+ assert.equal(mockFn(channelManager.createProjectChannel).mock.callCount(), 1);
158
+ });
159
+ it('logs error and skips when channel creation fails', async () => {
160
+ const failingCm = {
161
+ createProjectChannel: mock.fn(async () => { throw new Error('API error'); }),
162
+ };
163
+ const { bridge, sessionManager, logger } = buildBridge({
164
+ channelManager: failingCm,
165
+ });
166
+ bridge.start();
167
+ sessionManager.emit('session:started', {
168
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
169
+ });
170
+ await tick();
171
+ assert.ok(mockFn(logger.error).mock.callCount() > 0);
172
+ });
173
+ });
174
+ describe('session:event → format + verbosity filter + enqueue', () => {
175
+ it('formats event and enqueues to batcher (no errors)', async () => {
176
+ const { bridge, sessionManager, logger } = buildBridge();
177
+ bridge.start();
178
+ sessionManager.emit('session:started', {
179
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
180
+ });
181
+ await tick();
182
+ sessionManager.emit('session:event', {
183
+ sessionId: 'sess-1', projectDir: '/test/project',
184
+ event: { type: 'tool_execution_start', name: 'read' },
185
+ });
186
+ await tick();
187
+ // No errors
188
+ assert.equal(mockFn(logger.error).mock.callCount(), 0);
189
+ });
190
+ it('filters events based on verbosity', async () => {
191
+ const { bridge, sessionManager, channelManager, logger } = buildBridge();
192
+ bridge.start();
193
+ sessionManager.emit('session:started', {
194
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
195
+ });
196
+ await tick();
197
+ // Set quiet mode
198
+ bridge.getVerbosityManager().setLevel('ch-123', 'quiet');
199
+ // cost_update filtered in quiet
200
+ sessionManager.emit('session:event', {
201
+ sessionId: 'sess-1', projectDir: '/test/project',
202
+ event: { type: 'cost_update', cumulativeCost: 1.5 },
203
+ });
204
+ await tick();
205
+ // tool_execution_start filtered in quiet
206
+ sessionManager.emit('session:event', {
207
+ sessionId: 'sess-1', projectDir: '/test/project',
208
+ event: { type: 'tool_execution_start', name: 'read' },
209
+ });
210
+ await tick();
211
+ assert.equal(mockFn(logger.error).mock.callCount(), 0);
212
+ });
213
+ });
214
+ describe('session:blocked → blocker embed + buttons + optional DM', () => {
215
+ it('sends blocker embed and creates collector for confirm', async () => {
216
+ const { bridge, sessionManager, channelManager } = buildBridge();
217
+ bridge.start();
218
+ sessionManager.emit('session:started', {
219
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
220
+ });
221
+ await tick();
222
+ const blocker = {
223
+ id: 'blocker-1', method: 'confirm', message: 'Continue?',
224
+ event: { id: 'blocker-1', method: 'confirm', message: 'Continue?' },
225
+ };
226
+ sessionManager.emit('session:blocked', {
227
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
228
+ });
229
+ await tick();
230
+ assert.ok(mockFn(channelManager._channel.createMessageComponentCollector).mock.callCount() > 0);
231
+ });
232
+ it('sends DM when dm_on_blocker is configured', async () => {
233
+ const config = {
234
+ ...DEFAULT_CONFIG,
235
+ discord: { ...DEFAULT_CONFIG.discord, dm_on_blocker: true },
236
+ };
237
+ const client = createMockClient();
238
+ const { bridge, sessionManager } = buildBridge({ config, client });
239
+ bridge.start();
240
+ sessionManager.emit('session:started', {
241
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
242
+ });
243
+ await tick();
244
+ const blocker = {
245
+ id: 'blocker-1', method: 'input', message: 'Enter API key',
246
+ event: { id: 'blocker-1', method: 'input' },
247
+ };
248
+ sessionManager.emit('session:blocked', {
249
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
250
+ });
251
+ await tick();
252
+ const usersFetch = client.users.fetch;
253
+ assert.equal(mockFn(usersFetch).mock.callCount(), 1);
254
+ });
255
+ it('does not send DM when dm_on_blocker is false', async () => {
256
+ const client = createMockClient();
257
+ const { bridge, sessionManager } = buildBridge({ client });
258
+ bridge.start();
259
+ sessionManager.emit('session:started', {
260
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
261
+ });
262
+ await tick();
263
+ const blocker = {
264
+ id: 'blocker-1', method: 'input', message: 'Enter value',
265
+ event: { id: 'blocker-1', method: 'input' },
266
+ };
267
+ sessionManager.emit('session:blocked', {
268
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
269
+ });
270
+ await tick();
271
+ const usersFetch = client.users.fetch;
272
+ assert.equal(mockFn(usersFetch).mock.callCount(), 0);
273
+ });
274
+ });
275
+ describe('button collector → resolveBlocker', () => {
276
+ it('resolves blocker on button click from authorized user', async () => {
277
+ const { bridge, sessionManager, channelManager } = buildBridge();
278
+ bridge.start();
279
+ sessionManager.emit('session:started', {
280
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
281
+ });
282
+ await tick();
283
+ const blocker = {
284
+ id: 'blocker-1', method: 'confirm', message: 'Confirm?',
285
+ event: { id: 'blocker-1', method: 'confirm' },
286
+ };
287
+ sessionManager.emit('session:blocked', {
288
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
289
+ });
290
+ await tick();
291
+ const collectorCalls = mockFn(channelManager._channel.createMessageComponentCollector).mock.calls;
292
+ assert.ok(collectorCalls.length > 0);
293
+ const collector = collectorCalls[0].result;
294
+ const mockInteraction = {
295
+ customId: 'blocker:blocker-1:confirm:true',
296
+ user: { id: 'owner-1' },
297
+ update: mock.fn(async () => { }),
298
+ reply: mock.fn(async () => { }),
299
+ };
300
+ collector.emit('collect', mockInteraction);
301
+ await tick();
302
+ assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 1);
303
+ const args = mockFn(sessionManager.resolveBlocker).mock.calls[0].arguments;
304
+ assert.equal(args[0], 'sess-1');
305
+ assert.equal(args[1], 'true');
306
+ });
307
+ it('rejects button click from unauthorized user', async () => {
308
+ const { bridge, sessionManager, channelManager } = buildBridge();
309
+ bridge.start();
310
+ sessionManager.emit('session:started', {
311
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
312
+ });
313
+ await tick();
314
+ const blocker = {
315
+ id: 'blocker-1', method: 'confirm', message: 'Confirm?',
316
+ event: { id: 'blocker-1', method: 'confirm' },
317
+ };
318
+ sessionManager.emit('session:blocked', {
319
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
320
+ });
321
+ await tick();
322
+ const collectorCalls = mockFn(channelManager._channel.createMessageComponentCollector).mock.calls;
323
+ const collector = collectorCalls[0].result;
324
+ const mockInteraction = {
325
+ customId: 'blocker:blocker-1:confirm:true',
326
+ user: { id: 'stranger-99' },
327
+ update: mock.fn(async () => { }),
328
+ reply: mock.fn(async () => { }),
329
+ };
330
+ collector.emit('collect', mockInteraction);
331
+ await tick();
332
+ assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 0);
333
+ assert.equal(mockFn(mockInteraction.reply).mock.callCount(), 1);
334
+ });
335
+ it('posts error when resolveBlocker throws', async () => {
336
+ const { bridge, sessionManager, channelManager } = buildBridge();
337
+ sessionManager.resolveBlocker = mock.fn(async () => { throw new Error('No pending blocker'); });
338
+ bridge.start();
339
+ sessionManager.emit('session:started', {
340
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
341
+ });
342
+ await tick();
343
+ const blocker = {
344
+ id: 'blocker-1', method: 'confirm', message: 'Confirm?',
345
+ event: { id: 'blocker-1', method: 'confirm' },
346
+ };
347
+ sessionManager.emit('session:blocked', {
348
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
349
+ });
350
+ await tick();
351
+ const collectorCalls = mockFn(channelManager._channel.createMessageComponentCollector).mock.calls;
352
+ const collector = collectorCalls[0].result;
353
+ const mockInteraction = {
354
+ customId: 'blocker:blocker-1:confirm:true',
355
+ user: { id: 'owner-1' },
356
+ update: mock.fn(async () => { }),
357
+ reply: mock.fn(async () => { }),
358
+ };
359
+ collector.emit('collect', mockInteraction);
360
+ await tick();
361
+ assert.equal(mockFn(mockInteraction.reply).mock.callCount(), 1);
362
+ const replyArg = mockFn(mockInteraction.reply).mock.calls[0].arguments[0];
363
+ assert.ok(String(replyArg.content).includes('Failed to resolve'));
364
+ });
365
+ });
366
+ describe('messageCreate relay', () => {
367
+ it('relays message to session steer when no pending blocker', async () => {
368
+ const session = createMockSession();
369
+ const { bridge, sessionManager, client } = buildBridge();
370
+ sessionManager.getSession = mock.fn(() => session);
371
+ bridge.start();
372
+ sessionManager.emit('session:started', {
373
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
374
+ });
375
+ await tick();
376
+ const msg = {
377
+ author: { id: 'owner-1', bot: false },
378
+ channelId: 'ch-123',
379
+ content: 'check the test results',
380
+ react: mock.fn(async () => { }),
381
+ reply: mock.fn(async () => { }),
382
+ };
383
+ client.emit('messageCreate', msg);
384
+ await tick();
385
+ assert.equal(mockFn(session.client.steer).mock.callCount(), 1);
386
+ assert.equal(mockFn(session.client.steer).mock.calls[0].arguments[0], 'check the test results');
387
+ });
388
+ it('resolves blocker via relay for input method', async () => {
389
+ const blocker = {
390
+ id: 'blocker-2', method: 'input', message: 'Enter value',
391
+ event: { id: 'blocker-2', method: 'input' },
392
+ };
393
+ const session = createMockSession({ pendingBlocker: blocker, status: 'blocked' });
394
+ const { bridge, sessionManager, client } = buildBridge();
395
+ sessionManager.getSession = mock.fn(() => session);
396
+ bridge.start();
397
+ sessionManager.emit('session:started', {
398
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
399
+ });
400
+ await tick();
401
+ const msg = {
402
+ author: { id: 'owner-1', bot: false },
403
+ channelId: 'ch-123',
404
+ content: 'my-api-key-value',
405
+ react: mock.fn(async () => { }),
406
+ reply: mock.fn(async () => { }),
407
+ };
408
+ client.emit('messageCreate', msg);
409
+ await tick();
410
+ assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 1);
411
+ assert.equal(mockFn(sessionManager.resolveBlocker).mock.calls[0].arguments[1], 'my-api-key-value');
412
+ });
413
+ it('ignores bot messages', async () => {
414
+ const session = createMockSession();
415
+ const { bridge, sessionManager, client } = buildBridge();
416
+ sessionManager.getSession = mock.fn(() => session);
417
+ bridge.start();
418
+ sessionManager.emit('session:started', {
419
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
420
+ });
421
+ await tick();
422
+ client.emit('messageCreate', {
423
+ author: { id: 'bot-1', bot: true },
424
+ channelId: 'ch-123',
425
+ content: 'automated',
426
+ react: mock.fn(async () => { }),
427
+ reply: mock.fn(async () => { }),
428
+ });
429
+ await tick();
430
+ assert.equal(mockFn(session.client.steer).mock.callCount(), 0);
431
+ });
432
+ it('ignores messages in non-project channels', async () => {
433
+ const session = createMockSession();
434
+ const { bridge, sessionManager, client } = buildBridge();
435
+ sessionManager.getSession = mock.fn(() => session);
436
+ bridge.start();
437
+ client.emit('messageCreate', {
438
+ author: { id: 'owner-1', bot: false },
439
+ channelId: 'random-ch-999',
440
+ content: 'hello',
441
+ react: mock.fn(async () => { }),
442
+ reply: mock.fn(async () => { }),
443
+ });
444
+ await tick();
445
+ assert.equal(mockFn(session.client.steer).mock.callCount(), 0);
446
+ });
447
+ it('ignores messages from unauthorized users', async () => {
448
+ const session = createMockSession();
449
+ const { bridge, sessionManager, client } = buildBridge();
450
+ sessionManager.getSession = mock.fn(() => session);
451
+ bridge.start();
452
+ sessionManager.emit('session:started', {
453
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
454
+ });
455
+ await tick();
456
+ client.emit('messageCreate', {
457
+ author: { id: 'stranger-99', bot: false },
458
+ channelId: 'ch-123',
459
+ content: 'hack the planet',
460
+ react: mock.fn(async () => { }),
461
+ reply: mock.fn(async () => { }),
462
+ });
463
+ await tick();
464
+ assert.equal(mockFn(session.client.steer).mock.callCount(), 0);
465
+ });
466
+ it('posts error when steer fails', async () => {
467
+ const session = createMockSession();
468
+ session.client.steer = mock.fn(async () => {
469
+ throw new Error('session dead');
470
+ });
471
+ const { bridge, sessionManager, client } = buildBridge();
472
+ sessionManager.getSession = mock.fn(() => session);
473
+ bridge.start();
474
+ sessionManager.emit('session:started', {
475
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
476
+ });
477
+ await tick();
478
+ const msg = {
479
+ author: { id: 'owner-1', bot: false },
480
+ channelId: 'ch-123',
481
+ content: 'try this',
482
+ react: mock.fn(async () => { }),
483
+ reply: mock.fn(async () => { }),
484
+ };
485
+ client.emit('messageCreate', msg);
486
+ await tick();
487
+ assert.equal(mockFn(msg.reply).mock.callCount(), 1);
488
+ });
489
+ });
490
+ describe('session:completed → cleanup', () => {
491
+ it('posts completion embed and cleans up', async () => {
492
+ const { bridge, sessionManager, logger } = buildBridge();
493
+ bridge.start();
494
+ sessionManager.emit('session:started', {
495
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
496
+ });
497
+ await tick();
498
+ sessionManager.emit('session:completed', {
499
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
500
+ });
501
+ await tick();
502
+ // After cleanup, events for this session are silently ignored
503
+ sessionManager.emit('session:event', {
504
+ sessionId: 'sess-1', projectDir: '/test/project',
505
+ event: { type: 'tool_execution_start', name: 'read' },
506
+ });
507
+ await tick();
508
+ assert.equal(mockFn(logger.error).mock.callCount(), 0);
509
+ });
510
+ });
511
+ describe('session:error → cleanup', () => {
512
+ it('posts error embed and cleans up', async () => {
513
+ const { bridge, sessionManager, logger } = buildBridge();
514
+ bridge.start();
515
+ sessionManager.emit('session:started', {
516
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
517
+ });
518
+ await tick();
519
+ sessionManager.emit('session:error', {
520
+ sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', error: 'Process crashed',
521
+ });
522
+ await tick();
523
+ const infoCalls = mockFn(logger.info).mock.calls;
524
+ assert.ok(infoCalls.some((c) => String(c.arguments[0]).includes('session error')));
525
+ });
526
+ });
527
+ });
528
+ //# sourceMappingURL=event-bridge.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bridge.test.js","sourceRoot":"","sources":["../src/event-bridge.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * event-bridge.test.ts — Tests for EventBridge orchestrator.\n *\n * Uses mock SessionManager (EventEmitter), mock ChannelManager,\n * mock Discord Client, and mock Logger to test event wiring,\n * blocker handling, conversation relay, and cleanup.\n */\n\nimport { describe, it, mock } from 'node:test';\nimport assert from 'node:assert/strict';\nimport { EventEmitter } from 'node:events';\nimport { EventBridge } from './event-bridge.js';\nimport type { EventBridgeOptions, BridgeClient } from './event-bridge.js';\nimport type { PendingBlocker, ManagedSession, DaemonConfig, SessionStatus } from './types.js';\nimport type { RpcClient } from '@opengsd/rpc-client';\nimport type { RpcExtensionUIRequest, SdkAgentEvent } from '@opengsd/contracts';\n\n// ---------------------------------------------------------------------------\n// Mock factories\n// ---------------------------------------------------------------------------\n\nfunction createMockLogger() {\n return {\n debug: mock.fn(() => {}),\n info: mock.fn(() => {}),\n warn: mock.fn(() => {}),\n error: mock.fn(() => {}),\n };\n}\n\nfunction createMockChannelManager() {\n const sentMessages: unknown[] = [];\n const mockChannel = {\n id: 'ch-123',\n send: mock.fn(async (_payload: unknown) => {\n sentMessages.push(_payload);\n return { id: 'msg-1' };\n }),\n createMessageComponentCollector: mock.fn((_opts?: unknown) => {\n const collector = new EventEmitter() as EventEmitter & { stop: (reason?: string) => void };\n collector.stop = (reason?: string) => collector.emit('end', [], reason ?? 'manual');\n return collector;\n }),\n };\n return {\n createProjectChannel: mock.fn(async (_dir: string) => mockChannel),\n _channel: mockChannel,\n _sentMessages: sentMessages,\n };\n}\n\nfunction createMockClient(): BridgeClient & EventEmitter {\n const emitter = new EventEmitter();\n const dmSendFn = mock.fn(async () => ({}));\n const fetchFn = mock.fn(async (_id: string) => ({ send: dmSendFn }));\n (emitter as unknown as Record<string, unknown>).users = { fetch: fetchFn };\n return Object.assign(emitter, {\n users: { fetch: fetchFn },\n _dmSend: dmSendFn,\n }) as unknown as BridgeClient & EventEmitter;\n}\n\nfunction createMockSessionManager() {\n const sm = new EventEmitter() as EventEmitter & {\n getSession: ReturnType<typeof mock.fn>;\n resolveBlocker: ReturnType<typeof mock.fn>;\n };\n sm.getSession = mock.fn((_id: string) => undefined as ManagedSession | undefined);\n sm.resolveBlocker = mock.fn(async (_sid: string, _resp: string) => {});\n return sm;\n}\n\nfunction createMockSession(overrides?: Partial<ManagedSession>): ManagedSession {\n return {\n sessionId: 'sess-1',\n projectDir: '/test/project',\n projectName: 'project',\n status: 'running' as SessionStatus,\n client: {\n steer: mock.fn(async (_msg: string) => {}),\n prompt: mock.fn(async () => ({})),\n } as unknown as RpcClient,\n events: [],\n pendingBlocker: null,\n cost: { totalCost: 0, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } },\n startTime: Date.now(),\n ...overrides,\n };\n}\n\nconst DEFAULT_CONFIG: DaemonConfig = {\n discord: {\n token: 'dummy',\n guild_id: 'guild-1',\n owner_id: 'owner-1',\n dm_on_blocker: false,\n },\n projects: { scan_roots: [] },\n log: { file: '/tmp/test.log', level: 'debug', max_size_mb: 10 },\n};\n\nfunction buildBridge(overrides?: Partial<EventBridgeOptions>) {\n const sessionManager = createMockSessionManager();\n const channelManager = createMockChannelManager();\n const client = createMockClient();\n const logger = createMockLogger();\n\n const opts: EventBridgeOptions = {\n sessionManager: sessionManager as unknown as EventBridgeOptions['sessionManager'],\n channelManager: channelManager as unknown as EventBridgeOptions['channelManager'],\n client,\n config: DEFAULT_CONFIG,\n logger: logger as unknown as EventBridgeOptions['logger'],\n ownerId: 'owner-1',\n ...overrides,\n };\n\n const bridge = new EventBridge(opts);\n return { bridge, sessionManager, channelManager, client, logger };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\nconst tick = () => new Promise<void>((r) => setTimeout(r, 30));\n\nfunction mockFn(obj: unknown): { mock: { callCount(): number; calls: Array<{ arguments: unknown[]; result?: unknown }> } } {\n return obj as { mock: { callCount(): number; calls: Array<{ arguments: unknown[]; result?: unknown }> } };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe('EventBridge', () => {\n describe('lifecycle', () => {\n it('start() subscribes to session manager events and messageCreate', () => {\n const { bridge, sessionManager, client } = buildBridge();\n bridge.start();\n assert.ok(sessionManager.listenerCount('session:started') > 0);\n assert.ok(sessionManager.listenerCount('session:event') > 0);\n assert.ok(sessionManager.listenerCount('session:blocked') > 0);\n assert.ok(sessionManager.listenerCount('session:completed') > 0);\n assert.ok(sessionManager.listenerCount('session:error') > 0);\n assert.ok(client.listenerCount('messageCreate') > 0);\n });\n\n it('stop() unsubscribes from all events and clears mappings', async () => {\n const { bridge, sessionManager, client } = buildBridge();\n bridge.start();\n await bridge.stop();\n assert.equal(sessionManager.listenerCount('session:started'), 0);\n assert.equal(sessionManager.listenerCount('session:event'), 0);\n assert.equal(sessionManager.listenerCount('session:blocked'), 0);\n assert.equal(sessionManager.listenerCount('session:completed'), 0);\n assert.equal(sessionManager.listenerCount('session:error'), 0);\n assert.equal(client.listenerCount('messageCreate'), 0);\n });\n\n it('start() is idempotent', () => {\n const { bridge, sessionManager } = buildBridge();\n bridge.start();\n bridge.start();\n assert.equal(sessionManager.listenerCount('session:started'), 1);\n });\n\n it('getVerbosityManager() returns a VerbosityManager', () => {\n const { bridge } = buildBridge();\n const vm = bridge.getVerbosityManager();\n assert.ok(vm);\n assert.equal(typeof vm.shouldShow, 'function');\n });\n });\n\n describe('session:started → channel creation + welcome embed', () => {\n it('creates channel and batcher', async () => {\n const { bridge, sessionManager, channelManager } = buildBridge();\n bridge.start();\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n assert.equal(mockFn(channelManager.createProjectChannel).mock.callCount(), 1);\n });\n\n it('logs error and skips when channel creation fails', async () => {\n const failingCm = {\n createProjectChannel: mock.fn(async () => { throw new Error('API error'); }),\n };\n const { bridge, sessionManager, logger } = buildBridge({\n channelManager: failingCm as unknown as EventBridgeOptions['channelManager'],\n });\n bridge.start();\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n assert.ok(mockFn(logger.error).mock.callCount() > 0);\n });\n });\n\n describe('session:event → format + verbosity filter + enqueue', () => {\n it('formats event and enqueues to batcher (no errors)', async () => {\n const { bridge, sessionManager, logger } = buildBridge();\n bridge.start();\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n sessionManager.emit('session:event', {\n sessionId: 'sess-1', projectDir: '/test/project',\n event: { type: 'tool_execution_start', name: 'read' } as SdkAgentEvent,\n });\n await tick();\n // No errors\n assert.equal(mockFn(logger.error).mock.callCount(), 0);\n });\n\n it('filters events based on verbosity', async () => {\n const { bridge, sessionManager, channelManager, logger } = buildBridge();\n bridge.start();\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n // Set quiet mode\n bridge.getVerbosityManager().setLevel('ch-123', 'quiet');\n\n // cost_update filtered in quiet\n sessionManager.emit('session:event', {\n sessionId: 'sess-1', projectDir: '/test/project',\n event: { type: 'cost_update', cumulativeCost: 1.5 } as SdkAgentEvent,\n });\n await tick();\n // tool_execution_start filtered in quiet\n sessionManager.emit('session:event', {\n sessionId: 'sess-1', projectDir: '/test/project',\n event: { type: 'tool_execution_start', name: 'read' } as SdkAgentEvent,\n });\n await tick();\n assert.equal(mockFn(logger.error).mock.callCount(), 0);\n });\n });\n\n describe('session:blocked → blocker embed + buttons + optional DM', () => {\n it('sends blocker embed and creates collector for confirm', async () => {\n const { bridge, sessionManager, channelManager } = buildBridge();\n bridge.start();\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const blocker: PendingBlocker = {\n id: 'blocker-1', method: 'confirm', message: 'Continue?',\n event: { id: 'blocker-1', method: 'confirm', message: 'Continue?' } as RpcExtensionUIRequest,\n };\n sessionManager.emit('session:blocked', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,\n });\n await tick();\n assert.ok(mockFn(channelManager._channel.createMessageComponentCollector).mock.callCount() > 0);\n });\n\n it('sends DM when dm_on_blocker is configured', async () => {\n const config: DaemonConfig = {\n ...DEFAULT_CONFIG,\n discord: { ...DEFAULT_CONFIG.discord!, dm_on_blocker: true },\n };\n const client = createMockClient();\n const { bridge, sessionManager } = buildBridge({ config, client });\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const blocker: PendingBlocker = {\n id: 'blocker-1', method: 'input', message: 'Enter API key',\n event: { id: 'blocker-1', method: 'input' } as RpcExtensionUIRequest,\n };\n sessionManager.emit('session:blocked', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,\n });\n await tick();\n\n const usersFetch = (client as unknown as Record<string, { fetch: unknown }>).users.fetch;\n assert.equal(mockFn(usersFetch).mock.callCount(), 1);\n });\n\n it('does not send DM when dm_on_blocker is false', async () => {\n const client = createMockClient();\n const { bridge, sessionManager } = buildBridge({ client });\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const blocker: PendingBlocker = {\n id: 'blocker-1', method: 'input', message: 'Enter value',\n event: { id: 'blocker-1', method: 'input' } as RpcExtensionUIRequest,\n };\n sessionManager.emit('session:blocked', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,\n });\n await tick();\n\n const usersFetch = (client as unknown as Record<string, { fetch: unknown }>).users.fetch;\n assert.equal(mockFn(usersFetch).mock.callCount(), 0);\n });\n });\n\n describe('button collector → resolveBlocker', () => {\n it('resolves blocker on button click from authorized user', async () => {\n const { bridge, sessionManager, channelManager } = buildBridge();\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const blocker: PendingBlocker = {\n id: 'blocker-1', method: 'confirm', message: 'Confirm?',\n event: { id: 'blocker-1', method: 'confirm' } as RpcExtensionUIRequest,\n };\n sessionManager.emit('session:blocked', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,\n });\n await tick();\n\n const collectorCalls = mockFn(channelManager._channel.createMessageComponentCollector).mock.calls;\n assert.ok(collectorCalls.length > 0);\n const collector = collectorCalls[0]!.result as EventEmitter;\n\n const mockInteraction = {\n customId: 'blocker:blocker-1:confirm:true',\n user: { id: 'owner-1' },\n update: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n };\n collector.emit('collect', mockInteraction);\n await tick();\n\n assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 1);\n const args = mockFn(sessionManager.resolveBlocker).mock.calls[0]!.arguments;\n assert.equal(args[0], 'sess-1');\n assert.equal(args[1], 'true');\n });\n\n it('rejects button click from unauthorized user', async () => {\n const { bridge, sessionManager, channelManager } = buildBridge();\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const blocker: PendingBlocker = {\n id: 'blocker-1', method: 'confirm', message: 'Confirm?',\n event: { id: 'blocker-1', method: 'confirm' } as RpcExtensionUIRequest,\n };\n sessionManager.emit('session:blocked', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,\n });\n await tick();\n\n const collectorCalls = mockFn(channelManager._channel.createMessageComponentCollector).mock.calls;\n const collector = collectorCalls[0]!.result as EventEmitter;\n\n const mockInteraction = {\n customId: 'blocker:blocker-1:confirm:true',\n user: { id: 'stranger-99' },\n update: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n };\n collector.emit('collect', mockInteraction);\n await tick();\n\n assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 0);\n assert.equal(mockFn(mockInteraction.reply).mock.callCount(), 1);\n });\n\n it('posts error when resolveBlocker throws', async () => {\n const { bridge, sessionManager, channelManager } = buildBridge();\n sessionManager.resolveBlocker = mock.fn(async () => { throw new Error('No pending blocker'); });\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const blocker: PendingBlocker = {\n id: 'blocker-1', method: 'confirm', message: 'Confirm?',\n event: { id: 'blocker-1', method: 'confirm' } as RpcExtensionUIRequest,\n };\n sessionManager.emit('session:blocked', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,\n });\n await tick();\n\n const collectorCalls = mockFn(channelManager._channel.createMessageComponentCollector).mock.calls;\n const collector = collectorCalls[0]!.result as EventEmitter;\n\n const mockInteraction = {\n customId: 'blocker:blocker-1:confirm:true',\n user: { id: 'owner-1' },\n update: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n };\n collector.emit('collect', mockInteraction);\n await tick();\n\n assert.equal(mockFn(mockInteraction.reply).mock.callCount(), 1);\n const replyArg = mockFn(mockInteraction.reply).mock.calls[0]!.arguments[0] as Record<string, unknown>;\n assert.ok(String(replyArg.content).includes('Failed to resolve'));\n });\n });\n\n describe('messageCreate relay', () => {\n it('relays message to session steer when no pending blocker', async () => {\n const session = createMockSession();\n const { bridge, sessionManager, client } = buildBridge();\n sessionManager.getSession = mock.fn(() => session);\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const msg = {\n author: { id: 'owner-1', bot: false },\n channelId: 'ch-123',\n content: 'check the test results',\n react: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n };\n client.emit('messageCreate', msg);\n await tick();\n\n assert.equal(mockFn(session.client.steer).mock.callCount(), 1);\n assert.equal(mockFn(session.client.steer).mock.calls[0]!.arguments[0], 'check the test results');\n });\n\n it('resolves blocker via relay for input method', async () => {\n const blocker: PendingBlocker = {\n id: 'blocker-2', method: 'input', message: 'Enter value',\n event: { id: 'blocker-2', method: 'input' } as RpcExtensionUIRequest,\n };\n const session = createMockSession({ pendingBlocker: blocker, status: 'blocked' });\n const { bridge, sessionManager, client } = buildBridge();\n sessionManager.getSession = mock.fn(() => session);\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const msg = {\n author: { id: 'owner-1', bot: false },\n channelId: 'ch-123',\n content: 'my-api-key-value',\n react: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n };\n client.emit('messageCreate', msg);\n await tick();\n\n assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 1);\n assert.equal(mockFn(sessionManager.resolveBlocker).mock.calls[0]!.arguments[1], 'my-api-key-value');\n });\n\n it('ignores bot messages', async () => {\n const session = createMockSession();\n const { bridge, sessionManager, client } = buildBridge();\n sessionManager.getSession = mock.fn(() => session);\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n client.emit('messageCreate', {\n author: { id: 'bot-1', bot: true },\n channelId: 'ch-123',\n content: 'automated',\n react: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n });\n await tick();\n\n assert.equal(mockFn(session.client.steer).mock.callCount(), 0);\n });\n\n it('ignores messages in non-project channels', async () => {\n const session = createMockSession();\n const { bridge, sessionManager, client } = buildBridge();\n sessionManager.getSession = mock.fn(() => session);\n bridge.start();\n\n client.emit('messageCreate', {\n author: { id: 'owner-1', bot: false },\n channelId: 'random-ch-999',\n content: 'hello',\n react: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n });\n await tick();\n\n assert.equal(mockFn(session.client.steer).mock.callCount(), 0);\n });\n\n it('ignores messages from unauthorized users', async () => {\n const session = createMockSession();\n const { bridge, sessionManager, client } = buildBridge();\n sessionManager.getSession = mock.fn(() => session);\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n client.emit('messageCreate', {\n author: { id: 'stranger-99', bot: false },\n channelId: 'ch-123',\n content: 'hack the planet',\n react: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n });\n await tick();\n\n assert.equal(mockFn(session.client.steer).mock.callCount(), 0);\n });\n\n it('posts error when steer fails', async () => {\n const session = createMockSession();\n (session.client as unknown as Record<string, unknown>).steer = mock.fn(async () => {\n throw new Error('session dead');\n });\n const { bridge, sessionManager, client } = buildBridge();\n sessionManager.getSession = mock.fn(() => session);\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n const msg = {\n author: { id: 'owner-1', bot: false },\n channelId: 'ch-123',\n content: 'try this',\n react: mock.fn(async () => {}),\n reply: mock.fn(async () => {}),\n };\n client.emit('messageCreate', msg);\n await tick();\n\n assert.equal(mockFn(msg.reply).mock.callCount(), 1);\n });\n });\n\n describe('session:completed → cleanup', () => {\n it('posts completion embed and cleans up', async () => {\n const { bridge, sessionManager, logger } = buildBridge();\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n sessionManager.emit('session:completed', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n // After cleanup, events for this session are silently ignored\n sessionManager.emit('session:event', {\n sessionId: 'sess-1', projectDir: '/test/project',\n event: { type: 'tool_execution_start', name: 'read' } as SdkAgentEvent,\n });\n await tick();\n assert.equal(mockFn(logger.error).mock.callCount(), 0);\n });\n });\n\n describe('session:error → cleanup', () => {\n it('posts error embed and cleans up', async () => {\n const { bridge, sessionManager, logger } = buildBridge();\n bridge.start();\n\n sessionManager.emit('session:started', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',\n });\n await tick();\n\n sessionManager.emit('session:error', {\n sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', error: 'Process crashed',\n });\n await tick();\n\n const infoCalls = mockFn(logger.info).mock.calls;\n assert.ok(\n infoCalls.some((c) => String(c.arguments[0]).includes('session error')),\n );\n });\n });\n});\n"]}