@synkro-sh/cli 1.6.92 → 1.6.94
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 +86 -47
- 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
|
|
@@ -4265,7 +4286,7 @@ export function outputEmpty(): void {
|
|
|
4265
4286
|
`;
|
|
4266
4287
|
EDIT_PRECHECK_TS = `#!/usr/bin/env bun
|
|
4267
4288
|
import {
|
|
4268
|
-
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
|
|
4289
|
+
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade, cloudTaskDrift,
|
|
4269
4290
|
parseVerdict, parseCombinedVerdict, combinedEditGradeEnabled, dispatchCapture, dispatchFinding, ruleMode, reconstructContent, editSecretDenial, isPathUnder, postWithRetry,
|
|
4270
4291
|
readStdin, extractTranscript, readLastPrompt, findNearestDeps, filePathFromToolInput,
|
|
4271
4292
|
appendSessionAction, readSessionLog, compressSessionLog, sessionLogForGrade, log,
|
|
@@ -4422,6 +4443,15 @@ async function main() {
|
|
|
4422
4443
|
].join('\\n');
|
|
4423
4444
|
const graderPrompt = buildGraderPrompt(proposedShort);
|
|
4424
4445
|
|
|
4446
|
+
// Cloud task-adherence (additive, informational): fire the task-only grade in
|
|
4447
|
+
// PARALLEL with the rule grade so it adds no latency, then merge its progress
|
|
4448
|
+
// message into the pass output. Fail-silent ('' on any issue). Sent as a
|
|
4449
|
+
// synthetic Write of the reconstructed content so the container needs no base
|
|
4450
|
+
// bytes. cwd = the repo identifier the container keys active_tasks on.
|
|
4451
|
+
const taskDriftP: Promise<string> = process.env.SYNKRO_DEPLOY_LOCATION === 'cloud'
|
|
4452
|
+
? cloudTaskDrift({ payload: { tool_name: 'Write', tool_input: { file_path: filePath, content: proposedShort } }, harness: agentKind, cwd: gitRepo, sessionId }, jwt)
|
|
4453
|
+
: Promise.resolve('');
|
|
4454
|
+
|
|
4425
4455
|
// \u2500\u2500\u2500 Combined org-rules + CWE in ONE inference (SYNKRO_COMBINED_EDIT_GRADE) \u2500\u2500\u2500
|
|
4426
4456
|
// Self-contained early-return branch \u2014 the default two-grade path below is
|
|
4427
4457
|
// left untouched. Appends the CWE rule set to the rules prompt, runs one
|
|
@@ -4527,7 +4557,8 @@ async function main() {
|
|
|
4527
4557
|
ccModel: captureModel, ...lineMetrics,
|
|
4528
4558
|
});
|
|
4529
4559
|
const cPassLine = tagStr + ' editGuard ' + fileShort + ' \u2192 pass: ' + (ruleVerdict.reason || 'clean (rules + CWE)');
|
|
4530
|
-
|
|
4560
|
+
const cTaskMsg = await taskDriftP;
|
|
4561
|
+
outputJson({ systemMessage: cPassLine + (cTaskMsg ? '\\n' + cTaskMsg : ''), hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro combined edit judge (rules + CWE). ' + (ruleVerdict.reason || 'clean') } });
|
|
4531
4562
|
return;
|
|
4532
4563
|
}
|
|
4533
4564
|
|
|
@@ -4573,7 +4604,8 @@ async function main() {
|
|
|
4573
4604
|
ccModel: captureModel, ...lineMetrics,
|
|
4574
4605
|
});
|
|
4575
4606
|
const passLine = tagStr + ' editGuard ' + fileShort + ' \u2192 pass: ' + (verdict.reason || 'no policy violations detected');
|
|
4576
|
-
|
|
4607
|
+
const rTaskMsg = await taskDriftP;
|
|
4608
|
+
outputJson({ systemMessage: passLine + (rTaskMsg ? '\\n' + rTaskMsg : ''), hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro local edit judge. ' + (verdict.reason || 'no policy violations detected') } });
|
|
4577
4609
|
return;
|
|
4578
4610
|
}
|
|
4579
4611
|
|
|
@@ -5796,35 +5828,14 @@ async function main() {
|
|
|
5796
5828
|
return;
|
|
5797
5829
|
}
|
|
5798
5830
|
|
|
5799
|
-
if (isSafeInRepoRead(toolName, command, cwd)) {
|
|
5800
|
-
log('bashGuard ' + cmdShort + ' → instant allow (safe in-repo read)');
|
|
5801
|
-
appendLocalTelemetry({
|
|
5802
|
-
capture_type: 'local_verdict',
|
|
5803
|
-
verdict: 'pass',
|
|
5804
|
-
hook_type: 'bash',
|
|
5805
|
-
category: 'safe_read',
|
|
5806
|
-
tool_name: toolName,
|
|
5807
|
-
command: command.slice(0, 200),
|
|
5808
|
-
session_id: sessionId,
|
|
5809
|
-
repo: gitRepo || cwd,
|
|
5810
|
-
cc_model: transcript.ccModel,
|
|
5811
|
-
reasoning: 'Safe in-repo read — auto-allowed without an LLM grade.',
|
|
5812
|
-
});
|
|
5813
|
-
outputJson({
|
|
5814
|
-
systemMessage: tagStr + ' bashGuard → pass: safe in-repo read',
|
|
5815
|
-
hookSpecificOutput: {
|
|
5816
|
-
hookEventName: 'PreToolUse',
|
|
5817
|
-
additionalContext: tagStr + ' bashGuard pass: safe in-repo read.',
|
|
5818
|
-
},
|
|
5819
|
-
});
|
|
5820
|
-
return;
|
|
5821
|
-
}
|
|
5822
|
-
|
|
5823
5831
|
// ─── Secret firewall: BLOCK a literal credential in the command BEFORE it runs
|
|
5824
5832
|
// or reaches the grader — the value never lands in a file, shell history, OR the
|
|
5825
5833
|
// LLM provider (no leak to Anthropic/Cursor). Deterministic + local, no model
|
|
5826
5834
|
// call. High-confidence formats only (a false block stops the user), and
|
|
5827
|
-
// $VAR / placeholder refs never trip it.
|
|
5835
|
+
// $VAR / placeholder refs never trip it. MUST run BEFORE the safe-read fast-path
|
|
5836
|
+
// below — otherwise an echo/printf of a literal secret is treated as a safe read
|
|
5837
|
+
// and waved through. A genuine read carries no literal secret, so this never
|
|
5838
|
+
// false-blocks legitimate reads. ───
|
|
5828
5839
|
const secretHits = detectHardSecrets(command);
|
|
5829
5840
|
if (secretHits.length) {
|
|
5830
5841
|
const types = secretHits.map(h => h.type).join(', ');
|
|
@@ -5849,6 +5860,32 @@ async function main() {
|
|
|
5849
5860
|
return;
|
|
5850
5861
|
}
|
|
5851
5862
|
|
|
5863
|
+
// Safe in-repo read fast-path — runs AFTER the secret firewall above so a
|
|
5864
|
+
// literal credential is never waved through as a "safe read".
|
|
5865
|
+
if (isSafeInRepoRead(toolName, command, cwd)) {
|
|
5866
|
+
log('bashGuard ' + cmdShort + ' → instant allow (safe in-repo read)');
|
|
5867
|
+
appendLocalTelemetry({
|
|
5868
|
+
capture_type: 'local_verdict',
|
|
5869
|
+
verdict: 'pass',
|
|
5870
|
+
hook_type: 'bash',
|
|
5871
|
+
category: 'safe_read',
|
|
5872
|
+
tool_name: toolName,
|
|
5873
|
+
command: command.slice(0, 200),
|
|
5874
|
+
session_id: sessionId,
|
|
5875
|
+
repo: gitRepo || cwd,
|
|
5876
|
+
cc_model: transcript.ccModel,
|
|
5877
|
+
reasoning: 'Safe in-repo read — auto-allowed without an LLM grade.',
|
|
5878
|
+
});
|
|
5879
|
+
outputJson({
|
|
5880
|
+
systemMessage: tagStr + ' bashGuard → pass: safe in-repo read',
|
|
5881
|
+
hookSpecificOutput: {
|
|
5882
|
+
hookEventName: 'PreToolUse',
|
|
5883
|
+
additionalContext: tagStr + ' bashGuard pass: safe in-repo read.',
|
|
5884
|
+
},
|
|
5885
|
+
});
|
|
5886
|
+
return;
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5852
5889
|
// ─── Install protection: install-scan runs first and owns block traces ───
|
|
5853
5890
|
let scanConcern = '';
|
|
5854
5891
|
let scanBlockContext = '';
|
|
@@ -6872,6 +6909,25 @@ async function main() {
|
|
|
6872
6909
|
|
|
6873
6910
|
// Instant-allow read-only tool calls + safe in-repo bash reads \u2014 no grade,
|
|
6874
6911
|
// no network. Critical under Cursor's tight 15s beforeShellExecution budget.
|
|
6912
|
+
// \u2500\u2500\u2500 Secret firewall: BLOCK a literal credential in the command BEFORE it runs
|
|
6913
|
+
// or reaches the grader \u2014 never leaks to a file, shell history, or the model
|
|
6914
|
+
// (Anthropic/Cursor). Local + deterministic; $VAR / placeholder refs never trip
|
|
6915
|
+
// it. MUST run BEFORE the safe-read fast-path below \u2014 else an echo/printf of a
|
|
6916
|
+
// literal secret is waved through as a "safe read". \u2500\u2500\u2500
|
|
6917
|
+
const secretHits = detectHardSecrets(command);
|
|
6918
|
+
if (secretHits.length) {
|
|
6919
|
+
const types = secretHits.map(h => h.type).join(', ');
|
|
6920
|
+
const remediation = secretRemediation(command, secretHits);
|
|
6921
|
+
log('bashGuard ' + cmdShort + ' \u2192 BLOCKED: literal credential (' + types + ')');
|
|
6922
|
+
appendLocalTelemetry({
|
|
6923
|
+
capture_type: 'local_verdict', verdict: 'block', hook_type: 'bash',
|
|
6924
|
+
category: 'secret_exposure', tool_name: toolName, command: scrubSecrets(command).slice(0, 200),
|
|
6925
|
+
session_id: sessionId, repo: repo || cwd, cc_model: model,
|
|
6926
|
+
reasoning: 'Literal credential (' + types + ') in the proposed command \u2014 blocked locally before grading; never sent to the model.',
|
|
6927
|
+
});
|
|
6928
|
+
finishWith({ permission: 'deny', user_message: '[synkro] bashGuard \u2192 BLOCKED: literal credential in command (' + types + '). Use an env var reference instead.', agent_message: remediation });
|
|
6929
|
+
}
|
|
6930
|
+
|
|
6875
6931
|
if (isSafeInRepoRead(toolName, command, cwd)) {
|
|
6876
6932
|
log('bashGuard ' + cmdShort + ' \u2192 instant allow (safe in-repo read)');
|
|
6877
6933
|
appendLocalTelemetry({
|
|
@@ -6899,23 +6955,6 @@ async function main() {
|
|
|
6899
6955
|
const rt = await route(config, synkroFile);
|
|
6900
6956
|
const tagStr = tag(rt, config, graderPool);
|
|
6901
6957
|
|
|
6902
|
-
// Secret firewall: BLOCK a literal credential in the command BEFORE it runs or
|
|
6903
|
-
// reaches the grader \u2014 never leaks to a file, shell history, or the model
|
|
6904
|
-
// (Anthropic/Cursor). Local + deterministic; $VAR / placeholder refs never trip it.
|
|
6905
|
-
const secretHits = detectHardSecrets(command);
|
|
6906
|
-
if (secretHits.length) {
|
|
6907
|
-
const types = secretHits.map(h => h.type).join(', ');
|
|
6908
|
-
const remediation = secretRemediation(command, secretHits);
|
|
6909
|
-
log('bashGuard ' + cmdShort + ' \u2192 BLOCKED: literal credential (' + types + ')');
|
|
6910
|
-
appendLocalTelemetry({
|
|
6911
|
-
capture_type: 'local_verdict', verdict: 'block', hook_type: 'bash',
|
|
6912
|
-
category: 'secret_exposure', tool_name: toolName, command: scrubSecrets(command).slice(0, 200),
|
|
6913
|
-
session_id: sessionId, repo: repo || cwd, cc_model: model,
|
|
6914
|
-
reasoning: 'Literal credential (' + types + ') in the proposed command \u2014 blocked locally before grading; never sent to the model.',
|
|
6915
|
-
});
|
|
6916
|
-
finishWith({ permission: 'deny', user_message: tagStr + ' bashGuard \u2192 BLOCKED: literal credential in command (' + types + '). Use an env var reference instead.', agent_message: remediation });
|
|
6917
|
-
}
|
|
6918
|
-
|
|
6919
6958
|
// Install protection \u2014 install-scan runs first and owns block traces.
|
|
6920
6959
|
let scanConcern = '';
|
|
6921
6960
|
let scanBlockContext = '';
|
|
@@ -11230,7 +11269,7 @@ function writeConfigEnv(opts) {
|
|
|
11230
11269
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
11231
11270
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
11232
11271
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
11233
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
11272
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.94")}`
|
|
11234
11273
|
];
|
|
11235
11274
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
11236
11275
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -15295,7 +15334,7 @@ var args = process.argv.slice(2);
|
|
|
15295
15334
|
var cmd = args[0] || "";
|
|
15296
15335
|
var subArgs = args.slice(1);
|
|
15297
15336
|
function printVersion() {
|
|
15298
|
-
console.log("1.6.
|
|
15337
|
+
console.log("1.6.94");
|
|
15299
15338
|
}
|
|
15300
15339
|
function printHelp2() {
|
|
15301
15340
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|