@synkro-sh/cli 1.7.94 → 1.7.95

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/dist/bootstrap.js CHANGED
@@ -147,7 +147,7 @@ function getIdentity() {
147
147
  if (cached2) return cached2;
148
148
  let cliVersion = "0.0.0";
149
149
  try {
150
- cliVersion = "1.7.94";
150
+ cliVersion = "1.7.95";
151
151
  } catch {
152
152
  }
153
153
  const creds = loadCredentialsIdentity();
@@ -6612,8 +6612,16 @@ async function dockerInstall(opts = {}) {
6612
6612
  ...process.env.SYNKRO_MAX_BATCH_SIZE ? ["-e", `SYNKRO_MAX_BATCH_SIZE=${process.env.SYNKRO_MAX_BATCH_SIZE}`] : [],
6613
6613
  // Full verifier prompt/response tracing is explicit opt-in because it contains source code.
6614
6614
  ...process.env.SYNKRO_VERIFY_TRACE === "1" ? ["-e", "SYNKRO_VERIFY_TRACE=1"] : [],
6615
- // Cursor grading model tunable like SYNKRO_MAX_BATCH_SIZE.
6616
- ...process.env.SYNKRO_CURSOR_MODEL ? ["-e", `SYNKRO_CURSOR_MODEL=${process.env.SYNKRO_CURSOR_MODEL}`] : [],
6615
+ // Explicit model overrides are preserved across local install/update for
6616
+ // every provider and isolated lane. The server reports the resolved values
6617
+ // in /healthz so operators can audit exactly what is spending tokens.
6618
+ ...[
6619
+ "SYNKRO_CLAUDE_MODEL",
6620
+ "SYNKRO_CURSOR_MODEL",
6621
+ "SYNKRO_CODEX_MODEL",
6622
+ "SYNKRO_CONDUCTOR_MODEL",
6623
+ "SYNKRO_ROUTE_MODEL"
6624
+ ].flatMap((key) => process.env[key] ? ["-e", `${key}=${process.env[key]}`] : []),
6617
6625
  // Fix-poll kill switch. Default ON in the image; a benchmark/headless run
6618
6626
  // (e.g. sec-code-bench) sets SYNKRO_FIX_POLL=0 so ask-mode violations skip the
6619
6627
  // interactive AskUserQuestion poll and fall through to generate-the-fix. Only
@@ -6871,7 +6879,7 @@ var init_dockerInstall = __esm({
6871
6879
  HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
6872
6880
  CONTAINER_NAME = resolveContainerName();
6873
6881
  defaultImageVersion = () => {
6874
- if (true) return "1.7.94";
6882
+ if (true) return "1.7.95";
6875
6883
  try {
6876
6884
  const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
6877
6885
  if (pkg.version) return pkg.version;
@@ -7564,6 +7572,94 @@ var init_codexTranscriptMessages = __esm({
7564
7572
  }
7565
7573
  });
7566
7574
 
7575
+ // cli/scanning/claudeTranscriptUsage.ts
7576
+ function tokenCount(value) {
7577
+ const parsed = Number(value);
7578
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
7579
+ }
7580
+ function isoDay(value, fallbackDay) {
7581
+ if (typeof value === "string") {
7582
+ const parsed = new Date(value);
7583
+ if (Number.isFinite(parsed.getTime())) return parsed.toISOString().slice(0, 10);
7584
+ }
7585
+ return fallbackDay;
7586
+ }
7587
+ function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), options = {}) {
7588
+ const seen = options.seenStableIds ?? /* @__PURE__ */ new Set();
7589
+ const rollups = /* @__PURE__ */ new Map();
7590
+ const usage = {
7591
+ input_tokens: 0,
7592
+ output_tokens: 0,
7593
+ cache_creation_input_tokens: 0,
7594
+ cache_read_input_tokens: 0
7595
+ };
7596
+ let model = "";
7597
+ let turns = 0;
7598
+ let lineIndex = 0;
7599
+ for (const line of transcript.split("\n")) {
7600
+ lineIndex += 1;
7601
+ const text = line.trim();
7602
+ if (!text) continue;
7603
+ try {
7604
+ const entry = JSON.parse(text);
7605
+ const message = entry?.message;
7606
+ if (message?.role !== "assistant" || !message.usage || typeof message.usage !== "object") {
7607
+ continue;
7608
+ }
7609
+ const stableId = typeof entry.uuid === "string" && entry.uuid ? `uuid:${entry.uuid}` : typeof message.id === "string" && message.id ? `message:${message.id}:${String(entry.timestamp || "")}` : `${options.sourceId || "transcript"}:line:${lineIndex}`;
7610
+ if (seen.has(stableId)) continue;
7611
+ seen.add(stableId);
7612
+ const counts = {
7613
+ input_tokens: tokenCount(message.usage.input_tokens),
7614
+ output_tokens: tokenCount(message.usage.output_tokens),
7615
+ cache_creation_input_tokens: tokenCount(message.usage.cache_creation_input_tokens),
7616
+ cache_read_input_tokens: tokenCount(message.usage.cache_read_input_tokens)
7617
+ };
7618
+ const countTotal = counts.input_tokens + counts.output_tokens + counts.cache_creation_input_tokens + counts.cache_read_input_tokens;
7619
+ if (countTotal === 0) continue;
7620
+ const entryModel = typeof message.model === "string" && message.model ? message.model : "unknown";
7621
+ if (entryModel !== "<synthetic>") model = entryModel;
7622
+ const day = isoDay(entry.timestamp, fallbackDay);
7623
+ const key = `${day}\0${entryModel}`;
7624
+ const row = rollups.get(key) ?? {
7625
+ day,
7626
+ model: entryModel,
7627
+ turns: 0,
7628
+ input_tokens: 0,
7629
+ output_tokens: 0,
7630
+ cache_creation_input_tokens: 0,
7631
+ cache_read_input_tokens: 0
7632
+ };
7633
+ row.turns += 1;
7634
+ row.input_tokens += counts.input_tokens;
7635
+ row.output_tokens += counts.output_tokens;
7636
+ row.cache_creation_input_tokens += counts.cache_creation_input_tokens;
7637
+ row.cache_read_input_tokens += counts.cache_read_input_tokens;
7638
+ rollups.set(key, row);
7639
+ turns += 1;
7640
+ usage.input_tokens += counts.input_tokens;
7641
+ usage.output_tokens += counts.output_tokens;
7642
+ usage.cache_creation_input_tokens += counts.cache_creation_input_tokens;
7643
+ usage.cache_read_input_tokens += counts.cache_read_input_tokens;
7644
+ } catch {
7645
+ }
7646
+ }
7647
+ if (turns === 0) return null;
7648
+ return {
7649
+ usage,
7650
+ model: model || "unknown",
7651
+ rollups: [...rollups.values()].sort(
7652
+ (a, b) => a.day.localeCompare(b.day) || a.model.localeCompare(b.model)
7653
+ ),
7654
+ turns
7655
+ };
7656
+ }
7657
+ var init_claudeTranscriptUsage = __esm({
7658
+ "cli/scanning/claudeTranscriptUsage.ts"() {
7659
+ "use strict";
7660
+ }
7661
+ });
7662
+
7567
7663
  // cli/commands/install.ts
7568
7664
  var install_exports = {};
7569
7665
  __export(install_exports, {
@@ -7581,7 +7677,7 @@ __export(install_exports, {
7581
7677
  });
7582
7678
  import { existsSync as existsSync22, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17, chmodSync as chmodSync5, readFileSync as readFileSync21, readdirSync as readdirSync4, unlinkSync as unlinkSync7, statSync as statSync2 } from "fs";
7583
7679
  import { homedir as homedir21 } from "os";
7584
- import { join as join19, isAbsolute, resolve as resolve4 } from "path";
7680
+ import { basename, join as join19, isAbsolute, resolve as resolve4, sep } from "path";
7585
7681
  import { execSync as execSync4, spawn as spawn5 } from "child_process";
7586
7682
  import { createInterface as createInterface2 } from "readline";
7587
7683
  import { createHash as createHash4 } from "crypto";
@@ -7840,7 +7936,7 @@ function writeConfigEnv(opts) {
7840
7936
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
7841
7937
  `SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
7842
7938
  `SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
7843
- `SYNKRO_VERSION=${shellQuoteSingle2("1.7.94")}`
7939
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.7.95")}`
7844
7940
  ];
7845
7941
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
7846
7942
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
@@ -8018,6 +8114,11 @@ async function provisionCloudContainer(opts) {
8018
8114
  cursor_workers: cursorWorkers,
8019
8115
  codex_workers: codexWorkers,
8020
8116
  conductor_provider: selectedKind,
8117
+ claude_model: process.env.SYNKRO_CLAUDE_MODEL || "",
8118
+ cursor_model: process.env.SYNKRO_CURSOR_MODEL || "",
8119
+ codex_model: process.env.SYNKRO_CODEX_MODEL || "",
8120
+ conductor_model: process.env.SYNKRO_CONDUCTOR_MODEL || "",
8121
+ route_model: process.env.SYNKRO_ROUTE_MODEL || "",
8021
8122
  cursor_api_key: cursorApiKey,
8022
8123
  // never logged; gateway stores it as the org secret
8023
8124
  connected_repo: repo,
@@ -8582,7 +8683,7 @@ async function installCommand(opts = {}) {
8582
8683
  await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
8583
8684
  emit("install", {
8584
8685
  phase: "started",
8585
- cli_version_to: "1.7.94",
8686
+ cli_version_to: "1.7.95",
8586
8687
  agents_detected: agents.map((a) => a.kind),
8587
8688
  with_github: false,
8588
8689
  with_local_cc: false,
@@ -9649,8 +9750,8 @@ function ensureReachabilityGitHook() {
9649
9750
  }
9650
9751
  return "updated";
9651
9752
  }
9652
- const sep = cur.endsWith("\n") ? "" : "\n";
9653
- writeFileSync17(hookPath, cur + sep + "\n" + block + "\n");
9753
+ const sep3 = cur.endsWith("\n") ? "" : "\n";
9754
+ writeFileSync17(hookPath, cur + sep3 + "\n" + block + "\n");
9654
9755
  try {
9655
9756
  chmodSync5(hookPath, 493);
9656
9757
  } catch {
@@ -9677,10 +9778,33 @@ function detectGitRepo2() {
9677
9778
  }
9678
9779
  function getClaudeProjectsFolder() {
9679
9780
  const cwd = process.cwd();
9680
- const sanitized = "-" + cwd.replace(/\//g, "-");
9781
+ const sanitized = cwd.replace(/\//g, "-");
9681
9782
  const projectsDir = join19(homedir21(), ".claude", "projects", sanitized);
9682
9783
  return existsSync22(projectsDir) ? projectsDir : null;
9683
9784
  }
9785
+ function getClaudeTranscriptFileEntries(projectsDir) {
9786
+ let relativeFiles = [];
9787
+ try {
9788
+ relativeFiles = readdirSync4(projectsDir, { recursive: true, encoding: "utf-8" });
9789
+ } catch {
9790
+ return [];
9791
+ }
9792
+ return relativeFiles.filter((file) => file.endsWith(".jsonl")).map((file) => {
9793
+ const parts = file.split(sep);
9794
+ const subagentsIndex = parts.lastIndexOf("subagents");
9795
+ if (subagentsIndex > 0) {
9796
+ return {
9797
+ filePath: join19(projectsDir, file),
9798
+ sessionId: basename(file, ".jsonl"),
9799
+ parentSessionId: parts[subagentsIndex - 1]
9800
+ };
9801
+ }
9802
+ return {
9803
+ filePath: join19(projectsDir, file),
9804
+ sessionId: basename(file, ".jsonl")
9805
+ };
9806
+ });
9807
+ }
9684
9808
  function extractSessionInsights(projectsDir) {
9685
9809
  const insights = [];
9686
9810
  const files = readdirSync4(projectsDir).filter((f) => f.endsWith(".jsonl"));
@@ -9980,23 +10104,40 @@ function parseTranscriptFile(filePath) {
9980
10104
  async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
9981
10105
  const projectsDir = getClaudeProjectsFolder();
9982
10106
  if (!projectsDir) return { sessions: 0, messages: 0 };
9983
- const files = readdirSync4(projectsDir).filter((f) => f.endsWith(".jsonl"));
10107
+ const files = getClaudeTranscriptFileEntries(projectsDir);
9984
10108
  if (files.length === 0) return { sessions: 0, messages: 0 };
9985
10109
  console.log(` Found ${files.length} CC session transcripts, importing + embedding...`);
9986
10110
  let totalSessions = 0;
9987
10111
  let totalMessages = 0;
10112
+ const seenStableIds = /* @__PURE__ */ new Set();
9988
10113
  for (let i = 0; i < files.length; i++) {
9989
10114
  const file = files[i];
9990
- const sessionId = file.replace(".jsonl", "");
9991
- const filePath = join19(projectsDir, file);
10115
+ const sessionId = file.sessionId;
10116
+ const filePath = file.filePath;
9992
10117
  try {
10118
+ const transcript = readFileSync21(filePath, "utf-8");
10119
+ const transcriptUsage = parseClaudeTranscriptUsage(
10120
+ transcript,
10121
+ statSync2(filePath).mtime.toISOString().slice(0, 10),
10122
+ { seenStableIds, sourceId: file.parentSessionId ? `${file.parentSessionId}:subagent:${sessionId}` : sessionId }
10123
+ );
9993
10124
  const allMessages = parseTranscriptFile(filePath);
9994
10125
  const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
9995
10126
  if (messages.length === 0) continue;
9996
10127
  const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/conversation-sync`, {
9997
10128
  method: "POST",
9998
10129
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpToken}` },
9999
- body: JSON.stringify({ session_id: sessionId, repo, messages }),
10130
+ body: JSON.stringify({
10131
+ session_id: sessionId,
10132
+ parent_session_id: file.parentSessionId,
10133
+ repo,
10134
+ messages,
10135
+ session_usage: transcriptUsage?.usage,
10136
+ usage_rollups: transcriptUsage?.rollups ?? [],
10137
+ model: transcriptUsage?.model,
10138
+ harness: "claude-code",
10139
+ usage_cumulative: true
10140
+ }),
10000
10141
  signal: AbortSignal.timeout(15e3)
10001
10142
  });
10002
10143
  if (resp.ok) {
@@ -10010,9 +10151,10 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
10010
10151
  process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
10011
10152
  }
10012
10153
  try {
10013
- const content = readFileSync21(join19(projectsDir, file), "utf-8");
10154
+ const content = readFileSync21(filePath, "utf-8");
10014
10155
  const lineCount = content.split("\n").filter(Boolean).length;
10015
- writeFileSync17(join19(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
10156
+ const offsetId = file.parentSessionId ? `${file.parentSessionId}_${sessionId}` : sessionId;
10157
+ writeFileSync17(join19(OFFSETS_DIR, offsetId), String(lineCount), "utf-8");
10016
10158
  } catch {
10017
10159
  }
10018
10160
  }
@@ -10022,23 +10164,39 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
10022
10164
  async function syncTranscriptsBulk(gatewayUrl, token, repo) {
10023
10165
  const projectsDir = getClaudeProjectsFolder();
10024
10166
  if (!projectsDir) return { sessions: 0, messages: 0 };
10025
- const files = readdirSync4(projectsDir).filter((f) => f.endsWith(".jsonl"));
10167
+ const files = getClaudeTranscriptFileEntries(projectsDir);
10026
10168
  if (files.length === 0) return { sessions: 0, messages: 0 };
10027
10169
  console.log(`Found ${files.length} CC session transcripts, syncing...`);
10028
10170
  const maxMessagesPerSession = 500;
10029
10171
  let totalSessions = 0;
10030
10172
  let totalMessages = 0;
10173
+ const seenStableIds = /* @__PURE__ */ new Set();
10031
10174
  for (let i = 0; i < files.length; i += 5) {
10032
10175
  const batch = files.slice(i, i + 5);
10033
10176
  const sessions = [];
10034
10177
  for (const file of batch) {
10035
- const sessionId = file.replace(".jsonl", "");
10036
- const filePath = join19(projectsDir, file);
10178
+ const sessionId = file.sessionId;
10179
+ const filePath = file.filePath;
10037
10180
  try {
10181
+ const transcript = readFileSync21(filePath, "utf-8");
10182
+ const transcriptUsage = parseClaudeTranscriptUsage(
10183
+ transcript,
10184
+ statSync2(filePath).mtime.toISOString().slice(0, 10),
10185
+ { seenStableIds, sourceId: file.parentSessionId ? `${file.parentSessionId}:subagent:${sessionId}` : sessionId }
10186
+ );
10038
10187
  const allMessages = parseTranscriptFile(filePath);
10039
10188
  const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
10040
10189
  if (messages.length > 0) {
10041
- sessions.push({ cc_session_id: sessionId, messages });
10190
+ sessions.push({
10191
+ cc_session_id: sessionId,
10192
+ parent_session_id: file.parentSessionId,
10193
+ messages,
10194
+ model: transcriptUsage?.model,
10195
+ session_usage: transcriptUsage?.usage,
10196
+ usage_rollups: transcriptUsage?.rollups ?? [],
10197
+ harness: "claude-code",
10198
+ usage_cumulative: true
10199
+ });
10042
10200
  }
10043
10201
  } catch {
10044
10202
  }
@@ -10061,12 +10219,13 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
10061
10219
  } catch {
10062
10220
  }
10063
10221
  for (const file of batch) {
10064
- const sessionId = file.replace(".jsonl", "");
10065
- const filePath = join19(projectsDir, file);
10222
+ const sessionId = file.sessionId;
10223
+ const filePath = file.filePath;
10066
10224
  try {
10067
10225
  const content = readFileSync21(filePath, "utf-8");
10068
10226
  const lineCount = content.split("\n").filter(Boolean).length;
10069
- writeFileSync17(join19(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
10227
+ const offsetId = file.parentSessionId ? `${file.parentSessionId}_${sessionId}` : sessionId;
10228
+ writeFileSync17(join19(OFFSETS_DIR, offsetId), String(lineCount), "utf-8");
10070
10229
  } catch {
10071
10230
  }
10072
10231
  }
@@ -10149,6 +10308,7 @@ var init_install = __esm({
10149
10308
  init_graderSmoke();
10150
10309
  init_codexTranscriptUsage();
10151
10310
  init_codexTranscriptMessages();
10311
+ init_claudeTranscriptUsage();
10152
10312
  SYNKRO_DIR11 = join19(homedir21(), ".synkro");
10153
10313
  HOOKS_DIR = join19(SYNKRO_DIR11, "hooks");
10154
10314
  CONFIG_PATH4 = join19(SYNKRO_DIR11, "config.env");
@@ -12745,9 +12905,9 @@ var import_exports = {};
12745
12905
  __export(import_exports, {
12746
12906
  importCommand: () => importCommand
12747
12907
  });
12748
- import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6 } from "fs";
12908
+ import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
12749
12909
  import { homedir as homedir28 } from "os";
12750
- import { join as join27 } from "path";
12910
+ import { basename as basename2, join as join27, sep as sep2 } from "path";
12751
12911
  import { execSync as execSync6 } from "child_process";
12752
12912
  import { createInterface as createInterface4 } from "readline";
12753
12913
  function readMcpJwt() {
@@ -12775,6 +12935,23 @@ function projectsFolder() {
12775
12935
  const dir = join27(homedir28(), ".claude", "projects", sanitized);
12776
12936
  return existsSync29(dir) ? dir : null;
12777
12937
  }
12938
+ function transcriptFiles(projectsDir) {
12939
+ let relativeFiles = [];
12940
+ try {
12941
+ relativeFiles = readdirSync6(projectsDir, { recursive: true, encoding: "utf-8" });
12942
+ } catch {
12943
+ return [];
12944
+ }
12945
+ return relativeFiles.filter((file) => file.endsWith(".jsonl")).map((file) => {
12946
+ const parts = file.split(sep2);
12947
+ const subagentsIndex = parts.lastIndexOf("subagents");
12948
+ return {
12949
+ filePath: join27(projectsDir, file),
12950
+ sessionId: basename2(file, ".jsonl"),
12951
+ parentSessionId: subagentsIndex > 0 ? parts[subagentsIndex - 1] : void 0
12952
+ };
12953
+ });
12954
+ }
12778
12955
  function repoName() {
12779
12956
  try {
12780
12957
  const url = execSync6("git config --get remote.origin.url", { encoding: "utf-8" }).trim();
@@ -12811,8 +12988,18 @@ function extractToolResultText(content, e) {
12811
12988
  }
12812
12989
  return t;
12813
12990
  }
12814
- function parseSession(filePath, sessionId) {
12815
- const lines = readFileSync27(filePath, "utf-8").split("\n").filter(Boolean);
12991
+ function parseSession(file, seenStableIds) {
12992
+ const { filePath, sessionId, parentSessionId } = file;
12993
+ const transcript = readFileSync27(filePath, "utf-8");
12994
+ const lines = transcript.split("\n").filter(Boolean);
12995
+ const transcriptUsage = parseClaudeTranscriptUsage(
12996
+ transcript,
12997
+ statSync4(filePath).mtime.toISOString().slice(0, 10),
12998
+ {
12999
+ seenStableIds,
13000
+ sourceId: parentSessionId ? `${parentSessionId}:subagent:${sessionId}` : sessionId
13001
+ }
13002
+ );
12816
13003
  const messages = [];
12817
13004
  const actions = [];
12818
13005
  let step = 0;
@@ -12861,7 +13048,17 @@ function parseSession(filePath, sessionId) {
12861
13048
  }
12862
13049
  messages.push(msg);
12863
13050
  }
12864
- return { cc_session_id: sessionId, messages, actions };
13051
+ return {
13052
+ cc_session_id: sessionId,
13053
+ parent_session_id: parentSessionId,
13054
+ messages,
13055
+ actions,
13056
+ model: transcriptUsage?.model,
13057
+ session_usage: transcriptUsage?.usage,
13058
+ usage_rollups: transcriptUsage?.rollups ?? [],
13059
+ harness: "claude-code",
13060
+ usage_cumulative: true
13061
+ };
12865
13062
  }
12866
13063
  function ask2(q) {
12867
13064
  const rl = createInterface4({ input: process.stdin, output: process.stdout });
@@ -12879,7 +13076,7 @@ async function importCommand() {
12879
13076
  console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
12880
13077
  return;
12881
13078
  }
12882
- const files = readdirSync6(dir).filter((f) => f.endsWith(".jsonl"));
13079
+ const files = transcriptFiles(dir);
12883
13080
  if (!files.length) {
12884
13081
  console.log("No sessions to import.");
12885
13082
  return;
@@ -12892,7 +13089,8 @@ async function importCommand() {
12892
13089
  return;
12893
13090
  }
12894
13091
  }
12895
- const sessions = files.map((f) => parseSession(join27(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
13092
+ const seenStableIds = /* @__PURE__ */ new Set();
13093
+ const sessions = files.map((file) => parseSession(file, seenStableIds)).filter((s) => s.messages.length > 0);
12896
13094
  const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
12897
13095
  let ok = 0, fail = 0;
12898
13096
  if (isCloud) {
@@ -12938,7 +13136,19 @@ async function importCommand() {
12938
13136
  const r = await fetch(`http://127.0.0.1:${port}/api/ingest`, {
12939
13137
  method: "POST",
12940
13138
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpJwt2}` },
12941
- body: JSON.stringify({ capture_type: "transcript_sync", session_id: s.cc_session_id, repo, messages: s.messages, actions: s.actions }),
13139
+ body: JSON.stringify({
13140
+ capture_type: "transcript_sync",
13141
+ session_id: s.cc_session_id,
13142
+ parent_session_id: s.parent_session_id,
13143
+ repo,
13144
+ messages: s.messages,
13145
+ actions: s.actions,
13146
+ model: s.model,
13147
+ session_usage: s.session_usage,
13148
+ usage_rollups: s.usage_rollups,
13149
+ harness: s.harness,
13150
+ usage_cumulative: true
13151
+ }),
12942
13152
  // A full session can carry thousands of turns — 15s timed out mid-import.
12943
13153
  signal: AbortSignal.timeout(12e4)
12944
13154
  });
@@ -12967,6 +13177,7 @@ var init_import = __esm({
12967
13177
  "cli/commands/import.ts"() {
12968
13178
  "use strict";
12969
13179
  init_stub();
13180
+ init_claudeTranscriptUsage();
12970
13181
  CONFIG_PATH7 = join27(homedir28(), ".synkro", "config.env");
12971
13182
  }
12972
13183
  });
@@ -14339,7 +14550,7 @@ var subArgs = args.slice(1);
14339
14550
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
14340
14551
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
14341
14552
  function printVersion() {
14342
- console.log("1.7.94");
14553
+ console.log("1.7.95");
14343
14554
  }
14344
14555
  function printHelp2() {
14345
14556
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents