agent-dag 1.33.112 → 1.33.113

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>agents-deck</title>
7
7
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='84' font-size='84'%3E%E2%97%89%3C/text%3E%3C/svg%3E" />
8
- <script type="module" crossorigin src="/assets/index-C1xXayQj.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-C-JtsHUv.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-DjZdNYsz.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-dag",
3
- "version": "1.33.112",
3
+ "version": "1.33.113",
4
4
  "description": "Live deck of Claude Code and Codex agents — watch parallel subagents fork, call tools, and return on one calm canvas. Also available as npx ccdeck and npx agent-dag.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -68,22 +68,6 @@ const FRESH_MIN_AGE_MS = 190_000;
68
68
  // congestion control is deliberately holding back, and so do we.
69
69
  const RECENT_429_MS = 3_600_000;
70
70
 
71
- /**
72
- * Ask claude-swap to collect, if its own schedule says anything is due.
73
- *
74
- * Without this the panel is only as live as whatever else is running: if the
75
- * user has no `cswap watch`/`auto`/TUI open, nothing ever writes the store and
76
- * the panel shows frozen numbers while looking current.
77
- *
78
- * `cswap list` is the safe way to ask. It runs the same on-demand pass every
79
- * claude-swap surface uses, and the store — not us — decides which accounts,
80
- * if any, may actually hit the network. So this cannot outrun the request
81
- * budget no matter how often the panel polls: when nothing is due, it is a
82
- * local read that fetches nothing.
83
- *
84
- * Fire-and-forget. The result lands in the store for the next poll to pick up;
85
- * making the caller wait would just delay the numbers it already has.
86
- */
87
71
  /**
88
72
  * Whether anything is waiting to be collected.
89
73
  *
@@ -174,6 +158,10 @@ export function nextReadAt(row, matches, fetchedAtMs, isActive, now) {
174
158
  /**
175
159
  * Keep the store moving while someone is looking at it.
176
160
  *
161
+ * Without this the panel is only as live as whatever else is running: with no
162
+ * `cswap watch`/`auto`/TUI open, nothing ever writes the store and the panel
163
+ * shows frozen numbers while looking current.
164
+ *
177
165
  * Two ways to ask, and the cheaper one is preferred:
178
166
  * - something is due by claude-swap's own plan → `cswap list`, the ordinary
179
167
  * on-demand pass every surface uses;
@@ -339,11 +339,14 @@ function scanTranscript(path) {
339
339
  // ─── Model enrichment ────────────────────────────────────────────────────
340
340
  // CC's hook payloads never carry the `model` field — but every hook
341
341
  // references a `transcript_path` JSONL that contains lines like
342
- // `"model":"claude-opus-4-7"`. We read the tail of that file once per
343
- // session, cache the result, and (a) inject `model` into subsequent
342
+ // `"model":"claude-opus-4-7"`. We re-read the tail of that file on every
343
+ // event for the session (throttled to MODEL_READ_THROTTLE_MS), cache the
344
+ // resolved root + subagent models, and (a) inject `model` into subsequent
344
345
  // payloads for that session before broadcasting, (b) emit a synthetic
345
- // `ModelObserved` event so the client backfills agents created before
346
- // the model was resolved.
346
+ // `ModelObserved` event whenever that set CHANGES, so the client backfills
347
+ // agents created before the model was resolved. The cache is the emit
348
+ // filter, not a read filter: reading once per session meant a subagent
349
+ // model that only appears after the root is known was never picked up.
347
350
  const modelBySession = new Map(); // sessionId -> { rootModel, subsSig }
348
351
  const pendingTranscriptReads = new Set(); // sessionId currently being read
349
352
  const modelLastReadAt = new Map(); // sessionId -> ms timestamp (re-read throttle)
@@ -489,8 +492,11 @@ function maybeResolveUsage(payload) {
489
492
  // Approximation of `/context` since CC doesn't expose its breakdown via
490
493
  // hooks. We scan the transcript JSONL for message counts (user / assistant
491
494
  // / tool_use / tool_result / system-reminders) and walk up from cwd for
492
- // any CLAUDE.md files in scope. Token totals come from UsageObserved; this
493
- // scan is purely structural ("what does the context contain").
495
+ // any CLAUDE.md files in scope. Cumulative token totals come from
496
+ // UsageObserved; this scan produces the structural counts ("what does the
497
+ // context contain") plus the current window size — currentContextTokens, the
498
+ // last usage block after the most recent /clear or /compact, which is what
499
+ // the context donut and the modal's percentage are drawn from.
494
500
  const lastContextReadAt = new Map();
495
501
  const pendingContextReads = new Set();
496
502
  const CONTEXT_READ_THROTTLE_MS = 4000;
@@ -1192,10 +1198,11 @@ function pushEvent(raw, source, opts = {}) {
1192
1198
  // dead session ids before the first live event even arrives.
1193
1199
  if (!opts.replay && raw && typeof raw === "object") touchSession(raw.session_id);
1194
1200
 
1195
- // Kick off async transcript scans. Model arrives as a one-shot
1196
- // ModelObserved; usage is re-read periodically (throttled to 2.5s per
1197
- // session) so the cost columns track running totals as the session
1198
- // progresses. Both result in synthetic events.
1201
+ // Kick off async transcript scans. Model and usage are both re-read
1202
+ // periodically (throttled to 2.5s per session) so the cost columns track
1203
+ // running totals as the session progresses and late subagent models still
1204
+ // land; ModelObserved is only emitted when the resolved set changes. Both
1205
+ // result in synthetic events.
1199
1206
  // Provider gates the path: Claude reads transcript_path; Codex reads its
1200
1207
  // rollout JSONL under ~/.codex/sessions/. The Claude scanners short-circuit
1201
1208
  // when transcript_path is absent (always the case for Codex hooks).
@@ -226,15 +226,6 @@ function stripAnsi(s) {
226
226
  .replace(/\x1B[()][AB012]/g, "");
227
227
  }
228
228
 
229
- /**
230
- * Parse `claude --print /usage` output.
231
- *
232
- * Observed format (Claude Code ≥ 1.x):
233
- * "Current session: 84% used · resets Jun 18, 4:09pm (Europe/Chisinau)"
234
- * "Current week (all models): 85% used · resets Jun 21, 8:59am (Europe/Chisinau)"
235
- * "Current week (Sonnet only): 48% used · resets Jun 21, 9am (Europe/Chisinau)"
236
- * "Current week (Opus only): ..." (if present)
237
- */
238
229
  // Parse "Jun 18, 4:09pm" (local time, no tz) into unix seconds.
239
230
  // Claude shows times in the user's local timezone, so parsing as local is correct.
240
231
  // `now` is injectable so the year-boundary case is testable.
@@ -263,6 +254,15 @@ export function parseResetToSec(resetStr, now = Date.now()) {
263
254
  } catch { return null; }
264
255
  }
265
256
 
257
+ /**
258
+ * Parse `claude --print /usage` output.
259
+ *
260
+ * Observed format (Claude Code ≥ 1.x):
261
+ * "Current session: 84% used · resets Jun 18, 4:09pm (Europe/Chisinau)"
262
+ * "Current week (all models): 85% used · resets Jun 21, 8:59am (Europe/Chisinau)"
263
+ * "Current week (Sonnet only): 48% used · resets Jun 21, 9am (Europe/Chisinau)"
264
+ * "Current week (Opus only): ..." (if present)
265
+ */
266
266
  function parseUsageText(raw) {
267
267
  const text = stripAnsi(raw);
268
268
  const result = {};
@@ -15,7 +15,8 @@
15
15
  // So we report three distinct versions:
16
16
  // running — captured at boot by the caller, before an upgrade can land
17
17
  // installed — re-read from disk per call; what a restart would run
18
- // latest — npm's dist-tag, fetched at most once a day
18
+ // latest — npm's dist-tag, fetched at most once an hour (CHECK_MS below,
19
+ // which also explains why it is not the daily cadence it was)
19
20
  //
20
21
  // Installing is opt-in and narrow. `npm i -g` runs only when the user asks for
21
22
  // it by name and only where it can actually work: a global install, on a
@@ -778,9 +779,14 @@ export function lastMeaningfulLine(text) {
778
779
  return lines.length ? lines[lines.length - 1].slice(0, 300) : "";
779
780
  }
780
781
 
781
- /** Full answer for GET /api/version. Never throws, never blocks on the network
782
- * for longer than FETCH_TIMEOUT_MS, and answers the local half even when the
783
- * registry is unreachable. */
782
+ /** Full answer for GET /api/version. Never throws, and answers the local half
783
+ * even when the registry is unreachable.
784
+ *
785
+ * Network worst case is 2 x FETCH_TIMEOUT_MS, not one: the dist-tag lookup,
786
+ * plus — only when the tag has moved to a version this deck has not confirmed
787
+ * yet — the installability probe (see runCheck), each carrying its own
788
+ * AbortSignal timeout. That second request costs its timeout at most once per
789
+ * release; a deck sitting on the current release stays inside one. */
784
790
  export async function versionReport({ running, pkgRoot, name = "agents-deck", now = Date.now(), force = false }) {
785
791
  const installed = installedVersion(pkgRoot);
786
792
  // Asked about the package the command installs, not about the one this build