@stage-labs/metro 0.1.0-beta.4 → 0.1.0-beta.41

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 (249) hide show
  1. package/dist/api.js +131 -0
  2. package/dist/bedrock-eventstream.js +120 -0
  3. package/dist/bedrock-proxy.js +260 -0
  4. package/dist/bedrock-upstream.js +72 -0
  5. package/dist/bedrock.js +106 -0
  6. package/dist/claude.js +15 -0
  7. package/dist/cli.js +181 -207
  8. package/dist/control.js +173 -0
  9. package/dist/plugin.js +82 -0
  10. package/dist/prompt.js +61 -0
  11. package/dist/runtime.js +117 -0
  12. package/dist/serve.js +77 -0
  13. package/dist/store.js +93 -0
  14. package/dist/tail.js +120 -0
  15. package/dist/update.js +33 -0
  16. package/dist/version.js +97 -0
  17. package/package.json +34 -21
  18. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  19. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +119 -0
  20. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  21. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  22. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  23. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +210 -0
  24. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  25. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  26. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  27. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  28. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  29. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  30. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  31. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  32. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  33. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  34. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  35. package/runtime/node_modules/@metro-labs/mcp/package.json +67 -0
  36. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
  37. package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +367 -0
  38. package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
  39. package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
  40. package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
  41. package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
  42. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
  43. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +334 -0
  44. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-connector-api.ts +150 -0
  45. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-import.ts +76 -0
  46. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-pair.ts +20 -0
  47. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
  48. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +153 -0
  49. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
  50. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
  51. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +133 -0
  52. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +278 -0
  53. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +383 -0
  54. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +143 -0
  55. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +33 -0
  56. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +315 -0
  57. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
  58. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
  59. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
  60. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +100 -0
  61. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +57 -0
  62. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +333 -0
  63. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
  64. package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
  65. package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +117 -0
  66. package/runtime/node_modules/@metro-labs/mcp/src/daemon/hosted-connectors.ts +118 -0
  67. package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +369 -0
  68. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +86 -0
  69. package/runtime/node_modules/@metro-labs/mcp/src/daemon/import-api.ts +67 -0
  70. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-connectors-api.ts +56 -0
  71. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +187 -0
  72. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-secret.ts +21 -0
  73. package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
  74. package/runtime/node_modules/@metro-labs/mcp/src/daemon/mode-api.ts +31 -0
  75. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +162 -0
  76. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +120 -0
  77. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
  78. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +58 -0
  79. package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +145 -0
  80. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +214 -0
  81. package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +224 -0
  82. package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +303 -0
  83. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +139 -0
  84. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +105 -0
  85. package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
  86. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +27 -0
  87. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +63 -0
  88. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-config.ts +27 -0
  89. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +218 -0
  90. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-auth.ts +82 -0
  91. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-nonces.ts +43 -0
  92. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-routes.ts +89 -0
  93. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
  94. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +320 -0
  95. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
  96. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
  97. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +257 -0
  98. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +295 -0
  99. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
  100. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +91 -0
  101. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +214 -0
  102. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-connectors.ts +143 -0
  103. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +84 -0
  104. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
  105. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +37 -0
  106. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +147 -0
  107. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +126 -0
  108. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +326 -0
  109. package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +319 -0
  110. package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +152 -0
  111. package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +21 -0
  112. package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
  113. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +373 -0
  114. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +309 -0
  115. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +24 -0
  116. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +105 -0
  117. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +118 -0
  118. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +45 -0
  119. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +72 -0
  120. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +18 -0
  121. package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +141 -0
  122. package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +203 -0
  123. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
  124. package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
  125. package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
  126. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
  127. package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
  128. package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +192 -0
  129. package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
  130. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
  131. package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
  132. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +73 -0
  133. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
  134. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
  135. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +211 -0
  136. package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
  137. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +212 -0
  138. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +397 -0
  139. package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
  140. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +240 -0
  141. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
  142. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +80 -0
  143. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
  144. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
  145. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
  146. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
  147. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +182 -0
  148. package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
  149. package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
  150. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
  151. package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
  152. package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
  153. package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -0
  154. package/runtime/node_modules/@metro-labs/telegram/package.json +28 -0
  155. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +81 -0
  156. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  157. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  158. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  159. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +141 -0
  160. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  161. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  162. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  163. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  164. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  165. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  166. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  167. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  168. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  169. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  170. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  171. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  172. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  173. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  174. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  175. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  176. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
  177. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  178. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  179. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  180. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  181. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  183. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  184. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  185. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +40 -0
  186. package/runtime/node_modules/@metro-labs/whatsapp/package.json +29 -0
  187. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +67 -0
  188. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  189. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  190. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  191. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  192. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +318 -0
  193. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  194. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +132 -0
  195. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  196. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  197. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  198. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  199. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  200. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  201. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  202. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  203. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +141 -0
  204. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  205. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  206. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  207. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  208. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  209. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +113 -0
  210. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  211. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  212. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  213. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  214. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  215. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  216. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  217. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  218. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  219. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +333 -0
  220. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +78 -0
  221. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  222. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  223. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  224. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  225. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  226. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  227. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  228. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  229. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  230. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  231. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  232. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  233. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +39 -0
  234. package/runtime/runtime.json +3 -0
  235. package/runtime/trains/.keep +0 -0
  236. package/LICENSE +0 -21
  237. package/README.md +0 -117
  238. package/dist/agents/claude.js +0 -207
  239. package/dist/agents/codex.js +0 -207
  240. package/dist/agents/types.js +0 -2
  241. package/dist/channels/discord.js +0 -104
  242. package/dist/channels/telegram.js +0 -189
  243. package/dist/helpers/scope-cache.js +0 -65
  244. package/dist/helpers/streaming.js +0 -209
  245. package/dist/helpers/telegram-format.js +0 -39
  246. package/dist/helpers/turn.js +0 -40
  247. package/dist/log.js +0 -10
  248. package/dist/orchestrator.js +0 -208
  249. package/dist/paths.js +0 -68
@@ -0,0 +1,90 @@
1
+ import { errMsg } from '@metro-labs/mcp/log';
2
+ import { mintId, SELF_URI } from './wire.js';
3
+
4
+ type EmitInbound = (accountId: string, e: Record<string, unknown>) => void;
5
+
6
+ const WHISPER_BIN = process.env.METRO_WHISPER_BIN ?? 'whisper-cli';
7
+ const WHISPER_MODEL =
8
+ process.env.METRO_WHISPER_MODEL ??
9
+ `${process.env.HOME}/.cache/whisper-cpp/ggml-base.bin`;
10
+ const FFMPEG_BIN = process.env.METRO_FFMPEG_BIN ?? 'ffmpeg';
11
+
12
+ export async function transcribeAndEmit(
13
+ audio: Uint8Array,
14
+ line: string,
15
+ accountId: string,
16
+ sourceMsgId: string,
17
+ emitInbound: EmitInbound,
18
+ ): Promise<void> {
19
+ const {
20
+ existsSync: ex,
21
+ readFileSync: rf,
22
+ writeFileSync: wf,
23
+ unlinkSync,
24
+ mkdtempSync,
25
+ } = await import('node:fs');
26
+ const { tmpdir } = await import('node:os');
27
+ const path = await import('node:path');
28
+ const j = (...parts: string[]): string => path.join(...parts);
29
+ const { spawn } = await import('node:child_process');
30
+ if (!ex(WHISPER_MODEL)) return;
31
+ const dir = mkdtempSync(j(tmpdir(), 'xmtp-tx-'));
32
+ const inFile = j(dir, 'in.m4a');
33
+ const wav = j(dir, 'in.wav');
34
+ const out = j(dir, 'in');
35
+ const run = (bin: string, args: string[]): Promise<void> =>
36
+ new Promise((res, rej) => {
37
+ const p = spawn(bin, args, { stdio: 'ignore' });
38
+ p.on('error', rej);
39
+ p.on('exit', (c) => {
40
+ if (c === 0) res();
41
+ else rej(new Error(`${bin} ${String(c)}`));
42
+ });
43
+ });
44
+ try {
45
+ wf(inFile, audio);
46
+ await run(FFMPEG_BIN, [
47
+ '-y',
48
+ '-i',
49
+ inFile,
50
+ '-ar',
51
+ '16000',
52
+ '-ac',
53
+ '1',
54
+ wav,
55
+ ]);
56
+ await run(WHISPER_BIN, [
57
+ '-m',
58
+ WHISPER_MODEL,
59
+ '-f',
60
+ wav,
61
+ '--output-txt',
62
+ '-of',
63
+ out,
64
+ ]);
65
+ const text = rf(`${out}.txt`, 'utf8').trim();
66
+ if (!text) return;
67
+ emitInbound(accountId, {
68
+ id: mintId(),
69
+ ts: new Date().toISOString(),
70
+ station: 'xmtp',
71
+ line,
72
+ from: SELF_URI,
73
+ text: `🎙️ ${text}`,
74
+ payload: {
75
+ contentType: 'transcript',
76
+ transcribeFor: sourceMsgId,
77
+ transcript: text,
78
+ },
79
+ });
80
+ } catch (err) {
81
+ process.stderr.write(`xmtp transcribe failed: ${errMsg(err)}\n`);
82
+ } finally {
83
+ for (const f of [inFile, wav, `${out}.txt`]) {
84
+ try {
85
+ unlinkSync(f);
86
+ } catch {
87
+ }
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,84 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { Client, type ClientOptions, type Signer } from '@xmtp/node-sdk';
4
+ import { expandHome, signerFor, XMTP_ENV } from './identity.js';
5
+
6
+ export class XmtpVerifyError extends Error {}
7
+
8
+ export interface XmtpVerified {
9
+ inboxId: string;
10
+ address: string;
11
+ installationId: string;
12
+ dbPath: string;
13
+ }
14
+
15
+ interface RegisteredClient {
16
+ inboxId: string;
17
+ installationId: string;
18
+ isRegistered: boolean;
19
+ }
20
+
21
+ export type CreateXmtpClient = (
22
+ signer: Signer,
23
+ dbPath: string,
24
+ ) => Promise<RegisteredClient>;
25
+
26
+ const openInbox: CreateXmtpClient = (signer, dbPath) => {
27
+ const options: ClientOptions = { env: XMTP_ENV, dbPath };
28
+ return Client.create(signer, options);
29
+ };
30
+
31
+ const reason = (err: unknown): string =>
32
+ err instanceof Error && err.message !== '' ? err.message : 'no reason given';
33
+
34
+ export async function verifyXmtpKey(
35
+ privateKey: string,
36
+ dbPath: string,
37
+ create: CreateXmtpClient = openInbox,
38
+ ): Promise<XmtpVerified> {
39
+ let signer: Signer;
40
+ let address: string;
41
+ try {
42
+ ({ signer, address } = signerFor(privateKey));
43
+ } catch {
44
+ throw new XmtpVerifyError('that is not a usable XMTP private key');
45
+ }
46
+ const resolved = expandHome(dbPath);
47
+ mkdirSync(dirname(resolved), { recursive: true });
48
+ const client = await create(signer, resolved).catch((err: unknown) => {
49
+ throw new XmtpVerifyError(
50
+ `XMTP would not open an inbox for that key: ${reason(err)}`,
51
+ );
52
+ });
53
+ if (!client.isRegistered)
54
+ throw new XmtpVerifyError('XMTP did not register an inbox for that key');
55
+ if (client.inboxId === '')
56
+ throw new XmtpVerifyError('XMTP returned no inbox id for that key');
57
+ return {
58
+ inboxId: client.inboxId,
59
+ address,
60
+ installationId: client.installationId,
61
+ dbPath: resolved,
62
+ };
63
+ }
64
+
65
+ async function runCli(): Promise<void> {
66
+ let out: Record<string, unknown>;
67
+ try {
68
+ const input = JSON.parse(readFileSync(0, 'utf8')) as {
69
+ privateKey?: unknown;
70
+ dbPath?: unknown;
71
+ };
72
+ if (typeof input.privateKey !== 'string' || typeof input.dbPath !== 'string')
73
+ throw new XmtpVerifyError('xmtp verify needs a privateKey and a dbPath');
74
+ out = { ok: true, ...(await verifyXmtpKey(input.privateKey, input.dbPath)) };
75
+ } catch (err) {
76
+ out = { ok: false, error: reason(err) };
77
+ }
78
+ writeFileSync(1, `${JSON.stringify(out)}\n`);
79
+ }
80
+
81
+ if (import.meta.main) {
82
+ await runCli();
83
+ process.exit(0);
84
+ }
@@ -0,0 +1,39 @@
1
+ export { emit, respond, mintId } from '@metro-labs/mcp/stations/station-runtime';
2
+
3
+ export const SELF_URI = process.env.METRO_SELF_URI ?? '';
4
+
5
+ const UID_MAP_MAX = 5000;
6
+ const uidToXmtp = new Map<string, string>();
7
+
8
+ const INBOX_ETH_CACHE_MAX = 5000;
9
+ export const inboxEthCache = new Map<string, string>();
10
+
11
+ export function cacheInboxEth(inboxId: string, eth: string): void {
12
+ inboxEthCache.set(inboxId, eth);
13
+ if (inboxEthCache.size > INBOX_ETH_CACHE_MAX) {
14
+ const oldest = inboxEthCache.keys().next().value;
15
+ if (oldest !== undefined) inboxEthCache.delete(oldest);
16
+ }
17
+ }
18
+
19
+ export function rememberUid(
20
+ uid: string | undefined,
21
+ xmtpId: string | undefined,
22
+ ): void {
23
+ if (!uid || !xmtpId || !uid.startsWith('msg_')) return;
24
+ uidToXmtp.set(uid, xmtpId);
25
+ if (uidToXmtp.size > UID_MAP_MAX) {
26
+ const oldest = uidToXmtp.keys().next().value;
27
+ if (oldest !== undefined) uidToXmtp.delete(oldest);
28
+ }
29
+ }
30
+
31
+ export function resolveMsgId(rawId: string): string {
32
+ if (!rawId.startsWith('msg_')) return rawId;
33
+ const mapped = uidToXmtp.get(rawId);
34
+ if (mapped) return mapped;
35
+ throw new Error(
36
+ `could not resolve universal id ${rawId} to an xmtp message id ` +
37
+ '(not seen by this train; pass the raw xmtp message_id)',
38
+ );
39
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "version": "0.1.0-beta.41"
3
+ }
File without changes
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Stage Labs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/README.md DELETED
@@ -1,117 +0,0 @@
1
- # Metro
2
-
3
- Run a long-lived daemon that bridges Discord and Telegram to your Codex + Claude Code agents. Each chat thread/topic gets its own agent session with streaming responses and inline, persistent tool-call traces. Both agents run side-by-side — pick per-message with a `with claude` / `with codex` suffix.
4
-
5
- ## Prereqs
6
-
7
- - **Node ≥ 22** (or Bun ≥ 1.3).
8
- - **One or both agent CLIs** installed and authenticated:
9
- - **Claude Code** — run `claude` once interactively to log in. Metro shells out per turn and inherits your auth, plugins, settings.
10
- - **Codex** — run `codex` once interactively to log in. Metro spawns `codex app-server` and inherits your auth, MCPs, sandboxing.
11
- - **Discord bot** (optional) with **Message Content Intent** enabled (Developer Portal → Bot → Privileged Gateway Intents).
12
- - **Telegram bot** (optional). In supergroup forums, the bot also needs the **Manage Topics** admin permission so it can auto-create topics on @-mention.
13
-
14
- ## Quickstart
15
-
16
- ```bash
17
- npm install -g @stage-labs/metro@beta # or: bun add -g @stage-labs/metro@beta
18
-
19
- metro setup discord <token> # https://discord.com/developers/applications
20
- metro setup telegram <token> # https://t.me/BotFather
21
-
22
- metro doctor # verify
23
- metro # run the orchestrator
24
- ```
25
-
26
- Metro starts both agents at boot and listens on whichever platforms are configured. Each scope defaults to **Claude** for the first turn; once you've used an agent there, follow-up messages stick with it. Switch per-message by suffixing `with claude` or `with codex` (any casing):
27
-
28
- ```
29
- @Metro draft a release note
30
- → uses Claude (default for a new scope)
31
-
32
- How would Codex have done this? with codex
33
- → routes this turn to Codex; stays Codex on subsequent turns
34
- ```
35
-
36
- ### Discord
37
-
38
- @-mention the bot in any channel:
39
- 1. Metro creates a thread anchored on your message (named after the message).
40
- 2. Spins up an agent session for that thread.
41
- 3. Streams the agent's reply with each tool call as its own block: plain header `🛠 **Read**` followed by two fenced code blocks — input (`src/foo.ts`) above, output (file contents) below. Outputs are capped at 50 lines / 1500 chars per tool with a `_(N more lines)_` note if truncated. `Thinking…` shows as a transient status that vanishes once real content arrives.
42
-
43
- Follow-ups in the thread route automatically — no @-mention needed.
44
-
45
- ### Telegram
46
-
47
- - **DM the bot** — every message is implicitly addressed to it; one scope per chat.
48
- - **@-mention the bot in a forum supergroup's General topic** — metro auto-creates a new topic for the conversation (Discord-style "thread from message") and posts a deep link back in General so the new topic is one tap away. Subsequent messages in that topic route automatically.
49
- - **Inside an existing custom topic** — routes to that topic's scope on every message.
50
-
51
- Regular (non-forum) groups are not routed — they have no thread boundary.
52
-
53
- ## How it works
54
-
55
- ```
56
- Discord gateway ──┐ ┌─▶ codex app-server (long-lived subprocess, UDS JSON-RPC)
57
- ├─▶ metro orchestrator ─┤
58
- Telegram poller ──┘ └─▶ claude -p ... (per-turn subprocess, stream-json)
59
- │
60
- └──── scope map (scopes.json)
61
- ```
62
-
63
- - **One metro = one daemon.** Lockfile at `$METRO_STATE_DIR/.tail-lock` keeps things singleton.
64
- - **Both agents side-by-side.** A scope can have up to one session per agent — independent histories. Routing is per-message: explicit `with claude` / `with codex` suffix, otherwise the scope's last-used agent, otherwise Claude.
65
- - **Streaming.** Replies edit one message every ~1500 ms while deltas stream in (leading-edge first flush for fast initial feedback). Long replies split past ~1900 chars onto a follow-up message.
66
- - **Tool-call visibility.** Each tool call is rendered as a plain `🛠 **<tool>**` header plus two fenced code blocks — input then output — paired by tool id so parallel calls don't collide. Both blocks are fully visible (no collapse). Outputs are capped at 50 lines / 1500 chars per tool.
67
- - **Telegram formatting.** Agent markdown (`**bold**`, `*italic*`, `` `code` ``, fenced blocks, `[link](url)`, blockquotes) is converted to Telegram's HTML parse mode on the way out, so it renders as formatted text instead of literal characters.
68
- - **No link previews.** Outgoing messages set `link_preview_options.is_disabled` on Telegram and the `SUPPRESS_EMBEDS` flag on Discord, so URLs in agent replies don't unfurl into giant auto-embeds.
69
- - **Queueing.** Messages that arrive while a turn is running are buffered per-scope and answered together in the next reply.
70
- - **Catchup-on-restart.** Discord uses a per-scope `lastSeenMessageId` watermark and REST-fetches anything newer when metro comes back up. Telegram leans on its own update-id queue (persisted offset in `telegram-offset.json`).
71
-
72
- ## Config
73
-
74
- | Variable | Default | Description |
75
- |---|---|---|
76
- | `TELEGRAM_BOT_TOKEN`, `DISCORD_BOT_TOKEN` | — | Bot tokens. `metro setup` writes them here. |
77
- | `METRO_CONFIG_DIR` | `~/.config/metro` | Where the global `.env` lives. |
78
- | `METRO_STATE_DIR` | `~/.cache/metro` | Lockfile, scope cache, codex socket, telegram offset, claude session set. |
79
- | `METRO_LOG_LEVEL` | `info` | `trace` / `debug` / `info` / `warn` / `error` / `fatal`. |
80
-
81
- Token precedence: process env → `./.env` → `$METRO_CONFIG_DIR/.env`. Logs to stderr.
82
-
83
- ## Develop locally
84
-
85
- ```bash
86
- git clone https://github.com/bonustrack/metro && cd metro
87
- bun install && bun run build
88
- bun link # makes `metro` resolve to this checkout
89
- METRO_LOG_LEVEL=debug metro
90
- ```
91
-
92
- ## Reference
93
-
94
- - `metro --help` — command surface
95
- - `metro doctor` — health check (tokens + gateway/poller reachability + orchestrator status)
96
- - State files (`$METRO_STATE_DIR`, defaults to `~/.cache/metro/`):
97
- - `scopes.json` — Discord-thread / Telegram-topic ↔ agent-session map
98
- - `.tail-lock` — orchestrator pid
99
- - `codex-app-server.sock` — codex's UDS
100
- - `telegram-offset.json` — last processed update id (used for catchup on restart)
101
- - `claude-sessions.json` — set of started Claude session uuids (so restarts use `--resume` instead of `--session-id`)
102
-
103
- ## Uninstall
104
-
105
- ```bash
106
- metro setup clear
107
- rm -rf ~/.cache/metro/
108
- npm uninstall -g @stage-labs/metro
109
- ```
110
-
111
- ## Caveats
112
-
113
- - **No allowlist.** Anyone who can DM/@-mention your bot can spawn an agent session. Run against bots you own.
114
- - **Per-agent histories are separate.** Switching with `with codex` mid-scope spins up a fresh Codex session — it has no idea what you discussed with Claude in the same scope. Each agent only sees what was sent to it.
115
- - **One agent missing is OK.** If only `claude` or only `codex` is installed/authenticated, metro still starts; messages asking for the missing one surface an error in chat.
116
- - **Telegram non-forum groups are skipped.** Without a per-topic thread boundary the routing model breaks down. DMs and forum topics (incl. auto-created ones from General) work normally.
117
- - **Telegram bot privacy.** Default Telegram bot privacy is *enabled*, which can block group messages even with @-mentions. Disable in [@BotFather](https://t.me/BotFather) → Bot Settings → Group Privacy → Turn off, then kick + re-invite the bot.
@@ -1,207 +0,0 @@
1
- /** Claude Code adapter: per-turn `claude -p` subprocess; --session-id then --resume (persisted across restarts). */
2
- import { spawn } from 'node:child_process';
3
- import { randomUUID } from 'node:crypto';
4
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
5
- import { join } from 'node:path';
6
- import { errMsg, log } from '../log.js';
7
- import { STATE_DIR } from '../paths.js';
8
- const STARTED_FILE = join(STATE_DIR, 'claude-sessions.json');
9
- function loadStarted() {
10
- if (!existsSync(STARTED_FILE))
11
- return new Set();
12
- try {
13
- return new Set(JSON.parse(readFileSync(STARTED_FILE, 'utf8')));
14
- }
15
- catch (err) {
16
- log.warn({ err: errMsg(err), path: STARTED_FILE }, 'claude: started cache read failed');
17
- return new Set();
18
- }
19
- }
20
- export class ClaudeAgent {
21
- started = loadStarted();
22
- children = new Set();
23
- persistStarted() {
24
- try {
25
- writeFileSync(STARTED_FILE, JSON.stringify([...this.started]));
26
- }
27
- catch (err) {
28
- log.warn({ err: errMsg(err), path: STARTED_FILE }, 'claude: started cache write failed');
29
- }
30
- }
31
- async start() {
32
- await new Promise((resolve, reject) => {
33
- const c = spawn('claude', ['--version'], { stdio: ['ignore', 'pipe', 'pipe'] });
34
- let out = '';
35
- c.stdout.on('data', d => { out += String(d); });
36
- c.on('error', reject);
37
- c.on('exit', code => {
38
- if (code === 0) {
39
- log.info({ version: out.trim() }, 'claude agent: ready');
40
- resolve();
41
- }
42
- else
43
- reject(new Error(`claude --version exited with ${code}`));
44
- });
45
- });
46
- }
47
- async stop() {
48
- for (const c of this.children) {
49
- try {
50
- c.kill('SIGTERM');
51
- }
52
- catch { /* ignore */ }
53
- }
54
- this.children.clear();
55
- }
56
- async createThread() {
57
- const id = randomUUID();
58
- log.info({ thread: id }, 'claude agent: thread allocated');
59
- return id;
60
- }
61
- async sendTurn(threadId, text, callbacks) {
62
- const args = ['-p', '--output-format', 'stream-json', '--include-partial-messages', '--verbose'];
63
- args.push(this.started.has(threadId) ? '--resume' : '--session-id', threadId, text);
64
- const child = spawn('claude', args, { stdio: ['ignore', 'pipe', 'pipe'] });
65
- this.children.add(child);
66
- log.debug({ thread: threadId }, 'claude agent: turn started');
67
- const session = new TurnSession(callbacks);
68
- let buffer = '';
69
- child.stdout?.on('data', d => {
70
- buffer += String(d);
71
- let nl;
72
- while ((nl = buffer.indexOf('\n')) !== -1) {
73
- const line = buffer.slice(0, nl).trim();
74
- buffer = buffer.slice(nl + 1);
75
- if (!line)
76
- continue;
77
- try {
78
- session.handle(JSON.parse(line));
79
- }
80
- catch (err) {
81
- log.warn({ err: errMsg(err) }, 'claude agent: malformed event');
82
- }
83
- }
84
- });
85
- child.stderr?.on('data', d => log.trace({ src: 'claude-stderr' }, String(d).trim()));
86
- child.on('exit', code => {
87
- this.children.delete(child);
88
- if (!this.started.has(threadId)) {
89
- this.started.add(threadId);
90
- this.persistStarted();
91
- }
92
- if (!session.done) {
93
- if (code === 0)
94
- session.fireComplete();
95
- else
96
- session.fireError(new Error(`claude exited with code ${code}`));
97
- }
98
- });
99
- child.on('error', err => { this.children.delete(child); if (!session.done)
100
- session.fireError(err); });
101
- }
102
- }
103
- class TurnSession {
104
- cb;
105
- done = false;
106
- thinking = true;
107
- /** tool_use_id → kind; survives until the matching tool_result lands. */
108
- byUseId = new Map();
109
- /** Pending tool_use blocks: input JSON streams in over multiple `input_json_delta` events before the input is complete. */
110
- pendingTools = new Map();
111
- constructor(cb) {
112
- this.cb = cb;
113
- cb.onToolStart({ id: 'thinking', kind: 'thinking', name: 'Thinking…', transient: true });
114
- }
115
- fireComplete() { if (this.done)
116
- return; this.done = true; this.clearThinking(); this.cb.onComplete(); }
117
- fireError(err) { if (this.done)
118
- return; this.done = true; this.clearThinking(); this.cb.onError(err); }
119
- clearThinking() { if (!this.thinking)
120
- return; this.thinking = false; this.cb.onToolEnd('thinking'); }
121
- handle(ev) {
122
- if (ev.type === 'result') {
123
- if (ev.is_error)
124
- this.fireError(new Error(typeof ev.result === 'string' ? ev.result : 'claude error'));
125
- else
126
- this.fireComplete();
127
- return;
128
- }
129
- if (ev.type === 'user') {
130
- this.handleToolResults(ev);
131
- return;
132
- }
133
- if (ev.type !== 'stream_event' || !ev.event)
134
- return;
135
- const e = ev.event;
136
- if (e.type === 'content_block_start' && e.content_block?.type === 'tool_use') {
137
- this.clearThinking();
138
- const idx = e.index ?? -1;
139
- const kind = e.content_block.name ?? 'tool';
140
- const id = e.content_block.id ?? `${kind}:${idx}`;
141
- this.byUseId.set(id, kind);
142
- this.pendingTools.set(idx, { id, kind, toolName: e.content_block.name, inputJson: '' });
143
- }
144
- else if (e.type === 'content_block_delta' && e.delta?.type === 'input_json_delta') {
145
- const tool = this.pendingTools.get(e.index ?? -1);
146
- if (tool)
147
- tool.inputJson += e.delta.partial_json ?? '';
148
- }
149
- else if (e.type === 'content_block_delta' && e.delta?.type === 'text_delta') {
150
- this.clearThinking();
151
- this.cb.onDelta(e.delta.text ?? '');
152
- }
153
- else if (e.type === 'content_block_stop') {
154
- const tool = this.pendingTools.get(e.index ?? -1);
155
- if (!tool)
156
- return;
157
- this.pendingTools.delete(e.index ?? -1);
158
- let input;
159
- try {
160
- input = tool.inputJson ? JSON.parse(tool.inputJson) : undefined;
161
- }
162
- catch { /* malformed mid-stream — show name only */ }
163
- const { name, detail } = summarizeTool(tool.toolName, input);
164
- this.cb.onToolStart({ id: tool.id, kind: tool.kind, name, detail });
165
- }
166
- }
167
- /** Claude emits `tool_result` blocks inside a follow-up `user` message after the tool runs. */
168
- handleToolResults(ev) {
169
- for (const block of ev.message?.content ?? []) {
170
- if (block.type !== 'tool_result' || !block.tool_use_id)
171
- continue;
172
- if (!this.byUseId.has(block.tool_use_id))
173
- continue;
174
- this.byUseId.delete(block.tool_use_id);
175
- this.cb.onToolEnd(block.tool_use_id, extractResultText(block.content));
176
- }
177
- }
178
- }
179
- function extractResultText(content) {
180
- if (typeof content === 'string')
181
- return content.trim() || undefined;
182
- if (!Array.isArray(content))
183
- return undefined;
184
- const text = content.filter(b => b?.type === 'text').map(b => b.text ?? '').join('\n').trim();
185
- return text || undefined;
186
- }
187
- function summarizeTool(name, input) {
188
- const display = (name ?? 'Tool')[0].toUpperCase() + (name ?? 'Tool').slice(1);
189
- if (!input)
190
- return { name: display };
191
- const path = (input.file_path ?? input.path);
192
- const cmd = input.command;
193
- const pattern = input.pattern;
194
- switch (name) {
195
- case 'Bash': return { name: 'Bash', detail: cmd };
196
- case 'Edit':
197
- case 'Write':
198
- case 'NotebookEdit': return { name: display, detail: path };
199
- case 'Read': return { name: 'Read', detail: path };
200
- case 'Grep':
201
- case 'Glob': return { name: display, detail: pattern };
202
- case 'WebFetch': return { name: 'WebFetch', detail: input.url };
203
- case 'WebSearch': return { name: 'WebSearch', detail: input.query };
204
- case 'Task': return { name: 'Task', detail: (input.description ?? input.subagent_type) };
205
- default: return { name: display };
206
- }
207
- }