@synkro-sh/cli 1.6.93 → 1.6.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 +72 -10
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -1775,6 +1775,27 @@ async function hostedGrade(role: GradeRole, prompt: string, jwt: string, timeout
|
|
|
1775
1775
|
return String(data.result || '');
|
|
1776
1776
|
}
|
|
1777
1777
|
|
|
1778
|
+
// Cloud task-adherence: fire the task-only 'task-drift' surface on the org's
|
|
1779
|
+
// container and return ONLY the progress systemMessage (informational \u2014 never
|
|
1780
|
+
// blocks an edit). Fail-silent: any hiccup returns '' so task tracking can't
|
|
1781
|
+
// break grading. The payload IS a ScanEnvelope; the container runs getActiveTask
|
|
1782
|
+
// (resolved per (org,user,repo) via active_tasks) + taskDrift and returns the
|
|
1783
|
+
// decision. Secrets are scrubbed before the envelope crosses the network.
|
|
1784
|
+
export async function cloudTaskDrift(envelope: Record<string, unknown>, jwt: string, timeoutMs = 20000): Promise<string> {
|
|
1785
|
+
try {
|
|
1786
|
+
const body = JSON.stringify({ role: 'task-drift', payload: scrubSecrets(JSON.stringify(envelope)), content: '' });
|
|
1787
|
+
const resp = await fetch(CONTAINERS_URL + '/grade/submit', {
|
|
1788
|
+
method: 'POST',
|
|
1789
|
+
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
|
|
1790
|
+
body,
|
|
1791
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1792
|
+
});
|
|
1793
|
+
if (!resp.ok) return '';
|
|
1794
|
+
const j = await resp.json() as { systemMessage?: unknown };
|
|
1795
|
+
return typeof j?.systemMessage === 'string' ? j.systemMessage : '';
|
|
1796
|
+
} catch { return ''; }
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1778
1799
|
// Option B: when a cloud grade fails (esp. a timeout), the REAL reason lives
|
|
1779
1800
|
// inside the container, not on the wire \u2014 the client only ever sees "timed out".
|
|
1780
1801
|
// Pull the org's per-worker claude/cursor sick reasons + log tails from the
|
|
@@ -3666,6 +3687,31 @@ export function countEditLineDelta(source: {
|
|
|
3666
3687
|
return { linesAdded, linesRemoved };
|
|
3667
3688
|
}
|
|
3668
3689
|
|
|
3690
|
+
// Build the recorded edit diff (code_change) for the audit trail, same format as
|
|
3691
|
+
// the routed hook's summarizeEdit: trim the common prefix/suffix between the file
|
|
3692
|
+
// before the edit and the proposed full content, then emit minus/plus prefixed,
|
|
3693
|
+
// line-numbered rows (the dashboard CodeChangeCard colors each row by its leading
|
|
3694
|
+
// sign). Without this the event detail falls back to the raw file/content dump
|
|
3695
|
+
// instead of an added/removed diff.
|
|
3696
|
+
export function summarizeEditDiff(baseContent: string, proposed: string): string {
|
|
3697
|
+
const EDIT_AUDIT_CAP = 16000;
|
|
3698
|
+
const a = baseContent ? baseContent.split('\\n') : [];
|
|
3699
|
+
const b = proposed ? proposed.split('\\n') : [];
|
|
3700
|
+
let start = 0;
|
|
3701
|
+
while (start < a.length && start < b.length && a[start] === b[start]) start++;
|
|
3702
|
+
let endA = a.length;
|
|
3703
|
+
let endB = b.length;
|
|
3704
|
+
while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) { endA--; endB--; }
|
|
3705
|
+
const removed = a.slice(start, endA);
|
|
3706
|
+
const added = b.slice(start, endB);
|
|
3707
|
+
const out: string[] = [];
|
|
3708
|
+
for (let i = 0; i < removed.length; i++) out.push('-' + String(start + 1 + i).padStart(5) + ' ' + removed[i]);
|
|
3709
|
+
for (let i = 0; i < added.length; i++) out.push('+' + String(start + 1 + i).padStart(5) + ' ' + added[i]);
|
|
3710
|
+
let diff = out.join('\\n');
|
|
3711
|
+
if (diff.length > EDIT_AUDIT_CAP) diff = diff.slice(0, EDIT_AUDIT_CAP) + '\\n... [truncated]';
|
|
3712
|
+
return diff;
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3669
3715
|
/** Normalize model names so the same Cursor session doesn't split across agent buckets. */
|
|
3670
3716
|
export function normalizeCaptureModel(model: string): string {
|
|
3671
3717
|
const m = (model || '').trim();
|
|
@@ -4265,12 +4311,12 @@ export function outputEmpty(): void {
|
|
|
4265
4311
|
`;
|
|
4266
4312
|
EDIT_PRECHECK_TS = `#!/usr/bin/env bun
|
|
4267
4313
|
import {
|
|
4268
|
-
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
|
|
4314
|
+
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade, cloudTaskDrift,
|
|
4269
4315
|
parseVerdict, parseCombinedVerdict, combinedEditGradeEnabled, dispatchCapture, dispatchFinding, ruleMode, reconstructContent, editSecretDenial, isPathUnder, postWithRetry,
|
|
4270
4316
|
readStdin, extractTranscript, readLastPrompt, findNearestDeps, filePathFromToolInput,
|
|
4271
4317
|
appendSessionAction, readSessionLog, compressSessionLog, sessionLogForGrade, log,
|
|
4272
4318
|
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isEditTool, hookSessionId, GATEWAY_URL,
|
|
4273
|
-
logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, countEditLineDelta,
|
|
4319
|
+
logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, countEditLineDelta, summarizeEditDiff,
|
|
4274
4320
|
captureLineMetrics, cursorModelFromPayload, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
4275
4321
|
loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
|
|
4276
4322
|
type HookConfig, type Rule,
|
|
@@ -4365,6 +4411,11 @@ async function main() {
|
|
|
4365
4411
|
try { fileBefore = readFileSync(fullPath, 'utf-8').slice(0, 65536); } catch {}
|
|
4366
4412
|
}
|
|
4367
4413
|
|
|
4414
|
+
// Audit diff (code_change): added/removed lines for the event detail's Code Change
|
|
4415
|
+
// card. Base = the file before this edit (Write \u2192 file on disk if any). Captured on
|
|
4416
|
+
// every edit verdict below so cloud-graded events show the diff, not a raw dump.
|
|
4417
|
+
const editDiff = summarizeEditDiff(toolName === 'Write' ? existingContent : fileBefore, proposed);
|
|
4418
|
+
|
|
4368
4419
|
// Extract transcript context
|
|
4369
4420
|
const transcript = extractTranscript(transcriptPath);
|
|
4370
4421
|
const lastPrompt = readLastPrompt(sessionId);
|
|
@@ -4422,6 +4473,15 @@ async function main() {
|
|
|
4422
4473
|
].join('\\n');
|
|
4423
4474
|
const graderPrompt = buildGraderPrompt(proposedShort);
|
|
4424
4475
|
|
|
4476
|
+
// Cloud task-adherence (additive, informational): fire the task-only grade in
|
|
4477
|
+
// PARALLEL with the rule grade so it adds no latency, then merge its progress
|
|
4478
|
+
// message into the pass output. Fail-silent ('' on any issue). Sent as a
|
|
4479
|
+
// synthetic Write of the reconstructed content so the container needs no base
|
|
4480
|
+
// bytes. cwd = the repo identifier the container keys active_tasks on.
|
|
4481
|
+
const taskDriftP: Promise<string> = process.env.SYNKRO_DEPLOY_LOCATION === 'cloud'
|
|
4482
|
+
? cloudTaskDrift({ payload: { tool_name: 'Write', tool_input: { file_path: filePath, content: proposedShort } }, harness: agentKind, cwd: gitRepo, sessionId }, jwt)
|
|
4483
|
+
: Promise.resolve('');
|
|
4484
|
+
|
|
4425
4485
|
// \u2500\u2500\u2500 Combined org-rules + CWE in ONE inference (SYNKRO_COMBINED_EDIT_GRADE) \u2500\u2500\u2500
|
|
4426
4486
|
// Self-contained early-return branch \u2014 the default two-grade path below is
|
|
4427
4487
|
// left untouched. Appends the CWE rule set to the rules prompt, runs one
|
|
@@ -4500,7 +4560,7 @@ async function main() {
|
|
|
4500
4560
|
toolName, gitRepo, sessionId, config.captureDepth, {
|
|
4501
4561
|
command: editContent, reasoning: guardReason,
|
|
4502
4562
|
rulesChecked: relevantRules, violatedRules,
|
|
4503
|
-
ccModel: captureModel, ...lineMetrics,
|
|
4563
|
+
ccModel: captureModel, codeChange: editDiff, ...lineMetrics,
|
|
4504
4564
|
});
|
|
4505
4565
|
outputJson({
|
|
4506
4566
|
systemMessage: tagStr + ' editGuard ' + fileShort + ' \u2192 blocked: ' + guardReason + (cweFindings.length ? ' (+' + cweFindings.length + ' CWE)' : ''),
|
|
@@ -4524,10 +4584,11 @@ async function main() {
|
|
|
4524
4584
|
toolName, gitRepo, sessionId, config.captureDepth, {
|
|
4525
4585
|
command: editContent, reasoning: ruleVerdict.reason || 'no violations (rules + CWE)',
|
|
4526
4586
|
rulesChecked: relevantRules, violatedRules: [],
|
|
4527
|
-
ccModel: captureModel, ...lineMetrics,
|
|
4587
|
+
ccModel: captureModel, codeChange: editDiff, ...lineMetrics,
|
|
4528
4588
|
});
|
|
4529
4589
|
const cPassLine = tagStr + ' editGuard ' + fileShort + ' \u2192 pass: ' + (ruleVerdict.reason || 'clean (rules + CWE)');
|
|
4530
|
-
|
|
4590
|
+
const cTaskMsg = await taskDriftP;
|
|
4591
|
+
outputJson({ systemMessage: cPassLine + (cTaskMsg ? '\\n' + cTaskMsg : ''), hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro combined edit judge (rules + CWE). ' + (ruleVerdict.reason || 'clean') } });
|
|
4531
4592
|
return;
|
|
4532
4593
|
}
|
|
4533
4594
|
|
|
@@ -4556,7 +4617,7 @@ async function main() {
|
|
|
4556
4617
|
toolName, gitRepo, sessionId, config.captureDepth, {
|
|
4557
4618
|
command: editContent, reasoning: guardReason,
|
|
4558
4619
|
rulesChecked: relevantRules, violatedRules,
|
|
4559
|
-
ccModel: captureModel, ...lineMetrics,
|
|
4620
|
+
ccModel: captureModel, codeChange: editDiff, ...lineMetrics,
|
|
4560
4621
|
});
|
|
4561
4622
|
outputJson({
|
|
4562
4623
|
systemMessage: tagStr + ' editGuard ' + fileShort + ' \u2192 blocked: ' + guardReason,
|
|
@@ -4570,10 +4631,11 @@ async function main() {
|
|
|
4570
4631
|
toolName, gitRepo, sessionId, config.captureDepth, {
|
|
4571
4632
|
command: editContent, reasoning: verdict.reason || 'no policy violations detected',
|
|
4572
4633
|
rulesChecked: relevantRules, violatedRules: [],
|
|
4573
|
-
ccModel: captureModel, ...lineMetrics,
|
|
4634
|
+
ccModel: captureModel, codeChange: editDiff, ...lineMetrics,
|
|
4574
4635
|
});
|
|
4575
4636
|
const passLine = tagStr + ' editGuard ' + fileShort + ' \u2192 pass: ' + (verdict.reason || 'no policy violations detected');
|
|
4576
|
-
|
|
4637
|
+
const rTaskMsg = await taskDriftP;
|
|
4638
|
+
outputJson({ systemMessage: passLine + (rTaskMsg ? '\\n' + rTaskMsg : ''), hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge. ' + (verdict.reason || 'no policy violations detected') } });
|
|
4577
4639
|
return;
|
|
4578
4640
|
}
|
|
4579
4641
|
|
|
@@ -11237,7 +11299,7 @@ function writeConfigEnv(opts) {
|
|
|
11237
11299
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
11238
11300
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
11239
11301
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
11240
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
11302
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.95")}`
|
|
11241
11303
|
];
|
|
11242
11304
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
11243
11305
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -15302,7 +15364,7 @@ var args = process.argv.slice(2);
|
|
|
15302
15364
|
var cmd = args[0] || "";
|
|
15303
15365
|
var subArgs = args.slice(1);
|
|
15304
15366
|
function printVersion() {
|
|
15305
|
-
console.log("1.6.
|
|
15367
|
+
console.log("1.6.95");
|
|
15306
15368
|
}
|
|
15307
15369
|
function printHelp2() {
|
|
15308
15370
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|