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.
- package/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/app-path-routes-manifest.json +0 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/routes-manifest.json +0 -6
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/route.js +3 -3
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page.js +2 -2
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js +2 -2
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/team/page.js +2 -2
- package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app-paths-manifest.json +0 -1
- package/app/apps/web/.next/server/chunks/[externals]__0o43ejh._.js +3 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_036my~r._.js +49 -0
- package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
- package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0z7mg0b._.js → [root-of-the-server]__08l86e-._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0_hv6aq._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0dh0q7b._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0yv0m0q._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0o03ymy._.js → [root-of-the-server]__10~b4bf._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{node_modules_next_dist_0t-hj0x._.js → node_modules_0yrkyef._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
- package/app/apps/web/.next/server/functions-config-manifest.json +0 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/middleware-manifest.json +5 -5
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/005ad7l-h1-.6.js +1 -0
- package/app/apps/web/.next/static/chunks/0l4r0bqqtdju..css +1 -0
- package/app/apps/web/app/team/page.tsx +23 -75
- package/app/apps/web/components/previous-cycles-trend.tsx +4 -1
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/team-config.ts +0 -2
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +1970 -1611
- package/dist/index.js +1182 -161
- package/package.json +1 -1
- package/app/apps/web/.next/server/app/api/team-config/route/app-paths-manifest.json +0 -3
- package/app/apps/web/.next/server/app/api/team-config/route/build-manifest.json +0 -9
- package/app/apps/web/.next/server/app/api/team-config/route/server-reference-manifest.json +0 -4
- package/app/apps/web/.next/server/app/api/team-config/route.js +0 -8
- package/app/apps/web/.next/server/app/api/team-config/route.js.map +0 -5
- package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +0 -1
- package/app/apps/web/.next/server/app/api/team-config/route_client-reference-manifest.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0atn.dt._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b.b7~1._.js +0 -3
- package/app/apps/web/.next/server/chunks/_0euqdko._.js +0 -49
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team-config_route_actions_0ad2gak.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0q9qy0z._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_team_team-sync-form_tsx_0choz0h._.js +0 -3
- package/app/apps/web/.next/static/chunks/083~hzkv577.5.css +0 -1
- package/app/apps/web/.next/static/chunks/0uyctl-m8iydb.js +0 -1
- package/app/apps/web/.next/static/chunks/14joqbsr9ihhu.js +0 -1
- package/app/apps/web/app/api/team-config/route.ts +0 -39
- package/app/apps/web/app/team/team-sync-form.tsx +0 -144
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → 3VbAChKD8EChlt8Tm5N5r}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → 3VbAChKD8EChlt8Tm5N5r}/_clientMiddlewareManifest.js +0 -0
- /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
|
|
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 = {
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
5096
|
+
} else if (verify.installedVersion !== "0.14.0") {
|
|
4962
5097
|
console.log(
|
|
4963
|
-
` \u2192 This process is still running ${"0.
|
|
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.
|
|
5133
|
+
current: "0.14.0",
|
|
4999
5134
|
latest: null,
|
|
5000
5135
|
updateAvailable: false
|
|
5001
5136
|
};
|
|
5002
5137
|
return {
|
|
5003
|
-
current: "0.
|
|
5138
|
+
current: "0.14.0",
|
|
5004
5139
|
latest,
|
|
5005
|
-
updateAvailable: shouldUpdate("0.
|
|
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.
|
|
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.
|
|
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
|
|
6062
|
-
return
|
|
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
|
-
|
|
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,
|
|
6995
|
-
return join8(entriesDir(), `${entryKey(sessionId,
|
|
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,
|
|
7038
|
-
const p = pathFor(sessionId,
|
|
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(
|
|
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 !==
|
|
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
|
|
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}. ${
|
|
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
|
|
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) ${
|
|
12397
|
-
const frictions = enriched.map((e) => e.enrichment.friction_detail).filter((x) => !!x).slice(0, 6).map((f, i) => `${i + 1}. ${
|
|
12398
|
-
const instructions = enriched.flatMap((e) => e.enrichment.user_instructions).slice(0, 10).map((s, i) => `${i + 1}. ${
|
|
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
|
|
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 &&
|
|
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 =
|
|
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 =
|
|
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(
|
|
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,
|
|
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
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
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(
|
|
15123
|
-
const d = /* @__PURE__ */ new Date(`${
|
|
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(
|
|
15800
|
-
const d = /* @__PURE__ */ new Date(`${
|
|
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,
|
|
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
|
|
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,
|
|
16304
|
-
const enriched =
|
|
16305
|
-
return
|
|
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.
|
|
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
|
|
16351
|
-
import { dirname as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
17185
|
+
for (let d = dir; ; d = dirname11(d)) {
|
|
16394
17186
|
const config = gitConfigPathFor(d);
|
|
16395
17187
|
if (config && existsSync13(config)) {
|
|
16396
|
-
const url = originUrl(
|
|
17188
|
+
const url = originUrl(readFileSync15(config, "utf8"));
|
|
16397
17189
|
repo = url ? parseGithubRemote(url) : null;
|
|
16398
17190
|
break;
|
|
16399
17191
|
}
|
|
16400
|
-
if (d ===
|
|
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
|
|
16419
|
-
import { dirname as
|
|
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(
|
|
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 } =
|
|
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 =
|
|
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 =
|
|
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
|
|
16476
|
-
import { dirname as
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
16798
|
-
return
|
|
17621
|
+
const s = statSync6(file);
|
|
17622
|
+
return localDay(s.birthtime || s.mtime);
|
|
16799
17623
|
} catch {
|
|
16800
|
-
return (
|
|
17624
|
+
return todayLocal();
|
|
16801
17625
|
}
|
|
16802
17626
|
}
|
|
16803
|
-
function
|
|
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
|
|
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 =
|
|
17675
|
+
stat = statSync6(f);
|
|
16850
17676
|
} catch {
|
|
16851
17677
|
continue;
|
|
16852
17678
|
}
|
|
16853
|
-
const mtimeDay =
|
|
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:
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17095
|
-
|
|
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
|
-
|
|
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
|
|
17114
|
-
const
|
|
17115
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
18067
|
+
return buildIngestPayload({
|
|
17138
18068
|
rollup,
|
|
17139
18069
|
richExtras: richBlocks?.rich,
|
|
17140
18070
|
enrichedExtras: richBlocks?.enriched,
|
|
17141
18071
|
artifactSignals: artifactSignals ?? void 0,
|
|
17142
|
-
|
|
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
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
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
|
-
|
|
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
|
|
17171
|
-
|
|
17172
|
-
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
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
|
-
|
|
17182
|
-
|
|
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
|
-
|
|
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",
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
18971
|
+
console.log(`fleetlens ${"0.14.0"}`);
|
|
17951
18972
|
break;
|
|
17952
18973
|
case "help":
|
|
17953
18974
|
case "--help":
|