@agentdeck/bridge 0.2.1 → 0.2.3
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.
- package/assets/fonts/IBMPlexSans-Bold.ttf +0 -0
- package/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Bold.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/assets/fonts/LICENSES.md +22 -0
- package/dist/adapters/claude-code.d.ts +6 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +8 -0
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/openclaw.d.ts +24 -0
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +291 -35
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/adapters/opencode-adapter.d.ts +59 -0
- package/dist/adapters/opencode-adapter.d.ts.map +1 -1
- package/dist/adapters/opencode-adapter.js +297 -35
- package/dist/adapters/opencode-adapter.js.map +1 -1
- package/dist/adb-reverse.d.ts +1 -1
- package/dist/adb-reverse.js +1 -1
- package/dist/antigravity-local.d.ts +1 -0
- package/dist/antigravity-local.d.ts.map +1 -1
- package/dist/antigravity-local.js +33 -0
- package/dist/antigravity-local.js.map +1 -1
- package/dist/apme/adapters/claude-hook.d.ts.map +1 -1
- package/dist/apme/adapters/claude-hook.js +5 -0
- package/dist/apme/adapters/claude-hook.js.map +1 -1
- package/dist/apme/adapters/codex-hook.d.ts +17 -0
- package/dist/apme/adapters/codex-hook.d.ts.map +1 -0
- package/dist/apme/adapters/codex-hook.js +95 -0
- package/dist/apme/adapters/codex-hook.js.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts +86 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.js +496 -0
- package/dist/apme/adapters/codex-turn-manager.js.map +1 -0
- package/dist/apme/adapters/openclaw-hook.d.ts +99 -0
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -0
- package/dist/apme/adapters/openclaw-hook.js +234 -0
- package/dist/apme/adapters/openclaw-hook.js.map +1 -0
- package/dist/apme/adapters/opencode-hook.d.ts +56 -0
- package/dist/apme/adapters/opencode-hook.d.ts.map +1 -0
- package/dist/apme/adapters/opencode-hook.js +182 -0
- package/dist/apme/adapters/opencode-hook.js.map +1 -0
- package/dist/apme/adapters/timeline.d.ts.map +1 -1
- package/dist/apme/adapters/timeline.js +6 -1
- package/dist/apme/adapters/timeline.js.map +1 -1
- package/dist/apme/antigravity-transcript.d.ts +18 -0
- package/dist/apme/antigravity-transcript.d.ts.map +1 -0
- package/dist/apme/antigravity-transcript.js +94 -0
- package/dist/apme/antigravity-transcript.js.map +1 -0
- package/dist/apme/classify-turn.d.ts +13 -0
- package/dist/apme/classify-turn.d.ts.map +1 -0
- package/dist/apme/classify-turn.js +49 -0
- package/dist/apme/classify-turn.js.map +1 -0
- package/dist/apme/claude-transcript-reader.d.ts +10 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
- package/dist/apme/claude-transcript-reader.js +35 -0
- package/dist/apme/claude-transcript-reader.js.map +1 -1
- package/dist/apme/collector.d.ts +129 -6
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +470 -23
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +87 -11
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +43 -6
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/index.d.ts +26 -5
- package/dist/apme/index.d.ts.map +1 -1
- package/dist/apme/index.js +135 -9
- package/dist/apme/index.js.map +1 -1
- package/dist/apme/pareto.d.ts +39 -0
- package/dist/apme/pareto.d.ts.map +1 -0
- package/dist/apme/pareto.js +59 -0
- package/dist/apme/pareto.js.map +1 -0
- package/dist/apme/recommend.d.ts +3 -0
- package/dist/apme/recommend.d.ts.map +1 -1
- package/dist/apme/recommend.js +43 -1
- package/dist/apme/recommend.js.map +1 -1
- package/dist/apme/runner.d.ts +92 -4
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +586 -70
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/sample-to-timeline.d.ts +26 -0
- package/dist/apme/sample-to-timeline.d.ts.map +1 -0
- package/dist/apme/sample-to-timeline.js +95 -0
- package/dist/apme/sample-to-timeline.js.map +1 -0
- package/dist/apme/scorers/index.d.ts +47 -0
- package/dist/apme/scorers/index.d.ts.map +1 -0
- package/dist/apme/scorers/index.js +96 -0
- package/dist/apme/scorers/index.js.map +1 -0
- package/dist/apme/settings.d.ts +14 -10
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +34 -8
- package/dist/apme/settings.js.map +1 -1
- package/dist/apme/store.d.ts +33 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +334 -25
- package/dist/apme/store.js.map +1 -1
- package/dist/awaiting-overlay.d.ts +99 -0
- package/dist/awaiting-overlay.d.ts.map +1 -0
- package/dist/awaiting-overlay.js +148 -0
- package/dist/awaiting-overlay.js.map +1 -0
- package/dist/ble-sync-spawn.d.ts +63 -0
- package/dist/ble-sync-spawn.d.ts.map +1 -0
- package/dist/ble-sync-spawn.js +196 -0
- package/dist/ble-sync-spawn.js.map +1 -0
- package/dist/bridge-core.d.ts +24 -1
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +157 -24
- package/dist/bridge-core.js.map +1 -1
- package/dist/broadcast.d.ts +3 -0
- package/dist/broadcast.d.ts.map +1 -0
- package/dist/broadcast.js +122 -0
- package/dist/broadcast.js.map +1 -0
- package/dist/check-deps.d.ts.map +1 -1
- package/dist/check-deps.js +25 -9
- package/dist/check-deps.js.map +1 -1
- package/dist/cli.d.ts +15 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1025 -94
- package/dist/cli.js.map +1 -1
- package/dist/codex-auth.d.ts +21 -0
- package/dist/codex-auth.d.ts.map +1 -1
- package/dist/codex-auth.js +52 -1
- package/dist/codex-auth.js.map +1 -1
- package/dist/codex-output-parser.d.ts +8 -0
- package/dist/codex-output-parser.d.ts.map +1 -1
- package/dist/codex-output-parser.js +110 -25
- package/dist/codex-output-parser.js.map +1 -1
- package/dist/codex-rate-limits.d.ts +16 -0
- package/dist/codex-rate-limits.d.ts.map +1 -0
- package/dist/codex-rate-limits.js +197 -0
- package/dist/codex-rate-limits.js.map +1 -0
- package/dist/codex-rollout-response.d.ts +31 -0
- package/dist/codex-rollout-response.d.ts.map +1 -0
- package/dist/codex-rollout-response.js +135 -0
- package/dist/codex-rollout-response.js.map +1 -0
- package/dist/daemon-server.d.ts +85 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +1627 -159
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon.js +15 -6
- package/dist/daemon.js.map +1 -1
- package/dist/display-dim.d.ts +9 -0
- package/dist/display-dim.d.ts.map +1 -0
- package/dist/display-dim.js +53 -0
- package/dist/display-dim.js.map +1 -0
- package/dist/display-monitor.d.ts +15 -2
- package/dist/display-monitor.d.ts.map +1 -1
- package/dist/display-monitor.js +104 -26
- package/dist/display-monitor.js.map +1 -1
- package/dist/esp32-serial.d.ts +160 -1
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +950 -153
- package/dist/esp32-serial.js.map +1 -1
- package/dist/fallback-task-timeline.d.ts +42 -0
- package/dist/fallback-task-timeline.d.ts.map +1 -0
- package/dist/fallback-task-timeline.js +112 -0
- package/dist/fallback-task-timeline.js.map +1 -0
- package/dist/foundation-models-helper.d.ts +11 -0
- package/dist/foundation-models-helper.d.ts.map +1 -0
- package/dist/foundation-models-helper.js +241 -0
- package/dist/foundation-models-helper.js.map +1 -0
- package/dist/hook-migration.js +1 -1
- package/dist/hook-migration.js.map +1 -1
- package/dist/hook-server.d.ts +2 -3
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +22 -10
- package/dist/hook-server.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +33 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +164 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts +18 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.js +85 -0
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts +20 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.js +57 -0
- package/dist/idotmatrix/idotmatrix-settings.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +215 -179
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +15 -16
- package/dist/log-stream.d.ts.map +1 -1
- package/dist/log-stream.js +18 -96
- package/dist/log-stream.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -3
- package/dist/logger.js.map +1 -1
- package/dist/mdns.d.ts +17 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +52 -18
- package/dist/mdns.js.map +1 -1
- package/dist/modules/adb-module.d.ts +1 -1
- package/dist/modules/adb-module.d.ts.map +1 -1
- package/dist/modules/adb-module.js +4 -3
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/modules/broadcast-module.d.ts +17 -0
- package/dist/modules/broadcast-module.d.ts.map +1 -0
- package/dist/modules/broadcast-module.js +26 -0
- package/dist/modules/broadcast-module.js.map +1 -0
- package/dist/modules/idotmatrix-module.d.ts +9 -0
- package/dist/modules/idotmatrix-module.d.ts.map +1 -0
- package/dist/modules/idotmatrix-module.js +46 -0
- package/dist/modules/idotmatrix-module.js.map +1 -0
- package/dist/modules/index.d.ts +3 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +9 -3
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/pixoo-module.d.ts.map +1 -1
- package/dist/modules/pixoo-module.js +14 -4
- package/dist/modules/pixoo-module.js.map +1 -1
- package/dist/modules/serial-module.d.ts +4 -0
- package/dist/modules/serial-module.d.ts.map +1 -1
- package/dist/modules/serial-module.js +14 -12
- package/dist/modules/serial-module.js.map +1 -1
- package/dist/modules/timebox-module.d.ts +9 -0
- package/dist/modules/timebox-module.d.ts.map +1 -0
- package/dist/modules/timebox-module.js +46 -0
- package/dist/modules/timebox-module.js.map +1 -0
- package/dist/modules/types.d.ts +4 -2
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/openclaw-session.d.ts +18 -0
- package/dist/openclaw-session.d.ts.map +1 -0
- package/dist/openclaw-session.js +34 -0
- package/dist/openclaw-session.js.map +1 -0
- package/dist/output-parser.d.ts +30 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +125 -9
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +26 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +258 -49
- package/dist/passive-observer.js.map +1 -1
- package/dist/permission-resolver.d.ts +47 -0
- package/dist/permission-resolver.d.ts.map +1 -0
- package/dist/permission-resolver.js +103 -0
- package/dist/permission-resolver.js.map +1 -0
- package/dist/pixoo/micro-glyphs.d.ts +49 -0
- package/dist/pixoo/micro-glyphs.d.ts.map +1 -0
- package/dist/pixoo/micro-glyphs.js +276 -0
- package/dist/pixoo/micro-glyphs.js.map +1 -0
- package/dist/pixoo/pixoo-bridge.d.ts +2 -2
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
- package/dist/pixoo/pixoo-bridge.js +93 -25
- package/dist/pixoo/pixoo-bridge.js.map +1 -1
- package/dist/pixoo/pixoo-camera.d.ts +19 -3
- package/dist/pixoo/pixoo-camera.d.ts.map +1 -1
- package/dist/pixoo/pixoo-camera.js +62 -27
- package/dist/pixoo/pixoo-camera.js.map +1 -1
- package/dist/pixoo/pixoo-client.d.ts +13 -3
- package/dist/pixoo/pixoo-client.d.ts.map +1 -1
- package/dist/pixoo/pixoo-client.js +51 -22
- package/dist/pixoo/pixoo-client.js.map +1 -1
- package/dist/pixoo/pixoo-discover.d.ts +37 -0
- package/dist/pixoo/pixoo-discover.d.ts.map +1 -0
- package/dist/pixoo/pixoo-discover.js +105 -0
- package/dist/pixoo/pixoo-discover.js.map +1 -0
- package/dist/pixoo/pixoo-renderer.d.ts +8 -3
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +189 -60
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pixoo/pixoo-settings.d.ts +6 -0
- package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
- package/dist/pixoo/pixoo-settings.js +9 -0
- package/dist/pixoo/pixoo-settings.js.map +1 -1
- package/dist/pixoo/pixoo-sprites.d.ts +60 -11
- package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
- package/dist/pixoo/pixoo-sprites.js +512 -209
- package/dist/pixoo/pixoo-sprites.js.map +1 -1
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +8 -2
- package/dist/pty-manager.js.map +1 -1
- package/dist/session-activity.d.ts +14 -0
- package/dist/session-activity.d.ts.map +1 -0
- package/dist/session-activity.js +160 -0
- package/dist/session-activity.js.map +1 -0
- package/dist/session-aggregator.d.ts +17 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +48 -9
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-registry.d.ts +71 -3
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +137 -8
- package/dist/session-registry.js.map +1 -1
- package/dist/session-timeline-relay.d.ts.map +1 -1
- package/dist/session-timeline-relay.js +13 -4
- package/dist/session-timeline-relay.js.map +1 -1
- package/dist/session-transcript-timeline.d.ts +49 -0
- package/dist/session-transcript-timeline.d.ts.map +1 -0
- package/dist/session-transcript-timeline.js +276 -0
- package/dist/session-transcript-timeline.js.map +1 -0
- package/dist/state-machine.d.ts +6 -1
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +86 -28
- package/dist/state-machine.js.map +1 -1
- package/dist/timebox/timebox-daemon-sync.d.ts +19 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -0
- package/dist/timebox/timebox-daemon-sync.js +135 -0
- package/dist/timebox/timebox-daemon-sync.js.map +1 -0
- package/dist/timebox/timebox-discover.d.ts +19 -0
- package/dist/timebox/timebox-discover.d.ts.map +1 -0
- package/dist/timebox/timebox-discover.js +87 -0
- package/dist/timebox/timebox-discover.js.map +1 -0
- package/dist/timebox/timebox-settings.d.ts +32 -0
- package/dist/timebox/timebox-settings.d.ts.map +1 -0
- package/dist/timebox/timebox-settings.js +80 -0
- package/dist/timebox/timebox-settings.js.map +1 -0
- package/dist/timeline-store.d.ts +63 -3
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +292 -14
- package/dist/timeline-store.js.map +1 -1
- package/dist/timeline-summarizer.d.ts.map +1 -1
- package/dist/timeline-summarizer.js +29 -5
- package/dist/timeline-summarizer.js.map +1 -1
- package/dist/tui/ansi.d.ts +0 -4
- package/dist/tui/ansi.d.ts.map +1 -1
- package/dist/tui/ansi.js +14 -7
- package/dist/tui/ansi.js.map +1 -1
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.d.ts.map +1 -1
- package/dist/tui/dashboard.js +24 -7
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/tui/renderer.d.ts +36 -0
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +303 -119
- package/dist/tui/renderer.js.map +1 -1
- package/dist/tui/terrarium.d.ts.map +1 -1
- package/dist/tui/terrarium.js +37 -49
- package/dist/tui/terrarium.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +7 -1
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +10 -3
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +74 -12
- package/dist/usage-event.js.map +1 -1
- package/dist/utility-proxy.d.ts.map +1 -1
- package/dist/utility-proxy.js +8 -0
- package/dist/utility-proxy.js.map +1 -1
- package/dist/utils/project-name.d.ts +3 -0
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +44 -2
- package/dist/utils/project-name.js.map +1 -1
- package/dist/windows-service.d.ts +26 -0
- package/dist/windows-service.d.ts.map +1 -0
- package/dist/windows-service.js +145 -0
- package/dist/windows-service.js.map +1 -0
- package/dist/ws-server.d.ts +22 -1
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +89 -4
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +112 -0
- package/package.json +9 -3
- package/scripts/build-fm-helper.mjs +26 -0
- package/LICENSE +0 -21
- package/dist/apme/tuner.d.ts +0 -64
- package/dist/apme/tuner.d.ts.map +0 -1
- package/dist/apme/tuner.js +0 -297
- package/dist/apme/tuner.js.map +0 -1
- package/dist/d200h/hid-protocol.d.ts +0 -62
- package/dist/d200h/hid-protocol.d.ts.map +0 -1
- package/dist/d200h/hid-protocol.js +0 -124
- package/dist/d200h/hid-protocol.js.map +0 -1
- package/dist/d200h/image-renderer.d.ts +0 -36
- package/dist/d200h/image-renderer.d.ts.map +0 -1
- package/dist/d200h/image-renderer.js +0 -487
- package/dist/d200h/image-renderer.js.map +0 -1
- package/dist/modules/d200h-module.d.ts +0 -43
- package/dist/modules/d200h-module.d.ts.map +0 -1
- package/dist/modules/d200h-module.js +0 -360
- package/dist/modules/d200h-module.js.map +0 -1
- package/dist/modules/d200h-renderer.d.ts +0 -7
- package/dist/modules/d200h-renderer.d.ts.map +0 -1
- package/dist/modules/d200h-renderer.js +0 -321
- package/dist/modules/d200h-renderer.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -5,13 +5,18 @@
|
|
|
5
5
|
* Uses BridgeCore for shared infrastructure.
|
|
6
6
|
*/
|
|
7
7
|
import { BridgeCore } from './bridge-core.js';
|
|
8
|
-
import {
|
|
8
|
+
import { buildDisplayStateEvent } from './display-dim.js';
|
|
9
|
+
import { SERIAL_FORWARDED_EVENTS } from '@agentdeck/shared/protocol';
|
|
10
|
+
import { prepareForSerial } from './esp32-serial.js';
|
|
11
|
+
import { initApme, isTimelineProjectionEnabled } from './apme/index.js';
|
|
12
|
+
import { FallbackTaskTimeline } from './fallback-task-timeline.js';
|
|
9
13
|
import { readLastTurn as readClaudeTranscriptLastTurn } from './apme/claude-transcript-reader.js';
|
|
10
14
|
import { claudeHookToSpans } from './apme/adapters/claude-hook.js';
|
|
15
|
+
import { codexHookToSpans } from './apme/adapters/codex-hook.js';
|
|
16
|
+
import { CodexTurnManager } from './apme/adapters/codex-turn-manager.js';
|
|
11
17
|
import { claudePtyParserEventToSpans, claudePtyResponseToSpan } from './apme/adapters/claude-pty.js';
|
|
12
18
|
import { timelineEntryToSpans } from './apme/adapters/timeline.js';
|
|
13
|
-
import {
|
|
14
|
-
import { randomUUID } from 'crypto';
|
|
19
|
+
import { classifyAndEnqueueTurn } from './apme/classify-turn.js';
|
|
15
20
|
import { VoiceManager } from './voice.js';
|
|
16
21
|
import { checkDependencies } from './check-deps.js';
|
|
17
22
|
import { enableDebugLog, log, debug, setPtyMode } from './logger.js';
|
|
@@ -22,8 +27,8 @@ import { createAdapter, ClaudeCodeAdapter, CodexCliAdapter, OpenCodeAdapter } fr
|
|
|
22
27
|
import { MonitorAdapter } from './adapters/monitor.js';
|
|
23
28
|
import { UtilityProxy } from './utility-proxy.js';
|
|
24
29
|
import { BridgeLogStream } from './log-stream.js';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
30
|
+
import { cleanRawText, prepareMarkdownDetail, extractTopicHintWithKind, promptSnippetFallback, } from '@agentdeck/shared';
|
|
31
|
+
import { injectOpenClawSession } from './openclaw-session.js';
|
|
27
32
|
import { VoiceAssistantManager } from './voice-assistant.js';
|
|
28
33
|
import { TerminalStatus } from './terminal-status.js';
|
|
29
34
|
import { readFileSync } from 'fs';
|
|
@@ -142,6 +147,15 @@ export async function startSession(opts) {
|
|
|
142
147
|
log(`Starting AgentDeck bridge on port ${port} (agent: ${agentType})...`);
|
|
143
148
|
// ===== Create adapter =====
|
|
144
149
|
const adapter = createAdapter(agentType, opts.gatewayUrl);
|
|
150
|
+
// Seed the resolved (git-aware) name into the Claude output parser so its
|
|
151
|
+
// legacy PROJECT_DIR scrape doesn't override it: the scrape captures the
|
|
152
|
+
// bare basename of ANY path-like terminal line (first match sticks) and
|
|
153
|
+
// flows through the state-machine snapshot, which used to win over the
|
|
154
|
+
// resolver — a monorepo-subdir session would flip from the repo name to
|
|
155
|
+
// the subdir (or to an unrelated printed path).
|
|
156
|
+
if (adapter instanceof ClaudeCodeAdapter) {
|
|
157
|
+
adapter.seedProjectName(projectName);
|
|
158
|
+
}
|
|
145
159
|
// ===== Start adapter (creates HTTP server, spawns process) =====
|
|
146
160
|
try {
|
|
147
161
|
await adapter.start({ port, command: opts.command, gatewayUrl: opts.gatewayUrl });
|
|
@@ -162,36 +176,47 @@ export async function startSession(opts) {
|
|
|
162
176
|
projectName,
|
|
163
177
|
httpServer: adapter.getHttpServer(),
|
|
164
178
|
});
|
|
179
|
+
const esp32WifiEvents = new Set([
|
|
180
|
+
...SERIAL_FORWARDED_EVENTS,
|
|
181
|
+
'esp32_ota_begin',
|
|
182
|
+
'esp32_ota_chunk',
|
|
183
|
+
'esp32_ota_end',
|
|
184
|
+
'esp32_ota_abort',
|
|
185
|
+
]);
|
|
186
|
+
core.wsServer.setEventTransformer((event, client) => {
|
|
187
|
+
if (!core.wsServer.isEsp32Client(client))
|
|
188
|
+
return event;
|
|
189
|
+
if (!esp32WifiEvents.has(event.type))
|
|
190
|
+
return null;
|
|
191
|
+
return prepareForSerial(event);
|
|
192
|
+
});
|
|
165
193
|
// ===== APME (Agent Performance Monitoring & Evaluation) =====
|
|
166
194
|
// Optional: degrades to no-op if better-sqlite3 isn't installed.
|
|
167
|
-
|
|
195
|
+
// emitTimeline forwards task_start/task_end so the dashboard sees the task
|
|
196
|
+
// hierarchy header rows that group turns by APME boundary signal.
|
|
197
|
+
const projectTimeline = isTimelineProjectionEnabled();
|
|
198
|
+
const apme = await initApme(undefined, {
|
|
199
|
+
emitTimeline: (entry) => core.bridgeTimeline.addEntry(entry),
|
|
200
|
+
projectSampleTimeline: projectTimeline,
|
|
201
|
+
emitProjectedTimeline: (entry) => core.bridgeTimeline.addEntry(entry, { bypassSuppression: true }),
|
|
202
|
+
});
|
|
168
203
|
if (apme) {
|
|
169
204
|
core.setApme(apme, process.cwd());
|
|
205
|
+
if (projectTimeline) {
|
|
206
|
+
core.bridgeTimeline.setSuppressLocalChatTool(true);
|
|
207
|
+
log('APME timeline projection ENABLED (AGENTDECK_TIMELINE_PROJECTION=1) — chat/tool rows derive from SessionSample');
|
|
208
|
+
}
|
|
170
209
|
log('APME enabled — runs will be logged to ~/.agentdeck/apme.sqlite');
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
if (turnId) {
|
|
175
|
-
// Turn-level eval completed
|
|
176
|
-
const run = apme.store.getRun(runId);
|
|
177
|
-
if (!run)
|
|
178
|
-
return;
|
|
179
|
-
const turnEvals = apme.store.listEvalsForTurn(turnId);
|
|
180
|
-
const overall = turnEvals.find(e => e.metric === 'overall');
|
|
181
|
-
if (!overall)
|
|
182
|
-
return;
|
|
183
|
-
const pct = Math.round(overall.score * 100);
|
|
184
|
-
const axes = turnEvals.filter(e => e.metric !== 'overall');
|
|
185
|
-
const axisStr = axes.map(e => `${e.metric}=${Math.round(e.score * 100)}%`).join(' ');
|
|
186
|
-
core.bridgeTimeline.addEntry({
|
|
187
|
-
ts: Date.now(), type: 'eval_result',
|
|
188
|
-
raw: `★ turn ${pct}% [${run.taskCategory ?? '?'}]`,
|
|
189
|
-
detail: axisStr || `Turn eval · ${run.taskPrompt?.slice(0, 80) ?? ''}`,
|
|
190
|
-
agentType: run.agentType,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
});
|
|
210
|
+
// Eval results are no longer projected onto the timeline (the timeline is an
|
|
211
|
+
// activity log only). Scores are stored in apme.sqlite and surfaced via the
|
|
212
|
+
// scorecard / `apme_eval` WS event, keeping the timeline noise-free.
|
|
194
213
|
}
|
|
214
|
+
// APME down (better-sqlite3 failed to load): keep the timeline's task
|
|
215
|
+
// hierarchy alive by minting fallback task_start/task_end rows from the
|
|
216
|
+
// hook boundary signals. No storage, no eval — structure only.
|
|
217
|
+
const fallbackTasks = apme
|
|
218
|
+
? null
|
|
219
|
+
: new FallbackTaskTimeline((entry) => core.bridgeTimeline.addEntry(entry), { agentType });
|
|
195
220
|
// ===== Session-specific components =====
|
|
196
221
|
const voiceManager = new VoiceManager();
|
|
197
222
|
const utilityProxy = new UtilityProxy();
|
|
@@ -260,26 +285,29 @@ export async function startSession(opts) {
|
|
|
260
285
|
hookServer.setMeta({ agentType, projectName });
|
|
261
286
|
hookServer.setVoiceManager(voiceManager);
|
|
262
287
|
hookServer.onApiUsage(() => ({ usage: core.cachedApiUsage, fetchedAt: core.lastApiFetchTime }));
|
|
263
|
-
hookServer.pairingToken = core.authToken;
|
|
264
288
|
}
|
|
265
289
|
else if (adapter instanceof CodexCliAdapter) {
|
|
266
290
|
hookServer = adapter.getCodexHookServer();
|
|
267
291
|
hookServer.setMeta({ agentType, projectName });
|
|
268
292
|
hookServer.setVoiceManager(voiceManager);
|
|
269
|
-
hookServer.pairingToken = core.authToken;
|
|
270
293
|
}
|
|
271
294
|
else if (adapter instanceof OpenCodeAdapter) {
|
|
272
295
|
hookServer = adapter.getHookServer();
|
|
273
296
|
hookServer.setMeta({ agentType, projectName });
|
|
274
297
|
hookServer.setVoiceManager(voiceManager);
|
|
275
|
-
|
|
298
|
+
// OpenCode has no CLI-level hooks; SSE is the only event channel. Bind
|
|
299
|
+
// the adapter to the APME session so SSE-derived spans (tool_call /
|
|
300
|
+
// tool_result / task_boundary / turn_*) can reach the collector. Without
|
|
301
|
+
// this OpenCode sessions land in APME's runs table but never get
|
|
302
|
+
// per-task evaluation — every session collapses to a single session_end
|
|
303
|
+
// task boundary, defeating per-task scoring.
|
|
304
|
+
adapter.setApmeSession(core.sessionId, process.cwd());
|
|
276
305
|
}
|
|
277
306
|
else if (adapter instanceof MonitorAdapter) {
|
|
278
307
|
hookServer = adapter.getHookServer();
|
|
279
308
|
hookServer.setMeta({ agentType, projectName });
|
|
280
309
|
hookServer.setVoiceManager(voiceManager);
|
|
281
310
|
hookServer.onApiUsage(() => ({ usage: core.cachedApiUsage, fetchedAt: core.lastApiFetchTime }));
|
|
282
|
-
hookServer.pairingToken = core.authToken;
|
|
283
311
|
}
|
|
284
312
|
const broadcastSse = (event) => hookServer?.broadcastSse(event);
|
|
285
313
|
core.setSseBroadcast(broadcastSse);
|
|
@@ -305,6 +333,7 @@ export async function startSession(opts) {
|
|
|
305
333
|
adb: 'auto',
|
|
306
334
|
serial: false, // daemon-only — session bridges never talk to ESP32
|
|
307
335
|
pixoo: false, // daemon-only — session bridges never talk to Pixoo
|
|
336
|
+
timebox: false, // daemon-only — session bridges never talk to Timebox
|
|
308
337
|
};
|
|
309
338
|
const deviceModules = createDefaultModules(agentType);
|
|
310
339
|
// Set up ESP32 state provider before module init
|
|
@@ -318,7 +347,7 @@ export async function startSession(opts) {
|
|
|
318
347
|
if (lastStateEvent)
|
|
319
348
|
events.push(lastStateEvent);
|
|
320
349
|
events.push(core.buildUsage());
|
|
321
|
-
events.push(
|
|
350
|
+
events.push(buildDisplayStateEvent(core.displayMonitor.isDisplayOn()));
|
|
322
351
|
core.broadcastSessionsList().catch(() => { });
|
|
323
352
|
return events;
|
|
324
353
|
});
|
|
@@ -337,8 +366,12 @@ export async function startSession(opts) {
|
|
|
337
366
|
if (wifiConfig?.autoProvision) {
|
|
338
367
|
const lanIp = getLanIp();
|
|
339
368
|
onESP32Message((portPath, msg) => {
|
|
340
|
-
|
|
341
|
-
|
|
369
|
+
const shouldRefreshIps10Endpoint = msg.type === 'device_info' &&
|
|
370
|
+
msg.board === 'ips_10' &&
|
|
371
|
+
msg.wifiConnected &&
|
|
372
|
+
!msg.wifiRadioParked;
|
|
373
|
+
if (msg.type === 'device_info' && (!msg.wifiConnected || shouldRefreshIps10Endpoint)) {
|
|
374
|
+
const sent = sendWifiProvisionToAll({
|
|
342
375
|
type: 'wifi_provision',
|
|
343
376
|
ssid: wifiConfig.ssid,
|
|
344
377
|
password: wifiConfig.password,
|
|
@@ -346,7 +379,8 @@ export async function startSession(opts) {
|
|
|
346
379
|
bridgePort: port,
|
|
347
380
|
authToken: core.authToken,
|
|
348
381
|
});
|
|
349
|
-
|
|
382
|
+
if (sent > 0)
|
|
383
|
+
log(`WiFi provision sent to ${sent} ESP32 device(s) after ${portPath}`);
|
|
350
384
|
}
|
|
351
385
|
else if (msg.type === 'wifi_provision_ack') {
|
|
352
386
|
log(msg.success ? `ESP32 WiFi connected: ${msg.ip} \u2713` : `ESP32 WiFi failed: ${msg.error || 'unknown'}`);
|
|
@@ -365,7 +399,11 @@ export async function startSession(opts) {
|
|
|
365
399
|
],
|
|
366
400
|
}));
|
|
367
401
|
// Pixoo live preview frame getter for GET /pixoo/frame
|
|
368
|
-
hookServer?.setPixooFrameGetter(() =>
|
|
402
|
+
hookServer?.setPixooFrameGetter((size, layout) =>
|
|
403
|
+
// The frame cache holds the standard terrarium, so render micro fresh.
|
|
404
|
+
layout === 'micro'
|
|
405
|
+
? renderPreviewFrame(size, 'micro')
|
|
406
|
+
: (getLastFrame(size) ?? renderPreviewFrame(size)));
|
|
369
407
|
// ===== Diagnostics =====
|
|
370
408
|
adapter.onDiag((tail) => createDiagDump(core.stateMachine, core.wsServer, journal, ptyRingBuffer, tail));
|
|
371
409
|
adapter.onRawData((data) => {
|
|
@@ -400,14 +438,17 @@ export async function startSession(opts) {
|
|
|
400
438
|
switch (evt.source) {
|
|
401
439
|
case 'hook':
|
|
402
440
|
journal.write('hook', 'hook', { event: evt.event, data: evt.data });
|
|
403
|
-
// APME ingestion via the shared TelemetrySpan envelope.
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
441
|
+
// APME ingestion via the shared TelemetrySpan envelope. Codex
|
|
442
|
+
// CLI sessions go through codexHookToSpans (codex_* event prefix);
|
|
443
|
+
// Claude Code goes through claudeHookToSpans. Both adapters detect
|
|
444
|
+
// `/clear` and emit a `task_boundary` span so there's no separate
|
|
445
|
+
// splitRun branch here.
|
|
408
446
|
if (apme) {
|
|
409
447
|
const ctx = makeApmeAdapterCtx(apme, core.sessionId, agentType);
|
|
410
|
-
|
|
448
|
+
const spans = agentType === 'codex-cli'
|
|
449
|
+
? codexHookToSpans(ctx, evt.event, evt.data ?? {})
|
|
450
|
+
: claudeHookToSpans(ctx, evt.event, evt.data ?? {});
|
|
451
|
+
for (const span of spans) {
|
|
411
452
|
apme.collector.ingestSpan(core.sessionId, span);
|
|
412
453
|
}
|
|
413
454
|
}
|
|
@@ -433,6 +474,9 @@ export async function startSession(opts) {
|
|
|
433
474
|
}
|
|
434
475
|
if (evt.event === 'Stop')
|
|
435
476
|
pendingPtyResponse = null; // Stop has cleaner response
|
|
477
|
+
// APME-off fallback: task rows from boundary hooks alone.
|
|
478
|
+
if (fallbackTasks)
|
|
479
|
+
fallbackTasks.ingestHook(core.sessionId, evt.event, evt.data ?? {});
|
|
436
480
|
if (evt.event === 'shutdown') {
|
|
437
481
|
shutdown();
|
|
438
482
|
return;
|
|
@@ -613,10 +657,15 @@ export async function startSession(opts) {
|
|
|
613
657
|
if (core.wsServer.getClientCount() > 0 && Date.now() - core.lastApiFetchTime > 10_000) {
|
|
614
658
|
core.fetchAndUpdateUsage().catch(() => { });
|
|
615
659
|
}
|
|
616
|
-
// Voice assistant: if processing a wake-word prompt, speak the response
|
|
660
|
+
// Voice assistant: if processing a wake-word prompt, speak the response.
|
|
661
|
+
// Prefer chat_response (the turn's primary completion row now that a
|
|
662
|
+
// paired chat_end is no longer emitted for turns that have response
|
|
663
|
+
// text); fall back to chat_end for response-less turns. Read detail
|
|
664
|
+
// (full markdown) first, then the raw snippet.
|
|
617
665
|
if (voiceAssistant?.getState() === 'processing') {
|
|
618
|
-
const lastEntry = core.bridgeTimeline.getLastEntry('
|
|
619
|
-
|
|
666
|
+
const lastEntry = core.bridgeTimeline.getLastEntry('chat_response')
|
|
667
|
+
?? core.bridgeTimeline.getLastEntry('chat_end');
|
|
668
|
+
const responseText = lastEntry?.detail ?? lastEntry?.raw;
|
|
620
669
|
if (responseText) {
|
|
621
670
|
voiceAssistant.handleResponse(responseText).catch((err) => {
|
|
622
671
|
debug('agentdeck', `Voice assistant TTS error: ${err}`);
|
|
@@ -752,11 +801,17 @@ export async function startSession(opts) {
|
|
|
752
801
|
});
|
|
753
802
|
// Deck slot map relay
|
|
754
803
|
core.wsServer.onRawMessage((msg, sender) => {
|
|
804
|
+
if (msg.type === 'device_info') {
|
|
805
|
+
core.wsServer.markEsp32Client(sender);
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
755
808
|
if (msg.type === 'deck_slot_map') {
|
|
756
809
|
cachedSlotMap = msg;
|
|
757
810
|
core.wsServer.broadcastExcept(cachedSlotMap, sender);
|
|
758
811
|
broadcastSse(cachedSlotMap);
|
|
759
812
|
core.broadcast(computeButtonState());
|
|
813
|
+
// Re-broadcast sessions_list so slot buttons populate after plugin startup race
|
|
814
|
+
core.broadcastSessionsList().catch(() => { });
|
|
760
815
|
return true;
|
|
761
816
|
}
|
|
762
817
|
return false;
|
|
@@ -843,20 +898,12 @@ export async function startSession(opts) {
|
|
|
843
898
|
core.startGatewayProbe(800);
|
|
844
899
|
core.startGatewayHealthCheck();
|
|
845
900
|
core.startSessionsListPolling();
|
|
846
|
-
// Inject virtual OpenClaw session
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
return [...sessions, {
|
|
853
|
-
id: 'openclaw-gateway',
|
|
854
|
-
port: 18789,
|
|
855
|
-
projectName: 'OpenClaw',
|
|
856
|
-
agentType: 'openclaw',
|
|
857
|
-
alive: true,
|
|
858
|
-
}];
|
|
859
|
-
});
|
|
901
|
+
// Inject virtual OpenClaw session only after Gateway authentication succeeds
|
|
902
|
+
// (gatewayConnected). Reachability alone (cachedGatewayAvailable) is a
|
|
903
|
+
// topology signal, not proof commands can route — gating on it kept a phantom
|
|
904
|
+
// OpenClaw session alive whenever anything held port 18789 open. Shared
|
|
905
|
+
// injector with daemon-server.ts; mirror of Swift buildSessionsListEvent.
|
|
906
|
+
core.setSessionsEnricher((sessions) => injectOpenClawSession(sessions, { gatewayConnected: core.cachedGatewayConnected }));
|
|
860
907
|
// ===== Encoder state computation =====
|
|
861
908
|
function computeEncoderState() {
|
|
862
909
|
const snapshot = core.stateMachine.getSnapshot();
|
|
@@ -1133,110 +1180,60 @@ export async function startSession(opts) {
|
|
|
1133
1180
|
core.shutdown();
|
|
1134
1181
|
}
|
|
1135
1182
|
}
|
|
1136
|
-
// Mid-session classify + turn-level eval enqueue.
|
|
1137
|
-
// Shared between Claude spinner_stop, OpenClaw/OpenCode chat_response, and Codex spinner_stop
|
|
1138
|
-
// so all agents get the same turns.task_category stamping and non-code turn_judge evals.
|
|
1139
|
-
async function classifyAndEnqueueTurn(apme, sid) {
|
|
1140
|
-
const turnId = apme.collector.getActiveTurnId(sid);
|
|
1141
|
-
const runId = apme.collector.getRunId(sid);
|
|
1142
|
-
if (!turnId || !runId)
|
|
1143
|
-
return;
|
|
1144
|
-
const run = apme.store.getRun(runId);
|
|
1145
|
-
if (!run)
|
|
1146
|
-
return;
|
|
1147
|
-
let category = run.taskCategory ?? null;
|
|
1148
|
-
if (!category) {
|
|
1149
|
-
try {
|
|
1150
|
-
const { classifyRun } = await import('./apme/classifier.js');
|
|
1151
|
-
const { category: c, signals } = classifyRun(apme.store, run.id);
|
|
1152
|
-
if (c && c !== 'unknown') {
|
|
1153
|
-
category = c;
|
|
1154
|
-
apme.store.updateRun(run.id, {
|
|
1155
|
-
taskCategory: c,
|
|
1156
|
-
taskSignals: JSON.stringify(signals),
|
|
1157
|
-
taskCategorySource: 'rule',
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
catch (err) {
|
|
1162
|
-
debug('APME', `mid-session classify failed: ${String(err)}`);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
if (category) {
|
|
1166
|
-
try {
|
|
1167
|
-
apme.store.updateTurn(turnId, { taskCategory: category });
|
|
1168
|
-
}
|
|
1169
|
-
catch { /* ignore */ }
|
|
1170
|
-
}
|
|
1171
|
-
const NON_CODE = new Set(['conversation', 'planning', 'research', 'review']);
|
|
1172
|
-
if (category && NON_CODE.has(category)) {
|
|
1173
|
-
apme.runner.enqueueTurn({ runId: run.id, turnId, category });
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
1183
|
// ===== Non-Claude agent APME wiring =====
|
|
1177
1184
|
// For OpenCode, Codex, OpenClaw: intercepts adapter timeline events to create
|
|
1178
1185
|
// APME turns with prompts and responses. Claude Code uses hooks instead.
|
|
1179
1186
|
function wireAgentApme(adapter, agentType, apme, core, ptyRingBuffer) {
|
|
1180
1187
|
const sid = core.sessionId;
|
|
1188
|
+
// Codex turn boundaries (hook-primary, PTY-fallback) live in their own
|
|
1189
|
+
// module. The class encapsulates the seven-state-variable PTY-only
|
|
1190
|
+
// machine plus the hook-fresh shortcut so non-codex sessions allocate
|
|
1191
|
+
// nothing here.
|
|
1192
|
+
const codexManager = agentType === 'codex-cli'
|
|
1193
|
+
? new CodexTurnManager(core, apme, ptyRingBuffer, sid, agentType)
|
|
1194
|
+
: null;
|
|
1181
1195
|
adapter.on('event', (evt) => {
|
|
1182
1196
|
// ── Timeline events (OpenCode / OpenClaw) ──
|
|
1183
1197
|
// Both adapters emit { source: 'timeline', entry: TimelineEntry }.
|
|
1184
1198
|
// The timeline adapter normalizes them to AgentDeck telemetry spans.
|
|
1185
1199
|
if (evt.source === 'timeline' && evt.entry) {
|
|
1186
1200
|
const entry = evt.entry;
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1201
|
+
// Upserts refine an already-ingested row (chat_start topic hint, async
|
|
1202
|
+
// LLM summary) — converting them to spans again would open a phantom
|
|
1203
|
+
// turn or overwrite the captured response with a summary label.
|
|
1204
|
+
if (evt.upsert)
|
|
1205
|
+
return;
|
|
1206
|
+
// Adapters bound to APME directly (OpenCode via setApmeSession) ingest
|
|
1207
|
+
// richer spans from their native event stream; converting their
|
|
1208
|
+
// timeline rows as well double-counted every turn and tool (the tool
|
|
1209
|
+
// sample dedupCore is counter-based, so storage-time dedup can't
|
|
1210
|
+
// collapse the second copy). Timeline conversion is the fallback for
|
|
1211
|
+
// sessions without a direct binding.
|
|
1212
|
+
const directApme = adapter
|
|
1213
|
+
.hasDirectApmeIngestion?.() === true;
|
|
1214
|
+
if (!directApme) {
|
|
1215
|
+
const ctx = makeApmeAdapterCtx(apme, sid, agentType);
|
|
1216
|
+
for (const span of timelineEntryToSpans(ctx, entry)) {
|
|
1217
|
+
apme.collector.ingestSpan(sid, span);
|
|
1218
|
+
}
|
|
1190
1219
|
}
|
|
1191
|
-
// Mid-session classification still runs once we've captured a response
|
|
1220
|
+
// Mid-session classification still runs once we've captured a response
|
|
1221
|
+
// (both ingestion paths write the turn response before this fires).
|
|
1192
1222
|
if (entry.type === 'chat_response') {
|
|
1193
1223
|
void classifyAndEnqueueTurn(apme, sid);
|
|
1194
1224
|
}
|
|
1195
1225
|
}
|
|
1196
|
-
// ── Codex: PTY
|
|
1197
|
-
if (
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
// PTY-derived prompt = synthetic turn_start span. We construct it
|
|
1203
|
-
// inline rather than reusing claudeHookToSpans because the source
|
|
1204
|
-
// is a parser event, not a hook payload.
|
|
1205
|
-
const span = {
|
|
1206
|
-
traceId: ctx.traceId,
|
|
1207
|
-
spanId: randomUUID(),
|
|
1208
|
-
parentSpanId: ctx.activeTurnId,
|
|
1209
|
-
name: spanNameForKind('turn_start'),
|
|
1210
|
-
kind: 'turn_start',
|
|
1211
|
-
ts: Date.now(),
|
|
1212
|
-
attributes: {
|
|
1213
|
-
'agentdeck.agent_type': ctx.agentType,
|
|
1214
|
-
'agentdeck.prompt_text': text,
|
|
1215
|
-
...(ctx.cwd ? { 'agentdeck.cwd': ctx.cwd } : {}),
|
|
1216
|
-
},
|
|
1217
|
-
};
|
|
1218
|
-
apme.collector.ingestSpan(sid, span);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
if (evt.event === 'spinner_stop') {
|
|
1222
|
-
setTimeout(() => {
|
|
1223
|
-
const tail = ptyRingBuffer.getTail(5000);
|
|
1224
|
-
const lines = tail.split('\n').map(l => l.trim()).filter(Boolean);
|
|
1225
|
-
// Filter out spinner/UI artifacts from tail (Codex PTY output)
|
|
1226
|
-
const clean = lines.filter(l => !/^[✢✳✶✻✽⏸⏵❯─>]/.test(l) &&
|
|
1227
|
-
!/planmode|plan\s*mode|shift\+tab|accept\s*edits/i.test(l) &&
|
|
1228
|
-
!/\?\s*for\s*shortcuts/.test(l));
|
|
1229
|
-
const response = clean.slice(-5).join('\n');
|
|
1230
|
-
if (response.length > 2) {
|
|
1231
|
-
const span = claudePtyResponseToSpan(ctx, response);
|
|
1232
|
-
if (span)
|
|
1233
|
-
apme.collector.ingestSpan(sid, span);
|
|
1234
|
-
void classifyAndEnqueueTurn(apme, sid);
|
|
1235
|
-
}
|
|
1236
|
-
}, 500);
|
|
1237
|
-
}
|
|
1226
|
+
// ── Codex: hook (primary) + PTY-parser fallback ──
|
|
1227
|
+
if (codexManager) {
|
|
1228
|
+
if (evt.source === 'hook')
|
|
1229
|
+
codexManager.onHookEvent(evt);
|
|
1230
|
+
else if (evt.source === 'parser')
|
|
1231
|
+
codexManager.onParserEvent(evt);
|
|
1238
1232
|
}
|
|
1239
1233
|
});
|
|
1234
|
+
if (codexManager) {
|
|
1235
|
+
core.onShutdown(() => codexManager.cleanup());
|
|
1236
|
+
}
|
|
1240
1237
|
}
|
|
1241
1238
|
/** Build the AdapterContext used by every APME ingestion adapter. The
|
|
1242
1239
|
* trace id mirrors the active run id so future OTLP exports tie spans
|
|
@@ -1251,6 +1248,9 @@ function makeApmeAdapterCtx(apme, sid, agentType) {
|
|
|
1251
1248
|
activeTurnId: apme.collector.getActiveTurnId(sid) ?? undefined,
|
|
1252
1249
|
};
|
|
1253
1250
|
}
|
|
1251
|
+
function isClaudeTaskNotificationPrompt(text) {
|
|
1252
|
+
return text.trim().toLowerCase().startsWith('<task-notification>');
|
|
1253
|
+
}
|
|
1254
1254
|
// ===== Claude Code timeline wiring =====
|
|
1255
1255
|
function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
|
|
1256
1256
|
let ccChatStart = null;
|
|
@@ -1266,6 +1266,10 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
|
|
|
1266
1266
|
ccPendingChatStartTimer = null;
|
|
1267
1267
|
}
|
|
1268
1268
|
ccPendingChatStart = false;
|
|
1269
|
+
if (isClaudeTaskNotificationPrompt(text)) {
|
|
1270
|
+
ccLastPromptText = null;
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1269
1273
|
ccLastPromptText = text || null;
|
|
1270
1274
|
const snippet = text.length > 500 ? text.slice(0, 497) + '...' : text;
|
|
1271
1275
|
const detail = text.length > 100 ? (text.length > 1000 ? text.slice(0, 1000) + '...' : text) : undefined;
|
|
@@ -1274,11 +1278,16 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
|
|
|
1274
1278
|
const proj = core.stateMachine.getSnapshot().projectName;
|
|
1275
1279
|
return proj ? `Prompt \u00B7 ${proj}` : 'Prompt sent';
|
|
1276
1280
|
})();
|
|
1281
|
+
// taskId/runId are filled in by BridgeCore's timeline attributor
|
|
1282
|
+
// (wireTimeline → setAttributor) from the active APME task for this
|
|
1283
|
+
// session, so managed-session chat rows nest under their task header
|
|
1284
|
+
// without threading the id through here.
|
|
1277
1285
|
core.bridgeTimeline.addEntry({
|
|
1278
1286
|
ts: ccChatStart ?? Date.now(), type: 'chat_start',
|
|
1279
1287
|
raw: snippet || fallbackRaw,
|
|
1280
1288
|
...(detail ? { detail } : {}),
|
|
1281
1289
|
agentType: 'claude-code',
|
|
1290
|
+
startedAt: ccChatStart ?? Date.now(),
|
|
1282
1291
|
});
|
|
1283
1292
|
};
|
|
1284
1293
|
/** Extract response text from PTY ringbuffer after ⏺ marker (same pattern as APME) */
|
|
@@ -1306,59 +1315,86 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
|
|
|
1306
1315
|
}
|
|
1307
1316
|
return clean.join('\n').trim();
|
|
1308
1317
|
};
|
|
1309
|
-
/**
|
|
1318
|
+
/**
|
|
1319
|
+
* Emit a single Claude turn-close row (shared by Stop hook and PTY fallback):
|
|
1320
|
+
* a `chat_response` when there is meaningful response text, otherwise a
|
|
1321
|
+
* `chat_end`. The two are mutually exclusive so a turn never produces both —
|
|
1322
|
+
* the paired dimmed `chat_end` used to fragment every turn into three rows on
|
|
1323
|
+
* the flat surfaces (plugin / TUI / timeline.json).
|
|
1324
|
+
*/
|
|
1310
1325
|
const emitCompletion = (responseText, duration, toolSummary) => {
|
|
1311
1326
|
const now = Date.now();
|
|
1327
|
+
const startedAt = ccChatStart ?? undefined;
|
|
1312
1328
|
// Change 5: emit chat_response if meaningful response text exists
|
|
1313
1329
|
if (responseText.length > 20) {
|
|
1314
1330
|
const respRaw = responseText.length > 200 ? responseText.slice(0, 197) + '...' : responseText;
|
|
1315
1331
|
core.bridgeTimeline.addEntry({
|
|
1316
1332
|
ts: now - 1, type: 'chat_response',
|
|
1317
1333
|
raw: cleanRawText(respRaw),
|
|
1318
|
-
|
|
1334
|
+
// Preserve markdown so the dashboard renderer can apply heading /
|
|
1335
|
+
// table / bold / code styles. cleanDetailText would have stripped
|
|
1336
|
+
// all of that before it ever reached the client.
|
|
1337
|
+
detail: prepareMarkdownDetail(responseText.slice(0, 3000)) || undefined,
|
|
1319
1338
|
agentType: 'claude-code',
|
|
1339
|
+
...(startedAt ? { startedAt } : {}),
|
|
1340
|
+
endedAt: now,
|
|
1320
1341
|
});
|
|
1321
1342
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1343
|
+
// Pick a summary label.
|
|
1344
|
+
// - 'topic' kind from response \u2192 real heuristic content
|
|
1345
|
+
// - 'topic' kind from prompt \u2192 still meaningful, mark as heuristic
|
|
1346
|
+
// - 'fallback' kind (politeness-only) or prompt snippet \u2192 mark as 'none'
|
|
1347
|
+
// (clients use this to decide whether the detail pane is worth showing)
|
|
1348
|
+
// - literal "Completed" only when prompt is also empty
|
|
1349
|
+
const respHint = responseText ? extractTopicHintWithKind(responseText) : { hint: null, kind: null };
|
|
1350
|
+
const promptHint = ccLastPromptText ? extractTopicHintWithKind(ccLastPromptText) : { hint: null, kind: null };
|
|
1351
|
+
let completedLabel;
|
|
1352
|
+
let summaryKind;
|
|
1353
|
+
if (respHint.kind === 'topic' && respHint.hint) {
|
|
1354
|
+
completedLabel = respHint.hint;
|
|
1355
|
+
summaryKind = 'heuristic';
|
|
1356
|
+
}
|
|
1357
|
+
else if (promptHint.kind === 'topic' && promptHint.hint) {
|
|
1358
|
+
completedLabel = promptHint.hint;
|
|
1359
|
+
summaryKind = 'heuristic';
|
|
1360
|
+
}
|
|
1361
|
+
else if (respHint.hint || promptHint.hint) {
|
|
1362
|
+
completedLabel = (respHint.hint || promptHint.hint);
|
|
1363
|
+
summaryKind = 'none';
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
const fallback = promptSnippetFallback(ccLastPromptText, 60);
|
|
1367
|
+
completedLabel = fallback ?? 'Completed';
|
|
1368
|
+
summaryKind = 'none';
|
|
1369
|
+
}
|
|
1325
1370
|
let summary = duration != null ? `${completedLabel} \u00B7 ${duration}s` : completedLabel;
|
|
1326
1371
|
if (toolSummary)
|
|
1327
1372
|
summary += ` \u00B7 ${toolSummary}`;
|
|
1328
1373
|
const chatEndDetail = responseText
|
|
1329
|
-
? (() => { const c =
|
|
1374
|
+
? (() => { const c = prepareMarkdownDetail(responseText); return c ? (c.length > 1000 ? c.slice(0, 1000) + '...' : c) : undefined; })()
|
|
1330
1375
|
: ccLastPromptText
|
|
1331
1376
|
? `Prompt: ${ccLastPromptText.length > 200 ? ccLastPromptText.slice(0, 200) + '...' : ccLastPromptText}`
|
|
1332
1377
|
: undefined;
|
|
1333
1378
|
const chatEndTs = now;
|
|
1334
1379
|
ccChatStart = null;
|
|
1335
1380
|
ccLastPromptText = null;
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
//
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
debug('timeline', `CC LLM summary: ${llmSummary}`);
|
|
1354
|
-
core.bridgeTimeline.upsertEntry({
|
|
1355
|
-
ts: chatEndTs, type: 'chat_end',
|
|
1356
|
-
raw: enrichedParts.join(' \u00B7 '),
|
|
1357
|
-
...(savedDetail ? { detail: savedDetail } : {}),
|
|
1358
|
-
agentType: 'claude-code',
|
|
1359
|
-
});
|
|
1360
|
-
}
|
|
1361
|
-
}).catch(() => { });
|
|
1381
|
+
// Only emit a chat_end turn-close row when there is NO chat_response to act
|
|
1382
|
+
// as the turn's completion row (a tool-only turn, or a Stop hook with no
|
|
1383
|
+
// last_assistant_message). When a chat_response exists it already marks the
|
|
1384
|
+
// turn complete; the extra dimmed "Completed · Ns · topic" row is redundant
|
|
1385
|
+
// metadata — the dashboard already drops it on render — and on the flat
|
|
1386
|
+
// surfaces (Stream Deck plugin / TUI / persisted timeline.json) it
|
|
1387
|
+
// fragmented every Claude turn into three rows. Keeping chat_start +
|
|
1388
|
+
// chat_response matches the cleaner OpenClaw turn shape.
|
|
1389
|
+
if (responseText.length <= 20) {
|
|
1390
|
+
core.bridgeTimeline.addEntry({
|
|
1391
|
+
ts: chatEndTs, type: 'chat_end', raw: summary,
|
|
1392
|
+
...(chatEndDetail ? { detail: chatEndDetail } : {}),
|
|
1393
|
+
agentType: 'claude-code',
|
|
1394
|
+
...(startedAt ? { startedAt } : {}),
|
|
1395
|
+
endedAt: chatEndTs,
|
|
1396
|
+
summaryKind,
|
|
1397
|
+
});
|
|
1362
1398
|
}
|
|
1363
1399
|
};
|
|
1364
1400
|
// Change 4: late upsert — user_prompt metadata arriving after 500ms timer
|