@synkro-sh/cli 1.6.87 → 1.6.89

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
@@ -2143,10 +2143,12 @@ export function appendSessionAction(sessionId: string, entry: SessionAction): vo
2143
2143
  appendFileSync(logPath, JSON.stringify(entry) + '\\n', 'utf-8');
2144
2144
  } catch {}
2145
2145
 
2146
- // Cloud: ship the action to the API transcript-sync (org from login JWT) with an
2147
- // explicit step so streamed single-action sends stay idempotent + ordered.
2146
+ // Cloud: stream the action to the session_actions hypertable (salience tagged
2147
+ // server-side, indexed) so the grade path pulls a BOUNDED indexed slice instead
2148
+ // of re-reading a multi-GB log \u2014 what lets us scale to million-tool-call
2149
+ // sessions. Fire-and-forget, idempotent by (session, step).
2148
2150
  if (deployIsCloud()) {
2149
- void syncTranscriptCloud(sessionId, '', [], [{ step, tool: entry.tool, summary: entry.summary, file: entry.file, outcome: entry.outcome }]);
2151
+ shipCloud(loadJwt(), '/api/v1/cli/session-action', { session_id: sessionId, step, tool: entry.tool, summary: entry.summary, file: entry.file, outcome: entry.outcome });
2150
2152
  return;
2151
2153
  }
2152
2154
  const mcpPort = process.env.SYNKRO_MCP_PORT || '18931';
@@ -2274,6 +2276,39 @@ export function compressSessionLog(actions: SessionAction[], rules?: Rule[]): st
2274
2276
  return 'SESSION HISTORY (' + total + ' actions):\\n' + lines.join('\\n');
2275
2277
  }
2276
2278
 
