@agentdeck/bridge 0.1.0 → 0.2.1

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 (384) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/claude-code.d.ts +11 -27
  3. package/dist/adapters/claude-code.d.ts.map +1 -1
  4. package/dist/adapters/claude-code.js +29 -123
  5. package/dist/adapters/claude-code.js.map +1 -1
  6. package/dist/adapters/codex-cli.d.ts +21 -0
  7. package/dist/adapters/codex-cli.d.ts.map +1 -0
  8. package/dist/adapters/codex-cli.js +53 -0
  9. package/dist/adapters/codex-cli.js.map +1 -0
  10. package/dist/adapters/index.d.ts +4 -0
  11. package/dist/adapters/index.d.ts.map +1 -1
  12. package/dist/adapters/index.js +13 -0
  13. package/dist/adapters/index.js.map +1 -1
  14. package/dist/adapters/monitor.d.ts +34 -0
  15. package/dist/adapters/monitor.d.ts.map +1 -0
  16. package/dist/adapters/monitor.js +86 -0
  17. package/dist/adapters/monitor.js.map +1 -0
  18. package/dist/adapters/openclaw.d.ts +36 -2
  19. package/dist/adapters/openclaw.d.ts.map +1 -1
  20. package/dist/adapters/openclaw.js +329 -59
  21. package/dist/adapters/openclaw.js.map +1 -1
  22. package/dist/adapters/opencode-adapter.d.ts +48 -0
  23. package/dist/adapters/opencode-adapter.d.ts.map +1 -0
  24. package/dist/adapters/opencode-adapter.js +345 -0
  25. package/dist/adapters/opencode-adapter.js.map +1 -0
  26. package/dist/adapters/pty-adapter.d.ts +56 -0
  27. package/dist/adapters/pty-adapter.d.ts.map +1 -0
  28. package/dist/adapters/pty-adapter.js +143 -0
  29. package/dist/adapters/pty-adapter.js.map +1 -0
  30. package/dist/adb-reverse.d.ts +25 -0
  31. package/dist/adb-reverse.d.ts.map +1 -0
  32. package/dist/adb-reverse.js +129 -0
  33. package/dist/adb-reverse.js.map +1 -0
  34. package/dist/antigravity-local.d.ts +7 -0
  35. package/dist/antigravity-local.d.ts.map +1 -0
  36. package/dist/antigravity-local.js +153 -0
  37. package/dist/antigravity-local.js.map +1 -0
  38. package/dist/apme/adapters/claude-hook.d.ts +15 -0
  39. package/dist/apme/adapters/claude-hook.d.ts.map +1 -0
  40. package/dist/apme/adapters/claude-hook.js +85 -0
  41. package/dist/apme/adapters/claude-hook.js.map +1 -0
  42. package/dist/apme/adapters/claude-pty.d.ts +28 -0
  43. package/dist/apme/adapters/claude-pty.d.ts.map +1 -0
  44. package/dist/apme/adapters/claude-pty.js +88 -0
  45. package/dist/apme/adapters/claude-pty.js.map +1 -0
  46. package/dist/apme/adapters/timeline.d.ts +16 -0
  47. package/dist/apme/adapters/timeline.d.ts.map +1 -0
  48. package/dist/apme/adapters/timeline.js +66 -0
  49. package/dist/apme/adapters/timeline.js.map +1 -0
  50. package/dist/apme/classifier.d.ts +53 -0
  51. package/dist/apme/classifier.d.ts.map +1 -0
  52. package/dist/apme/classifier.js +266 -0
  53. package/dist/apme/classifier.js.map +1 -0
  54. package/dist/apme/claude-transcript-reader.d.ts +38 -0
  55. package/dist/apme/claude-transcript-reader.d.ts.map +1 -0
  56. package/dist/apme/claude-transcript-reader.js +127 -0
  57. package/dist/apme/claude-transcript-reader.js.map +1 -0
  58. package/dist/apme/collector.d.ts +108 -0
  59. package/dist/apme/collector.d.ts.map +1 -0
  60. package/dist/apme/collector.js +659 -0
  61. package/dist/apme/collector.js.map +1 -0
  62. package/dist/apme/dashboard-html.d.ts +9 -0
  63. package/dist/apme/dashboard-html.d.ts.map +1 -0
  64. package/dist/apme/dashboard-html.js +416 -0
  65. package/dist/apme/dashboard-html.js.map +1 -0
  66. package/dist/apme/http.d.ts +19 -0
  67. package/dist/apme/http.d.ts.map +1 -0
  68. package/dist/apme/http.js +210 -0
  69. package/dist/apme/http.js.map +1 -0
  70. package/dist/apme/hw-sampler.d.ts +30 -0
  71. package/dist/apme/hw-sampler.d.ts.map +1 -0
  72. package/dist/apme/hw-sampler.js +106 -0
  73. package/dist/apme/hw-sampler.js.map +1 -0
  74. package/dist/apme/index.d.ts +43 -0
  75. package/dist/apme/index.d.ts.map +1 -0
  76. package/dist/apme/index.js +75 -0
  77. package/dist/apme/index.js.map +1 -0
  78. package/dist/apme/outcome.d.ts +48 -0
  79. package/dist/apme/outcome.d.ts.map +1 -0
  80. package/dist/apme/outcome.js +232 -0
  81. package/dist/apme/outcome.js.map +1 -0
  82. package/dist/apme/recommend.d.ts +32 -0
  83. package/dist/apme/recommend.d.ts.map +1 -0
  84. package/dist/apme/recommend.js +41 -0
  85. package/dist/apme/recommend.js.map +1 -0
  86. package/dist/apme/runner.d.ts +94 -0
  87. package/dist/apme/runner.d.ts.map +1 -0
  88. package/dist/apme/runner.js +812 -0
  89. package/dist/apme/runner.js.map +1 -0
  90. package/dist/apme/settings.d.ts +65 -0
  91. package/dist/apme/settings.d.ts.map +1 -0
  92. package/dist/apme/settings.js +97 -0
  93. package/dist/apme/settings.js.map +1 -0
  94. package/dist/apme/store.d.ts +99 -0
  95. package/dist/apme/store.d.ts.map +1 -0
  96. package/dist/apme/store.js +911 -0
  97. package/dist/apme/store.js.map +1 -0
  98. package/dist/apme/tuner.d.ts +64 -0
  99. package/dist/apme/tuner.d.ts.map +1 -0
  100. package/dist/apme/tuner.js +297 -0
  101. package/dist/apme/tuner.js.map +1 -0
  102. package/dist/apme/types.d.ts +11 -0
  103. package/dist/apme/types.d.ts.map +1 -0
  104. package/dist/apme/types.js +10 -0
  105. package/dist/apme/types.js.map +1 -0
  106. package/dist/auth.d.ts +1 -1
  107. package/dist/auth.d.ts.map +1 -1
  108. package/dist/auth.js +15 -5
  109. package/dist/auth.js.map +1 -1
  110. package/dist/bridge-core.d.ts +162 -0
  111. package/dist/bridge-core.d.ts.map +1 -0
  112. package/dist/bridge-core.js +627 -0
  113. package/dist/bridge-core.js.map +1 -0
  114. package/dist/check-deps.d.ts +3 -1
  115. package/dist/check-deps.d.ts.map +1 -1
  116. package/dist/check-deps.js +34 -13
  117. package/dist/check-deps.js.map +1 -1
  118. package/dist/cli.d.ts +3 -0
  119. package/dist/cli.d.ts.map +1 -0
  120. package/dist/cli.js +1235 -0
  121. package/dist/cli.js.map +1 -0
  122. package/dist/codex-auth.d.ts +10 -0
  123. package/dist/codex-auth.d.ts.map +1 -0
  124. package/dist/codex-auth.js +63 -0
  125. package/dist/codex-auth.js.map +1 -0
  126. package/dist/codex-output-parser.d.ts +21 -0
  127. package/dist/codex-output-parser.d.ts.map +1 -0
  128. package/dist/codex-output-parser.js +253 -0
  129. package/dist/codex-output-parser.js.map +1 -0
  130. package/dist/d200h/hid-protocol.d.ts +62 -0
  131. package/dist/d200h/hid-protocol.d.ts.map +1 -0
  132. package/dist/d200h/hid-protocol.js +124 -0
  133. package/dist/d200h/hid-protocol.js.map +1 -0
  134. package/dist/d200h/image-renderer.d.ts +36 -0
  135. package/dist/d200h/image-renderer.d.ts.map +1 -0
  136. package/dist/d200h/image-renderer.js +487 -0
  137. package/dist/d200h/image-renderer.js.map +1 -0
  138. package/dist/daemon-server.d.ts +19 -0
  139. package/dist/daemon-server.d.ts.map +1 -0
  140. package/dist/daemon-server.js +1168 -0
  141. package/dist/daemon-server.js.map +1 -0
  142. package/dist/daemon-ws-client.d.ts +82 -0
  143. package/dist/daemon-ws-client.d.ts.map +1 -0
  144. package/dist/daemon-ws-client.js +179 -0
  145. package/dist/daemon-ws-client.js.map +1 -0
  146. package/dist/daemon.d.ts +10 -0
  147. package/dist/daemon.d.ts.map +1 -0
  148. package/dist/daemon.js +56 -0
  149. package/dist/daemon.js.map +1 -0
  150. package/dist/display-monitor.d.ts +25 -0
  151. package/dist/display-monitor.d.ts.map +1 -0
  152. package/dist/display-monitor.js +143 -0
  153. package/dist/display-monitor.js.map +1 -0
  154. package/dist/esp32-serial.d.ts +112 -0
  155. package/dist/esp32-serial.d.ts.map +1 -0
  156. package/dist/esp32-serial.js +502 -0
  157. package/dist/esp32-serial.js.map +1 -0
  158. package/dist/gateway-probe.d.ts +12 -0
  159. package/dist/gateway-probe.d.ts.map +1 -0
  160. package/dist/gateway-probe.js +39 -0
  161. package/dist/gateway-probe.js.map +1 -0
  162. package/dist/hook-migration.d.ts +2 -0
  163. package/dist/hook-migration.d.ts.map +1 -0
  164. package/dist/hook-migration.js +104 -0
  165. package/dist/hook-migration.js.map +1 -0
  166. package/dist/hook-server.d.ts +29 -0
  167. package/dist/hook-server.d.ts.map +1 -1
  168. package/dist/hook-server.js +366 -8
  169. package/dist/hook-server.js.map +1 -1
  170. package/dist/index.d.ts +20 -2
  171. package/dist/index.d.ts.map +1 -1
  172. package/dist/index.js +1304 -562
  173. package/dist/index.js.map +1 -1
  174. package/dist/log-stream.d.ts +26 -0
  175. package/dist/log-stream.d.ts.map +1 -0
  176. package/dist/log-stream.js +111 -0
  177. package/dist/log-stream.js.map +1 -0
  178. package/dist/logger.d.ts +9 -2
  179. package/dist/logger.d.ts.map +1 -1
  180. package/dist/logger.js +36 -5
  181. package/dist/logger.js.map +1 -1
  182. package/dist/mdns.d.ts +15 -1
  183. package/dist/mdns.d.ts.map +1 -1
  184. package/dist/mdns.js +143 -28
  185. package/dist/mdns.js.map +1 -1
  186. package/dist/mlx-probe.d.ts +14 -0
  187. package/dist/mlx-probe.d.ts.map +1 -0
  188. package/dist/mlx-probe.js +44 -0
  189. package/dist/mlx-probe.js.map +1 -0
  190. package/dist/model-catalog.d.ts +5 -3
  191. package/dist/model-catalog.d.ts.map +1 -1
  192. package/dist/model-catalog.js +45 -29
  193. package/dist/model-catalog.js.map +1 -1
  194. package/dist/modules/adb-module.d.ts +14 -0
  195. package/dist/modules/adb-module.d.ts.map +1 -0
  196. package/dist/modules/adb-module.js +36 -0
  197. package/dist/modules/adb-module.js.map +1 -0
  198. package/dist/modules/d200h-module.d.ts +43 -0
  199. package/dist/modules/d200h-module.d.ts.map +1 -0
  200. package/dist/modules/d200h-module.js +360 -0
  201. package/dist/modules/d200h-module.js.map +1 -0
  202. package/dist/modules/d200h-renderer.d.ts +7 -0
  203. package/dist/modules/d200h-renderer.d.ts.map +1 -0
  204. package/dist/modules/d200h-renderer.js +321 -0
  205. package/dist/modules/d200h-renderer.js.map +1 -0
  206. package/dist/modules/index.d.ts +23 -0
  207. package/dist/modules/index.d.ts.map +1 -0
  208. package/dist/modules/index.js +65 -0
  209. package/dist/modules/index.js.map +1 -0
  210. package/dist/modules/mdns-module.d.ts +12 -0
  211. package/dist/modules/mdns-module.d.ts.map +1 -0
  212. package/dist/modules/mdns-module.js +21 -0
  213. package/dist/modules/mdns-module.js.map +1 -0
  214. package/dist/modules/pixoo-module.d.ts +8 -0
  215. package/dist/modules/pixoo-module.d.ts.map +1 -0
  216. package/dist/modules/pixoo-module.js +24 -0
  217. package/dist/modules/pixoo-module.js.map +1 -0
  218. package/dist/modules/serial-module.d.ts +18 -0
  219. package/dist/modules/serial-module.d.ts.map +1 -0
  220. package/dist/modules/serial-module.js +47 -0
  221. package/dist/modules/serial-module.js.map +1 -0
  222. package/dist/modules/types.d.ts +39 -0
  223. package/dist/modules/types.d.ts.map +1 -0
  224. package/dist/modules/types.js +2 -0
  225. package/dist/modules/types.js.map +1 -0
  226. package/dist/ollama-probe.d.ts +13 -0
  227. package/dist/ollama-probe.d.ts.map +1 -0
  228. package/dist/ollama-probe.js +46 -0
  229. package/dist/ollama-probe.js.map +1 -0
  230. package/dist/opencode-client.d.ts +126 -0
  231. package/dist/opencode-client.d.ts.map +1 -0
  232. package/dist/opencode-client.js +181 -0
  233. package/dist/opencode-client.js.map +1 -0
  234. package/dist/output-parser.d.ts +3 -0
  235. package/dist/output-parser.d.ts.map +1 -1
  236. package/dist/output-parser.js +29 -3
  237. package/dist/output-parser.js.map +1 -1
  238. package/dist/passive-observer.d.ts +38 -0
  239. package/dist/passive-observer.d.ts.map +1 -0
  240. package/dist/passive-observer.js +632 -0
  241. package/dist/passive-observer.js.map +1 -0
  242. package/dist/pixoo/pixoo-bridge.d.ts +50 -0
  243. package/dist/pixoo/pixoo-bridge.d.ts.map +1 -0
  244. package/dist/pixoo/pixoo-bridge.js +280 -0
  245. package/dist/pixoo/pixoo-bridge.js.map +1 -0
  246. package/dist/pixoo/pixoo-camera.d.ts +80 -0
  247. package/dist/pixoo/pixoo-camera.d.ts.map +1 -0
  248. package/dist/pixoo/pixoo-camera.js +271 -0
  249. package/dist/pixoo/pixoo-camera.js.map +1 -0
  250. package/dist/pixoo/pixoo-client.d.ts +67 -0
  251. package/dist/pixoo/pixoo-client.d.ts.map +1 -0
  252. package/dist/pixoo/pixoo-client.js +335 -0
  253. package/dist/pixoo/pixoo-client.js.map +1 -0
  254. package/dist/pixoo/pixoo-font.d.ts +24 -0
  255. package/dist/pixoo/pixoo-font.d.ts.map +1 -0
  256. package/dist/pixoo/pixoo-font.js +121 -0
  257. package/dist/pixoo/pixoo-font.js.map +1 -0
  258. package/dist/pixoo/pixoo-renderer.d.ts +32 -0
  259. package/dist/pixoo/pixoo-renderer.d.ts.map +1 -0
  260. package/dist/pixoo/pixoo-renderer.js +696 -0
  261. package/dist/pixoo/pixoo-renderer.js.map +1 -0
  262. package/dist/pixoo/pixoo-settings.d.ts +10 -0
  263. package/dist/pixoo/pixoo-settings.d.ts.map +1 -0
  264. package/dist/pixoo/pixoo-settings.js +47 -0
  265. package/dist/pixoo/pixoo-settings.js.map +1 -0
  266. package/dist/pixoo/pixoo-sprites.d.ts +187 -0
  267. package/dist/pixoo/pixoo-sprites.d.ts.map +1 -0
  268. package/dist/pixoo/pixoo-sprites.js +926 -0
  269. package/dist/pixoo/pixoo-sprites.js.map +1 -0
  270. package/dist/pty-manager.d.ts +1 -1
  271. package/dist/pty-manager.d.ts.map +1 -1
  272. package/dist/pty-manager.js +36 -15
  273. package/dist/pty-manager.js.map +1 -1
  274. package/dist/session-aggregator.d.ts +41 -0
  275. package/dist/session-aggregator.d.ts.map +1 -0
  276. package/dist/session-aggregator.js +76 -0
  277. package/dist/session-aggregator.js.map +1 -0
  278. package/dist/session-focus-relay.d.ts +35 -0
  279. package/dist/session-focus-relay.d.ts.map +1 -0
  280. package/dist/session-focus-relay.js +127 -0
  281. package/dist/session-focus-relay.js.map +1 -0
  282. package/dist/session-registry.d.ts +88 -1
  283. package/dist/session-registry.d.ts.map +1 -1
  284. package/dist/session-registry.js +222 -15
  285. package/dist/session-registry.js.map +1 -1
  286. package/dist/session-timeline-relay.d.ts +27 -0
  287. package/dist/session-timeline-relay.d.ts.map +1 -0
  288. package/dist/session-timeline-relay.js +124 -0
  289. package/dist/session-timeline-relay.js.map +1 -0
  290. package/dist/state-machine.d.ts +1 -0
  291. package/dist/state-machine.d.ts.map +1 -1
  292. package/dist/state-machine.js +14 -0
  293. package/dist/state-machine.js.map +1 -1
  294. package/dist/terminal-status.d.ts +28 -0
  295. package/dist/terminal-status.d.ts.map +1 -0
  296. package/dist/terminal-status.js +152 -0
  297. package/dist/terminal-status.js.map +1 -0
  298. package/dist/timeline-store.d.ts +22 -0
  299. package/dist/timeline-store.d.ts.map +1 -0
  300. package/dist/timeline-store.js +79 -0
  301. package/dist/timeline-store.js.map +1 -0
  302. package/dist/timeline-summarizer.d.ts +14 -0
  303. package/dist/timeline-summarizer.d.ts.map +1 -0
  304. package/dist/timeline-summarizer.js +154 -0
  305. package/dist/timeline-summarizer.js.map +1 -0
  306. package/dist/tts.d.ts +25 -0
  307. package/dist/tts.d.ts.map +1 -0
  308. package/dist/tts.js +99 -0
  309. package/dist/tts.js.map +1 -0
  310. package/dist/tui/ansi.d.ts +92 -0
  311. package/dist/tui/ansi.d.ts.map +1 -0
  312. package/dist/tui/ansi.js +181 -0
  313. package/dist/tui/ansi.js.map +1 -0
  314. package/dist/tui/dashboard.d.ts +35 -0
  315. package/dist/tui/dashboard.d.ts.map +1 -0
  316. package/dist/tui/dashboard.js +436 -0
  317. package/dist/tui/dashboard.js.map +1 -0
  318. package/dist/tui/gauge.d.ts +15 -0
  319. package/dist/tui/gauge.d.ts.map +1 -0
  320. package/dist/tui/gauge.js +82 -0
  321. package/dist/tui/gauge.js.map +1 -0
  322. package/dist/tui/renderer.d.ts +15 -0
  323. package/dist/tui/renderer.d.ts.map +1 -0
  324. package/dist/tui/renderer.js +758 -0
  325. package/dist/tui/renderer.js.map +1 -0
  326. package/dist/tui/screen.d.ts +27 -0
  327. package/dist/tui/screen.d.ts.map +1 -0
  328. package/dist/tui/screen.js +109 -0
  329. package/dist/tui/screen.js.map +1 -0
  330. package/dist/tui/terrarium.d.ts +96 -0
  331. package/dist/tui/terrarium.d.ts.map +1 -0
  332. package/dist/tui/terrarium.js +912 -0
  333. package/dist/tui/terrarium.js.map +1 -0
  334. package/dist/types.d.ts +4 -2
  335. package/dist/types.d.ts.map +1 -1
  336. package/dist/types.js +3 -2
  337. package/dist/types.js.map +1 -1
  338. package/dist/usage-api.d.ts +9 -2
  339. package/dist/usage-api.d.ts.map +1 -1
  340. package/dist/usage-api.js +224 -42
  341. package/dist/usage-api.js.map +1 -1
  342. package/dist/usage-event.d.ts +12 -0
  343. package/dist/usage-event.d.ts.map +1 -0
  344. package/dist/usage-event.js +78 -0
  345. package/dist/usage-event.js.map +1 -0
  346. package/dist/usage-tracker.d.ts +4 -0
  347. package/dist/usage-tracker.d.ts.map +1 -1
  348. package/dist/usage-tracker.js +17 -0
  349. package/dist/usage-tracker.js.map +1 -1
  350. package/dist/utility-proxy.d.ts +30 -0
  351. package/dist/utility-proxy.d.ts.map +1 -0
  352. package/dist/utility-proxy.js +217 -0
  353. package/dist/utility-proxy.js.map +1 -0
  354. package/dist/utils/project-name.d.ts +22 -0
  355. package/dist/utils/project-name.d.ts.map +1 -0
  356. package/dist/utils/project-name.js +72 -0
  357. package/dist/utils/project-name.js.map +1 -0
  358. package/dist/version-check.d.ts +26 -0
  359. package/dist/version-check.d.ts.map +1 -0
  360. package/dist/version-check.js +225 -0
  361. package/dist/version-check.js.map +1 -0
  362. package/dist/voice-assistant.d.ts +63 -0
  363. package/dist/voice-assistant.d.ts.map +1 -0
  364. package/dist/voice-assistant.js +406 -0
  365. package/dist/voice-assistant.js.map +1 -0
  366. package/dist/voice.d.ts +2 -0
  367. package/dist/voice.d.ts.map +1 -1
  368. package/dist/voice.js +26 -1
  369. package/dist/voice.js.map +1 -1
  370. package/dist/wake-word.d.ts +35 -0
  371. package/dist/wake-word.d.ts.map +1 -0
  372. package/dist/wake-word.js +225 -0
  373. package/dist/wake-word.js.map +1 -0
  374. package/dist/whisper-server-manager.d.ts +1 -1
  375. package/dist/whisper-server-manager.js +1 -1
  376. package/dist/wifi-config.d.ts +24 -0
  377. package/dist/wifi-config.d.ts.map +1 -0
  378. package/dist/wifi-config.js +84 -0
  379. package/dist/wifi-config.js.map +1 -0
  380. package/dist/ws-server.d.ts +12 -0
  381. package/dist/ws-server.d.ts.map +1 -1
  382. package/dist/ws-server.js +81 -3
  383. package/dist/ws-server.js.map +1 -1
  384. package/package.json +21 -13
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Claude Code PTY parser → TelemetrySpan adapter.
3
+ *
4
+ * Two distinct emission points:
5
+ *
6
+ * 1. **Parser events** (`tool_start` / `tool_end` / `spinner_start` / `idle` /
7
+ * `spinner_stop`) — fired by the BridgeCore output parser as terminal text
8
+ * scrolls. We map these to lifecycle spans (`tool_call` / `tool_result`)
9
+ * plus generic `raw_step` for state transitions. This is the **fallback**
10
+ * when Claude Code 2.1+ hooks are flaky (memory: feedback_apme_stop_hook).
11
+ *
12
+ * 2. **Extracted response text** — `spinner_stop` triggers a 500 ms timeout
13
+ * in the bridge, the PTY ringbuffer tail is scanned for the `⏺` marker,
14
+ * and the cleaned text is fed to `claudePtyResponseToSpan` to emit a
15
+ * `turn_response` span.
16
+ *
17
+ * Splitting these into two functions keeps each call site free to do its own
18
+ * timing / extraction work without smuggling sentinels through `data`.
19
+ */
20
+ import { randomUUID } from 'crypto';
21
+ import { spanNameForKind } from '@agentdeck/shared';
22
+ const PARSER_TO_KIND = {
23
+ tool_start: 'tool_call',
24
+ tool_end: 'tool_result',
25
+ spinner_start: 'raw_step',
26
+ spinner_stop: 'raw_step',
27
+ idle: 'raw_step',
28
+ };
29
+ export function claudePtyParserEventToSpans(ctx, event, data = {}) {
30
+ const kind = PARSER_TO_KIND[event];
31
+ if (!kind)
32
+ return [];
33
+ const ts = Date.now();
34
+ const toolName = typeof data.tool_name === 'string' ? data.tool_name : undefined;
35
+ const base = {
36
+ 'agentdeck.agent_type': ctx.agentType,
37
+ ...(ctx.cwd ? { 'agentdeck.cwd': ctx.cwd } : {}),
38
+ };
39
+ let attributes;
40
+ if (kind === 'tool_call' || kind === 'tool_result') {
41
+ attributes = {
42
+ ...base,
43
+ ...(toolName ? { 'gen_ai.tool.name': toolName, 'agentdeck.tool_name': toolName } : {}),
44
+ 'agentdeck.raw_payload': data,
45
+ // Map the parser event back to the legacy hook event name so the
46
+ // collector's existing dispatch (PreToolUse/PostToolUse) keeps firing.
47
+ 'agentdeck.raw_event': event === 'tool_start' ? 'PreToolUse' : 'PostToolUse',
48
+ };
49
+ }
50
+ else {
51
+ attributes = {
52
+ ...base,
53
+ 'agentdeck.raw_event': event === 'spinner_start' ? 'processing' : event,
54
+ 'agentdeck.raw_payload': data,
55
+ };
56
+ }
57
+ return [{
58
+ traceId: ctx.traceId,
59
+ spanId: randomUUID(),
60
+ parentSpanId: ctx.activeTurnId,
61
+ name: spanNameForKind(kind),
62
+ kind,
63
+ ts,
64
+ attributes,
65
+ }];
66
+ }
67
+ /** Extracted PTY response text → `turn_response` span. The caller is responsible
68
+ * for the actual extraction (PTY ringbuffer + `⏺` marker + cleaning) since
69
+ * that logic is intertwined with `BridgeCore` timing. */
70
+ export function claudePtyResponseToSpan(ctx, response, options = {}) {
71
+ if (!response || response.trim().length < 2)
72
+ return null;
73
+ return {
74
+ traceId: ctx.traceId,
75
+ spanId: randomUUID(),
76
+ parentSpanId: ctx.activeTurnId,
77
+ name: spanNameForKind('turn_response'),
78
+ kind: 'turn_response',
79
+ ts: Date.now(),
80
+ attributes: {
81
+ 'agentdeck.agent_type': ctx.agentType,
82
+ ...(ctx.cwd ? { 'agentdeck.cwd': ctx.cwd } : {}),
83
+ 'agentdeck.response_text': response,
84
+ ...(options.fallbackToLastClosed ? { 'agentdeck.fallback_to_last_closed': true } : {}),
85
+ },
86
+ };
87
+ }
88
+ //# sourceMappingURL=claude-pty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-pty.js","sourceRoot":"","sources":["../../../src/apme/adapters/claude-pty.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Timeline event → TelemetrySpan adapter.
3
+ *
4
+ * Used by OpenClaw and OpenCode adapters which emit `chat_start` /
5
+ * `chat_response` / `chat_end` / `tool_request` / `tool_resolved` timeline
6
+ * entries (`shared/src/timeline.ts`). Translates each into the canonical
7
+ * AgentDeck span set so the collector sees one consistent ingestion stream.
8
+ *
9
+ * `chat_end` carries the assistant response in `entry.detail` only as a
10
+ * fallback path for sources that didn't emit `chat_response` mid-stream.
11
+ * That case maps to `turn_response` with `fallbackToLastClosed: true` so the
12
+ * collector applies it via `setLastClosedTurnResponse`.
13
+ */
14
+ import type { AdapterContext, TelemetrySpan, TimelineEntry } from '@agentdeck/shared';
15
+ export declare function timelineEntryToSpans(ctx: AdapterContext, entry: TimelineEntry): TelemetrySpan[];
16
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/apme/adapters/timeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,aAAa,EACd,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,aAAa,GACnB,aAAa,EAAE,CAmDjB"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Timeline event → TelemetrySpan adapter.
3
+ *
4
+ * Used by OpenClaw and OpenCode adapters which emit `chat_start` /
5
+ * `chat_response` / `chat_end` / `tool_request` / `tool_resolved` timeline
6
+ * entries (`shared/src/timeline.ts`). Translates each into the canonical
7
+ * AgentDeck span set so the collector sees one consistent ingestion stream.
8
+ *
9
+ * `chat_end` carries the assistant response in `entry.detail` only as a
10
+ * fallback path for sources that didn't emit `chat_response` mid-stream.
11
+ * That case maps to `turn_response` with `fallbackToLastClosed: true` so the
12
+ * collector applies it via `setLastClosedTurnResponse`.
13
+ */
14
+ import { randomUUID } from 'crypto';
15
+ import { spanNameForKind } from '@agentdeck/shared';
16
+ export function timelineEntryToSpans(ctx, entry) {
17
+ const ts = entry.ts ?? Date.now();
18
+ const baseAttrs = {
19
+ 'agentdeck.agent_type': ctx.agentType,
20
+ ...(ctx.cwd ? { 'agentdeck.cwd': ctx.cwd } : {}),
21
+ };
22
+ const make = (kind, attributes) => ({
23
+ traceId: ctx.traceId,
24
+ spanId: randomUUID(),
25
+ parentSpanId: ctx.activeTurnId,
26
+ name: spanNameForKind(kind),
27
+ kind,
28
+ ts,
29
+ attributes: { ...baseAttrs, ...attributes },
30
+ });
31
+ switch (entry.type) {
32
+ case 'chat_start': {
33
+ const prompt = entry.detail || entry.raw || '';
34
+ return [make('turn_start', { 'agentdeck.prompt_text': prompt })];
35
+ }
36
+ case 'chat_response': {
37
+ const response = entry.detail || entry.raw || '';
38
+ if (response.length <= 2)
39
+ return [];
40
+ return [make('turn_response', { 'agentdeck.response_text': response })];
41
+ }
42
+ case 'chat_end': {
43
+ const response = entry.detail || '';
44
+ if (response.length <= 2)
45
+ return [];
46
+ return [make('turn_response', {
47
+ 'agentdeck.response_text': response,
48
+ 'agentdeck.fallback_to_last_closed': true,
49
+ })];
50
+ }
51
+ case 'tool_request': {
52
+ // Some sources pack the tool name as the first whitespace-delimited token
53
+ // in `raw`; when missing, the collector falls back to the literal 'tool'.
54
+ const toolName = entry.raw?.split(' ')[0] ?? 'tool';
55
+ return [make('tool_call', {
56
+ 'gen_ai.tool.name': toolName,
57
+ 'agentdeck.tool_name': toolName,
58
+ })];
59
+ }
60
+ case 'tool_resolved':
61
+ return [make('tool_result', {})];
62
+ default:
63
+ return [];
64
+ }
65
+ }
66
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../src/apme/adapters/timeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAOpC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAClC,GAAmB,EACnB,KAAoB;IAEpB,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,SAAS,GAAwB;QACrC,sBAAsB,EAAE,GAAG,CAAC,SAAS;QACrC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjD,CAAC;IACF,MAAM,IAAI,GAAG,CACX,IAA2B,EAC3B,UAA+B,EAChB,EAAE,CAAC,CAAC;QACnB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM,EAAE,UAAU,EAAE;QACpB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC;QAC3B,IAAI;QACJ,EAAE;QACF,UAAU,EAAE,EAAE,GAAG,SAAS,EAAE,GAAG,UAAU,EAAE;KAC5C,CAAC,CAAC;IAEH,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;YACjD,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,yBAAyB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC1E,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;YACpC,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE;oBAC5B,yBAAyB,EAAE,QAAQ;oBACnC,mCAAmC,EAAE,IAAI;iBAC1C,CAAC,CAAC,CAAC;QACN,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,0EAA0E;YAC1E,0EAA0E;YAC1E,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE;oBACxB,kBAAkB,EAAE,QAAQ;oBAC5B,qBAAqB,EAAE,QAAQ;iBAChC,CAAC,CAAC,CAAC;QACN,CAAC;QACD,KAAK,eAAe;YAClB,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC;QACnC;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * APME Task Classifier — 2-layer: signal vector + derived label.
3
+ *
4
+ * Layer 1: `computeSignals()` aggregates steps into a fixed-shape TaskSignals
5
+ * object that is stored as JSON on `runs.task_signals`. The signals are
6
+ * agent-agnostic so the same shape works for Claude Code, Codex, OpenCode,
7
+ * and OpenClaw runs alike.
8
+ *
9
+ * Layer 2: `classify()` maps a TaskSignals object to a task_category label
10
+ * via a priority-ordered rule table. Users can override with `agentdeck apme
11
+ * tag <id> <category>`. The taxonomy is intentionally simple — the raw
12
+ * signals are always available for re-classification with a smarter model.
13
+ */
14
+ import type { ApmeStore } from './store.js';
15
+ export interface TaskSignals {
16
+ toolCounts: Record<string, number>;
17
+ dominantTool: string | null;
18
+ totalToolCalls: number;
19
+ turnCount: number;
20
+ sessionDurationSec: number;
21
+ promptLengthChars: number;
22
+ planModeUsed: boolean;
23
+ permissionRequests: number;
24
+ diffReviews: number;
25
+ filesCreated: number;
26
+ filesModified: number;
27
+ testCommandsRun: number;
28
+ webSearches: number;
29
+ agentDelegations: number;
30
+ isAutomated?: boolean;
31
+ ocToolNames?: string[];
32
+ }
33
+ export type TaskCategory = 'planning' | 'research' | 'coding' | 'debugging' | 'refactoring' | 'review' | 'ops' | 'conversation' | 'multi_agent' | 'unknown';
34
+ export declare const TASK_CATEGORIES: readonly TaskCategory[];
35
+ export declare function computeSignals(store: ApmeStore, runId: string): TaskSignals;
36
+ export declare function classify(signals: TaskSignals): TaskCategory;
37
+ /** Compute signals + classify in one call, return both. */
38
+ export declare function classifyRun(store: ApmeStore, runId: string): {
39
+ signals: TaskSignals;
40
+ category: TaskCategory;
41
+ };
42
+ /**
43
+ * Classify a run using the local MLX server. Falls back to rule-based
44
+ * classification if MLX is unavailable. Cost: $0 (local inference).
45
+ */
46
+ export declare function classifyWithLlm(taskPrompt: string, signals: TaskSignals): Promise<TaskCategory>;
47
+ /** Classify with LLM if rule-based gives unknown, otherwise use rules. */
48
+ export declare function classifyRunSmart(store: ApmeStore, runId: string): Promise<{
49
+ signals: TaskSignals;
50
+ category: TaskCategory;
51
+ source: 'rule' | 'llm';
52
+ }>;
53
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../../src/apme/classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IAEvB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAE1B,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IAEzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAID,MAAM,MAAM,YAAY,GACpB,UAAU,GACV,UAAU,GACV,QAAQ,GACR,WAAW,GACX,aAAa,GACb,QAAQ,GACR,KAAK,GACL,cAAc,GACd,aAAa,GACb,SAAS,CAAC;AAEd,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAGlD,CAAC;AAMF,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAmF3E;AAwDD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CAK3D;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,CAI7G;AAoBD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CAsDvB;AAED,0EAA0E;AAC1E,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,CAAC,CAenF"}
@@ -0,0 +1,266 @@
1
+ /**
2
+ * APME Task Classifier — 2-layer: signal vector + derived label.
3
+ *
4
+ * Layer 1: `computeSignals()` aggregates steps into a fixed-shape TaskSignals
5
+ * object that is stored as JSON on `runs.task_signals`. The signals are
6
+ * agent-agnostic so the same shape works for Claude Code, Codex, OpenCode,
7
+ * and OpenClaw runs alike.
8
+ *
9
+ * Layer 2: `classify()` maps a TaskSignals object to a task_category label
10
+ * via a priority-ordered rule table. Users can override with `agentdeck apme
11
+ * tag <id> <category>`. The taxonomy is intentionally simple — the raw
12
+ * signals are always available for re-classification with a smarter model.
13
+ */
14
+ export const TASK_CATEGORIES = [
15
+ 'planning', 'research', 'coding', 'debugging', 'refactoring',
16
+ 'review', 'ops', 'conversation', 'multi_agent', 'unknown',
17
+ ];
18
+ // ─── Signal computation ─────────────────────────────────────────────────────
19
+ const TEST_PATTERNS = /\b(test|vitest|jest|pytest|cargo\s+test|go\s+test|xcodebuild\s+test|gradlew\s+test|pnpm\s+test|npm\s+test)\b/i;
20
+ export function computeSignals(store, runId) {
21
+ const run = store.getRun(runId);
22
+ const steps = store.listSteps(runId);
23
+ const toolCounts = {};
24
+ let turnCount = 0;
25
+ let planModeUsed = false;
26
+ let permissionRequests = 0;
27
+ let diffReviews = 0;
28
+ let filesCreated = 0;
29
+ let filesModified = 0;
30
+ let testCommandsRun = 0;
31
+ let webSearches = 0;
32
+ let agentDelegations = 0;
33
+ let isAutomated;
34
+ const ocToolNames = new Set();
35
+ for (const step of steps) {
36
+ if (step.kind === 'PreToolUse' && step.toolName) {
37
+ toolCounts[step.toolName] = (toolCounts[step.toolName] ?? 0) + 1;
38
+ if (step.toolName === 'Write')
39
+ filesCreated++;
40
+ if (step.toolName === 'Edit')
41
+ filesModified++;
42
+ if (step.toolName === 'WebSearch' || step.toolName === 'WebFetch')
43
+ webSearches++;
44
+ if (step.toolName === 'Agent')
45
+ agentDelegations++;
46
+ if (step.toolName === 'Bash') {
47
+ try {
48
+ const payload = JSON.parse(step.payload);
49
+ const cmd = typeof payload.command === 'string' ? payload.command : '';
50
+ if (TEST_PATTERNS.test(cmd))
51
+ testCommandsRun++;
52
+ }
53
+ catch { /* ignore */ }
54
+ }
55
+ }
56
+ if (step.kind === 'UserPromptSubmit')
57
+ turnCount++;
58
+ // State-based signals from step payloads
59
+ try {
60
+ const payload = JSON.parse(step.payload);
61
+ if (payload.mode === 'plan' || step.kind === 'mode_change') {
62
+ if (typeof payload.mode === 'string' && payload.mode === 'plan')
63
+ planModeUsed = true;
64
+ }
65
+ if (step.kind === 'permission_prompt')
66
+ permissionRequests++;
67
+ if (step.kind === 'diff_prompt')
68
+ diffReviews++;
69
+ // OpenClaw signals
70
+ if (typeof payload.chatIsAutomated === 'boolean')
71
+ isAutomated = payload.chatIsAutomated;
72
+ if (Array.isArray(payload.chatToolNames)) {
73
+ for (const t of payload.chatToolNames) {
74
+ if (typeof t === 'string')
75
+ ocToolNames.add(t);
76
+ }
77
+ }
78
+ }
79
+ catch { /* ignore */ }
80
+ }
81
+ const totalToolCalls = Object.values(toolCounts).reduce((a, b) => a + b, 0);
82
+ let dominantTool = null;
83
+ let maxCount = 0;
84
+ for (const [tool, count] of Object.entries(toolCounts)) {
85
+ if (count > maxCount) {
86
+ maxCount = count;
87
+ dominantTool = tool;
88
+ }
89
+ }
90
+ const sessionDurationSec = run?.endedAt && run?.startedAt
91
+ ? Math.round((run.endedAt - run.startedAt) / 1000)
92
+ : 0;
93
+ return {
94
+ toolCounts,
95
+ dominantTool,
96
+ totalToolCalls,
97
+ turnCount,
98
+ sessionDurationSec,
99
+ promptLengthChars: run?.taskPrompt?.length ?? 0,
100
+ planModeUsed,
101
+ permissionRequests,
102
+ diffReviews,
103
+ filesCreated,
104
+ filesModified,
105
+ testCommandsRun,
106
+ webSearches,
107
+ agentDelegations,
108
+ isAutomated: isAutomated ?? undefined,
109
+ ocToolNames: ocToolNames.size > 0 ? [...ocToolNames] : undefined,
110
+ };
111
+ }
112
+ // ─── Rule-based classifier ──────────────────────────────────────────────────
113
+ function toolPct(signals, ...tools) {
114
+ if (signals.totalToolCalls === 0)
115
+ return 0;
116
+ const sum = tools.reduce((a, t) => a + (signals.toolCounts[t] ?? 0), 0);
117
+ return sum / signals.totalToolCalls;
118
+ }
119
+ const RULES = [
120
+ // Highest-signal categories first — these have unambiguous markers.
121
+ {
122
+ category: 'multi_agent',
123
+ test: (s) => s.agentDelegations >= 2,
124
+ },
125
+ {
126
+ category: 'planning',
127
+ test: (s) => s.planModeUsed,
128
+ },
129
+ {
130
+ category: 'conversation',
131
+ test: (s) => s.totalToolCalls <= 2 && s.sessionDurationSec < 120,
132
+ },
133
+ {
134
+ category: 'planning',
135
+ test: (s) => s.turnCount >= 1 && s.turnCount <= 3 && s.totalToolCalls <= 5 && s.filesModified === 0 && s.filesCreated === 0,
136
+ },
137
+ {
138
+ category: 'research',
139
+ test: (s) => s.webSearches > 0 || (toolPct(s, 'Grep', 'Glob') > 0.4 && s.filesModified === 0 && s.filesCreated === 0),
140
+ },
141
+ {
142
+ category: 'debugging',
143
+ test: (s) => s.testCommandsRun >= 1 && (s.filesModified > 0 || s.filesCreated > 0) && toolPct(s, 'Bash') > 0.2,
144
+ },
145
+ {
146
+ category: 'refactoring',
147
+ test: (s) => toolPct(s, 'Edit') > 0.5 && s.filesCreated === 0 && s.filesModified >= 3,
148
+ },
149
+ {
150
+ category: 'coding',
151
+ test: (s) => toolPct(s, 'Edit', 'Write') > 0.3 && (s.filesModified >= 1 || s.filesCreated >= 1),
152
+ },
153
+ {
154
+ category: 'review',
155
+ test: (s) => toolPct(s, 'Read') > 0.5 && s.totalToolCalls >= 5 && s.filesModified <= 1 && s.filesCreated === 0,
156
+ },
157
+ {
158
+ category: 'ops',
159
+ test: (s) => toolPct(s, 'Bash') > 0.5 && toolPct(s, 'Edit', 'Write') < 0.2,
160
+ },
161
+ ];
162
+ export function classify(signals) {
163
+ for (const rule of RULES) {
164
+ if (rule.test(signals))
165
+ return rule.category;
166
+ }
167
+ return 'unknown';
168
+ }
169
+ /** Compute signals + classify in one call, return both. */
170
+ export function classifyRun(store, runId) {
171
+ const signals = computeSignals(store, runId);
172
+ const category = classify(signals);
173
+ return { signals, category };
174
+ }
175
+ // ──�� LLM-based classification (local MLX, cost-free) ──────────────────────
176
+ const LLM_CLASSIFY_PROMPT = `You are a task classifier for coding agent sessions.
177
+ Given the user's prompt and tool usage summary, classify this task into exactly ONE category.
178
+
179
+ Categories:
180
+ - planning: architecture design, plan mode, thinking about approach
181
+ - research: searching code, reading docs, web search, investigating
182
+ - coding: writing/editing code, creating files, implementing features
183
+ - debugging: fixing bugs, running tests, investigating failures
184
+ - refactoring: restructuring existing code without changing behavior
185
+ - review: reading code for understanding, code review
186
+ - ops: git operations, deployments, config changes, CI/CD
187
+ - conversation: quick question, chat, no tools used
188
+ - multi_agent: delegating to sub-agents
189
+
190
+ Respond with ONLY the category name, nothing else.`;
191
+ /**
192
+ * Classify a run using the local MLX server. Falls back to rule-based
193
+ * classification if MLX is unavailable. Cost: $0 (local inference).
194
+ */
195
+ export async function classifyWithLlm(taskPrompt, signals) {
196
+ if (!taskPrompt || taskPrompt.trim().length < 5)
197
+ return classify(signals);
198
+ const toolSummary = Object.entries(signals.toolCounts)
199
+ .sort((a, b) => b[1] - a[1])
200
+ .slice(0, 5)
201
+ .map(([t, c]) => `${t}×${c}`)
202
+ .join(', ');
203
+ const userMsg = `Prompt: "${taskPrompt.slice(0, 500)}"
204
+ Tools used: ${toolSummary || 'none'} (${signals.totalToolCalls} total)
205
+ Files modified: ${signals.filesModified}, created: ${signals.filesCreated}
206
+ Duration: ${signals.sessionDurationSec}s, turns: ${signals.turnCount}`;
207
+ try {
208
+ // Auto-detect MLX endpoint + model (same logic as runner.ts callMlx)
209
+ let model = 'default';
210
+ const base = 'http://127.0.0.1:8800';
211
+ for (const path of ['/v1/models', '/models']) {
212
+ const mResp = await fetch(`${base}${path}`, { signal: AbortSignal.timeout(2000) }).catch(() => null);
213
+ if (mResp?.ok) {
214
+ const mJson = await mResp.json();
215
+ const first = mJson.data?.find(m => m.id && !m.id.toLowerCase().includes('nanollava'))?.id;
216
+ if (first) {
217
+ model = first;
218
+ break;
219
+ }
220
+ }
221
+ }
222
+ const resp = await fetch(`${base}/chat/completions`, {
223
+ method: 'POST',
224
+ headers: { 'Content-Type': 'application/json' },
225
+ body: JSON.stringify({
226
+ model,
227
+ messages: [
228
+ { role: 'system', content: LLM_CLASSIFY_PROMPT },
229
+ { role: 'user', content: userMsg },
230
+ ],
231
+ temperature: 0,
232
+ max_tokens: 20,
233
+ }),
234
+ signal: AbortSignal.timeout(15_000),
235
+ });
236
+ if (!resp.ok)
237
+ return classify(signals);
238
+ const json = await resp.json();
239
+ const raw = json.choices?.[0]?.message?.content?.trim().toLowerCase().replace(/[^a-z_]/g, '') ?? '';
240
+ if (TASK_CATEGORIES.includes(raw))
241
+ return raw;
242
+ // Partial match
243
+ const match = TASK_CATEGORIES.find(c => raw.includes(c));
244
+ return match ?? classify(signals);
245
+ }
246
+ catch {
247
+ // MLX not available — fall back to rules
248
+ return classify(signals);
249
+ }
250
+ }
251
+ /** Classify with LLM if rule-based gives unknown, otherwise use rules. */
252
+ export async function classifyRunSmart(store, runId) {
253
+ const signals = computeSignals(store, runId);
254
+ const ruleCategory = classify(signals);
255
+ if (ruleCategory !== 'unknown') {
256
+ return { signals, category: ruleCategory, source: 'rule' };
257
+ }
258
+ // Rule-based gave unknown — try LLM
259
+ const run = store.getRun(runId);
260
+ const prompt = run?.taskPrompt;
261
+ if (!prompt)
262
+ return { signals, category: 'unknown', source: 'rule' };
263
+ const llmCategory = await classifyWithLlm(prompt, signals);
264
+ return { signals, category: llmCategory, source: llmCategory !== 'unknown' ? 'llm' : 'rule' };
265
+ }
266
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/apme/classifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Read the most recent turn's assistant text from a Claude Code transcript
3
+ * JSONL file (path comes from the Stop hook's `transcript_path` field).
4
+ *
5
+ * Why this exists: the `last_assistant_message` field on the Stop hook payload
6
+ * is only ~18% reliable (see DEVELOPMENT_LOG.md note). Pure-tool turns often
7
+ * emit no assistant text, and text-bearing turns sometimes drop the field on
8
+ * the hook boundary. The transcript JSONL is the authoritative source Claude
9
+ * Code itself writes, with one JSON object per line capturing every user /
10
+ * assistant / tool_use / tool_result event.
11
+ *
12
+ * Scope: CLI bridge only. The App Store Swift daemon runs under a sandbox
13
+ * that only grants security-scoped access to `~/.claude/settings.json` — not
14
+ * the per-session `~/.claude/projects/<proj>/<session>.jsonl` files. For that
15
+ * build, Task 2's `response_kind` heuristic (empty text + tool_calls > 0 →
16
+ * `tool_only`) is the fallback.
17
+ */
18
+ export interface LastTurnExcerpt {
19
+ userPrompt: string;
20
+ assistantText: string;
21
+ toolUseCount: number;
22
+ /** Whether the last assistant block(s) contained any `text` content. */
23
+ hasAssistantText: boolean;
24
+ }
25
+ /**
26
+ * Parse the last user→assistant turn from a Claude Code transcript JSONL.
27
+ *
28
+ * Returns `null` on any parse failure (file missing, no user/assistant
29
+ * entries, malformed JSON). Callers treat `null` as "use the other source"
30
+ * — this function never throws.
31
+ *
32
+ * Implementation: read the full file (capped size), walk lines in reverse
33
+ * to find the last `user` role entry, then scan forward collecting the
34
+ * `assistant` entries that follow. `content` on each message is either a
35
+ * string (legacy shape) or an array of blocks with `type: 'text' | 'tool_use'`.
36
+ */
37
+ export declare function readLastTurn(transcriptPath: string): LastTurnExcerpt | null;
38
+ //# sourceMappingURL=claude-transcript-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-transcript-reader.d.ts","sourceRoot":"","sources":["../../src/apme/claude-transcript-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAUD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAsD3E"}