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