2279
+ // Cloud: fetch the BOUNDED, indexed session context from Timescale (salient +
2280
+ // recent + complete summary) \u2014 flat cost at any session length. Returns null if
2281
+ // the session isn't streamed yet (brand-new / streaming lagging) so the caller
2282
+ // falls back to the local path.
2283
+ async function fetchSessionContext(sessionId: string): Promise<string | null> {
2284
+ const jwt = loadJwt();
2285
+ if (!jwt) return null;
2286
+ try {
2287
+ const r = await fetch(GATEWAY_URL + '/api/v1/hook/session-context', {
2288
+ method: 'POST',
2289
+ headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
2290
+ body: JSON.stringify({ session_id: sessionId }),
2291
+ signal: AbortSignal.timeout(3000),
2292
+ });
2293
+ if (!r.ok) return null;
2294
+ const d = await r.json() as { context?: string };
2295
+ return (d && typeof d.context === 'string' && d.context) ? d.context : null;
2296
+ } catch {
2297
+ return null;
2298
+ }
2299
+ }
2300
+
2301
+ // Grade-time session log. CLOUD \u2192 Timescale-backed bounded context (this is the
2302
+ // million-tool-call path: no full-history read); falls back to the local
2303
+ // retrieval compressor only if the session isn't streamed yet. LOCAL \u2192 the
2304
+ // on-device dump, completely UNTOUCHED.
2305
+ export async function sessionLogForGrade(sessionId: string, rules: Rule[]): Promise<string> {
2306
+ if (isLocalStorageMode()) return compressSessionLog(readSessionLog(sessionId));
2307
+ const ctx = await fetchSessionContext(sessionId);
2308
+ if (ctx) return ctx;
2309
+ return compressSessionLog(readSessionLog(sessionId), rules);
2310
+ }
2311
+
2277
2312
  export function cleanupSessionLog(sessionId: string): void {
2278
2313
  const logPath = sessionLogPath(sessionId);
2279
2314
  if (!logPath) return;
@@ -2764,6 +2799,12 @@ export function filePathFromToolInput(toolInput: any): string {
2764
2799
  }
2765
2800
 
2766
2801
  export function reconstructContent(toolName: string, toolInput: any, filePath: string, cwd?: string): string {
2802
+ // Cap the grade payload (64KB ~= 16K tokens) so a huge Write/whole-file edit
2803
+ // cannot push grader inference past its 45s budget. Applies UNIFORMLY to every
2804
+ // path, including the previously-uncapped Write/StrReplace/default branches.
2805
+ return reconstructContentInner(toolName, toolInput, filePath, cwd).slice(0, 65536);
2806
+ }
2807
+ function reconstructContentInner(toolName: string, toolInput: any, filePath: string, cwd?: string): string {
2767
2808
  const canRead = filePath && cwd && isPathUnder(filePath, cwd);
2768
2809
  switch (toolName) {
2769
2810
  case 'ApplyPatch':
@@ -4021,7 +4062,7 @@ import {
4021
4062
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
4022
4063
  parseVerdict, parseCombinedVerdict, combinedEditGradeEnabled, dispatchCapture, dispatchFinding, ruleMode, reconstructContent, isPathUnder, postWithRetry,
4023
4064
  readStdin, extractTranscript, readLastPrompt, findNearestDeps, filePathFromToolInput,
4024
- appendSessionAction, readSessionLog, compressSessionLog, log,
4065
+ appendSessionAction, readSessionLog, compressSessionLog, sessionLogForGrade, log,
4025
4066
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isEditTool, hookSessionId, GATEWAY_URL,
4026
4067
  logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, countEditLineDelta,
4027
4068
  captureLineMetrics, cursorModelFromPayload, resolveTranscriptPath, isCursorInvokingCcHook,
@@ -4148,7 +4189,7 @@ async function main() {
4148
4189
  if (rt === 'local') {
4149
4190
  // \u2500\u2500\u2500 Local grading: org rules ONLY (channel 1, port 18929) \u2500\u2500\u2500
4150
4191
  const proposedShort = proposed.slice(0, 4000);
4151
- const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
4192
+ const sessionLog = await sessionLogForGrade(sessionId, config.rules);
4152
4193
  const graderContent = 'file=' + filePath + ' content=' + proposedShort;
4153
4194
  const relevantRules = await filterRules(
4154
4195
  ruleFilterText(graderContent, transcript.userIntent || lastPrompt),
@@ -4341,7 +4382,7 @@ async function main() {
4341
4382
  recent_user_messages: transcript.recentUserMessages,
4342
4383
  recent_messages: transcript.recentMessages,
4343
4384
  recent_actions: transcript.recentActions,
4344
- session_history: compressSessionLog(readSessionLog(sessionId), config.rules),
4385
+ session_history: await sessionLogForGrade(sessionId, config.rules),
4345
4386
  session_id: sessionId || null,
4346
4387
  tool_use_id: toolUseId || null,
4347
4388
  cwd: cwd || null,
@@ -5412,7 +5453,7 @@ import process from 'node:process';
5412
5453
  import {
5413
5454
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
5414
5455
  parseVerdict, dispatchCapture, dispatchFinding, ruleMode, postWithRetry, readStdin,
5415
- extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
5456
+ extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, sessionLogForGrade, log,
5416
5457
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isShellTool, hookSessionId, GATEWAY_URL,
5417
5458
  logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, appendLocalTelemetry, isSafeInRepoRead,
5418
5459
  loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
@@ -5580,7 +5621,7 @@ async function main() {
5580
5621
  // mode was also checked up there.
5581
5622
 
5582
5623
  if (rt === 'local') {
5583
- const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
5624
+ const sessionLog = await sessionLogForGrade(sessionId, config.rules);
5584
5625
  const relevantRules = await filterRules(
5585
5626
  ruleFilterText(command, transcript.userIntent || lastPrompt),
5586
5627
  config.rules,
@@ -5662,7 +5703,7 @@ async function main() {
5662
5703
  recent_user_messages: transcript.recentUserMessages,
5663
5704
  recent_messages: transcript.recentMessages,
5664
5705
  recent_actions: transcript.recentActions,
5665
- session_history: compressSessionLog(readSessionLog(sessionId), config.rules),
5706
+ session_history: await sessionLogForGrade(sessionId, config.rules),
5666
5707
  session_id: sessionId || null,
5667
5708
  tool_use_id: toolUseId || null,
5668
5709
  cwd: cwd || null,
@@ -5713,7 +5754,7 @@ main();
5713
5754
  import {
5714
5755
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
5715
5756
  parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,
5716
- extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
5757
+ extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, sessionLogForGrade, log,
5717
5758
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isAgentTool, hookSessionId, GATEWAY_URL,
5718
5759
  logGraderUnavailable, graderUnavailableMessage, filterRules, normalizeMode, resolveTranscriptPath, isCursorInvokingCcHook,
5719
5760
  loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
@@ -5795,7 +5836,7 @@ async function main() {
5795
5836
  }
5796
5837
 
5797
5838
  if (rt === 'local') {
5798
- const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
5839
+ const sessionLog = await sessionLogForGrade(sessionId, config.rules);
5799
5840
  const agentText = 'agent=' + subagentType + ' description=' + description + ' prompt=' + prompt.slice(0, 2000);
5800
5841
  const relevantRules = await filterRules(agentText, config.rules);
5801
5842
  const graderPrompt = [
@@ -5868,7 +5909,7 @@ async function main() {
5868
5909
  recent_user_messages: transcript.recentUserMessages,
5869
5910
  recent_messages: transcript.recentMessages,
5870
5911
  recent_actions: transcript.recentActions,
5871
- session_history: compressSessionLog(readSessionLog(sessionId), config.rules),
5912
+ session_history: await sessionLogForGrade(sessionId, config.rules),
5872
5913
  session_id: sessionId || null,
5873
5914
  tool_use_id: toolUseId || null,
5874
5915
  cwd: cwd || null,
@@ -5906,7 +5947,7 @@ main();
5906
5947
  PLAN_JUDGE_TS = `#!/usr/bin/env bun
5907
5948
  import {
5908
5949
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
5909
- parseVerdict, dispatchCapture, appendSessionAction, readSessionLog, compressSessionLog, postWithRetry, readStdin, log,
5950
+ parseVerdict, dispatchCapture, appendSessionAction, readSessionLog, compressSessionLog, sessionLogForGrade, postWithRetry, readStdin, log,
5910
5951
  outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isPlanTool, hookSessionId, GATEWAY_URL,
5911
5952
  filterRules, graderUnavailableMessage, logGraderUnavailable, resolveTranscriptPath, isCursorInvokingCcHook,
5912
5953
  loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
@@ -6013,7 +6054,7 @@ async function main() {
6013
6054
  }
6014
6055
 
6015
6056
  if (rt === 'local') {
6016
- const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
6057
+ const sessionLog = await sessionLogForGrade(sessionId, config.rules);
6017
6058
  const relevantRules = await filterRules(plan.slice(0, 2000), config.rules);
6018
6059
  const graderPrompt = [
6019
6060
  'Working directory: ' + (cwd || '.'),
@@ -6446,7 +6487,7 @@ import {
6446
6487
  loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
6447
6488
  parseVerdict, dispatchCapture, dispatchFinding, ruleMode, normalizeMode, filterRules, ruleFilterText,
6448
6489
  isSafeInRepoRead, resolveTranscriptPath, postWithRetry, readStdin, hashCommand,
6449
- extractTranscript, readLastPrompt, readSessionLog, compressSessionLog,
6490
+ extractTranscript, readLastPrompt, readSessionLog, compressSessionLog, sessionLogForGrade,
6450
6491
  appendLocalTelemetry, logGraderUnavailable, graderUnavailableMessage, log, GATEWAY_URL,
6451
6492
  loadSynkroFile, effectiveGraderPool, synkroFilePresent,
6452
6493
  type Rule,
@@ -6619,7 +6660,7 @@ async function main() {
6619
6660
  }
6620
6661
 
6621
6662
  if (rt === 'local') {
6622
- const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
6663
+ const sessionLog = await sessionLogForGrade(sessionId, config.rules);
6623
6664
  const relevantRules = await filterRules(
6624
6665
  ruleFilterText(command, transcript.userIntent || lastPrompt),
6625
6666
  config.rules,
@@ -10916,7 +10957,7 @@ function writeConfigEnv(opts) {
10916
10957
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
10917
10958
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
10918
10959
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
10919
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.87")}`
10960
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.89")}`
10920
10961
  ];
10921
10962
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
10922
10963
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -14959,7 +15000,7 @@ var args = process.argv.slice(2);
14959
15000
  var cmd = args[0] || "";
14960
15001
  var subArgs = args.slice(1);
14961
15002
  function printVersion() {
14962
- console.log("1.6.87");
15003
+ console.log("1.6.89");
14963
15004
  }
14964
15005
  function printHelp2() {
14965
15006
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents