@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
@@ -12,6 +12,11 @@ import type { AgentAdapter, AgentCapabilities, AdapterStartOptions, PluginComman
12
12
  * - Has no terminal (PTY), mode switching, or diff review
13
13
  * - Handles select_option/navigate_option/send_prompt directly via RPC
14
14
  */
15
+ export interface OpenClawAdapterOptions {
16
+ gatewayUrl?: string;
17
+ /** Set false to disable automatic reconnect (daemon manages lifecycle externally) */
18
+ autoReconnect?: boolean;
19
+ }
15
20
  export declare class OpenClawAdapter extends EventEmitter implements AgentAdapter {
16
21
  readonly capabilities: AgentCapabilities;
17
22
  private gatewayUrl;
@@ -23,19 +28,31 @@ export declare class OpenClawAdapter extends EventEmitter implements AgentAdapte
23
28
  private projectName;
24
29
  private alive;
25
30
  private shutdownRequested;
31
+ private externalHttpServer;
26
32
  private diagHandler;
27
33
  private rawDataCallback;
34
+ private autoReconnect;
28
35
  private currentSessionKey;
29
36
  private currentRunId;
30
37
  private pendingApprovalId;
38
+ private chatStarted;
39
+ private chatStartTime;
40
+ private chatToolCount;
41
+ private chatToolNames;
42
+ private lastPrompt;
43
+ private accumulatedResponse;
44
+ private topicExtracted;
45
+ private chatIsAutomated;
31
46
  private deviceIdentity;
32
47
  private deviceAuthToken;
33
48
  /** Reconnect delay (doubles on each failure, max 30s) */
34
49
  private reconnectDelay;
35
50
  private static readonly MAX_RECONNECT_DELAY;
36
51
  private static readonly RPC_TIMEOUT;
37
- constructor(gatewayUrl?: string);
38
- start(options: AdapterStartOptions): Promise<void>;
52
+ constructor(options?: string | OpenClawAdapterOptions);
53
+ start(options: AdapterStartOptions & {
54
+ externalServer?: Server;
55
+ }): Promise<void>;
39
56
  handleCommand(cmd: PluginCommand): boolean;
40
57
  writeInput(data: string): void;
41
58
  isAlive(): boolean;
@@ -53,6 +70,12 @@ export declare class OpenClawAdapter extends EventEmitter implements AgentAdapte
53
70
  private buildDeviceAuth;
54
71
  private connectGateway;
55
72
  private scheduleReconnect;
73
+ /**
74
+ * Typed RPC dispatch. `GatewayMethodMap` correlates the method name with
75
+ * its param shape and result shape (declared in `shared/gateway-protocol.ts`),
76
+ * so misuse — wrong params for a method, or calling an unknown method — is
77
+ * a compile error here rather than a runtime surprise.
78
+ */
56
79
  private rpcCall;
57
80
  private handleGatewayMessage;
58
81
  private handleGatewayEvent;
@@ -65,6 +88,17 @@ export declare class OpenClawAdapter extends EventEmitter implements AgentAdapte
65
88
  private fetchSessions;
66
89
  /** Fetch model catalog via CLI and emit events. Retries once on failure. */
67
90
  private emitModelCatalog;
91
+ /**
92
+ * Extract text from Gateway chat message structure.
93
+ * Format: payload.message = { role, content: [{ type: "text", text: "..." }], timestamp }
94
+ */
95
+ private extractMessageText;
96
+ /** Build tool count summary like "Read(3), Bash(2)" */
97
+ private buildToolSummary;
98
+ /** Emit a timeline event through the adapter event system */
99
+ private emitTimelineEntry;
100
+ /** Emit a timeline upsert (update existing entry with same ts+type, or add new) */
101
+ private emitTimelineUpsert;
68
102
  private emitAdapterEvent;
69
103
  }
70
104
  //# sourceMappingURL=openclaw.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openclaw.d.ts","sourceRoot":"","sources":["../../src/adapters/openclaw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAOjD,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EAEnB,aAAa,EACd,MAAM,aAAa,CAAC;AA8DrB;;;;;;;;;;GAUG;AACH,qBAAa,eAAgB,SAAQ,YAAa,YAAW,YAAY;IACvE,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAyB;IAEjE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAIb;IACL,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,WAAW,CAA6C;IAChE,OAAO,CAAC,eAAe,CAAyC;IAGhE,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,iBAAiB,CAAuB;IAGhD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,eAAe,CAAgC;IAEvD,yDAAyD;IACzD,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAU;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAU;gBAEjC,UAAU,CAAC,EAAE,MAAM;IA0CzB,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BxD,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO;IA4F1C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAc9B,OAAO,IAAI,OAAO;IAIlB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI;IAInF,UAAU,IAAI,MAAM,GAAG,SAAS;IAIhC,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B,aAAa,IAAI,MAAM;IAIvB,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI;IAIjD,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI3C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B/B,OAAO,CAAC,kBAAkB;IA8B1B,kEAAkE;IAClE,OAAO,CAAC,qBAAqB;IAQ7B,mDAAmD;IACnD,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,cAAc;IAwDtB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,OAAO;IA+Bf,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,kBAAkB;IAkG1B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuF1B,sEAAsE;YACxD,aAAa;IAoC3B,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB;IAuCxB,OAAO,CAAC,gBAAgB;CAGzB"}
1
+ {"version":3,"file":"openclaw.d.ts","sourceRoot":"","sources":["../../src/adapters/openclaw.ts"],"names":[],"mappings":"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"}
@@ -6,23 +6,10 @@ import { homedir } from 'os';
6
6
  import { createPublicKey, createPrivateKey, sign as cryptoSign, randomUUID } from 'crypto';
7
7
  import WebSocket from 'ws';
8
8
  import { debug } from '../logger.js';
9
+ import { summarizeResponse, extractTopicHint } from '../timeline-summarizer.js';
10
+ import { cleanDetailText, cleanRawText, cleanNopMarkers, ED25519_SPKI_PREFIX_LEN, GATEWAY_PROTOCOL_VERSION, } from '@agentdeck/shared';
9
11
  import { OPENCLAW_CAPABILITIES, OPENCLAW_GATEWAY_PORT } from '../types.js';
10
12
  import { fetchModelCatalog, getDefaultModelName, invalidateModelCache } from '../model-catalog.js';
11
- /** Ed25519 SPKI DER prefix length (bytes before the raw 32-byte key) */
12
- const ED25519_SPKI_PREFIX_LEN = 12;
13
- /** Protocol version supported by this adapter */
14
- const PROTOCOL_VERSION = 3;
15
- /**
16
- * OpenClaw adapter — connects to OpenClaw Gateway via WebSocket.
17
- *
18
- * Protocol: Custom framing (req/res/event), Ed25519 device auth handshake,
19
- * v3 protocol. Unlike ClaudeCodeAdapter (PTY-based), this adapter:
20
- * - Connects to an already-running Gateway process
21
- * - Authenticates via Ed25519 device signature (~/.openclaw/identity/)
22
- * - Uses req/res framing over WebSocket for all commands
23
- * - Has no terminal (PTY), mode switching, or diff review
24
- * - Handles select_option/navigate_option/send_prompt directly via RPC
25
- */
26
13
  export class OpenClawAdapter extends EventEmitter {
27
14
  capabilities = OPENCLAW_CAPABILITIES;
28
15
  gatewayUrl;
@@ -34,12 +21,23 @@ export class OpenClawAdapter extends EventEmitter {
34
21
  projectName = null;
35
22
  alive = false;
36
23
  shutdownRequested = false;
24
+ externalHttpServer = false;
37
25
  diagHandler = null;
38
26
  rawDataCallback = null;
27
+ autoReconnect;
39
28
  // Session tracking
40
29
  currentSessionKey = null;
41
30
  currentRunId = null;
42
31
  pendingApprovalId = null;
32
+ // Chat tracking for timeline events
33
+ chatStarted = false;
34
+ chatStartTime = 0;
35
+ chatToolCount = 0;
36
+ chatToolNames = [];
37
+ lastPrompt = null;
38
+ accumulatedResponse = '';
39
+ topicExtracted = false;
40
+ chatIsAutomated = false;
43
41
  // Device identity (loaded once on start)
44
42
  deviceIdentity = null;
45
43
  deviceAuthToken = null;
@@ -47,9 +45,17 @@ export class OpenClawAdapter extends EventEmitter {
47
45
  reconnectDelay = 1000;
48
46
  static MAX_RECONNECT_DELAY = 30_000;
49
47
  static RPC_TIMEOUT = 10_000;
50
- constructor(gatewayUrl) {
48
+ constructor(options) {
51
49
  super();
52
- this.gatewayUrl = gatewayUrl || `ws://127.0.0.1:${OPENCLAW_GATEWAY_PORT}`;
50
+ if (typeof options === 'string') {
51
+ // Legacy: constructor(gatewayUrl?: string)
52
+ this.gatewayUrl = options || `ws://127.0.0.1:${OPENCLAW_GATEWAY_PORT}`;
53
+ this.autoReconnect = true;
54
+ }
55
+ else {
56
+ this.gatewayUrl = options?.gatewayUrl || `ws://127.0.0.1:${OPENCLAW_GATEWAY_PORT}`;
57
+ this.autoReconnect = options?.autoReconnect ?? true;
58
+ }
53
59
  // Create bare HTTP server for WsServer (plugin) attachment.
54
60
  this.httpServer = createServer((req, res) => {
55
61
  if (req.method === 'GET' && req.url === '/health') {
@@ -92,21 +98,29 @@ export class OpenClawAdapter extends EventEmitter {
92
98
  }
93
99
  // Load device identity for Ed25519 auth (non-fatal if missing)
94
100
  this.loadDeviceIdentity();
95
- // Start HTTP server for plugin WebSocket attachment
96
- await new Promise((resolve, reject) => {
97
- this.httpServer.on('error', (err) => {
98
- if (err.code === 'EADDRINUSE') {
99
- reject(new Error(`Port ${options.port} is already in use.`));
100
- }
101
- else {
102
- reject(err);
103
- }
104
- });
105
- this.httpServer.listen(options.port, '0.0.0.0', () => {
106
- debug('adapter:openclaw', `HTTP server listening on 0.0.0.0:${options.port}`);
107
- resolve();
101
+ if (options.externalServer) {
102
+ // Use externally provided HTTP server (daemon mode)
103
+ this.httpServer = options.externalServer;
104
+ this.externalHttpServer = true;
105
+ debug('adapter:openclaw', 'Using external HTTP server');
106
+ }
107
+ else {
108
+ // Start own HTTP server for plugin WebSocket attachment
109
+ await new Promise((resolve, reject) => {
110
+ this.httpServer.on('error', (err) => {
111
+ if (err.code === 'EADDRINUSE') {
112
+ reject(new Error(`Port ${options.port} is already in use.`));
113
+ }
114
+ else {
115
+ reject(err);
116
+ }
117
+ });
118
+ this.httpServer.listen(options.port, '127.0.0.1', () => {
119
+ debug('adapter:openclaw', `HTTP server listening on 127.0.0.1:${options.port}`);
120
+ resolve();
121
+ });
108
122
  });
109
- });
123
+ }
110
124
  // Connect to OpenClaw Gateway
111
125
  this.connectGateway();
112
126
  }
@@ -146,6 +160,23 @@ export class OpenClawAdapter extends EventEmitter {
146
160
  return true;
147
161
  case 'send_prompt': {
148
162
  debug('adapter:openclaw', `send_prompt: "${cmd.text.slice(0, 60)}"`);
163
+ this.lastPrompt = cmd.text;
164
+ // Optimistic: immediate timeline + PROCESSING state (no waiting for delta)
165
+ if (!this.chatStarted) {
166
+ this.chatStarted = true;
167
+ this.chatIsAutomated = false;
168
+ this.chatStartTime = Date.now();
169
+ this.chatToolCount = 0;
170
+ this.chatToolNames = [];
171
+ const prompt = cmd.text;
172
+ const promptRaw = prompt.length > 500 ? prompt.slice(0, 497) + '...' : prompt;
173
+ const promptDetail = prompt.length > 100 ? (prompt.length > 1000 ? prompt.slice(0, 997) + '...' : prompt) : undefined;
174
+ this.emitTimelineEntry({
175
+ ts: Date.now(), type: 'chat_start', raw: promptRaw,
176
+ ...(promptDetail ? { detail: promptDetail } : {}),
177
+ });
178
+ this.emitAdapterEvent({ source: 'parser', event: 'spinner_start' });
179
+ }
149
180
  if (this.currentSessionKey) {
150
181
  this.rpcCall('chat.send', {
151
182
  sessionKey: this.currentSessionKey,
@@ -153,10 +184,24 @@ export class OpenClawAdapter extends EventEmitter {
153
184
  idempotencyKey: randomUUID(),
154
185
  }).catch((err) => {
155
186
  debug('adapter:openclaw', `chat.send failed: ${err}`);
187
+ this.emitTimelineEntry({
188
+ ts: Date.now(), type: 'error', raw: `Send failed: ${err instanceof Error ? err.message : String(err)}`,
189
+ });
190
+ this.chatStarted = false;
191
+ this.lastPrompt = null;
192
+ this.accumulatedResponse = '';
193
+ this.currentRunId = null;
194
+ this.emitAdapterEvent({ source: 'parser', event: 'idle' });
156
195
  });
157
196
  }
158
197
  else {
159
198
  debug('adapter:openclaw', 'send_prompt: no active session');
199
+ this.emitTimelineEntry({
200
+ ts: Date.now(), type: 'error', raw: 'No active session',
201
+ });
202
+ this.chatStarted = false;
203
+ this.emitAdapterEvent({ source: 'parser', event: 'idle' });
204
+ return false;
160
205
  }
161
206
  return true;
162
207
  }
@@ -243,9 +288,11 @@ export class OpenClawAdapter extends EventEmitter {
243
288
  this.ws = null;
244
289
  }
245
290
  this.alive = false;
246
- await new Promise((resolve) => {
247
- this.httpServer.close(() => resolve());
248
- });
291
+ if (!this.externalHttpServer) {
292
+ await new Promise((resolve) => {
293
+ this.httpServer.close(() => resolve());
294
+ });
295
+ }
249
296
  }
250
297
  // ===== Private: Device Identity =====
251
298
  loadDeviceIdentity() {
@@ -359,7 +406,7 @@ export class OpenClawAdapter extends EventEmitter {
359
406
  });
360
407
  }
361
408
  scheduleReconnect() {
362
- if (this.shutdownRequested || this.reconnectTimer)
409
+ if (this.shutdownRequested || this.reconnectTimer || !this.autoReconnect)
363
410
  return;
364
411
  debug('adapter:openclaw', `Reconnecting in ${this.reconnectDelay}ms...`);
365
412
  this.reconnectTimer = setTimeout(() => {
@@ -369,6 +416,12 @@ export class OpenClawAdapter extends EventEmitter {
369
416
  this.reconnectDelay = Math.min(this.reconnectDelay * 2, OpenClawAdapter.MAX_RECONNECT_DELAY);
370
417
  }
371
418
  // ===== Private: Request/Response =====
419
+ /**
420
+ * Typed RPC dispatch. `GatewayMethodMap` correlates the method name with
421
+ * its param shape and result shape (declared in `shared/gateway-protocol.ts`),
422
+ * so misuse — wrong params for a method, or calling an unknown method — is
423
+ * a compile error here rather than a runtime surprise.
424
+ */
372
425
  rpcCall(method, params) {
373
426
  return new Promise((resolve, reject) => {
374
427
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
@@ -377,7 +430,11 @@ export class OpenClawAdapter extends EventEmitter {
377
430
  }
378
431
  const id = `r${++this.reqId}`;
379
432
  const message = { type: 'req', id, method, params };
380
- this.pendingRpc.set(id, { resolve, reject, method });
433
+ this.pendingRpc.set(id, {
434
+ resolve: resolve,
435
+ reject,
436
+ method,
437
+ });
381
438
  setTimeout(() => {
382
439
  if (this.pendingRpc.has(id)) {
383
440
  this.pendingRpc.delete(id);
@@ -418,7 +475,7 @@ export class OpenClawAdapter extends EventEmitter {
418
475
  if (this.alive) {
419
476
  this.emitAdapterEvent({ source: 'activity' });
420
477
  }
421
- this.handleGatewayEvent(msg.event, msg.payload || {});
478
+ this.handleGatewayEvent(msg.event, (msg.payload ?? {}));
422
479
  }
423
480
  }
424
481
  handleGatewayEvent(event, payload) {
@@ -440,29 +497,177 @@ export class OpenClawAdapter extends EventEmitter {
440
497
  const state = payload.state;
441
498
  const runId = payload.runId;
442
499
  const sessionKey = payload.sessionKey;
500
+ // Debug: log payload structure for diagnostic (delta/final only)
501
+ if (state === 'delta' || state === 'final') {
502
+ const keys = Object.keys(payload).join(',');
503
+ debug('adapter:openclaw', `Chat ${state} keys=[${keys}]`);
504
+ if (state === 'delta') {
505
+ // Log first delta fully for prompt discovery, then just keys
506
+ if (!this.chatStarted) {
507
+ debug('adapter:openclaw', `First delta payload: ${JSON.stringify(payload).slice(0, 800)}`);
508
+ }
509
+ }
510
+ else {
511
+ debug('adapter:openclaw', `Final payload: ${JSON.stringify(payload).slice(0, 800)}`);
512
+ }
513
+ }
443
514
  // Track active run and session
444
515
  if (runId)
445
516
  this.currentRunId = runId;
446
517
  if (sessionKey)
447
518
  this.currentSessionKey = sessionKey;
448
519
  switch (state) {
449
- case 'delta':
520
+ case 'delta': {
521
+ // Extract text from Gateway message structure:
522
+ // payload.message = { role: "assistant", content: [{ type: "text", text: "..." }] }
523
+ const deltaText = this.extractMessageText(payload);
524
+ if (!this.chatStarted) {
525
+ // Non-optimistic path: gateway-initiated chat (cron, web, etc.)
526
+ this.chatStarted = true;
527
+ this.chatIsAutomated = !this.lastPrompt;
528
+ this.chatStartTime = Date.now();
529
+ this.chatToolCount = 0;
530
+ this.chatToolNames = [];
531
+ this.topicExtracted = false;
532
+ this.accumulatedResponse = deltaText || '';
533
+ const prompt = this.lastPrompt || '자동 작업';
534
+ const promptRaw = prompt.length > 500 ? prompt.slice(0, 497) + '...' : prompt;
535
+ const promptDetail = prompt.length > 100 ? (prompt.length > 1000 ? prompt.slice(0, 997) + '...' : prompt) : undefined;
536
+ this.emitTimelineEntry({
537
+ ts: Date.now(), type: 'chat_start', raw: promptRaw,
538
+ ...(promptDetail ? { detail: promptDetail } : {}),
539
+ ...(this.chatIsAutomated ? { automated: true } : {}),
540
+ });
541
+ }
542
+ else {
543
+ // Gateway always sends cumulative content — replace, never append
544
+ if (deltaText) {
545
+ this.accumulatedResponse = deltaText;
546
+ }
547
+ // Early topic extraction — upsert chat_start with topic hint
548
+ if (!this.topicExtracted && this.accumulatedResponse.length > 20) {
549
+ const topicHint = extractTopicHint(this.accumulatedResponse);
550
+ if (topicHint && (!this.lastPrompt || this.lastPrompt === '자동 작업')) {
551
+ this.topicExtracted = true;
552
+ this.emitTimelineUpsert({
553
+ ts: this.chatStartTime, type: 'chat_start',
554
+ raw: cleanRawText(topicHint),
555
+ });
556
+ }
557
+ }
558
+ }
450
559
  this.emitAdapterEvent({
451
560
  source: 'parser',
452
561
  event: 'spinner_start',
453
562
  data: { runId, sessionKey },
454
563
  });
455
564
  break;
456
- case 'final':
457
- case 'aborted':
565
+ }
566
+ case 'final': {
567
+ // Extract modelId from payload if present (fallback when catalog probe fails)
568
+ const modelId = payload.model ?? payload.modelId;
569
+ if (typeof modelId === 'string' && modelId) {
570
+ this.emitAdapterEvent({
571
+ source: 'parser', event: 'model_info',
572
+ data: { model: modelId, plan: null },
573
+ });
574
+ }
575
+ const duration = this.chatStarted ? Math.round((Date.now() - this.chatStartTime) / 1000) : 0;
576
+ const toolSummary = this.buildToolSummary();
577
+ // Extract response content for detail + LLM summarization
578
+ const finalText = this.extractMessageText(payload);
579
+ const responseContent = (finalText || undefined)
580
+ || (this.accumulatedResponse || undefined);
581
+ // Emit chat_response for APME turn response capture
582
+ if (responseContent) {
583
+ const respRaw = responseContent.length > 500 ? responseContent.slice(0, 497) + '...' : responseContent;
584
+ this.emitTimelineEntry({
585
+ ts: Date.now(), type: 'chat_response',
586
+ raw: cleanRawText(respRaw),
587
+ detail: cleanDetailText(responseContent.slice(0, 3000)),
588
+ });
589
+ }
590
+ const cleanedResponse = responseContent ? cleanNopMarkers(cleanDetailText(responseContent)) : undefined;
591
+ const responseDetail = cleanedResponse
592
+ ? (cleanedResponse.length > 1000 ? cleanedResponse.slice(0, 997) + '...' : cleanedResponse)
593
+ : undefined;
594
+ // Emit chat_end with heuristic summary, then async LLM enrichment
595
+ const heuristicLabel = (responseContent && extractTopicHint(responseContent)) || 'Completed';
596
+ const parts = [heuristicLabel];
597
+ if (duration > 0)
598
+ parts.push(`${duration}s`);
599
+ if (toolSummary)
600
+ parts.push(toolSummary);
601
+ const chatEndTs = Date.now();
602
+ this.emitTimelineEntry({
603
+ ts: chatEndTs, type: 'chat_end', raw: parts.join(' \u00b7 '),
604
+ ...(responseDetail ? { detail: responseDetail } : {}),
605
+ ...(this.chatIsAutomated ? { automated: true } : {}),
606
+ });
607
+ // Async LLM summarization — fire-and-forget, upsert chat_end when ready
608
+ if (responseContent && responseContent.length > 30) {
609
+ const savedToolSummary = toolSummary;
610
+ const savedDuration = duration;
611
+ const savedAutomated = this.chatIsAutomated;
612
+ summarizeResponse(responseContent).then((summary) => {
613
+ if (summary) {
614
+ const enrichedParts = [summary];
615
+ if (savedDuration > 0)
616
+ enrichedParts.push(`${savedDuration}s`);
617
+ if (savedToolSummary)
618
+ enrichedParts.push(savedToolSummary);
619
+ debug('adapter:openclaw', `LLM summary: ${summary}`);
620
+ // Upsert chat_end with LLM summary replacing "Completed"
621
+ this.emitTimelineUpsert({
622
+ ts: chatEndTs, type: 'chat_end',
623
+ raw: enrichedParts.join(' \u00b7 '),
624
+ ...(responseDetail ? { detail: responseDetail } : {}),
625
+ ...(savedAutomated ? { automated: true } : {}),
626
+ });
627
+ }
628
+ }).catch(() => { });
629
+ }
630
+ this.chatStarted = false;
631
+ this.lastPrompt = null;
632
+ this.accumulatedResponse = '';
458
633
  this.currentRunId = null;
459
634
  this.emitAdapterEvent({ source: 'parser', event: 'idle' });
460
635
  break;
461
- case 'error':
636
+ }
637
+ case 'aborted': {
638
+ const abortDuration = this.chatStarted ? Math.round((Date.now() - this.chatStartTime) / 1000) : 0;
639
+ const abortToolSummary = this.buildToolSummary();
640
+ const abortParts = ['Aborted'];
641
+ if (abortDuration > 0)
642
+ abortParts.push(`after ${abortDuration}s`);
643
+ if (abortToolSummary)
644
+ abortParts.push(abortToolSummary);
645
+ const abortDetail = this.accumulatedResponse || undefined;
646
+ this.emitTimelineEntry({
647
+ ts: Date.now(), type: 'chat_end', raw: abortParts.join(' \u00b7 '),
648
+ ...(abortDetail ? { detail: abortDetail } : {}),
649
+ ...(this.chatIsAutomated ? { automated: true } : {}),
650
+ });
651
+ this.chatStarted = false;
652
+ this.lastPrompt = null;
653
+ this.accumulatedResponse = '';
654
+ this.currentRunId = null;
655
+ this.emitAdapterEvent({ source: 'parser', event: 'idle' });
656
+ break;
657
+ }
658
+ case 'error': {
659
+ const errMsg = payload.errorMessage || 'unknown';
660
+ this.emitTimelineEntry({
661
+ ts: Date.now(), type: 'error', raw: errMsg,
662
+ });
663
+ this.chatStarted = false;
664
+ this.lastPrompt = null;
665
+ this.accumulatedResponse = '';
462
666
  this.currentRunId = null;
463
- debug('adapter:openclaw', `Chat error: ${payload.errorMessage || 'unknown'}`);
667
+ debug('adapter:openclaw', `Chat error: ${errMsg}`);
464
668
  this.emitAdapterEvent({ source: 'parser', event: 'idle' });
465
669
  break;
670
+ }
466
671
  }
467
672
  break;
468
673
  }
@@ -472,6 +677,20 @@ export class OpenClawAdapter extends EventEmitter {
472
677
  const command = payload.command;
473
678
  const ask = payload.ask;
474
679
  this.pendingApprovalId = approvalId;
680
+ // Track tool for chat summary
681
+ const toolName = command?.split(' ')[0] || 'tool';
682
+ this.chatToolCount++;
683
+ if (!this.chatToolNames.includes(toolName)) {
684
+ this.chatToolNames.push(toolName);
685
+ }
686
+ const toolRaw = ask ? `${command}: ${ask}` : (command || 'Approve tool execution?');
687
+ const toolRequestRaw = toolRaw.length > 500 ? toolRaw.slice(0, 497) + '...' : toolRaw;
688
+ const toolRequestDetail = ask && ask.length > 100 ? (ask.length > 1000 ? ask.slice(0, 997) + '...' : ask) : undefined;
689
+ this.emitTimelineEntry({
690
+ ts: Date.now(), type: 'tool_request', raw: toolRequestRaw,
691
+ ...(toolRequestDetail ? { detail: toolRequestDetail } : {}),
692
+ approvalId, status: 'pending',
693
+ });
475
694
  this.emitAdapterEvent({
476
695
  source: 'parser',
477
696
  event: 'permission_prompt',
@@ -487,11 +706,32 @@ export class OpenClawAdapter extends EventEmitter {
487
706
  });
488
707
  break;
489
708
  }
490
- case 'exec.approval.resolved':
709
+ case 'exec.approval.resolved': {
491
710
  // Approval resolved (by us or another client) → processing continues
711
+ const resolvedId = this.pendingApprovalId;
712
+ const decision = payload.decision || 'allow';
492
713
  this.pendingApprovalId = null;
714
+ if (resolvedId) {
715
+ this.emitTimelineEntry({
716
+ ts: Date.now(), type: 'tool_resolved',
717
+ raw: decision === 'allow' ? 'Approved' : 'Denied',
718
+ approvalId: resolvedId,
719
+ status: decision === 'allow' ? 'approved' : 'denied',
720
+ });
721
+ }
493
722
  this.emitAdapterEvent({ source: 'parser', event: 'spinner_start' });
494
723
  break;
724
+ }
725
+ // ===== Gateway health =====
726
+ case 'health': {
727
+ const ok = payload.ok;
728
+ this.emitAdapterEvent({
729
+ source: 'metadata',
730
+ event: 'gateway_health',
731
+ data: { ok, payload },
732
+ });
733
+ break;
734
+ }
495
735
  // ===== Presence / keepalive =====
496
736
  case 'presence':
497
737
  case 'tick':
@@ -520,8 +760,8 @@ export class OpenClawAdapter extends EventEmitter {
520
760
  ?? ['operator.admin', 'operator.approvals', 'operator.read'];
521
761
  const authToken = this.deviceAuthToken?.token ?? '';
522
762
  const params = {
523
- minProtocol: PROTOCOL_VERSION,
524
- maxProtocol: PROTOCOL_VERSION,
763
+ minProtocol: GATEWAY_PROTOCOL_VERSION,
764
+ maxProtocol: GATEWAY_PROTOCOL_VERSION,
525
765
  client: {
526
766
  id: 'gateway-client',
527
767
  displayName: 'AgentDeck',
@@ -563,9 +803,9 @@ export class OpenClawAdapter extends EventEmitter {
563
803
  }
564
804
  }
565
805
  // Fetch sessions (async, non-blocking)
566
- this.fetchSessions();
806
+ this.fetchSessions().catch(err => debug('adapter:openclaw', `fetchSessions error: ${err}`));
567
807
  // Fetch model catalog (async, non-blocking)
568
- this.emitModelCatalog();
808
+ this.emitModelCatalog().catch(err => debug('adapter:openclaw', `emitModelCatalog error: ${err}`));
569
809
  },
570
810
  reject: (err) => {
571
811
  debug('adapter:openclaw', `Handshake failed: ${err.message}`);
@@ -605,15 +845,14 @@ export class OpenClawAdapter extends EventEmitter {
605
845
  // Pick the most recently updated session
606
846
  const sorted = [...sessions].sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
607
847
  this.currentSessionKey = sorted[0].key;
608
- const label = sorted[0].label || sorted[0].displayName;
609
- if (label) {
610
- this.projectName = label;
611
- this.emitAdapterEvent({
612
- source: 'parser',
613
- event: 'project_name',
614
- data: { name: label },
615
- });
616
- }
848
+ // Use fixed name Gateway session labels can be user identifiers
849
+ // (e.g. phone numbers) which are unsuitable as project names
850
+ this.projectName = 'OpenClaw';
851
+ this.emitAdapterEvent({
852
+ source: 'parser',
853
+ event: 'project_name',
854
+ data: { name: 'OpenClaw' },
855
+ });
617
856
  debug('adapter:openclaw', `Active session: ${this.currentSessionKey}`);
618
857
  }
619
858
  catch (err) {
@@ -621,11 +860,11 @@ export class OpenClawAdapter extends EventEmitter {
621
860
  }
622
861
  }
623
862
  /** Fetch model catalog via CLI and emit events. Retries once on failure. */
624
- emitModelCatalog(retry = true) {
863
+ async emitModelCatalog(retry = true) {
625
864
  // Invalidate cache on reconnect to get fresh data
626
865
  invalidateModelCache();
627
866
  try {
628
- const catalog = fetchModelCatalog();
867
+ const catalog = await fetchModelCatalog();
629
868
  if (!catalog) {
630
869
  if (retry && this.alive) {
631
870
  debug('adapter:openclaw', 'Model catalog empty — retrying in 10s');
@@ -634,7 +873,7 @@ export class OpenClawAdapter extends EventEmitter {
634
873
  return;
635
874
  }
636
875
  // Emit model_info for default model name (StateMachine uses this)
637
- const defaultModel = getDefaultModelName();
876
+ const defaultModel = await getDefaultModelName();
638
877
  if (defaultModel) {
639
878
  this.emitAdapterEvent({
640
879
  source: 'parser',
@@ -657,6 +896,37 @@ export class OpenClawAdapter extends EventEmitter {
657
896
  }
658
897
  }
659
898
  }
899
+ /**
900
+ * Extract text from Gateway chat message structure.
901
+ * Format: payload.message = { role, content: [{ type: "text", text: "..." }], timestamp }
902
+ */
903
+ extractMessageText(payload) {
904
+ const msg = payload.message;
905
+ if (!msg)
906
+ return undefined;
907
+ const content = msg.content;
908
+ if (!content || !Array.isArray(content))
909
+ return undefined;
910
+ const texts = content
911
+ .filter(c => c.type === 'text' && c.text)
912
+ .map(c => c.text);
913
+ return texts.length > 0 ? texts.join('') : undefined;
914
+ }
915
+ /** Build tool count summary like "Read(3), Bash(2)" */
916
+ buildToolSummary() {
917
+ if (this.chatToolNames.length === 0)
918
+ return '';
919
+ // Count occurrences (simplified — uses total count distributed)
920
+ return this.chatToolNames.join(', ');
921
+ }
922
+ /** Emit a timeline event through the adapter event system */
923
+ emitTimelineEntry(entry) {
924
+ this.emitAdapterEvent({ source: 'timeline', entry });
925
+ }
926
+ /** Emit a timeline upsert (update existing entry with same ts+type, or add new) */
927
+ emitTimelineUpsert(entry) {
928
+ this.emitAdapterEvent({ source: 'timeline', entry, upsert: true });
929
+ }
660
930
  emitAdapterEvent(evt) {
661
931
  this.emit('event', evt);
662
932
  }