@agentdeck/bridge 1.0.18 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/adapters/claude-code.d.ts +4 -2
  2. package/dist/adapters/claude-code.d.ts.map +1 -1
  3. package/dist/adapters/claude-code.js +9 -10
  4. package/dist/adapters/claude-code.js.map +1 -1
  5. package/dist/adapters/codex-cli.d.ts +3 -4
  6. package/dist/adapters/codex-cli.d.ts.map +1 -1
  7. package/dist/adapters/codex-cli.js +8 -12
  8. package/dist/adapters/codex-cli.js.map +1 -1
  9. package/dist/adapters/pty-adapter.d.ts +2 -2
  10. package/dist/adapters/pty-adapter.js +2 -2
  11. package/dist/apme/adapters/codex-turn-manager.d.ts +13 -77
  12. package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -1
  13. package/dist/apme/adapters/codex-turn-manager.js +97 -403
  14. package/dist/apme/adapters/codex-turn-manager.js.map +1 -1
  15. package/dist/check-deps.d.ts.map +1 -1
  16. package/dist/check-deps.js +3 -1
  17. package/dist/check-deps.js.map +1 -1
  18. package/dist/cli.d.ts.map +1 -1
  19. package/dist/cli.js +19 -3
  20. package/dist/cli.js.map +1 -1
  21. package/dist/codex-rate-limits.d.ts +1 -1
  22. package/dist/codex-rate-limits.d.ts.map +1 -1
  23. package/dist/codex-rate-limits.js +27 -17
  24. package/dist/codex-rate-limits.js.map +1 -1
  25. package/dist/daemon.js +1 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +27 -192
  28. package/dist/index.js.map +1 -1
  29. package/dist/state-machine.d.ts +5 -0
  30. package/dist/state-machine.d.ts.map +1 -1
  31. package/dist/state-machine.js +34 -4
  32. package/dist/state-machine.js.map +1 -1
  33. package/dist/types.d.ts +11 -1
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/package.json +4 -3
  37. package/dist/apme/adapters/claude-pty.d.ts +0 -28
  38. package/dist/apme/adapters/claude-pty.d.ts.map +0 -1
  39. package/dist/apme/adapters/claude-pty.js +0 -88
  40. package/dist/apme/adapters/claude-pty.js.map +0 -1
@@ -67,10 +67,17 @@ function candidateRolloutFiles(root, maxDays = 3, maxFiles = 6) {
67
67
  files.sort((a, b) => b.mtime - a.mtime);
68
68
  return files.slice(0, maxFiles);
69
69
  }
70
- /** Scan candidates newest-first and return the first that yields a usable
71
- * rate-limits snapshot. Falls through past a newer file that carries no
72
- * `rate_limits` line (e.g. a just-started session) to an older active one. */
70
+ /** Scan every candidate and return the usable rate-limits snapshot with the
71
+ * newest CAPTURE timestamp.
72
+ *
73
+ * File mtime only identifies which rollouts are active enough to inspect. It
74
+ * cannot decide which ACCOUNT snapshot is newest when several Codex sessions
75
+ * append concurrently: a reasoning/tool line can make one file newest while
76
+ * its last rate_limits line is older than a snapshot in another file. Choosing
77
+ * the first usable file made usage move backwards or appear frozen. */
73
78
  function parseFirstUsable(candidates) {
79
+ let newest = null;
80
+ let newestCapturedAtMs = -Infinity;
74
81
  for (const { full, mtime } of candidates) {
75
82
  const tail = readTail(full);
76
83
  const parsed = tail ? parseCodexRateLimitsFromText(tail) : null;
@@ -81,10 +88,14 @@ function parseFirstUsable(candidates) {
81
88
  // reading stays put — that would under-report the age of exactly the frozen
82
89
  // snapshot this anchor exists to expose. mtime is the fallback.
83
90
  parsed.capturedAt = parsed.capturedAt ?? new Date(mtime).toISOString();
84
- return parsed;
91
+ const capturedAtMs = new Date(parsed.capturedAt).getTime();
92
+ if (!newest || capturedAtMs > newestCapturedAtMs) {
93
+ newest = parsed;
94
+ newestCapturedAtMs = capturedAtMs;
95
+ }
85
96
  }
86
97
  }
87
- return null;
98
+ return newest;
88
99
  }
