@synkro-sh/cli 1.7.89 → 1.7.91

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.89";
150
+ cliVersion = "1.7.91";
151
151
  } catch {
152
152
  }
153
153
  const creds = loadCredentialsIdentity();
@@ -2117,7 +2117,11 @@ function installCodexHooks(hooksJsonPath, config) {
2117
2117
  push(h, "SubagentStart", [cmd(config.subagentStartScriptPath, 5)]);
2118
2118
  if (!config.skipTranscriptSync) push(h, "SubagentStop", [cmd(config.subagentStopScriptPath, 5)]);
2119
2119
  push(h, "SessionEnd", [cmd(config.stopSummaryScriptPath, 3)]);
2120
- if (!config.skipTranscriptSync) push(h, "Stop", [cmd(config.transcriptSyncScriptPath, 3)]);
2120
+ push(h, "Stop", [
2121
+ cmd(config.cweStopScriptPath, 50, "Checking completed edits for weaknesses"),
2122
+ cmd(config.cveStopScriptPath, 50, "Checking completed edits for vulnerabilities"),
2123
+ ...!config.skipTranscriptSync ? [cmd(config.transcriptSyncScriptPath, 3)] : []
2124
+ ]);
2121
2125
  writeHooksFileAtomic2(hooksJsonPath, file);
2122
2126
  }
2123
2127
  function uninstallCodexHooks(hooksJsonPath) {
@@ -2540,7 +2544,7 @@ var init_skillParser = __esm({
2540
2544
  function stubHook(surface, optsLiteral) {
2541
2545
  return "#!/usr/bin/env bun\nimport { runStub } from './_synkro-stub-common.ts';\nrunStub(" + JSON.stringify(surface) + ", " + optsLiteral + ");\n";
2542
2546
  }
2543
- var STUB_COMMON_TS, STUB_EDIT_PRECHECK_TS, STUB_EDIT_FOLLOWUP_TS, STUB_CWE_PRECHECK_TS, STUB_CVE_PRECHECK_TS, STUB_BASH_JUDGE_TS, STUB_SKILL_JUDGE_TS, STUB_INSTALL_SCAN_TS, STUB_AGENT_JUDGE_TS, STUB_MCP_GATE_TS, STUB_PLAN_JUDGE_TS, STUB_STOP_SUMMARY_TS, STUB_SESSION_START_TS, STUB_TRANSCRIPT_SYNC_TS, STUB_SUBAGENT_START_TS, STUB_SUBAGENT_STOP_TS, STUB_USER_PROMPT_SUBMIT_TS, STUB_BASH_FOLLOWUP_TS, STUB_PROMPT_ROUTE_TS, STUB_TASK_ACTIVATE_INTENT_TS, STUB_CURSOR_BASH_JUDGE_TS, STUB_CURSOR_SKILL_JUDGE_TS, STUB_CURSOR_EDIT_CAPTURE_TS, STUB_CURSOR_AGENT_CAPTURE_TS;
2547
+ var STUB_COMMON_TS, STUB_EDIT_PRECHECK_TS, STUB_EDIT_FOLLOWUP_TS, STUB_CWE_PRECHECK_TS, STUB_CVE_PRECHECK_TS, STUB_BASH_JUDGE_TS, STUB_SKILL_JUDGE_TS, STUB_INSTALL_SCAN_TS, STUB_AGENT_JUDGE_TS, STUB_MCP_GATE_TS, STUB_PLAN_JUDGE_TS, STUB_STOP_SUMMARY_TS, STUB_SESSION_START_TS, STUB_TRANSCRIPT_SYNC_TS, STUB_CODEX_CWE_STOP_TS, STUB_CODEX_CVE_STOP_TS, STUB_SUBAGENT_START_TS, STUB_SUBAGENT_STOP_TS, STUB_USER_PROMPT_SUBMIT_TS, STUB_BASH_FOLLOWUP_TS, STUB_PROMPT_ROUTE_TS, STUB_TASK_ACTIVATE_INTENT_TS, STUB_CURSOR_BASH_JUDGE_TS, STUB_CURSOR_SKILL_JUDGE_TS, STUB_CURSOR_EDIT_CAPTURE_TS, STUB_CURSOR_AGENT_CAPTURE_TS;
2544
2548
  var init_hookScriptsTs = __esm({
2545
2549
  "cli/installer/hookScriptsTs.ts"() {
2546
2550
  "use strict";
@@ -2752,6 +2756,28 @@ function failOpen(harness: string): string {
2752
2756
  return harness === 'cursor' ? '{"permission":"allow"}' : '{}';
2753
2757
  }
2754
2758
 
2759
+ /** Convert an existing security PreToolUse verdict into Codex's Stop contract. */
2760
+ export function codexStopResponse(responseText: string): string {
2761
+ try {
2762
+ const parsed = JSON.parse(responseText || '{}');
2763
+ const specific = parsed?.hookSpecificOutput;
2764
+ if (specific?.permissionDecision !== 'deny') return '{}';
2765
+ const reason = String(
2766
+ specific.permissionDecisionReason
2767
+ || specific.additionalContext
2768
+ || parsed.systemMessage
2769
+ || 'A completed edit contains a CWE weakness. Fix it before stopping.',
2770
+ );
2771
+ return JSON.stringify({
2772
+ decision: 'block',
2773
+ reason,
2774
+ ...(parsed.systemMessage ? { systemMessage: parsed.systemMessage } : {}),
2775
+ });
2776
+ } catch {
2777
+ return '{}';
2778
+ }
2779
+ }
2780
+
2755
2781
  function out(s: string): void { try { process.stdout.write(s + '\n'); } catch {} }
2756
2782
 
2757
2783
  function gitRoot(cwd: string): string {
@@ -3100,6 +3126,40 @@ export function extractCompletedCodexPatchEdits(
3100
3126
  return results;
3101
3127
  }
3102
3128
 
3129
+ /**
3130
+ * Recover only the latest completed apply_patch for each file in the current
3131
+ * Codex turn. Stop hooks may fire repeatedly while an agent self-corrects, so
3132
+ * scanning the latest on-disk state avoids re-blocking an earlier vulnerable
3133
+ * patch after a later patch fixed it.
3134
+ */
3135
+ export function extractCompletedCodexTurnPatchEdits(
3136
+ transcript: string,
3137
+ repoRoot: string,
3138
+ ): RecoveredCodexEdit[] {
3139
+ if (!transcript || !repoRoot) return [];
3140
+ const lines = transcript.split('\n');
3141
+ let latestUserLine = -1;
3142
+ for (let index = 0; index < lines.length; index++) {
3143
+ if (!lines[index].trim()) continue;
3144
+ let entry: any;
3145
+ try { entry = JSON.parse(lines[index]); } catch { continue; }
3146
+ const canonicalUser = entry?.type === 'event_msg'
3147
+ && entry?.payload?.type === 'user_message';
3148
+ const compatibilityUser = entry?.type === 'response_item'
3149
+ && entry?.payload?.type === 'message'
3150
+ && entry?.payload?.role === 'user';
3151
+ if (canonicalUser || compatibilityUser) latestUserLine = index;
3152
+ }
3153
+ const turnTranscript = lines.slice(latestUserLine + 1).join('\n');
3154
+ const recovered = extractCompletedCodexPatchEdits(turnTranscript, repoRoot);
3155
+ const latestByFile = new Map<string, RecoveredCodexEdit>();
3156
+ for (const item of recovered) {
3157
+ const filePath = filePathFromToolInput(item.payload?.tool_input);
3158
+ if (filePath) latestByFile.set(filePath, item);
3159
+ }
3160
+ return [...latestByFile.values()];
3161
+ }
3162
+
3103
3163
  interface StubOpts {
3104
3164
  needsFile?: boolean;
3105
3165
  needsTranscript?: boolean;
@@ -3110,6 +3170,10 @@ interface StubOpts {
3110
3170
  telemetry?: boolean;
3111
3171
  harness?: string;
3112
3172
  postEdit?: boolean;
3173
+ /** Recover only this Codex turn's latest completed patch per file. */
3174
+ codexTurnEdits?: boolean;
3175
+ /** Translate a security PreToolUse deny into Codex's Stop block response. */
3176
+ stopContract?: boolean;
3113
3177
  /** Codex SubagentStart/SubagentStop report the parent as session_id and
3114
3178
  * the child as agent_id. Remap them to the ordinary child-session envelope. */
3115
3179
  subagent?: boolean;
@@ -3361,12 +3425,33 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
3361
3425
  const cap = opts.fullTranscript ? 400000 : 200000;
3362
3426
  envelope.transcript = t.length <= cap ? t : t.slice(t.length - cap);
3363
3427
  if (harness === 'codex') {
3364
- const recovered = extractCompletedCodexPatchEdits(envelope.transcript, root || cwd);
3365
- if (recovered.length) envelope.transcriptEdits = recovered;
3428
+ const recovered = opts.codexTurnEdits
3429
+ ? extractCompletedCodexTurnPatchEdits(envelope.transcript, root || cwd)
3430
+ : extractCompletedCodexPatchEdits(envelope.transcript, root || cwd);
3431
+ if (recovered.length) {
3432
+ envelope.transcriptEdits = recovered;
3433
+ if (opts.codexTurnEdits) {
3434
+ envelope.editBatch = recovered.map((item) => ({
3435
+ ...item,
3436
+ payload: {
3437
+ ...item.payload,
3438
+ __synkro_post_edit_enforcement: true,
3439
+ },
3440
+ }));
3441
+ envelope.payload = envelope.editBatch[0].payload;
3442
+ envelope.baseContent = envelope.editBatch[0].baseContent;
3443
+ }
3444
+ }
3366
3445
  }
3367
3446
  } catch {}
3368
3447
  }
3369
3448
  }
3449
+ // No completed apply_patch in this turn means there is nothing for the
3450
+ // fallback to scan. Stay silent and avoid touching the server/database.
3451
+ if (opts.codexTurnEdits && (!Array.isArray(envelope.editBatch) || envelope.editBatch.length === 0)) {
3452
+ out(failOpen(harness));
3453
+ return;
3454
+ }
3370
3455
  if (opts.needsPlan) {
3371
3456
  const ti = payload.tool_input || {};
3372
3457
  envelope.planText = String(ti.plan || ti.content || payload.plan || '');
@@ -3421,7 +3506,10 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
3421
3506
  } catch (e) { /* connection refused / timeout → retry below */ }
3422
3507
  if (i < attempts - 1) await new Promise((r) => setTimeout(r, 500 * (i + 1)));
3423
3508
  }
3424
- const responseText = text || failOpen(harness);
3509
+ const rawResponseText = text || failOpen(harness);
3510
+ const responseText = opts.stopContract && harness === 'codex'
3511
+ ? codexStopResponse(rawResponseText)
3512
+ : rawResponseText;
3425
3513
  out(responseText);
3426
3514
  emitStubTelemetry(surface, harness, telemPayload, responseText, Date.now() - startedAt, telemCwd, telemSessionId);
3427
3515
  } catch (err) {
@@ -3668,6 +3756,8 @@ function emitStubTelemetry(
3668
3756
  STUB_STOP_SUMMARY_TS = stubHook("stop-summary", "{ needsTranscript: true, fullTranscript: true }");
3669
3757
  STUB_SESSION_START_TS = stubHook("session-start", "{ telemetry: true }");
3670
3758
  STUB_TRANSCRIPT_SYNC_TS = stubHook("transcript-sync", "{ needsTranscript: true, fullTranscript: true, telemetry: true }");
3759
+ STUB_CODEX_CWE_STOP_TS = stubHook("cwe-precheck", "{ needsTranscript: true, fullTranscript: true, codexTurnEdits: true, stopContract: true }");
3760
+ STUB_CODEX_CVE_STOP_TS = stubHook("cve-precheck", "{ needsTranscript: true, fullTranscript: true, codexTurnEdits: true, stopContract: true }");
3671
3761
  STUB_SUBAGENT_START_TS = stubHook("subagent-start", "{ telemetry: true, subagent: true }");
3672
3762
  STUB_SUBAGENT_STOP_TS = stubHook("subagent-stop", "{ needsTranscript: true, fullTranscript: true, telemetry: true, subagent: true }");
3673
3763
  STUB_USER_PROMPT_SUBMIT_TS = stubHook("prompt-submit", "{ telemetry: true }");
@@ -6695,7 +6785,7 @@ var init_dockerInstall = __esm({
6695
6785
  HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
6696
6786
  CONTAINER_NAME = resolveContainerName();
6697
6787
  defaultImageVersion = () => {
6698
- if (true) return "1.7.89";
6788
+ if (true) return "1.7.91";
6699
6789
  try {
6700
6790
  const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
6701
6791
  if (pkg.version) return pkg.version;
@@ -7625,6 +7715,8 @@ function writeHookScripts() {
7625
7715
  const planJudgeScriptPath = join19(HOOKS_DIR, "cc-plan-judge.ts");
7626
7716
  const agentJudgeScriptPath = join19(HOOKS_DIR, "cc-agent-judge.ts");
7627
7717
  const stopSummaryScriptPath = join19(HOOKS_DIR, "cc-stop-summary.ts");
7718
+ const cweStopScriptPath = join19(HOOKS_DIR, "codex-cwe-stop.ts");
7719
+ const cveStopScriptPath = join19(HOOKS_DIR, "codex-cve-stop.ts");
7628
7720
  const sessionStartScriptPath = join19(HOOKS_DIR, "cc-session-start.ts");
7629
7721
  const transcriptSyncScriptPath = join19(HOOKS_DIR, "cc-transcript-sync.ts");
7630
7722
  const subagentStartScriptPath = join19(HOOKS_DIR, "codex-subagent-start.ts");
@@ -7654,6 +7746,8 @@ function writeHookScripts() {
7654
7746
  [planJudgeScriptPath, STUB_PLAN_JUDGE_TS],
7655
7747
  [agentJudgeScriptPath, STUB_AGENT_JUDGE_TS],
7656
7748
  [stopSummaryScriptPath, STUB_STOP_SUMMARY_TS],
7749
+ [cweStopScriptPath, STUB_CODEX_CWE_STOP_TS],
7750
+ [cveStopScriptPath, STUB_CODEX_CVE_STOP_TS],
7657
7751
  [sessionStartScriptPath, STUB_SESSION_START_TS],
7658
7752
  [transcriptSyncScriptPath, STUB_TRANSCRIPT_SYNC_TS],
7659
7753
  [subagentStartScriptPath, STUB_SUBAGENT_START_TS],
@@ -7691,6 +7785,8 @@ function writeHookScripts() {
7691
7785
  planJudgeScript: planJudgeScriptPath,
7692
7786
  agentJudgeScript: agentJudgeScriptPath,
7693
7787
  stopSummaryScript: stopSummaryScriptPath,
7788
+ cweStopScript: cweStopScriptPath,
7789
+ cveStopScript: cveStopScriptPath,
7694
7790
  sessionStartScript: sessionStartScriptPath,
7695
7791
  transcriptSyncScript: transcriptSyncScriptPath,
7696
7792
  subagentStartScript: subagentStartScriptPath,
@@ -7734,7 +7830,7 @@ function writeConfigEnv(opts) {
7734
7830
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
7735
7831
  `SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
7736
7832
  `SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
7737
- `SYNKRO_VERSION=${shellQuoteSingle2("1.7.89")}`
7833
+ `SYNKRO_VERSION=${shellQuoteSingle2("1.7.91")}`
7738
7834
  ];
7739
7835
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
7740
7836
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
@@ -8476,7 +8572,7 @@ async function installCommand(opts = {}) {
8476
8572
  await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
8477
8573
  emit("install", {
8478
8574
  phase: "started",
8479
- cli_version_to: "1.7.89",
8575
+ cli_version_to: "1.7.91",
8480
8576
  agents_detected: agents.map((a) => a.kind),
8481
8577
  with_github: false,
8482
8578
  with_local_cc: false,
@@ -8558,6 +8654,8 @@ async function installCommand(opts = {}) {
8558
8654
  cvePrecheckScriptPath: scripts.cvePrecheckScript,
8559
8655
  agentJudgeScriptPath: scripts.agentJudgeScript,
8560
8656
  stopSummaryScriptPath: scripts.stopSummaryScript,
8657
+ cweStopScriptPath: scripts.cweStopScript,
8658
+ cveStopScriptPath: scripts.cveStopScript,
8561
8659
  sessionStartScriptPath: scripts.sessionStartScript,
8562
8660
  transcriptSyncScriptPath: scripts.transcriptSyncScript,
8563
8661
  subagentStartScriptPath: scripts.subagentStartScript,
@@ -9239,6 +9337,8 @@ function reconcileHarness() {
9239
9337
  cvePrecheckScriptPath: scripts.cvePrecheckScript,
9240
9338
  agentJudgeScriptPath: scripts.agentJudgeScript,
9241
9339
  stopSummaryScriptPath: scripts.stopSummaryScript,
9340
+ cweStopScriptPath: scripts.cweStopScript,
9341
+ cveStopScriptPath: scripts.cveStopScript,
9242
9342
  sessionStartScriptPath: scripts.sessionStartScript,
9243
9343
  transcriptSyncScriptPath: scripts.transcriptSyncScript,
9244
9344
  subagentStartScriptPath: scripts.subagentStartScript,
@@ -14511,7 +14611,7 @@ var subArgs = args.slice(1);
14511
14611
  var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
14512
14612
  var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
14513
14613
  function printVersion() {
14514
- console.log("1.7.89");
14614
+ console.log("1.7.91");
14515
14615
  }
14516
14616
  function printHelp2() {
14517
14617
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents