fleetlens 0.10.1 → 0.10.2
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/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0s4dg1y._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0tejjy_._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0m67-xi._.js +1 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/063r4x2.9csh8.js +14 -0
- package/app/apps/web/.next/static/chunks/{0g8ujtb0lp._9.js → 0rtba0saz74e1.js} +2 -2
- package/app/apps/web/app/sessions/[id]/session-view.tsx +133 -49
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +5 -2
- package/dist/index.js +10 -7
- package/package.json +1 -1
- package/app/apps/web/.next/static/chunks/0~n2a9f52wju9.js +0 -14
- /package/app/apps/web/.next/static/{sxcMbKfZ5XgVXYN4QHOHd → XTns3Y6jHWmb7EYKzQi55}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{sxcMbKfZ5XgVXYN4QHOHd → XTns3Y6jHWmb7EYKzQi55}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{sxcMbKfZ5XgVXYN4QHOHd → XTns3Y6jHWmb7EYKzQi55}/_ssgManifest.js +0 -0
|
@@ -292,34 +292,42 @@ export function SessionView({
|
|
|
292
292
|
/** Detect PR creations in this session. */
|
|
293
293
|
const prMarkers = useMemo(() => detectPrMarkers(session), [session]);
|
|
294
294
|
|
|
295
|
-
/** Idle bands derived from raw event timestamps
|
|
295
|
+
/** Idle bands derived from raw event timestamps. Anchors include every
|
|
296
|
+
* timestamped event (agent-thinking and meta are activity too), and
|
|
297
|
+
* each emitted band's `start..end` is a literal time range so the
|
|
298
|
+
* minimap stripe width tracks actual idle duration — no across-gap
|
|
299
|
+
* merging.
|
|
296
300
|
*
|
|
297
|
-
* Two
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
301
|
+
* Two exclusions keep "delegated work" and "response latency" out of
|
|
302
|
+
* idle:
|
|
303
|
+
* - Subagent run spans are carved out of every candidate gap. When a
|
|
304
|
+
* parent dispatches an Agent tool call the gap between tool_use and
|
|
305
|
+
* tool_result is the subagent's runtime — not idle. Background
|
|
306
|
+
* runs that overlap a user-away gap likewise get clipped out so
|
|
307
|
+
* only the genuinely-unwatched portion remains. The carve can
|
|
308
|
+
* produce multiple surviving slices per gap; each becomes its own
|
|
309
|
+
* band so the visual extent matches reality.
|
|
310
|
+
* - "Awaiting first response" — the period between a user message
|
|
311
|
+
* and the first agent/tool-call anchor — is dropped wholesale,
|
|
312
|
+
* even if intermediate thinking/meta anchors split it into
|
|
313
|
+
* multiple sub-gaps. Long model reasoning before the first reply
|
|
314
|
+
* is composing, not idle. (User→user gaps still register as
|
|
315
|
+
* between-turn idle because no agent activity occurred between.)
|
|
309
316
|
*
|
|
310
317
|
* Side effect: the leading warm-up gap before the first agent response
|
|
311
318
|
* drops out unless it crosses MIN_GAP_MS — which the first 6s in a
|
|
312
319
|
* typical Codex session does not. */
|
|
313
320
|
const rawIdleBands = useMemo(() => {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
321
|
+
// Anchor on ANY timestamped event so agent-thinking, meta, and other
|
|
322
|
+
// intra-turn signals prove the parent is doing work. Only what we
|
|
323
|
+
// need to distinguish is "user" (turn boundary) vs everything else.
|
|
324
|
+
const isUserRole = (role: string) => role === "user";
|
|
325
|
+
// 30s threshold: keeps ordinary tool latency (Read big file, slow Bash,
|
|
326
|
+
// model thinking between anchors) out of the idle stripes while still
|
|
327
|
+
// catching anything that reads as a real pause.
|
|
328
|
+
const MIN_GAP_MS = 30_000;
|
|
320
329
|
const anchors: { ms: number; role: string }[] = [];
|
|
321
330
|
for (const e of events) {
|
|
322
|
-
if (!isAnchor(e.role)) continue;
|
|
323
331
|
if (!e.timestamp) continue;
|
|
324
332
|
const ms = Date.parse(e.timestamp);
|
|
325
333
|
if (!Number.isFinite(ms)) continue;
|
|
@@ -329,38 +337,103 @@ export function SessionView({
|
|
|
329
337
|
anchors.sort((a, b) => a.ms - b.ms);
|
|
330
338
|
const sessionStartMs = anchors[0]!.ms - (events.find((e) => e.timestamp)?.tOffsetMs ?? 0);
|
|
331
339
|
|
|
340
|
+
const subagentSpans = (session.subagents ?? [])
|
|
341
|
+
.filter(
|
|
342
|
+
(s): s is SubagentRun & { startTOffsetMs: number; endTOffsetMs: number } =>
|
|
343
|
+
s.startTOffsetMs !== undefined && s.endTOffsetMs !== undefined,
|
|
344
|
+
)
|
|
345
|
+
.map((s) => ({ start: s.startTOffsetMs, end: s.endTOffsetMs }))
|
|
346
|
+
.sort((a, b) => a.start - b.start);
|
|
347
|
+
|
|
348
|
+
/** Subtract subagent spans from [start, end] in offset coordinates,
|
|
349
|
+
* returning 0+ remaining slices in time order. */
|
|
350
|
+
const carveOutSubagents = (
|
|
351
|
+
start: number,
|
|
352
|
+
end: number,
|
|
353
|
+
): { start: number; end: number }[] => {
|
|
354
|
+
let pieces: { start: number; end: number }[] = [{ start, end }];
|
|
355
|
+
for (const span of subagentSpans) {
|
|
356
|
+
if (span.end <= start) continue;
|
|
357
|
+
if (span.start >= end) break;
|
|
358
|
+
const next: { start: number; end: number }[] = [];
|
|
359
|
+
for (const p of pieces) {
|
|
360
|
+
if (span.end <= p.start || span.start >= p.end) {
|
|
361
|
+
next.push(p);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (span.start > p.start) next.push({ start: p.start, end: span.start });
|
|
365
|
+
if (span.end < p.end) next.push({ start: span.end, end: p.end });
|
|
366
|
+
}
|
|
367
|
+
pieces = next;
|
|
368
|
+
}
|
|
369
|
+
return pieces;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const overlapsSubagent = (start: number, end: number): boolean => {
|
|
373
|
+
for (const span of subagentSpans) {
|
|
374
|
+
if (span.end <= start) continue;
|
|
375
|
+
// subagentSpans is sorted by start, so once span.start >= end no
|
|
376
|
+
// later span can overlap [start, end] either — bail out cleanly.
|
|
377
|
+
if (span.start >= end) return false;
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
return false;
|
|
381
|
+
};
|
|
382
|
+
|
|
332
383
|
type Band = { start: number; end: number; durationMs: number };
|
|
333
384
|
const bands: Band[] = [];
|
|
334
|
-
let inTurn: Band | null = null;
|
|
335
385
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
386
|
+
// "Awaiting first response" — true after a user message, false once an
|
|
387
|
+
// agent or tool-call anchor proves the model started replying.
|
|
388
|
+
// Thinking and meta events don't clear it, so a user→thinking→agent
|
|
389
|
+
// sequence where thinking→agent crosses MIN_GAP_MS still reads as
|
|
390
|
+
// response latency rather than idle.
|
|
391
|
+
let awaitingFirstResponse = false;
|
|
392
|
+
if (anchors[0]) {
|
|
393
|
+
if (isUserRole(anchors[0].role)) awaitingFirstResponse = true;
|
|
394
|
+
}
|
|
340
395
|
|
|
341
396
|
for (let i = 1; i < anchors.length; i++) {
|
|
342
|
-
const
|
|
343
|
-
|
|
397
|
+
const rawGap = anchors[i]!.ms - anchors[i - 1]!.ms;
|
|
398
|
+
const curRole = anchors[i]!.role;
|
|
399
|
+
// Snapshot the phase BEFORE updating, so the gap is classified by
|
|
400
|
+
// the state at its start (anchors[i-1]).
|
|
401
|
+
const skipAsLatency = awaitingFirstResponse && !isUserRole(curRole);
|
|
402
|
+
|
|
403
|
+
if (isUserRole(curRole)) awaitingFirstResponse = true;
|
|
404
|
+
else if (curRole === "agent" || curRole === "tool-call") awaitingFirstResponse = false;
|
|
405
|
+
|
|
406
|
+
if (rawGap <= MIN_GAP_MS) continue;
|
|
407
|
+
if (skipAsLatency) continue;
|
|
408
|
+
|
|
344
409
|
const gStart = anchors[i - 1]!.ms - sessionStartMs;
|
|
345
410
|
const gEnd = anchors[i]!.ms - sessionStartMs;
|
|
411
|
+
const betweenTurn = isUserRole(curRole);
|
|
412
|
+
|
|
413
|
+
if (!betweenTurn) {
|
|
414
|
+
// In-turn gap: the parent is mid-turn. If any subagent run overlaps,
|
|
415
|
+
// the parent is waiting on delegated work — not idle. Drop the
|
|
416
|
+
// whole gap rather than carving out startup/teardown slivers that
|
|
417
|
+
// would clutter the minimap with thin stripes. Gaps with no
|
|
418
|
+
// subagent overlap (long Bash runs, model thinking between tool
|
|
419
|
+
// calls) still emit as a single band.
|
|
420
|
+
if (overlapsSubagent(gStart, gEnd)) continue;
|
|
421
|
+
bands.push({ start: gStart, end: gEnd, durationMs: gEnd - gStart });
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
346
424
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (!inTurn) inTurn = { start: gStart, end: gEnd, durationMs: gap };
|
|
355
|
-
else {
|
|
356
|
-
inTurn.end = gEnd;
|
|
357
|
-
inTurn.durationMs += gap;
|
|
425
|
+
// Between-turn gap: user walked away. Carve out background subagent
|
|
426
|
+
// activity so only the genuinely-unwatched portion counts as idle.
|
|
427
|
+
// Each surviving slice becomes its own band.
|
|
428
|
+
for (const slice of carveOutSubagents(gStart, gEnd)) {
|
|
429
|
+
const dur = slice.end - slice.start;
|
|
430
|
+
if (dur > MIN_GAP_MS) {
|
|
431
|
+
bands.push({ start: slice.start, end: slice.end, durationMs: dur });
|
|
358
432
|
}
|
|
359
433
|
}
|
|
360
434
|
}
|
|
361
|
-
flushInTurn();
|
|
362
435
|
return bands;
|
|
363
|
-
}, [events]);
|
|
436
|
+
}, [events, session.subagents]);
|
|
364
437
|
|
|
365
438
|
const coldResumeMarkers = useMemo(() => {
|
|
366
439
|
const seen = new Set<string>();
|
|
@@ -2668,14 +2741,15 @@ function TranscriptList({
|
|
|
2668
2741
|
}
|
|
2669
2742
|
|
|
2670
2743
|
const idleBandList = (rawIdleBands ?? []).slice().sort((a, b) => a.start - b.start);
|
|
2671
|
-
/** Returns the
|
|
2672
|
-
* the given row's tOffset
|
|
2673
|
-
*
|
|
2674
|
-
|
|
2675
|
-
|
|
2744
|
+
/** Returns the first matching idle band (or null) whose end falls within
|
|
2745
|
+
* 1s of the given row's tOffset. Tolerance absorbs Date.parse rounding
|
|
2746
|
+
* between anchor.ms and the presentation row's tOffsetMs. */
|
|
2747
|
+
const idleBandBeforeOffset = (
|
|
2748
|
+
tOff: number | undefined,
|
|
2749
|
+
): { start: number; durationMs: number } | null => {
|
|
2676
2750
|
if (tOff === undefined) return null;
|
|
2677
2751
|
for (const b of idleBandList) {
|
|
2678
|
-
if (Math.abs(b.end - tOff) < 1000) return b
|
|
2752
|
+
if (Math.abs(b.end - tOff) < 1000) return b;
|
|
2679
2753
|
}
|
|
2680
2754
|
return null;
|
|
2681
2755
|
};
|
|
@@ -2687,6 +2761,11 @@ function TranscriptList({
|
|
|
2687
2761
|
};
|
|
2688
2762
|
|
|
2689
2763
|
const out: React.ReactNode[] = [];
|
|
2764
|
+
// A user row and the turn-collapsed row that follows it share a
|
|
2765
|
+
// tOffsetMs (turn anchors at the user message — see buildMegaRows), so
|
|
2766
|
+
// both would match the same between-turn band without this guard. Track
|
|
2767
|
+
// emitted band starts and skip duplicates.
|
|
2768
|
+
const emittedBandStarts = new Set<number>();
|
|
2690
2769
|
for (let i = 0; i < displayRows.length; i++) {
|
|
2691
2770
|
const d = displayRows[i];
|
|
2692
2771
|
// Emit a "Session idle" divider before any row that starts right after
|
|
@@ -2694,9 +2773,14 @@ function TranscriptList({
|
|
|
2694
2773
|
// expanded turn doesn't render dividers for its inner steps.
|
|
2695
2774
|
const indented = d.kind === "presentation" ? d.indented : false;
|
|
2696
2775
|
if (!indented) {
|
|
2697
|
-
const
|
|
2698
|
-
if (
|
|
2699
|
-
|
|
2776
|
+
const band = idleBandBeforeOffset(rowTOffset(d));
|
|
2777
|
+
if (
|
|
2778
|
+
band !== null &&
|
|
2779
|
+
band.durationMs > IDLE_THRESHOLD_MS &&
|
|
2780
|
+
!emittedBandStarts.has(band.start)
|
|
2781
|
+
) {
|
|
2782
|
+
emittedBandStarts.add(band.start);
|
|
2783
|
+
out.push(<IdleDivider key={`idle-before-${i}`} gapMs={band.durationMs} />);
|
|
2700
2784
|
}
|
|
2701
2785
|
}
|
|
2702
2786
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTOGENERATED by scripts/build-changelog.mjs from CHANGELOG.md.
|
|
2
2
|
// Do not edit by hand — run `pnpm changelog:sync`.
|
|
3
|
-
export const RAW_CHANGELOG = "# Changelog\n\nAll notable user-facing changes to the Fleetlens CLI (`fleetlens` on npm) are\nrecorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\nThe team-server has its own log at `packages/team-server/CHANGELOG.md`.\n\n## [0.10.1] — 2026-05-14\n\n### Added\n- **Alternative claude runtime via tmux.** When `tmux` is on PATH, both the LLM pipeline (digest synth, entry enrichment, top-session perception) and the `/ask` feature now drive `claude` through a detached tmux session instead of `claude -p`. Sessions produced this way carry `entrypoint: cli` in their JSONL, which is also extracted by the parser and surfaced as a small badge on `/sessions/<id>` next to the model chip — green for `cli` / `claude-desktop`, amber for `sdk-*`. The path falls back to `claude -p` whenever tmux is unavailable or the run errors; set `FLEETLENS_FORCE_PRINT_MODE=1` to disable the tmux path entirely.\n- **`CCLENS_HOME` env override.** All `~/.cclens/` state (pid file, daemon log, perception state, usage snapshot log, llm-runs traces) is now reached through a shared `cclensHome()` helper that honors `CCLENS_HOME`. Lets multiple installs / workspaces run side-by-side without stomping on each other's state.\n\n### Fixed\n- `/api/runs` rendered every successful tmux-driven run as `status=error`. Tmux runs have no subprocess exit code (the session is killed by cleanup), and the runs viewer was treating a missing code as failure. End records now stamp `exit_code: 0` on success and the active-process filter no longer requires the literal `claude -p` flag, so tmux-driven processes also show up in the \"active\" panel while they're running.\n- `JobQueueWidget`'s `/api/jobs` polling effect listed `jobs` in its deps, so each tick re-armed the effect on the freshly-set state ref — effective cadence collapsed to a 1–4 ms refetch loop. Now tracks `hasActive` in a closure-local var and runs the effect once on mount.\n\n### Conductor\n- New `conductor.json` + `scripts/conductor-{setup,run,archive}.sh` so each Conductor workspace allocates its own 3-port band (web / team-server / postgres) and boots its own `fleetlens-<workspace>` Compose stack against `.harness/cclens-state`. Replaces ~30 hard-coded `join(homedir(), \".cclens\", …)` call sites with the shared `cclensHome()/cclensPath()` helper so a second workspace can boot without colliding with the user's primary install.\n\n## [0.10.0] — 2026-05-08\n\n### Changed\n- **Plan utilization reframed.** High utilization is now \"getting plan value\" (green); low utilization is \"paying for unused headroom\" (red). Mirrors how an operator actually looks at the dashboard: the wasteful outcome is paying for a plan and barely touching it, not running near the cap. Applied across `/usage` burndown, gauges, sidebar, and previous-cycles strip.\n- **In-progress cycles colored by pace, not peak.** A 50% peak halfway through a 7-day cycle reads on-pace green, not amber — the chart no longer cries wolf on a fresh cycle that just hasn't burnt yet. Completed cycles still use peak tone.\n- **Pace label ±15pp band.** \"On pace\" now covers a ±15pp window around ideal; outside that, label is \"below pace\" (under-burning) or \"may exhaust early\" (over-burning). The previous +5/-50 thresholds flipped amber every time the user took a night off.\n- **Pace label collapses to three states.** \"May exhaust early\" / \"below pace\" / \"on pace\" — same color tones, one fewer qualifier to read.\n- **Wording softened across the utilization views** to describe the pattern rather than judge the user. \"Behind schedule\" → \"below pace\"; \"on track (+x%)\" → \"on pace (+xpp vs ideal)\".\n\n### Added\n- **Conductor multi-agent workspace support.** Sessions inside Conductor's worktree-style agent workspaces (`~/conductor/workspaces/<repo>/<agent>/...`) now group correctly under their parent project, with each agent shown as a separate worktree. Mirrors the existing `.worktrees/` handling.\n\n### Fixed\n- Hydration mismatch on `/usage` when pace labels rendered before client mount.\n\n### Notes for operators\n- The CLI's team push now ships **only Claude Code usage snapshots** to the team server. Codex snapshots stay local until `plan_utilization` gets a per-agent partition — without one, the team-server's \"latest snapshot\" pick flips between agents and miscolors the in-progress cycle. Existing rows in `plan_utilization` stay; new pushes are claude-code only.\n\n## [0.9.0] — 2026-05-07\n\n### Added\n- **Gemini CLI** as the third agent source. Sessions written to `~/.gemini/tmp/<slug>/chats/session-*.jsonl` are now read, parsed, and rendered alongside Claude Code and Codex. Tool calls (`read_file`, `list_directory`, `glob`, `run_shell_command`, `update_topic`, …) and their results show in the session timeline; thinking blocks, multi-write status replay, `$set` / `$rewindTo` semantics, and the legacy single-JSON file shape all handled. Slug → cwd inversion via `~/.gemini/projects.json`. `✺` glyph + purple accent in the UI.\n\n### Changed\n- Session detail timeline drops in-turn idle hatching. Within a turn, every wait — model loading, tool execution, network — is the agent's active time. Only between-turn idle (user reading + composing the next message) renders as hatched.\n- Gemini parser anchors `firstTimestamp`, `lastTimestamp`, and `activeSegments` on conversational events only. Login/OAuth `info` records and `metadata.startTime` (file-creation moment) no longer stretch the session bar back to before the first user message.\n\n## [0.7.1] — 2026-05-07\n\n### Fixed\n- Cycle-peaks trend no longer renders two bars labeled the same day. When Anthropic's rolling 7-day window slid its anchor between consecutive polls, the daemon's bucketer (hour-rounded) recorded both reset boundaries as distinct cycles. They're now merged within a 12 h tolerance — same fix in `apps/web/lib/cycle-peaks.ts` (used by `/usage`) and in `packages/cli/src/team/sync.ts` (the daemon's team-server push), so both the personal `/usage` chart and the team-server's plan-utilization view stay consistent. 5-hour cycles keep no merge tolerance (their anchor is stable; merging would collapse legitimate distinct cycles).\n\n## [0.6.4] — 2026-05-04\n\n### Fixed\n- Cached calibration events per-file so `/usage` warm renders complete in well under a second on large session histories.\n\n## [0.6.3] — 2026-05-04\n\n### Fixed\n- Dropped poisoned auto-week locks left behind by interrupted runs; backfill now gates on the live pipeline lock instead.\n\n## [0.6.2] — 2026-05-04\n\n### Added\n- Last week's digest auto-generates on launch when AI features are on. Disable in Settings → AI features.\n\n## [0.6.1] — 2026-05-04\n\n### Changed\n- Smaller install size — published bundle no longer drags in unused workspace metadata.\n\n## [0.6.0] — 2026-04-30\n\n### Added\n- V2 perception layer. Daily, weekly, and monthly digests synthesized from per-session entries via a strict hierarchical pipeline (entry → day → week → month). Past-period digests are immutable on disk; current-period digests use a 10-min in-memory TTL.\n- `/insights` route with on-demand week and month digest generation, picker history, and force-regen.\n- `fleetlens digest day | week | month [--json]` — CLI surface that reproduces the same digests served at `/insights`.\n- `fleetlens entries [--day | --session | --all] [--json]` — inspect the per-session entry primitive.\n\n### Changed\n- Renamed \"active time\" to \"agent time\" across the dashboard and CLI for clarity.\n\n## [0.4.1] — 2026-04-23\n\n### Added\n- Concurrency view on `/parallelism` page. Bursts colored teal for same-project and purple for cross-project (the genuinely interesting fleet-work signal).\n- Plan utilization sparkline on the sidebar, fed by the usage daemon's 5-min snapshots.\n\n### Fixed\n- Auto-update no longer leaves a zombie web server on the old binary; both server and daemon are torn down before re-exec.\n\n## [0.4.0] — 2026-04-18\n\n### Added\n- Usage daemon (`fleetlens daemon …`) polls `/api/oauth/usage` every 5 min and appends snapshots to `~/.cclens/usage.jsonl`. `fleetlens start` and `fleetlens stop` manage server + daemon together.\n- `fleetlens usage --history` for inspecting past plan utilization.\n\n## [0.3.0] — 2026-04-10\n\n### Added\n- Project rollups with worktree aggregation. Sessions inside `<project>/.worktrees/<name>` roll up under their parent canonical project with a `+N wt` badge.\n- Pinned projects in the sidebar (per-browser via localStorage).\n\n## [0.2.0] — 2026-04-01\n\n### Changed\n- Active-segment computation now uses all timestamped events (system + summary + sidechain + conversational) instead of conversational-only. Earlier filter undercounted by up to 100x on tool-heavy sessions.\n\n## [0.1.0] — 2026-03-15\n\n### Added\n- Initial release. Local-only dashboard for Claude Code sessions: timeline, projects, daily activity bucketing, parallelism detection, per-model cost estimation. Reads JSONL transcripts from `~/.claude/projects/`.\n";
|
|
3
|
+
export const RAW_CHANGELOG = "# Changelog\n\nAll notable user-facing changes to the Fleetlens CLI (`fleetlens` on npm) are\nrecorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\nThe team-server has its own log at `packages/team-server/CHANGELOG.md`.\n\n## [0.10.2] — 2026-05-15\n\n### Fixed\n- **Session-detail minimap no longer paints subagent runtime as idle.** Long agent runs that dispatched many subagents were reading as wall-to-wall \"Session idle\" stripes because the parent's tool_use → tool_result gap (the subagent's actual runtime) was classified as in-turn idle. `rawIdleBands` now carves subagent run spans out of every candidate gap; for in-turn gaps that overlap any subagent run the entire gap is dropped, and between-turn gaps with background subagent activity keep only the genuinely-unwatched residue.\n- **First-response thinking stops registering as idle.** Anchors now include every timestamped event (agent-thinking, meta, etc.), and the loop tracks an \"awaiting first response\" phase across intermediate thinking anchors. A `user → thinking → thinking → agent` sequence where the model takes a while to compose its first reply is now treated as response latency rather than painted as a stripe.\n- **Turn duration no longer shows `0ms` for single-row turns.** `buildMegaRows` now anchors a turn's `tOffsetMs` and `durationMs` at the originating user message rather than at the first agent row. Single-row turns show the actual user → agent latency, and the visual span of the turn rectangle on the minimap covers the whole arc.\n- **Single `Session idle` divider per band.** A user row and the turn-collapsed row that follows it now share a `tOffsetMs`, so the body's `IdleDivider` emitter dedupes per band to stop two consecutive identical idle markers around the same boundary.\n\n### Changed\n- Minimap idle threshold raised from 10s to 30s. Ordinary tool latency (slow Bash, big-file Read, between-anchor model thinking) no longer registers as idle.\n\n## [0.10.1] — 2026-05-14\n\n### Added\n- **Alternative claude runtime via tmux.** When `tmux` is on PATH, both the LLM pipeline (digest synth, entry enrichment, top-session perception) and the `/ask` feature now drive `claude` through a detached tmux session instead of `claude -p`. Sessions produced this way carry `entrypoint: cli` in their JSONL, which is also extracted by the parser and surfaced as a small badge on `/sessions/<id>` next to the model chip — green for `cli` / `claude-desktop`, amber for `sdk-*`. The path falls back to `claude -p` whenever tmux is unavailable or the run errors; set `FLEETLENS_FORCE_PRINT_MODE=1` to disable the tmux path entirely.\n- **`CCLENS_HOME` env override.** All `~/.cclens/` state (pid file, daemon log, perception state, usage snapshot log, llm-runs traces) is now reached through a shared `cclensHome()` helper that honors `CCLENS_HOME`. Lets multiple installs / workspaces run side-by-side without stomping on each other's state.\n\n### Fixed\n- `/api/runs` rendered every successful tmux-driven run as `status=error`. Tmux runs have no subprocess exit code (the session is killed by cleanup), and the runs viewer was treating a missing code as failure. End records now stamp `exit_code: 0` on success and the active-process filter no longer requires the literal `claude -p` flag, so tmux-driven processes also show up in the \"active\" panel while they're running.\n- `JobQueueWidget`'s `/api/jobs` polling effect listed `jobs` in its deps, so each tick re-armed the effect on the freshly-set state ref — effective cadence collapsed to a 1–4 ms refetch loop. Now tracks `hasActive` in a closure-local var and runs the effect once on mount.\n\n### Conductor\n- New `conductor.json` + `scripts/conductor-{setup,run,archive}.sh` so each Conductor workspace allocates its own 3-port band (web / team-server / postgres) and boots its own `fleetlens-<workspace>` Compose stack against `.harness/cclens-state`. Replaces ~30 hard-coded `join(homedir(), \".cclens\", …)` call sites with the shared `cclensHome()/cclensPath()` helper so a second workspace can boot without colliding with the user's primary install.\n\n## [0.10.0] — 2026-05-08\n\n### Changed\n- **Plan utilization reframed.** High utilization is now \"getting plan value\" (green); low utilization is \"paying for unused headroom\" (red). Mirrors how an operator actually looks at the dashboard: the wasteful outcome is paying for a plan and barely touching it, not running near the cap. Applied across `/usage` burndown, gauges, sidebar, and previous-cycles strip.\n- **In-progress cycles colored by pace, not peak.** A 50% peak halfway through a 7-day cycle reads on-pace green, not amber — the chart no longer cries wolf on a fresh cycle that just hasn't burnt yet. Completed cycles still use peak tone.\n- **Pace label ±15pp band.** \"On pace\" now covers a ±15pp window around ideal; outside that, label is \"below pace\" (under-burning) or \"may exhaust early\" (over-burning). The previous +5/-50 thresholds flipped amber every time the user took a night off.\n- **Pace label collapses to three states.** \"May exhaust early\" / \"below pace\" / \"on pace\" — same color tones, one fewer qualifier to read.\n- **Wording softened across the utilization views** to describe the pattern rather than judge the user. \"Behind schedule\" → \"below pace\"; \"on track (+x%)\" → \"on pace (+xpp vs ideal)\".\n\n### Added\n- **Conductor multi-agent workspace support.** Sessions inside Conductor's worktree-style agent workspaces (`~/conductor/workspaces/<repo>/<agent>/...`) now group correctly under their parent project, with each agent shown as a separate worktree. Mirrors the existing `.worktrees/` handling.\n\n### Fixed\n- Hydration mismatch on `/usage` when pace labels rendered before client mount.\n\n### Notes for operators\n- The CLI's team push now ships **only Claude Code usage snapshots** to the team server. Codex snapshots stay local until `plan_utilization` gets a per-agent partition — without one, the team-server's \"latest snapshot\" pick flips between agents and miscolors the in-progress cycle. Existing rows in `plan_utilization` stay; new pushes are claude-code only.\n\n## [0.9.0] — 2026-05-07\n\n### Added\n- **Gemini CLI** as the third agent source. Sessions written to `~/.gemini/tmp/<slug>/chats/session-*.jsonl` are now read, parsed, and rendered alongside Claude Code and Codex. Tool calls (`read_file`, `list_directory`, `glob`, `run_shell_command`, `update_topic`, …) and their results show in the session timeline; thinking blocks, multi-write status replay, `$set` / `$rewindTo` semantics, and the legacy single-JSON file shape all handled. Slug → cwd inversion via `~/.gemini/projects.json`. `✺` glyph + purple accent in the UI.\n\n### Changed\n- Session detail timeline drops in-turn idle hatching. Within a turn, every wait — model loading, tool execution, network — is the agent's active time. Only between-turn idle (user reading + composing the next message) renders as hatched.\n- Gemini parser anchors `firstTimestamp`, `lastTimestamp`, and `activeSegments` on conversational events only. Login/OAuth `info` records and `metadata.startTime` (file-creation moment) no longer stretch the session bar back to before the first user message.\n\n## [0.7.1] — 2026-05-07\n\n### Fixed\n- Cycle-peaks trend no longer renders two bars labeled the same day. When Anthropic's rolling 7-day window slid its anchor between consecutive polls, the daemon's bucketer (hour-rounded) recorded both reset boundaries as distinct cycles. They're now merged within a 12 h tolerance — same fix in `apps/web/lib/cycle-peaks.ts` (used by `/usage`) and in `packages/cli/src/team/sync.ts` (the daemon's team-server push), so both the personal `/usage` chart and the team-server's plan-utilization view stay consistent. 5-hour cycles keep no merge tolerance (their anchor is stable; merging would collapse legitimate distinct cycles).\n\n## [0.6.4] — 2026-05-04\n\n### Fixed\n- Cached calibration events per-file so `/usage` warm renders complete in well under a second on large session histories.\n\n## [0.6.3] — 2026-05-04\n\n### Fixed\n- Dropped poisoned auto-week locks left behind by interrupted runs; backfill now gates on the live pipeline lock instead.\n\n## [0.6.2] — 2026-05-04\n\n### Added\n- Last week's digest auto-generates on launch when AI features are on. Disable in Settings → AI features.\n\n## [0.6.1] — 2026-05-04\n\n### Changed\n- Smaller install size — published bundle no longer drags in unused workspace metadata.\n\n## [0.6.0] — 2026-04-30\n\n### Added\n- V2 perception layer. Daily, weekly, and monthly digests synthesized from per-session entries via a strict hierarchical pipeline (entry → day → week → month). Past-period digests are immutable on disk; current-period digests use a 10-min in-memory TTL.\n- `/insights` route with on-demand week and month digest generation, picker history, and force-regen.\n- `fleetlens digest day | week | month [--json]` — CLI surface that reproduces the same digests served at `/insights`.\n- `fleetlens entries [--day | --session | --all] [--json]` — inspect the per-session entry primitive.\n\n### Changed\n- Renamed \"active time\" to \"agent time\" across the dashboard and CLI for clarity.\n\n## [0.4.1] — 2026-04-23\n\n### Added\n- Concurrency view on `/parallelism` page. Bursts colored teal for same-project and purple for cross-project (the genuinely interesting fleet-work signal).\n- Plan utilization sparkline on the sidebar, fed by the usage daemon's 5-min snapshots.\n\n### Fixed\n- Auto-update no longer leaves a zombie web server on the old binary; both server and daemon are torn down before re-exec.\n\n## [0.4.0] — 2026-04-18\n\n### Added\n- Usage daemon (`fleetlens daemon …`) polls `/api/oauth/usage` every 5 min and appends snapshots to `~/.cclens/usage.jsonl`. `fleetlens start` and `fleetlens stop` manage server + daemon together.\n- `fleetlens usage --history` for inspecting past plan utilization.\n\n## [0.3.0] — 2026-04-10\n\n### Added\n- Project rollups with worktree aggregation. Sessions inside `<project>/.worktrees/<name>` roll up under their parent canonical project with a `+N wt` badge.\n- Pinned projects in the sidebar (per-browser via localStorage).\n\n## [0.2.0] — 2026-04-01\n\n### Changed\n- Active-segment computation now uses all timestamped events (system + summary + sidechain + conversational) instead of conversational-only. Earlier filter undercounted by up to 100x on tool-heavy sessions.\n\n## [0.1.0] — 2026-03-15\n\n### Added\n- Initial release. Local-only dashboard for Claude Code sessions: timeline, projects, daily activity bucketing, parallelism detection, per-model cost estimation. Reads JSONL transcripts from `~/.claude/projects/`.\n";
|