89
100
  /**
90
101
  * Read the newest usable Codex rate-limits snapshot from a sessions tree.
@@ -115,7 +126,9 @@ function readTail(file, maxBytes = 262144) {
115
126
  try {
116
127
  fs.closeSync(fd);
117
128
  }
118
- catch { /* ignore */ }
129
+ catch {
130
+ /* ignore */
131
+ }
119
132
  }
120
133
  }
121
134
  function toCredits(raw) {
@@ -140,9 +153,7 @@ function toWindow(raw) {
140
153
  if (!raw || typeof raw.used_percent !== 'number' || typeof raw.window_minutes !== 'number') {
141
154
  return undefined;
142
155
  }
143
- const resetsAt = typeof raw.resets_at === 'number' && raw.resets_at > 0
144
- ? new Date(raw.resets_at * 1000).toISOString()
145
- : undefined;
156
+ const resetsAt = typeof raw.resets_at === 'number' && raw.resets_at > 0 ? new Date(raw.resets_at * 1000).toISOString() : undefined;
146
157
  return {
147
158
  usedPercent: Math.min(100, Math.max(0, raw.used_percent)),
148
159
  windowMinutes: raw.window_minutes,
@@ -189,16 +200,15 @@ export function parseCodexRateLimitsFromText(text) {
189
200
  }
190
201
  return null;
191
202
  }
192
- // Cache keyed on the newest candidate rollout's path + mtime so repeated usage
193
- // polls don't re-scan unchanged files. Keying on the newest candidate (not the
194
- // one that yielded the snapshot) means any fresh write to the active session
195
- // invalidates the cache and re-scans.
203
+ // Cache keyed on EVERY candidate rollout's path + mtime. Multiple Codex
204
+ // sessions append concurrently, and the freshest rate_limits snapshot may live
205
+ // outside candidates[0]. Keying on only that first file let a newer snapshot in
206
+ // another candidate remain invisible until the first file changed again.
196
207
  let cacheKey = '';
197
208
  let cacheValue = null;
198
- export function readCodexRateLimits() {
199
- const candidates = candidateRolloutFiles(defaultSessionsRoot());
200
- const newest = candidates[0];
201
- const key = newest ? `${newest.full}:${newest.mtime}` : '';
209
+ export function readCodexRateLimits(root = defaultSessionsRoot()) {
210
+ const candidates = candidateRolloutFiles(root);
211
+ const key = candidates.map(({ full, mtime }) => `${full}:${mtime}`).join('|');
202
212
  if (key && key === cacheKey)
203
213
  return cacheValue;
204
214
  const parsed = parseFirstUsable(candidates);
@@ -1 +1 @@
1
- {"version":3,"file":"codex-rate-limits.js","sourceRoot":"","sources":["../src/codex-rate-limits.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codex-rate-limits.js","sourceRoot":"","sources":["../src/codex-rate-limits.ts"],"names":[],"mappings":"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"}
package/dist/daemon.js CHANGED
@@ -17,7 +17,7 @@ const program = new Command();
17
17
  program
18
18
  .name('agentdeck')
19
19
  .description('AgentDeck daemon (legacy entry point — use `agentdeck daemon` instead)')
20
- .version('1.0.18');
20
+ .version('1.0.19');
21
21
  program
22
22
  .command('start', { isDefault: true })
23
23
  .description('Start monitoring daemon')
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4DH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUxD,OAAO,EAOL,KAAK,SAAS,EAQf,MAAM,YAAY,CAAC;AAwDpB,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;kFAC8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;0EACsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAisCtE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2DH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUxD,OAAO,EAOL,KAAK,SAAS,EAQf,MAAM,YAAY,CAAC;AAwDpB,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;kFAC8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;0EACsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8nCtE"}
package/dist/index.js CHANGED
@@ -14,7 +14,6 @@ import { readLastTurn as readClaudeTranscriptLastTurn } from './apme/claude-tran
14
14
  import { claudeHookToSpans } from './apme/adapters/claude-hook.js';
15
15
  import { codexHookToSpans } from './apme/adapters/codex-hook.js';
16
16
  import { CodexTurnManager } from './apme/adapters/codex-turn-manager.js';
17
- import { claudePtyParserEventToSpans, claudePtyResponseToSpan } from './apme/adapters/claude-pty.js';
18
17
  import { timelineEntryToSpans } from './apme/adapters/timeline.js';
19
18
  import { classifyAndEnqueueTurn } from './apme/classify-turn.js';
20
19
  import { VoiceManager } from './voice.js';
@@ -106,6 +105,13 @@ export async function startSession(opts) {
106
105
  process.exit(1);
107
106
  for (const w of deps.warnings)
108
107
  log(`WARNING: ${w}`);
108
+ if (agentType === 'codex-cli' && deps.agentVersion) {
109
+ const { satisfiesRange } = await import('./version-check.js');
110
+ if (!satisfiesRange(deps.agentVersion, '>=0.141.0')) {
111
+ log(`WARNING: Codex ${deps.agentVersion} predates the lifecycle-hook baseline (>=0.141.0). ` +
112
+ 'The terminal can start, but AgentDeck lifecycle monitoring requires a newer Codex CLI.');
113
+ }
114
+ }
109
115
  // Version compatibility check (Claude Code only — uses npm registry + compatibleClaudeCode range)
110
116
  if (agentType === 'claude-code' && !opts.noUpdateCheck) {
111
117
  const { checkVersionCompatibility } = await import('./version-check.js');
@@ -443,20 +449,13 @@ export async function startSession(opts) {
443
449
  });
444
450
  // Claude Code hook events → timeline entries
445
451
  if (agentType === 'claude-code') {
446
- wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer);
452
+ wireClaudeCodeTimeline(adapter, core);
447
453
  }
448
454
  // APME wiring for non-Claude-Code agents (OpenCode, Codex, OpenClaw)
449
455
  if (apme && agentType !== 'claude-code' && agentType !== 'monitor') {
450
- wireAgentApme(adapter, agentType, apme, core, ptyRingBuffer);
456
+ wireAgentApme(adapter, agentType, apme, core);
451
457
  }
452
458
  // ===== Wire adapter events → StateMachine + journal =====
453
- // APME: PTY response fallback — captures response from terminal output when
454
- // the Stop hook doesn't fire (unreliable in Claude Code v2.1+).
455
- // Three capture paths handle the hook/PTY race condition:
456
- // Path A: idle fires, turn exists → apply directly
457
- // Path B: idle fires before hook (fast responses) → buffer, apply on hook arrival
458
- // Path C: UserPromptSubmit closes prev turn → apply PTY text to closed turn
459
- let pendingPtyResponse = null;
460
459
  adapter.on('event', (evt) => {
461
460
  switch (evt.source) {
462
461
  case 'hook':
@@ -479,28 +478,6 @@ export async function startSession(opts) {
479
478
  apme.collector.ingestSpan(core.sessionId, span);
480
479
  }
481
480
  }
482
- if (evt.event === 'UserPromptSubmit' && apme) {
483
- // Path B: apply buffered response to the NEW turn (idle fired before hook)
484
- if (pendingPtyResponse) {
485
- debug('APME', `hook:UPS Path B: applying pending (${pendingPtyResponse.length} chars)`);
486
- const ctx = makeApmeAdapterCtx(apme, core.sessionId, agentType);
487
- const span = claudePtyResponseToSpan(ctx, pendingPtyResponse);
488
- if (span)
489
- apme.collector.ingestSpan(core.sessionId, span);
490
- pendingPtyResponse = null;
491
- }
492
- // Path C: prev turn was just closed by ingestHook — apply pending response
493
- if (pendingPtyResponse) {
494
- debug('APME', `hook:UPS Path C: applying pending to closed turn (${pendingPtyResponse.length} chars)`);
495
- const ctx = makeApmeAdapterCtx(apme, core.sessionId, agentType);
496
- const span = claudePtyResponseToSpan(ctx, pendingPtyResponse, { fallbackToLastClosed: true });
497
- if (span)
498
- apme.collector.ingestSpan(core.sessionId, span);
499
- pendingPtyResponse = null;
500
- }
501
- }
502
- if (evt.event === 'Stop')
503
- pendingPtyResponse = null; // Stop has cleaner response
504
481
  // APME-off fallback: task rows from boundary hooks alone.
505
482
  if (fallbackTasks)
506
483
  fallbackTasks.ingestHook(core.sessionId, evt.event, evt.data ?? {});
@@ -511,67 +488,14 @@ export async function startSession(opts) {
511
488
  core.stateMachine.handleHookEvent(evt.event, evt.data);
512
489
  break;
513
490
  case 'parser':
514
- journal.write('parser_emit', 'pty', { event: evt.event, ...evt.data });
491
+ // Structured adapters (OpenCode SSE / OpenClaw Gateway) normalize
492
+ // their native event streams into the legacy state vocabulary.
493
+ journal.write('adapter_signal', agentType, { event: evt.event, ...evt.data });
515
494
  core.stateMachine.handleParserEvent(evt.event, evt.data);
516
- // APME ingestion fallback: PTY parser sees tool use + state changes
517
- // even when Claude Code hooks are flaky (memory: feedback_apme_stop_hook).
518
- if (apme && evt.event) {
519
- const ctx = makeApmeAdapterCtx(apme, core.sessionId, agentType);
520
- for (const span of claudePtyParserEventToSpans(ctx, evt.event, evt.data ?? {})) {
521
- apme.collector.ingestSpan(core.sessionId, span);
522
- }
523
- }
524
- // APME: Path A — spinner_stop fires when Claude finishes responding.
525
- // Delay 500ms to let response render in PTY, then extract text after ⏺ marker.
526
- if (apme && evt.event === 'spinner_stop') {
527
- const sid = core.sessionId;
528
- setTimeout(async () => {
529
- if (!apme)
530
- return;
531
- const tail = ptyRingBuffer.getTail(5000);
532
- // Claude's response starts with ⏺ — extract content after last ⏺ marker.
533
- // Take only meaningful lines (stop at spinner chars ✢✻⏸, prompt ❯, or separator ──).
534
- const marker = tail.lastIndexOf('⏺');
535
- let response = '';
536
- if (marker >= 0) {
537
- const afterMarker = tail.slice(marker + 1).trim();
538
- const lines = afterMarker.split('\n');
539
- const clean = [];
540
- for (const line of lines) {
541
- const trimmed = line.trim();
542
- if (!trimmed)
543
- continue;
544
- // Stop at UI artifacts: spinner chars (✢✳✶✻✽), plan mode (⏸), prompt (❯), separator (─)
545
- if (/^[✢✳✶✻✽⏸⏵❯─>]/.test(trimmed))
546
- break;
547
- if (/planmode|plan\s*mode|shift\+tab|accept\s*edits/i.test(trimmed))
548
- break;
549
- // Status text: "Whirring…", "Finagling…", token count, shortcuts hint
550
- if (/^\S+…(\s|$)/.test(trimmed) && /tokens|shortcuts|\d+[ms]\s/i.test(trimmed))
551
- break;
552
- if (/\?\s*for\s*shortcuts/.test(trimmed))
553
- break;
554
- clean.push(trimmed);
555
- }
556
- response = clean.join('\n').trim();
557
- }
558
- debug('APME', `spinner_stop+500ms: tailLen=${tail.length} marker=${marker} respLen=${response.length}`);
559
- if (response.length > 2) {
560
- const turnId = apme.collector.getActiveTurnId(sid);
561
- if (turnId) {
562
- const ctx = makeApmeAdapterCtx(apme, sid, agentType);
563
- const span = claudePtyResponseToSpan(ctx, response);
564
- if (span)
565
- apme.collector.ingestSpan(sid, span);
566
- pendingPtyResponse = null;
567
- await classifyAndEnqueueTurn(apme, sid);
568
- }
569
- else {
570
- pendingPtyResponse = response;
571
- }
572
- }
573
- }, 500);
574
- }
495
+ break;
496
+ case 'terminal_ui':
497
+ journal.write('terminal_ui', 'pty', { event: evt.event, ...evt.data });
498
+ core.stateMachine.handleTerminalUiEvent(evt.event, evt.data);
575
499
  break;
576
500
  case 'metadata':
577
501
  switch (evt.event) {
@@ -587,11 +511,6 @@ export async function startSession(opts) {
587
511
  const text = evt.data?.text;
588
512
  if (text)
589
513
  core.broadcast({ type: 'user_prompt', text });
590
- // APME: user_prompt is the PTY-detected prompt — use it to open a turn
591
- // and capture the prompt text (fallback for when hooks don't fire).
592
- if (apme && text) {
593
- apme.collector.ingestHook(core.sessionId, 'UserPromptSubmit', { message: { content: text } });
594
- }
595
514
  break;
596
515
  }
597
516
  case 'model_catalog': {
@@ -1266,14 +1185,12 @@ export async function startSession(opts) {
1266
1185
  // ===== Non-Claude agent APME wiring =====
1267
1186
  // For OpenCode, Codex, OpenClaw: intercepts adapter timeline events to create
1268
1187
  // APME turns with prompts and responses. Claude Code uses hooks instead.
1269
- function wireAgentApme(adapter, agentType, apme, core, ptyRingBuffer) {
1188
+ function wireAgentApme(adapter, agentType, apme, core) {
1270
1189
  const sid = core.sessionId;
1271
- // Codex turn boundaries (hook-primary, PTY-fallback) live in their own
1272
- // module. The class encapsulates the seven-state-variable PTY-only
1273
- // machine plus the hook-fresh shortcut so non-codex sessions allocate
1274
- // nothing here.
1190
+ // Codex turn boundaries and rollout response capture live in their own
1191
+ // hook-owned module so non-Codex sessions allocate nothing here.
1275
1192
  const codexManager = agentType === 'codex-cli'
1276
- ? new CodexTurnManager(core, apme, ptyRingBuffer, sid, agentType)
1193
+ ? new CodexTurnManager(core, apme, sid, agentType)
1277
1194
  : null;
1278
1195
  adapter.on('event', (evt) => {
1279
1196
  if (evt.source === 'hook' && isSubagentOnlyHook(evt.event, evt.data ?? {})) {
@@ -1309,12 +1226,10 @@ function wireAgentApme(adapter, agentType, apme, core, ptyRingBuffer) {
1309
1226
  void classifyAndEnqueueTurn(apme, sid);
1310
1227
  }
1311
1228
  }
1312
- // ── Codex: hook (primary) + PTY-parser fallback ──
1229
+ // ── Codex: lifecycle hooks + notify turn-complete fallback ──
1313
1230
  if (codexManager) {
1314
1231
  if (evt.source === 'hook')
1315
1232
  codexManager.onHookEvent(evt);
1316
- else if (evt.source === 'parser')
1317
- codexManager.onParserEvent(evt);
1318
1233
  }
1319
1234
  });
1320
1235
  if (codexManager) {
@@ -1338,14 +1253,12 @@ function isClaudeTaskNotificationPrompt(text) {
1338
1253
  return text.trim().toLowerCase().startsWith('<task-notification>');
1339
1254
  }
1340
1255
  // ===== Claude Code timeline wiring =====
1341
- function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1256
+ function wireClaudeCodeTimeline(adapter, core) {
1342
1257
  let ccChatStart = null;
1343
1258
  let ccPendingChatStart = false;
1344
1259
  let ccPendingChatStartTimer = null;
1345
1260
  let ccLastPromptText = null;
1346
- let ccLastHookToolTs = 0; // Change 1: track last hook-based tool_request
1347
- let ccPendingCompletion = false; // Change 2: track pending chat_end for PTY fallback
1348
- let ccPendingCompletionTimer = null;
1261
+ let ccPendingCompletion = false;
1349
1262
  const emitChatStart = (text) => {
1350
1263
  if (ccPendingChatStartTimer) {
1351
1264
  clearTimeout(ccPendingChatStartTimer);
@@ -1376,33 +1289,8 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1376
1289
  startedAt: ccChatStart ?? Date.now(),
1377
1290
  });
1378
1291
  };
1379
- /** Extract response text from PTY ringbuffer after ⏺ marker (same pattern as APME) */
1380
- const extractPtyResponse = () => {
1381
- const tail = ptyRingBuffer.getTail(5000);
1382
- const marker = tail.lastIndexOf('⏺');
1383
- if (marker < 0)
1384
- return '';
1385
- const afterMarker = tail.slice(marker + 1).trim();
1386
- const lines = afterMarker.split('\n');
1387
- const clean = [];
1388
- for (const line of lines) {
1389
- const trimmed = line.trim();
1390
- if (!trimmed)
1391
- continue;
1392
- if (/^[✢✳✶✻✽⏸⏵❯─>]/.test(trimmed))
1393
- break;
1394
- if (/planmode|plan\s*mode|shift\+tab|accept\s*edits/i.test(trimmed))
1395
- break;
1396
- if (/^\S+…(\s|$)/.test(trimmed) && /tokens|shortcuts|\d+[ms]\s/i.test(trimmed))
1397
- break;
1398
- if (/\?\s*for\s*shortcuts/.test(trimmed))
1399
- break;
1400
- clean.push(trimmed);
1401
- }
1402
- return clean.join('\n').trim();
1403
- };
1404
1292
  /**
1405
- * Emit a single Claude turn-close row (shared by Stop hook and PTY fallback):
1293
+ * Emit a single Claude turn-close row from the authoritative Stop hook:
1406
1294
  * a `chat_response` when there is meaningful response text, otherwise a
1407
1295
  * `chat_end`. The two are mutually exclusive so a turn never produces both —
1408
1296
  * the paired dimmed `chat_end` used to fragment every turn into three rows on
@@ -1501,48 +1389,6 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1501
1389
  }
1502
1390
  }
1503
1391
  });
1504
- // Change 1: PTY tool_action → tool_exec timeline entry (fills gap when hooks don't fire)
1505
- adapter.on('event', (evt) => {
1506
- if (evt.source !== 'parser' || evt.event !== 'tool_action')
1507
- return;
1508
- const now = Date.now();
1509
- // Skip if hook already emitted tool_request within 2s (avoid duplicates)
1510
- if (now - ccLastHookToolTs < 2000)
1511
- return;
1512
- const toolName = evt.data?.toolName || 'tool';
1513
- const toolArgs = evt.data?.toolArgs || '';
1514
- const raw = toolArgs ? `${toolName} ${toolArgs}` : toolName;
1515
- core.bridgeTimeline.addEntry({
1516
- ts: now, type: 'tool_exec',
1517
- raw: raw.length > 500 ? raw.slice(0, 497) + '...' : raw,
1518
- agentType: 'claude-code',
1519
- });
1520
- });
1521
- // Change 2: PTY fallback chat_end from spinner_stop/idle (when Stop hook doesn't fire)
1522
- adapter.on('event', (evt) => {
1523
- if (evt.source !== 'parser')
1524
- return;
1525
- if ((evt.event === 'spinner_stop' || evt.event === 'idle') && ccPendingCompletion) {
1526
- // Wait 1.5s — Stop hook may still arrive
1527
- if (ccPendingCompletionTimer)
1528
- clearTimeout(ccPendingCompletionTimer);
1529
- ccPendingCompletionTimer = setTimeout(() => {
1530
- ccPendingCompletionTimer = null;
1531
- if (!ccPendingCompletion)
1532
- return; // Stop hook already handled it
1533
- ccPendingCompletion = false;
1534
- if (ccPendingChatStart)
1535
- emitChatStart('');
1536
- const now = Date.now();
1537
- const duration = ccChatStart ? Math.round((now - ccChatStart) / 1000) : null;
1538
- const toolSummary = core.usageTracker.getToolSummary();
1539
- // Extract response from PTY ringbuffer
1540
- const responseText = extractPtyResponse();
1541
- debug('timeline', `CC PTY fallback chat_end: respLen=${responseText.length} duration=${duration}`);
1542
- emitCompletion(responseText, duration, toolSummary);
1543
- }, 1500);
1544
- }
1545
- });
1546
1392
  adapter.on('event', (evt) => {
1547
1393
  if (evt.source !== 'hook')
1548
1394
  return;
@@ -1554,7 +1400,7 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1554
1400
  ccChatStart = now;
1555
1401
  core.usageTracker.resetToolCounts();
1556
1402
  ccPendingChatStart = true;
1557
- ccPendingCompletion = true; // Change 2: arm PTY fallback
1403
+ ccPendingCompletion = true;
1558
1404
  // Claude Code v2.1+ sends { message: { content: "..." } }, not { prompt: "..." }
1559
1405
  const msg = evt.data?.message;
1560
1406
  const hookText = evt.data?.prompt
@@ -1569,7 +1415,6 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1569
1415
  break;
1570
1416
  }
1571
1417
  case 'PreToolUse': {
1572
- ccLastHookToolTs = now; // Change 1: mark hook tool time for dedup
1573
1418
  const toolName = evt.data?.tool_name || 'tool';
1574
1419
  const formatted = formatToolInputForTimeline(toolName, evt.data?.tool_input);
1575
1420
  core.bridgeTimeline.addEntry({
@@ -1586,18 +1431,10 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1586
1431
  break;
1587
1432
  }
1588
1433
  case 'Stop': {
1589
- // Race guard: if ccPendingCompletion is already false, the PTY fallback
1590
- // timer fired first (Stop hook arrived >1.5s after spinner_stop) and
1591
- // already emitted chat_response/chat_end. Skipping here prevents the
1592
- // duplicate-line bug visible on the dashboard timeline.
1593
1434
  const wasPending = ccPendingCompletion;
1594
- ccPendingCompletion = false; // Change 2: disarm PTY fallback — hook handled it
1595
- if (ccPendingCompletionTimer) {
1596
- clearTimeout(ccPendingCompletionTimer);
1597
- ccPendingCompletionTimer = null;
1598
- }
1435
+ ccPendingCompletion = false;
1599
1436
  if (!wasPending) {
1600
- debug('timeline', 'CC Stop hook arrived after PTY fallback emit — skipping duplicate');
1437
+ debug('timeline', 'duplicate Claude Stop hook ignored');
1601
1438
  break;
1602
1439
  }
1603
1440
  if (ccPendingChatStart)
@@ -1612,8 +1449,6 @@ function wireClaudeCodeTimeline(adapter, core, journal, ptyRingBuffer) {
1612
1449
  // payload points at it.
1613
1450
  // 2. last_assistant_message — ~18% reliable field on the hook
1614
1451
  // payload; honoured when the transcript isn't reachable.
1615
- // 3. PTY ringbuffer — last-resort fallback already handled by
1616
- // the existing spinner_stop pathway.
1617
1452
  const transcriptPath = evt.data?.transcript_path || '';
1618
1453
  let lastAssistantMsg = evt.data?.last_assistant_message || '';
1619
1454
  if (transcriptPath) {