fleetlens 0.13.1 → 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/events/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/jobs/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/runs/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/api/workflow-agent/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]__0~.f_3z._.js +3 -3
- 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_app_day_[date]_day-view_tsx_0wjoe1u._.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/{0vz4~a~~hq399.js → 02xxpexzk3rf-.js} +1 -1
- package/app/apps/web/.next/static/chunks/0l4r0bqqtdju..css +1 -0
- package/app/apps/web/.next/static/chunks/0~qdw1b2uz._5.js +10 -0
- package/app/apps/web/app/parallelism/gantt-chart.tsx +8 -1
- package/app/apps/web/app/sessions/[id]/session-view/minimap.tsx +1587 -0
- package/app/apps/web/app/sessions/[id]/session-view/workflows-panel.tsx +944 -0
- package/app/apps/web/app/sessions/[id]/session-view.tsx +909 -3223
- 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/components/tail-mode.tsx +7 -1
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/format.ts +17 -0
- 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/02bv_ejzhhzvp.js +0 -10
- package/app/apps/web/.next/static/chunks/083~hzkv577.5.css +0 -1
- package/app/apps/web/.next/static/chunks/0q8dg4txlzno0.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/{VEfemGVAQ0xCHjj15orZU → 3VbAChKD8EChlt8Tm5N5r}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{VEfemGVAQ0xCHjj15orZU → 3VbAChKD8EChlt8Tm5N5r}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{VEfemGVAQ0xCHjj15orZU → 3VbAChKD8EChlt8Tm5N5r}/_ssgManifest.js +0 -0
package/dist/daemon-worker.js
CHANGED
|
@@ -3977,13 +3977,138 @@ async function readJsonl2(filePath) {
|
|
|
3977
3977
|
}
|
|
3978
3978
|
return out;
|
|
3979
3979
|
}
|
|
3980
|
+
function dedupeReplayedLines(lines) {
|
|
3981
|
+
const keepAt = /* @__PURE__ */ new Map();
|
|
3982
|
+
lines.forEach((line, i) => {
|
|
3983
|
+
const rec = line;
|
|
3984
|
+
if (typeof rec.uuid !== "string")
|
|
3985
|
+
return;
|
|
3986
|
+
const prev = keepAt.get(rec.uuid);
|
|
3987
|
+
if (prev === void 0) {
|
|
3988
|
+
keepAt.set(rec.uuid, i);
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
const prevHasTs = lines[prev].timestamp !== void 0;
|
|
3992
|
+
if (!prevHasTs && rec.timestamp !== void 0)
|
|
3993
|
+
keepAt.set(rec.uuid, i);
|
|
3994
|
+
});
|
|
3995
|
+
return lines.filter((line, i) => {
|
|
3996
|
+
const rec = line;
|
|
3997
|
+
return typeof rec.uuid !== "string" || keepAt.get(rec.uuid) === i;
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
function textFromContent(content) {
|
|
4001
|
+
if (typeof content === "string")
|
|
4002
|
+
return content;
|
|
4003
|
+
if (Array.isArray(content)) {
|
|
4004
|
+
const parts = [];
|
|
4005
|
+
for (const b of content) {
|
|
4006
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
4007
|
+
const t = b.text;
|
|
4008
|
+
if (typeof t === "string")
|
|
4009
|
+
parts.push(t);
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
if (parts.length)
|
|
4013
|
+
return parts.join("\n");
|
|
4014
|
+
}
|
|
4015
|
+
return void 0;
|
|
4016
|
+
}
|
|
4017
|
+
function partitionCoworkLines(lines) {
|
|
4018
|
+
const mainLines = [];
|
|
4019
|
+
const subagentGroups = /* @__PURE__ */ new Map();
|
|
4020
|
+
for (const line of lines) {
|
|
4021
|
+
const ptid = line.parent_tool_use_id;
|
|
4022
|
+
if (typeof ptid === "string") {
|
|
4023
|
+
const g = subagentGroups.get(ptid);
|
|
4024
|
+
if (g)
|
|
4025
|
+
g.push(line);
|
|
4026
|
+
else
|
|
4027
|
+
subagentGroups.set(ptid, [line]);
|
|
4028
|
+
} else {
|
|
4029
|
+
mainLines.push(line);
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
return { mainLines, subagentGroups };
|
|
4033
|
+
}
|
|
4034
|
+
function buildCoworkSubagents(subagentGroups, allLines, sessionStartMs) {
|
|
4035
|
+
if (subagentGroups.size === 0)
|
|
4036
|
+
return [];
|
|
4037
|
+
const refs = /* @__PURE__ */ new Map();
|
|
4038
|
+
for (const line of allLines) {
|
|
4039
|
+
const rec = line;
|
|
4040
|
+
const content = rec.message?.content;
|
|
4041
|
+
if (!Array.isArray(content))
|
|
4042
|
+
continue;
|
|
4043
|
+
for (const b of content) {
|
|
4044
|
+
if (!b || typeof b !== "object")
|
|
4045
|
+
continue;
|
|
4046
|
+
const block = b;
|
|
4047
|
+
if (block.type === "tool_use" && block.name === "Agent" && typeof block.id === "string") {
|
|
4048
|
+
const input = block.input ?? {};
|
|
4049
|
+
const ref = refs.get(block.id) ?? {};
|
|
4050
|
+
if (typeof rec.uuid === "string")
|
|
4051
|
+
ref.parentUuid = rec.uuid;
|
|
4052
|
+
if (typeof input.description === "string")
|
|
4053
|
+
ref.description = input.description;
|
|
4054
|
+
if (typeof input.subagent_type === "string")
|
|
4055
|
+
ref.agentType = input.subagent_type;
|
|
4056
|
+
if (typeof input.prompt === "string")
|
|
4057
|
+
ref.prompt = input.prompt;
|
|
4058
|
+
refs.set(block.id, ref);
|
|
4059
|
+
} else if (block.type === "tool_result" && typeof block.tool_use_id === "string" && subagentGroups.has(block.tool_use_id)) {
|
|
4060
|
+
const ref = refs.get(block.tool_use_id) ?? {};
|
|
4061
|
+
ref.finalText = textFromContent(block.content) ?? ref.finalText;
|
|
4062
|
+
refs.set(block.tool_use_id, ref);
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
const runs = [];
|
|
4067
|
+
for (const [toolUseId, groupLines] of subagentGroups) {
|
|
4068
|
+
const summary = summarizeSubagentLines(groupLines);
|
|
4069
|
+
const ref = refs.get(toolUseId) ?? {};
|
|
4070
|
+
const finalText = ref.finalText ?? summary.finalText;
|
|
4071
|
+
const finalPreview = finalText ? finalText.replace(/\s+/g, " ").trim().slice(0, 240) : summary.finalPreview;
|
|
4072
|
+
const startTOffsetMs = sessionStartMs !== void 0 && summary.startMs !== void 0 ? Math.max(0, summary.startMs - sessionStartMs) : void 0;
|
|
4073
|
+
const endTOffsetMs = sessionStartMs !== void 0 && summary.endMs !== void 0 ? Math.max(0, summary.endMs - sessionStartMs) : void 0;
|
|
4074
|
+
const durationMs = summary.startMs !== void 0 && summary.endMs !== void 0 ? summary.endMs - summary.startMs : void 0;
|
|
4075
|
+
runs.push({
|
|
4076
|
+
agentId: toolUseId,
|
|
4077
|
+
agentType: ref.agentType ?? "unknown",
|
|
4078
|
+
description: ref.description ?? (ref.prompt ? ref.prompt.replace(/\s+/g, " ").trim().slice(0, 80) : "(no description)"),
|
|
4079
|
+
startMs: summary.startMs,
|
|
4080
|
+
endMs: summary.endMs,
|
|
4081
|
+
durationMs,
|
|
4082
|
+
startTOffsetMs,
|
|
4083
|
+
endTOffsetMs,
|
|
4084
|
+
eventCount: summary.eventCount,
|
|
4085
|
+
totalUsage: summary.totalUsage,
|
|
4086
|
+
parentUuid: ref.parentUuid,
|
|
4087
|
+
parentToolUseId: toolUseId,
|
|
4088
|
+
runInBackground: false,
|
|
4089
|
+
finalPreview,
|
|
4090
|
+
finalText,
|
|
4091
|
+
prompt: ref.prompt,
|
|
4092
|
+
model: summary.model,
|
|
4093
|
+
toolCalls: summary.toolCalls,
|
|
4094
|
+
toolCallCount: summary.toolCallCount,
|
|
4095
|
+
assistantMessageCount: summary.assistantMessageCount
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
runs.sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
|
|
4099
|
+
return runs;
|
|
4100
|
+
}
|
|
3980
4101
|
async function parseCoworkSession(file) {
|
|
3981
4102
|
const [lines, coworkMeta, spaceIdToPath] = await Promise.all([
|
|
3982
4103
|
readJsonl2(file.auditPath),
|
|
3983
4104
|
readCoworkMeta(file.metaPath),
|
|
3984
4105
|
loadSpacesForWorkspace(file.workspaceDir)
|
|
3985
4106
|
]);
|
|
3986
|
-
const
|
|
4107
|
+
const deduped = dedupeReplayedLines(lines);
|
|
4108
|
+
const { mainLines, subagentGroups } = partitionCoworkLines(deduped);
|
|
4109
|
+
const { meta: baseMeta, events } = parseTranscript(mainLines);
|
|
4110
|
+
const sessionStartMs = baseMeta.firstTimestamp ? Date.parse(baseMeta.firstTimestamp) : void 0;
|
|
4111
|
+
const subagents = buildCoworkSubagents(subagentGroups, deduped, sessionStartMs);
|
|
3987
4112
|
const { projectName, projectDir, cwd } = resolveProject(coworkMeta, spaceIdToPath);
|
|
3988
4113
|
const meta = {
|
|
3989
4114
|
...baseMeta,
|
|
@@ -3996,7 +4121,7 @@ async function parseCoworkSession(file) {
|
|
|
3996
4121
|
cwd: cwd ?? baseMeta.cwd,
|
|
3997
4122
|
model: baseMeta.model ?? coworkMeta.model
|
|
3998
4123
|
};
|
|
3999
|
-
return { meta, events };
|
|
4124
|
+
return { meta, events, subagents };
|
|
4000
4125
|
}
|
|
4001
4126
|
function cacheKeyFor(file) {
|
|
4002
4127
|
return {
|
|
@@ -4047,8 +4172,12 @@ async function getCoworkSession(id, opts = {}) {
|
|
|
4047
4172
|
const cached = detailCache5.get(file.auditPath);
|
|
4048
4173
|
if (cached && cacheKeyMatches(cached.key, key))
|
|
4049
4174
|
return cached.detail;
|
|
4050
|
-
const { meta, events } = await parseCoworkSession(file);
|
|
4051
|
-
const detail = {
|
|
4175
|
+
const { meta, events, subagents } = await parseCoworkSession(file);
|
|
4176
|
+
const detail = {
|
|
4177
|
+
...meta,
|
|
4178
|
+
events,
|
|
4179
|
+
...subagents.length ? { subagents } : {}
|
|
4180
|
+
};
|
|
4052
4181
|
detailCache5.set(file.auditPath, { detail, key });
|
|
4053
4182
|
return detail;
|
|
4054
4183
|
}
|
|
@@ -4057,6 +4186,7 @@ var init_cowork = __esm({
|
|
|
4057
4186
|
"../parser/dist/cowork.js"() {
|
|
4058
4187
|
"use strict";
|
|
4059
4188
|
init_analytics();
|
|
4189
|
+
init_claude_code();
|
|
4060
4190
|
init_parser();
|
|
4061
4191
|
DEFAULT_COWORK_ROOT = defaultCoworkRoot();
|
|
4062
4192
|
SESSION_ID_RE = /^local_[0-9a-f-]+$/;
|
|
@@ -4375,9 +4505,14 @@ function buildCalibrationCurve(events, snapshots, tier = "pro-max-20x", granular
|
|
|
4375
4505
|
curve.push({
|
|
4376
4506
|
ts: new Date(cur).toISOString(),
|
|
4377
4507
|
real_5h: real5,
|
|
4378
|
-
|
|
4508
|
+
// Predicted utilization LEGITIMATELY exceeds 200 on overage (the
|
|
4509
|
+
// extrapolated cycle close). The old min(200) clamp silently capped it,
|
|
4510
|
+
// so cyclePeaks.peakPct could never carry a *predicted* overage to the
|
|
4511
|
+
// team server (which now accepts up to 10000). Keep only a generous
|
|
4512
|
+
// finite ceiling as a corruption guard, matching the wire cap.
|
|
4513
|
+
pred_5h: Math.max(0, Math.min(1e4, p5)),
|
|
4379
4514
|
real_7d: real7,
|
|
4380
|
-
pred_7d: Math.max(0, Math.min(
|
|
4515
|
+
pred_7d: Math.max(0, Math.min(1e4, p7)),
|
|
4381
4516
|
cycle_end_5h: r5k != null ? new Date(r5k).toISOString() : null,
|
|
4382
4517
|
cycle_end_7d: r7k != null ? new Date(r7k).toISOString() : null
|
|
4383
4518
|
});
|
|
@@ -4971,7 +5106,7 @@ __export(file_probe_exports, {
|
|
|
4971
5106
|
probeArtifactSignals: () => probeArtifactSignals
|
|
4972
5107
|
});
|
|
4973
5108
|
import { createHash } from "node:crypto";
|
|
4974
|
-
import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as
|
|
5109
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
4975
5110
|
import { homedir as homedir2 } from "node:os";
|
|
4976
5111
|
import { join as join7, relative, resolve as resolve2, sep } from "node:path";
|
|
4977
5112
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -5002,7 +5137,7 @@ function listMarkdownFiles(dir) {
|
|
|
5002
5137
|
const full = join7(dir, name);
|
|
5003
5138
|
let stat;
|
|
5004
5139
|
try {
|
|
5005
|
-
stat =
|
|
5140
|
+
stat = statSync4(full);
|
|
5006
5141
|
} catch {
|
|
5007
5142
|
continue;
|
|
5008
5143
|
}
|
|
@@ -5019,19 +5154,19 @@ function firstSeenDateOf(file) {
|
|
|
5019
5154
|
const out = execFileSync2(
|
|
5020
5155
|
"git",
|
|
5021
5156
|
["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
|
|
5022
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd:
|
|
5157
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname7(file) }
|
|
5023
5158
|
).trim().split("\n").filter(Boolean);
|
|
5024
5159
|
if (out.length > 0) return out[out.length - 1];
|
|
5025
5160
|
} catch {
|
|
5026
5161
|
}
|
|
5027
5162
|
try {
|
|
5028
|
-
const s =
|
|
5029
|
-
return
|
|
5163
|
+
const s = statSync4(file);
|
|
5164
|
+
return localDay(s.birthtime || s.mtime);
|
|
5030
5165
|
} catch {
|
|
5031
|
-
return (
|
|
5166
|
+
return todayLocal();
|
|
5032
5167
|
}
|
|
5033
5168
|
}
|
|
5034
|
-
function
|
|
5169
|
+
function dirname7(p) {
|
|
5035
5170
|
const i = p.lastIndexOf(sep);
|
|
5036
5171
|
return i < 0 ? "." : p.slice(0, i);
|
|
5037
5172
|
}
|
|
@@ -5046,13 +5181,15 @@ function detectGitEmail(cwd) {
|
|
|
5046
5181
|
return null;
|
|
5047
5182
|
}
|
|
5048
5183
|
}
|
|
5049
|
-
function
|
|
5050
|
-
const d = /* @__PURE__ */ new Date();
|
|
5184
|
+
function localDay(d) {
|
|
5051
5185
|
const y = d.getFullYear();
|
|
5052
5186
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
5053
5187
|
const day = String(d.getDate()).padStart(2, "0");
|
|
5054
5188
|
return `${y}-${m}-${day}`;
|
|
5055
5189
|
}
|
|
5190
|
+
function todayLocal() {
|
|
5191
|
+
return localDay(/* @__PURE__ */ new Date());
|
|
5192
|
+
}
|
|
5056
5193
|
function scanAuthored(roots, subdir, day) {
|
|
5057
5194
|
const out = [];
|
|
5058
5195
|
for (const r of roots) {
|
|
@@ -5077,11 +5214,11 @@ function scanEdited(roots, day) {
|
|
|
5077
5214
|
if (firstSeen === day) continue;
|
|
5078
5215
|
let stat;
|
|
5079
5216
|
try {
|
|
5080
|
-
stat =
|
|
5217
|
+
stat = statSync4(f);
|
|
5081
5218
|
} catch {
|
|
5082
5219
|
continue;
|
|
5083
5220
|
}
|
|
5084
|
-
const mtimeDay =
|
|
5221
|
+
const mtimeDay = localDay(stat.mtime);
|
|
5085
5222
|
if (mtimeDay !== day) continue;
|
|
5086
5223
|
const rel = relative(dir, f);
|
|
5087
5224
|
out.push({ pathHash: pathHash(`${r.label}:skills:${rel}`), lineDelta: 0 });
|
|
@@ -5103,7 +5240,7 @@ function claudemdLineDelta(extraRoots, authorEmail, day) {
|
|
|
5103
5240
|
const out = execFileSync2(
|
|
5104
5241
|
"git",
|
|
5105
5242
|
["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
|
|
5106
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd:
|
|
5243
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname7(file) }
|
|
5107
5244
|
);
|
|
5108
5245
|
let currentDate = "";
|
|
5109
5246
|
let currentEmail = "";
|
|
@@ -5154,7 +5291,7 @@ var init_file_probe = __esm({
|
|
|
5154
5291
|
|
|
5155
5292
|
// src/daemon-worker.ts
|
|
5156
5293
|
import { spawn as spawn2 } from "node:child_process";
|
|
5157
|
-
import {
|
|
5294
|
+
import { closeSync, existsSync as existsSync13, mkdirSync as mkdirSync13, openSync, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "node:fs";
|
|
5158
5295
|
import { dirname as dirname10, join as join11 } from "node:path";
|
|
5159
5296
|
import { fileURLToPath } from "node:url";
|
|
5160
5297
|
|
|
@@ -5300,6 +5437,21 @@ function normalizeExtra(raw) {
|
|
|
5300
5437
|
init_storage();
|
|
5301
5438
|
init_fs();
|
|
5302
5439
|
|
|
5440
|
+
// src/daemon-log.ts
|
|
5441
|
+
init_fs();
|
|
5442
|
+
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
5443
|
+
function appendDaemonLogLine(level, msg) {
|
|
5444
|
+
try {
|
|
5445
|
+
appendFileSync2(
|
|
5446
|
+
cclensPath("daemon.log"),
|
|
5447
|
+
`${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${msg}
|
|
5448
|
+
`,
|
|
5449
|
+
"utf8"
|
|
5450
|
+
);
|
|
5451
|
+
} catch {
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
|
|
5303
5455
|
// src/usage/backoff.ts
|
|
5304
5456
|
var BASE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
5305
5457
|
var MAX_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -5375,8 +5527,8 @@ function skippedTrivialEnrichment(generatedAt) {
|
|
|
5375
5527
|
retry_count: 0
|
|
5376
5528
|
};
|
|
5377
5529
|
}
|
|
5378
|
-
function entryKey(sessionId,
|
|
5379
|
-
return `${sessionId}__${
|
|
5530
|
+
function entryKey(sessionId, localDay2) {
|
|
5531
|
+
return `${sessionId}__${localDay2}`;
|
|
5380
5532
|
}
|
|
5381
5533
|
function parseEntryKey(key) {
|
|
5382
5534
|
const m = /^([^_]+(?:_[^_]+)*)__(\d{4}-\d{2}-\d{2})$/.exec(key);
|
|
@@ -5504,8 +5656,8 @@ function entriesDir() {
|
|
|
5504
5656
|
entriesDirCached = envOverride ?? cclensPath("entries");
|
|
5505
5657
|
return entriesDirCached;
|
|
5506
5658
|
}
|
|
5507
|
-
function pathFor(sessionId,
|
|
5508
|
-
return join4(entriesDir(), `${entryKey(sessionId,
|
|
5659
|
+
function pathFor(sessionId, localDay2) {
|
|
5660
|
+
return join4(entriesDir(), `${entryKey(sessionId, localDay2)}.json`);
|
|
5509
5661
|
}
|
|
5510
5662
|
function tmpPathFor(final) {
|
|
5511
5663
|
return `${final}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
@@ -5551,24 +5703,27 @@ function writeEntryPreservingEnrichment(fresh) {
|
|
|
5551
5703
|
writeFileSync3(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
|
|
5552
5704
|
renameSync2(tmp, final);
|
|
5553
5705
|
}
|
|
5554
|
-
function readEntry(sessionId,
|
|
5555
|
-
const p = pathFor(sessionId,
|
|
5706
|
+
function readEntry(sessionId, localDay2) {
|
|
5707
|
+
const p = pathFor(sessionId, localDay2);
|
|
5556
5708
|
if (!existsSync3(p))
|
|
5557
5709
|
return null;
|
|
5558
5710
|
const raw = readFileSync5(p, "utf8");
|
|
5559
5711
|
return JSON.parse(raw);
|
|
5560
5712
|
}
|
|
5713
|
+
function entryExists(sessionId, localDay2) {
|
|
5714
|
+
return existsSync3(pathFor(sessionId, localDay2));
|
|
5715
|
+
}
|
|
5561
5716
|
function listEntryKeys() {
|
|
5562
5717
|
const dir = entriesDir();
|
|
5563
5718
|
if (!existsSync3(dir))
|
|
5564
5719
|
return [];
|
|
5565
5720
|
return readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length));
|
|
5566
5721
|
}
|
|
5567
|
-
function listEntriesForDay(
|
|
5722
|
+
function listEntriesForDay(localDay2) {
|
|
5568
5723
|
const out = [];
|
|
5569
5724
|
for (const key of listEntryKeys()) {
|
|
5570
5725
|
const parsed = parseEntryKey(key);
|
|
5571
|
-
if (!parsed || parsed.local_day !==
|
|
5726
|
+
if (!parsed || parsed.local_day !== localDay2)
|
|
5572
5727
|
continue;
|
|
5573
5728
|
const e = readEntry(parsed.session_id, parsed.local_day);
|
|
5574
5729
|
if (e)
|
|
@@ -5577,1657 +5732,1888 @@ function listEntriesForDay(localDay) {
|
|
|
5577
5732
|
return out;
|
|
5578
5733
|
}
|
|
5579
5734
|
|
|
5580
|
-
// src/
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
utilization: raw.extra_usage.utilization
|
|
5604
|
-
} : null
|
|
5605
|
-
};
|
|
5606
|
-
}
|
|
5607
|
-
function bucketToRollup(b) {
|
|
5608
|
-
return {
|
|
5609
|
-
day: b.date,
|
|
5610
|
-
agentTimeMs: b.airTimeMs,
|
|
5611
|
-
sessions: b.sessions,
|
|
5612
|
-
toolCalls: b.toolCalls,
|
|
5613
|
-
turns: b.turns,
|
|
5614
|
-
tokens: { ...b.tokens }
|
|
5615
|
-
};
|
|
5616
|
-
}
|
|
5617
|
-
function buildRollupsForRange(sessions, sinceDay) {
|
|
5618
|
-
const startCounts = /* @__PURE__ */ new Map();
|
|
5619
|
-
for (const s of sessions) {
|
|
5620
|
-
const d = sessionDay(s);
|
|
5621
|
-
if (d) startCounts.set(d, (startCounts.get(d) ?? 0) + 1);
|
|
5622
|
-
}
|
|
5623
|
-
return dailyActivity(sessions).filter((b) => !sinceDay || b.date >= sinceDay).map((b) => ({ ...bucketToRollup(b), uniqueSessions: startCounts.get(b.date) ?? 0 }));
|
|
5735
|
+
// src/perception/build-entries.ts
|
|
5736
|
+
init_dist();
|
|
5737
|
+
import { statSync, readFileSync as readFileSync6 } from "node:fs";
|
|
5738
|
+
import { basename, dirname as dirname3 } from "node:path";
|
|
5739
|
+
|
|
5740
|
+
// ../entries/dist/signals.js
|
|
5741
|
+
init_dist();
|
|
5742
|
+
var TEAMMATE_RE = /^<teammate-message\b/;
|
|
5743
|
+
var SKILL_LOAD_RE = /^Base directory for this skill:/;
|
|
5744
|
+
var SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
|
|
5745
|
+
function classifyUserInputSource(text) {
|
|
5746
|
+
if (!text)
|
|
5747
|
+
return "human";
|
|
5748
|
+
const stripped = stripFrameworkBoilerplate(text);
|
|
5749
|
+
if (!stripped)
|
|
5750
|
+
return "system_instruction";
|
|
5751
|
+
if (TEAMMATE_RE.test(stripped))
|
|
5752
|
+
return "teammate";
|
|
5753
|
+
if (SKILL_LOAD_RE.test(stripped))
|
|
5754
|
+
return "skill_load";
|
|
5755
|
+
if (SLASH_COMMAND_RE2.test(stripped))
|
|
5756
|
+
return "slash_command";
|
|
5757
|
+
return "human";
|
|
5624
5758
|
}
|
|
5625
|
-
|
|
5626
|
-
|
|
5759
|
+
var HAPPY_RE = /\b(?:yay|yaay|yass|woohoo|nice|great|love(?:ly)?|perfect|amazing|awesome)\s*!|!!!/gi;
|
|
5760
|
+
var SATISFIED_RE = /\b(?:thanks|thank you|looks good|lgtm|works|that works|all good|sounds good)\b/gi;
|
|
5761
|
+
var DISSATISFIED_RE = /\b(?:that'?s (?:not|wrong)|try again|no, (?:that|this)|not quite|incorrect)\b/gi;
|
|
5762
|
+
var FRUSTRATED_RE = /\b(?:broken|stop|why (?:did|are|would) you|give up|ugh|argh|wtf)\b/gi;
|
|
5763
|
+
function countSatisfactionSignals(text) {
|
|
5764
|
+
if (!text)
|
|
5765
|
+
return { happy: 0, satisfied: 0, dissatisfied: 0, frustrated: 0 };
|
|
5627
5766
|
return {
|
|
5628
|
-
|
|
5629
|
-
|
|
5767
|
+
happy: (text.match(HAPPY_RE) ?? []).length,
|
|
5768
|
+
satisfied: (text.match(SATISFIED_RE) ?? []).length,
|
|
5769
|
+
dissatisfied: (text.match(DISSATISFIED_RE) ?? []).length,
|
|
5770
|
+
frustrated: (text.match(FRUSTRATED_RE) ?? []).length
|
|
5630
5771
|
};
|
|
5631
5772
|
}
|
|
5632
|
-
|
|
5633
|
-
|
|
5773
|
+
var INSTRUCTION_RE = /\b(?:can you|please|let's|let us|i need|could you|would you)\s+([^.?!\n]{5,200})/gi;
|
|
5774
|
+
function extractUserInstructions(text) {
|
|
5775
|
+
if (!text)
|
|
5776
|
+
return [];
|
|
5777
|
+
INSTRUCTION_RE.lastIndex = 0;
|
|
5634
5778
|
const out = [];
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5779
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5780
|
+
let m;
|
|
5781
|
+
while ((m = INSTRUCTION_RE.exec(text)) !== null) {
|
|
5782
|
+
const phrase = m[1].trim().replace(/\s+/g, " ");
|
|
5783
|
+
const lc = phrase.toLowerCase();
|
|
5784
|
+
if (seen.has(lc))
|
|
5785
|
+
continue;
|
|
5786
|
+
seen.add(lc);
|
|
5787
|
+
out.push(phrase);
|
|
5788
|
+
if (out.length >= 5)
|
|
5789
|
+
break;
|
|
5639
5790
|
}
|
|
5640
5791
|
return out;
|
|
5641
5792
|
}
|
|
5642
|
-
function
|
|
5643
|
-
const
|
|
5644
|
-
if (s
|
|
5645
|
-
return
|
|
5793
|
+
function classifySubagentRole(sa) {
|
|
5794
|
+
const text = `${sa.description} ${sa.prompt_preview}`.toLowerCase();
|
|
5795
|
+
if (/\b(re-?review|review[s]?|verify|audit|spec[- ]review|code[- ]quality|code[- ]reuse|efficiency review|sanity check|quick test)\b/.test(text)) {
|
|
5796
|
+
return "reviewer";
|
|
5646
5797
|
}
|
|
5647
|
-
|
|
5798
|
+
if (/\b(implement|build chunk|build task|initialize|create the|add the)\b/.test(text)) {
|
|
5799
|
+
return "implementer";
|
|
5800
|
+
}
|
|
5801
|
+
if (/\bexplore\b|\binventory\b|\bmap\b.*\b(codebase|repo|branch)\b|\baudit\b.*coverage/.test(text)) {
|
|
5802
|
+
return "explorer";
|
|
5803
|
+
}
|
|
5804
|
+
if (/\b(investigate|research|reverse[- ]engineer|study|analy[sz]e|look up|tell me|brief me on|find out)\b/.test(text)) {
|
|
5805
|
+
return "researcher";
|
|
5806
|
+
}
|
|
5807
|
+
if (/\b(env(?:ironment)? setup|configure|setup|bootstrap)\b/.test(text)) {
|
|
5808
|
+
return "env-setup";
|
|
5809
|
+
}
|
|
5810
|
+
if (/\b(polish|cleanup|fix|refactor)\b/.test(text)) {
|
|
5811
|
+
return "polish";
|
|
5812
|
+
}
|
|
5813
|
+
return "other";
|
|
5648
5814
|
}
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5815
|
+
var STOCK_SUBAGENT_PREFIXES = /^(general-purpose|Explore|Plan|claude-code-guide|playwright-qa-verifier|statusline-setup|frontend-design:|code-review:|code-simplifier:|codex:|superpowers:)/;
|
|
5816
|
+
function isStockSubagentType(type) {
|
|
5817
|
+
return STOCK_SUBAGENT_PREFIXES.test(type);
|
|
5818
|
+
}
|
|
5819
|
+
function inferWorkingShape(entry) {
|
|
5820
|
+
if (entry.numbers.turn_count < 2)
|
|
5821
|
+
return null;
|
|
5822
|
+
const subagents = entry.subagents ?? [];
|
|
5823
|
+
const roles = subagents.map(classifySubagentRole);
|
|
5824
|
+
const reviewerCount = roles.filter((r) => r === "reviewer").length;
|
|
5825
|
+
const implementerCount = roles.filter((r) => r === "implementer").length;
|
|
5826
|
+
const explorerOrResearcher = roles.filter((r) => r === "explorer" || r === "researcher").length;
|
|
5827
|
+
const hasBackground = subagents.some((sa) => sa.background);
|
|
5828
|
+
if (implementerCount >= 2) {
|
|
5829
|
+
const chunkRefs = subagents.filter((sa) => /\b(chunk|task)\s*\d+/i.test(`${sa.description} ${sa.prompt_preview}`));
|
|
5830
|
+
if (chunkRefs.length >= 2)
|
|
5831
|
+
return "chunk-implementation";
|
|
5656
5832
|
}
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
for (const d of e.edited_dirs ?? []) {
|
|
5665
|
-
const r = resolveRepo(d);
|
|
5666
|
-
if (r) counts.set(r, (counts.get(r) ?? 0) + 1);
|
|
5667
|
-
}
|
|
5668
|
-
const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
5669
|
-
if (dominant) return dominant;
|
|
5833
|
+
if (reviewerCount >= 3 && implementerCount === 0) {
|
|
5834
|
+
const lensSigs = /* @__PURE__ */ new Set();
|
|
5835
|
+
for (let i = 0; i < subagents.length; i++) {
|
|
5836
|
+
if (roles[i] !== "reviewer")
|
|
5837
|
+
continue;
|
|
5838
|
+
const sig = subagents[i].description.toLowerCase().replace(/[^a-z0-9 ]/g, "").split(/\s+/).filter((w) => w.length > 2).slice(0, 3).join("|");
|
|
5839
|
+
lensSigs.add(sig);
|
|
5670
5840
|
}
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
const sessionRepo = /* @__PURE__ */ new Map();
|
|
5674
|
-
for (const e of entries) {
|
|
5675
|
-
if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
|
|
5676
|
-
const r = repoForEntry(e);
|
|
5677
|
-
if (r) sessionRepo.set(e.session_id, r);
|
|
5841
|
+
if (lensSigs.size >= 3)
|
|
5842
|
+
return "reviewer-triad";
|
|
5678
5843
|
}
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
if (privateProjects.has(name) || privateProjects.has(canonical)) continue;
|
|
5684
|
-
const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
|
|
5685
|
-
const key = repo ?? name;
|
|
5686
|
-
const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
|
|
5687
|
-
const cur = projects.get(key) ?? { project: name, agentTimeMs: 0, sessions: 0, ...repo ? { resolved: repo } : {} };
|
|
5688
|
-
cur.agentTimeMs += ms;
|
|
5689
|
-
cur.sessions += 1;
|
|
5690
|
-
projects.set(key, cur);
|
|
5844
|
+
if (reviewerCount >= 2)
|
|
5845
|
+
return "spec-review-loop";
|
|
5846
|
+
if (explorerOrResearcher >= 2 || explorerOrResearcher >= 1 && implementerCount >= 1) {
|
|
5847
|
+
return "research-then-build";
|
|
5691
5848
|
}
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
let longCount = 0;
|
|
5703
|
-
let longTotalMin = 0;
|
|
5704
|
-
let longMaxSingleMin = 0;
|
|
5705
|
-
for (const e of entries) {
|
|
5706
|
-
if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
|
|
5707
|
-
const shape = e.signals?.working_shape ?? null;
|
|
5708
|
-
if (shape) {
|
|
5709
|
-
const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
|
|
5710
|
-
cur.sessions += 1;
|
|
5711
|
-
cur.agentTimeMs += Math.round(e.numbers.active_min * 6e4);
|
|
5712
|
-
workingShapes.set(shape, cur);
|
|
5713
|
-
}
|
|
5714
|
-
for (const [name, count] of Object.entries(e.skills)) {
|
|
5715
|
-
if (count > 0) skills.set(name, (skills.get(name) ?? 0) + 1);
|
|
5716
|
-
}
|
|
5717
|
-
for (const sa of e.subagents) {
|
|
5718
|
-
subagents.set(sa.type, (subagents.get(sa.type) ?? 0) + 1);
|
|
5719
|
-
}
|
|
5720
|
-
toolErrors += e.numbers.tool_errors;
|
|
5721
|
-
if (e.signals?.brainstorm_warmup) brainstorm += 1;
|
|
5722
|
-
if (e.numbers.exit_plan_calls > 0) planMode += 1;
|
|
5723
|
-
prs += e.numbers.prs;
|
|
5724
|
-
commits += e.numbers.commits;
|
|
5725
|
-
pushes += e.numbers.pushes;
|
|
5726
|
-
if (e.github_repos?.length) {
|
|
5727
|
-
const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
|
|
5728
|
-
const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
|
|
5729
|
-
for (const r of e.github_repos) set.add(r);
|
|
5730
|
-
projectRepos.set(key, set);
|
|
5731
|
-
}
|
|
5732
|
-
if (e.flags.includes("long_autonomous")) {
|
|
5733
|
-
longCount += 1;
|
|
5734
|
-
longTotalMin += e.numbers.active_min;
|
|
5735
|
-
if (e.numbers.active_min > longMaxSingleMin) longMaxSingleMin = e.numbers.active_min;
|
|
5736
|
-
}
|
|
5849
|
+
if (hasBackground && subagents.length >= 1)
|
|
5850
|
+
return "background-coordinated";
|
|
5851
|
+
if (subagents.length === 0) {
|
|
5852
|
+
const first = (entry.first_user || "").trim().toLowerCase();
|
|
5853
|
+
if (/^continue\b/.test(first) || first === "continue.")
|
|
5854
|
+
return "solo-continuation";
|
|
5855
|
+
const skills = Object.keys(entry.skills ?? {});
|
|
5856
|
+
if (skills.some((s) => /brainstorm|writing-plans|brainstorming/i.test(s)))
|
|
5857
|
+
return "solo-design";
|
|
5858
|
+
return "solo-build";
|
|
5737
5859
|
}
|
|
5738
|
-
return
|
|
5739
|
-
projects: Array.from(projects.entries()).map(([key, v]) => {
|
|
5740
|
-
const extras = [...projectRepos.get(key) ?? []].filter((r) => r !== v.resolved);
|
|
5741
|
-
const repos = v.resolved ? [v.resolved, ...extras] : extras;
|
|
5742
|
-
return {
|
|
5743
|
-
project: v.project,
|
|
5744
|
-
agentTimeMs: v.agentTimeMs,
|
|
5745
|
-
sessions: v.sessions,
|
|
5746
|
-
...repos.length ? { githubRepos: repos.slice(0, 5) } : {}
|
|
5747
|
-
};
|
|
5748
|
-
}).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
|
|
5749
|
-
workingShapes: Array.from(workingShapes.entries()).map(([shape, v]) => ({ shape, ...v })).sort((a, b) => b.sessions - a.sessions),
|
|
5750
|
-
concurrencyPeak: stats.peakConcurrent,
|
|
5751
|
-
parallelMinutes: Math.round(stats.totalParallelMs / 6e4),
|
|
5752
|
-
// Wire schema requires integer minutes (active_min is a float).
|
|
5753
|
-
longAutonomous: { count: longCount, totalMin: Math.round(longTotalMin), maxSingleMin: Math.round(longMaxSingleMin) },
|
|
5754
|
-
toolErrors,
|
|
5755
|
-
skillsLoaded: Array.from(skills.entries()).map(([name, sessions]) => ({ name, sessions })).sort((a, b) => b.sessions - a.sessions),
|
|
5756
|
-
subagentsDispatched: Array.from(subagents.entries()).map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count),
|
|
5757
|
-
brainstormWarmupSessions: brainstorm,
|
|
5758
|
-
planModeUsed: planMode,
|
|
5759
|
-
prs,
|
|
5760
|
-
commits,
|
|
5761
|
-
pushes
|
|
5762
|
-
};
|
|
5860
|
+
return "solo-build";
|
|
5763
5861
|
}
|
|
5764
|
-
function
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
const
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5862
|
+
function detectPromptFrames(text) {
|
|
5863
|
+
if (!text)
|
|
5864
|
+
return [];
|
|
5865
|
+
const out = [];
|
|
5866
|
+
if (/<teammate-message\b/i.test(text))
|
|
5867
|
+
out.push("teammate");
|
|
5868
|
+
if (/<task-notification\b/i.test(text))
|
|
5869
|
+
out.push("task-notification");
|
|
5870
|
+
if (/<local-command-caveat\b/i.test(text))
|
|
5871
|
+
out.push("local-command-caveat");
|
|
5872
|
+
if (/<command-(message|name)\b/i.test(text))
|
|
5873
|
+
out.push("slash-command");
|
|
5874
|
+
if (/\[Image #\d+\]/.test(text))
|
|
5875
|
+
out.push("image-attached");
|
|
5876
|
+
if (/\b(here'?s the (handoff|handover) prompt|session-close summary|wrap-up summary|all wrapped up|all captured\.)\b/i.test(text) || /^##? (Wrap-up|Session conclusion|Handoff)\b/m.test(text) || /^# Handoff:/m.test(text)) {
|
|
5877
|
+
out.push("handoff-prose");
|
|
5779
5878
|
}
|
|
5780
|
-
return
|
|
5781
|
-
}
|
|
5782
|
-
function buildRichBlocksForDay(day, daySessions, privateProjects, enrichmentOptIn, resolveRepo) {
|
|
5783
|
-
const entries = listEntriesForDay(day);
|
|
5784
|
-
if (entries.length === 0) return void 0;
|
|
5785
|
-
const rich = buildRichRollupBlocks(day, daySessions, entries, privateProjects, resolveRepo);
|
|
5786
|
-
const enriched = enrichmentOptIn ? buildEnrichedExtras(entries) : void 0;
|
|
5787
|
-
return enriched ? { rich, enriched } : { rich };
|
|
5788
|
-
}
|
|
5789
|
-
function buildIngestPayload(inputs) {
|
|
5790
|
-
const richRollup = inputs.rollup && inputs.richExtras ? { ...inputs.rollup, ...inputs.richExtras } : void 0;
|
|
5791
|
-
return {
|
|
5792
|
-
ingestId: randomUUID(),
|
|
5793
|
-
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5794
|
-
cliVersion: "0.13.1",
|
|
5795
|
-
...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
|
|
5796
|
-
...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
|
|
5797
|
-
...richRollup ? { richRollup } : {},
|
|
5798
|
-
...inputs.enrichedExtras ? { enrichedExtras: inputs.enrichedExtras } : {},
|
|
5799
|
-
...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
|
|
5800
|
-
...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
|
|
5801
|
-
...inputs.planTier ? { planTier: inputs.planTier } : {},
|
|
5802
|
-
...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {}
|
|
5803
|
-
};
|
|
5804
|
-
}
|
|
5805
|
-
async function pushToTeamServer(config, payload) {
|
|
5806
|
-
const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
|
|
5807
|
-
method: "POST",
|
|
5808
|
-
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
5809
|
-
headers: {
|
|
5810
|
-
"Content-Type": "application/json",
|
|
5811
|
-
Authorization: `Bearer ${config.bearerToken}`
|
|
5812
|
-
},
|
|
5813
|
-
body: JSON.stringify(payload)
|
|
5814
|
-
});
|
|
5815
|
-
const body = await res.json().catch(() => null);
|
|
5816
|
-
return { ok: res.ok, status: res.status, body };
|
|
5879
|
+
return out;
|
|
5817
5880
|
}
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5881
|
+
var STOCK_SKILL_PREFIXES = /^(superpowers|mcp__|frontend-design|code-review|codex|claude-code-guide|claude-api):/i;
|
|
5882
|
+
function classifySkill(name) {
|
|
5883
|
+
if (name.startsWith("(ToolSearch:"))
|
|
5884
|
+
return "infra";
|
|
5885
|
+
if (STOCK_SKILL_PREFIXES.test(name))
|
|
5886
|
+
return "stock";
|
|
5887
|
+
if (name === "using-superpowers")
|
|
5888
|
+
return "stock";
|
|
5889
|
+
return "user";
|
|
5826
5890
|
}
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5891
|
+
var EXTERNAL_REF_PATTERNS = [
|
|
5892
|
+
{ kind: "linear-kip", re: /\bKIP-\d+\b/i },
|
|
5893
|
+
{ kind: "github-issue-pr", re: /\b(issue|pr|pull request)\s*#\d+\b|github\.com\/[^\s]+\/(issues|pull)\/\d+/i },
|
|
5894
|
+
{ kind: "branch-ref", re: /\b(branch|feat|fix|chore|refactor)[/:]\s*[\w./-]+|on `[\w./-]+`/i },
|
|
5895
|
+
{ kind: "url", re: /https?:\/\/\S+/ }
|
|
5896
|
+
];
|
|
5897
|
+
function detectExternalRef(text) {
|
|
5898
|
+
if (!text)
|
|
5899
|
+
return null;
|
|
5900
|
+
if (/<teammate-message|<task-notification|<local-command-caveat/i.test(text.slice(0, 400)))
|
|
5901
|
+
return null;
|
|
5902
|
+
for (const { kind, re } of EXTERNAL_REF_PATTERNS) {
|
|
5903
|
+
const m = text.match(re);
|
|
5904
|
+
if (m)
|
|
5905
|
+
return { kind, preview: text.slice(0, 160) };
|
|
5838
5906
|
}
|
|
5839
5907
|
return null;
|
|
5840
5908
|
}
|
|
5841
|
-
function
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
if (
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
const commondirFile = join5(gd, "commondir");
|
|
5850
|
-
if (existsSync4(commondirFile)) {
|
|
5851
|
-
const common = readFileSync6(commondirFile, "utf8").trim();
|
|
5852
|
-
return join5(isAbsolute(common) ? common : resolve(gd, common), "config");
|
|
5853
|
-
}
|
|
5854
|
-
return join5(gd, "config");
|
|
5909
|
+
function bucketVerbosity(n) {
|
|
5910
|
+
if (n < 100)
|
|
5911
|
+
return "short";
|
|
5912
|
+
if (n < 500)
|
|
5913
|
+
return "medium";
|
|
5914
|
+
if (n < 2e3)
|
|
5915
|
+
return "long";
|
|
5916
|
+
return "very_long";
|
|
5855
5917
|
}
|
|
5856
|
-
function
|
|
5857
|
-
const
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
repo = null;
|
|
5875
|
-
}
|
|
5876
|
-
cache.set(dir, repo);
|
|
5877
|
-
return repo;
|
|
5918
|
+
function computeEntrySignals(entry) {
|
|
5919
|
+
const fu = entry.first_user || "";
|
|
5920
|
+
const verbosity = bucketVerbosity(fu.length);
|
|
5921
|
+
const ext = detectExternalRef(fu);
|
|
5922
|
+
const external_refs = ext ? [ext] : [];
|
|
5923
|
+
const skillNames = Object.keys(entry.skills ?? {});
|
|
5924
|
+
const brainstorm_warmup = skillNames.some((s) => /brainstorm|writing-plans/i.test(s));
|
|
5925
|
+
const fuTrim = fu.trim().toLowerCase();
|
|
5926
|
+
const frames = detectPromptFrames(fu);
|
|
5927
|
+
const continuation_kind = /^continue\b/.test(fuTrim) || fuTrim === "continue." ? "literal-continue" : frames.includes("handoff-prose") ? "handoff-prose" : "none";
|
|
5928
|
+
return {
|
|
5929
|
+
working_shape: inferWorkingShape(entry),
|
|
5930
|
+
prompt_frames: frames,
|
|
5931
|
+
subagent_roles: (entry.subagents ?? []).map(classifySubagentRole),
|
|
5932
|
+
verbosity,
|
|
5933
|
+
external_refs,
|
|
5934
|
+
brainstorm_warmup,
|
|
5935
|
+
continuation_kind
|
|
5878
5936
|
};
|
|
5879
5937
|
}
|
|
5880
5938
|
|
|
5881
|
-
//
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
import { dirname as dirname4 } from "node:path";
|
|
5885
|
-
var QUEUE_PATH = cclensPath("ingest-queue.jsonl");
|
|
5886
|
-
var MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
5887
|
-
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
5888
|
-
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
5889
|
-
mkdirSync5(dirname4(queuePath), { recursive: true });
|
|
5890
|
-
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5891
|
-
appendFileSync2(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
5892
|
-
try {
|
|
5893
|
-
const { size } = statSync2(queuePath);
|
|
5894
|
-
if (size > MAX_SIZE_BYTES) {
|
|
5895
|
-
pruneOldest(queuePath);
|
|
5896
|
-
}
|
|
5897
|
-
} catch {
|
|
5898
|
-
}
|
|
5939
|
+
// ../entries/dist/trivial.js
|
|
5940
|
+
function isTrivial(n) {
|
|
5941
|
+
return n.active_min < 1 && n.turn_count < 3 && n.tools_total === 0;
|
|
5899
5942
|
}
|
|
5900
|
-
function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
5901
|
-
let lines;
|
|
5902
|
-
try {
|
|
5903
|
-
lines = readFileSync7(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
5904
|
-
} catch {
|
|
5905
|
-
return [];
|
|
5906
|
-
}
|
|
5907
|
-
const now = Date.now();
|
|
5908
|
-
const valid = [];
|
|
5909
|
-
for (const line of lines) {
|
|
5910
|
-
try {
|
|
5911
|
-
const entry = JSON.parse(line);
|
|
5912
|
-
if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
|
|
5913
|
-
valid.push(entry);
|
|
5914
|
-
}
|
|
5915
|
-
} catch {
|
|
5916
|
-
}
|
|
5917
|
-
}
|
|
5918
|
-
writeFileSync4(queuePath, "", "utf8");
|
|
5919
|
-
return valid.map((e) => e.payload);
|
|
5920
|
-
}
|
|
5921
|
-
function pruneOldest(queuePath) {
|
|
5922
|
-
let lines;
|
|
5923
|
-
try {
|
|
5924
|
-
lines = readFileSync7(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
5925
|
-
} catch {
|
|
5926
|
-
return;
|
|
5927
|
-
}
|
|
5928
|
-
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
5929
|
-
writeFileSync4(queuePath, keep.join("\n") + "\n", "utf8");
|
|
5930
|
-
}
|
|
5931
|
-
|
|
5932
|
-
// src/team/backfill.ts
|
|
5933
|
-
init_storage();
|
|
5934
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
5935
5943
|
|
|
5936
|
-
//
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
case "default_claude_max_20x":
|
|
5950
|
-
return "pro-max-20x";
|
|
5951
|
-
default:
|
|
5952
|
-
return "custom";
|
|
5953
|
-
}
|
|
5954
|
-
}
|
|
5955
|
-
async function fetchProfile(nowMs = Date.now()) {
|
|
5956
|
-
const creds = readOAuthCredentials();
|
|
5957
|
-
if (!creds) return null;
|
|
5958
|
-
if (creds.expiresAt - 6e4 <= nowMs) return null;
|
|
5959
|
-
let res;
|
|
5960
|
-
try {
|
|
5961
|
-
res = await fetch(PROFILE_ENDPOINT, {
|
|
5962
|
-
headers: {
|
|
5963
|
-
Authorization: `Bearer ${creds.accessToken}`,
|
|
5964
|
-
"anthropic-beta": BETA_HEADER2
|
|
5965
|
-
}
|
|
5966
|
-
});
|
|
5967
|
-
} catch {
|
|
5968
|
-
return null;
|
|
5969
|
-
}
|
|
5970
|
-
if (!res.ok) return null;
|
|
5971
|
-
const body = await res.json().catch(() => null);
|
|
5972
|
-
if (!body) return null;
|
|
5973
|
-
const rateLimitTier = body.organization?.rate_limit_tier ?? null;
|
|
5944
|
+
// ../entries/dist/build.js
|
|
5945
|
+
init_analytics();
|
|
5946
|
+
function viewEvent(ev) {
|
|
5947
|
+
let rawType;
|
|
5948
|
+
if (ev.role === "user" || ev.role === "tool-result")
|
|
5949
|
+
rawType = "user";
|
|
5950
|
+
else if (ev.role === "agent" || ev.role === "agent-thinking" || ev.role === "tool-call")
|
|
5951
|
+
rawType = "assistant";
|
|
5952
|
+
else if (ev.rawType === "summary")
|
|
5953
|
+
rawType = "summary";
|
|
5954
|
+
else
|
|
5955
|
+
rawType = "system";
|
|
5956
|
+
const rawCwd = ev.raw?.cwd;
|
|
5974
5957
|
return {
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5958
|
+
rawType,
|
|
5959
|
+
content: ev.blocks ?? [],
|
|
5960
|
+
model: ev.model,
|
|
5961
|
+
msgId: ev.messageId,
|
|
5962
|
+
usage: ev.usage ? {
|
|
5963
|
+
input_tokens: ev.usage.input,
|
|
5964
|
+
output_tokens: ev.usage.output,
|
|
5965
|
+
cache_read_input_tokens: ev.usage.cacheRead,
|
|
5966
|
+
cache_creation_input_tokens: ev.usage.cacheWrite
|
|
5967
|
+
} : void 0,
|
|
5968
|
+
cwd: rawCwd
|
|
5978
5969
|
};
|
|
5979
5970
|
}
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5971
|
+
var IDLE_GAP_MS4 = 3 * 60 * 1e3;
|
|
5972
|
+
var INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
|
|
5973
|
+
var PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
|
|
5974
|
+
var BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
|
|
5975
|
+
var SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
|
|
5976
|
+
var IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
|
|
5977
|
+
function parseMs(iso) {
|
|
5978
|
+
if (!iso)
|
|
5979
|
+
return void 0;
|
|
5980
|
+
const ms = Date.parse(iso);
|
|
5981
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
5989
5982
|
}
|
|
5990
|
-
function
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
{ mode: 384 }
|
|
5996
|
-
);
|
|
5983
|
+
function trunc(s, n) {
|
|
5984
|
+
if (!s)
|
|
5985
|
+
return "";
|
|
5986
|
+
const one = s.replace(/\s+/g, " ").trim();
|
|
5987
|
+
return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
|
|
5997
5988
|
}
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
5989
|
+
function minimizeImages(s) {
|
|
5990
|
+
if (!s)
|
|
5991
|
+
return s;
|
|
5992
|
+
let counter = 0;
|
|
5993
|
+
return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
|
|
5994
|
+
}
|
|
5995
|
+
function bashVerb(cmd) {
|
|
5996
|
+
if (!cmd)
|
|
5997
|
+
return "?";
|
|
5998
|
+
let firstSeg = cmd.split(BASH_CHAIN_SPLIT_RE, 1)[0].trim();
|
|
5999
|
+
if (firstSeg.startsWith("cd ") || firstSeg === "cd") {
|
|
6000
|
+
const parts = cmd.split(/&&|;/, 3);
|
|
6001
|
+
if (parts.length >= 2)
|
|
6002
|
+
firstSeg = parts[1].trim();
|
|
6002
6003
|
}
|
|
6003
|
-
const
|
|
6004
|
-
|
|
6005
|
-
|
|
6004
|
+
const tokens = firstSeg.split(/\s+/).filter(Boolean);
|
|
6005
|
+
while (tokens.length && SHELL_NOISE.has(tokens[0]))
|
|
6006
|
+
tokens.shift();
|
|
6007
|
+
if (!tokens.length)
|
|
6008
|
+
return "?";
|
|
6009
|
+
let first = tokens[0];
|
|
6010
|
+
if (first.includes("=") && !first.startsWith("/")) {
|
|
6011
|
+
tokens.shift();
|
|
6012
|
+
if (!tokens.length)
|
|
6013
|
+
return "?";
|
|
6014
|
+
first = tokens[0];
|
|
6006
6015
|
}
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
}
|
|
6010
|
-
|
|
6011
|
-
// src/team/backfill.ts
|
|
6012
|
-
init_fs();
|
|
6013
|
-
var USAGE_LOG = cclensPath("usage.jsonl");
|
|
6014
|
-
var PROFILE_CACHE = cclensPath("profile.json");
|
|
6015
|
-
var BATCH_SIZE = 500;
|
|
6016
|
-
var noopLog = () => {
|
|
6017
|
-
};
|
|
6018
|
-
function rawToWire(raw) {
|
|
6019
|
-
const win = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
6020
|
-
return {
|
|
6021
|
-
capturedAt: raw.captured_at,
|
|
6022
|
-
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
6023
|
-
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
6024
|
-
sevenDayOpus: win(raw.seven_day_opus),
|
|
6025
|
-
sevenDaySonnet: win(raw.seven_day_sonnet),
|
|
6026
|
-
sevenDayOauthApps: win(raw.seven_day_oauth_apps),
|
|
6027
|
-
sevenDayCowork: win(raw.seven_day_cowork),
|
|
6028
|
-
extraUsage: raw.extra_usage ? {
|
|
6029
|
-
isEnabled: raw.extra_usage.is_enabled,
|
|
6030
|
-
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
6031
|
-
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
6032
|
-
utilization: raw.extra_usage.utilization
|
|
6033
|
-
} : null
|
|
6034
|
-
};
|
|
6035
|
-
}
|
|
6036
|
-
function chunk(items, size) {
|
|
6037
|
-
if (size <= 0) return [items];
|
|
6038
|
-
const out = [];
|
|
6039
|
-
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
6040
|
-
return out;
|
|
6041
|
-
}
|
|
6042
|
-
async function postBatch(config, snapshots, planTier) {
|
|
6043
|
-
const payload = {
|
|
6044
|
-
ingestId: `backfill-${randomUUID2()}`,
|
|
6045
|
-
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6046
|
-
snapshotHistory: snapshots,
|
|
6047
|
-
...planTier ? { planTier } : {}
|
|
6048
|
-
};
|
|
6049
|
-
const result = await pushToTeamServer(config, payload);
|
|
6050
|
-
if (!result.ok) {
|
|
6051
|
-
return { inserted: 0, skipped: snapshots.length, status: result.status };
|
|
6016
|
+
if (first.includes("/") && !first.startsWith("./")) {
|
|
6017
|
+
first = first.replace(/\/+$/, "").split("/").pop();
|
|
6052
6018
|
}
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6019
|
+
return first.replace(/^["']|["']$/g, "") || "?";
|
|
6020
|
+
}
|
|
6021
|
+
function firstTextBlock(blocks) {
|
|
6022
|
+
if (!Array.isArray(blocks))
|
|
6023
|
+
return "";
|
|
6024
|
+
for (const b of blocks) {
|
|
6025
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
6026
|
+
return String(b.text ?? "");
|
|
6027
|
+
}
|
|
6058
6028
|
}
|
|
6059
|
-
return
|
|
6060
|
-
inserted: body.snapshotHistory.inserted ?? 0,
|
|
6061
|
-
skipped: body.snapshotHistory.skipped ?? 0,
|
|
6062
|
-
status: result.status
|
|
6063
|
-
};
|
|
6029
|
+
return "";
|
|
6064
6030
|
}
|
|
6065
|
-
function
|
|
6066
|
-
if (!
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
const capturedMs = Date.parse(snapshot.captured_at);
|
|
6070
|
-
return !Number.isNaN(capturedMs) && capturedMs > sinceMs;
|
|
6031
|
+
function blockText(blocks) {
|
|
6032
|
+
if (!blocks)
|
|
6033
|
+
return "";
|
|
6034
|
+
return blocks.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(" ");
|
|
6071
6035
|
}
|
|
6072
|
-
function
|
|
6073
|
-
const
|
|
6074
|
-
|
|
6036
|
+
function groupEventsByLocalDay(events) {
|
|
6037
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
6038
|
+
for (const ev of events) {
|
|
6039
|
+
if (!ev.timestamp)
|
|
6040
|
+
continue;
|
|
6041
|
+
const ms = Date.parse(ev.timestamp);
|
|
6042
|
+
if (Number.isNaN(ms))
|
|
6043
|
+
continue;
|
|
6044
|
+
const day = toLocalDay(ms);
|
|
6045
|
+
let bucket = byDay.get(day);
|
|
6046
|
+
if (!bucket) {
|
|
6047
|
+
bucket = [];
|
|
6048
|
+
byDay.set(day, bucket);
|
|
6049
|
+
}
|
|
6050
|
+
bucket.push(ev);
|
|
6051
|
+
}
|
|
6052
|
+
return byDay;
|
|
6075
6053
|
}
|
|
6076
|
-
function
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6054
|
+
function newTurn(startMs, userInput = "") {
|
|
6055
|
+
return {
|
|
6056
|
+
startMs,
|
|
6057
|
+
lastMs: startMs,
|
|
6058
|
+
eventTs: startMs !== void 0 ? [startMs] : [],
|
|
6059
|
+
userInput,
|
|
6060
|
+
toolsTotal: 0,
|
|
6061
|
+
toolMix: /* @__PURE__ */ new Map(),
|
|
6062
|
+
bashVerbs: /* @__PURE__ */ new Map(),
|
|
6063
|
+
skills: /* @__PURE__ */ new Map(),
|
|
6064
|
+
subagents: [],
|
|
6065
|
+
taskOps: 0,
|
|
6066
|
+
toolErrors: 0,
|
|
6067
|
+
consecSameToolMax: 0,
|
|
6068
|
+
consec: 0,
|
|
6069
|
+
endedInInterrupt: false,
|
|
6070
|
+
models: /* @__PURE__ */ new Map(),
|
|
6071
|
+
tokensIn: 0,
|
|
6072
|
+
tokensOut: 0,
|
|
6073
|
+
tokensCacheR: 0,
|
|
6074
|
+
tokensCacheW: 0,
|
|
6075
|
+
seenMsgIds: /* @__PURE__ */ new Set()
|
|
6076
|
+
};
|
|
6084
6077
|
}
|
|
6085
|
-
|
|
6086
|
-
const
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
(s) => (!s.agent || s.agent === "claude-code") && isAfterSince(s, options.sinceCapturedAt)
|
|
6093
|
-
).sort((a, b) => capturedAtMs(a) - capturedAtMs(b));
|
|
6094
|
-
if (claudeOnly.length === 0) {
|
|
6095
|
-
log3("info", "team backfill: no usage snapshots to send");
|
|
6096
|
-
return { paired: true, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
6078
|
+
function closeTurn(t, closed, cwd) {
|
|
6079
|
+
const ts = [...t.eventTs].sort((a, b) => a - b);
|
|
6080
|
+
let active = 0;
|
|
6081
|
+
for (let i = 1; i < ts.length; i++) {
|
|
6082
|
+
const dt = ts[i] - ts[i - 1];
|
|
6083
|
+
if (dt < IDLE_GAP_MS4)
|
|
6084
|
+
active += dt;
|
|
6097
6085
|
}
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6086
|
+
closed.push({
|
|
6087
|
+
startMs: t.startMs ?? 0,
|
|
6088
|
+
activeMs: active,
|
|
6089
|
+
userInput: t.userInput,
|
|
6090
|
+
firstAgentText: t.firstAgentText,
|
|
6091
|
+
lastAgentText: t.lastAgentText,
|
|
6092
|
+
toolsTotal: t.toolsTotal,
|
|
6093
|
+
toolMix: t.toolMix,
|
|
6094
|
+
bashVerbs: t.bashVerbs,
|
|
6095
|
+
skills: t.skills,
|
|
6096
|
+
subagents: t.subagents,
|
|
6097
|
+
taskOps: t.taskOps,
|
|
6098
|
+
toolErrors: t.toolErrors,
|
|
6099
|
+
consecSameToolMax: t.consecSameToolMax,
|
|
6100
|
+
endedInInterrupt: t.endedInInterrupt,
|
|
6101
|
+
models: t.models,
|
|
6102
|
+
totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
|
|
6103
|
+
cwd
|
|
6104
|
+
});
|
|
6105
|
+
}
|
|
6106
|
+
function aggregateDay(dayEvents, _sessionFallbackProject) {
|
|
6107
|
+
const prTitles = [];
|
|
6108
|
+
let commits = 0;
|
|
6109
|
+
let pushes = 0;
|
|
6110
|
+
const gitToolUseIds = /* @__PURE__ */ new Set();
|
|
6111
|
+
const githubRepos = /* @__PURE__ */ new Set();
|
|
6112
|
+
const editedDirCounts = /* @__PURE__ */ new Map();
|
|
6113
|
+
const modelTurns = /* @__PURE__ */ new Map();
|
|
6114
|
+
let totalToolErrors = 0;
|
|
6115
|
+
let totalInterrupts = 0;
|
|
6116
|
+
let exitPlanCalls = 0;
|
|
6117
|
+
let taskOpsTotal = 0;
|
|
6118
|
+
let firstUser;
|
|
6119
|
+
let finalAgent;
|
|
6120
|
+
const cwdCounts = /* @__PURE__ */ new Map();
|
|
6121
|
+
const closed = [];
|
|
6122
|
+
let cur;
|
|
6123
|
+
const allTs = [];
|
|
6124
|
+
let lastEventCwd;
|
|
6125
|
+
for (const ev of dayEvents) {
|
|
6126
|
+
const tsMs = parseMs(ev.timestamp);
|
|
6127
|
+
if (tsMs !== void 0)
|
|
6128
|
+
allTs.push(tsMs);
|
|
6129
|
+
const view = viewEvent(ev);
|
|
6130
|
+
const rawType = view.rawType;
|
|
6131
|
+
const msg = { content: view.content, id: view.msgId, model: view.model, usage: view.usage };
|
|
6132
|
+
const content = view.content;
|
|
6133
|
+
const eventCwd = view.cwd;
|
|
6134
|
+
if (eventCwd)
|
|
6135
|
+
lastEventCwd = eventCwd;
|
|
6136
|
+
if (rawType === "user") {
|
|
6137
|
+
const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
6138
|
+
if (!isToolResultOnly) {
|
|
6139
|
+
const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
|
|
6140
|
+
if (cur) {
|
|
6141
|
+
if (text && INTERRUPT_RE2.test(text)) {
|
|
6142
|
+
cur.endedInInterrupt = true;
|
|
6143
|
+
totalInterrupts++;
|
|
6144
|
+
}
|
|
6145
|
+
closeTurn(cur, closed, eventCwd);
|
|
6146
|
+
}
|
|
6147
|
+
cur = newTurn(tsMs, text);
|
|
6148
|
+
if (!firstUser && text && classifyUserInputSource(text) === "human") {
|
|
6149
|
+
firstUser = text;
|
|
6150
|
+
}
|
|
6151
|
+
} else {
|
|
6152
|
+
if (Array.isArray(content)) {
|
|
6153
|
+
for (const c of content) {
|
|
6154
|
+
if (!c || typeof c !== "object" || c.type !== "tool_result")
|
|
6155
|
+
continue;
|
|
6156
|
+
if (c.is_error) {
|
|
6157
|
+
totalToolErrors++;
|
|
6158
|
+
if (cur)
|
|
6159
|
+
cur.toolErrors++;
|
|
6160
|
+
}
|
|
6161
|
+
const tuId = c.tool_use_id;
|
|
6162
|
+
if (tuId && gitToolUseIds.has(tuId)) {
|
|
6163
|
+
const rc = c.content;
|
|
6164
|
+
const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
|
|
6165
|
+
for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
|
|
6166
|
+
if (m[1].toLowerCase() === "user-attachments")
|
|
6167
|
+
continue;
|
|
6168
|
+
githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
}
|
|
6172
|
+
}
|
|
6173
|
+
if (cur && tsMs !== void 0) {
|
|
6174
|
+
cur.eventTs.push(tsMs);
|
|
6175
|
+
cur.lastMs = tsMs;
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
} else if (rawType === "assistant") {
|
|
6179
|
+
if (cur && tsMs !== void 0) {
|
|
6180
|
+
cur.eventTs.push(tsMs);
|
|
6181
|
+
cur.lastMs = tsMs;
|
|
6182
|
+
}
|
|
6183
|
+
const model = msg.model;
|
|
6184
|
+
if (model) {
|
|
6185
|
+
modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
|
|
6186
|
+
if (cur)
|
|
6187
|
+
cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
|
|
6188
|
+
}
|
|
6189
|
+
const msgId = msg.id;
|
|
6190
|
+
const usage = msg.usage;
|
|
6191
|
+
if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
|
|
6192
|
+
cur.seenMsgIds.add(msgId);
|
|
6193
|
+
cur.tokensIn += usage.input_tokens ?? 0;
|
|
6194
|
+
cur.tokensOut += usage.output_tokens ?? 0;
|
|
6195
|
+
cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
|
|
6196
|
+
cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
|
|
6197
|
+
}
|
|
6198
|
+
if (Array.isArray(content)) {
|
|
6199
|
+
for (const c of content) {
|
|
6200
|
+
if (!c || typeof c !== "object")
|
|
6201
|
+
continue;
|
|
6202
|
+
const ct = c;
|
|
6203
|
+
if (ct.type === "text" && ct.text) {
|
|
6204
|
+
finalAgent = ct.text;
|
|
6205
|
+
if (cur) {
|
|
6206
|
+
if (cur.firstAgentText === void 0)
|
|
6207
|
+
cur.firstAgentText = ct.text;
|
|
6208
|
+
cur.lastAgentText = ct.text;
|
|
6209
|
+
}
|
|
6210
|
+
} else if (ct.type === "tool_use") {
|
|
6211
|
+
const name = ct.name ?? "unknown";
|
|
6212
|
+
if (cur) {
|
|
6213
|
+
cur.toolsTotal++;
|
|
6214
|
+
cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
|
|
6215
|
+
if (name === cur.lastTool)
|
|
6216
|
+
cur.consec++;
|
|
6217
|
+
else {
|
|
6218
|
+
cur.lastTool = name;
|
|
6219
|
+
cur.consec = 1;
|
|
6220
|
+
}
|
|
6221
|
+
if (cur.consec > cur.consecSameToolMax)
|
|
6222
|
+
cur.consecSameToolMax = cur.consec;
|
|
6223
|
+
if (name === "Skill") {
|
|
6224
|
+
const sk = String(ct.input?.skill ?? "unknown");
|
|
6225
|
+
cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
|
|
6226
|
+
}
|
|
6227
|
+
if (name === "Agent") {
|
|
6228
|
+
const inp = ct.input ?? {};
|
|
6229
|
+
cur.subagents.push({
|
|
6230
|
+
type: String(inp.subagent_type ?? "general-purpose"),
|
|
6231
|
+
description: trunc(String(inp.description ?? ""), 80),
|
|
6232
|
+
prompt_preview: trunc(String(inp.prompt ?? ""), 240),
|
|
6233
|
+
background: Boolean(inp.run_in_background)
|
|
6234
|
+
});
|
|
6235
|
+
}
|
|
6236
|
+
if (name === "ExitPlanMode")
|
|
6237
|
+
exitPlanCalls++;
|
|
6238
|
+
if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
|
|
6239
|
+
cur.taskOps++;
|
|
6240
|
+
taskOpsTotal++;
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
6243
|
+
if (name === "Bash") {
|
|
6244
|
+
const cmd = String(ct.input?.command ?? "");
|
|
6245
|
+
if (cur)
|
|
6246
|
+
cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
|
|
6247
|
+
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
6248
|
+
const m = cmd.match(PR_TITLE_RE);
|
|
6249
|
+
prTitles.push(m?.[1] ?? cmd.slice(0, 120));
|
|
6250
|
+
}
|
|
6251
|
+
if (/\bgit\s+commit\b/.test(cmd))
|
|
6252
|
+
commits++;
|
|
6253
|
+
if (/\bgit\s+push\b/.test(cmd))
|
|
6254
|
+
pushes++;
|
|
6255
|
+
if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
|
|
6256
|
+
gitToolUseIds.add(ct.id);
|
|
6257
|
+
}
|
|
6258
|
+
if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
|
|
6259
|
+
cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
|
|
6260
|
+
}
|
|
6261
|
+
if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
|
|
6262
|
+
const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
|
|
6263
|
+
const slash = fp.lastIndexOf("/");
|
|
6264
|
+
if (slash > 0) {
|
|
6265
|
+
const dir = fp.slice(0, slash);
|
|
6266
|
+
editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
|
|
6267
|
+
}
|
|
6268
|
+
}
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
6119
6271
|
}
|
|
6120
|
-
inserted += result.inserted;
|
|
6121
|
-
skipped += result.skipped;
|
|
6122
|
-
lastSnapshotAt = latestCapturedAt(batch) ?? lastSnapshotAt;
|
|
6123
|
-
} catch (err) {
|
|
6124
|
-
log3("warn", `team backfill: batch ${i + 1}/${batches.length} error: ${err.message}`);
|
|
6125
|
-
return {
|
|
6126
|
-
paired: true,
|
|
6127
|
-
sentSnapshots: wire.length,
|
|
6128
|
-
insertedSnapshots: inserted,
|
|
6129
|
-
skippedSnapshots: skipped,
|
|
6130
|
-
batches: i,
|
|
6131
|
-
lastSnapshotAt,
|
|
6132
|
-
error: err.message
|
|
6133
|
-
};
|
|
6134
6272
|
}
|
|
6135
6273
|
}
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6274
|
+
if (cur)
|
|
6275
|
+
closeTurn(cur, closed, lastEventCwd);
|
|
6276
|
+
const tsSorted = [...allTs].sort((a, b) => a - b);
|
|
6277
|
+
let activeMs2 = 0;
|
|
6278
|
+
for (let i = 1; i < tsSorted.length; i++) {
|
|
6279
|
+
const dt = tsSorted[i] - tsSorted[i - 1];
|
|
6280
|
+
if (dt < IDLE_GAP_MS4)
|
|
6281
|
+
activeMs2 += dt;
|
|
6282
|
+
}
|
|
6283
|
+
const tsSortedStrings = dayEvents.map((e) => e.timestamp).filter((t) => !!t).sort();
|
|
6284
|
+
const start_iso = tsSortedStrings[0] ?? "";
|
|
6285
|
+
const end_iso = tsSortedStrings[tsSortedStrings.length - 1] ?? "";
|
|
6140
6286
|
return {
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6287
|
+
closed,
|
|
6288
|
+
firstUser,
|
|
6289
|
+
finalAgent,
|
|
6290
|
+
prTitles,
|
|
6291
|
+
commits,
|
|
6292
|
+
pushes,
|
|
6293
|
+
githubRepos: [...githubRepos].slice(0, 5),
|
|
6294
|
+
editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
|
|
6295
|
+
modelTurns,
|
|
6296
|
+
totalToolErrors,
|
|
6297
|
+
totalInterrupts,
|
|
6298
|
+
exitPlanCalls,
|
|
6299
|
+
taskOpsTotal,
|
|
6300
|
+
cwdCounts,
|
|
6301
|
+
start_iso,
|
|
6302
|
+
end_iso,
|
|
6303
|
+
activeMs: activeMs2
|
|
6147
6304
|
};
|
|
6148
6305
|
}
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6306
|
+
function buildTopTools(closed) {
|
|
6307
|
+
const toolMix = /* @__PURE__ */ new Map();
|
|
6308
|
+
const bashVerbsAll = /* @__PURE__ */ new Map();
|
|
6309
|
+
for (const t of closed) {
|
|
6310
|
+
for (const [k, v] of t.toolMix)
|
|
6311
|
+
toolMix.set(k, (toolMix.get(k) ?? 0) + v);
|
|
6312
|
+
for (const [k, v] of t.bashVerbs)
|
|
6313
|
+
bashVerbsAll.set(k, (bashVerbsAll.get(k) ?? 0) + v);
|
|
6314
|
+
}
|
|
6315
|
+
const top = [...toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
6316
|
+
return top.map(([name, count]) => {
|
|
6317
|
+
if (name === "Bash" && bashVerbsAll.size > 0) {
|
|
6318
|
+
const sub = [...bashVerbsAll.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([v, c]) => c > 1 ? `${v}\xD7${c}` : v).join(", ");
|
|
6319
|
+
return `Bash\xD7${count} (${sub})`;
|
|
6320
|
+
}
|
|
6321
|
+
return count > 1 ? `${name}\xD7${count}` : name;
|
|
6322
|
+
});
|
|
6162
6323
|
}
|
|
6163
|
-
function
|
|
6164
|
-
|
|
6324
|
+
function buildEntries(sessionDetail) {
|
|
6325
|
+
const byDay = groupEventsByLocalDay(sessionDetail.events);
|
|
6326
|
+
const entries = [];
|
|
6327
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6328
|
+
const sessionFallbackProject = canonicalProjectName(sessionDetail.projectName ?? "");
|
|
6329
|
+
for (const [local_day, dayEvents] of byDay) {
|
|
6330
|
+
if (dayEvents.length === 0)
|
|
6331
|
+
continue;
|
|
6332
|
+
const agg = aggregateDay(dayEvents, sessionFallbackProject);
|
|
6333
|
+
const { closed } = agg;
|
|
6334
|
+
const turnCount = closed.length;
|
|
6335
|
+
const toolsTotal = closed.reduce((s, t) => s + t.toolsTotal, 0);
|
|
6336
|
+
const tokensTotal = closed.reduce((s, t) => s + t.totalTokens, 0);
|
|
6337
|
+
const subagentCalls = closed.reduce((s, t) => s + t.subagents.length, 0);
|
|
6338
|
+
const skillsMap = /* @__PURE__ */ new Map();
|
|
6339
|
+
for (const t of closed) {
|
|
6340
|
+
for (const [k, v] of t.skills)
|
|
6341
|
+
skillsMap.set(k, (skillsMap.get(k) ?? 0) + v);
|
|
6342
|
+
}
|
|
6343
|
+
const skillCalls = [...skillsMap.values()].reduce((a, b) => a + b, 0);
|
|
6344
|
+
const taskOps = agg.taskOpsTotal;
|
|
6345
|
+
const interrupts = agg.totalInterrupts;
|
|
6346
|
+
const toolErrors = agg.totalToolErrors;
|
|
6347
|
+
const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
|
|
6348
|
+
const exitPlanCalls = agg.exitPlanCalls;
|
|
6349
|
+
const prs = agg.prTitles.length;
|
|
6350
|
+
const commits = agg.commits;
|
|
6351
|
+
const pushes = agg.pushes;
|
|
6352
|
+
const activeMin = Math.round(agg.activeMs / 6e3) / 10;
|
|
6353
|
+
const numbers = {
|
|
6354
|
+
active_min: activeMin,
|
|
6355
|
+
turn_count: turnCount,
|
|
6356
|
+
tools_total: toolsTotal,
|
|
6357
|
+
subagent_calls: subagentCalls,
|
|
6358
|
+
skill_calls: skillCalls,
|
|
6359
|
+
task_ops: taskOps,
|
|
6360
|
+
interrupts,
|
|
6361
|
+
tool_errors: toolErrors,
|
|
6362
|
+
consec_same_tool_max: consecMax,
|
|
6363
|
+
exit_plan_calls: exitPlanCalls,
|
|
6364
|
+
prs,
|
|
6365
|
+
commits,
|
|
6366
|
+
pushes,
|
|
6367
|
+
tokens_total: tokensTotal
|
|
6368
|
+
};
|
|
6369
|
+
const longestTurnActiveMs = Math.max(0, ...closed.map((t) => t.activeMs));
|
|
6370
|
+
const flags = [];
|
|
6371
|
+
if (interrupts >= 3)
|
|
6372
|
+
flags.push("interrupt_heavy");
|
|
6373
|
+
if (toolErrors >= 20)
|
|
6374
|
+
flags.push("high_errors");
|
|
6375
|
+
if (consecMax >= 8)
|
|
6376
|
+
flags.push("loop_suspected");
|
|
6377
|
+
if (agg.activeMs < 5 * 6e4 && prs >= 1)
|
|
6378
|
+
flags.push("fast_ship");
|
|
6379
|
+
if (exitPlanCalls > 0)
|
|
6380
|
+
flags.push("plan_used");
|
|
6381
|
+
const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
|
|
6382
|
+
if (subagentTurns >= 3)
|
|
6383
|
+
flags.push("orchestrated");
|
|
6384
|
+
if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
|
|
6385
|
+
flags.push("long_autonomous");
|
|
6386
|
+
const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
|
|
6387
|
+
const modelMix = Object.fromEntries(agg.modelTurns);
|
|
6388
|
+
const firstUserRaw = agg.firstUser ?? "";
|
|
6389
|
+
const first_user = trunc(minimizeImages(firstUserRaw), 400);
|
|
6390
|
+
const finalAgentRaw = agg.finalAgent ?? "";
|
|
6391
|
+
const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
|
|
6392
|
+
let project = sessionFallbackProject;
|
|
6393
|
+
if (agg.cwdCounts.size > 0) {
|
|
6394
|
+
const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
6395
|
+
project = canonicalProjectName(best[0]);
|
|
6396
|
+
}
|
|
6397
|
+
const top_tools = buildTopTools(closed);
|
|
6398
|
+
const skills = Object.fromEntries(skillsMap);
|
|
6399
|
+
const subagents = closed.flatMap((t) => t.subagents);
|
|
6400
|
+
const humanText = dayEvents.filter((ev) => {
|
|
6401
|
+
const v = viewEvent(ev);
|
|
6402
|
+
if (v.rawType !== "user")
|
|
6403
|
+
return false;
|
|
6404
|
+
const content = v.content;
|
|
6405
|
+
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
6406
|
+
if (isToolResultOnly)
|
|
6407
|
+
return false;
|
|
6408
|
+
const text = blockText(ev.blocks) || ev.preview || "";
|
|
6409
|
+
return classifyUserInputSource(text) === "human";
|
|
6410
|
+
}).map((ev) => blockText(ev.blocks) || ev.preview || "").join("\n");
|
|
6411
|
+
const satisfaction_signals = countSatisfactionSignals(humanText);
|
|
6412
|
+
const user_input_sources = {
|
|
6413
|
+
human: 0,
|
|
6414
|
+
teammate: 0,
|
|
6415
|
+
skill_load: 0,
|
|
6416
|
+
slash_command: 0,
|
|
6417
|
+
system_instruction: 0
|
|
6418
|
+
};
|
|
6419
|
+
for (const ev of dayEvents) {
|
|
6420
|
+
const v = viewEvent(ev);
|
|
6421
|
+
if (v.rawType !== "user")
|
|
6422
|
+
continue;
|
|
6423
|
+
const content = v.content;
|
|
6424
|
+
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
6425
|
+
if (isToolResultOnly)
|
|
6426
|
+
continue;
|
|
6427
|
+
const text = blockText(ev.blocks) || ev.preview || "";
|
|
6428
|
+
const src = classifyUserInputSource(text);
|
|
6429
|
+
user_input_sources[src]++;
|
|
6430
|
+
}
|
|
6431
|
+
const pendingUserInstructions = extractUserInstructions(humanText).slice(0, 5);
|
|
6432
|
+
const trivial = isTrivial(numbers);
|
|
6433
|
+
const entry = {
|
|
6434
|
+
version: CURRENT_ENTRY_SCHEMA_VERSION,
|
|
6435
|
+
agent: sessionDetail.agent,
|
|
6436
|
+
session_id: sessionDetail.id,
|
|
6437
|
+
local_day,
|
|
6438
|
+
project,
|
|
6439
|
+
start_iso: agg.start_iso,
|
|
6440
|
+
end_iso: agg.end_iso,
|
|
6441
|
+
numbers,
|
|
6442
|
+
flags,
|
|
6443
|
+
primary_model: primaryModel,
|
|
6444
|
+
model_mix: modelMix,
|
|
6445
|
+
first_user,
|
|
6446
|
+
final_agent,
|
|
6447
|
+
pr_titles: agg.prTitles,
|
|
6448
|
+
...agg.githubRepos.length ? { github_repos: agg.githubRepos } : {},
|
|
6449
|
+
...agg.editedDirs.length ? { edited_dirs: agg.editedDirs } : {},
|
|
6450
|
+
top_tools,
|
|
6451
|
+
skills,
|
|
6452
|
+
subagents,
|
|
6453
|
+
satisfaction_signals,
|
|
6454
|
+
user_input_sources,
|
|
6455
|
+
enrichment: trivial ? skippedTrivialEnrichment(generatedAt) : { ...pendingEnrichment(), user_instructions: pendingUserInstructions },
|
|
6456
|
+
generated_at: generatedAt,
|
|
6457
|
+
source_jsonl: sessionDetail.filePath ?? "",
|
|
6458
|
+
source_checkpoint: { byte_offset: 0, last_event_ts: agg.end_iso || null }
|
|
6459
|
+
};
|
|
6460
|
+
entry.signals = computeEntrySignals(entry);
|
|
6461
|
+
entries.push(entry);
|
|
6462
|
+
}
|
|
6463
|
+
entries.sort((a, b) => a.local_day.localeCompare(b.local_day));
|
|
6464
|
+
return entries;
|
|
6165
6465
|
}
|
|
6166
6466
|
|
|
6167
|
-
// src/
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
return runActivityBackfill(command, config, log3);
|
|
6172
|
-
default:
|
|
6173
|
-
return {
|
|
6174
|
-
id: command.id,
|
|
6175
|
-
ok: false,
|
|
6176
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6177
|
-
error: `Unknown command type: ${command.type}`
|
|
6178
|
-
};
|
|
6467
|
+
// src/perception/build-entries.ts
|
|
6468
|
+
function decodeProjectDirName(projectDir) {
|
|
6469
|
+
if (projectDir.startsWith("-")) {
|
|
6470
|
+
return "/" + projectDir.slice(1).replace(/-/g, "/");
|
|
6179
6471
|
}
|
|
6472
|
+
return projectDir.replace(/-/g, "/");
|
|
6180
6473
|
}
|
|
6181
|
-
|
|
6182
|
-
const
|
|
6183
|
-
const
|
|
6184
|
-
const
|
|
6185
|
-
const days = command.params.days;
|
|
6186
|
-
const todayMs = Date.now();
|
|
6187
|
-
const targetDayMs = todayMs - (days - 1) * 24 * 60 * 60 * 1e3;
|
|
6188
|
-
const targetDay = toLocalDay3(targetDayMs);
|
|
6189
|
-
log3("info", `command ${command.id}: backfill-activity from ${targetDay} (${days} days)`);
|
|
6190
|
-
let sessions;
|
|
6191
|
-
try {
|
|
6192
|
-
sessions = await listSessions2({ limit: 1e4 });
|
|
6193
|
-
} catch (err) {
|
|
6194
|
-
return {
|
|
6195
|
-
id: command.id,
|
|
6196
|
-
ok: false,
|
|
6197
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6198
|
-
error: `Failed to read sessions: ${err.message}`
|
|
6199
|
-
};
|
|
6200
|
-
}
|
|
6201
|
-
const rollups = buildRollupsForRange(sessions, targetDay);
|
|
6202
|
-
if (rollups.length === 0) {
|
|
6203
|
-
return {
|
|
6204
|
-
id: command.id,
|
|
6205
|
-
ok: true,
|
|
6206
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6207
|
-
summary: { pushed: 0, fromDay: targetDay }
|
|
6208
|
-
};
|
|
6209
|
-
}
|
|
6210
|
-
const privateProjects = new Set(config.privateProjects ?? []);
|
|
6211
|
-
const enrichmentOptIn = !!config.enrichmentOptIn;
|
|
6212
|
-
const resolveRepo = createRepoResolver();
|
|
6213
|
-
let pushed = 0;
|
|
6214
|
-
for (const rollup of rollups) {
|
|
6215
|
-
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
6216
|
-
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, privateProjects, enrichmentOptIn, resolveRepo);
|
|
6217
|
-
let artifactSignals = null;
|
|
6474
|
+
function buildEntriesForFile(filePath) {
|
|
6475
|
+
const stat = statSync(filePath);
|
|
6476
|
+
const raw = readFileSync6(filePath, "utf8");
|
|
6477
|
+
const rawLines = raw.split("\n").filter(Boolean).map((l) => {
|
|
6218
6478
|
try {
|
|
6219
|
-
|
|
6479
|
+
return JSON.parse(l);
|
|
6220
6480
|
} catch {
|
|
6481
|
+
return null;
|
|
6221
6482
|
}
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6483
|
+
}).filter((x) => x !== null);
|
|
6484
|
+
if (rawLines.length === 0) return null;
|
|
6485
|
+
const { meta, events } = parseTranscript(rawLines);
|
|
6486
|
+
const fileName = basename(filePath);
|
|
6487
|
+
const projectDir = basename(dirname3(filePath));
|
|
6488
|
+
const sessionId = fileName.replace(/\.jsonl$/, "");
|
|
6489
|
+
const sd = {
|
|
6490
|
+
...meta,
|
|
6491
|
+
id: sessionId,
|
|
6492
|
+
filePath,
|
|
6493
|
+
projectDir,
|
|
6494
|
+
projectName: meta.cwd ?? decodeProjectDirName(projectDir),
|
|
6495
|
+
events
|
|
6496
|
+
};
|
|
6497
|
+
const built = buildEntries(sd);
|
|
6498
|
+
for (const e of built) {
|
|
6499
|
+
e.source_checkpoint.byte_offset = stat.size;
|
|
6238
6500
|
}
|
|
6239
|
-
|
|
6501
|
+
return built;
|
|
6502
|
+
}
|
|
6503
|
+
|
|
6504
|
+
// src/team/push.ts
|
|
6505
|
+
init_storage();
|
|
6506
|
+
var SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
|
|
6507
|
+
var POST_TIMEOUT_MS = 15e3;
|
|
6508
|
+
function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
|
|
6509
|
+
const raw = latestClaudeCodeSnapshot(filePath);
|
|
6510
|
+
if (!raw) return null;
|
|
6511
|
+
const capturedMs = Date.parse(raw.captured_at);
|
|
6512
|
+
if (Number.isNaN(capturedMs)) return null;
|
|
6513
|
+
if (nowMs - capturedMs > SNAPSHOT_FRESHNESS_MS) return null;
|
|
6514
|
+
const toWire = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
6240
6515
|
return {
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6516
|
+
capturedAt: raw.captured_at,
|
|
6517
|
+
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
6518
|
+
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
6519
|
+
sevenDayOpus: toWire(raw.seven_day_opus),
|
|
6520
|
+
sevenDaySonnet: toWire(raw.seven_day_sonnet),
|
|
6521
|
+
sevenDayOauthApps: toWire(raw.seven_day_oauth_apps),
|
|
6522
|
+
sevenDayCowork: toWire(raw.seven_day_cowork),
|
|
6523
|
+
extraUsage: raw.extra_usage ? {
|
|
6524
|
+
isEnabled: raw.extra_usage.is_enabled,
|
|
6525
|
+
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
6526
|
+
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
6527
|
+
utilization: raw.extra_usage.utilization
|
|
6528
|
+
} : null
|
|
6245
6529
|
};
|
|
6246
6530
|
}
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
}
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
const
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6531
|
+
function bucketToRollup(b) {
|
|
6532
|
+
return {
|
|
6533
|
+
day: b.date,
|
|
6534
|
+
agentTimeMs: b.airTimeMs,
|
|
6535
|
+
sessions: b.sessions,
|
|
6536
|
+
toolCalls: b.toolCalls,
|
|
6537
|
+
turns: b.turns,
|
|
6538
|
+
tokens: { ...b.tokens }
|
|
6539
|
+
};
|
|
6540
|
+
}
|
|
6541
|
+
function buildRollupsForRange(sessions, sinceDay) {
|
|
6542
|
+
const startCounts = /* @__PURE__ */ new Map();
|
|
6543
|
+
for (const s of sessions) {
|
|
6544
|
+
const d = sessionDay(s);
|
|
6545
|
+
if (d) startCounts.set(d, (startCounts.get(d) ?? 0) + 1);
|
|
6546
|
+
}
|
|
6547
|
+
return dailyActivity(sessions).filter((b) => !sinceDay || b.date >= sinceDay).map((b) => ({ ...bucketToRollup(b), uniqueSessions: startCounts.get(b.date) ?? 0 }));
|
|
6548
|
+
}
|
|
6549
|
+
function dayBoundsMs(day) {
|
|
6550
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
6551
|
+
return {
|
|
6552
|
+
startMs: new Date(y, m - 1, d, 0, 0, 0, 0).getTime(),
|
|
6553
|
+
endMs: new Date(y, m - 1, d + 1, 0, 0, 0, 0).getTime()
|
|
6554
|
+
};
|
|
6555
|
+
}
|
|
6556
|
+
function clipSegmentsToDay(segments, bounds) {
|
|
6557
|
+
if (!segments || segments.length === 0) return [];
|
|
6558
|
+
const out = [];
|
|
6559
|
+
for (const seg of segments) {
|
|
6560
|
+
const s = Math.max(seg.startMs, bounds.startMs);
|
|
6561
|
+
const e = Math.min(seg.endMs, bounds.endMs);
|
|
6562
|
+
if (e > s) out.push({ startMs: s, endMs: e });
|
|
6563
|
+
}
|
|
6564
|
+
return out;
|
|
6565
|
+
}
|
|
6566
|
+
function sessionTouchesDay(s, day) {
|
|
6567
|
+
const bounds = dayBoundsMs(day);
|
|
6568
|
+
if (s.activeSegments && s.activeSegments.length > 0) {
|
|
6569
|
+
return s.activeSegments.some((seg) => seg.endMs > bounds.startMs && seg.startMs < bounds.endMs);
|
|
6570
|
+
}
|
|
6571
|
+
return sessionDay(s) === day;
|
|
6572
|
+
}
|
|
6573
|
+
function buildRichRollupBlocks(day, daySessions, entries, resolveRepo) {
|
|
6574
|
+
const bounds = dayBoundsMs(day);
|
|
6575
|
+
const clippedSessions = [];
|
|
6576
|
+
for (const s of daySessions) {
|
|
6577
|
+
const clipped = clipSegmentsToDay(s.activeSegments, bounds);
|
|
6578
|
+
if (clipped.length === 0) continue;
|
|
6579
|
+
clippedSessions.push({ ...s, activeSegments: clipped });
|
|
6580
|
+
}
|
|
6581
|
+
const bursts = computeBurstsFromSessions(clippedSessions);
|
|
6582
|
+
const stats = summarizeBursts(bursts);
|
|
6583
|
+
const repoForEntry = (e) => {
|
|
6584
|
+
if (resolveRepo) {
|
|
6585
|
+
const own = resolveRepo(e.project);
|
|
6586
|
+
if (own) return own;
|
|
6587
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6588
|
+
for (const d of e.edited_dirs ?? []) {
|
|
6589
|
+
const r = resolveRepo(d);
|
|
6590
|
+
if (r) counts.set(r, (counts.get(r) ?? 0) + 1);
|
|
6302
6591
|
}
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6592
|
+
const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
6593
|
+
if (dominant) return dominant;
|
|
6594
|
+
}
|
|
6595
|
+
return e.github_repos?.length === 1 ? e.github_repos[0] : null;
|
|
6596
|
+
};
|
|
6597
|
+
const sessionRepo = /* @__PURE__ */ new Map();
|
|
6598
|
+
for (const e of entries) {
|
|
6599
|
+
const r = repoForEntry(e);
|
|
6600
|
+
if (r) sessionRepo.set(e.session_id, r);
|
|
6601
|
+
}
|
|
6602
|
+
const projects = /* @__PURE__ */ new Map();
|
|
6603
|
+
for (const s of clippedSessions) {
|
|
6604
|
+
const canonical = canonicalProjectName(s.projectName);
|
|
6605
|
+
const name = projectRepoName(s.projectName);
|
|
6606
|
+
const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
|
|
6607
|
+
const key = repo ?? name;
|
|
6608
|
+
const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
|
|
6609
|
+
const cur = projects.get(key) ?? { project: name, agentTimeMs: 0, sessions: 0, ...repo ? { resolved: repo } : {} };
|
|
6610
|
+
cur.agentTimeMs += ms;
|
|
6611
|
+
cur.sessions += 1;
|
|
6612
|
+
projects.set(key, cur);
|
|
6613
|
+
}
|
|
6614
|
+
const projectRepos = /* @__PURE__ */ new Map();
|
|
6615
|
+
const workingShapes = /* @__PURE__ */ new Map();
|
|
6616
|
+
const skills = /* @__PURE__ */ new Map();
|
|
6617
|
+
const subagents = /* @__PURE__ */ new Map();
|
|
6618
|
+
let toolErrors = 0;
|
|
6619
|
+
let brainstorm = 0;
|
|
6620
|
+
let planMode = 0;
|
|
6621
|
+
let prs = 0;
|
|
6622
|
+
let commits = 0;
|
|
6623
|
+
let pushes = 0;
|
|
6624
|
+
let longCount = 0;
|
|
6625
|
+
let longTotalMin = 0;
|
|
6626
|
+
let longMaxSingleMin = 0;
|
|
6627
|
+
for (const e of entries) {
|
|
6628
|
+
const shape = e.signals?.working_shape ?? null;
|
|
6629
|
+
if (shape) {
|
|
6630
|
+
const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
|
|
6631
|
+
cur.sessions += 1;
|
|
6632
|
+
cur.agentTimeMs += Math.round(e.numbers.active_min * 6e4);
|
|
6633
|
+
workingShapes.set(shape, cur);
|
|
6309
6634
|
}
|
|
6310
|
-
const
|
|
6311
|
-
|
|
6312
|
-
const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG2) ?? void 0;
|
|
6313
|
-
const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
|
|
6314
|
-
const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
|
|
6315
|
-
if (rollups.length === 0) {
|
|
6316
|
-
const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
|
|
6317
|
-
if (!hasLiveData) {
|
|
6318
|
-
if (usageBackfill.sentSnapshots === 0) log3("info", "team push: nothing to sync");
|
|
6319
|
-
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
6320
|
-
}
|
|
6321
|
-
const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks });
|
|
6322
|
-
const result = await pushToTeamServer(config, payload);
|
|
6323
|
-
if (!result.ok) {
|
|
6324
|
-
const errLine = `team push failed (${result.status})`;
|
|
6325
|
-
log3("warn", `${errLine}; queueing`);
|
|
6326
|
-
writeLastPushFailure(payload, errLine);
|
|
6327
|
-
enqueuePayload(payload);
|
|
6328
|
-
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
|
|
6329
|
-
}
|
|
6330
|
-
writeLastPushSuccess(payload);
|
|
6331
|
-
collectCommands(result.body?.commands);
|
|
6332
|
-
let queuedDrained2 = 0;
|
|
6333
|
-
const backlog = dequeuePayloads();
|
|
6334
|
-
for (let i = 0; i < backlog.length; i++) {
|
|
6335
|
-
const qResult = await pushToTeamServer(config, backlog[i]);
|
|
6336
|
-
if (!qResult.ok) {
|
|
6337
|
-
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
6338
|
-
break;
|
|
6339
|
-
}
|
|
6340
|
-
collectCommands(qResult.body?.commands);
|
|
6341
|
-
queuedDrained2++;
|
|
6342
|
-
}
|
|
6343
|
-
log3("info", `team push ok: live-only (no new daily activity)` + (queuedDrained2 ? `, ${queuedDrained2} queued retried` : ""));
|
|
6344
|
-
await dispatchAndReport();
|
|
6345
|
-
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
|
|
6635
|
+
for (const [name, count] of Object.entries(e.skills)) {
|
|
6636
|
+
if (count > 0) skills.set(name, (skills.get(name) ?? 0) + 1);
|
|
6346
6637
|
}
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
let failedDay;
|
|
6350
|
-
let lastPushedDay;
|
|
6351
|
-
const privateProjects = new Set(config.privateProjects ?? []);
|
|
6352
|
-
const enrichmentOptIn = !!config.enrichmentOptIn;
|
|
6353
|
-
const resolveRepo = createRepoResolver();
|
|
6354
|
-
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
6355
|
-
for (let i = 0; i < rollups.length; i++) {
|
|
6356
|
-
const rollup = rollups[i];
|
|
6357
|
-
const isLatest = i === rollups.length - 1;
|
|
6358
|
-
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
6359
|
-
const richBlocks = buildRichBlocksForDay(
|
|
6360
|
-
rollup.day,
|
|
6361
|
-
daySessions,
|
|
6362
|
-
privateProjects,
|
|
6363
|
-
enrichmentOptIn,
|
|
6364
|
-
resolveRepo
|
|
6365
|
-
);
|
|
6366
|
-
let artifactSignals = null;
|
|
6367
|
-
try {
|
|
6368
|
-
artifactSignals = probeArtifactSignals2({
|
|
6369
|
-
day: rollup.day,
|
|
6370
|
-
extraRoots: [process.cwd()]
|
|
6371
|
-
});
|
|
6372
|
-
} catch {
|
|
6373
|
-
}
|
|
6374
|
-
const payload = buildIngestPayload({
|
|
6375
|
-
rollup,
|
|
6376
|
-
richExtras: richBlocks?.rich,
|
|
6377
|
-
enrichedExtras: richBlocks?.enriched,
|
|
6378
|
-
artifactSignals: artifactSignals ?? void 0,
|
|
6379
|
-
usageSnapshot: isLatest ? usageSnapshot : void 0,
|
|
6380
|
-
planTier,
|
|
6381
|
-
// Same rationale as usageSnapshot — current cycle data only on the
|
|
6382
|
-
// latest rollup so older days don't get tagged with today's peaks.
|
|
6383
|
-
cyclePeaks: isLatest ? cyclePeaks : void 0
|
|
6384
|
-
});
|
|
6385
|
-
const result = await pushToTeamServer(config, payload);
|
|
6386
|
-
if (!result.ok) {
|
|
6387
|
-
const errLine = `team push failed on ${rollup.day} (${result.status})`;
|
|
6388
|
-
log3("warn", `${errLine}; queueing`);
|
|
6389
|
-
writeLastPushFailure(payload, errLine);
|
|
6390
|
-
enqueuePayload(payload);
|
|
6391
|
-
queued++;
|
|
6392
|
-
failedDay = rollup.day;
|
|
6393
|
-
break;
|
|
6394
|
-
}
|
|
6395
|
-
writeLastPushSuccess(payload);
|
|
6396
|
-
collectCommands(result.body?.commands);
|
|
6397
|
-
pushed++;
|
|
6398
|
-
lastPushedDay = rollup.day;
|
|
6638
|
+
for (const sa of e.subagents) {
|
|
6639
|
+
subagents.set(sa.type, (subagents.get(sa.type) ?? 0) + 1);
|
|
6399
6640
|
}
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6641
|
+
toolErrors += e.numbers.tool_errors;
|
|
6642
|
+
if (e.signals?.brainstorm_warmup) brainstorm += 1;
|
|
6643
|
+
if (e.numbers.exit_plan_calls > 0) planMode += 1;
|
|
6644
|
+
prs += e.numbers.prs;
|
|
6645
|
+
commits += e.numbers.commits;
|
|
6646
|
+
pushes += e.numbers.pushes;
|
|
6647
|
+
if (e.github_repos?.length) {
|
|
6648
|
+
const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
|
|
6649
|
+
const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
|
|
6650
|
+
for (const r of e.github_repos) set.add(r);
|
|
6651
|
+
projectRepos.set(key, set);
|
|
6404
6652
|
}
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6653
|
+
if (e.flags.includes("long_autonomous")) {
|
|
6654
|
+
longCount += 1;
|
|
6655
|
+
longTotalMin += e.numbers.active_min;
|
|
6656
|
+
if (e.numbers.active_min > longMaxSingleMin) longMaxSingleMin = e.numbers.active_min;
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6659
|
+
return {
|
|
6660
|
+
projects: Array.from(projects.entries()).map(([key, v]) => {
|
|
6661
|
+
const extras = [...projectRepos.get(key) ?? []].filter((r) => r !== v.resolved);
|
|
6662
|
+
const repos = v.resolved ? [v.resolved, ...extras] : extras;
|
|
6663
|
+
return {
|
|
6664
|
+
project: v.project,
|
|
6665
|
+
agentTimeMs: v.agentTimeMs,
|
|
6666
|
+
sessions: v.sessions,
|
|
6667
|
+
...repos.length ? { githubRepos: repos.slice(0, 5) } : {}
|
|
6668
|
+
};
|
|
6669
|
+
}).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
|
|
6670
|
+
workingShapes: Array.from(workingShapes.entries()).map(([shape, v]) => ({ shape, ...v })).sort((a, b) => b.sessions - a.sessions),
|
|
6671
|
+
concurrencyPeak: stats.peakConcurrent,
|
|
6672
|
+
parallelMinutes: Math.round(stats.totalParallelMs / 6e4),
|
|
6673
|
+
// Wire schema requires integer minutes (active_min is a float).
|
|
6674
|
+
longAutonomous: { count: longCount, totalMin: Math.round(longTotalMin), maxSingleMin: Math.round(longMaxSingleMin) },
|
|
6675
|
+
toolErrors,
|
|
6676
|
+
skillsLoaded: Array.from(skills.entries()).map(([name, sessions]) => ({ name, sessions })).sort((a, b) => b.sessions - a.sessions),
|
|
6677
|
+
subagentsDispatched: Array.from(subagents.entries()).map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count),
|
|
6678
|
+
brainstormWarmupSessions: brainstorm,
|
|
6679
|
+
planModeUsed: planMode,
|
|
6680
|
+
prs,
|
|
6681
|
+
commits,
|
|
6682
|
+
pushes
|
|
6683
|
+
};
|
|
6684
|
+
}
|
|
6685
|
+
function buildEnrichedExtras(entries) {
|
|
6686
|
+
const outcomeMix = {};
|
|
6687
|
+
const helpfulnessMix = {};
|
|
6688
|
+
const goalMix = {};
|
|
6689
|
+
for (const e of entries) {
|
|
6690
|
+
if (e.enrichment.status !== "done") continue;
|
|
6691
|
+
if (e.enrichment.outcome) {
|
|
6692
|
+
outcomeMix[e.enrichment.outcome] = (outcomeMix[e.enrichment.outcome] ?? 0) + 1;
|
|
6417
6693
|
}
|
|
6418
|
-
if (
|
|
6419
|
-
|
|
6694
|
+
if (e.enrichment.claude_helpfulness) {
|
|
6695
|
+
helpfulnessMix[e.enrichment.claude_helpfulness] = (helpfulnessMix[e.enrichment.claude_helpfulness] ?? 0) + 1;
|
|
6696
|
+
}
|
|
6697
|
+
for (const [cat, mins] of Object.entries(e.enrichment.goal_categories)) {
|
|
6698
|
+
if (typeof mins === "number") goalMix[cat] = (goalMix[cat] ?? 0) + mins;
|
|
6420
6699
|
}
|
|
6421
|
-
await dispatchAndReport();
|
|
6422
|
-
return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
|
|
6423
|
-
} catch (err) {
|
|
6424
|
-
const message = err.message;
|
|
6425
|
-
log3("warn", `team push error: ${message}`);
|
|
6426
|
-
writeLastPushFailure(
|
|
6427
|
-
{ ingestId: "n/a", observedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
6428
|
-
`team push error: ${message}`
|
|
6429
|
-
);
|
|
6430
|
-
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
|
|
6431
6700
|
}
|
|
6701
|
+
return { outcomeMix, helpfulnessMix, goalMix };
|
|
6432
6702
|
}
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
const
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
const
|
|
6445
|
-
|
|
6446
|
-
const ms = Date.parse(k);
|
|
6447
|
-
if (Number.isNaN(ms)) continue;
|
|
6448
|
-
const bucket = Math.round(ms / HOUR) * HOUR;
|
|
6449
|
-
const arr = byCycle.get(bucket) ?? [];
|
|
6450
|
-
arr.push(p);
|
|
6451
|
-
byCycle.set(bucket, arr);
|
|
6703
|
+
var ensuredSessions = /* @__PURE__ */ new Set();
|
|
6704
|
+
function resetEnsuredSessions() {
|
|
6705
|
+
ensuredSessions.clear();
|
|
6706
|
+
}
|
|
6707
|
+
function ensureEntriesForDay(day, daySessions) {
|
|
6708
|
+
for (const s of daySessions) {
|
|
6709
|
+
if (ensuredSessions.has(s.id)) continue;
|
|
6710
|
+
if ((s.agent ?? "claude-code") !== "claude-code") continue;
|
|
6711
|
+
if (entryExists(s.id, day)) continue;
|
|
6712
|
+
ensuredSessions.add(s.id);
|
|
6713
|
+
try {
|
|
6714
|
+
for (const e of buildEntriesForFile(s.filePath) ?? []) writeEntryPreservingEnrichment(e);
|
|
6715
|
+
} catch {
|
|
6452
6716
|
}
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
6719
|
+
function buildRichBlocksForDay(day, daySessions, resolveRepo) {
|
|
6720
|
+
ensureEntriesForDay(day, daySessions);
|
|
6721
|
+
const entries = listEntriesForDay(day);
|
|
6722
|
+
if (entries.length === 0) return void 0;
|
|
6723
|
+
const rich = buildRichRollupBlocks(day, daySessions, entries, resolveRepo);
|
|
6724
|
+
const enriched = buildEnrichedExtras(entries);
|
|
6725
|
+
return { rich, enriched };
|
|
6726
|
+
}
|
|
6727
|
+
function buildIngestPayload(inputs) {
|
|
6728
|
+
const richRollup = inputs.rollup && inputs.richExtras ? { ...inputs.rollup, ...inputs.richExtras } : void 0;
|
|
6729
|
+
return {
|
|
6730
|
+
ingestId: randomUUID(),
|
|
6731
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6732
|
+
cliVersion: "0.14.0",
|
|
6733
|
+
...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
|
|
6734
|
+
...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
|
|
6735
|
+
...richRollup ? { richRollup } : {},
|
|
6736
|
+
...inputs.enrichedExtras ? { enrichedExtras: inputs.enrichedExtras } : {},
|
|
6737
|
+
...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
|
|
6738
|
+
...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
|
|
6739
|
+
...inputs.planTier ? { planTier: inputs.planTier } : {},
|
|
6740
|
+
...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {},
|
|
6741
|
+
...inputs.syncLog && inputs.syncLog.length ? { syncLog: inputs.syncLog } : {}
|
|
6742
|
+
};
|
|
6743
|
+
}
|
|
6744
|
+
async function pushToTeamServer(config, payload) {
|
|
6745
|
+
const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
|
|
6746
|
+
method: "POST",
|
|
6747
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
6748
|
+
headers: {
|
|
6749
|
+
"Content-Type": "application/json",
|
|
6750
|
+
Authorization: `Bearer ${config.bearerToken}`
|
|
6751
|
+
},
|
|
6752
|
+
body: JSON.stringify(payload)
|
|
6753
|
+
});
|
|
6754
|
+
const body = await res.json().catch(() => null);
|
|
6755
|
+
return { ok: res.ok, status: res.status, body };
|
|
6756
|
+
}
|
|
6757
|
+
|
|
6758
|
+
// src/team/git-remote.ts
|
|
6759
|
+
import { existsSync as existsSync4, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
|
|
6760
|
+
import { dirname as dirname4, isAbsolute, join as join5, resolve } from "node:path";
|
|
6761
|
+
var GITHUB_URL_RE = /github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?$/;
|
|
6762
|
+
function parseGithubRemote(url) {
|
|
6763
|
+
const m = url.trim().match(GITHUB_URL_RE);
|
|
6764
|
+
return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
|
|
6765
|
+
}
|
|
6766
|
+
function originUrl(configText) {
|
|
6767
|
+
let inOrigin = false;
|
|
6768
|
+
for (const line of configText.split("\n")) {
|
|
6769
|
+
const t = line.trim();
|
|
6770
|
+
if (t.startsWith("[")) {
|
|
6771
|
+
inOrigin = /^\[remote\s+"origin"\]/.test(t);
|
|
6772
|
+
continue;
|
|
6462
6773
|
}
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6774
|
+
if (!inOrigin) continue;
|
|
6775
|
+
const m = t.match(/^url\s*=\s*(.+)$/);
|
|
6776
|
+
if (m) return m[1].trim();
|
|
6777
|
+
}
|
|
6778
|
+
return null;
|
|
6779
|
+
}
|
|
6780
|
+
function gitConfigPathFor(dir) {
|
|
6781
|
+
const dotGit = join5(dir, ".git");
|
|
6782
|
+
const st = statSync2(dotGit, { throwIfNoEntry: false });
|
|
6783
|
+
if (!st) return null;
|
|
6784
|
+
if (st.isDirectory()) return join5(dotGit, "config");
|
|
6785
|
+
const m = readFileSync7(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
|
|
6786
|
+
if (!m) return null;
|
|
6787
|
+
const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
|
|
6788
|
+
const commondirFile = join5(gd, "commondir");
|
|
6789
|
+
if (existsSync4(commondirFile)) {
|
|
6790
|
+
const common = readFileSync7(commondirFile, "utf8").trim();
|
|
6791
|
+
return join5(isAbsolute(common) ? common : resolve(gd, common), "config");
|
|
6792
|
+
}
|
|
6793
|
+
return join5(gd, "config");
|
|
6794
|
+
}
|
|
6795
|
+
function createRepoResolver() {
|
|
6796
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6797
|
+
return (dir) => {
|
|
6798
|
+
if (!dir || !isAbsolute(dir)) return null;
|
|
6799
|
+
const hit = cache.get(dir);
|
|
6800
|
+
if (hit !== void 0) return hit;
|
|
6801
|
+
let repo = null;
|
|
6802
|
+
try {
|
|
6803
|
+
for (let d = dir; ; d = dirname4(d)) {
|
|
6804
|
+
const config = gitConfigPathFor(d);
|
|
6805
|
+
if (config && existsSync4(config)) {
|
|
6806
|
+
const url = originUrl(readFileSync7(config, "utf8"));
|
|
6807
|
+
repo = url ? parseGithubRemote(url) : null;
|
|
6808
|
+
break;
|
|
6477
6809
|
}
|
|
6810
|
+
if (d === dirname4(d)) break;
|
|
6478
6811
|
}
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
peakPct: Math.round(peak * 10) / 10,
|
|
6482
|
-
source,
|
|
6483
|
-
current: endMs > nowMs
|
|
6484
|
-
});
|
|
6812
|
+
} catch {
|
|
6813
|
+
repo = null;
|
|
6485
6814
|
}
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
return {
|
|
6489
|
-
fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
|
|
6490
|
-
sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 12)
|
|
6815
|
+
cache.set(dir, repo);
|
|
6816
|
+
return repo;
|
|
6491
6817
|
};
|
|
6492
6818
|
}
|
|
6493
6819
|
|
|
6494
|
-
// src/
|
|
6495
|
-
init_dist();
|
|
6820
|
+
// src/team/queue.ts
|
|
6496
6821
|
init_fs();
|
|
6497
|
-
import { statSync as
|
|
6498
|
-
import {
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
return "teammate";
|
|
6513
|
-
if (SKILL_LOAD_RE.test(stripped))
|
|
6514
|
-
return "skill_load";
|
|
6515
|
-
if (SLASH_COMMAND_RE2.test(stripped))
|
|
6516
|
-
return "slash_command";
|
|
6517
|
-
return "human";
|
|
6518
|
-
}
|
|
6519
|
-
var HAPPY_RE = /\b(?:yay|yaay|yass|woohoo|nice|great|love(?:ly)?|perfect|amazing|awesome)\s*!|!!!/gi;
|
|
6520
|
-
var SATISFIED_RE = /\b(?:thanks|thank you|looks good|lgtm|works|that works|all good|sounds good)\b/gi;
|
|
6521
|
-
var DISSATISFIED_RE = /\b(?:that'?s (?:not|wrong)|try again|no, (?:that|this)|not quite|incorrect)\b/gi;
|
|
6522
|
-
var FRUSTRATED_RE = /\b(?:broken|stop|why (?:did|are|would) you|give up|ugh|argh|wtf)\b/gi;
|
|
6523
|
-
function countSatisfactionSignals(text) {
|
|
6524
|
-
if (!text)
|
|
6525
|
-
return { happy: 0, satisfied: 0, dissatisfied: 0, frustrated: 0 };
|
|
6526
|
-
return {
|
|
6527
|
-
happy: (text.match(HAPPY_RE) ?? []).length,
|
|
6528
|
-
satisfied: (text.match(SATISFIED_RE) ?? []).length,
|
|
6529
|
-
dissatisfied: (text.match(DISSATISFIED_RE) ?? []).length,
|
|
6530
|
-
frustrated: (text.match(FRUSTRATED_RE) ?? []).length
|
|
6531
|
-
};
|
|
6532
|
-
}
|
|
6533
|
-
var INSTRUCTION_RE = /\b(?:can you|please|let's|let us|i need|could you|would you)\s+([^.?!\n]{5,200})/gi;
|
|
6534
|
-
function extractUserInstructions(text) {
|
|
6535
|
-
if (!text)
|
|
6536
|
-
return [];
|
|
6537
|
-
INSTRUCTION_RE.lastIndex = 0;
|
|
6538
|
-
const out = [];
|
|
6539
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6540
|
-
let m;
|
|
6541
|
-
while ((m = INSTRUCTION_RE.exec(text)) !== null) {
|
|
6542
|
-
const phrase = m[1].trim().replace(/\s+/g, " ");
|
|
6543
|
-
const lc = phrase.toLowerCase();
|
|
6544
|
-
if (seen.has(lc))
|
|
6545
|
-
continue;
|
|
6546
|
-
seen.add(lc);
|
|
6547
|
-
out.push(phrase);
|
|
6548
|
-
if (out.length >= 5)
|
|
6549
|
-
break;
|
|
6550
|
-
}
|
|
6551
|
-
return out;
|
|
6552
|
-
}
|
|
6553
|
-
function classifySubagentRole(sa) {
|
|
6554
|
-
const text = `${sa.description} ${sa.prompt_preview}`.toLowerCase();
|
|
6555
|
-
if (/\b(re-?review|review[s]?|verify|audit|spec[- ]review|code[- ]quality|code[- ]reuse|efficiency review|sanity check|quick test)\b/.test(text)) {
|
|
6556
|
-
return "reviewer";
|
|
6557
|
-
}
|
|
6558
|
-
if (/\b(implement|build chunk|build task|initialize|create the|add the)\b/.test(text)) {
|
|
6559
|
-
return "implementer";
|
|
6560
|
-
}
|
|
6561
|
-
if (/\bexplore\b|\binventory\b|\bmap\b.*\b(codebase|repo|branch)\b|\baudit\b.*coverage/.test(text)) {
|
|
6562
|
-
return "explorer";
|
|
6563
|
-
}
|
|
6564
|
-
if (/\b(investigate|research|reverse[- ]engineer|study|analy[sz]e|look up|tell me|brief me on|find out)\b/.test(text)) {
|
|
6565
|
-
return "researcher";
|
|
6566
|
-
}
|
|
6567
|
-
if (/\b(env(?:ironment)? setup|configure|setup|bootstrap)\b/.test(text)) {
|
|
6568
|
-
return "env-setup";
|
|
6569
|
-
}
|
|
6570
|
-
if (/\b(polish|cleanup|fix|refactor)\b/.test(text)) {
|
|
6571
|
-
return "polish";
|
|
6822
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, appendFileSync as appendFileSync3, statSync as statSync3, mkdirSync as mkdirSync5 } from "node:fs";
|
|
6823
|
+
import { dirname as dirname5 } from "node:path";
|
|
6824
|
+
var QUEUE_PATH = cclensPath("ingest-queue.jsonl");
|
|
6825
|
+
var MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
6826
|
+
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
6827
|
+
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
6828
|
+
mkdirSync5(dirname5(queuePath), { recursive: true });
|
|
6829
|
+
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6830
|
+
appendFileSync3(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
6831
|
+
try {
|
|
6832
|
+
const { size } = statSync3(queuePath);
|
|
6833
|
+
if (size > MAX_SIZE_BYTES) {
|
|
6834
|
+
pruneOldest(queuePath);
|
|
6835
|
+
}
|
|
6836
|
+
} catch {
|
|
6572
6837
|
}
|
|
6573
|
-
return "other";
|
|
6574
|
-
}
|
|
6575
|
-
var STOCK_SUBAGENT_PREFIXES = /^(general-purpose|Explore|Plan|claude-code-guide|playwright-qa-verifier|statusline-setup|frontend-design:|code-review:|code-simplifier:|codex:|superpowers:)/;
|
|
6576
|
-
function isStockSubagentType(type) {
|
|
6577
|
-
return STOCK_SUBAGENT_PREFIXES.test(type);
|
|
6578
6838
|
}
|
|
6579
|
-
function
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
const implementerCount = roles.filter((r) => r === "implementer").length;
|
|
6586
|
-
const explorerOrResearcher = roles.filter((r) => r === "explorer" || r === "researcher").length;
|
|
6587
|
-
const hasBackground = subagents.some((sa) => sa.background);
|
|
6588
|
-
if (implementerCount >= 2) {
|
|
6589
|
-
const chunkRefs = subagents.filter((sa) => /\b(chunk|task)\s*\d+/i.test(`${sa.description} ${sa.prompt_preview}`));
|
|
6590
|
-
if (chunkRefs.length >= 2)
|
|
6591
|
-
return "chunk-implementation";
|
|
6839
|
+
function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
6840
|
+
let lines;
|
|
6841
|
+
try {
|
|
6842
|
+
lines = readFileSync8(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
6843
|
+
} catch {
|
|
6844
|
+
return [];
|
|
6592
6845
|
}
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6846
|
+
const now = Date.now();
|
|
6847
|
+
const valid = [];
|
|
6848
|
+
for (const line of lines) {
|
|
6849
|
+
try {
|
|
6850
|
+
const entry = JSON.parse(line);
|
|
6851
|
+
if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
|
|
6852
|
+
valid.push(entry);
|
|
6853
|
+
}
|
|
6854
|
+
} catch {
|
|
6600
6855
|
}
|
|
6601
|
-
if (lensSigs.size >= 3)
|
|
6602
|
-
return "reviewer-triad";
|
|
6603
|
-
}
|
|
6604
|
-
if (reviewerCount >= 2)
|
|
6605
|
-
return "spec-review-loop";
|
|
6606
|
-
if (explorerOrResearcher >= 2 || explorerOrResearcher >= 1 && implementerCount >= 1) {
|
|
6607
|
-
return "research-then-build";
|
|
6608
6856
|
}
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
return "solo-build";
|
|
6857
|
+
writeFileSync4(queuePath, "", "utf8");
|
|
6858
|
+
return valid.map((e) => e.payload);
|
|
6859
|
+
}
|
|
6860
|
+
function pruneOldest(queuePath) {
|
|
6861
|
+
let lines;
|
|
6862
|
+
try {
|
|
6863
|
+
lines = readFileSync8(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
6864
|
+
} catch {
|
|
6865
|
+
return;
|
|
6619
6866
|
}
|
|
6620
|
-
|
|
6867
|
+
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
6868
|
+
writeFileSync4(queuePath, keep.join("\n") + "\n", "utf8");
|
|
6621
6869
|
}
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6870
|
+
|
|
6871
|
+
// src/team/backfill.ts
|
|
6872
|
+
init_storage();
|
|
6873
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
6874
|
+
|
|
6875
|
+
// src/usage/profile.ts
|
|
6876
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6877
|
+
import { dirname as dirname6 } from "node:path";
|
|
6878
|
+
var PROFILE_ENDPOINT = "https://api.anthropic.com/api/oauth/profile";
|
|
6879
|
+
var BETA_HEADER2 = "oauth-2025-04-20";
|
|
6880
|
+
var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6881
|
+
function mapRateLimitTier(rateLimitTier) {
|
|
6882
|
+
switch (rateLimitTier) {
|
|
6883
|
+
case "default_claude_pro":
|
|
6884
|
+
return "pro";
|
|
6885
|
+
case "default_claude_max":
|
|
6886
|
+
case "default_claude_max_5x":
|
|
6887
|
+
return "pro-max";
|
|
6888
|
+
case "default_claude_max_20x":
|
|
6889
|
+
return "pro-max-20x";
|
|
6890
|
+
default:
|
|
6891
|
+
return "custom";
|
|
6638
6892
|
}
|
|
6639
|
-
return out;
|
|
6640
6893
|
}
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
if (
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6894
|
+
async function fetchProfile(nowMs = Date.now()) {
|
|
6895
|
+
const creds = readOAuthCredentials();
|
|
6896
|
+
if (!creds) return null;
|
|
6897
|
+
if (creds.expiresAt - 6e4 <= nowMs) return null;
|
|
6898
|
+
let res;
|
|
6899
|
+
try {
|
|
6900
|
+
res = await fetch(PROFILE_ENDPOINT, {
|
|
6901
|
+
headers: {
|
|
6902
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
6903
|
+
"anthropic-beta": BETA_HEADER2
|
|
6904
|
+
}
|
|
6905
|
+
});
|
|
6906
|
+
} catch {
|
|
6907
|
+
return null;
|
|
6908
|
+
}
|
|
6909
|
+
if (!res.ok) return null;
|
|
6910
|
+
const body = await res.json().catch(() => null);
|
|
6911
|
+
if (!body) return null;
|
|
6912
|
+
const rateLimitTier = body.organization?.rate_limit_tier ?? null;
|
|
6913
|
+
return {
|
|
6914
|
+
planTier: mapRateLimitTier(rateLimitTier),
|
|
6915
|
+
rateLimitTier,
|
|
6916
|
+
organizationType: body.organization?.organization_type ?? null
|
|
6917
|
+
};
|
|
6650
6918
|
}
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
{
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
if (!text)
|
|
6659
|
-
return null;
|
|
6660
|
-
if (/<teammate-message|<task-notification|<local-command-caveat/i.test(text.slice(0, 400)))
|
|
6919
|
+
function readCachedProfile(cachePath) {
|
|
6920
|
+
if (!existsSync5(cachePath)) return null;
|
|
6921
|
+
try {
|
|
6922
|
+
const raw = JSON.parse(readFileSync9(cachePath, "utf8"));
|
|
6923
|
+
if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
|
|
6924
|
+
return raw;
|
|
6925
|
+
} catch {
|
|
6661
6926
|
return null;
|
|
6662
|
-
for (const { kind, re } of EXTERNAL_REF_PATTERNS) {
|
|
6663
|
-
const m = text.match(re);
|
|
6664
|
-
if (m)
|
|
6665
|
-
return { kind, preview: text.slice(0, 160) };
|
|
6666
6927
|
}
|
|
6667
|
-
return null;
|
|
6668
6928
|
}
|
|
6669
|
-
function
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
return "very_long";
|
|
6929
|
+
function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
|
|
6930
|
+
mkdirSync6(dirname6(cachePath), { recursive: true });
|
|
6931
|
+
writeFileSync5(
|
|
6932
|
+
cachePath,
|
|
6933
|
+
JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
|
|
6934
|
+
{ mode: 384 }
|
|
6935
|
+
);
|
|
6677
6936
|
}
|
|
6678
|
-
function
|
|
6679
|
-
const
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
const
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6937
|
+
async function getPlanTier(cachePath, nowMs = Date.now(), fetcher = fetchProfile) {
|
|
6938
|
+
const cached = readCachedProfile(cachePath);
|
|
6939
|
+
if (cached && nowMs - cached.fetchedAtMs < PROFILE_CACHE_TTL_MS) {
|
|
6940
|
+
return cached.profile.planTier;
|
|
6941
|
+
}
|
|
6942
|
+
const fresh = await fetcher(nowMs);
|
|
6943
|
+
if (!fresh) {
|
|
6944
|
+
return cached?.profile.planTier ?? null;
|
|
6945
|
+
}
|
|
6946
|
+
writeCachedProfile(cachePath, fresh, nowMs);
|
|
6947
|
+
return fresh.planTier;
|
|
6948
|
+
}
|
|
6949
|
+
|
|
6950
|
+
// src/team/backfill.ts
|
|
6951
|
+
init_fs();
|
|
6952
|
+
var USAGE_LOG = cclensPath("usage.jsonl");
|
|
6953
|
+
var PROFILE_CACHE = cclensPath("profile.json");
|
|
6954
|
+
var BATCH_SIZE = 500;
|
|
6955
|
+
var noopLog = () => {
|
|
6956
|
+
};
|
|
6957
|
+
function rawToWire(raw) {
|
|
6958
|
+
const win = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
6688
6959
|
return {
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6960
|
+
capturedAt: raw.captured_at,
|
|
6961
|
+
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
6962
|
+
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
6963
|
+
sevenDayOpus: win(raw.seven_day_opus),
|
|
6964
|
+
sevenDaySonnet: win(raw.seven_day_sonnet),
|
|
6965
|
+
sevenDayOauthApps: win(raw.seven_day_oauth_apps),
|
|
6966
|
+
sevenDayCowork: win(raw.seven_day_cowork),
|
|
6967
|
+
extraUsage: raw.extra_usage ? {
|
|
6968
|
+
isEnabled: raw.extra_usage.is_enabled,
|
|
6969
|
+
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
6970
|
+
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
6971
|
+
utilization: raw.extra_usage.utilization
|
|
6972
|
+
} : null
|
|
6696
6973
|
};
|
|
6697
6974
|
}
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6975
|
+
function chunk(items, size) {
|
|
6976
|
+
if (size <= 0) return [items];
|
|
6977
|
+
const out = [];
|
|
6978
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
6979
|
+
return out;
|
|
6702
6980
|
}
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6981
|
+
async function postBatch(config, snapshots, planTier) {
|
|
6982
|
+
const payload = {
|
|
6983
|
+
ingestId: `backfill-${randomUUID2()}`,
|
|
6984
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6985
|
+
snapshotHistory: snapshots,
|
|
6986
|
+
...planTier ? { planTier } : {}
|
|
6987
|
+
};
|
|
6988
|
+
const result = await pushToTeamServer(config, payload);
|
|
6989
|
+
if (!result.ok) {
|
|
6990
|
+
return { inserted: 0, skipped: snapshots.length, status: result.status };
|
|
6991
|
+
}
|
|
6992
|
+
const body = result.body;
|
|
6993
|
+
if (!body?.snapshotHistory) {
|
|
6994
|
+
throw new Error(
|
|
6995
|
+
"team server accepted snapshotHistory but did not return a result block \u2014 older image, upgrade required"
|
|
6996
|
+
);
|
|
6997
|
+
}
|
|
6717
6998
|
return {
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
msgId: ev.messageId,
|
|
6722
|
-
usage: ev.usage ? {
|
|
6723
|
-
input_tokens: ev.usage.input,
|
|
6724
|
-
output_tokens: ev.usage.output,
|
|
6725
|
-
cache_read_input_tokens: ev.usage.cacheRead,
|
|
6726
|
-
cache_creation_input_tokens: ev.usage.cacheWrite
|
|
6727
|
-
} : void 0,
|
|
6728
|
-
cwd: rawCwd
|
|
6999
|
+
inserted: body.snapshotHistory.inserted ?? 0,
|
|
7000
|
+
skipped: body.snapshotHistory.skipped ?? 0,
|
|
7001
|
+
status: result.status
|
|
6729
7002
|
};
|
|
6730
7003
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
function parseMs(iso) {
|
|
6738
|
-
if (!iso)
|
|
6739
|
-
return void 0;
|
|
6740
|
-
const ms = Date.parse(iso);
|
|
6741
|
-
return Number.isNaN(ms) ? void 0 : ms;
|
|
6742
|
-
}
|
|
6743
|
-
function trunc(s, n) {
|
|
6744
|
-
if (!s)
|
|
6745
|
-
return "";
|
|
6746
|
-
const one = s.replace(/\s+/g, " ").trim();
|
|
6747
|
-
return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
|
|
7004
|
+
function isAfterSince(snapshot, sinceCapturedAt) {
|
|
7005
|
+
if (!sinceCapturedAt) return true;
|
|
7006
|
+
const sinceMs = Date.parse(sinceCapturedAt);
|
|
7007
|
+
if (Number.isNaN(sinceMs)) return true;
|
|
7008
|
+
const capturedMs = Date.parse(snapshot.captured_at);
|
|
7009
|
+
return !Number.isNaN(capturedMs) && capturedMs > sinceMs;
|
|
6748
7010
|
}
|
|
6749
|
-
function
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
let counter = 0;
|
|
6753
|
-
return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
|
|
7011
|
+
function capturedAtMs(snapshot) {
|
|
7012
|
+
const ms = Date.parse(snapshot.captured_at);
|
|
7013
|
+
return Number.isNaN(ms) ? Number.MAX_SAFE_INTEGER : ms;
|
|
6754
7014
|
}
|
|
6755
|
-
function
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
if (parts.length >= 2)
|
|
6762
|
-
firstSeg = parts[1].trim();
|
|
6763
|
-
}
|
|
6764
|
-
const tokens = firstSeg.split(/\s+/).filter(Boolean);
|
|
6765
|
-
while (tokens.length && SHELL_NOISE.has(tokens[0]))
|
|
6766
|
-
tokens.shift();
|
|
6767
|
-
if (!tokens.length)
|
|
6768
|
-
return "?";
|
|
6769
|
-
let first = tokens[0];
|
|
6770
|
-
if (first.includes("=") && !first.startsWith("/")) {
|
|
6771
|
-
tokens.shift();
|
|
6772
|
-
if (!tokens.length)
|
|
6773
|
-
return "?";
|
|
6774
|
-
first = tokens[0];
|
|
6775
|
-
}
|
|
6776
|
-
if (first.includes("/") && !first.startsWith("./")) {
|
|
6777
|
-
first = first.replace(/\/+$/, "").split("/").pop();
|
|
7015
|
+
function latestCapturedAt(snapshots) {
|
|
7016
|
+
let latest = null;
|
|
7017
|
+
for (const s of snapshots) {
|
|
7018
|
+
const ms = Date.parse(s.capturedAt);
|
|
7019
|
+
if (Number.isNaN(ms)) continue;
|
|
7020
|
+
if (!latest || ms > latest.ms) latest = { at: s.capturedAt, ms };
|
|
6778
7021
|
}
|
|
6779
|
-
return
|
|
7022
|
+
return latest?.at;
|
|
6780
7023
|
}
|
|
6781
|
-
function
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
if (b && typeof b === "object" && b.type === "text") {
|
|
6786
|
-
return String(b.text ?? "");
|
|
6787
|
-
}
|
|
7024
|
+
async function runTeamBackfill(log3 = noopLog, filePath = USAGE_LOG, configOverride, options = {}) {
|
|
7025
|
+
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
7026
|
+
if (!config) {
|
|
7027
|
+
return { paired: false, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
6788
7028
|
}
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
}
|
|
6796
|
-
|
|
6797
|
-
const
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
const
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
7029
|
+
const raw = readSnapshots(filePath);
|
|
7030
|
+
const claudeOnly = raw.filter(
|
|
7031
|
+
(s) => (!s.agent || s.agent === "claude-code") && isAfterSince(s, options.sinceCapturedAt)
|
|
7032
|
+
).sort((a, b) => capturedAtMs(a) - capturedAtMs(b));
|
|
7033
|
+
if (claudeOnly.length === 0) {
|
|
7034
|
+
log3("info", "team backfill: no usage snapshots to send");
|
|
7035
|
+
return { paired: true, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
7036
|
+
}
|
|
7037
|
+
const wire = claudeOnly.map(rawToWire);
|
|
7038
|
+
const batches = chunk(wire, BATCH_SIZE);
|
|
7039
|
+
const planTier = await getPlanTier(PROFILE_CACHE).catch(() => null) ?? void 0;
|
|
7040
|
+
let inserted = 0;
|
|
7041
|
+
let skipped = 0;
|
|
7042
|
+
let lastSnapshotAt;
|
|
7043
|
+
for (let i = 0; i < batches.length; i++) {
|
|
7044
|
+
const batch = batches[i];
|
|
7045
|
+
try {
|
|
7046
|
+
const result = await postBatch(config, batch, planTier);
|
|
7047
|
+
if (result.status >= 400) {
|
|
7048
|
+
log3("warn", `team backfill: batch ${i + 1}/${batches.length} failed (${result.status})`);
|
|
7049
|
+
return {
|
|
7050
|
+
paired: true,
|
|
7051
|
+
sentSnapshots: wire.length,
|
|
7052
|
+
insertedSnapshots: inserted,
|
|
7053
|
+
skippedSnapshots: skipped,
|
|
7054
|
+
batches: i,
|
|
7055
|
+
lastSnapshotAt,
|
|
7056
|
+
error: `HTTP ${result.status}`
|
|
7057
|
+
};
|
|
7058
|
+
}
|
|
7059
|
+
inserted += result.inserted;
|
|
7060
|
+
skipped += result.skipped;
|
|
7061
|
+
lastSnapshotAt = latestCapturedAt(batch) ?? lastSnapshotAt;
|
|
7062
|
+
} catch (err) {
|
|
7063
|
+
log3("warn", `team backfill: batch ${i + 1}/${batches.length} error: ${err.message}`);
|
|
7064
|
+
return {
|
|
7065
|
+
paired: true,
|
|
7066
|
+
sentSnapshots: wire.length,
|
|
7067
|
+
insertedSnapshots: inserted,
|
|
7068
|
+
skippedSnapshots: skipped,
|
|
7069
|
+
batches: i,
|
|
7070
|
+
lastSnapshotAt,
|
|
7071
|
+
error: err.message
|
|
7072
|
+
};
|
|
6809
7073
|
}
|
|
6810
|
-
bucket.push(ev);
|
|
6811
7074
|
}
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
7075
|
+
log3(
|
|
7076
|
+
"info",
|
|
7077
|
+
`team backfill: ${inserted} new, ${skipped} already-known across ${batches.length} batch${batches.length === 1 ? "" : "es"}`
|
|
7078
|
+
);
|
|
6815
7079
|
return {
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
bashVerbs: /* @__PURE__ */ new Map(),
|
|
6823
|
-
skills: /* @__PURE__ */ new Map(),
|
|
6824
|
-
subagents: [],
|
|
6825
|
-
taskOps: 0,
|
|
6826
|
-
toolErrors: 0,
|
|
6827
|
-
consecSameToolMax: 0,
|
|
6828
|
-
consec: 0,
|
|
6829
|
-
endedInInterrupt: false,
|
|
6830
|
-
models: /* @__PURE__ */ new Map(),
|
|
6831
|
-
tokensIn: 0,
|
|
6832
|
-
tokensOut: 0,
|
|
6833
|
-
tokensCacheR: 0,
|
|
6834
|
-
tokensCacheW: 0,
|
|
6835
|
-
seenMsgIds: /* @__PURE__ */ new Set()
|
|
7080
|
+
paired: true,
|
|
7081
|
+
sentSnapshots: wire.length,
|
|
7082
|
+
insertedSnapshots: inserted,
|
|
7083
|
+
skippedSnapshots: skipped,
|
|
7084
|
+
batches: batches.length,
|
|
7085
|
+
lastSnapshotAt
|
|
6836
7086
|
};
|
|
6837
7087
|
}
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
7088
|
+
|
|
7089
|
+
// src/team/sync-log.ts
|
|
7090
|
+
init_fs();
|
|
7091
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
7092
|
+
var MAX_LINES = 300;
|
|
7093
|
+
var MAX_MSG_CHARS = 1900;
|
|
7094
|
+
function readPendingSyncLog(watermark) {
|
|
7095
|
+
let raw;
|
|
7096
|
+
try {
|
|
7097
|
+
raw = readFileSync10(cclensPath("daemon.log"), "utf8");
|
|
7098
|
+
} catch {
|
|
7099
|
+
return { lines: [] };
|
|
6845
7100
|
}
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
skills: t.skills,
|
|
6856
|
-
subagents: t.subagents,
|
|
6857
|
-
taskOps: t.taskOps,
|
|
6858
|
-
toolErrors: t.toolErrors,
|
|
6859
|
-
consecSameToolMax: t.consecSameToolMax,
|
|
6860
|
-
endedInInterrupt: t.endedInInterrupt,
|
|
6861
|
-
models: t.models,
|
|
6862
|
-
totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
|
|
6863
|
-
cwd
|
|
6864
|
-
});
|
|
6865
|
-
}
|
|
6866
|
-
function aggregateDay(dayEvents, _sessionFallbackProject) {
|
|
6867
|
-
const prTitles = [];
|
|
6868
|
-
let commits = 0;
|
|
6869
|
-
let pushes = 0;
|
|
6870
|
-
const gitToolUseIds = /* @__PURE__ */ new Set();
|
|
6871
|
-
const githubRepos = /* @__PURE__ */ new Set();
|
|
6872
|
-
const editedDirCounts = /* @__PURE__ */ new Map();
|
|
6873
|
-
const modelTurns = /* @__PURE__ */ new Map();
|
|
6874
|
-
let totalToolErrors = 0;
|
|
6875
|
-
let totalInterrupts = 0;
|
|
6876
|
-
let exitPlanCalls = 0;
|
|
6877
|
-
let taskOpsTotal = 0;
|
|
6878
|
-
let firstUser;
|
|
6879
|
-
let finalAgent;
|
|
6880
|
-
const cwdCounts = /* @__PURE__ */ new Map();
|
|
6881
|
-
const closed = [];
|
|
6882
|
-
let cur;
|
|
6883
|
-
const allTs = [];
|
|
6884
|
-
let lastEventCwd;
|
|
6885
|
-
for (const ev of dayEvents) {
|
|
6886
|
-
const tsMs = parseMs(ev.timestamp);
|
|
6887
|
-
if (tsMs !== void 0)
|
|
6888
|
-
allTs.push(tsMs);
|
|
6889
|
-
const view = viewEvent(ev);
|
|
6890
|
-
const rawType = view.rawType;
|
|
6891
|
-
const msg = { content: view.content, id: view.msgId, model: view.model, usage: view.usage };
|
|
6892
|
-
const content = view.content;
|
|
6893
|
-
const eventCwd = view.cwd;
|
|
6894
|
-
if (eventCwd)
|
|
6895
|
-
lastEventCwd = eventCwd;
|
|
6896
|
-
if (rawType === "user") {
|
|
6897
|
-
const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
6898
|
-
if (!isToolResultOnly) {
|
|
6899
|
-
const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
|
|
6900
|
-
if (cur) {
|
|
6901
|
-
if (text && INTERRUPT_RE2.test(text)) {
|
|
6902
|
-
cur.endedInInterrupt = true;
|
|
6903
|
-
totalInterrupts++;
|
|
6904
|
-
}
|
|
6905
|
-
closeTurn(cur, closed, eventCwd);
|
|
6906
|
-
}
|
|
6907
|
-
cur = newTurn(tsMs, text);
|
|
6908
|
-
if (!firstUser && text && classifyUserInputSource(text) === "human") {
|
|
6909
|
-
firstUser = text;
|
|
6910
|
-
}
|
|
6911
|
-
} else {
|
|
6912
|
-
if (Array.isArray(content)) {
|
|
6913
|
-
for (const c of content) {
|
|
6914
|
-
if (!c || typeof c !== "object" || c.type !== "tool_result")
|
|
6915
|
-
continue;
|
|
6916
|
-
if (c.is_error) {
|
|
6917
|
-
totalToolErrors++;
|
|
6918
|
-
if (cur)
|
|
6919
|
-
cur.toolErrors++;
|
|
6920
|
-
}
|
|
6921
|
-
const tuId = c.tool_use_id;
|
|
6922
|
-
if (tuId && gitToolUseIds.has(tuId)) {
|
|
6923
|
-
const rc = c.content;
|
|
6924
|
-
const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
|
|
6925
|
-
for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
|
|
6926
|
-
if (m[1].toLowerCase() === "user-attachments")
|
|
6927
|
-
continue;
|
|
6928
|
-
githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
|
|
6929
|
-
}
|
|
6930
|
-
}
|
|
6931
|
-
}
|
|
6932
|
-
}
|
|
6933
|
-
if (cur && tsMs !== void 0) {
|
|
6934
|
-
cur.eventTs.push(tsMs);
|
|
6935
|
-
cur.lastMs = tsMs;
|
|
6936
|
-
}
|
|
6937
|
-
}
|
|
6938
|
-
} else if (rawType === "assistant") {
|
|
6939
|
-
if (cur && tsMs !== void 0) {
|
|
6940
|
-
cur.eventTs.push(tsMs);
|
|
6941
|
-
cur.lastMs = tsMs;
|
|
6942
|
-
}
|
|
6943
|
-
const model = msg.model;
|
|
6944
|
-
if (model) {
|
|
6945
|
-
modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
|
|
6946
|
-
if (cur)
|
|
6947
|
-
cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
|
|
6948
|
-
}
|
|
6949
|
-
const msgId = msg.id;
|
|
6950
|
-
const usage = msg.usage;
|
|
6951
|
-
if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
|
|
6952
|
-
cur.seenMsgIds.add(msgId);
|
|
6953
|
-
cur.tokensIn += usage.input_tokens ?? 0;
|
|
6954
|
-
cur.tokensOut += usage.output_tokens ?? 0;
|
|
6955
|
-
cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
|
|
6956
|
-
cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
|
|
6957
|
-
}
|
|
6958
|
-
if (Array.isArray(content)) {
|
|
6959
|
-
for (const c of content) {
|
|
6960
|
-
if (!c || typeof c !== "object")
|
|
6961
|
-
continue;
|
|
6962
|
-
const ct = c;
|
|
6963
|
-
if (ct.type === "text" && ct.text) {
|
|
6964
|
-
finalAgent = ct.text;
|
|
6965
|
-
if (cur) {
|
|
6966
|
-
if (cur.firstAgentText === void 0)
|
|
6967
|
-
cur.firstAgentText = ct.text;
|
|
6968
|
-
cur.lastAgentText = ct.text;
|
|
6969
|
-
}
|
|
6970
|
-
} else if (ct.type === "tool_use") {
|
|
6971
|
-
const name = ct.name ?? "unknown";
|
|
6972
|
-
if (cur) {
|
|
6973
|
-
cur.toolsTotal++;
|
|
6974
|
-
cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
|
|
6975
|
-
if (name === cur.lastTool)
|
|
6976
|
-
cur.consec++;
|
|
6977
|
-
else {
|
|
6978
|
-
cur.lastTool = name;
|
|
6979
|
-
cur.consec = 1;
|
|
6980
|
-
}
|
|
6981
|
-
if (cur.consec > cur.consecSameToolMax)
|
|
6982
|
-
cur.consecSameToolMax = cur.consec;
|
|
6983
|
-
if (name === "Skill") {
|
|
6984
|
-
const sk = String(ct.input?.skill ?? "unknown");
|
|
6985
|
-
cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
|
|
6986
|
-
}
|
|
6987
|
-
if (name === "Agent") {
|
|
6988
|
-
const inp = ct.input ?? {};
|
|
6989
|
-
cur.subagents.push({
|
|
6990
|
-
type: String(inp.subagent_type ?? "general-purpose"),
|
|
6991
|
-
description: trunc(String(inp.description ?? ""), 80),
|
|
6992
|
-
prompt_preview: trunc(String(inp.prompt ?? ""), 240),
|
|
6993
|
-
background: Boolean(inp.run_in_background)
|
|
6994
|
-
});
|
|
6995
|
-
}
|
|
6996
|
-
if (name === "ExitPlanMode")
|
|
6997
|
-
exitPlanCalls++;
|
|
6998
|
-
if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
|
|
6999
|
-
cur.taskOps++;
|
|
7000
|
-
taskOpsTotal++;
|
|
7001
|
-
}
|
|
7002
|
-
}
|
|
7003
|
-
if (name === "Bash") {
|
|
7004
|
-
const cmd = String(ct.input?.command ?? "");
|
|
7005
|
-
if (cur)
|
|
7006
|
-
cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
|
|
7007
|
-
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
7008
|
-
const m = cmd.match(PR_TITLE_RE);
|
|
7009
|
-
prTitles.push(m?.[1] ?? cmd.slice(0, 120));
|
|
7010
|
-
}
|
|
7011
|
-
if (/\bgit\s+commit\b/.test(cmd))
|
|
7012
|
-
commits++;
|
|
7013
|
-
if (/\bgit\s+push\b/.test(cmd))
|
|
7014
|
-
pushes++;
|
|
7015
|
-
if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
|
|
7016
|
-
gitToolUseIds.add(ct.id);
|
|
7017
|
-
}
|
|
7018
|
-
if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
|
|
7019
|
-
cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
|
|
7020
|
-
}
|
|
7021
|
-
if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
|
|
7022
|
-
const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
|
|
7023
|
-
const slash = fp.lastIndexOf("/");
|
|
7024
|
-
if (slash > 0) {
|
|
7025
|
-
const dir = fp.slice(0, slash);
|
|
7026
|
-
editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
|
|
7027
|
-
}
|
|
7028
|
-
}
|
|
7029
|
-
}
|
|
7030
|
-
}
|
|
7031
|
-
}
|
|
7032
|
-
}
|
|
7101
|
+
const lines = [];
|
|
7102
|
+
for (const line of raw.split("\n")) {
|
|
7103
|
+
const m = line.match(/^(\S+) (INFO|WARN|ERROR) (.*)$/);
|
|
7104
|
+
if (!m) continue;
|
|
7105
|
+
const [, ts, levelRaw, msg] = m;
|
|
7106
|
+
if (!msg.startsWith("[sync] ")) continue;
|
|
7107
|
+
if (watermark && ts <= watermark) continue;
|
|
7108
|
+
const clipped = msg.length > MAX_MSG_CHARS ? msg.slice(0, MAX_MSG_CHARS) + "\u2026" : msg;
|
|
7109
|
+
lines.push({ ts, level: levelRaw.toLowerCase(), msg: clipped });
|
|
7033
7110
|
}
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7111
|
+
const capped = lines.slice(0, MAX_LINES);
|
|
7112
|
+
return { lines: capped, watermark: capped.length ? capped[capped.length - 1].ts : void 0 };
|
|
7113
|
+
}
|
|
7114
|
+
|
|
7115
|
+
// src/team/last-push.ts
|
|
7116
|
+
init_fs();
|
|
7117
|
+
import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, mkdirSync as mkdirSync7 } from "node:fs";
|
|
7118
|
+
import { join as join6 } from "node:path";
|
|
7119
|
+
var LAST_PUSH_FILE = "team-last-push.json";
|
|
7120
|
+
function write(record, dir) {
|
|
7121
|
+
const d = dir ?? cclensHome();
|
|
7122
|
+
mkdirSync7(d, { recursive: true });
|
|
7123
|
+
writeFileSync6(join6(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
|
|
7124
|
+
}
|
|
7125
|
+
function writeLastPushSuccess(payload, dir) {
|
|
7126
|
+
write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: true, payload }, dir);
|
|
7127
|
+
}
|
|
7128
|
+
function writeLastPushFailure(payload, error, dir) {
|
|
7129
|
+
write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: false, payload, error }, dir);
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7132
|
+
// src/team/commands.ts
|
|
7133
|
+
async function dispatchCommand(command, config, log3) {
|
|
7134
|
+
switch (command.type) {
|
|
7135
|
+
case "backfill-activity":
|
|
7136
|
+
return runActivityBackfill(command, config, log3);
|
|
7137
|
+
default:
|
|
7138
|
+
return {
|
|
7139
|
+
id: command.id,
|
|
7140
|
+
ok: false,
|
|
7141
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7142
|
+
error: `Unknown command type: ${command.type}`
|
|
7143
|
+
};
|
|
7042
7144
|
}
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
const
|
|
7145
|
+
}
|
|
7146
|
+
async function runActivityBackfill(command, config, log3) {
|
|
7147
|
+
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
7148
|
+
const { toLocalDay: toLocalDay3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7149
|
+
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
7150
|
+
const days = command.params.days;
|
|
7151
|
+
const todayMs = Date.now();
|
|
7152
|
+
const targetDayMs = todayMs - (days - 1) * 24 * 60 * 60 * 1e3;
|
|
7153
|
+
const targetDay = toLocalDay3(targetDayMs);
|
|
7154
|
+
log3("info", `command ${command.id}: backfill-activity from ${targetDay} (${days} days)`);
|
|
7155
|
+
let sessions;
|
|
7156
|
+
try {
|
|
7157
|
+
sessions = await listSessions2({ limit: 1e4 });
|
|
7158
|
+
} catch (err) {
|
|
7159
|
+
return {
|
|
7160
|
+
id: command.id,
|
|
7161
|
+
ok: false,
|
|
7162
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7163
|
+
error: `Failed to read sessions: ${err.message}`
|
|
7164
|
+
};
|
|
7165
|
+
}
|
|
7166
|
+
const rollups = buildRollupsForRange(sessions, targetDay);
|
|
7167
|
+
if (rollups.length === 0) {
|
|
7168
|
+
return {
|
|
7169
|
+
id: command.id,
|
|
7170
|
+
ok: true,
|
|
7171
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7172
|
+
summary: { pushed: 0, fromDay: targetDay }
|
|
7173
|
+
};
|
|
7174
|
+
}
|
|
7175
|
+
const resolveRepo = createRepoResolver();
|
|
7176
|
+
let pushed = 0;
|
|
7177
|
+
for (const rollup of rollups) {
|
|
7178
|
+
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
7179
|
+
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
|
|
7180
|
+
let artifactSignals = null;
|
|
7181
|
+
try {
|
|
7182
|
+
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
|
|
7183
|
+
} catch {
|
|
7184
|
+
}
|
|
7185
|
+
const payload = buildIngestPayload({
|
|
7186
|
+
rollup,
|
|
7187
|
+
richExtras: richBlocks?.rich,
|
|
7188
|
+
enrichedExtras: richBlocks?.enriched,
|
|
7189
|
+
artifactSignals: artifactSignals ?? void 0
|
|
7190
|
+
});
|
|
7191
|
+
const result = await pushToTeamServer(config, payload);
|
|
7192
|
+
if (!result.ok) {
|
|
7193
|
+
return {
|
|
7194
|
+
id: command.id,
|
|
7195
|
+
ok: false,
|
|
7196
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7197
|
+
error: `Push failed on ${rollup.day} (HTTP ${result.status}); pushed ${pushed}/${rollups.length} before failing`
|
|
7198
|
+
};
|
|
7199
|
+
}
|
|
7200
|
+
pushed++;
|
|
7201
|
+
}
|
|
7202
|
+
log3("info", `command ${command.id}: backfill-activity ok \u2014 pushed ${pushed} day${pushed === 1 ? "" : "s"}`);
|
|
7046
7203
|
return {
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
commits,
|
|
7052
|
-
pushes,
|
|
7053
|
-
githubRepos: [...githubRepos].slice(0, 5),
|
|
7054
|
-
editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
|
|
7055
|
-
modelTurns,
|
|
7056
|
-
totalToolErrors,
|
|
7057
|
-
totalInterrupts,
|
|
7058
|
-
exitPlanCalls,
|
|
7059
|
-
taskOpsTotal,
|
|
7060
|
-
cwdCounts,
|
|
7061
|
-
start_iso,
|
|
7062
|
-
end_iso,
|
|
7063
|
-
activeMs: activeMs2
|
|
7204
|
+
id: command.id,
|
|
7205
|
+
ok: true,
|
|
7206
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7207
|
+
summary: { pushed, fromDay: targetDay }
|
|
7064
7208
|
};
|
|
7065
7209
|
}
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7210
|
+
|
|
7211
|
+
// src/team/sync.ts
|
|
7212
|
+
init_fs();
|
|
7213
|
+
var inFlightCommands = /* @__PURE__ */ new Set();
|
|
7214
|
+
var USAGE_LOG2 = cclensPath("usage.jsonl");
|
|
7215
|
+
var PROFILE_CACHE2 = cclensPath("profile.json");
|
|
7216
|
+
var noopLog2 = () => {
|
|
7217
|
+
};
|
|
7218
|
+
function syncLogDelivered(body) {
|
|
7219
|
+
const blocks = body?.blocks;
|
|
7220
|
+
return !blocks || blocks.accepted.includes("syncLog");
|
|
7221
|
+
}
|
|
7222
|
+
function levelForStatus(s) {
|
|
7223
|
+
if (s === "ok" || s === "idle") return "info";
|
|
7224
|
+
if (s === "degraded" || s === "failed") return "warn";
|
|
7225
|
+
return "error";
|
|
7226
|
+
}
|
|
7227
|
+
function fmtDuration(ms) {
|
|
7228
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
|
|
7229
|
+
}
|
|
7230
|
+
function liveSnapshotStr(snap) {
|
|
7231
|
+
if (!snap) return void 0;
|
|
7232
|
+
const f = snap.fiveHour?.utilization;
|
|
7233
|
+
const s = snap.sevenDay?.utilization;
|
|
7234
|
+
const parts = [
|
|
7235
|
+
typeof f === "number" ? `5h ${Math.round(f)}%` : null,
|
|
7236
|
+
typeof s === "number" ? `7d ${Math.round(s)}%` : null
|
|
7237
|
+
].filter(Boolean);
|
|
7238
|
+
return parts.length ? parts.join(" / ") : void 0;
|
|
7239
|
+
}
|
|
7240
|
+
function buildSyncLine(status, trigger, s, elapsedMs, nextSyncMs) {
|
|
7241
|
+
const parts = [`[sync] ${status}`, trigger];
|
|
7242
|
+
if (s.pushedDays.length) {
|
|
7243
|
+
parts.push(
|
|
7244
|
+
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]})`
|
|
7245
|
+
);
|
|
7074
7246
|
}
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
}
|
|
7247
|
+
if (s.queued) {
|
|
7248
|
+
parts.push(
|
|
7249
|
+
s.queuedDay ? `queued ${s.queued} for retry (${s.queuedDay}, HTTP ${s.queuedStatus})` : `queued ${s.queued} for retry`
|
|
7250
|
+
);
|
|
7251
|
+
}
|
|
7252
|
+
if (s.droppedDays.length) {
|
|
7253
|
+
parts.push(`dropped ${s.droppedDays.length} unrecoverable (${s.droppedDays.join(",")})`);
|
|
7254
|
+
}
|
|
7255
|
+
if (s.recoveredDays?.length) {
|
|
7256
|
+
parts.push(
|
|
7257
|
+
`recovered ${s.recoveredDays.length} dropped day${s.recoveredDays.length === 1 ? "" : "s"} (${s.recoveredDays.join(",")})`
|
|
7258
|
+
);
|
|
7259
|
+
}
|
|
7260
|
+
if (s.queuedDrained) parts.push(`drained ${s.queuedDrained} from backlog`);
|
|
7261
|
+
if (s.idleReason) parts.push(s.idleReason);
|
|
7262
|
+
if (s.live) parts.push(`live ${s.live}`);
|
|
7263
|
+
if (s.usageSnapshots > 0) {
|
|
7264
|
+
parts.push(`usage +${s.usageSnapshots} snapshot${s.usageSnapshots === 1 ? "" : "s"}`);
|
|
7265
|
+
}
|
|
7266
|
+
if (s.accepted?.length) parts.push(`server accepted ${s.accepted.join(",")}`);
|
|
7267
|
+
if (s.skipped && Object.keys(s.skipped).length) {
|
|
7268
|
+
parts.push(
|
|
7269
|
+
`server SKIPPED ${Object.entries(s.skipped).map(([k, v]) => `${k} (${v})`).join("; ")}`
|
|
7270
|
+
);
|
|
7271
|
+
}
|
|
7272
|
+
if (s.errorMsg) parts.push(s.errorMsg);
|
|
7273
|
+
parts.push(fmtDuration(elapsedMs));
|
|
7274
|
+
if (nextSyncMs) parts.push(`next ~${Math.round(nextSyncMs / 6e4)}m`);
|
|
7275
|
+
return parts.join(" \xB7 ");
|
|
7083
7276
|
}
|
|
7084
|
-
function
|
|
7085
|
-
const
|
|
7086
|
-
|
|
7087
|
-
const
|
|
7088
|
-
const
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7277
|
+
async function runTeamSync(log3 = noopLog2, configOverride, options = {}) {
|
|
7278
|
+
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
7279
|
+
if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
|
|
7280
|
+
const startedAt = Date.now();
|
|
7281
|
+
const trigger = options.trigger ?? "auto";
|
|
7282
|
+
const summary = {
|
|
7283
|
+
pushedDays: [],
|
|
7284
|
+
droppedDays: [],
|
|
7285
|
+
queued: 0,
|
|
7286
|
+
queuedDrained: 0,
|
|
7287
|
+
usageSnapshots: 0
|
|
7288
|
+
};
|
|
7289
|
+
const finish = (status) => {
|
|
7290
|
+
log3(
|
|
7291
|
+
levelForStatus(status),
|
|
7292
|
+
buildSyncLine(status, trigger, summary, Date.now() - startedAt, options.nextSyncMs)
|
|
7293
|
+
);
|
|
7294
|
+
};
|
|
7295
|
+
const recordVerdict = (body) => {
|
|
7296
|
+
if (body?.blocks) {
|
|
7297
|
+
summary.accepted = [.../* @__PURE__ */ new Set([...summary.accepted ?? [], ...body.blocks.accepted])];
|
|
7298
|
+
summary.skipped = { ...summary.skipped, ...body.blocks.skipped };
|
|
7102
7299
|
}
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
const
|
|
7107
|
-
const
|
|
7108
|
-
const
|
|
7109
|
-
|
|
7110
|
-
const
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7300
|
+
};
|
|
7301
|
+
try {
|
|
7302
|
+
resetEnsuredSessions();
|
|
7303
|
+
const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
7304
|
+
const { toLocalDay: toLocalDay3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7305
|
+
const today = toLocalDay3(Date.now());
|
|
7306
|
+
let nextConfig = { ...config };
|
|
7307
|
+
const persistConfig = (patch) => {
|
|
7308
|
+
nextConfig = { ...nextConfig, ...patch };
|
|
7309
|
+
writeTeamConfig(nextConfig);
|
|
7310
|
+
};
|
|
7311
|
+
const pendingLog = readPendingSyncLog(config.lastSyncedLogAt);
|
|
7312
|
+
const collectedCommands = /* @__PURE__ */ new Map();
|
|
7313
|
+
const collectCommands = (commands) => {
|
|
7314
|
+
if (!commands) return;
|
|
7315
|
+
for (const cmd of commands) {
|
|
7316
|
+
if (!collectedCommands.has(cmd.id)) collectedCommands.set(cmd.id, cmd);
|
|
7317
|
+
}
|
|
7318
|
+
};
|
|
7319
|
+
const dispatchAndReport = async () => {
|
|
7320
|
+
try {
|
|
7321
|
+
if (collectedCommands.size === 0) return;
|
|
7322
|
+
const results = [];
|
|
7323
|
+
for (const cmd of collectedCommands.values()) {
|
|
7324
|
+
if (inFlightCommands.has(cmd.id)) continue;
|
|
7325
|
+
inFlightCommands.add(cmd.id);
|
|
7326
|
+
try {
|
|
7327
|
+
const result = await dispatchCommand(cmd, config, log3);
|
|
7328
|
+
results.push(result);
|
|
7329
|
+
} finally {
|
|
7330
|
+
inFlightCommands.delete(cmd.id);
|
|
7331
|
+
}
|
|
7332
|
+
}
|
|
7333
|
+
if (results.length === 0) return;
|
|
7334
|
+
const resultsPayload = {
|
|
7335
|
+
ingestId: randomUUID3(),
|
|
7336
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7337
|
+
commandResults: results
|
|
7338
|
+
};
|
|
7339
|
+
const r = await pushToTeamServer(config, resultsPayload);
|
|
7340
|
+
if (!r.ok) {
|
|
7341
|
+
log3("warn", `team commandResults push failed (${r.status}); will retry on next sync`);
|
|
7342
|
+
} else {
|
|
7343
|
+
log3("info", `team commandResults push ok: ${results.length} result${results.length === 1 ? "" : "s"}`);
|
|
7344
|
+
}
|
|
7345
|
+
} catch (err) {
|
|
7346
|
+
log3("warn", `team command dispatch error: ${err.message}`);
|
|
7347
|
+
}
|
|
7348
|
+
};
|
|
7349
|
+
const isValidationPoison = (status2) => status2 === 400 || status2 === 422;
|
|
7350
|
+
const drainBacklog = async () => {
|
|
7351
|
+
let drained = 0;
|
|
7352
|
+
const backlog = dequeuePayloads();
|
|
7353
|
+
for (let i = 0; i < backlog.length; i++) {
|
|
7354
|
+
const qResult = await pushToTeamServer(config, backlog[i]);
|
|
7355
|
+
if (!qResult.ok) {
|
|
7356
|
+
if (isValidationPoison(qResult.status)) {
|
|
7357
|
+
log3("warn", `team push: queued payload rejected (HTTP ${qResult.status}); dropping unrecoverable item`);
|
|
7358
|
+
continue;
|
|
7359
|
+
}
|
|
7360
|
+
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
7361
|
+
break;
|
|
7362
|
+
}
|
|
7363
|
+
collectCommands(qResult.body?.commands);
|
|
7364
|
+
drained++;
|
|
7365
|
+
}
|
|
7366
|
+
return drained;
|
|
7128
7367
|
};
|
|
7129
|
-
const
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
flags.push("high_errors");
|
|
7135
|
-
if (consecMax >= 8)
|
|
7136
|
-
flags.push("loop_suspected");
|
|
7137
|
-
if (agg.activeMs < 5 * 6e4 && prs >= 1)
|
|
7138
|
-
flags.push("fast_ship");
|
|
7139
|
-
if (exitPlanCalls > 0)
|
|
7140
|
-
flags.push("plan_used");
|
|
7141
|
-
const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
|
|
7142
|
-
if (subagentTurns >= 3)
|
|
7143
|
-
flags.push("orchestrated");
|
|
7144
|
-
if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
|
|
7145
|
-
flags.push("long_autonomous");
|
|
7146
|
-
const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
|
|
7147
|
-
const modelMix = Object.fromEntries(agg.modelTurns);
|
|
7148
|
-
const firstUserRaw = agg.firstUser ?? "";
|
|
7149
|
-
const first_user = trunc(minimizeImages(firstUserRaw), 400);
|
|
7150
|
-
const finalAgentRaw = agg.finalAgent ?? "";
|
|
7151
|
-
const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
|
|
7152
|
-
let project = sessionFallbackProject;
|
|
7153
|
-
if (agg.cwdCounts.size > 0) {
|
|
7154
|
-
const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
7155
|
-
project = canonicalProjectName(best[0]);
|
|
7368
|
+
const usageBackfill = await runTeamBackfill(log3, USAGE_LOG2, config, {
|
|
7369
|
+
sinceCapturedAt: options.forceUsageBackfill ? void 0 : config.lastSyncedUsageSnapshotAt
|
|
7370
|
+
});
|
|
7371
|
+
if (usageBackfill.lastSnapshotAt) {
|
|
7372
|
+
persistConfig({ lastSyncedUsageSnapshotAt: usageBackfill.lastSnapshotAt });
|
|
7156
7373
|
}
|
|
7157
|
-
const
|
|
7158
|
-
const
|
|
7159
|
-
const
|
|
7160
|
-
const
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
const
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7374
|
+
const sessions = await listSessions2({ limit: 1e4 });
|
|
7375
|
+
const allRollups = buildRollupsForRange(sessions);
|
|
7376
|
+
const rollups = allRollups.filter((r) => !config.lastSyncedDay || r.day >= config.lastSyncedDay);
|
|
7377
|
+
const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG2) ?? void 0;
|
|
7378
|
+
const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
|
|
7379
|
+
const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
|
|
7380
|
+
if (rollups.length === 0) {
|
|
7381
|
+
const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
|
|
7382
|
+
if (!hasLiveData) {
|
|
7383
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
7384
|
+
if (usageBackfill.sentSnapshots > 0) {
|
|
7385
|
+
finish("ok");
|
|
7386
|
+
} else {
|
|
7387
|
+
summary.idleReason = "nothing to sync";
|
|
7388
|
+
finish("idle");
|
|
7389
|
+
}
|
|
7390
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
7391
|
+
}
|
|
7392
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
7393
|
+
summary.live = liveSnapshotStr(usageSnapshot);
|
|
7394
|
+
const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks, syncLog: pendingLog.lines });
|
|
7395
|
+
const result = await pushToTeamServer(config, payload);
|
|
7396
|
+
if (!result.ok) {
|
|
7397
|
+
if (isValidationPoison(result.status)) {
|
|
7398
|
+
const errLine2 = `team push: live-only payload rejected (HTTP ${result.status}); dropping unrecoverable payload`;
|
|
7399
|
+
writeLastPushFailure(payload, errLine2);
|
|
7400
|
+
summary.errorMsg = `live-snapshot push HTTP ${result.status} (validation) \u2014 dropped unrecoverable`;
|
|
7401
|
+
finish("error");
|
|
7402
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
7403
|
+
}
|
|
7404
|
+
const errLine = `team push failed (${result.status})`;
|
|
7405
|
+
writeLastPushFailure(payload, errLine);
|
|
7406
|
+
enqueuePayload(payload);
|
|
7407
|
+
summary.queued = 1;
|
|
7408
|
+
summary.errorMsg = `live-snapshot push HTTP ${result.status} \u2014 queued for retry`;
|
|
7409
|
+
finish("failed");
|
|
7410
|
+
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
|
|
7411
|
+
}
|
|
7412
|
+
writeLastPushSuccess(payload);
|
|
7413
|
+
if (pendingLog.watermark && syncLogDelivered(result.body)) {
|
|
7414
|
+
persistConfig({ lastSyncedLogAt: pendingLog.watermark });
|
|
7415
|
+
}
|
|
7416
|
+
collectCommands(result.body?.commands);
|
|
7417
|
+
recordVerdict(result.body);
|
|
7418
|
+
const queuedDrained2 = await drainBacklog();
|
|
7419
|
+
summary.queuedDrained = queuedDrained2;
|
|
7420
|
+
summary.idleReason = "no new daily activity";
|
|
7421
|
+
finish(summary.skipped && Object.keys(summary.skipped).length ? "degraded" : "idle");
|
|
7422
|
+
await dispatchAndReport();
|
|
7423
|
+
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
|
|
7190
7424
|
}
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7425
|
+
let pushed = 0;
|
|
7426
|
+
let queued = 0;
|
|
7427
|
+
let failedDay;
|
|
7428
|
+
let lastResolvedDay;
|
|
7429
|
+
const droppedDaysBefore = config.droppedDays ?? [];
|
|
7430
|
+
const workingDropped = [...droppedDaysBefore];
|
|
7431
|
+
let syncLogWasDelivered = false;
|
|
7432
|
+
const resolveRepo = createRepoResolver();
|
|
7433
|
+
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
7434
|
+
const buildDayPayload = (rollup, opts) => {
|
|
7435
|
+
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
7436
|
+
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
|
|
7437
|
+
let artifactSignals = null;
|
|
7438
|
+
try {
|
|
7439
|
+
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
|
|
7440
|
+
} catch {
|
|
7441
|
+
}
|
|
7442
|
+
return buildIngestPayload({
|
|
7443
|
+
rollup,
|
|
7444
|
+
richExtras: richBlocks?.rich,
|
|
7445
|
+
enrichedExtras: richBlocks?.enriched,
|
|
7446
|
+
artifactSignals: artifactSignals ?? void 0,
|
|
7447
|
+
// Current-cycle data only on the latest day so older days aren't tagged
|
|
7448
|
+
// with today's snapshot / peaks.
|
|
7449
|
+
usageSnapshot: opts.latest ? usageSnapshot : void 0,
|
|
7450
|
+
planTier,
|
|
7451
|
+
cyclePeaks: opts.latest ? cyclePeaks : void 0,
|
|
7452
|
+
syncLog: opts.syncLog
|
|
7453
|
+
});
|
|
7219
7454
|
};
|
|
7220
|
-
|
|
7221
|
-
|
|
7455
|
+
for (let i = 0; i < rollups.length; i++) {
|
|
7456
|
+
const rollup = rollups[i];
|
|
7457
|
+
const isLatest = i === rollups.length - 1;
|
|
7458
|
+
const payload = buildDayPayload(rollup, {
|
|
7459
|
+
latest: isLatest,
|
|
7460
|
+
syncLog: i === 0 ? pendingLog.lines : void 0
|
|
7461
|
+
});
|
|
7462
|
+
const result = await pushToTeamServer(config, payload);
|
|
7463
|
+
if (!result.ok) {
|
|
7464
|
+
if (isValidationPoison(result.status)) {
|
|
7465
|
+
const errLine2 = `team push: ${rollup.day} rejected (HTTP ${result.status}); skipping past unrecoverable day`;
|
|
7466
|
+
writeLastPushFailure(payload, errLine2);
|
|
7467
|
+
summary.droppedDays.push(rollup.day);
|
|
7468
|
+
if (!workingDropped.includes(rollup.day)) workingDropped.push(rollup.day);
|
|
7469
|
+
lastResolvedDay = rollup.day;
|
|
7470
|
+
continue;
|
|
7471
|
+
}
|
|
7472
|
+
const errLine = `team push failed on ${rollup.day} (${result.status})`;
|
|
7473
|
+
writeLastPushFailure(payload, errLine);
|
|
7474
|
+
enqueuePayload(payload);
|
|
7475
|
+
queued++;
|
|
7476
|
+
summary.queuedDay = rollup.day;
|
|
7477
|
+
summary.queuedStatus = result.status;
|
|
7478
|
+
failedDay = rollup.day;
|
|
7479
|
+
break;
|
|
7480
|
+
}
|
|
7481
|
+
writeLastPushSuccess(payload);
|
|
7482
|
+
collectCommands(result.body?.commands);
|
|
7483
|
+
recordVerdict(result.body);
|
|
7484
|
+
if (i === 0) syncLogWasDelivered = syncLogDelivered(result.body);
|
|
7485
|
+
pushed++;
|
|
7486
|
+
summary.pushedDays.push(rollup.day);
|
|
7487
|
+
lastResolvedDay = rollup.day;
|
|
7488
|
+
}
|
|
7489
|
+
if (!failedDay) {
|
|
7490
|
+
const retryDay = [...droppedDaysBefore].sort().find((d) => !summary.droppedDays.includes(d));
|
|
7491
|
+
const retryRollup = retryDay ? allRollups.find((r) => r.day === retryDay) : void 0;
|
|
7492
|
+
if (retryDay && retryRollup) {
|
|
7493
|
+
const payload = buildDayPayload(retryRollup, { latest: false });
|
|
7494
|
+
const result = await pushToTeamServer(config, payload);
|
|
7495
|
+
if (result.ok) {
|
|
7496
|
+
writeLastPushSuccess(payload);
|
|
7497
|
+
collectCommands(result.body?.commands);
|
|
7498
|
+
recordVerdict(result.body);
|
|
7499
|
+
const idx = workingDropped.indexOf(retryDay);
|
|
7500
|
+
if (idx >= 0) workingDropped.splice(idx, 1);
|
|
7501
|
+
summary.recoveredDays = [retryDay];
|
|
7502
|
+
} else {
|
|
7503
|
+
writeLastPushFailure(payload, `team push: dropped-day retry ${retryDay} failed (HTTP ${result.status})`);
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
const nextDropped = [...new Set(workingDropped)].sort().slice(-90);
|
|
7508
|
+
const prevDropped = [...new Set(droppedDaysBefore)].sort();
|
|
7509
|
+
const droppedChanged = nextDropped.length !== prevDropped.length || nextDropped.some((d, i) => d !== prevDropped[i]);
|
|
7510
|
+
const droppedPatch = droppedChanged ? { droppedDays: nextDropped } : {};
|
|
7511
|
+
if (failedDay) {
|
|
7512
|
+
if (lastResolvedDay || droppedChanged) {
|
|
7513
|
+
persistConfig({ ...lastResolvedDay ? { lastSyncedDay: lastResolvedDay } : {}, ...droppedPatch });
|
|
7514
|
+
}
|
|
7515
|
+
} else {
|
|
7516
|
+
persistConfig({ lastSyncedDay: today, ...droppedPatch });
|
|
7517
|
+
}
|
|
7518
|
+
if (syncLogWasDelivered && pendingLog.watermark) {
|
|
7519
|
+
persistConfig({ lastSyncedLogAt: pendingLog.watermark });
|
|
7520
|
+
}
|
|
7521
|
+
const queuedDrained = !failedDay ? await drainBacklog() : 0;
|
|
7522
|
+
summary.queued = queued;
|
|
7523
|
+
summary.queuedDrained = queuedDrained;
|
|
7524
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
7525
|
+
let status;
|
|
7526
|
+
if (failedDay) status = "failed";
|
|
7527
|
+
else if (summary.droppedDays.length || summary.skipped && Object.keys(summary.skipped).length) status = "degraded";
|
|
7528
|
+
else status = "ok";
|
|
7529
|
+
finish(status);
|
|
7530
|
+
await dispatchAndReport();
|
|
7531
|
+
return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
|
|
7532
|
+
} catch (err) {
|
|
7533
|
+
const message = err.message;
|
|
7534
|
+
summary.errorMsg = `sync aborted: ${message}`;
|
|
7535
|
+
finish("error");
|
|
7536
|
+
writeLastPushFailure(
|
|
7537
|
+
{ ingestId: "n/a", observedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
7538
|
+
`team push error: ${message}`
|
|
7539
|
+
);
|
|
7540
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
|
|
7222
7541
|
}
|
|
7223
|
-
entries.sort((a, b) => a.local_day.localeCompare(b.local_day));
|
|
7224
|
-
return entries;
|
|
7225
7542
|
}
|
|
7543
|
+
async function buildCyclePeaksForPush(planTier, loadCurve) {
|
|
7544
|
+
const validTiers = ["pro", "pro-max", "pro-max-20x", "custom"];
|
|
7545
|
+
const tier = validTiers.find((t) => t === planTier) ?? "pro-max-20x";
|
|
7546
|
+
const dump = await loadCurve(tier).catch(() => null);
|
|
7547
|
+
if (!dump || dump.curve.length === 0) return void 0;
|
|
7548
|
+
const HOUR = 36e5;
|
|
7549
|
+
const nowMs = Date.now();
|
|
7550
|
+
const peaksFor = (cycleKey, realKey, predKey, maxCycles) => {
|
|
7551
|
+
const TOLERANCE_MS = cycleKey === "cycle_end_7d" ? 12 * HOUR : 0;
|
|
7552
|
+
const byCycle = /* @__PURE__ */ new Map();
|
|
7553
|
+
for (const p of dump.curve) {
|
|
7554
|
+
const k = p[cycleKey];
|
|
7555
|
+
if (!k) continue;
|
|
7556
|
+
const ms = Date.parse(k);
|
|
7557
|
+
if (Number.isNaN(ms)) continue;
|
|
7558
|
+
const bucket = Math.round(ms / HOUR) * HOUR;
|
|
7559
|
+
const arr = byCycle.get(bucket) ?? [];
|
|
7560
|
+
arr.push(p);
|
|
7561
|
+
byCycle.set(bucket, arr);
|
|
7562
|
+
}
|
|
7563
|
+
const merged = [];
|
|
7564
|
+
for (const [endMs, points] of Array.from(byCycle.entries()).sort((a, b) => a[0] - b[0])) {
|
|
7565
|
+
const last = merged[merged.length - 1];
|
|
7566
|
+
if (last && endMs - last.endMs <= TOLERANCE_MS) {
|
|
7567
|
+
last.endMs = endMs;
|
|
7568
|
+
last.points.push(...points);
|
|
7569
|
+
} else {
|
|
7570
|
+
merged.push({ endMs, points: [...points] });
|
|
7571
|
+
}
|
|
7572
|
+
}
|
|
7573
|
+
const out = [];
|
|
7574
|
+
for (const { endMs, points } of merged) {
|
|
7575
|
+
let peak = 0;
|
|
7576
|
+
let source = "predicted";
|
|
7577
|
+
for (const p of points) {
|
|
7578
|
+
const r = p[realKey];
|
|
7579
|
+
if (typeof r === "number" && r > peak) {
|
|
7580
|
+
peak = r;
|
|
7581
|
+
source = "real";
|
|
7582
|
+
}
|
|
7583
|
+
const v = p[predKey] ?? 0;
|
|
7584
|
+
if (v > peak) {
|
|
7585
|
+
peak = v;
|
|
7586
|
+
source = "predicted";
|
|
7587
|
+
}
|
|
7588
|
+
}
|
|
7589
|
+
out.push({
|
|
7590
|
+
endsAt: new Date(endMs).toISOString(),
|
|
7591
|
+
peakPct: Math.round(peak * 10) / 10,
|
|
7592
|
+
source,
|
|
7593
|
+
current: endMs > nowMs
|
|
7594
|
+
});
|
|
7595
|
+
}
|
|
7596
|
+
return out.slice(-maxCycles);
|
|
7597
|
+
};
|
|
7598
|
+
return {
|
|
7599
|
+
// 7d cycle history cap (26 ≈ six months) MUST stay in lock-step with two
|
|
7600
|
+
// downstream sites or the strip silently truncates: the server read
|
|
7601
|
+
// (loadMembership7dCyclePeaks maxCyclesPerMember in plan-queries.ts) and
|
|
7602
|
+
// the renderer (CyclePeaksStrip maxBars in member-plan-block.tsx). The 5h
|
|
7603
|
+
// cap (24) is independent — leave it.
|
|
7604
|
+
fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
|
|
7605
|
+
sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 26)
|
|
7606
|
+
};
|
|
7607
|
+
}
|
|
7608
|
+
|
|
7609
|
+
// src/perception/worker.ts
|
|
7610
|
+
import { statSync as statSync5 } from "node:fs";
|
|
7611
|
+
init_fs();
|
|
7226
7612
|
|
|
7227
7613
|
// src/perception/state.ts
|
|
7228
7614
|
init_fs();
|
|
7229
|
-
import { existsSync as existsSync7, readFileSync as
|
|
7230
|
-
import { dirname as
|
|
7615
|
+
import { existsSync as existsSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, renameSync as renameSync3 } from "node:fs";
|
|
7616
|
+
import { dirname as dirname8 } from "node:path";
|
|
7231
7617
|
var STALE_MS = 15 * 60 * 1e3;
|
|
7232
7618
|
var pathCached = null;
|
|
7233
7619
|
function statePath() {
|
|
@@ -7245,12 +7631,12 @@ function readState() {
|
|
|
7245
7631
|
file_checkpoints: {}
|
|
7246
7632
|
};
|
|
7247
7633
|
}
|
|
7248
|
-
const raw =
|
|
7634
|
+
const raw = readFileSync11(p, "utf8");
|
|
7249
7635
|
return JSON.parse(raw);
|
|
7250
7636
|
}
|
|
7251
7637
|
function writeState(s) {
|
|
7252
7638
|
const p = statePath();
|
|
7253
|
-
mkdirSync8(
|
|
7639
|
+
mkdirSync8(dirname8(p), { recursive: true });
|
|
7254
7640
|
const tmp = `${p}.tmp`;
|
|
7255
7641
|
writeFileSync7(tmp, JSON.stringify(s, null, 2), "utf8");
|
|
7256
7642
|
renameSync3(tmp, p);
|
|
@@ -7306,12 +7692,6 @@ async function listAllSessionJsonls(root) {
|
|
|
7306
7692
|
function log(msg) {
|
|
7307
7693
|
console.error(`[perception] ${msg}`);
|
|
7308
7694
|
}
|
|
7309
|
-
function decodeProjectDirName(projectDir) {
|
|
7310
|
-
if (projectDir.startsWith("-")) {
|
|
7311
|
-
return "/" + projectDir.slice(1).replace(/-/g, "/");
|
|
7312
|
-
}
|
|
7313
|
-
return projectDir.replace(/-/g, "/");
|
|
7314
|
-
}
|
|
7315
7695
|
async function runPerceptionSweep(opts = {}) {
|
|
7316
7696
|
const state = readState();
|
|
7317
7697
|
if (state.sweep_in_progress && !isSweepStale()) {
|
|
@@ -7325,33 +7705,12 @@ async function runPerceptionSweep(opts = {}) {
|
|
|
7325
7705
|
const files = await listAllSessionJsonls(opts.projectsRoot);
|
|
7326
7706
|
for (const f of files) {
|
|
7327
7707
|
try {
|
|
7328
|
-
const stat =
|
|
7708
|
+
const stat = statSync5(f);
|
|
7329
7709
|
const prev = state.file_checkpoints[f];
|
|
7330
7710
|
if (prev && prev.byte_offset >= stat.size) continue;
|
|
7331
|
-
const
|
|
7332
|
-
|
|
7333
|
-
try {
|
|
7334
|
-
return JSON.parse(l);
|
|
7335
|
-
} catch {
|
|
7336
|
-
return null;
|
|
7337
|
-
}
|
|
7338
|
-
}).filter((x) => x !== null);
|
|
7339
|
-
if (rawLines.length === 0) continue;
|
|
7340
|
-
const { meta, events } = parseTranscript(rawLines);
|
|
7341
|
-
const fileName = basename(f);
|
|
7342
|
-
const projectDir = basename(dirname8(f));
|
|
7343
|
-
const sessionId = fileName.replace(/\.jsonl$/, "");
|
|
7344
|
-
const sd = {
|
|
7345
|
-
...meta,
|
|
7346
|
-
id: sessionId,
|
|
7347
|
-
filePath: f,
|
|
7348
|
-
projectDir,
|
|
7349
|
-
projectName: meta.cwd ?? decodeProjectDirName(projectDir),
|
|
7350
|
-
events
|
|
7351
|
-
};
|
|
7352
|
-
const built = buildEntries(sd);
|
|
7711
|
+
const built = buildEntriesForFile(f);
|
|
7712
|
+
if (built === null) continue;
|
|
7353
7713
|
for (const e of built) {
|
|
7354
|
-
e.source_checkpoint.byte_offset = stat.size;
|
|
7355
7714
|
writeEntryPreservingEnrichment(e);
|
|
7356
7715
|
entries++;
|
|
7357
7716
|
}
|
|
@@ -7378,7 +7737,7 @@ async function runPerceptionSweep(opts = {}) {
|
|
|
7378
7737
|
const built = buildEntries(detail);
|
|
7379
7738
|
for (const e of built) {
|
|
7380
7739
|
try {
|
|
7381
|
-
const stat =
|
|
7740
|
+
const stat = statSync5(meta.filePath);
|
|
7382
7741
|
e.source_checkpoint.byte_offset = stat.size;
|
|
7383
7742
|
} catch {
|
|
7384
7743
|
}
|
|
@@ -11535,12 +11894,12 @@ var ENRICHMENT_SYSTEM_PROMPT = SYSTEM_PROMPT;
|
|
|
11535
11894
|
// ../entries/dist/llm-runner.js
|
|
11536
11895
|
init_fs();
|
|
11537
11896
|
import { spawn } from "node:child_process";
|
|
11538
|
-
import { mkdirSync as mkdirSync10, appendFileSync as
|
|
11897
|
+
import { mkdirSync as mkdirSync10, appendFileSync as appendFileSync4, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "node:fs";
|
|
11539
11898
|
import { join as join10 } from "node:path";
|
|
11540
11899
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
11541
11900
|
|
|
11542
11901
|
// ../entries/dist/tmux-runner.js
|
|
11543
|
-
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, existsSync as existsSync8, readdirSync as readdirSync5, realpathSync, readFileSync as
|
|
11902
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, existsSync as existsSync8, readdirSync as readdirSync5, realpathSync, readFileSync as readFileSync12, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
|
|
11544
11903
|
import { join as join9 } from "node:path";
|
|
11545
11904
|
import { homedir as homedir4 } from "node:os";
|
|
11546
11905
|
import { spawnSync } from "node:child_process";
|
|
@@ -11579,7 +11938,7 @@ function ensureRuntimeCwdTrusted(cwd) {
|
|
|
11579
11938
|
const cfgPath = join9(homedir4(), ".claude.json");
|
|
11580
11939
|
let cfg;
|
|
11581
11940
|
try {
|
|
11582
|
-
cfg = JSON.parse(
|
|
11941
|
+
cfg = JSON.parse(readFileSync12(cfgPath, "utf8"));
|
|
11583
11942
|
} catch {
|
|
11584
11943
|
return;
|
|
11585
11944
|
}
|
|
@@ -11731,7 +12090,7 @@ function snapshotAllTranscripts() {
|
|
|
11731
12090
|
function readJsonlCwd(path7, maxLines = 8) {
|
|
11732
12091
|
let raw;
|
|
11733
12092
|
try {
|
|
11734
|
-
raw =
|
|
12093
|
+
raw = readFileSync12(path7, "utf8");
|
|
11735
12094
|
} catch {
|
|
11736
12095
|
return void 0;
|
|
11737
12096
|
}
|
|
@@ -11788,7 +12147,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
|
|
|
11788
12147
|
function parseTranscript2(path7, defaultModel) {
|
|
11789
12148
|
let raw;
|
|
11790
12149
|
try {
|
|
11791
|
-
raw =
|
|
12150
|
+
raw = readFileSync12(path7, "utf8");
|
|
11792
12151
|
} catch {
|
|
11793
12152
|
return { assembled: "", inputTokens: 0, outputTokens: 0, modelUsed: defaultModel, done: false };
|
|
11794
12153
|
}
|
|
@@ -11896,7 +12255,7 @@ function tracePath(runId) {
|
|
|
11896
12255
|
}
|
|
11897
12256
|
function safeAppend(path7, line) {
|
|
11898
12257
|
try {
|
|
11899
|
-
|
|
12258
|
+
appendFileSync4(path7, line);
|
|
11900
12259
|
} catch {
|
|
11901
12260
|
}
|
|
11902
12261
|
}
|
|
@@ -12291,7 +12650,7 @@ function applyEnrichmentError(entry, errorMessage, model, costUsd, generatedAt,
|
|
|
12291
12650
|
|
|
12292
12651
|
// ../entries/dist/budget.js
|
|
12293
12652
|
init_fs();
|
|
12294
|
-
import { appendFileSync as
|
|
12653
|
+
import { appendFileSync as appendFileSync5, readFileSync as readFileSync13, existsSync as existsSync10, mkdirSync as mkdirSync11 } from "node:fs";
|
|
12295
12654
|
import { dirname as dirname9 } from "node:path";
|
|
12296
12655
|
var spendPathCached = null;
|
|
12297
12656
|
function spendPath() {
|
|
@@ -12303,7 +12662,7 @@ function spendPath() {
|
|
|
12303
12662
|
function appendSpend(record) {
|
|
12304
12663
|
const p = spendPath();
|
|
12305
12664
|
mkdirSync11(dirname9(p), { recursive: true });
|
|
12306
|
-
|
|
12665
|
+
appendFileSync5(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
|
|
12307
12666
|
}
|
|
12308
12667
|
function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
|
|
12309
12668
|
const p = spendPath();
|
|
@@ -12312,7 +12671,7 @@ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
|
|
|
12312
12671
|
const year = now.getUTCFullYear();
|
|
12313
12672
|
const month = now.getUTCMonth();
|
|
12314
12673
|
let total = 0;
|
|
12315
|
-
const raw =
|
|
12674
|
+
const raw = readFileSync13(p, "utf8");
|
|
12316
12675
|
for (const line of raw.split("\n")) {
|
|
12317
12676
|
if (!line)
|
|
12318
12677
|
continue;
|
|
@@ -12334,7 +12693,7 @@ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
|
|
|
12334
12693
|
|
|
12335
12694
|
// ../entries/dist/pipeline-lock.js
|
|
12336
12695
|
init_fs();
|
|
12337
|
-
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync11, readFileSync as
|
|
12696
|
+
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync11, readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
|
|
12338
12697
|
var STALE_MS2 = 60 * 1e3;
|
|
12339
12698
|
var HEARTBEAT_MS = 30 * 1e3;
|
|
12340
12699
|
var lockPathCached = null;
|
|
@@ -12383,10 +12742,10 @@ function interactiveLockFresh(nowMs) {
|
|
|
12383
12742
|
if (!existsSync11(p))
|
|
12384
12743
|
return false;
|
|
12385
12744
|
try {
|
|
12386
|
-
const mtime =
|
|
12745
|
+
const mtime = statSync6(p).mtimeMs;
|
|
12387
12746
|
if (nowMs - mtime > STALE_MS2)
|
|
12388
12747
|
return false;
|
|
12389
|
-
const pid = Number(
|
|
12748
|
+
const pid = Number(readFileSync14(p, "utf8").trim());
|
|
12390
12749
|
if (!Number.isFinite(pid) || pid <= 0)
|
|
12391
12750
|
return false;
|
|
12392
12751
|
if (!pidAlive(pid))
|
|
@@ -12401,7 +12760,7 @@ function interactiveLockFresh(nowMs) {
|
|
|
12401
12760
|
var THIRTY_MIN_MS = 30 * 60 * 1e3;
|
|
12402
12761
|
|
|
12403
12762
|
// ../entries/dist/settings.js
|
|
12404
|
-
import { readFileSync as
|
|
12763
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, renameSync as renameSync5, chmodSync as chmodSync2, mkdirSync as mkdirSync12, existsSync as existsSync12 } from "node:fs";
|
|
12405
12764
|
init_fs();
|
|
12406
12765
|
var DEFAULT_SETTINGS = {
|
|
12407
12766
|
ai_features: {
|
|
@@ -12436,7 +12795,7 @@ function readSettings() {
|
|
|
12436
12795
|
if (!existsSync12(p))
|
|
12437
12796
|
return DEFAULT_SETTINGS;
|
|
12438
12797
|
try {
|
|
12439
|
-
const raw =
|
|
12798
|
+
const raw = readFileSync15(p, "utf8");
|
|
12440
12799
|
return fromDisk(JSON.parse(raw));
|
|
12441
12800
|
} catch {
|
|
12442
12801
|
return DEFAULT_SETTINGS;
|
|
@@ -15053,8 +15412,8 @@ function toLocalDay2(ms) {
|
|
|
15053
15412
|
const day = String(d.getDate()).padStart(2, "0");
|
|
15054
15413
|
return `${y}-${m}-${day}`;
|
|
15055
15414
|
}
|
|
15056
|
-
function mondayOf(
|
|
15057
|
-
const d = /* @__PURE__ */ new Date(`${
|
|
15415
|
+
function mondayOf(localDay2) {
|
|
15416
|
+
const d = /* @__PURE__ */ new Date(`${localDay2}T00:00:00`);
|
|
15058
15417
|
const dow = d.getDay();
|
|
15059
15418
|
const offset = dow === 0 ? -6 : 1 - dow;
|
|
15060
15419
|
d.setDate(d.getDate() + offset);
|
|
@@ -15193,14 +15552,14 @@ async function getUpdateAvailability() {
|
|
|
15193
15552
|
if (isDevMode()) return null;
|
|
15194
15553
|
const latest = await fetchLatestVersion();
|
|
15195
15554
|
if (latest === null) return {
|
|
15196
|
-
current: "0.
|
|
15555
|
+
current: "0.14.0",
|
|
15197
15556
|
latest: null,
|
|
15198
15557
|
updateAvailable: false
|
|
15199
15558
|
};
|
|
15200
15559
|
return {
|
|
15201
|
-
current: "0.
|
|
15560
|
+
current: "0.14.0",
|
|
15202
15561
|
latest,
|
|
15203
|
-
updateAvailable: shouldUpdate("0.
|
|
15562
|
+
updateAvailable: shouldUpdate("0.14.0", latest)
|
|
15204
15563
|
};
|
|
15205
15564
|
}
|
|
15206
15565
|
|
|
@@ -15212,15 +15571,11 @@ var WATCHDOG_INTERVAL_MS = 5 * 1e3;
|
|
|
15212
15571
|
var AUTO_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
15213
15572
|
mkdirSync13(dirname10(USAGE_LOG3), { recursive: true });
|
|
15214
15573
|
function log2(level, message) {
|
|
15215
|
-
|
|
15216
|
-
`;
|
|
15217
|
-
try {
|
|
15218
|
-
appendFileSync5(DAEMON_LOG, line, "utf8");
|
|
15219
|
-
} catch {
|
|
15220
|
-
}
|
|
15574
|
+
appendDaemonLogLine(level, message);
|
|
15221
15575
|
}
|
|
15222
15576
|
var nextPollAtMs = 0;
|
|
15223
15577
|
var nextTeamSyncAtMs = 0;
|
|
15578
|
+
var firstTeamSync = true;
|
|
15224
15579
|
var currentIntervalMs = BASE_INTERVAL_MS;
|
|
15225
15580
|
var waitingForRefresh = false;
|
|
15226
15581
|
var updateCheckInFlight = false;
|
|
@@ -15252,7 +15607,7 @@ var perceptionHandle = setInterval(async () => {
|
|
|
15252
15607
|
}, PERCEPTION_INTERVAL_MS);
|
|
15253
15608
|
function readUpdateState() {
|
|
15254
15609
|
try {
|
|
15255
|
-
return JSON.parse(
|
|
15610
|
+
return JSON.parse(readFileSync16(UPDATE_STATE, "utf8"));
|
|
15256
15611
|
} catch {
|
|
15257
15612
|
return null;
|
|
15258
15613
|
}
|
|
@@ -15429,7 +15784,11 @@ async function runLoop() {
|
|
|
15429
15784
|
}
|
|
15430
15785
|
const teamNow = Date.now();
|
|
15431
15786
|
if (teamNow >= nextTeamSyncAtMs) {
|
|
15432
|
-
await runTeamSync(log2
|
|
15787
|
+
await runTeamSync(log2, void 0, {
|
|
15788
|
+
trigger: firstTeamSync ? "boot" : "auto",
|
|
15789
|
+
nextSyncMs: BASE_INTERVAL_MS
|
|
15790
|
+
});
|
|
15791
|
+
firstTeamSync = false;
|
|
15433
15792
|
nextTeamSyncAtMs = Date.now() + BASE_INTERVAL_MS;
|
|
15434
15793
|
}
|
|
15435
15794
|
if (Date.now() >= nextUpdateCheckAtMs) {
|