@agentdeck/bridge 1.0.20 → 1.0.22
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/dist/adapters/openclaw.d.ts +55 -1
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +278 -45
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/adb-reverse.d.ts +26 -3
- package/dist/adb-reverse.d.ts.map +1 -1
- package/dist/adb-reverse.js +26 -5
- package/dist/adb-reverse.js.map +1 -1
- package/dist/apme/adapters/openclaw-hook.d.ts +45 -1
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -1
- package/dist/apme/adapters/openclaw-hook.js +84 -4
- package/dist/apme/adapters/openclaw-hook.js.map +1 -1
- package/dist/apme/claude-transcript-reader.d.ts +54 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
- package/dist/apme/claude-transcript-reader.js +77 -0
- package/dist/apme/claude-transcript-reader.js.map +1 -1
- package/dist/apme/collector.d.ts +63 -5
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +147 -10
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/store.d.ts +23 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +81 -2
- package/dist/apme/store.js.map +1 -1
- package/dist/apme/types.d.ts +1 -1
- package/dist/apme/types.d.ts.map +1 -1
- package/dist/apme/types.js.map +1 -1
- package/dist/auth.d.ts +37 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +23 -0
- package/dist/auth.js.map +1 -1
- package/dist/ble-sync-spawn.d.ts +27 -0
- package/dist/ble-sync-spawn.d.ts.map +1 -1
- package/dist/ble-sync-spawn.js +32 -0
- package/dist/ble-sync-spawn.js.map +1 -1
- package/dist/bridge-core.d.ts +21 -4
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +52 -23
- package/dist/bridge-core.js.map +1 -1
- package/dist/claude-interrupt-marker.d.ts +31 -0
- package/dist/claude-interrupt-marker.d.ts.map +1 -0
- package/dist/claude-interrupt-marker.js +58 -0
- package/dist/claude-interrupt-marker.js.map +1 -0
- package/dist/claude-turn-watchdog.d.ts +23 -0
- package/dist/claude-turn-watchdog.d.ts.map +1 -1
- package/dist/claude-turn-watchdog.js +30 -4
- package/dist/claude-turn-watchdog.js.map +1 -1
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +411 -115
- package/dist/cli.js.map +1 -1
- package/dist/codex-rate-limits-live.d.ts +58 -9
- package/dist/codex-rate-limits-live.d.ts.map +1 -1
- package/dist/codex-rate-limits-live.js +64 -15
- package/dist/codex-rate-limits-live.js.map +1 -1
- package/dist/codex-rate-limits.d.ts +2 -2
- package/dist/codex-rate-limits.d.ts.map +1 -1
- package/dist/codex-rate-limits.js +34 -16
- package/dist/codex-rate-limits.js.map +1 -1
- package/dist/daemon-port.d.ts +90 -0
- package/dist/daemon-port.d.ts.map +1 -0
- package/dist/daemon-port.js +119 -0
- package/dist/daemon-port.js.map +1 -0
- package/dist/daemon-server.d.ts +60 -3
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +362 -109
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-settings.d.ts +46 -0
- package/dist/daemon-settings.d.ts.map +1 -0
- package/dist/daemon-settings.js +85 -0
- package/dist/daemon-settings.js.map +1 -0
- package/dist/daemon-takeover.d.ts +75 -0
- package/dist/daemon-takeover.d.ts.map +1 -0
- package/dist/daemon-takeover.js +137 -0
- package/dist/daemon-takeover.js.map +1 -0
- package/dist/daemon.js +1 -1
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +17 -0
- package/dist/hook-server.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +13 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -12
- package/dist/index.js.map +1 -1
- package/dist/kiro-diagnostics.d.ts +90 -0
- package/dist/kiro-diagnostics.d.ts.map +1 -0
- package/dist/kiro-diagnostics.js +278 -0
- package/dist/kiro-diagnostics.js.map +1 -0
- package/dist/kiro-session.d.ts +113 -0
- package/dist/kiro-session.d.ts.map +1 -0
- package/dist/kiro-session.js +688 -0
- package/dist/kiro-session.js.map +1 -0
- package/dist/kiro-timeline-feed.d.ts +45 -0
- package/dist/kiro-timeline-feed.d.ts.map +1 -0
- package/dist/kiro-timeline-feed.js +116 -0
- package/dist/kiro-timeline-feed.js.map +1 -0
- package/dist/kiro-transcript-timeline.d.ts +57 -0
- package/dist/kiro-transcript-timeline.d.ts.map +1 -0
- package/dist/kiro-transcript-timeline.js +183 -0
- package/dist/kiro-transcript-timeline.js.map +1 -0
- package/dist/linux-service.d.ts +2 -1
- package/dist/linux-service.d.ts.map +1 -1
- package/dist/linux-service.js +8 -3
- package/dist/linux-service.js.map +1 -1
- package/dist/mdns-discover.d.ts +27 -0
- package/dist/mdns-discover.d.ts.map +1 -1
- package/dist/mdns-discover.js +60 -7
- package/dist/mdns-discover.js.map +1 -1
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +16 -3
- package/dist/mdns.js.map +1 -1
- package/dist/modules/adb-module.d.ts.map +1 -1
- package/dist/modules/adb-module.js +5 -2
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/modules/index.d.ts +2 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +1 -0
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/types.d.ts +34 -9
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/modules/types.js +34 -1
- package/dist/modules/types.js.map +1 -1
- package/dist/network-posture.d.ts +67 -0
- package/dist/network-posture.d.ts.map +1 -0
- package/dist/network-posture.js +100 -0
- package/dist/network-posture.js.map +1 -0
- package/dist/observed-steering.d.ts +14 -0
- package/dist/observed-steering.d.ts.map +1 -1
- package/dist/observed-steering.js +18 -0
- package/dist/observed-steering.js.map +1 -1
- package/dist/observed-turn-watchdogs.d.ts +84 -0
- package/dist/observed-turn-watchdogs.d.ts.map +1 -0
- package/dist/observed-turn-watchdogs.js +134 -0
- package/dist/observed-turn-watchdogs.js.map +1 -0
- package/dist/openclaw-session.d.ts +10 -0
- package/dist/openclaw-session.d.ts.map +1 -1
- package/dist/openclaw-session.js +12 -0
- package/dist/openclaw-session.js.map +1 -1
- package/dist/passive-observer.d.ts +19 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +208 -24
- package/dist/passive-observer.js.map +1 -1
- package/dist/pixoo/micro-glyphs.d.ts +1 -1
- package/dist/pixoo/micro-glyphs.d.ts.map +1 -1
- package/dist/pixoo/micro-glyphs.js +2 -0
- package/dist/pixoo/micro-glyphs.js.map +1 -1
- package/dist/pixoo/official-dot-glyphs.generated.d.ts +1 -1
- package/dist/pixoo/official-dot-glyphs.generated.d.ts.map +1 -1
- package/dist/pixoo/official-dot-glyphs.generated.js +47 -0
- package/dist/pixoo/official-dot-glyphs.generated.js.map +1 -1
- package/dist/pixoo/pixoo-discover.d.ts +8 -1
- package/dist/pixoo/pixoo-discover.d.ts.map +1 -1
- package/dist/pixoo/pixoo-discover.js +15 -8
- package/dist/pixoo/pixoo-discover.js.map +1 -1
- package/dist/pixoo/pixoo-renderer.d.ts +4 -2
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +105 -36
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pixoo/pixoo-settings.d.ts +19 -2
- package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
- package/dist/pixoo/pixoo-settings.js +22 -4
- package/dist/pixoo/pixoo-settings.js.map +1 -1
- package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
- package/dist/pixoo/pixoo-sprites.js +2 -0
- package/dist/pixoo/pixoo-sprites.js.map +1 -1
- package/dist/python-ble-runtime.d.ts +2 -0
- package/dist/python-ble-runtime.d.ts.map +1 -1
- package/dist/python-ble-runtime.js +101 -7
- package/dist/python-ble-runtime.js.map +1 -1
- package/dist/session-aggregator.d.ts +9 -1
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +1 -1
- 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 +126 -19
- package/dist/session-registry.js.map +1 -1
- package/dist/subagent-timeline.d.ts +38 -7
- package/dist/subagent-timeline.d.ts.map +1 -1
- package/dist/subagent-timeline.js +154 -23
- package/dist/subagent-timeline.js.map +1 -1
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
- package/dist/timebox/timebox-daemon-sync.js +11 -1
- package/dist/timebox/timebox-daemon-sync.js.map +1 -1
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +26 -1
- package/dist/timeline-store.js.map +1 -1
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +13 -1
- package/dist/tui/renderer.js.map +1 -1
- package/dist/tui/terrarium.d.ts.map +1 -1
- package/dist/tui/terrarium.js +10 -5
- package/dist/tui/terrarium.js.map +1 -1
- package/dist/usage-api.d.ts +28 -1
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +74 -33
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +2 -17
- package/dist/usage-event.js.map +1 -1
- package/dist/utils/redact-secrets.d.ts +3 -0
- package/dist/utils/redact-secrets.d.ts.map +1 -0
- package/dist/utils/redact-secrets.js +29 -0
- package/dist/utils/redact-secrets.js.map +1 -0
- package/dist/windows-service.d.ts +2 -1
- package/dist/windows-service.d.ts.map +1 -1
- package/dist/windows-service.js +6 -3
- package/dist/windows-service.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-transcript-reader.d.ts","sourceRoot":"","sources":["../../src/apme/claude-transcript-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"claude-transcript-reader.d.ts","sourceRoot":"","sources":["../../src/apme/claude-transcript-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,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;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsB7E;AAED,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,qEAAqE;IACrE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;2EACuE;IACvE,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;gCAWgC;AAChC,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAExD;;qDAEqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;mEAC+D;IAC/D,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAkC5E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CA8BrG"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { readFileSync, openSync, readSync, fstatSync, closeSync } from 'fs';
|
|
19
19
|
import { debug } from '../logger.js';
|
|
20
|
+
import { isClaudeInterruptRecord } from '../claude-interrupt-marker.js';
|
|
20
21
|
/**
|
|
21
22
|
* Parse the last user→assistant turn from a Claude Code transcript JSONL.
|
|
22
23
|
*
|
|
@@ -123,6 +124,19 @@ export function readModelFromTranscript(transcriptPath) {
|
|
|
123
124
|
}
|
|
124
125
|
return null;
|
|
125
126
|
}
|
|
127
|
+
/** `stop_reason` Claude Code writes when the CLIENT ended the turn instead of
|
|
128
|
+
* the model: usage limit, expired auth, credit limit, an API 429/529. The
|
|
129
|
+
* record carries the user-facing message ("You've hit your session limit ·
|
|
130
|
+
* resets 5:50pm") and NO Stop hook follows it — so a turn that ends this way
|
|
131
|
+
* owes no Stop and must not be charged to the dropped-hook rate.
|
|
132
|
+
*
|
|
133
|
+
* Measured before it was trusted: across 211 local transcripts holding 61,281
|
|
134
|
+
* assistant records, `stop_reason` was `tool_use` 58,765 times, `end_turn`
|
|
135
|
+
* 2,513 times, and `stop_sequence` 37 times — every one of those 37 a client
|
|
136
|
+
* abort message, none followed by a `stop_hook_summary` record, and none
|
|
137
|
+
* followed by further assistant work in the same turn. There is no benign
|
|
138
|
+
* shape to confuse it with. */
|
|
139
|
+
export const CLIENT_ABORT_STOP_REASON = 'stop_sequence';
|
|
126
140
|
/**
|
|
127
141
|
* Probe whether the transcript's most recent turn has finished. A completed
|
|
128
142
|
* turn's last message-bearing record is `role: "assistant"` with
|
|
@@ -131,6 +145,11 @@ export function readModelFromTranscript(transcriptPath) {
|
|
|
131
145
|
* can trail the assistant message, so the walk skips records without a
|
|
132
146
|
* `message.role`. Never throws; returns null when unreadable/empty.
|
|
133
147
|
*
|
|
148
|
+
* The OTHER way a turn ends is the user pressing ESC, which produces no
|
|
149
|
+
* assistant `end_turn` at all — just the interrupt marker record — so
|
|
150
|
+
* `interrupted` is reported alongside, and a caller that only checked for
|
|
151
|
+
* `end_turn` would wait forever on a turn nothing will ever close.
|
|
152
|
+
*
|
|
134
153
|
* Used by the turn watchdog to close a turn whose Stop hook was dropped —
|
|
135
154
|
* the caller must additionally check `timestampMs` against its own turn-open
|
|
136
155
|
* time, because at turn start the tail still shows the PREVIOUS turn's
|
|
@@ -167,10 +186,68 @@ export function readTurnEndProbe(transcriptPath) {
|
|
|
167
186
|
role,
|
|
168
187
|
stopReason: rec.message?.stop_reason ?? null,
|
|
169
188
|
timestampMs: Number.isFinite(ts) ? ts : null,
|
|
189
|
+
interrupted: isClaudeInterruptRecord(rec),
|
|
170
190
|
};
|
|
171
191
|
}
|
|
172
192
|
return null;
|
|
173
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Everything a bounded tail read can say about a turn that opened at `sinceMs`
|
|
196
|
+
* and is still open. Returns null when the transcript is unreadable — the
|
|
197
|
+
* absence of evidence, which callers must never resolve as a verdict.
|
|
198
|
+
*
|
|
199
|
+
* Three questions, one read, because the close path that runs at the next
|
|
200
|
+
* prompt has to ask all of them and each answer is a different bucket:
|
|
201
|
+
*
|
|
202
|
+
* - Did the user cancel? `readTurnEndProbe` only sees the ESC marker while it
|
|
203
|
+
* is still the tail, and the common shape is "cancel, then immediately
|
|
204
|
+
* retype", which buries it under the new user message within seconds.
|
|
205
|
+
* - Did the CLIENT abort (usage limit, auth, API error)? Same burial, and
|
|
206
|
+
* Claude Code owes no Stop for it either.
|
|
207
|
+
* - Did the assistant write anything at all? If not, this prompt never got a
|
|
208
|
+
* turn of its own — a second prompt landed first and one model turn served
|
|
209
|
+
* both, so the displaced row is a counting artifact, not a lost hook.
|
|
210
|
+
*
|
|
211
|
+
* The backward walk stops at the first record older than the window, so
|
|
212
|
+
* evidence belonging to an earlier turn can neither be found nor paid for.
|
|
213
|
+
*/
|
|
214
|
+
export function readOpenTurnEvidence(transcriptPath, sinceMs) {
|
|
215
|
+
const EVIDENCE_TAIL_BYTES = 256 * 1024;
|
|
216
|
+
const tail = readTailString(transcriptPath, EVIDENCE_TAIL_BYTES);
|
|
217
|
+
if (tail == null)
|
|
218
|
+
return null;
|
|
219
|
+
const evidence = { interruptedAt: null, abortedAt: null, sawAssistant: false };
|
|
220
|
+
const lines = tail.split('\n');
|
|
221
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
222
|
+
const line = lines[i];
|
|
223
|
+
if (!line.trim())
|
|
224
|
+
continue;
|
|
225
|
+
let rec;
|
|
226
|
+
try {
|
|
227
|
+
rec = JSON.parse(line);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
continue; // skip malformed (possibly truncated) line
|
|
231
|
+
}
|
|
232
|
+
const ts = rec.timestamp ? Date.parse(rec.timestamp) : NaN;
|
|
233
|
+
// A record with no usable timestamp cannot be placed in or out of the
|
|
234
|
+
// window, so it is skipped rather than ending the walk — ending here
|
|
235
|
+
// would let one stampless line hide every marker behind it.
|
|
236
|
+
if (!Number.isFinite(ts))
|
|
237
|
+
continue;
|
|
238
|
+
if (ts < sinceMs)
|
|
239
|
+
break;
|
|
240
|
+
if (evidence.interruptedAt == null && isClaudeInterruptRecord(rec))
|
|
241
|
+
evidence.interruptedAt = ts;
|
|
242
|
+
if (rec.message?.role === 'assistant') {
|
|
243
|
+
evidence.sawAssistant = true;
|
|
244
|
+
if (evidence.abortedAt == null && rec.message?.stop_reason === CLIENT_ABORT_STOP_REASON) {
|
|
245
|
+
evidence.abortedAt = ts;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return evidence;
|
|
250
|
+
}
|
|
174
251
|
/** Read at most `maxBytes` from the end of a file without loading the rest.
|
|
175
252
|
* Returns null when the file is unreadable. */
|
|
176
253
|
function readTailString(path, maxBytes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-transcript-reader.js","sourceRoot":"","sources":["../../src/apme/claude-transcript-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-transcript-reader.js","sourceRoot":"","sources":["../../src/apme/claude-transcript-reader.ts"],"names":[],"mappings":"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"}
|
package/dist/apme/collector.d.ts
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
import type { UsageSnapshot } from '../types.js';
|
|
15
15
|
import type { SessionEntry } from '../session-registry.js';
|
|
16
16
|
import type { ApmeStore } from './store.js';
|
|
17
|
-
import type { AgentType, TelemetrySpan, ApmeSampleEventRow } from '@agentdeck/shared';
|
|
17
|
+
import type { AgentType, TelemetrySpan, ApmeSampleEventRow, TurnEndSource } from '@agentdeck/shared';
|
|
18
18
|
import type { ApmeHwSampler } from './hw-sampler.js';
|
|
19
|
+
import { type OpenTurnEvidence } from './claude-transcript-reader.js';
|
|
19
20
|
export interface OpenRunInput {
|
|
20
21
|
sessionId: string;
|
|
21
22
|
agentType: AgentType;
|
|
@@ -87,6 +88,10 @@ export type OnTaskMilestone = (args: {
|
|
|
87
88
|
export declare class ApmeCollector {
|
|
88
89
|
private readonly store;
|
|
89
90
|
private readonly hwSampler?;
|
|
91
|
+
/** What the transcript proves about a turn open since a timestamp — cancel,
|
|
92
|
+
* client abort, or never having run at all. Injectable for tests; the
|
|
93
|
+
* default reads the Claude transcript tail once for all three. */
|
|
94
|
+
private readonly openTurnProbe;
|
|
90
95
|
private readonly sessionToRun;
|
|
91
96
|
private readonly sessionToAgentType;
|
|
92
97
|
private readonly sessionToTurn;
|
|
@@ -109,7 +114,11 @@ export declare class ApmeCollector {
|
|
|
109
114
|
/** Last milestone key (`taskId:turnIndex`) per session — a turn can carry
|
|
110
115
|
* several all-completed TodoWrite calls; surface only the first. */
|
|
111
116
|
private readonly sessionToLastMilestone;
|
|
112
|
-
constructor(store: ApmeStore, hwSampler?: ApmeHwSampler | undefined
|
|
117
|
+
constructor(store: ApmeStore, hwSampler?: ApmeHwSampler | undefined,
|
|
118
|
+
/** What the transcript proves about a turn open since a timestamp — cancel,
|
|
119
|
+
* client abort, or never having run at all. Injectable for tests; the
|
|
120
|
+
* default reads the Claude transcript tail once for all three. */
|
|
121
|
+
openTurnProbe?: (transcriptPath: string, sinceMs: number) => OpenTurnEvidence | null);
|
|
113
122
|
/** Start a new run and return its id. Safe to call if store disabled (returns ''). */
|
|
114
123
|
openRun(input: OpenRunInput): string;
|
|
115
124
|
/** Record a hook event as a step + manage turn lifecycle.
|
|
@@ -128,8 +137,57 @@ export declare class ApmeCollector {
|
|
|
128
137
|
* to finalize the turn row immediately rather than wait for the next
|
|
129
138
|
* UserPromptSubmit / closeRun to flush endedAt + buffered counters.
|
|
130
139
|
* Idempotent — no-op when no active turn for the session. */
|
|
131
|
-
closeTurnForSession(sessionId: string): void;
|
|
132
|
-
/**
|
|
140
|
+
closeTurnForSession(sessionId: string, source?: TurnEndSource): void;
|
|
141
|
+
/** The agent reported its turn finished (Claude `Stop`, real or synthetic).
|
|
142
|
+
*
|
|
143
|
+
* Two jobs, both of which need to happen at the Stop and not one prompt
|
|
144
|
+
* later. First, `ended_at` is stamped HERE, so a turn's duration is the
|
|
145
|
+
* agent's working time instead of that plus however long the user took to
|
|
146
|
+
* type the next thing — before this, an overnight gap between prompts was
|
|
147
|
+
* indistinguishable from a 5-hour turn, and every duration-derived
|
|
148
|
+
* efficiency number was reading user think time. Second, `end_source`
|
|
149
|
+
* records WHICH signal closed it, which is the whole Stop-delivery
|
|
150
|
+
* instrument: a turn that reaches the next prompt still open is a Stop that
|
|
151
|
+
* was lost, and one closed by `synthetic_stop` is a Stop that was lost and
|
|
152
|
+
* recovered. Neither is visible if every turn closes the same way.
|
|
153
|
+
*
|
|
154
|
+
* Callers must record the response BEFORE calling this — `setTurnResponse`
|
|
155
|
+
* does fall back to the last-closed turn, but the sample trajectory and the
|
|
156
|
+
* `response_kind` tag read cleaner while the turn is still active.
|
|
157
|
+
*
|
|
158
|
+
* Idempotent: a duplicate or late Stop finds no active turn and no-ops, so
|
|
159
|
+
* a synthetic Stop racing a real one cannot overwrite the real attribution. */
|
|
160
|
+
noteTurnStop(sessionId: string, opts?: {
|
|
161
|
+
synthetic?: boolean;
|
|
162
|
+
interrupted?: boolean;
|
|
163
|
+
aborted?: boolean;
|
|
164
|
+
}): void;
|
|
165
|
+
/** Attribute a turn that is still open when the next prompt arrives.
|
|
166
|
+
*
|
|
167
|
+
* `next_prompt` is the only bucket that means "a Stop was owed and never
|
|
168
|
+
* came", so everything that reaches here for some other reason has to be
|
|
169
|
+
* told apart before it inflates the very loss rate this instrument
|
|
170
|
+
* measures. Three do, and one bounded tail read answers all three — on this
|
|
171
|
+
* path only, since a healthy turn is closed by its Stop long before this
|
|
172
|
+
* runs:
|
|
173
|
+
*
|
|
174
|
+
* - A CANCEL. The watchdog catches one only while the interrupt marker is
|
|
175
|
+
* still the transcript's tail; the commonest ESC shape — cancel, then
|
|
176
|
+
* retype straight away — buries it under the new user message in seconds
|
|
177
|
+
* and fires a fresh `UserPromptSubmit`.
|
|
178
|
+
* - A CLIENT ABORT (usage limit, expired auth, API error). The watchdog
|
|
179
|
+
* closes those at +15s, but not if it was swept for idleness first
|
|
180
|
+
* (30 min) or the daemon restarted mid-turn — and an abort is exactly
|
|
181
|
+
* the shape that then sits open for hours.
|
|
182
|
+
* - A turn that NEVER RAN. Queued prompts and `<task-notification>`
|
|
183
|
+
* injections arrive in pairs ~130 ms apart; Claude serves both with one
|
|
184
|
+
* model turn and owes exactly one Stop, which closes the LAST of them.
|
|
185
|
+
* A displaced row with no assistant record behind it at all is that
|
|
186
|
+
* artifact, not a lost hook. */
|
|
187
|
+
private resolveDisplacedTurnSource;
|
|
188
|
+
/** Close the current turn for a session (called on Stop, new prompt, or
|
|
189
|
+
* session end). `source` records which of those it was — see
|
|
190
|
+
* `TurnEndSource`. */
|
|
133
191
|
private closeTurn;
|
|
134
192
|
/** Get the current active turn ID for a session (if any). */
|
|
135
193
|
getActiveTurnId(sessionId: string): string | null;
|
|
@@ -233,7 +291,7 @@ export declare class ApmeCollector {
|
|
|
233
291
|
/** Finalize a run. Returns the runId so callers can enqueue evaluation.
|
|
234
292
|
* Empty runs (no prompts, no steps, no turns) are deleted — they're just
|
|
235
293
|
* connection noise and clutter the dashboard. */
|
|
236
|
-
closeRun(sessionId: string, exitCode?: number, projectPath?: string): string | null;
|
|
294
|
+
closeRun(sessionId: string, exitCode?: number, projectPath?: string, turnEndSource?: TurnEndSource): string | null;
|
|
237
295
|
/** Save the git diff produced by this run as an artifact file. */
|
|
238
296
|
private saveDiffArtifact;
|
|
239
297
|
/** Translate a `SessionEntry` + optional extras into an OpenRunInput. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/apme/collector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,kBAAkB,EAAuB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/apme/collector.ts"],"names":[],"mappings":"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"}
|
package/dist/apme/collector.js
CHANGED
|
@@ -20,14 +20,22 @@ import { debug } from '../logger.js';
|
|
|
20
20
|
import { resolveProjectName } from '../utils/project-name.js';
|
|
21
21
|
import { priceUsd, providerFor } from '@agentdeck/shared';
|
|
22
22
|
import { classifyRunSmart, computeSignals, classify } from './classifier.js';
|
|
23
|
+
import { readOpenTurnEvidence } from './claude-transcript-reader.js';
|
|
23
24
|
/** A turn_start with the same prompt landing on a fresh, still-empty turn
|
|
24
25
|
* within this window is treated as a transport echo (e.g. OpenClaw
|
|
25
26
|
* `chat.send` span + the gateway's `session.message` role=user re-delivery
|
|
26
27
|
* of the same text), not a new turn. */
|
|
27
28
|
const DUPLICATE_TURN_OPEN_WINDOW_MS = 15_000;
|
|
29
|
+
/** Clock slack when asking whether an interrupt marker belongs to THIS turn.
|
|
30
|
+
* The turn's `startedAt` is the daemon's clock at the hook POST while the
|
|
31
|
+
* marker carries Claude Code's own record timestamp; without a little give,
|
|
32
|
+
* a cancel milliseconds into a turn reads as belonging to the previous one.
|
|
33
|
+
* Matches the watchdog's `TURN_OPEN_SLACK_MS` — same comparison, same risk. */
|
|
34
|
+
const TURN_INTERRUPT_SLACK_MS = 2_000;
|
|
28
35
|
export class ApmeCollector {
|
|
29
36
|
store;
|
|
30
37
|
hwSampler;
|
|
38
|
+
openTurnProbe;
|
|
31
39
|
sessionToRun = new Map(); // sessionId → runId
|
|
32
40
|
sessionToAgentType = new Map(); // sessionId → agentType (survives closeRun for late-attributed timeline rows)
|
|
33
41
|
sessionToTurn = new Map(); // sessionId → current turn
|
|
@@ -50,9 +58,14 @@ export class ApmeCollector {
|
|
|
50
58
|
/** Last milestone key (`taskId:turnIndex`) per session — a turn can carry
|
|
51
59
|
* several all-completed TodoWrite calls; surface only the first. */
|
|
52
60
|
sessionToLastMilestone = new Map();
|
|
53
|
-
constructor(store, hwSampler
|
|
61
|
+
constructor(store, hwSampler,
|
|
62
|
+
/** What the transcript proves about a turn open since a timestamp — cancel,
|
|
63
|
+
* client abort, or never having run at all. Injectable for tests; the
|
|
64
|
+
* default reads the Claude transcript tail once for all three. */
|
|
65
|
+
openTurnProbe = readOpenTurnEvidence) {
|
|
54
66
|
this.store = store;
|
|
55
67
|
this.hwSampler = hwSampler;
|
|
68
|
+
this.openTurnProbe = openTurnProbe;
|
|
56
69
|
}
|
|
57
70
|
/** Start a new run and return its id. Safe to call if store disabled (returns ''). */
|
|
58
71
|
openRun(input) {
|
|
@@ -133,7 +146,11 @@ export class ApmeCollector {
|
|
|
133
146
|
prevIndex = lastIdx;
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
|
-
|
|
149
|
+
// A turn still open when the NEXT prompt arrives never got its Stop —
|
|
150
|
+
// and no watchdog recovered it either, or the synthetic Stop would have
|
|
151
|
+
// closed it already. That is the unrecovered-loss bucket, UNLESS the
|
|
152
|
+
// user cancelled it, in which case no Stop was ever owed.
|
|
153
|
+
this.closeTurn(sessionId, this.resolveDisplacedTurnSource(sessionId, data));
|
|
137
154
|
// Open new turn
|
|
138
155
|
const turnIndex = prevIndex + 1;
|
|
139
156
|
const run = this.store.getRun(runId);
|
|
@@ -275,11 +292,107 @@ export class ApmeCollector {
|
|
|
275
292
|
* to finalize the turn row immediately rather than wait for the next
|
|
276
293
|
* UserPromptSubmit / closeRun to flush endedAt + buffered counters.
|
|
277
294
|
* Idempotent — no-op when no active turn for the session. */
|
|
278
|
-
closeTurnForSession(sessionId) {
|
|
279
|
-
this.closeTurn(sessionId);
|
|
295
|
+
closeTurnForSession(sessionId, source = 'stop') {
|
|
296
|
+
this.closeTurn(sessionId, source);
|
|
280
297
|
}
|
|
281
|
-
/**
|
|
282
|
-
|
|
298
|
+
/** The agent reported its turn finished (Claude `Stop`, real or synthetic).
|
|
299
|
+
*
|
|
300
|
+
* Two jobs, both of which need to happen at the Stop and not one prompt
|
|
301
|
+
* later. First, `ended_at` is stamped HERE, so a turn's duration is the
|
|
302
|
+
* agent's working time instead of that plus however long the user took to
|
|
303
|
+
* type the next thing — before this, an overnight gap between prompts was
|
|
304
|
+
* indistinguishable from a 5-hour turn, and every duration-derived
|
|
305
|
+
* efficiency number was reading user think time. Second, `end_source`
|
|
306
|
+
* records WHICH signal closed it, which is the whole Stop-delivery
|
|
307
|
+
* instrument: a turn that reaches the next prompt still open is a Stop that
|
|
308
|
+
* was lost, and one closed by `synthetic_stop` is a Stop that was lost and
|
|
309
|
+
* recovered. Neither is visible if every turn closes the same way.
|
|
310
|
+
*
|
|
311
|
+
* Callers must record the response BEFORE calling this — `setTurnResponse`
|
|
312
|
+
* does fall back to the last-closed turn, but the sample trajectory and the
|
|
313
|
+
* `response_kind` tag read cleaner while the turn is still active.
|
|
314
|
+
*
|
|
315
|
+
* Idempotent: a duplicate or late Stop finds no active turn and no-ops, so
|
|
316
|
+
* a synthetic Stop racing a real one cannot overwrite the real attribution. */
|
|
317
|
+
noteTurnStop(sessionId, opts = {}) {
|
|
318
|
+
if (!this.store.enabled)
|
|
319
|
+
return;
|
|
320
|
+
// `interrupted` and `aborted` both outrank `synthetic`: neither ending
|
|
321
|
+
// produces a real Stop, so each can only ever arrive AS a synthetic one,
|
|
322
|
+
// and reading the flags the other way round would file every user cancel
|
|
323
|
+
// and every usage-limit abort as a dropped hook. Cancel wins over abort on
|
|
324
|
+
// the impossible both-set case, for the same reason it wins over synthetic
|
|
325
|
+
// — it is the narrower, user-caused claim.
|
|
326
|
+
const source = opts.interrupted ? 'interrupted'
|
|
327
|
+
: opts.aborted ? 'aborted'
|
|
328
|
+
: opts.synthetic ? 'synthetic_stop'
|
|
329
|
+
: 'stop';
|
|
330
|
+
this.closeTurn(sessionId, source);
|
|
331
|
+
}
|
|
332
|
+
/** Attribute a turn that is still open when the next prompt arrives.
|
|
333
|
+
*
|
|
334
|
+
* `next_prompt` is the only bucket that means "a Stop was owed and never
|
|
335
|
+
* came", so everything that reaches here for some other reason has to be
|
|
336
|
+
* told apart before it inflates the very loss rate this instrument
|
|
337
|
+
* measures. Three do, and one bounded tail read answers all three — on this
|
|
338
|
+
* path only, since a healthy turn is closed by its Stop long before this
|
|
339
|
+
* runs:
|
|
340
|
+
*
|
|
341
|
+
* - A CANCEL. The watchdog catches one only while the interrupt marker is
|
|
342
|
+
* still the transcript's tail; the commonest ESC shape — cancel, then
|
|
343
|
+
* retype straight away — buries it under the new user message in seconds
|
|
344
|
+
* and fires a fresh `UserPromptSubmit`.
|
|
345
|
+
* - A CLIENT ABORT (usage limit, expired auth, API error). The watchdog
|
|
346
|
+
* closes those at +15s, but not if it was swept for idleness first
|
|
347
|
+
* (30 min) or the daemon restarted mid-turn — and an abort is exactly
|
|
348
|
+
* the shape that then sits open for hours.
|
|
349
|
+
* - A turn that NEVER RAN. Queued prompts and `<task-notification>`
|
|
350
|
+
* injections arrive in pairs ~130 ms apart; Claude serves both with one
|
|
351
|
+
* model turn and owes exactly one Stop, which closes the LAST of them.
|
|
352
|
+
* A displaced row with no assistant record behind it at all is that
|
|
353
|
+
* artifact, not a lost hook. */
|
|
354
|
+
resolveDisplacedTurnSource(sessionId, data) {
|
|
355
|
+
const turn = this.sessionToTurn.get(sessionId);
|
|
356
|
+
// No open turn: closeTurn no-ops, so the value is immaterial.
|
|
357
|
+
if (!turn)
|
|
358
|
+
return 'next_prompt';
|
|
359
|
+
// Claude-only: the marker and the JSONL shape are Claude Code's.
|
|
360
|
+
if (this.sessionToAgentType.get(sessionId) !== 'claude-code')
|
|
361
|
+
return 'next_prompt';
|
|
362
|
+
const transcriptPath = typeof data.transcript_path === 'string' ? data.transcript_path : '';
|
|
363
|
+
if (!transcriptPath)
|
|
364
|
+
return 'next_prompt';
|
|
365
|
+
try {
|
|
366
|
+
const evidence = this.openTurnProbe(transcriptPath, turn.startedAt - TURN_INTERRUPT_SLACK_MS);
|
|
367
|
+
// An unreadable transcript is no evidence of anything — never guess.
|
|
368
|
+
if (!evidence)
|
|
369
|
+
return 'next_prompt';
|
|
370
|
+
if (evidence.interruptedAt != null)
|
|
371
|
+
return 'interrupted';
|
|
372
|
+
if (evidence.abortedAt != null)
|
|
373
|
+
return 'aborted';
|
|
374
|
+
// Ordered last on purpose: a cancelled or aborted turn that also wrote
|
|
375
|
+
// nothing is still a cancel or an abort, not a superseded prompt.
|
|
376
|
+
//
|
|
377
|
+
// Two independent facts must agree before a turn is called one that
|
|
378
|
+
// never ran, because the transcript walk alone is not quite enough for
|
|
379
|
+
// this claim: it stops at the first record older than the window, and
|
|
380
|
+
// Claude Code's JSONL is not strictly ordered (auxiliary records land
|
|
381
|
+
// tens of ms out of sequence), so one inverted stamp near the tail could
|
|
382
|
+
// truncate the walk and hide the assistant work behind it. A turn that
|
|
383
|
+
// called tools demonstrably ran whatever the transcript says.
|
|
384
|
+
if (!evidence.sawAssistant && turn.toolCalls === 0)
|
|
385
|
+
return 'superseded';
|
|
386
|
+
return 'next_prompt';
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
return 'next_prompt';
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/** Close the current turn for a session (called on Stop, new prompt, or
|
|
393
|
+
* session end). `source` records which of those it was — see
|
|
394
|
+
* `TurnEndSource`. */
|
|
395
|
+
closeTurn(sessionId, source) {
|
|
283
396
|
const turn = this.sessionToTurn.get(sessionId);
|
|
284
397
|
if (!turn)
|
|
285
398
|
return;
|
|
@@ -295,8 +408,9 @@ export class ApmeCollector {
|
|
|
295
408
|
filesModified: turn.filesModified,
|
|
296
409
|
filesCreated: turn.filesCreated,
|
|
297
410
|
gitAfter,
|
|
411
|
+
endSource: source,
|
|
298
412
|
});
|
|
299
|
-
debug('APME', `closeTurn ${turn.id.slice(0, 8)} index=${turn.index} tools=${turn.toolCalls}`);
|
|
413
|
+
debug('APME', `closeTurn ${turn.id.slice(0, 8)} index=${turn.index} tools=${turn.toolCalls} src=${source}`);
|
|
300
414
|
}
|
|
301
415
|
catch (err) {
|
|
302
416
|
debug('APME', `closeTurn failed: ${String(err)}`);
|
|
@@ -792,6 +906,29 @@ export class ApmeCollector {
|
|
|
792
906
|
}
|
|
793
907
|
return;
|
|
794
908
|
}
|
|
909
|
+
case 'agent_error': {
|
|
910
|
+
// Record the failure in the trajectory. Deliberately does NOT close
|
|
911
|
+
// the turn or the task: the agent may retry the same prompt, and the
|
|
912
|
+
// adapter owns the boundary (OpenClaw re-arms its idle-gap timer).
|
|
913
|
+
// Without this the judge sees a turn_start with no response and no
|
|
914
|
+
// reason — indistinguishable from a dropped event.
|
|
915
|
+
const label = a['agentdeck.error_label']?.trim();
|
|
916
|
+
if (!label)
|
|
917
|
+
return;
|
|
918
|
+
const detail = a['agentdeck.error_detail']?.trim();
|
|
919
|
+
const task = this.sessionToTask.get(sessionId);
|
|
920
|
+
const turnIndex = this.sessionToTurn.get(sessionId)?.index;
|
|
921
|
+
if (!task || turnIndex === undefined) {
|
|
922
|
+
debug('APME', `agent_error span dropped: no open task/turn for ${sessionId}`);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
this.appendSampleEvent({ taskId: task.id, runId: task.runId, turnIndex, turnId: this.turnIdFor(sessionId) }, {
|
|
926
|
+
kind: 'info',
|
|
927
|
+
dedupCore: `agent_error:${label}`,
|
|
928
|
+
payloadObj: { label, ...(detail ? { detail } : {}) },
|
|
929
|
+
});
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
795
932
|
case 'raw_step': {
|
|
796
933
|
const event = a['agentdeck.raw_event'] ?? 'raw';
|
|
797
934
|
const payload = a['agentdeck.raw_payload'] ?? {};
|
|
@@ -880,7 +1017,7 @@ export class ApmeCollector {
|
|
|
880
1017
|
// boundary=clear before closeRun tears everything down.
|
|
881
1018
|
this.closeTask(sessionId, 'clear');
|
|
882
1019
|
// Close current run (no exitCode — session is still alive)
|
|
883
|
-
this.closeRun(sessionId, undefined, projectPath);
|
|
1020
|
+
this.closeRun(sessionId, undefined, projectPath, 'clear');
|
|
884
1021
|
// Open a new run with the same session parameters, pointing back at the run
|
|
885
1022
|
// it continues. `/clear` resets the agent's context, not the user's work —
|
|
886
1023
|
// without this edge one conversation becomes N disconnected runs (a live
|
|
@@ -916,14 +1053,14 @@ export class ApmeCollector {
|
|
|
916
1053
|
/** Finalize a run. Returns the runId so callers can enqueue evaluation.
|
|
917
1054
|
* Empty runs (no prompts, no steps, no turns) are deleted — they're just
|
|
918
1055
|
* connection noise and clutter the dashboard. */
|
|
919
|
-
closeRun(sessionId, exitCode, projectPath) {
|
|
1056
|
+
closeRun(sessionId, exitCode, projectPath, turnEndSource = 'session_end') {
|
|
920
1057
|
if (!this.store.enabled)
|
|
921
1058
|
return null;
|
|
922
1059
|
const runId = this.sessionToRun.get(sessionId);
|
|
923
1060
|
if (!runId)
|
|
924
1061
|
return null;
|
|
925
1062
|
// Close the last open turn + task before finalizing the run.
|
|
926
|
-
this.closeTurn(sessionId);
|
|
1063
|
+
this.closeTurn(sessionId, turnEndSource);
|
|
927
1064
|
// splitRun already called closeTask('clear') before us, so this is usually
|
|
928
1065
|
// a no-op in the split path. Direct closeRun (session exit) still needs it.
|
|
929
1066
|
this.closeTask(sessionId, 'session_end');
|