@synkro-sh/cli 1.6.86 → 1.6.87

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
@@ -2173,8 +2173,68 @@ export function readSessionLog(sessionId: string): SessionAction[] {
2173
2173
  } catch { return []; }
2174
2174
  }
2175
2175
 
2176
- export function compressSessionLog(actions: SessionAction[]): string {
2176
+ const RETRIEVAL_RECENT_N = 40;
2177
+ const PROCESS_KW = ['test', 'typecheck', 'tsc', 'lint', 'build', 'migrate', 'migration', 'deploy', 'publish', 'push', 'seed'];
2178
+
2179
+ // Is this session action security- or rule-relevant (i.e. can it participate in a
2180
+ // violation)? Conservative net \u2014 uses substring checks (no regex, to stay safe
2181
+ // inside the materialized-hook template). Plus rule-awareness: an action that
2182
+ // performs a process keyword an active rule actually mentions (e.g. a "pnpm test"
2183
+ // when a rule is about tests) is kept so precondition rules stay resolvable.
2184
+ function actionSalience(a: SessionAction, ruleText: string): string | null {
2185
+ const x = ((a.tool || '') + ' ' + (a.file || '') + ' ' + (a.summary || '')).toLowerCase();
2186
+ const has = (arr: string[]): boolean => { for (const k of arr) if (x.indexOf(k) >= 0) return true; return false; };
2187
+ if (has(['.env', 'credential', 'secret', 'token', '.pem', '.key', 'password', 'api key', 'api_key', 'api-key', 'authorization', 'bearer', 'oauth', '.aws'])) return 'secret';
2188
+ if (has(['curl ', 'wget ', 'ssh ', 'scp ', 'netcat', 'http://', 'https://', 'fetch('])) return 'network';
2189
+ if (has(['rm -rf', 'rm -r ', 'drop table', 'drop database', 'truncate', 'chmod', 'chown', 'sudo', 'kill ', 'wrangler', '--force', 'deploy', 'publish', 'git push'])) return 'destructive';
2190
+ for (const kw of PROCESS_KW) if (ruleText.indexOf(kw) >= 0 && x.indexOf(kw) >= 0) return 'rule';
2191
+ return null;
2192
+ }
2193
+
2194
+ // Cloud session log \u2014 retrieval style (validated 4/4 vs the last-200 dump in the
2195
+ // shadow corpus). Keeps EVERY security-salient + rule-relevant action in order
2196
+ // regardless of age (so cross-turn violations the dump's 200-window drops are
2197
+ // still seen), a recent verbatim window, and a COMPLETENESS-asserting summary of
2198
+ // the routine remainder (the assertion is what lets the grader trust the collapsed
2199
+ // part is benign \u2014 safe only because the salience net above is conservative).
2200
+ function compressSessionLogRetrieval(actions: SessionAction[], rules: Rule[]): string {
2201
+ if (actions.length === 0) return '';
2202
+ const ruleText = (rules || []).map(r => String((r as any).text || '').toLowerCase()).join(' ');
2203
+ const total = actions.length;
2204
+ const recentStart = Math.max(0, total - RETRIEVAL_RECENT_N);
2205
+ const salient: Array<{ a: SessionAction; s: string; i: number }> = [];
2206
+ const noise: Record<string, number> = {};
2207
+ for (let i = 0; i < recentStart; i++) {
2208
+ const a = actions[i];
2209
+ const s = actionSalience(a, ruleText);
2210
+ if (s) salient.push({ a, s, i });
2211
+ else noise[a.tool] = (noise[a.tool] || 0) + 1;
2212
+ }
2213
+ const lines: string[] = ['SESSION HISTORY (' + total + ' actions; security-salient + recent, in order):'];
2214
+ if (salient.length) {
2215
+ lines.push(' --- relevant (any point this session) ---');
2216
+ for (const r of salient) {
2217
+ const target = r.a.file || (r.a.summary || '').slice(0, 80);
2218
+ lines.push(' ' + (r.i + 1) + '. [' + r.s + '] ' + r.a.tool + ' ' + target + (r.a.outcome ? ' -> ' + r.a.outcome : ''));
2219
+ }
2220
+ }
2221
+ const noiseN = Object.values(noise).reduce((n, c) => n + c, 0);
2222
+ if (noiseN) {
2223
+ lines.push(' [' + noiseN + ' earlier routine actions (' + Object.entries(noise).map(([t, c]) => c + ' ' + t).join(', ') + '). The relevant list above is COMPLETE: every credential/secret read, outbound network call, and destructive or rule-gated action this session is listed there; these ' + noiseN + ' are non-salient in-repo reads/edits/builds only.]');
2224
+ }
2225
+ lines.push(' --- recent ---');
2226
+ for (let i = recentStart; i < total; i++) {
2227
+ const a = actions[i];
2228
+ lines.push(' ' + (i + 1) + '. ' + (a.summary || a.tool) + (a.outcome ? ' -> ' + a.outcome : ''));
2229
+ }
2230
+ return lines.join('\\n');
2231
+ }
2232
+
2233
+ export function compressSessionLog(actions: SessionAction[], rules?: Rule[]): string {
2177
2234
  if (actions.length === 0) return '';
2235
+ // Cloud grade path passes the active rules \u2192 retrieval-style context. Local
2236
+ // (no rules arg, or local storage mode) keeps the dump below, UNTOUCHED.
2237
+ if (rules !== undefined && !isLocalStorageMode()) return compressSessionLogRetrieval(actions, rules);
2178
2238
  const total = actions.length;
2179
2239
  const lines: string[] = [];
2180
2240
 
@@ -4088,7 +4148,7 @@ async function main() {
4088
4148
  if (rt === 'local') {
4089
4149
  // \u2500\u2500\u2500 Local grading: org rules ONLY (channel 1, port 18929) \u2500\u2500\u2500
4090
4150
  const proposedShort = proposed.slice(0, 4000);
4091
- const sessionLog = compressSessionLog(readSessionLog(sessionId));
4151
+ const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
4092
4152
  const graderContent = 'file=' + filePath + ' content=' + proposedShort;
4093
4153
  const relevantRules = await filterRules(
4094
4154
  ruleFilterText(graderContent, transcript.userIntent || lastPrompt),
@@ -4281,7 +4341,7 @@ async function main() {
4281
4341
  recent_user_messages: transcript.recentUserMessages,
4282
4342
  recent_messages: transcript.recentMessages,
4283
4343
  recent_actions: transcript.recentActions,
4284
- session_history: compressSessionLog(readSessionLog(sessionId)),
4344
+ session_history: compressSessionLog(readSessionLog(sessionId), config.rules),
4285
4345
  session_id: sessionId || null,
4286
4346
  tool_use_id: toolUseId || null,
4287
4347
  cwd: cwd || null,
@@ -5520,7 +5580,7 @@ async function main() {
5520
5580
  // mode was also checked up there.
5521
5581
 
5522
5582
  if (rt === 'local') {
5523
- const sessionLog = compressSessionLog(readSessionLog(sessionId));
5583
+ const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
5524
5584
  const relevantRules = await filterRules(
5525
5585
  ruleFilterText(command, transcript.userIntent || lastPrompt),
5526
5586
  config.rules,
@@ -5602,7 +5662,7 @@ async function main() {
5602
5662
  recent_user_messages: transcript.recentUserMessages,
5603
5663
  recent_messages: transcript.recentMessages,
5604
5664
  recent_actions: transcript.recentActions,
5605
- session_history: compressSessionLog(readSessionLog(sessionId)),
5665
+ session_history: compressSessionLog(readSessionLog(sessionId), config.rules),
5606
5666
  session_id: sessionId || null,
5607
5667
  tool_use_id: toolUseId || null,
5608
5668
  cwd: cwd || null,
@@ -5735,7 +5795,7 @@ async function main() {
5735
5795
  }
5736
5796
 
5737
5797
  if (rt === 'local') {
5738
- const sessionLog = compressSessionLog(readSessionLog(sessionId));
5798
+ const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
5739
5799
  const agentText = 'agent=' + subagentType + ' description=' + description + ' prompt=' + prompt.slice(0, 2000);
5740
5800
  const relevantRules = await filterRules(agentText, config.rules);
5741
5801
  const graderPrompt = [
@@ -5808,7 +5868,7 @@ async function main() {
5808
5868
  recent_user_messages: transcript.recentUserMessages,
5809
5869
  recent_messages: transcript.recentMessages,
5810
5870
  recent_actions: transcript.recentActions,
5811
- session_history: compressSessionLog(readSessionLog(sessionId)),
5871
+ session_history: compressSessionLog(readSessionLog(sessionId), config.rules),
5812
5872
  session_id: sessionId || null,
5813
5873
  tool_use_id: toolUseId || null,
5814
5874
  cwd: cwd || null,
@@ -5953,7 +6013,7 @@ async function main() {
5953
6013
  }
5954
6014
 
5955
6015
  if (rt === 'local') {
5956
- const sessionLog = compressSessionLog(readSessionLog(sessionId));
6016
+ const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
5957
6017
  const relevantRules = await filterRules(plan.slice(0, 2000), config.rules);
5958
6018
  const graderPrompt = [
5959
6019
  'Working directory: ' + (cwd || '.'),
@@ -6559,7 +6619,7 @@ async function main() {
6559
6619
  }
6560
6620
 
6561
6621
  if (rt === 'local') {
6562
- const sessionLog = compressSessionLog(readSessionLog(sessionId));
6622
+ const sessionLog = compressSessionLog(readSessionLog(sessionId), config.rules);
6563
6623
  const relevantRules = await filterRules(
6564
6624
  ruleFilterText(command, transcript.userIntent || lastPrompt),
6565
6625
  config.rules,
@@ -10856,7 +10916,7 @@ function writeConfigEnv(opts) {
10856
10916
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
10857
10917
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
10858
10918
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
10859
- `SYNKRO_VERSION=${shellQuoteSingle("1.6.86")}`
10919
+ `SYNKRO_VERSION=${shellQuoteSingle("1.6.87")}`
10860
10920
  ];
10861
10921
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
10862
10922
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -14899,7 +14959,7 @@ var args = process.argv.slice(2);
14899
14959
  var cmd = args[0] || "";
14900
14960
  var subArgs = args.slice(1);
14901
14961
  function printVersion() {
14902
- console.log("1.6.86");
14962
+ console.log("1.6.87");
14903
14963
  }
14904
14964
  function printHelp2() {
14905
14965
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents