fleetlens 0.13.2 → 0.14.0

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 (136) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +0 -1
  3. package/app/apps/web/.next/build-manifest.json +3 -3
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/routes-manifest.json +0 -6
  6. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
  14. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  17. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  18. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  20. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  21. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  22. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
  23. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  24. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/settings/route.js +3 -3
  26. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  27. package/app/apps/web/.next/server/app/changelog/page.js +1 -1
  28. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  29. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  30. package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
  31. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  32. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  33. package/app/apps/web/.next/server/app/day/page.js +1 -1
  34. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  36. package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
  37. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  38. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  39. package/app/apps/web/.next/server/app/digest/page.js +1 -1
  40. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  41. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  42. package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
  43. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  44. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  45. package/app/apps/web/.next/server/app/insights/page.js +1 -1
  46. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  47. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  48. package/app/apps/web/.next/server/app/page.js +2 -2
  49. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  50. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  51. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  52. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  53. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  54. package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
  55. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  56. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  57. package/app/apps/web/.next/server/app/projects/[slug]/page.js +2 -2
  58. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  59. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  60. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  61. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  62. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  63. package/app/apps/web/.next/server/app/runs/page.js +1 -1
  64. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  65. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  66. package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
  67. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  68. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  69. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  70. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  71. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  72. package/app/apps/web/.next/server/app/settings/page.js +1 -1
  73. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  74. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  75. package/app/apps/web/.next/server/app/team/page.js +2 -2
  76. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  77. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  78. package/app/apps/web/.next/server/app/usage/page.js +1 -1
  79. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  80. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  81. package/app/apps/web/.next/server/app-paths-manifest.json +0 -1
  82. package/app/apps/web/.next/server/chunks/[externals]__0o43ejh._.js +3 -0
  83. package/app/apps/web/.next/server/chunks/node_modules_next_036my~r._.js +49 -0
  84. package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
  85. package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
  86. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0z7mg0b._.js → [root-of-the-server]__08l86e-._.js} +2 -2
  87. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0_hv6aq._.js +3 -0
  88. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0dh0q7b._.js +3 -0
  89. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
  90. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0yv0m0q._.js +3 -0
  91. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0o03ymy._.js → [root-of-the-server]__10~b4bf._.js} +2 -2
  92. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  93. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  94. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  95. package/app/apps/web/.next/server/chunks/ssr/{node_modules_next_dist_0t-hj0x._.js → node_modules_0yrkyef._.js} +2 -2
  96. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
  97. package/app/apps/web/.next/server/functions-config-manifest.json +0 -1
  98. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  99. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  100. package/app/apps/web/.next/server/pages/500.html +1 -1
  101. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  102. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  103. package/app/apps/web/.next/static/chunks/005ad7l-h1-.6.js +1 -0
  104. package/app/apps/web/.next/static/chunks/0l4r0bqqtdju..css +1 -0
  105. package/app/apps/web/app/team/page.tsx +23 -75
  106. package/app/apps/web/components/previous-cycles-trend.tsx +4 -1
  107. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  108. package/app/apps/web/lib/team-config.ts +0 -2
  109. package/app/apps/web/package.json +1 -1
  110. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  111. package/dist/daemon-worker.js +1970 -1611
  112. package/dist/index.js +1182 -161
  113. package/package.json +1 -1
  114. package/app/apps/web/.next/server/app/api/team-config/route/app-paths-manifest.json +0 -3
  115. package/app/apps/web/.next/server/app/api/team-config/route/build-manifest.json +0 -9
  116. package/app/apps/web/.next/server/app/api/team-config/route/server-reference-manifest.json +0 -4
  117. package/app/apps/web/.next/server/app/api/team-config/route.js +0 -8
  118. package/app/apps/web/.next/server/app/api/team-config/route.js.map +0 -5
  119. package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +0 -1
  120. package/app/apps/web/.next/server/app/api/team-config/route_client-reference-manifest.js +0 -3
  121. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0atn.dt._.js +0 -3
  122. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b.b7~1._.js +0 -3
  123. package/app/apps/web/.next/server/chunks/_0euqdko._.js +0 -49
  124. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team-config_route_actions_0ad2gak.js +0 -3
  125. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +0 -3
  126. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +0 -3
  127. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0q9qy0z._.js +0 -3
  128. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_team_team-sync-form_tsx_0choz0h._.js +0 -3
  129. package/app/apps/web/.next/static/chunks/083~hzkv577.5.css +0 -1
  130. package/app/apps/web/.next/static/chunks/0uyctl-m8iydb.js +0 -1
  131. package/app/apps/web/.next/static/chunks/14joqbsr9ihhu.js +0 -1
  132. package/app/apps/web/app/api/team-config/route.ts +0 -39
  133. package/app/apps/web/app/team/team-sync-form.tsx +0 -144
  134. /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → 3VbAChKD8EChlt8Tm5N5r}/_buildManifest.js +0 -0
  135. /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → 3VbAChKD8EChlt8Tm5N5r}/_clientMiddlewareManifest.js +0 -0
  136. /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → 3VbAChKD8EChlt8Tm5N5r}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -4002,13 +4002,138 @@ async function readJsonl2(filePath) {
4002
4002
  }
4003
4003
  return out;
4004
4004
  }
4005
+ function dedupeReplayedLines(lines) {
4006
+ const keepAt = /* @__PURE__ */ new Map();
4007
+ lines.forEach((line, i) => {
4008
+ const rec = line;
4009
+ if (typeof rec.uuid !== "string")
4010
+ return;
4011
+ const prev = keepAt.get(rec.uuid);
4012
+ if (prev === void 0) {
4013
+ keepAt.set(rec.uuid, i);
4014
+ return;
4015
+ }
4016
+ const prevHasTs = lines[prev].timestamp !== void 0;
4017
+ if (!prevHasTs && rec.timestamp !== void 0)
4018
+ keepAt.set(rec.uuid, i);
4019
+ });
4020
+ return lines.filter((line, i) => {
4021
+ const rec = line;
4022
+ return typeof rec.uuid !== "string" || keepAt.get(rec.uuid) === i;
4023
+ });
4024
+ }
4025
+ function textFromContent(content) {
4026
+ if (typeof content === "string")
4027
+ return content;
4028
+ if (Array.isArray(content)) {
4029
+ const parts = [];
4030
+ for (const b of content) {
4031
+ if (b && typeof b === "object" && b.type === "text") {
4032
+ const t = b.text;
4033
+ if (typeof t === "string")
4034
+ parts.push(t);
4035
+ }
4036
+ }
4037
+ if (parts.length)
4038
+ return parts.join("\n");
4039
+ }
4040
+ return void 0;
4041
+ }
4042
+ function partitionCoworkLines(lines) {
4043
+ const mainLines = [];
4044
+ const subagentGroups = /* @__PURE__ */ new Map();
4045
+ for (const line of lines) {
4046
+ const ptid = line.parent_tool_use_id;
4047
+ if (typeof ptid === "string") {
4048
+ const g = subagentGroups.get(ptid);
4049
+ if (g)
4050
+ g.push(line);
4051
+ else
4052
+ subagentGroups.set(ptid, [line]);
4053
+ } else {
4054
+ mainLines.push(line);
4055
+ }
4056
+ }
4057
+ return { mainLines, subagentGroups };
4058
+ }
4059
+ function buildCoworkSubagents(subagentGroups, allLines, sessionStartMs) {
4060
+ if (subagentGroups.size === 0)
4061
+ return [];
4062
+ const refs = /* @__PURE__ */ new Map();
4063
+ for (const line of allLines) {
4064
+ const rec = line;
4065
+ const content = rec.message?.content;
4066
+ if (!Array.isArray(content))
4067
+ continue;
4068
+ for (const b of content) {
4069
+ if (!b || typeof b !== "object")
4070
+ continue;
4071
+ const block = b;
4072
+ if (block.type === "tool_use" && block.name === "Agent" && typeof block.id === "string") {
4073
+ const input = block.input ?? {};
4074
+ const ref = refs.get(block.id) ?? {};
4075
+ if (typeof rec.uuid === "string")
4076
+ ref.parentUuid = rec.uuid;
4077
+ if (typeof input.description === "string")
4078
+ ref.description = input.description;
4079
+ if (typeof input.subagent_type === "string")
4080
+ ref.agentType = input.subagent_type;
4081
+ if (typeof input.prompt === "string")
4082
+ ref.prompt = input.prompt;
4083
+ refs.set(block.id, ref);
4084
+ } else if (block.type === "tool_result" && typeof block.tool_use_id === "string" && subagentGroups.has(block.tool_use_id)) {
4085
+ const ref = refs.get(block.tool_use_id) ?? {};
4086
+ ref.finalText = textFromContent(block.content) ?? ref.finalText;
4087
+ refs.set(block.tool_use_id, ref);
4088
+ }
4089
+ }
4090
+ }
4091
+ const runs2 = [];
4092
+ for (const [toolUseId, groupLines] of subagentGroups) {
4093
+ const summary = summarizeSubagentLines(groupLines);
4094
+ const ref = refs.get(toolUseId) ?? {};
4095
+ const finalText = ref.finalText ?? summary.finalText;
4096
+ const finalPreview = finalText ? finalText.replace(/\s+/g, " ").trim().slice(0, 240) : summary.finalPreview;
4097
+ const startTOffsetMs = sessionStartMs !== void 0 && summary.startMs !== void 0 ? Math.max(0, summary.startMs - sessionStartMs) : void 0;
4098
+ const endTOffsetMs = sessionStartMs !== void 0 && summary.endMs !== void 0 ? Math.max(0, summary.endMs - sessionStartMs) : void 0;
4099
+ const durationMs = summary.startMs !== void 0 && summary.endMs !== void 0 ? summary.endMs - summary.startMs : void 0;
4100
+ runs2.push({
4101
+ agentId: toolUseId,
4102
+ agentType: ref.agentType ?? "unknown",
4103
+ description: ref.description ?? (ref.prompt ? ref.prompt.replace(/\s+/g, " ").trim().slice(0, 80) : "(no description)"),
4104
+ startMs: summary.startMs,
4105
+ endMs: summary.endMs,
4106
+ durationMs,
4107
+ startTOffsetMs,
4108
+ endTOffsetMs,
4109
+ eventCount: summary.eventCount,
4110
+ totalUsage: summary.totalUsage,
4111
+ parentUuid: ref.parentUuid,
4112
+ parentToolUseId: toolUseId,
4113
+ runInBackground: false,
4114
+ finalPreview,
4115
+ finalText,
4116
+ prompt: ref.prompt,
4117
+ model: summary.model,
4118
+ toolCalls: summary.toolCalls,
4119
+ toolCallCount: summary.toolCallCount,
4120
+ assistantMessageCount: summary.assistantMessageCount
4121
+ });
4122
+ }
4123
+ runs2.sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
4124
+ return runs2;
4125
+ }
4005
4126
  async function parseCoworkSession(file) {
4006
4127
  const [lines, coworkMeta, spaceIdToPath] = await Promise.all([
4007
4128
  readJsonl2(file.auditPath),
4008
4129
  readCoworkMeta(file.metaPath),
4009
4130
  loadSpacesForWorkspace(file.workspaceDir)
4010
4131
  ]);
4011
- const { meta: baseMeta, events } = parseTranscript(lines);
4132
+ const deduped = dedupeReplayedLines(lines);
4133
+ const { mainLines, subagentGroups } = partitionCoworkLines(deduped);
4134
+ const { meta: baseMeta, events } = parseTranscript(mainLines);
4135
+ const sessionStartMs = baseMeta.firstTimestamp ? Date.parse(baseMeta.firstTimestamp) : void 0;
4136
+ const subagents = buildCoworkSubagents(subagentGroups, deduped, sessionStartMs);
4012
4137
  const { projectName, projectDir, cwd } = resolveProject(coworkMeta, spaceIdToPath);
4013
4138
  const meta = {
4014
4139
  ...baseMeta,
@@ -4021,7 +4146,7 @@ async function parseCoworkSession(file) {
4021
4146
  cwd: cwd ?? baseMeta.cwd,
4022
4147
  model: baseMeta.model ?? coworkMeta.model
4023
4148
  };
4024
- return { meta, events };
4149
+ return { meta, events, subagents };
4025
4150
  }
4026
4151
  function cacheKeyFor(file) {
4027
4152
  return {
@@ -4072,8 +4197,12 @@ async function getCoworkSession(id, opts = {}) {
4072
4197
  const cached = detailCache5.get(file.auditPath);
4073
4198
  if (cached && cacheKeyMatches(cached.key, key))
4074
4199
  return cached.detail;
4075
- const { meta, events } = await parseCoworkSession(file);
4076
- const detail = { ...meta, events };
4200
+ const { meta, events, subagents } = await parseCoworkSession(file);
4201
+ const detail = {
4202
+ ...meta,
4203
+ events,
4204
+ ...subagents.length ? { subagents } : {}
4205
+ };
4077
4206
  detailCache5.set(file.auditPath, { detail, key });
4078
4207
  return detail;
4079
4208
  }
@@ -4082,6 +4211,7 @@ var init_cowork = __esm({
4082
4211
  "../parser/dist/cowork.js"() {
4083
4212
  "use strict";
4084
4213
  init_analytics();
4214
+ init_claude_code();
4085
4215
  init_parser();
4086
4216
  DEFAULT_COWORK_ROOT = defaultCoworkRoot();
4087
4217
  SESSION_ID_RE = /^local_[0-9a-f-]+$/;
@@ -4400,9 +4530,14 @@ function buildCalibrationCurve(events, snapshots, tier = "pro-max-20x", granular
4400
4530
  curve.push({
4401
4531
  ts: new Date(cur).toISOString(),
4402
4532
  real_5h: real5,
4403
- pred_5h: Math.max(0, Math.min(200, p5)),
4533
+ // Predicted utilization LEGITIMATELY exceeds 200 on overage (the
4534
+ // extrapolated cycle close). The old min(200) clamp silently capped it,
4535
+ // so cyclePeaks.peakPct could never carry a *predicted* overage to the
4536
+ // team server (which now accepts up to 10000). Keep only a generous
4537
+ // finite ceiling as a corruption guard, matching the wire cap.
4538
+ pred_5h: Math.max(0, Math.min(1e4, p5)),
4404
4539
  real_7d: real7,
4405
- pred_7d: Math.max(0, Math.min(200, p7)),
4540
+ pred_7d: Math.max(0, Math.min(1e4, p7)),
4406
4541
  cycle_end_5h: r5k != null ? new Date(r5k).toISOString() : null,
4407
4542
  cycle_end_7d: r7k != null ? new Date(r7k).toISOString() : null
4408
4543
  });
@@ -4542,7 +4677,7 @@ function getServerStatus() {
4542
4677
  return { running: false };
4543
4678
  }
4544
4679
  function isServerStale(status2) {
4545
- return status2.running === true && status2.version !== "0.13.2";
4680
+ return status2.running === true && status2.version !== "0.14.0";
4546
4681
  }
4547
4682
  async function ensureCurrentServer(opts = {}) {
4548
4683
  const status2 = getServerStatus();
@@ -4598,7 +4733,7 @@ async function startServer(opts = {}) {
4598
4733
  });
4599
4734
  child.unref();
4600
4735
  const pid = child.pid;
4601
- writePid(PID_FILE, pid, port, "0.13.2");
4736
+ writePid(PID_FILE, pid, port, "0.14.0");
4602
4737
  await waitForHealth(`http://localhost:${port}`, 1e4);
4603
4738
  return { pid, port };
4604
4739
  }
@@ -4958,9 +5093,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
4958
5093
  console.warn(
4959
5094
  ` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
4960
5095
  );
4961
- } else if (verify.installedVersion !== "0.13.2") {
5096
+ } else if (verify.installedVersion !== "0.14.0") {
4962
5097
  console.log(
4963
- ` \u2192 This process is still running ${"0.13.2"}. Next invocation will use ${verify.installedVersion}.`
5098
+ ` \u2192 This process is still running ${"0.14.0"}. Next invocation will use ${verify.installedVersion}.`
4964
5099
  );
4965
5100
  }
4966
5101
  }
@@ -4995,14 +5130,14 @@ async function getUpdateAvailability() {
4995
5130
  if (isDevMode()) return null;
4996
5131
  const latest = await fetchLatestVersion();
4997
5132
  if (latest === null) return {
4998
- current: "0.13.2",
5133
+ current: "0.14.0",
4999
5134
  latest: null,
5000
5135
  updateAvailable: false
5001
5136
  };
5002
5137
  return {
5003
- current: "0.13.2",
5138
+ current: "0.14.0",
5004
5139
  latest,
5005
- updateAvailable: shouldUpdate("0.13.2", latest)
5140
+ updateAvailable: shouldUpdate("0.14.0", latest)
5006
5141
  };
5007
5142
  }
5008
5143
  async function checkForUpdate() {
@@ -5092,7 +5227,7 @@ async function restartServices(state) {
5092
5227
  }
5093
5228
  async function forceUpdate() {
5094
5229
  const latest = await fetchLatestVersion();
5095
- const current = "0.13.2";
5230
+ const current = "0.14.0";
5096
5231
  if (latest === null) {
5097
5232
  console.error("Could not reach npm registry. Check your network.");
5098
5233
  process.exit(1);
@@ -5450,7 +5585,7 @@ async function status() {
5450
5585
  console.log(`Server: running on ${url} (PID ${server.pid}, v${ver})`);
5451
5586
  if (isServerStale(server)) {
5452
5587
  console.log(
5453
- ` \u26A0 serving stale ${ver} (CLI is ${"0.13.2"}) \u2014 run 'fleetlens start' to restart on the current version`
5588
+ ` \u26A0 serving stale ${ver} (CLI is ${"0.14.0"}) \u2014 run 'fleetlens start' to restart on the current version`
5454
5589
  );
5455
5590
  }
5456
5591
  } else {
@@ -6058,8 +6193,42 @@ var init_usage = __esm({
6058
6193
  });
6059
6194
 
6060
6195
  // ../entries/dist/types.js
6061
- function entryKey(sessionId, localDay) {
6062
- return `${sessionId}__${localDay}`;
6196
+ function pendingEnrichment() {
6197
+ return {
6198
+ status: "pending",
6199
+ generated_at: null,
6200
+ model: null,
6201
+ cost_usd: null,
6202
+ error: null,
6203
+ brief_summary: null,
6204
+ underlying_goal: null,
6205
+ friction_detail: null,
6206
+ user_instructions: [],
6207
+ outcome: null,
6208
+ claude_helpfulness: null,
6209
+ goal_categories: {},
6210
+ retry_count: 0
6211
+ };
6212
+ }
6213
+ function skippedTrivialEnrichment(generatedAt) {
6214
+ return {
6215
+ status: "skipped_trivial",
6216
+ generated_at: generatedAt,
6217
+ model: null,
6218
+ cost_usd: null,
6219
+ error: null,
6220
+ brief_summary: null,
6221
+ underlying_goal: null,
6222
+ friction_detail: null,
6223
+ user_instructions: [],
6224
+ outcome: "trivial",
6225
+ claude_helpfulness: null,
6226
+ goal_categories: {},
6227
+ retry_count: 0
6228
+ };
6229
+ }
6230
+ function entryKey(sessionId, localDay2) {
6231
+ return `${sessionId}__${localDay2}`;
6063
6232
  }
6064
6233
  function parseEntryKey(key) {
6065
6234
  const m = /^([^_]+(?:_[^_]+)*)__(\d{4}-\d{2}-\d{2})$/.exec(key);
@@ -6067,10 +6236,11 @@ function parseEntryKey(key) {
6067
6236
  return null;
6068
6237
  return { session_id: m[1], local_day: m[2] };
6069
6238
  }
6070
- var GOAL_CATEGORIES, CURRENT_DAY_DIGEST_SCHEMA_VERSION, CURRENT_WEEK_DIGEST_SCHEMA_VERSION, CURRENT_MONTH_DIGEST_SCHEMA_VERSION, PROMPT_FRAME_ORIGIN;
6239
+ var CURRENT_ENTRY_SCHEMA_VERSION, GOAL_CATEGORIES, CURRENT_DAY_DIGEST_SCHEMA_VERSION, CURRENT_WEEK_DIGEST_SCHEMA_VERSION, CURRENT_MONTH_DIGEST_SCHEMA_VERSION, PROMPT_FRAME_ORIGIN;
6071
6240
  var init_types = __esm({
6072
6241
  "../entries/dist/types.js"() {
6073
6242
  "use strict";
6243
+ CURRENT_ENTRY_SCHEMA_VERSION = 2;
6074
6244
  GOAL_CATEGORIES = [
6075
6245
  "build",
6076
6246
  "plan",
@@ -6588,6 +6758,35 @@ function classifyUserInputSource(text) {
6588
6758
  return "slash_command";
6589
6759
  return "human";
6590
6760
  }
6761
+ function countSatisfactionSignals(text) {
6762
+ if (!text)
6763
+ return { happy: 0, satisfied: 0, dissatisfied: 0, frustrated: 0 };
6764
+ return {
6765
+ happy: (text.match(HAPPY_RE) ?? []).length,
6766
+ satisfied: (text.match(SATISFIED_RE) ?? []).length,
6767
+ dissatisfied: (text.match(DISSATISFIED_RE) ?? []).length,
6768
+ frustrated: (text.match(FRUSTRATED_RE) ?? []).length
6769
+ };
6770
+ }
6771
+ function extractUserInstructions(text) {
6772
+ if (!text)
6773
+ return [];
6774
+ INSTRUCTION_RE.lastIndex = 0;
6775
+ const out = [];
6776
+ const seen = /* @__PURE__ */ new Set();
6777
+ let m;
6778
+ while ((m = INSTRUCTION_RE.exec(text)) !== null) {
6779
+ const phrase = m[1].trim().replace(/\s+/g, " ");
6780
+ const lc = phrase.toLowerCase();
6781
+ if (seen.has(lc))
6782
+ continue;
6783
+ seen.add(lc);
6784
+ out.push(phrase);
6785
+ if (out.length >= 5)
6786
+ break;
6787
+ }
6788
+ return out;
6789
+ }
6591
6790
  function classifySubagentRole(sa) {
6592
6791
  const text = `${sa.description} ${sa.prompt_preview}`.toLowerCase();
6593
6792
  if (/\b(re-?review|review[s]?|verify|audit|spec[- ]review|code[- ]quality|code[- ]reuse|efficiency review|sanity check|quick test)\b/.test(text)) {
@@ -6725,7 +6924,7 @@ function computeEntrySignals(entry) {
6725
6924
  continuation_kind
6726
6925
  };
6727
6926
  }
6728
- var TEAMMATE_RE, SKILL_LOAD_RE, SLASH_COMMAND_RE2, STOCK_SUBAGENT_PREFIXES, STOCK_SKILL_PREFIXES, EXTERNAL_REF_PATTERNS;
6927
+ var TEAMMATE_RE, SKILL_LOAD_RE, SLASH_COMMAND_RE2, HAPPY_RE, SATISFIED_RE, DISSATISFIED_RE, FRUSTRATED_RE, INSTRUCTION_RE, STOCK_SUBAGENT_PREFIXES, STOCK_SKILL_PREFIXES, EXTERNAL_REF_PATTERNS;
6729
6928
  var init_signals = __esm({
6730
6929
  "../entries/dist/signals.js"() {
6731
6930
  "use strict";
@@ -6733,6 +6932,11 @@ var init_signals = __esm({
6733
6932
  TEAMMATE_RE = /^<teammate-message\b/;
6734
6933
  SKILL_LOAD_RE = /^Base directory for this skill:/;
6735
6934
  SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
6935
+ HAPPY_RE = /\b(?:yay|yaay|yass|woohoo|nice|great|love(?:ly)?|perfect|amazing|awesome)\s*!|!!!/gi;
6936
+ SATISFIED_RE = /\b(?:thanks|thank you|looks good|lgtm|works|that works|all good|sounds good)\b/gi;
6937
+ DISSATISFIED_RE = /\b(?:that'?s (?:not|wrong)|try again|no, (?:that|this)|not quite|incorrect)\b/gi;
6938
+ FRUSTRATED_RE = /\b(?:broken|stop|why (?:did|are|would) you|give up|ugh|argh|wtf)\b/gi;
6939
+ INSTRUCTION_RE = /\b(?:can you|please|let's|let us|i need|could you|would you)\s+([^.?!\n]{5,200})/gi;
6736
6940
  STOCK_SUBAGENT_PREFIXES = /^(general-purpose|Explore|Plan|claude-code-guide|playwright-qa-verifier|statusline-setup|frontend-design:|code-review:|code-simplifier:|codex:|superpowers:)/;
6737
6941
  STOCK_SKILL_PREFIXES = /^(superpowers|mcp__|frontend-design|code-review|codex|claude-code-guide|claude-api):/i;
6738
6942
  EXTERNAL_REF_PATTERNS = [
@@ -6745,6 +6949,9 @@ var init_signals = __esm({
6745
6949
  });
6746
6950
 
6747
6951
  // ../entries/dist/trivial.js
6952
+ function isTrivial(n) {
6953
+ return n.active_min < 1 && n.turn_count < 3 && n.tools_total === 0;
6954
+ }
6748
6955
  var init_trivial = __esm({
6749
6956
  "../entries/dist/trivial.js"() {
6750
6957
  "use strict";
@@ -6752,7 +6959,521 @@ var init_trivial = __esm({
6752
6959
  });
6753
6960
 
6754
6961
  // ../entries/dist/build.js
6755
- var IDLE_GAP_MS4;
6962
+ function viewEvent(ev) {
6963
+ let rawType;
6964
+ if (ev.role === "user" || ev.role === "tool-result")
6965
+ rawType = "user";
6966
+ else if (ev.role === "agent" || ev.role === "agent-thinking" || ev.role === "tool-call")
6967
+ rawType = "assistant";
6968
+ else if (ev.rawType === "summary")
6969
+ rawType = "summary";
6970
+ else
6971
+ rawType = "system";
6972
+ const rawCwd = ev.raw?.cwd;
6973
+ return {
6974
+ rawType,
6975
+ content: ev.blocks ?? [],
6976
+ model: ev.model,
6977
+ msgId: ev.messageId,
6978
+ usage: ev.usage ? {
6979
+ input_tokens: ev.usage.input,
6980
+ output_tokens: ev.usage.output,
6981
+ cache_read_input_tokens: ev.usage.cacheRead,
6982
+ cache_creation_input_tokens: ev.usage.cacheWrite
6983
+ } : void 0,
6984
+ cwd: rawCwd
6985
+ };
6986
+ }
6987
+ function parseMs(iso) {
6988
+ if (!iso)
6989
+ return void 0;
6990
+ const ms = Date.parse(iso);
6991
+ return Number.isNaN(ms) ? void 0 : ms;
6992
+ }
6993
+ function trunc(s, n) {
6994
+ if (!s)
6995
+ return "";
6996
+ const one = s.replace(/\s+/g, " ").trim();
6997
+ return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
6998
+ }
6999
+ function minimizeImages(s) {
7000
+ if (!s)
7001
+ return s;
7002
+ let counter = 0;
7003
+ return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
7004
+ }
7005
+ function bashVerb(cmd) {
7006
+ if (!cmd)
7007
+ return "?";
7008
+ let firstSeg = cmd.split(BASH_CHAIN_SPLIT_RE, 1)[0].trim();
7009
+ if (firstSeg.startsWith("cd ") || firstSeg === "cd") {
7010
+ const parts = cmd.split(/&&|;/, 3);
7011
+ if (parts.length >= 2)
7012
+ firstSeg = parts[1].trim();
7013
+ }
7014
+ const tokens = firstSeg.split(/\s+/).filter(Boolean);
7015
+ while (tokens.length && SHELL_NOISE.has(tokens[0]))
7016
+ tokens.shift();
7017
+ if (!tokens.length)
7018
+ return "?";
7019
+ let first = tokens[0];
7020
+ if (first.includes("=") && !first.startsWith("/")) {
7021
+ tokens.shift();
7022
+ if (!tokens.length)
7023
+ return "?";
7024
+ first = tokens[0];
7025
+ }
7026
+ if (first.includes("/") && !first.startsWith("./")) {
7027
+ first = first.replace(/\/+$/, "").split("/").pop();
7028
+ }
7029
+ return first.replace(/^["']|["']$/g, "") || "?";
7030
+ }
7031
+ function firstTextBlock(blocks) {
7032
+ if (!Array.isArray(blocks))
7033
+ return "";
7034
+ for (const b of blocks) {
7035
+ if (b && typeof b === "object" && b.type === "text") {
7036
+ return String(b.text ?? "");
7037
+ }
7038
+ }
7039
+ return "";
7040
+ }
7041
+ function blockText(blocks) {
7042
+ if (!blocks)
7043
+ return "";
7044
+ return blocks.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(" ");
7045
+ }
7046
+ function groupEventsByLocalDay(events) {
7047
+ const byDay = /* @__PURE__ */ new Map();
7048
+ for (const ev of events) {
7049
+ if (!ev.timestamp)
7050
+ continue;
7051
+ const ms = Date.parse(ev.timestamp);
7052
+ if (Number.isNaN(ms))
7053
+ continue;
7054
+ const day2 = toLocalDay(ms);
7055
+ let bucket = byDay.get(day2);
7056
+ if (!bucket) {
7057
+ bucket = [];
7058
+ byDay.set(day2, bucket);
7059
+ }
7060
+ bucket.push(ev);
7061
+ }
7062
+ return byDay;
7063
+ }
7064
+ function newTurn(startMs, userInput = "") {
7065
+ return {
7066
+ startMs,
7067
+ lastMs: startMs,
7068
+ eventTs: startMs !== void 0 ? [startMs] : [],
7069
+ userInput,
7070
+ toolsTotal: 0,
7071
+ toolMix: /* @__PURE__ */ new Map(),
7072
+ bashVerbs: /* @__PURE__ */ new Map(),
7073
+ skills: /* @__PURE__ */ new Map(),
7074
+ subagents: [],
7075
+ taskOps: 0,
7076
+ toolErrors: 0,
7077
+ consecSameToolMax: 0,
7078
+ consec: 0,
7079
+ endedInInterrupt: false,
7080
+ models: /* @__PURE__ */ new Map(),
7081
+ tokensIn: 0,
7082
+ tokensOut: 0,
7083
+ tokensCacheR: 0,
7084
+ tokensCacheW: 0,
7085
+ seenMsgIds: /* @__PURE__ */ new Set()
7086
+ };
7087
+ }
7088
+ function closeTurn(t, closed, cwd) {
7089
+ const ts = [...t.eventTs].sort((a, b) => a - b);
7090
+ let active = 0;
7091
+ for (let i = 1; i < ts.length; i++) {
7092
+ const dt = ts[i] - ts[i - 1];
7093
+ if (dt < IDLE_GAP_MS4)
7094
+ active += dt;
7095
+ }
7096
+ closed.push({
7097
+ startMs: t.startMs ?? 0,
7098
+ activeMs: active,
7099
+ userInput: t.userInput,
7100
+ firstAgentText: t.firstAgentText,
7101
+ lastAgentText: t.lastAgentText,
7102
+ toolsTotal: t.toolsTotal,
7103
+ toolMix: t.toolMix,
7104
+ bashVerbs: t.bashVerbs,
7105
+ skills: t.skills,
7106
+ subagents: t.subagents,
7107
+ taskOps: t.taskOps,
7108
+ toolErrors: t.toolErrors,
7109
+ consecSameToolMax: t.consecSameToolMax,
7110
+ endedInInterrupt: t.endedInInterrupt,
7111
+ models: t.models,
7112
+ totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
7113
+ cwd
7114
+ });
7115
+ }
7116
+ function aggregateDay(dayEvents, _sessionFallbackProject) {
7117
+ const prTitles = [];
7118
+ let commits = 0;
7119
+ let pushes = 0;
7120
+ const gitToolUseIds = /* @__PURE__ */ new Set();
7121
+ const githubRepos = /* @__PURE__ */ new Set();
7122
+ const editedDirCounts = /* @__PURE__ */ new Map();
7123
+ const modelTurns = /* @__PURE__ */ new Map();
7124
+ let totalToolErrors = 0;
7125
+ let totalInterrupts = 0;
7126
+ let exitPlanCalls = 0;
7127
+ let taskOpsTotal = 0;
7128
+ let firstUser;
7129
+ let finalAgent;
7130
+ const cwdCounts = /* @__PURE__ */ new Map();
7131
+ const closed = [];
7132
+ let cur;
7133
+ const allTs = [];
7134
+ let lastEventCwd;
7135
+ for (const ev of dayEvents) {
7136
+ const tsMs = parseMs(ev.timestamp);
7137
+ if (tsMs !== void 0)
7138
+ allTs.push(tsMs);
7139
+ const view = viewEvent(ev);
7140
+ const rawType = view.rawType;
7141
+ const msg = { content: view.content, id: view.msgId, model: view.model, usage: view.usage };
7142
+ const content = view.content;
7143
+ const eventCwd = view.cwd;
7144
+ if (eventCwd)
7145
+ lastEventCwd = eventCwd;
7146
+ if (rawType === "user") {
7147
+ const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
7148
+ if (!isToolResultOnly) {
7149
+ const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
7150
+ if (cur) {
7151
+ if (text && INTERRUPT_RE2.test(text)) {
7152
+ cur.endedInInterrupt = true;
7153
+ totalInterrupts++;
7154
+ }
7155
+ closeTurn(cur, closed, eventCwd);
7156
+ }
7157
+ cur = newTurn(tsMs, text);
7158
+ if (!firstUser && text && classifyUserInputSource(text) === "human") {
7159
+ firstUser = text;
7160
+ }
7161
+ } else {
7162
+ if (Array.isArray(content)) {
7163
+ for (const c of content) {
7164
+ if (!c || typeof c !== "object" || c.type !== "tool_result")
7165
+ continue;
7166
+ if (c.is_error) {
7167
+ totalToolErrors++;
7168
+ if (cur)
7169
+ cur.toolErrors++;
7170
+ }
7171
+ const tuId = c.tool_use_id;
7172
+ if (tuId && gitToolUseIds.has(tuId)) {
7173
+ const rc = c.content;
7174
+ const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
7175
+ for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
7176
+ if (m[1].toLowerCase() === "user-attachments")
7177
+ continue;
7178
+ githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
7179
+ }
7180
+ }
7181
+ }
7182
+ }
7183
+ if (cur && tsMs !== void 0) {
7184
+ cur.eventTs.push(tsMs);
7185
+ cur.lastMs = tsMs;
7186
+ }
7187
+ }
7188
+ } else if (rawType === "assistant") {
7189
+ if (cur && tsMs !== void 0) {
7190
+ cur.eventTs.push(tsMs);
7191
+ cur.lastMs = tsMs;
7192
+ }
7193
+ const model = msg.model;
7194
+ if (model) {
7195
+ modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
7196
+ if (cur)
7197
+ cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
7198
+ }
7199
+ const msgId = msg.id;
7200
+ const usage2 = msg.usage;
7201
+ if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage2) {
7202
+ cur.seenMsgIds.add(msgId);
7203
+ cur.tokensIn += usage2.input_tokens ?? 0;
7204
+ cur.tokensOut += usage2.output_tokens ?? 0;
7205
+ cur.tokensCacheR += usage2.cache_read_input_tokens ?? 0;
7206
+ cur.tokensCacheW += usage2.cache_creation_input_tokens ?? 0;
7207
+ }
7208
+ if (Array.isArray(content)) {
7209
+ for (const c of content) {
7210
+ if (!c || typeof c !== "object")
7211
+ continue;
7212
+ const ct = c;
7213
+ if (ct.type === "text" && ct.text) {
7214
+ finalAgent = ct.text;
7215
+ if (cur) {
7216
+ if (cur.firstAgentText === void 0)
7217
+ cur.firstAgentText = ct.text;
7218
+ cur.lastAgentText = ct.text;
7219
+ }
7220
+ } else if (ct.type === "tool_use") {
7221
+ const name = ct.name ?? "unknown";
7222
+ if (cur) {
7223
+ cur.toolsTotal++;
7224
+ cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
7225
+ if (name === cur.lastTool)
7226
+ cur.consec++;
7227
+ else {
7228
+ cur.lastTool = name;
7229
+ cur.consec = 1;
7230
+ }
7231
+ if (cur.consec > cur.consecSameToolMax)
7232
+ cur.consecSameToolMax = cur.consec;
7233
+ if (name === "Skill") {
7234
+ const sk = String(ct.input?.skill ?? "unknown");
7235
+ cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
7236
+ }
7237
+ if (name === "Agent") {
7238
+ const inp = ct.input ?? {};
7239
+ cur.subagents.push({
7240
+ type: String(inp.subagent_type ?? "general-purpose"),
7241
+ description: trunc(String(inp.description ?? ""), 80),
7242
+ prompt_preview: trunc(String(inp.prompt ?? ""), 240),
7243
+ background: Boolean(inp.run_in_background)
7244
+ });
7245
+ }
7246
+ if (name === "ExitPlanMode")
7247
+ exitPlanCalls++;
7248
+ if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
7249
+ cur.taskOps++;
7250
+ taskOpsTotal++;
7251
+ }
7252
+ }
7253
+ if (name === "Bash") {
7254
+ const cmd = String(ct.input?.command ?? "");
7255
+ if (cur)
7256
+ cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
7257
+ if (/\bgh\s+pr\s+create\b/.test(cmd)) {
7258
+ const m = cmd.match(PR_TITLE_RE);
7259
+ prTitles.push(m?.[1] ?? cmd.slice(0, 120));
7260
+ }
7261
+ if (/\bgit\s+commit\b/.test(cmd))
7262
+ commits++;
7263
+ if (/\bgit\s+push\b/.test(cmd))
7264
+ pushes++;
7265
+ if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
7266
+ gitToolUseIds.add(ct.id);
7267
+ }
7268
+ if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
7269
+ cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
7270
+ }
7271
+ if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
7272
+ const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
7273
+ const slash = fp.lastIndexOf("/");
7274
+ if (slash > 0) {
7275
+ const dir = fp.slice(0, slash);
7276
+ editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
7277
+ }
7278
+ }
7279
+ }
7280
+ }
7281
+ }
7282
+ }
7283
+ }
7284
+ if (cur)
7285
+ closeTurn(cur, closed, lastEventCwd);
7286
+ const tsSorted = [...allTs].sort((a, b) => a - b);
7287
+ let activeMs2 = 0;
7288
+ for (let i = 1; i < tsSorted.length; i++) {
7289
+ const dt = tsSorted[i] - tsSorted[i - 1];
7290
+ if (dt < IDLE_GAP_MS4)
7291
+ activeMs2 += dt;
7292
+ }
7293
+ const tsSortedStrings = dayEvents.map((e) => e.timestamp).filter((t) => !!t).sort();
7294
+ const start_iso = tsSortedStrings[0] ?? "";
7295
+ const end_iso = tsSortedStrings[tsSortedStrings.length - 1] ?? "";
7296
+ return {
7297
+ closed,
7298
+ firstUser,
7299
+ finalAgent,
7300
+ prTitles,
7301
+ commits,
7302
+ pushes,
7303
+ githubRepos: [...githubRepos].slice(0, 5),
7304
+ editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
7305
+ modelTurns,
7306
+ totalToolErrors,
7307
+ totalInterrupts,
7308
+ exitPlanCalls,
7309
+ taskOpsTotal,
7310
+ cwdCounts,
7311
+ start_iso,
7312
+ end_iso,
7313
+ activeMs: activeMs2
7314
+ };
7315
+ }
7316
+ function buildTopTools(closed) {
7317
+ const toolMix = /* @__PURE__ */ new Map();
7318
+ const bashVerbsAll = /* @__PURE__ */ new Map();
7319
+ for (const t of closed) {
7320
+ for (const [k, v] of t.toolMix)
7321
+ toolMix.set(k, (toolMix.get(k) ?? 0) + v);
7322
+ for (const [k, v] of t.bashVerbs)
7323
+ bashVerbsAll.set(k, (bashVerbsAll.get(k) ?? 0) + v);
7324
+ }
7325
+ const top = [...toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
7326
+ return top.map(([name, count]) => {
7327
+ if (name === "Bash" && bashVerbsAll.size > 0) {
7328
+ const sub = [...bashVerbsAll.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([v, c]) => c > 1 ? `${v}\xD7${c}` : v).join(", ");
7329
+ return `Bash\xD7${count} (${sub})`;
7330
+ }
7331
+ return count > 1 ? `${name}\xD7${count}` : name;
7332
+ });
7333
+ }
7334
+ function buildEntries(sessionDetail) {
7335
+ const byDay = groupEventsByLocalDay(sessionDetail.events);
7336
+ const entries2 = [];
7337
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
7338
+ const sessionFallbackProject = canonicalProjectName(sessionDetail.projectName ?? "");
7339
+ for (const [local_day, dayEvents] of byDay) {
7340
+ if (dayEvents.length === 0)
7341
+ continue;
7342
+ const agg = aggregateDay(dayEvents, sessionFallbackProject);
7343
+ const { closed } = agg;
7344
+ const turnCount = closed.length;
7345
+ const toolsTotal = closed.reduce((s, t) => s + t.toolsTotal, 0);
7346
+ const tokensTotal = closed.reduce((s, t) => s + t.totalTokens, 0);
7347
+ const subagentCalls = closed.reduce((s, t) => s + t.subagents.length, 0);
7348
+ const skillsMap = /* @__PURE__ */ new Map();
7349
+ for (const t of closed) {
7350
+ for (const [k, v] of t.skills)
7351
+ skillsMap.set(k, (skillsMap.get(k) ?? 0) + v);
7352
+ }
7353
+ const skillCalls = [...skillsMap.values()].reduce((a, b) => a + b, 0);
7354
+ const taskOps = agg.taskOpsTotal;
7355
+ const interrupts = agg.totalInterrupts;
7356
+ const toolErrors = agg.totalToolErrors;
7357
+ const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
7358
+ const exitPlanCalls = agg.exitPlanCalls;
7359
+ const prs = agg.prTitles.length;
7360
+ const commits = agg.commits;
7361
+ const pushes = agg.pushes;
7362
+ const activeMin = Math.round(agg.activeMs / 6e3) / 10;
7363
+ const numbers = {
7364
+ active_min: activeMin,
7365
+ turn_count: turnCount,
7366
+ tools_total: toolsTotal,
7367
+ subagent_calls: subagentCalls,
7368
+ skill_calls: skillCalls,
7369
+ task_ops: taskOps,
7370
+ interrupts,
7371
+ tool_errors: toolErrors,
7372
+ consec_same_tool_max: consecMax,
7373
+ exit_plan_calls: exitPlanCalls,
7374
+ prs,
7375
+ commits,
7376
+ pushes,
7377
+ tokens_total: tokensTotal
7378
+ };
7379
+ const longestTurnActiveMs = Math.max(0, ...closed.map((t) => t.activeMs));
7380
+ const flags = [];
7381
+ if (interrupts >= 3)
7382
+ flags.push("interrupt_heavy");
7383
+ if (toolErrors >= 20)
7384
+ flags.push("high_errors");
7385
+ if (consecMax >= 8)
7386
+ flags.push("loop_suspected");
7387
+ if (agg.activeMs < 5 * 6e4 && prs >= 1)
7388
+ flags.push("fast_ship");
7389
+ if (exitPlanCalls > 0)
7390
+ flags.push("plan_used");
7391
+ const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
7392
+ if (subagentTurns >= 3)
7393
+ flags.push("orchestrated");
7394
+ if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
7395
+ flags.push("long_autonomous");
7396
+ const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
7397
+ const modelMix = Object.fromEntries(agg.modelTurns);
7398
+ const firstUserRaw = agg.firstUser ?? "";
7399
+ const first_user = trunc(minimizeImages(firstUserRaw), 400);
7400
+ const finalAgentRaw = agg.finalAgent ?? "";
7401
+ const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
7402
+ let project = sessionFallbackProject;
7403
+ if (agg.cwdCounts.size > 0) {
7404
+ const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
7405
+ project = canonicalProjectName(best[0]);
7406
+ }
7407
+ const top_tools = buildTopTools(closed);
7408
+ const skills = Object.fromEntries(skillsMap);
7409
+ const subagents = closed.flatMap((t) => t.subagents);
7410
+ const humanText = dayEvents.filter((ev) => {
7411
+ const v = viewEvent(ev);
7412
+ if (v.rawType !== "user")
7413
+ return false;
7414
+ const content = v.content;
7415
+ const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
7416
+ if (isToolResultOnly)
7417
+ return false;
7418
+ const text = blockText(ev.blocks) || ev.preview || "";
7419
+ return classifyUserInputSource(text) === "human";
7420
+ }).map((ev) => blockText(ev.blocks) || ev.preview || "").join("\n");
7421
+ const satisfaction_signals = countSatisfactionSignals(humanText);
7422
+ const user_input_sources = {
7423
+ human: 0,
7424
+ teammate: 0,
7425
+ skill_load: 0,
7426
+ slash_command: 0,
7427
+ system_instruction: 0
7428
+ };
7429
+ for (const ev of dayEvents) {
7430
+ const v = viewEvent(ev);
7431
+ if (v.rawType !== "user")
7432
+ continue;
7433
+ const content = v.content;
7434
+ const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
7435
+ if (isToolResultOnly)
7436
+ continue;
7437
+ const text = blockText(ev.blocks) || ev.preview || "";
7438
+ const src = classifyUserInputSource(text);
7439
+ user_input_sources[src]++;
7440
+ }
7441
+ const pendingUserInstructions = extractUserInstructions(humanText).slice(0, 5);
7442
+ const trivial = isTrivial(numbers);
7443
+ const entry = {
7444
+ version: CURRENT_ENTRY_SCHEMA_VERSION,
7445
+ agent: sessionDetail.agent,
7446
+ session_id: sessionDetail.id,
7447
+ local_day,
7448
+ project,
7449
+ start_iso: agg.start_iso,
7450
+ end_iso: agg.end_iso,
7451
+ numbers,
7452
+ flags,
7453
+ primary_model: primaryModel,
7454
+ model_mix: modelMix,
7455
+ first_user,
7456
+ final_agent,
7457
+ pr_titles: agg.prTitles,
7458
+ ...agg.githubRepos.length ? { github_repos: agg.githubRepos } : {},
7459
+ ...agg.editedDirs.length ? { edited_dirs: agg.editedDirs } : {},
7460
+ top_tools,
7461
+ skills,
7462
+ subagents,
7463
+ satisfaction_signals,
7464
+ user_input_sources,
7465
+ enrichment: trivial ? skippedTrivialEnrichment(generatedAt) : { ...pendingEnrichment(), user_instructions: pendingUserInstructions },
7466
+ generated_at: generatedAt,
7467
+ source_jsonl: sessionDetail.filePath ?? "",
7468
+ source_checkpoint: { byte_offset: 0, last_event_ts: agg.end_iso || null }
7469
+ };
7470
+ entry.signals = computeEntrySignals(entry);
7471
+ entries2.push(entry);
7472
+ }
7473
+ entries2.sort((a, b) => a.local_day.localeCompare(b.local_day));
7474
+ return entries2;
7475
+ }
7476
+ var IDLE_GAP_MS4, INTERRUPT_RE2, PR_TITLE_RE, BASH_CHAIN_SPLIT_RE, SHELL_NOISE, IMAGE_BLOCK_RE;
6756
7477
  var init_build = __esm({
6757
7478
  "../entries/dist/build.js"() {
6758
7479
  "use strict";
@@ -6761,6 +7482,11 @@ var init_build = __esm({
6761
7482
  init_signals();
6762
7483
  init_trivial();
6763
7484
  IDLE_GAP_MS4 = 3 * 60 * 1e3;
7485
+ INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
7486
+ PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
7487
+ BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
7488
+ SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
7489
+ IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
6764
7490
  }
6765
7491
  });
6766
7492
 
@@ -6955,6 +7681,7 @@ __export(fs_exports2, {
6955
7681
  __setDigestsDirForTest: () => __setDigestsDirForTest,
6956
7682
  __setEntriesDirForTest: () => __setEntriesDirForTest,
6957
7683
  entriesDir: () => entriesDir,
7684
+ entryExists: () => entryExists,
6958
7685
  getCurrentMonthDigestFromCache: () => getCurrentMonthDigestFromCache,
6959
7686
  getCurrentWeekDigestFromCache: () => getCurrentWeekDigestFromCache,
6960
7687
  getTodayDigestFromCache: () => getTodayDigestFromCache,
@@ -6991,8 +7718,8 @@ function __setEntriesDirForTest(path7) {
6991
7718
  entriesDirCached = path7;
6992
7719
  mkdirSync6(path7, { recursive: true });
6993
7720
  }
6994
- function pathFor(sessionId, localDay) {
6995
- return join8(entriesDir(), `${entryKey(sessionId, localDay)}.json`);
7721
+ function pathFor(sessionId, localDay2) {
7722
+ return join8(entriesDir(), `${entryKey(sessionId, localDay2)}.json`);
6996
7723
  }
6997
7724
  function tmpPathFor(final) {
6998
7725
  return `${final}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
@@ -7034,24 +7761,27 @@ function writeEntryPreservingEnrichment(fresh) {
7034
7761
  writeFileSync5(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
7035
7762
  renameSync2(tmp, final);
7036
7763
  }
7037
- function readEntry(sessionId, localDay) {
7038
- const p = pathFor(sessionId, localDay);
7764
+ function readEntry(sessionId, localDay2) {
7765
+ const p = pathFor(sessionId, localDay2);
7039
7766
  if (!existsSync7(p))
7040
7767
  return null;
7041
7768
  const raw = readFileSync9(p, "utf8");
7042
7769
  return JSON.parse(raw);
7043
7770
  }
7771
+ function entryExists(sessionId, localDay2) {
7772
+ return existsSync7(pathFor(sessionId, localDay2));
7773
+ }
7044
7774
  function listEntryKeys() {
7045
7775
  const dir = entriesDir();
7046
7776
  if (!existsSync7(dir))
7047
7777
  return [];
7048
7778
  return readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length));
7049
7779
  }
7050
- function listEntriesForDay(localDay) {
7780
+ function listEntriesForDay(localDay2) {
7051
7781
  const out = [];
7052
7782
  for (const key of listEntryKeys()) {
7053
7783
  const parsed = parseEntryKey(key);
7054
- if (!parsed || parsed.local_day !== localDay)
7784
+ if (!parsed || parsed.local_day !== localDay2)
7055
7785
  continue;
7056
7786
  const e = readEntry(parsed.session_id, parsed.local_day);
7057
7787
  if (e)
@@ -11220,7 +11950,7 @@ var init_zod = __esm({
11220
11950
  });
11221
11951
 
11222
11952
  // ../entries/dist/prompts/enrich.js
11223
- function trunc(s, n) {
11953
+ function trunc2(s, n) {
11224
11954
  if (s.length <= n)
11225
11955
  return s;
11226
11956
  return s.slice(0, n - 1) + "\u2026";
@@ -11241,7 +11971,7 @@ function buildEnrichmentUserPrompt(entry, humanTurns) {
11241
11971
  satisfaction_signals: entry.satisfaction_signals,
11242
11972
  user_input_sources: entry.user_input_sources
11243
11973
  };
11244
- const turns = humanTurns.slice(0, 8).map((t, i) => `${i + 1}. ${trunc(t, 300)}`).join("\n");
11974
+ const turns = humanTurns.slice(0, 8).map((t, i) => `${i + 1}. ${trunc2(t, 300)}`).join("\n");
11245
11975
  return `SLICE FACTS:
11246
11976
  ${JSON.stringify(facts, null, 2)}
11247
11977
 
@@ -12382,7 +13112,7 @@ var init_settings = __esm({
12382
13112
  });
12383
13113
 
12384
13114
  // ../entries/dist/prompts/digest-day.js
12385
- function trunc2(s, n) {
13115
+ function trunc3(s, n) {
12386
13116
  if (s.length <= n)
12387
13117
  return s;
12388
13118
  return s.slice(0, n - 1) + "\u2026";
@@ -12393,9 +13123,9 @@ function prettyProject(p) {
12393
13123
  }
12394
13124
  function buildDigestUserPrompt(base, entries2) {
12395
13125
  const enriched = entries2.filter((e) => e.enrichment.status === "done");
12396
- const summaries = enriched.slice(0, 12).map((e) => `- (${prettyProject(e.project)}, ${Math.round(e.numbers.active_min)}m) ${trunc2(e.enrichment.brief_summary ?? "", 240)}`).join("\n");
12397
- const frictions = enriched.map((e) => e.enrichment.friction_detail).filter((x) => !!x).slice(0, 6).map((f, i) => `${i + 1}. ${trunc2(f, 240)}`).join("\n");
12398
- const instructions = enriched.flatMap((e) => e.enrichment.user_instructions).slice(0, 10).map((s, i) => `${i + 1}. ${trunc2(s, 240)}`).join("\n");
13126
+ const summaries = enriched.slice(0, 12).map((e) => `- (${prettyProject(e.project)}, ${Math.round(e.numbers.active_min)}m) ${trunc3(e.enrichment.brief_summary ?? "", 240)}`).join("\n");
13127
+ const frictions = enriched.map((e) => e.enrichment.friction_detail).filter((x) => !!x).slice(0, 6).map((f, i) => `${i + 1}. ${trunc3(f, 240)}`).join("\n");
13128
+ const instructions = enriched.flatMap((e) => e.enrichment.user_instructions).slice(0, 10).map((s, i) => `${i + 1}. ${trunc3(s, 240)}`).join("\n");
12399
13129
  const per_entry = entries2.map((e) => ({
12400
13130
  agent: e.agent ?? "claude-code",
12401
13131
  project: prettyProject(e.project),
@@ -14425,7 +15155,7 @@ function activeMs(timestamps) {
14425
15155
  }
14426
15156
  return active;
14427
15157
  }
14428
- function bashVerb(cmd) {
15158
+ function bashVerb2(cmd) {
14429
15159
  if (!cmd)
14430
15160
  return "?";
14431
15161
  let firstSeg = cmd.split(/\|\||&&|;|\|/, 1)[0].trim();
@@ -14435,7 +15165,7 @@ function bashVerb(cmd) {
14435
15165
  firstSeg = parts[1].trim();
14436
15166
  }
14437
15167
  const tokens = firstSeg.split(/\s+/).filter(Boolean);
14438
- while (tokens.length && SHELL_NOISE.has(tokens[0]))
15168
+ while (tokens.length && SHELL_NOISE2.has(tokens[0]))
14439
15169
  tokens.shift();
14440
15170
  if (!tokens.length)
14441
15171
  return "?";
@@ -14508,7 +15238,7 @@ function eventsToTurns(events, windowStartMs, windowEndMs) {
14508
15238
  const isToolOnly = blocks.length > 0 && blocks.every((c) => c.type === "tool_result");
14509
15239
  if (!isToolOnly) {
14510
15240
  const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
14511
- const interrupted = INTERRUPT_RE2.test(text);
15241
+ const interrupted = INTERRUPT_RE3.test(text);
14512
15242
  if (cur && interrupted)
14513
15243
  cur.interrupts += 1;
14514
15244
  if (cur)
@@ -14562,10 +15292,10 @@ function eventsToTurns(events, windowStartMs, windowEndMs) {
14562
15292
  cur.todo_ops += 1;
14563
15293
  } else if (name === "Bash") {
14564
15294
  const cmd = String(ct.input?.command ?? "");
14565
- const v = bashVerb(cmd);
15295
+ const v = bashVerb2(cmd);
14566
15296
  cur.bash_verbs.set(v, (cur.bash_verbs.get(v) ?? 0) + 1);
14567
15297
  if (/\bgh\s+pr\s+create\b/.test(cmd)) {
14568
- const m = cmd.match(PR_TITLE_RE);
15298
+ const m = cmd.match(PR_TITLE_RE2);
14569
15299
  cur.pr_created = m?.[1] ?? cmd.slice(0, 120);
14570
15300
  }
14571
15301
  }
@@ -14988,7 +15718,7 @@ async function generateTopSession(slice, opts = {}) {
14988
15718
  void lastModel;
14989
15719
  }
14990
15720
  }
14991
- var IDLE_GAP_MS5, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE, INTERRUPT_RE2, PR_TITLE_RE, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
15721
+ var IDLE_GAP_MS5, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE2, INTERRUPT_RE3, PR_TITLE_RE2, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
14992
15722
  var init_top_sessions = __esm({
14993
15723
  "../entries/dist/top-sessions.js"() {
14994
15724
  "use strict";
@@ -15002,9 +15732,9 @@ var init_top_sessions = __esm({
15002
15732
  N_LAST_AGENT = 200;
15003
15733
  N_USER_TURN1 = 400;
15004
15734
  N_SUBAGENT = 240;
15005
- SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
15006
- INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
15007
- PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
15735
+ SHELL_NOISE2 = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
15736
+ INTERRUPT_RE3 = /\[request interrupted|interrupted by user/i;
15737
+ PR_TITLE_RE2 = /--title\s+["']([^"']+)["']/;
15008
15738
  PinResponseSchema = external_exports.object({
15009
15739
  start_min: external_exports.number(),
15010
15740
  end_min: external_exports.number().optional(),
@@ -15119,8 +15849,8 @@ VOCABULARY:
15119
15849
  });
15120
15850
 
15121
15851
  // ../entries/dist/digest-week-pipeline.js
15122
- function mondayFor(localDay) {
15123
- const d = /* @__PURE__ */ new Date(`${localDay}T00:00:00`);
15852
+ function mondayFor(localDay2) {
15853
+ const d = /* @__PURE__ */ new Date(`${localDay2}T00:00:00`);
15124
15854
  const dow = d.getDay();
15125
15855
  const offset = dow === 0 ? -6 : 1 - dow;
15126
15856
  d.setDate(d.getDate() + offset);
@@ -15796,8 +16526,8 @@ async function digest(args2) {
15796
16526
  printHelp2();
15797
16527
  process.exit(1);
15798
16528
  }
15799
- function mondayOf(localDay) {
15800
- const d = /* @__PURE__ */ new Date(`${localDay}T00:00:00`);
16529
+ function mondayOf(localDay2) {
16530
+ const d = /* @__PURE__ */ new Date(`${localDay2}T00:00:00`);
15801
16531
  const dow = d.getDay();
15802
16532
  const offset = dow === 0 ? -6 : 1 - dow;
15803
16533
  d.setDate(d.getDate() + offset);
@@ -16097,6 +16827,52 @@ var init_config = __esm({
16097
16827
  }
16098
16828
  });
16099
16829
 
16830
+ // src/perception/build-entries.ts
16831
+ import { statSync as statSync3, readFileSync as readFileSync14 } from "node:fs";
16832
+ import { basename, dirname as dirname10 } from "node:path";
16833
+ function decodeProjectDirName(projectDir) {
16834
+ if (projectDir.startsWith("-")) {
16835
+ return "/" + projectDir.slice(1).replace(/-/g, "/");
16836
+ }
16837
+ return projectDir.replace(/-/g, "/");
16838
+ }
16839
+ function buildEntriesForFile(filePath) {
16840
+ const stat = statSync3(filePath);
16841
+ const raw = readFileSync14(filePath, "utf8");
16842
+ const rawLines = raw.split("\n").filter(Boolean).map((l) => {
16843
+ try {
16844
+ return JSON.parse(l);
16845
+ } catch {
16846
+ return null;
16847
+ }
16848
+ }).filter((x) => x !== null);
16849
+ if (rawLines.length === 0) return null;
16850
+ const { meta, events } = parseTranscript(rawLines);
16851
+ const fileName = basename(filePath);
16852
+ const projectDir = basename(dirname10(filePath));
16853
+ const sessionId = fileName.replace(/\.jsonl$/, "");
16854
+ const sd = {
16855
+ ...meta,
16856
+ id: sessionId,
16857
+ filePath,
16858
+ projectDir,
16859
+ projectName: meta.cwd ?? decodeProjectDirName(projectDir),
16860
+ events
16861
+ };
16862
+ const built = buildEntries(sd);
16863
+ for (const e of built) {
16864
+ e.source_checkpoint.byte_offset = stat.size;
16865
+ }
16866
+ return built;
16867
+ }
16868
+ var init_build_entries = __esm({
16869
+ "src/perception/build-entries.ts"() {
16870
+ "use strict";
16871
+ init_dist();
16872
+ init_dist2();
16873
+ }
16874
+ });
16875
+
16100
16876
  // src/team/push.ts
16101
16877
  import { randomUUID as randomUUID2 } from "node:crypto";
16102
16878
  function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
@@ -16164,7 +16940,7 @@ function sessionTouchesDay(s, day2) {
16164
16940
  }
16165
16941
  return sessionDay(s) === day2;
16166
16942
  }
16167
- function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, resolveRepo) {
16943
+ function buildRichRollupBlocks(day2, daySessions, entries2, resolveRepo) {
16168
16944
  const bounds = dayBoundsMs(day2);
16169
16945
  const clippedSessions = [];
16170
16946
  for (const s of daySessions) {
@@ -16190,7 +16966,6 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, res
16190
16966
  };
16191
16967
  const sessionRepo = /* @__PURE__ */ new Map();
16192
16968
  for (const e of entries2) {
16193
- if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
16194
16969
  const r = repoForEntry(e);
16195
16970
  if (r) sessionRepo.set(e.session_id, r);
16196
16971
  }
@@ -16198,7 +16973,6 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, res
16198
16973
  for (const s of clippedSessions) {
16199
16974
  const canonical = canonicalProjectName(s.projectName);
16200
16975
  const name = projectRepoName(s.projectName);
16201
- if (privateProjects.has(name) || privateProjects.has(canonical)) continue;
16202
16976
  const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
16203
16977
  const key = repo ?? name;
16204
16978
  const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
@@ -16221,7 +16995,6 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, res
16221
16995
  let longTotalMin = 0;
16222
16996
  let longMaxSingleMin = 0;
16223
16997
  for (const e of entries2) {
16224
- if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
16225
16998
  const shape = e.signals?.working_shape ?? null;
16226
16999
  if (shape) {
16227
17000
  const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
@@ -16297,19 +17070,35 @@ function buildEnrichedExtras(entries2) {
16297
17070
  }
16298
17071
  return { outcomeMix, helpfulnessMix, goalMix };
16299
17072
  }
16300
- function buildRichBlocksForDay(day2, daySessions, privateProjects, enrichmentOptIn, resolveRepo) {
17073
+ function resetEnsuredSessions() {
17074
+ ensuredSessions.clear();
17075
+ }
17076
+ function ensureEntriesForDay(day2, daySessions) {
17077
+ for (const s of daySessions) {
17078
+ if (ensuredSessions.has(s.id)) continue;
17079
+ if ((s.agent ?? "claude-code") !== "claude-code") continue;
17080
+ if (entryExists(s.id, day2)) continue;
17081
+ ensuredSessions.add(s.id);
17082
+ try {
17083
+ for (const e of buildEntriesForFile(s.filePath) ?? []) writeEntryPreservingEnrichment(e);
17084
+ } catch {
17085
+ }
17086
+ }
17087
+ }
17088
+ function buildRichBlocksForDay(day2, daySessions, resolveRepo) {
17089
+ ensureEntriesForDay(day2, daySessions);
16301
17090
  const entries2 = listEntriesForDay(day2);
16302
17091
  if (entries2.length === 0) return void 0;
16303
- const rich = buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, resolveRepo);
16304
- const enriched = enrichmentOptIn ? buildEnrichedExtras(entries2) : void 0;
16305
- return enriched ? { rich, enriched } : { rich };
17092
+ const rich = buildRichRollupBlocks(day2, daySessions, entries2, resolveRepo);
17093
+ const enriched = buildEnrichedExtras(entries2);
17094
+ return { rich, enriched };
16306
17095
  }
16307
17096
  function buildIngestPayload(inputs) {
16308
17097
  const richRollup = inputs.rollup && inputs.richExtras ? { ...inputs.rollup, ...inputs.richExtras } : void 0;
16309
17098
  return {
16310
17099
  ingestId: randomUUID2(),
16311
17100
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
16312
- cliVersion: "0.13.2",
17101
+ cliVersion: "0.14.0",
16313
17102
  ...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
16314
17103
  ...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
16315
17104
  ...richRollup ? { richRollup } : {},
@@ -16317,7 +17106,8 @@ function buildIngestPayload(inputs) {
16317
17106
  ...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
16318
17107
  ...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
16319
17108
  ...inputs.planTier ? { planTier: inputs.planTier } : {},
16320
- ...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {}
17109
+ ...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {},
17110
+ ...inputs.syncLog && inputs.syncLog.length ? { syncLog: inputs.syncLog } : {}
16321
17111
  };
16322
17112
  }
16323
17113
  async function pushToTeamServer(config, payload) {
@@ -16333,22 +17123,24 @@ async function pushToTeamServer(config, payload) {
16333
17123
  const body = await res.json().catch(() => null);
16334
17124
  return { ok: res.ok, status: res.status, body };
16335
17125
  }
16336
- var SNAPSHOT_FRESHNESS_MS, POST_TIMEOUT_MS;
17126
+ var SNAPSHOT_FRESHNESS_MS, POST_TIMEOUT_MS, ensuredSessions;
16337
17127
  var init_push = __esm({
16338
17128
  "src/team/push.ts"() {
16339
17129
  "use strict";
16340
17130
  init_dist();
16341
17131
  init_fs();
16342
17132
  init_fs2();
17133
+ init_build_entries();
16343
17134
  init_storage();
16344
17135
  SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
16345
17136
  POST_TIMEOUT_MS = 15e3;
17137
+ ensuredSessions = /* @__PURE__ */ new Set();
16346
17138
  }
16347
17139
  });
16348
17140
 
16349
17141
  // src/team/git-remote.ts
16350
- import { existsSync as existsSync13, readFileSync as readFileSync14, statSync as statSync3 } from "node:fs";
16351
- import { dirname as dirname10, isAbsolute, join as join11, resolve } from "node:path";
17142
+ import { existsSync as existsSync13, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
17143
+ import { dirname as dirname11, isAbsolute, join as join11, resolve } from "node:path";
16352
17144
  function parseGithubRemote(url) {
16353
17145
  const m = url.trim().match(GITHUB_URL_RE);
16354
17146
  return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
@@ -16369,15 +17161,15 @@ function originUrl(configText) {
16369
17161
  }
16370
17162
  function gitConfigPathFor(dir) {
16371
17163
  const dotGit = join11(dir, ".git");
16372
- const st = statSync3(dotGit, { throwIfNoEntry: false });
17164
+ const st = statSync4(dotGit, { throwIfNoEntry: false });
16373
17165
  if (!st) return null;
16374
17166
  if (st.isDirectory()) return join11(dotGit, "config");
16375
- const m = readFileSync14(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
17167
+ const m = readFileSync15(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
16376
17168
  if (!m) return null;
16377
17169
  const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
16378
17170
  const commondirFile = join11(gd, "commondir");
16379
17171
  if (existsSync13(commondirFile)) {
16380
- const common = readFileSync14(commondirFile, "utf8").trim();
17172
+ const common = readFileSync15(commondirFile, "utf8").trim();
16381
17173
  return join11(isAbsolute(common) ? common : resolve(gd, common), "config");
16382
17174
  }
16383
17175
  return join11(gd, "config");
@@ -16390,14 +17182,14 @@ function createRepoResolver() {
16390
17182
  if (hit !== void 0) return hit;
16391
17183
  let repo = null;
16392
17184
  try {
16393
- for (let d = dir; ; d = dirname10(d)) {
17185
+ for (let d = dir; ; d = dirname11(d)) {
16394
17186
  const config = gitConfigPathFor(d);
16395
17187
  if (config && existsSync13(config)) {
16396
- const url = originUrl(readFileSync14(config, "utf8"));
17188
+ const url = originUrl(readFileSync15(config, "utf8"));
16397
17189
  repo = url ? parseGithubRemote(url) : null;
16398
17190
  break;
16399
17191
  }
16400
- if (d === dirname10(d)) break;
17192
+ if (d === dirname11(d)) break;
16401
17193
  }
16402
17194
  } catch {
16403
17195
  repo = null;
@@ -16415,14 +17207,14 @@ var init_git_remote = __esm({
16415
17207
  });
16416
17208
 
16417
17209
  // src/team/queue.ts
16418
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, appendFileSync as appendFileSync4, statSync as statSync4, mkdirSync as mkdirSync11 } from "node:fs";
16419
- import { dirname as dirname11 } from "node:path";
17210
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, appendFileSync as appendFileSync4, statSync as statSync5, mkdirSync as mkdirSync11 } from "node:fs";
17211
+ import { dirname as dirname12 } from "node:path";
16420
17212
  function enqueuePayload(payload, queuePath = QUEUE_PATH) {
16421
- mkdirSync11(dirname11(queuePath), { recursive: true });
17213
+ mkdirSync11(dirname12(queuePath), { recursive: true });
16422
17214
  const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
16423
17215
  appendFileSync4(queuePath, JSON.stringify(entry) + "\n", "utf8");
16424
17216
  try {
16425
- const { size } = statSync4(queuePath);
17217
+ const { size } = statSync5(queuePath);
16426
17218
  if (size > MAX_SIZE_BYTES) {
16427
17219
  pruneOldest(queuePath);
16428
17220
  }
@@ -16432,7 +17224,7 @@ function enqueuePayload(payload, queuePath = QUEUE_PATH) {
16432
17224
  function dequeuePayloads(queuePath = QUEUE_PATH) {
16433
17225
  let lines;
16434
17226
  try {
16435
- lines = readFileSync15(queuePath, "utf8").trim().split("\n").filter(Boolean);
17227
+ lines = readFileSync16(queuePath, "utf8").trim().split("\n").filter(Boolean);
16436
17228
  } catch {
16437
17229
  return [];
16438
17230
  }
@@ -16453,7 +17245,7 @@ function dequeuePayloads(queuePath = QUEUE_PATH) {
16453
17245
  function pruneOldest(queuePath) {
16454
17246
  let lines;
16455
17247
  try {
16456
- lines = readFileSync15(queuePath, "utf8").trim().split("\n").filter(Boolean);
17248
+ lines = readFileSync16(queuePath, "utf8").trim().split("\n").filter(Boolean);
16457
17249
  } catch {
16458
17250
  return;
16459
17251
  }
@@ -16472,8 +17264,8 @@ var init_queue2 = __esm({
16472
17264
  });
16473
17265
 
16474
17266
  // src/usage/profile.ts
16475
- import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "node:fs";
16476
- import { dirname as dirname12 } from "node:path";
17267
+ import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
17268
+ import { dirname as dirname13 } from "node:path";
16477
17269
  function mapRateLimitTier(rateLimitTier) {
16478
17270
  switch (rateLimitTier) {
16479
17271
  case "default_claude_pro":
@@ -16515,7 +17307,7 @@ async function fetchProfile(nowMs = Date.now()) {
16515
17307
  function readCachedProfile(cachePath) {
16516
17308
  if (!existsSync14(cachePath)) return null;
16517
17309
  try {
16518
- const raw = JSON.parse(readFileSync16(cachePath, "utf8"));
17310
+ const raw = JSON.parse(readFileSync17(cachePath, "utf8"));
16519
17311
  if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
16520
17312
  return raw;
16521
17313
  } catch {
@@ -16523,7 +17315,7 @@ function readCachedProfile(cachePath) {
16523
17315
  }
16524
17316
  }
16525
17317
  function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
16526
- mkdirSync12(dirname12(cachePath), { recursive: true });
17318
+ mkdirSync12(dirname13(cachePath), { recursive: true });
16527
17319
  writeFileSync11(
16528
17320
  cachePath,
16529
17321
  JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
@@ -16703,6 +17495,38 @@ var init_backfill = __esm({
16703
17495
  }
16704
17496
  });
16705
17497
 
17498
+ // src/team/sync-log.ts
17499
+ import { readFileSync as readFileSync18 } from "node:fs";
17500
+ function readPendingSyncLog(watermark) {
17501
+ let raw;
17502
+ try {
17503
+ raw = readFileSync18(cclensPath("daemon.log"), "utf8");
17504
+ } catch {
17505
+ return { lines: [] };
17506
+ }
17507
+ const lines = [];
17508
+ for (const line of raw.split("\n")) {
17509
+ const m = line.match(/^(\S+) (INFO|WARN|ERROR) (.*)$/);
17510
+ if (!m) continue;
17511
+ const [, ts, levelRaw, msg] = m;
17512
+ if (!msg.startsWith("[sync] ")) continue;
17513
+ if (watermark && ts <= watermark) continue;
17514
+ const clipped = msg.length > MAX_MSG_CHARS ? msg.slice(0, MAX_MSG_CHARS) + "\u2026" : msg;
17515
+ lines.push({ ts, level: levelRaw.toLowerCase(), msg: clipped });
17516
+ }
17517
+ const capped = lines.slice(0, MAX_LINES);
17518
+ return { lines: capped, watermark: capped.length ? capped[capped.length - 1].ts : void 0 };
17519
+ }
17520
+ var MAX_LINES, MAX_MSG_CHARS;
17521
+ var init_sync_log = __esm({
17522
+ "src/team/sync-log.ts"() {
17523
+ "use strict";
17524
+ init_fs();
17525
+ MAX_LINES = 300;
17526
+ MAX_MSG_CHARS = 1900;
17527
+ }
17528
+ });
17529
+
16706
17530
  // src/team/last-push.ts
16707
17531
  import { writeFileSync as writeFileSync12, unlinkSync as unlinkSync5, mkdirSync as mkdirSync13 } from "node:fs";
16708
17532
  import { join as join12 } from "node:path";
@@ -16740,7 +17564,7 @@ __export(file_probe_exports, {
16740
17564
  probeArtifactSignals: () => probeArtifactSignals
16741
17565
  });
16742
17566
  import { createHash } from "node:crypto";
16743
- import { existsSync as existsSync15, readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
17567
+ import { existsSync as existsSync15, readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
16744
17568
  import { homedir as homedir5 } from "node:os";
16745
17569
  import { join as join13, relative, resolve as resolve2, sep } from "node:path";
16746
17570
  import { execFileSync as execFileSync2 } from "node:child_process";
@@ -16771,7 +17595,7 @@ function listMarkdownFiles(dir) {
16771
17595
  const full = join13(dir, name);
16772
17596
  let stat;
16773
17597
  try {
16774
- stat = statSync5(full);
17598
+ stat = statSync6(full);
16775
17599
  } catch {
16776
17600
  continue;
16777
17601
  }
@@ -16788,19 +17612,19 @@ function firstSeenDateOf(file) {
16788
17612
  const out = execFileSync2(
16789
17613
  "git",
16790
17614
  ["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
16791
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname13(file) }
17615
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname14(file) }
16792
17616
  ).trim().split("\n").filter(Boolean);
16793
17617
  if (out.length > 0) return out[out.length - 1];
16794
17618
  } catch {
16795
17619
  }
16796
17620
  try {
16797
- const s = statSync5(file);
16798
- return new Date((s.birthtime || s.mtime).getTime()).toISOString().slice(0, 10);
17621
+ const s = statSync6(file);
17622
+ return localDay(s.birthtime || s.mtime);
16799
17623
  } catch {
16800
- return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
17624
+ return todayLocal();
16801
17625
  }
16802
17626
  }
16803
- function dirname13(p) {
17627
+ function dirname14(p) {
16804
17628
  const i = p.lastIndexOf(sep);
16805
17629
  return i < 0 ? "." : p.slice(0, i);
16806
17630
  }
@@ -16815,13 +17639,15 @@ function detectGitEmail(cwd) {
16815
17639
  return null;
16816
17640
  }
16817
17641
  }
16818
- function todayLocal() {
16819
- const d = /* @__PURE__ */ new Date();
17642
+ function localDay(d) {
16820
17643
  const y = d.getFullYear();
16821
17644
  const m = String(d.getMonth() + 1).padStart(2, "0");
16822
17645
  const day2 = String(d.getDate()).padStart(2, "0");
16823
17646
  return `${y}-${m}-${day2}`;
16824
17647
  }
17648
+ function todayLocal() {
17649
+ return localDay(/* @__PURE__ */ new Date());
17650
+ }
16825
17651
  function scanAuthored(roots, subdir, day2) {
16826
17652
  const out = [];
16827
17653
  for (const r of roots) {
@@ -16846,11 +17672,11 @@ function scanEdited(roots, day2) {
16846
17672
  if (firstSeen === day2) continue;
16847
17673
  let stat;
16848
17674
  try {
16849
- stat = statSync5(f);
17675
+ stat = statSync6(f);
16850
17676
  } catch {
16851
17677
  continue;
16852
17678
  }
16853
- const mtimeDay = new Date(stat.mtime.getTime()).toISOString().slice(0, 10);
17679
+ const mtimeDay = localDay(stat.mtime);
16854
17680
  if (mtimeDay !== day2) continue;
16855
17681
  const rel = relative(dir, f);
16856
17682
  out.push({ pathHash: pathHash(`${r.label}:skills:${rel}`), lineDelta: 0 });
@@ -16872,7 +17698,7 @@ function claudemdLineDelta(extraRoots, authorEmail, day2) {
16872
17698
  const out = execFileSync2(
16873
17699
  "git",
16874
17700
  ["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
16875
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname13(file) }
17701
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname14(file) }
16876
17702
  );
16877
17703
  let currentDate = "";
16878
17704
  let currentEmail = "";
@@ -16964,13 +17790,11 @@ async function runActivityBackfill(command2, config, log) {
16964
17790
  summary: { pushed: 0, fromDay: targetDay }
16965
17791
  };
16966
17792
  }
16967
- const privateProjects = new Set(config.privateProjects ?? []);
16968
- const enrichmentOptIn = !!config.enrichmentOptIn;
16969
17793
  const resolveRepo = createRepoResolver();
16970
17794
  let pushed = 0;
16971
17795
  for (const rollup of rollups) {
16972
17796
  const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
16973
- const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, privateProjects, enrichmentOptIn, resolveRepo);
17797
+ const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
16974
17798
  let artifactSignals = null;
16975
17799
  try {
16976
17800
  artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
@@ -17012,13 +17836,95 @@ var init_commands = __esm({
17012
17836
  // src/team/sync.ts
17013
17837
  var sync_exports = {};
17014
17838
  __export(sync_exports, {
17839
+ buildSyncLine: () => buildSyncLine,
17015
17840
  runTeamSync: () => runTeamSync
17016
17841
  });
17017
17842
  import { randomUUID as randomUUID4 } from "node:crypto";
17843
+ function syncLogDelivered(body) {
17844
+ const blocks = body?.blocks;
17845
+ return !blocks || blocks.accepted.includes("syncLog");
17846
+ }
17847
+ function levelForStatus(s) {
17848
+ if (s === "ok" || s === "idle") return "info";
17849
+ if (s === "degraded" || s === "failed") return "warn";
17850
+ return "error";
17851
+ }
17852
+ function fmtDuration(ms) {
17853
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
17854
+ }
17855
+ function liveSnapshotStr(snap) {
17856
+ if (!snap) return void 0;
17857
+ const f = snap.fiveHour?.utilization;
17858
+ const s = snap.sevenDay?.utilization;
17859
+ const parts = [
17860
+ typeof f === "number" ? `5h ${Math.round(f)}%` : null,
17861
+ typeof s === "number" ? `7d ${Math.round(s)}%` : null
17862
+ ].filter(Boolean);
17863
+ return parts.length ? parts.join(" / ") : void 0;
17864
+ }
17865
+ function buildSyncLine(status2, trigger, s, elapsedMs, nextSyncMs) {
17866
+ const parts = [`[sync] ${status2}`, trigger];
17867
+ if (s.pushedDays.length) {
17868
+ parts.push(
17869
+ s.pushedDays.length === 1 ? `pushed 1 day (${s.pushedDays[0]})` : `pushed ${s.pushedDays.length} days (${s.pushedDays[0]}\u2192${s.pushedDays[s.pushedDays.length - 1]})`
17870
+ );
17871
+ }
17872
+ if (s.queued) {
17873
+ parts.push(
17874
+ s.queuedDay ? `queued ${s.queued} for retry (${s.queuedDay}, HTTP ${s.queuedStatus})` : `queued ${s.queued} for retry`
17875
+ );
17876
+ }
17877
+ if (s.droppedDays.length) {
17878
+ parts.push(`dropped ${s.droppedDays.length} unrecoverable (${s.droppedDays.join(",")})`);
17879
+ }
17880
+ if (s.recoveredDays?.length) {
17881
+ parts.push(
17882
+ `recovered ${s.recoveredDays.length} dropped day${s.recoveredDays.length === 1 ? "" : "s"} (${s.recoveredDays.join(",")})`
17883
+ );
17884
+ }
17885
+ if (s.queuedDrained) parts.push(`drained ${s.queuedDrained} from backlog`);
17886
+ if (s.idleReason) parts.push(s.idleReason);
17887
+ if (s.live) parts.push(`live ${s.live}`);
17888
+ if (s.usageSnapshots > 0) {
17889
+ parts.push(`usage +${s.usageSnapshots} snapshot${s.usageSnapshots === 1 ? "" : "s"}`);
17890
+ }
17891
+ if (s.accepted?.length) parts.push(`server accepted ${s.accepted.join(",")}`);
17892
+ if (s.skipped && Object.keys(s.skipped).length) {
17893
+ parts.push(
17894
+ `server SKIPPED ${Object.entries(s.skipped).map(([k, v]) => `${k} (${v})`).join("; ")}`
17895
+ );
17896
+ }
17897
+ if (s.errorMsg) parts.push(s.errorMsg);
17898
+ parts.push(fmtDuration(elapsedMs));
17899
+ if (nextSyncMs) parts.push(`next ~${Math.round(nextSyncMs / 6e4)}m`);
17900
+ return parts.join(" \xB7 ");
17901
+ }
17018
17902
  async function runTeamSync(log = noopLog2, configOverride, options = {}) {
17019
17903
  const config = configOverride === void 0 ? readTeamConfig() : configOverride;
17020
17904
  if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
17905
+ const startedAt = Date.now();
17906
+ const trigger = options.trigger ?? "auto";
17907
+ const summary = {
17908
+ pushedDays: [],
17909
+ droppedDays: [],
17910
+ queued: 0,
17911
+ queuedDrained: 0,
17912
+ usageSnapshots: 0
17913
+ };
17914
+ const finish = (status2) => {
17915
+ log(
17916
+ levelForStatus(status2),
17917
+ buildSyncLine(status2, trigger, summary, Date.now() - startedAt, options.nextSyncMs)
17918
+ );
17919
+ };
17920
+ const recordVerdict = (body) => {
17921
+ if (body?.blocks) {
17922
+ summary.accepted = [.../* @__PURE__ */ new Set([...summary.accepted ?? [], ...body.blocks.accepted])];
17923
+ summary.skipped = { ...summary.skipped, ...body.blocks.skipped };
17924
+ }
17925
+ };
17021
17926
  try {
17927
+ resetEnsuredSessions();
17022
17928
  const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
17023
17929
  const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
17024
17930
  const today = toLocalDay4(Date.now());
@@ -17027,6 +17933,7 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
17027
17933
  nextConfig = { ...nextConfig, ...patch };
17028
17934
  writeTeamConfig(nextConfig);
17029
17935
  };
17936
+ const pendingLog = readPendingSyncLog(config.lastSyncedLogAt);
17030
17937
  const collectedCommands = /* @__PURE__ */ new Map();
17031
17938
  const collectCommands = (commands) => {
17032
17939
  if (!commands) return;
@@ -17064,6 +17971,25 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
17064
17971
  log("warn", `team command dispatch error: ${err.message}`);
17065
17972
  }
17066
17973
  };
17974
+ const isValidationPoison = (status3) => status3 === 400 || status3 === 422;
17975
+ const drainBacklog = async () => {
17976
+ let drained = 0;
17977
+ const backlog = dequeuePayloads();
17978
+ for (let i = 0; i < backlog.length; i++) {
17979
+ const qResult = await pushToTeamServer(config, backlog[i]);
17980
+ if (!qResult.ok) {
17981
+ if (isValidationPoison(qResult.status)) {
17982
+ log("warn", `team push: queued payload rejected (HTTP ${qResult.status}); dropping unrecoverable item`);
17983
+ continue;
17984
+ }
17985
+ for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
17986
+ break;
17987
+ }
17988
+ collectCommands(qResult.body?.commands);
17989
+ drained++;
17990
+ }
17991
+ return drained;
17992
+ };
17067
17993
  const usageBackfill = await runTeamBackfill(log, USAGE_LOG4, config, {
17068
17994
  sinceCapturedAt: options.forceUsageBackfill ? void 0 : config.lastSyncedUsageSnapshotAt
17069
17995
  });
@@ -17071,121 +17997,167 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
17071
17997
  persistConfig({ lastSyncedUsageSnapshotAt: usageBackfill.lastSnapshotAt });
17072
17998
  }
17073
17999
  const sessions = await listSessions2({ limit: 1e4 });
17074
- const rollups = buildRollupsForRange(sessions, config.lastSyncedDay);
18000
+ const allRollups = buildRollupsForRange(sessions);
18001
+ const rollups = allRollups.filter((r) => !config.lastSyncedDay || r.day >= config.lastSyncedDay);
17075
18002
  const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG4) ?? void 0;
17076
18003
  const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
17077
18004
  const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
17078
18005
  if (rollups.length === 0) {
17079
18006
  const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
17080
18007
  if (!hasLiveData) {
17081
- if (usageBackfill.sentSnapshots === 0) log("info", "team push: nothing to sync");
18008
+ summary.usageSnapshots = usageBackfill.sentSnapshots;
18009
+ if (usageBackfill.sentSnapshots > 0) {
18010
+ finish("ok");
18011
+ } else {
18012
+ summary.idleReason = "nothing to sync";
18013
+ finish("idle");
18014
+ }
17082
18015
  return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
17083
18016
  }
17084
- const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks });
18017
+ summary.usageSnapshots = usageBackfill.sentSnapshots;
18018
+ summary.live = liveSnapshotStr(usageSnapshot);
18019
+ const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks, syncLog: pendingLog.lines });
17085
18020
  const result = await pushToTeamServer(config, payload);
17086
18021
  if (!result.ok) {
18022
+ if (isValidationPoison(result.status)) {
18023
+ const errLine2 = `team push: live-only payload rejected (HTTP ${result.status}); dropping unrecoverable payload`;
18024
+ writeLastPushFailure(payload, errLine2);
18025
+ summary.errorMsg = `live-snapshot push HTTP ${result.status} (validation) \u2014 dropped unrecoverable`;
18026
+ finish("error");
18027
+ return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
18028
+ }
17087
18029
  const errLine = `team push failed (${result.status})`;
17088
- log("warn", `${errLine}; queueing`);
17089
18030
  writeLastPushFailure(payload, errLine);
17090
18031
  enqueuePayload(payload);
18032
+ summary.queued = 1;
18033
+ summary.errorMsg = `live-snapshot push HTTP ${result.status} \u2014 queued for retry`;
18034
+ finish("failed");
17091
18035
  return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
17092
18036
  }
17093
18037
  writeLastPushSuccess(payload);
17094
- collectCommands(result.body?.commands);
17095
- let queuedDrained2 = 0;
17096
- const backlog = dequeuePayloads();
17097
- for (let i = 0; i < backlog.length; i++) {
17098
- const qResult = await pushToTeamServer(config, backlog[i]);
17099
- if (!qResult.ok) {
17100
- for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
17101
- break;
17102
- }
17103
- collectCommands(qResult.body?.commands);
17104
- queuedDrained2++;
18038
+ if (pendingLog.watermark && syncLogDelivered(result.body)) {
18039
+ persistConfig({ lastSyncedLogAt: pendingLog.watermark });
17105
18040
  }
17106
- log("info", `team push ok: live-only (no new daily activity)` + (queuedDrained2 ? `, ${queuedDrained2} queued retried` : ""));
18041
+ collectCommands(result.body?.commands);
18042
+ recordVerdict(result.body);
18043
+ const queuedDrained2 = await drainBacklog();
18044
+ summary.queuedDrained = queuedDrained2;
18045
+ summary.idleReason = "no new daily activity";
18046
+ finish(summary.skipped && Object.keys(summary.skipped).length ? "degraded" : "idle");
17107
18047
  await dispatchAndReport();
17108
18048
  return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
17109
18049
  }
17110
18050
  let pushed = 0;
17111
18051
  let queued = 0;
17112
18052
  let failedDay;
17113
- let lastPushedDay;
17114
- const privateProjects = new Set(config.privateProjects ?? []);
17115
- const enrichmentOptIn = !!config.enrichmentOptIn;
18053
+ let lastResolvedDay;
18054
+ const droppedDaysBefore = config.droppedDays ?? [];
18055
+ const workingDropped = [...droppedDaysBefore];
18056
+ let syncLogWasDelivered = false;
17116
18057
  const resolveRepo = createRepoResolver();
17117
18058
  const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
17118
- for (let i = 0; i < rollups.length; i++) {
17119
- const rollup = rollups[i];
17120
- const isLatest = i === rollups.length - 1;
18059
+ const buildDayPayload = (rollup, opts) => {
17121
18060
  const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
17122
- const richBlocks = buildRichBlocksForDay(
17123
- rollup.day,
17124
- daySessions,
17125
- privateProjects,
17126
- enrichmentOptIn,
17127
- resolveRepo
17128
- );
18061
+ const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
17129
18062
  let artifactSignals = null;
17130
18063
  try {
17131
- artifactSignals = probeArtifactSignals2({
17132
- day: rollup.day,
17133
- extraRoots: [process.cwd()]
17134
- });
18064
+ artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
17135
18065
  } catch {
17136
18066
  }
17137
- const payload = buildIngestPayload({
18067
+ return buildIngestPayload({
17138
18068
  rollup,
17139
18069
  richExtras: richBlocks?.rich,
17140
18070
  enrichedExtras: richBlocks?.enriched,
17141
18071
  artifactSignals: artifactSignals ?? void 0,
17142
- usageSnapshot: isLatest ? usageSnapshot : void 0,
18072
+ // Current-cycle data only on the latest day so older days aren't tagged
18073
+ // with today's snapshot / peaks.
18074
+ usageSnapshot: opts.latest ? usageSnapshot : void 0,
17143
18075
  planTier,
17144
- // Same rationale as usageSnapshot — current cycle data only on the
17145
- // latest rollup so older days don't get tagged with today's peaks.
17146
- cyclePeaks: isLatest ? cyclePeaks : void 0
18076
+ cyclePeaks: opts.latest ? cyclePeaks : void 0,
18077
+ syncLog: opts.syncLog
18078
+ });
18079
+ };
18080
+ for (let i = 0; i < rollups.length; i++) {
18081
+ const rollup = rollups[i];
18082
+ const isLatest = i === rollups.length - 1;
18083
+ const payload = buildDayPayload(rollup, {
18084
+ latest: isLatest,
18085
+ syncLog: i === 0 ? pendingLog.lines : void 0
17147
18086
  });
17148
18087
  const result = await pushToTeamServer(config, payload);
17149
18088
  if (!result.ok) {
18089
+ if (isValidationPoison(result.status)) {
18090
+ const errLine2 = `team push: ${rollup.day} rejected (HTTP ${result.status}); skipping past unrecoverable day`;
18091
+ writeLastPushFailure(payload, errLine2);
18092
+ summary.droppedDays.push(rollup.day);
18093
+ if (!workingDropped.includes(rollup.day)) workingDropped.push(rollup.day);
18094
+ lastResolvedDay = rollup.day;
18095
+ continue;
18096
+ }
17150
18097
  const errLine = `team push failed on ${rollup.day} (${result.status})`;
17151
- log("warn", `${errLine}; queueing`);
17152
18098
  writeLastPushFailure(payload, errLine);
17153
18099
  enqueuePayload(payload);
17154
18100
  queued++;
18101
+ summary.queuedDay = rollup.day;
18102
+ summary.queuedStatus = result.status;
17155
18103
  failedDay = rollup.day;
17156
18104
  break;
17157
18105
  }
17158
18106
  writeLastPushSuccess(payload);
17159
18107
  collectCommands(result.body?.commands);
18108
+ recordVerdict(result.body);
18109
+ if (i === 0) syncLogWasDelivered = syncLogDelivered(result.body);
17160
18110
  pushed++;
17161
- lastPushedDay = rollup.day;
18111
+ summary.pushedDays.push(rollup.day);
18112
+ lastResolvedDay = rollup.day;
17162
18113
  }
17163
- if (failedDay) {
17164
- if (lastPushedDay) persistConfig({ lastSyncedDay: lastPushedDay });
17165
- } else {
17166
- persistConfig({ lastSyncedDay: today });
17167
- }
17168
- let queuedDrained = 0;
17169
18114
  if (!failedDay) {
17170
- const backlog = dequeuePayloads();
17171
- for (let i = 0; i < backlog.length; i++) {
17172
- const qResult = await pushToTeamServer(config, backlog[i]);
17173
- if (!qResult.ok) {
17174
- for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
17175
- break;
18115
+ const retryDay = [...droppedDaysBefore].sort().find((d) => !summary.droppedDays.includes(d));
18116
+ const retryRollup = retryDay ? allRollups.find((r) => r.day === retryDay) : void 0;
18117
+ if (retryDay && retryRollup) {
18118
+ const payload = buildDayPayload(retryRollup, { latest: false });
18119
+ const result = await pushToTeamServer(config, payload);
18120
+ if (result.ok) {
18121
+ writeLastPushSuccess(payload);
18122
+ collectCommands(result.body?.commands);
18123
+ recordVerdict(result.body);
18124
+ const idx = workingDropped.indexOf(retryDay);
18125
+ if (idx >= 0) workingDropped.splice(idx, 1);
18126
+ summary.recoveredDays = [retryDay];
18127
+ } else {
18128
+ writeLastPushFailure(payload, `team push: dropped-day retry ${retryDay} failed (HTTP ${result.status})`);
17176
18129
  }
17177
- collectCommands(qResult.body?.commands);
17178
- queuedDrained++;
17179
18130
  }
17180
18131
  }
17181
- if (pushed > 0) {
17182
- log("info", `team push ok: ${pushed} day${pushed === 1 ? "" : "s"} pushed` + (queuedDrained ? `, ${queuedDrained} queued retried` : "") + (queued ? `, ${queued} queued for retry` : ""));
17183
- }
18132
+ const nextDropped = [...new Set(workingDropped)].sort().slice(-90);
18133
+ const prevDropped = [...new Set(droppedDaysBefore)].sort();
18134
+ const droppedChanged = nextDropped.length !== prevDropped.length || nextDropped.some((d, i) => d !== prevDropped[i]);
18135
+ const droppedPatch = droppedChanged ? { droppedDays: nextDropped } : {};
18136
+ if (failedDay) {
18137
+ if (lastResolvedDay || droppedChanged) {
18138
+ persistConfig({ ...lastResolvedDay ? { lastSyncedDay: lastResolvedDay } : {}, ...droppedPatch });
18139
+ }
18140
+ } else {
18141
+ persistConfig({ lastSyncedDay: today, ...droppedPatch });
18142
+ }
18143
+ if (syncLogWasDelivered && pendingLog.watermark) {
18144
+ persistConfig({ lastSyncedLogAt: pendingLog.watermark });
18145
+ }
18146
+ const queuedDrained = !failedDay ? await drainBacklog() : 0;
18147
+ summary.queued = queued;
18148
+ summary.queuedDrained = queuedDrained;
18149
+ summary.usageSnapshots = usageBackfill.sentSnapshots;
18150
+ let status2;
18151
+ if (failedDay) status2 = "failed";
18152
+ else if (summary.droppedDays.length || summary.skipped && Object.keys(summary.skipped).length) status2 = "degraded";
18153
+ else status2 = "ok";
18154
+ finish(status2);
17184
18155
  await dispatchAndReport();
17185
18156
  return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
17186
18157
  } catch (err) {
17187
18158
  const message = err.message;
17188
- log("warn", `team push error: ${message}`);
18159
+ summary.errorMsg = `sync aborted: ${message}`;
18160
+ finish("error");
17189
18161
  writeLastPushFailure(
17190
18162
  { ingestId: "n/a", observedAt: (/* @__PURE__ */ new Date()).toISOString() },
17191
18163
  `team push error: ${message}`
@@ -17249,8 +18221,13 @@ async function buildCyclePeaksForPush(planTier, loadCurve) {
17249
18221
  return out.slice(-maxCycles);
17250
18222
  };
17251
18223
  return {
18224
+ // 7d cycle history cap (26 ≈ six months) MUST stay in lock-step with two
18225
+ // downstream sites or the strip silently truncates: the server read
18226
+ // (loadMembership7dCyclePeaks maxCyclesPerMember in plan-queries.ts) and
18227
+ // the renderer (CyclePeaksStrip maxBars in member-plan-block.tsx). The 5h
18228
+ // cap (24) is independent — leave it.
17252
18229
  fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
17253
- sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 12)
18230
+ sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 26)
17254
18231
  };
17255
18232
  }
17256
18233
  var inFlightCommands, USAGE_LOG4, PROFILE_CACHE2, noopLog2;
@@ -17262,6 +18239,7 @@ var init_sync = __esm({
17262
18239
  init_git_remote();
17263
18240
  init_queue2();
17264
18241
  init_backfill();
18242
+ init_sync_log();
17265
18243
  init_last_push();
17266
18244
  init_commands();
17267
18245
  init_profile();
@@ -17274,6 +18252,30 @@ var init_sync = __esm({
17274
18252
  }
17275
18253
  });
17276
18254
 
18255
+ // src/daemon-log.ts
18256
+ var daemon_log_exports = {};
18257
+ __export(daemon_log_exports, {
18258
+ appendDaemonLogLine: () => appendDaemonLogLine
18259
+ });
18260
+ import { appendFileSync as appendFileSync5 } from "node:fs";
18261
+ function appendDaemonLogLine(level, msg) {
18262
+ try {
18263
+ appendFileSync5(
18264
+ cclensPath("daemon.log"),
18265
+ `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${msg}
18266
+ `,
18267
+ "utf8"
18268
+ );
18269
+ } catch {
18270
+ }
18271
+ }
18272
+ var init_daemon_log = __esm({
18273
+ "src/daemon-log.ts"() {
18274
+ "use strict";
18275
+ init_fs();
18276
+ }
18277
+ });
18278
+
17277
18279
  // src/team/join.ts
17278
18280
  var join_exports = {};
17279
18281
  __export(join_exports, {
@@ -17304,13 +18306,21 @@ async function joinTeam(args2) {
17304
18306
  bearerToken,
17305
18307
  teamSlug: data.team.slug,
17306
18308
  teamName: data.team.name,
17307
- pairedAt: (/* @__PURE__ */ new Date()).toISOString()
18309
+ pairedAt: (/* @__PURE__ */ new Date()).toISOString(),
18310
+ // Fence off any daemon.log history from a PREVIOUS team so the first push
18311
+ // doesn't sweep the prior team's [sync] lines onto this team's log. The
18312
+ // pair run's own [sync] line is written after this instant, so it still
18313
+ // uploads on the next sync.
18314
+ lastSyncedLogAt: (/* @__PURE__ */ new Date()).toISOString()
17308
18315
  };
17309
18316
  writeTeamConfig(config);
17310
18317
  console.log(`Paired with "${data.team.name}" as ${data.user.displayName || data.user.email}`);
17311
18318
  console.log(` role: ${data.membership.role}`);
17312
18319
  console.log(" Syncing local history\u2026");
17313
- const sync = await runTeamSync(void 0, config, { forceUsageBackfill: true });
18320
+ const sync = await runTeamSync(logToDaemonLog, config, {
18321
+ forceUsageBackfill: true,
18322
+ trigger: "pair"
18323
+ });
17314
18324
  const backfill = sync.usageBackfill;
17315
18325
  if (backfill?.error) {
17316
18326
  console.log(` \u26A0 Usage history sync failed: ${backfill.error} \u2014 daemon will retry automatically.`);
@@ -17332,11 +18342,14 @@ async function joinTeam(args2) {
17332
18342
  }
17333
18343
  console.log(" Daemon polls every 5 minutes \u2014 next push happens automatically.");
17334
18344
  }
18345
+ var logToDaemonLog;
17335
18346
  var init_join = __esm({
17336
18347
  "src/team/join.ts"() {
17337
18348
  "use strict";
17338
18349
  init_config();
17339
18350
  init_sync();
18351
+ init_daemon_log();
18352
+ logToDaemonLog = appendDaemonLogLine;
17340
18353
  }
17341
18354
  });
17342
18355
 
@@ -17415,12 +18428,12 @@ var logs_exports = {};
17415
18428
  __export(logs_exports, {
17416
18429
  teamLogs: () => teamLogs
17417
18430
  });
17418
- import { readFileSync as readFileSync17 } from "node:fs";
18431
+ import { readFileSync as readFileSync19 } from "node:fs";
17419
18432
  async function teamLogs() {
17420
18433
  const logPath = cclensPath("daemon.log");
17421
18434
  try {
17422
- const lines = readFileSync17(logPath, "utf8").trim().split("\n");
17423
- const teamLines = lines.filter((l) => l.includes("team "));
18435
+ const lines = readFileSync19(logPath, "utf8").trim().split("\n");
18436
+ const teamLines = lines.filter((l) => l.includes("[sync] ") || l.includes("team "));
17424
18437
  if (teamLines.length === 0) {
17425
18438
  console.log("No team-related log entries found.");
17426
18439
  return;
@@ -17469,7 +18482,15 @@ async function team(args2) {
17469
18482
  }
17470
18483
  case "sync": {
17471
18484
  const { runTeamSync: runTeamSync2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
17472
- const outcome = await runTeamSync2((level, msg) => console.log(`[${level}] ${msg}`));
18485
+ const { appendDaemonLogLine: appendDaemonLogLine2 } = await Promise.resolve().then(() => (init_daemon_log(), daemon_log_exports));
18486
+ const outcome = await runTeamSync2(
18487
+ (level, msg) => {
18488
+ console.log(`[${level}] ${msg}`);
18489
+ appendDaemonLogLine2(level, msg);
18490
+ },
18491
+ void 0,
18492
+ { trigger: "manual" }
18493
+ );
17473
18494
  if (!outcome.paired) {
17474
18495
  console.error("Not paired. Run 'fleetlens team join <url> <device-token>' first.");
17475
18496
  process.exit(1);
@@ -17525,7 +18546,7 @@ __export(runs_exports, {
17525
18546
  runs: () => runs
17526
18547
  });
17527
18548
  import { execSync as execSync2 } from "node:child_process";
17528
- import { existsSync as existsSync16, readFileSync as readFileSync18, readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
18549
+ import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
17529
18550
  import { join as join14 } from "node:path";
17530
18551
  async function runs(args2) {
17531
18552
  const json = args2.includes("--json");
@@ -17611,7 +18632,7 @@ function listActiveRuns() {
17611
18632
  }
17612
18633
  function readSpend(sinceMs) {
17613
18634
  if (!existsSync16(SPEND_PATH)) return [];
17614
- const lines = readFileSync18(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
18635
+ const lines = readFileSync20(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
17615
18636
  const out = [];
17616
18637
  for (const line of lines) {
17617
18638
  try {
@@ -17736,7 +18757,7 @@ function listTraceFiles() {
17736
18757
  const entries2 = readdirSync5(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
17737
18758
  return entries2.map((f) => {
17738
18759
  const path7 = join14(RUNS_DIR2, f);
17739
- const st = statSync6(path7);
18760
+ const st = statSync7(path7);
17740
18761
  return { runId: f.replace(/\.jsonl$/, ""), path: path7, mtimeMs: st.mtimeMs };
17741
18762
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
17742
18763
  }
@@ -17760,7 +18781,7 @@ function resolveRunId(arg) {
17760
18781
  return null;
17761
18782
  }
17762
18783
  function readTrace(path7) {
17763
- return readFileSync18(path7, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
18784
+ return readFileSync20(path7, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
17764
18785
  try {
17765
18786
  return JSON.parse(l);
17766
18787
  } catch {
@@ -17792,13 +18813,13 @@ recent runs:`);
17792
18813
  const events = readTrace(r.path);
17793
18814
  printTrace(r.runId, events);
17794
18815
  if (!follow) return;
17795
- let lastSize = statSync6(r.path).size;
18816
+ let lastSize = statSync7(r.path).size;
17796
18817
  let lastIdx = events.length;
17797
18818
  console.log("\n \u27F6 following \xB7 ^C to quit\n");
17798
18819
  process.on("SIGINT", () => process.exit(0));
17799
18820
  while (true) {
17800
18821
  await new Promise((res) => setTimeout(res, 500));
17801
- const st = statSync6(r.path);
18822
+ const st = statSync7(r.path);
17802
18823
  if (st.size === lastSize) continue;
17803
18824
  lastSize = st.size;
17804
18825
  const all = readTrace(r.path);
@@ -17947,7 +18968,7 @@ async function main() {
17947
18968
  case "version":
17948
18969
  case "--version":
17949
18970
  case "-v":
17950
- console.log(`fleetlens ${"0.13.2"}`);
18971
+ console.log(`fleetlens ${"0.14.0"}`);
17951
18972
  break;
17952
18973
  case "help":
17953
18974
  case "--help":