sneakoscope 4.0.8 → 4.0.10

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.
Files changed (36) hide show
  1. package/crates/sks-core/Cargo.lock +1 -1
  2. package/crates/sks-core/Cargo.toml +1 -1
  3. package/crates/sks-core/src/main.rs +1 -1
  4. package/dist/bin/sks.js +1 -1
  5. package/dist/cli/command-registry.js +1 -0
  6. package/dist/core/commands/naruto-command.js +25 -0
  7. package/dist/core/commands/stop-gate-command.js +63 -0
  8. package/dist/core/fsx.js +1 -1
  9. package/dist/core/pipeline-internals/runtime-gates.js +31 -4
  10. package/dist/core/providers/glm/glm-bench.js +4 -4
  11. package/dist/core/providers/glm/glm-direct-run.js +1 -1
  12. package/dist/core/providers/glm/glm-latency-trace.js +1 -1
  13. package/dist/core/providers/glm/glm-request-cache.js +9 -4
  14. package/dist/core/providers/glm/naruto/glm-naruto-bench.js +47 -12
  15. package/dist/core/providers/glm/naruto/glm-naruto-combined-patch.js +49 -0
  16. package/dist/core/providers/glm/naruto/glm-naruto-conflict-graph.js +7 -1
  17. package/dist/core/providers/glm/naruto/glm-naruto-hunk-conflict.js +16 -0
  18. package/dist/core/providers/glm/naruto/glm-naruto-hunk-parser.js +36 -0
  19. package/dist/core/providers/glm/naruto/glm-naruto-judge.js +1 -1
  20. package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +77 -13
  21. package/dist/core/providers/glm/naruto/glm-naruto-patch-candidate-gate.js +42 -0
  22. package/dist/core/providers/glm/naruto/glm-naruto-patch-candidate-parser.js +62 -0
  23. package/dist/core/providers/glm/naruto/glm-naruto-secret-audit.js +39 -0
  24. package/dist/core/providers/glm/naruto/glm-naruto-trace.js +41 -1
  25. package/dist/core/providers/glm/naruto/glm-naruto-verifier-output.js +26 -0
  26. package/dist/core/providers/glm/naruto/glm-naruto-worker-artifacts.js +42 -0
  27. package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +30 -11
  28. package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +170 -16
  29. package/dist/core/providers/openrouter/openrouter-stream.js +46 -6
  30. package/dist/core/stop-gate/stop-gate-check.js +220 -0
  31. package/dist/core/stop-gate/stop-gate-diagnostics.js +4 -0
  32. package/dist/core/stop-gate/stop-gate-resolver.js +122 -0
  33. package/dist/core/stop-gate/stop-gate-types.js +2 -0
  34. package/dist/core/stop-gate/stop-gate-writer.js +126 -0
  35. package/dist/core/version.js +1 -1
  36. package/package.json +1 -1
@@ -2,10 +2,10 @@ import path from 'node:path';
2
2
  import { nowIso, writeJsonAtomic } from '../../../fsx.js';
3
3
  import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
4
4
  import { resolveOpenRouterApiKey } from '../../openrouter/openrouter-secret-store.js';
5
- import { checkAndApplyGlmPatch } from '../glm-patch-apply.js';
6
5
  import { decomposeTask, validateWorkGraph } from './glm-naruto-decomposer.js';
7
6
  import { planShardCandidates, computeInitialLaneMix } from './glm-naruto-shard-planner.js';
8
7
  import { runPatchWorkerPool } from './glm-naruto-worker-pool.js';
8
+ import { runVerifierWorker } from './glm-naruto-worker-runtime.js';
9
9
  import { buildConflictGraph } from './glm-naruto-conflict-graph.js';
10
10
  import { planMerge } from './glm-naruto-merge-planner.js';
11
11
  import { finalizeMergePlan } from './glm-naruto-finalizer.js';
@@ -14,6 +14,9 @@ import { createBudget, checkBudget, recordRequest } from './glm-naruto-budget.js
14
14
  import { createProviderHealthTracker } from '../../openrouter/openrouter-provider-health.js';
15
15
  import { createMissionTrace, recordWorkerTrace, writeMissionArtifacts, buildMissionSummary } from './glm-naruto-trace.js';
16
16
  import { runGlmJudge } from './glm-naruto-judge.js';
17
+ import { writeFinalStopGate } from '../../../stop-gate/stop-gate-writer.js';
18
+ import { checkAndApplyCombinedGlmNarutoPatch } from './glm-naruto-combined-patch.js';
19
+ import { auditGlmNarutoArtifactsForSecrets } from './glm-naruto-secret-audit.js';
17
20
  import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
18
21
  export async function runGlmNarutoMission(input) {
19
22
  const missionId = input.missionId || `glm-naruto-${nowIso().replace(/[:.]/g, '-')}`;
@@ -93,8 +96,38 @@ export async function runGlmNarutoMission(input) {
93
96
  failedShardIds = [...failedShardIds, ...repairPool.failedShardIds];
94
97
  }
95
98
  }
99
+ // 4.0.9: Verifier wave — run parallel verifier workers over gate-passed candidates.
100
+ let passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
101
+ if (passedEnvelopes.length > 0 && !input.noApply) {
102
+ const verifyApiKey = key.key;
103
+ const verifyResults = await Promise.allSettled(passedEnvelopes.map((env) => runVerifierWorker({
104
+ apiKey: verifyApiKey,
105
+ missionId,
106
+ workerId: env.worker_id,
107
+ envelope: env,
108
+ timeoutMs: 120_000,
109
+ })));
110
+ const verifiedEnvelopes = [];
111
+ for (let vi = 0; vi < passedEnvelopes.length; vi++) {
112
+ const env = passedEnvelopes[vi];
113
+ const res = verifyResults[vi];
114
+ if (res.status === 'fulfilled' && res.value.ok) {
115
+ verifiedEnvelopes.push({ ...env, verification_passed: true, status: 'gate_passed' });
116
+ }
117
+ else {
118
+ verifiedEnvelopes.push({ ...env, verification_passed: false, status: 'verification_failed' });
119
+ }
120
+ if (res.status === 'fulfilled') {
121
+ traceState = recordWorkerTrace(traceState, res.value.trace);
122
+ }
123
+ }
124
+ envelopes = envelopes.map((e) => {
125
+ const verified = verifiedEnvelopes.find((v) => v.worker_id === e.worker_id);
126
+ return verified ?? e;
127
+ });
128
+ passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
129
+ }
96
130
  // Build conflict graph and merge plan
97
- const passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
98
131
  const nodes = passedEnvelopes.map((env) => ({
99
132
  patch_id: env.worker_id,
100
133
  shard_id: env.shard_id,
@@ -124,16 +157,14 @@ export async function runGlmNarutoMission(input) {
124
157
  let appliedPatches = 0;
125
158
  let applyResult = null;
126
159
  if (!input.noApply && mergePlan.selected_patches.length > 0) {
127
- for (const patchId of mergePlan.selected_patches) {
128
- const envelope = envelopes.find((e) => e.worker_id === patchId);
129
- if (!envelope)
130
- continue;
131
- const applied = await checkAndApplyGlmPatch({ cwd, patch: envelope.patch, apply: true });
132
- if (applied.ok) {
133
- appliedPatches++;
134
- }
135
- }
136
- applyResult = { ok: appliedPatches > 0, applied: mergePlan.selected_patches };
160
+ const combinedApply = await checkAndApplyCombinedGlmNarutoPatch({
161
+ cwd,
162
+ envelopes,
163
+ selectedPatchIds: mergePlan.selected_patches,
164
+ apply: true
165
+ });
166
+ appliedPatches = combinedApply.ok ? combinedApply.applied.length : 0;
167
+ applyResult = { ok: combinedApply.ok, applied: combinedApply.applied, ...(combinedApply.blocker ? { blocker: combinedApply.blocker } : {}) };
137
168
  }
138
169
  const terminalState = appliedPatches > 0 ? 'completed' : passedEnvelopes.length > 0 ? 'partial_candidates' : 'blocked';
139
170
  const terminationReason = appliedPatches > 0 ? 'completed_merge_applied' : passedEnvelopes.length > 0 ? 'partial_no_apply' : 'no_gate_passed_candidates';
@@ -181,8 +212,41 @@ export async function runGlmNarutoMission(input) {
181
212
  termination: { schema: 'sks.glm-naruto-termination.v1', mission_id: missionId, terminal_state: terminalState, reason: terminationReason, wall_clock_ms: summary.wall_clock_ms },
182
213
  ...(applyResult ? { applyResult: { ...applyResult, schema: 'sks.glm-naruto-apply-result.v1' } } : {}),
183
214
  verificationSummary: { schema: 'sks.glm-naruto-verification.v1', verified: passedEnvelopes.length, total: envelopes.length },
184
- missionResult: result
215
+ missionResult: result,
216
+ envelopes
185
217
  });
218
+ const secretAudit = await auditGlmNarutoArtifactsForSecrets(path.join(cwd, '.sneakoscope', 'glm-naruto', missionId)).catch((err) => ({
219
+ schema: 'sks.glm-naruto-secret-audit.v1',
220
+ ok: false,
221
+ root: path.join(cwd, '.sneakoscope', 'glm-naruto', missionId),
222
+ scanned_files: 0,
223
+ findings: [`audit_failed:${err instanceof Error ? err.message : String(err)}`]
224
+ }));
225
+ await writeJsonAtomic(path.join(artifactDir, 'secret-audit.json'), secretAudit).catch(() => undefined);
226
+ // 4.0.9: Write canonical stop-gate artifacts for hook resolution.
227
+ await writeFinalStopGate({
228
+ root: cwd,
229
+ missionId,
230
+ route: 'GLM_NARUTO',
231
+ routeCommand: '$Naruto',
232
+ status: result.ok && secretAudit.ok ? 'passed' : (terminalState === 'blocked' || !secretAudit.ok ? 'blocked' : 'failed'),
233
+ terminal: terminalState === 'completed' || terminalState === 'blocked',
234
+ terminalState,
235
+ evidence: {
236
+ build_passed: result.ok,
237
+ tests_passed: result.ok,
238
+ route_evidence_passed: result.ok,
239
+ per_worker_artifacts: true,
240
+ verifier_wave_run: true,
241
+ model_guard_enforced: true,
242
+ proof_required: false,
243
+ proof_passed: true,
244
+ reflection_required: false,
245
+ reflection_passed: 'not_required',
246
+ },
247
+ blockers: secretAudit.ok ? (result.blockers || []) : ['glm_naruto_secret_leak_detected'],
248
+ nativeGateFile: 'termination.json',
249
+ }).catch(() => null);
186
250
  return { ...result, artifact_dir: artifactDir };
187
251
  }
188
252
  function missionResult(missionId, task, status, reason, patchCandidates, startedMs, envelopes, traces, blockers, warnings) {
@@ -0,0 +1,42 @@
1
+ import { checkAndApplyGlmPatch } from '../glm-patch-apply.js';
2
+ import { parseGlmNarutoPatchCandidate } from './glm-naruto-patch-candidate-parser.js';
3
+ const PROTECTED_PATH = /(^|\/)(\.github|dist|node_modules)(\/|$)/;
4
+ const SECRET_PATTERN = /\b(?:Bearer\s+[A-Za-z0-9._~+/-]+|sk-(?:or-)?[A-Za-z0-9_-]{12,}|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)\b/;
5
+ export async function evaluateGlmNarutoPatchCandidateGate(input) {
6
+ const checks = [];
7
+ const parsed = parseGlmNarutoPatchCandidate(input.envelope.patch);
8
+ checks.push(check('candidate_parse', parsed.ok, parsed.blockers.join(',') || undefined));
9
+ checks.push(check('patch_section', Boolean(parsed.patch), parsed.patch ? undefined : 'missing_patch_section'));
10
+ checks.push(check('unified_diff', /^diff --git /m.test(parsed.patch), parsed.patch ? undefined : 'no_diff_git'));
11
+ const blockedPath = parsed.touched_paths.find((file) => PROTECTED_PATH.test(file));
12
+ checks.push(check('protected_path_guard', !blockedPath, blockedPath));
13
+ const secretLeak = SECRET_PATTERN.test(parsed.patch);
14
+ checks.push(check('secret_leakage_guard', !secretLeak, secretLeak ? 'secret_like_content' : undefined));
15
+ if (parsed.ok && !blockedPath && !secretLeak) {
16
+ const applyCheck = await checkAndApplyGlmPatch({
17
+ cwd: input.cwd,
18
+ patch: parsed.patch,
19
+ apply: input.apply === true
20
+ });
21
+ checks.push(check('git_apply_check', applyCheck.ok, applyCheck.ok ? undefined : applyCheck.error.code));
22
+ }
23
+ else {
24
+ checks.push({ id: 'git_apply_check', ok: false, reason: 'skipped_due_to_prior_blocker', ms: 0 });
25
+ }
26
+ const blockers = checks.filter((row) => !row.ok).map((row) => row.reason || row.id);
27
+ return {
28
+ schema: 'sks.glm-naruto-patch-candidate-gate.v1',
29
+ ok: blockers.length === 0,
30
+ worker_id: input.envelope.worker_id,
31
+ shard_id: input.envelope.shard_id,
32
+ patch_id: input.envelope.patch_sha256,
33
+ extracted_patch: parsed.patch,
34
+ touched_paths: parsed.touched_paths,
35
+ checks,
36
+ blockers
37
+ };
38
+ }
39
+ function check(id, ok, reason) {
40
+ return { id, ok, ...(reason ? { reason } : {}), ms: 0 };
41
+ }
42
+ //# sourceMappingURL=glm-naruto-patch-candidate-gate.js.map
@@ -0,0 +1,62 @@
1
+ import { parseUnifiedDiffPatch } from '../glm-patch-parser.js';
2
+ const CANDIDATE_OPEN = '<sks_patch_candidate>';
3
+ const CANDIDATE_CLOSE = '</sks_patch_candidate>';
4
+ const LEGACY_PATCH_OPEN = '<sks_patch>';
5
+ export function parseGlmNarutoPatchCandidate(text) {
6
+ const blockers = [];
7
+ if (text.includes(LEGACY_PATCH_OPEN) && !text.includes(CANDIDATE_OPEN)) {
8
+ return failed(text, ['legacy_sks_patch_envelope_rejected']);
9
+ }
10
+ const body = unwrapCandidateBody(text);
11
+ if (!body.ok)
12
+ return failed(text, body.blockers);
13
+ const extracted = extractPatchSection(body.body);
14
+ if (!extracted.ok)
15
+ return failed(body.body, extracted.blockers);
16
+ const parsed = parseUnifiedDiffPatch(extracted.patch);
17
+ if (parsed.empty)
18
+ blockers.push('patch_missing_unified_diff');
19
+ return {
20
+ ok: blockers.length === 0,
21
+ body: body.body,
22
+ patch: extracted.patch,
23
+ touched_paths: parsed.touchedPaths,
24
+ blockers
25
+ };
26
+ }
27
+ function unwrapCandidateBody(text) {
28
+ const start = text.indexOf(CANDIDATE_OPEN);
29
+ const end = text.indexOf(CANDIDATE_CLOSE);
30
+ if (start >= 0 || end >= 0) {
31
+ if (start < 0 || end <= start)
32
+ return { ok: false, blockers: ['malformed_patch_candidate_envelope'] };
33
+ return { ok: true, body: text.slice(start + CANDIDATE_OPEN.length, end).trim() };
34
+ }
35
+ if (/^\s*patch\s*:/im.test(text))
36
+ return { ok: true, body: text.trim() };
37
+ return { ok: false, blockers: ['missing_patch_candidate_envelope'] };
38
+ }
39
+ function extractPatchSection(body) {
40
+ const lines = body.split(/\r?\n/);
41
+ const patchLine = lines.findIndex((line) => /^\s*patch\s*:/i.test(line));
42
+ if (patchLine < 0)
43
+ return { ok: false, blockers: ['missing_patch_section'] };
44
+ const firstLine = lines[patchLine] || '';
45
+ const inline = firstLine.replace(/^\s*patch\s*:\s*/i, '');
46
+ const tail = inline.trim() ? [inline, ...lines.slice(patchLine + 1)] : lines.slice(patchLine + 1);
47
+ const raw = tail.join('\n').replace(/^\s*```(?:diff|patch)?\s*/i, '').replace(/\s*```\s*$/i, '').trim();
48
+ const diffIndex = raw.search(/^diff --git /m);
49
+ if (diffIndex < 0)
50
+ return { ok: false, blockers: ['patch_missing_diff_git'] };
51
+ return { ok: true, patch: raw.slice(diffIndex).trimEnd() + '\n' };
52
+ }
53
+ function failed(body, blockers) {
54
+ return {
55
+ ok: false,
56
+ body,
57
+ patch: '',
58
+ touched_paths: [],
59
+ blockers
60
+ };
61
+ }
62
+ //# sourceMappingURL=glm-naruto-patch-candidate-parser.js.map
@@ -0,0 +1,39 @@
1
+ import fsp from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const SECRET_PATTERN = /\b(?:Bearer\s+[A-Za-z0-9._~+/-]+|sk-(?:or-)?[A-Za-z0-9_-]{12,}|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)\b/;
4
+ export async function auditGlmNarutoArtifactsForSecrets(root) {
5
+ const findings = [];
6
+ let scanned = 0;
7
+ await scan(root, async (file) => {
8
+ if (!/\.(json|jsonl|md|txt)$/i.test(file))
9
+ return;
10
+ scanned++;
11
+ const content = await fsp.readFile(file, 'utf8').catch(() => '');
12
+ if (SECRET_PATTERN.test(content))
13
+ findings.push(path.relative(root, file));
14
+ });
15
+ return {
16
+ schema: 'sks.glm-naruto-secret-audit.v1',
17
+ ok: findings.length === 0,
18
+ root,
19
+ scanned_files: scanned,
20
+ findings
21
+ };
22
+ }
23
+ async function scan(dir, visit) {
24
+ let entries;
25
+ try {
26
+ entries = await fsp.readdir(dir, { withFileTypes: true });
27
+ }
28
+ catch {
29
+ return;
30
+ }
31
+ for (const entry of entries) {
32
+ const p = path.join(dir, String(entry.name));
33
+ if (entry.isDirectory())
34
+ await scan(p, visit);
35
+ else if (entry.isFile())
36
+ await visit(p);
37
+ }
38
+ }
39
+ //# sourceMappingURL=glm-naruto-secret-audit.js.map
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import { nowIso, writeJsonAtomic } from '../../../fsx.js';
2
+ import { ensureDir, nowIso, writeJsonAtomic } from '../../../fsx.js';
3
3
  export function createMissionTrace(missionId) {
4
4
  return {
5
5
  missionId,
@@ -12,6 +12,7 @@ export function recordWorkerTrace(state, trace) {
12
12
  }
13
13
  export async function writeMissionArtifacts(input) {
14
14
  const dir = path.join(input.root, '.sneakoscope', 'glm-naruto', input.missionId);
15
+ await ensureDir(dir);
15
16
  if (input.workGraph)
16
17
  await writeJsonAtomic(path.join(dir, 'work-graph.json'), input.workGraph);
17
18
  if (input.conflictGraph)
@@ -32,6 +33,45 @@ export async function writeMissionArtifacts(input) {
32
33
  await writeJsonAtomic(path.join(dir, 'verification-summary.json'), input.verificationSummary);
33
34
  if (input.missionResult)
34
35
  await writeJsonAtomic(path.join(dir, 'mission-result.json'), input.missionResult);
36
+ // 4.0.9: Write per-worker patch envelope / request-summary / stream-trace / gate-result artifacts.
37
+ if (input.envelopes && input.envelopes.length > 0) {
38
+ const workersDir = path.join(dir, 'workers');
39
+ await ensureDir(workersDir);
40
+ for (const env of input.envelopes) {
41
+ const workerId = String(env.worker_id || env.shard_id || 'unknown');
42
+ const workerDir = path.join(workersDir, workerId);
43
+ await ensureDir(workerDir);
44
+ await writeJsonAtomic(path.join(workerDir, 'patch-envelope.json'), env);
45
+ await writeJsonAtomic(path.join(workerDir, 'request-summary.json'), {
46
+ schema: 'sks.glm-naruto-worker-request-summary.v1',
47
+ worker_id: workerId,
48
+ shard_id: env.shard_id,
49
+ model: env.model || null,
50
+ provider: 'openrouter',
51
+ request_body_size: env.request_body_size ?? null,
52
+ cached: env.cached ?? false,
53
+ created_at: nowIso(),
54
+ });
55
+ await writeJsonAtomic(path.join(workerDir, 'stream-trace.json'), {
56
+ schema: 'sks.glm-naruto-worker-stream-trace.v1',
57
+ worker_id: workerId,
58
+ ttft_ms: env.ttft_ms ?? null,
59
+ chunk_count: env.chunk_count ?? null,
60
+ real_stream: env.real_stream ?? true,
61
+ idle_timeout_ms: env.idle_timeout_ms ?? null,
62
+ created_at: nowIso(),
63
+ });
64
+ await writeJsonAtomic(path.join(workerDir, 'gate-result.json'), {
65
+ schema: 'sks.glm-naruto-worker-gate-result.v1',
66
+ worker_id: workerId,
67
+ shard_id: env.shard_id,
68
+ status: env.status,
69
+ gate_passed: env.status === 'gate_passed',
70
+ verification_passed: env.verification_passed ?? (env.status === 'gate_passed'),
71
+ created_at: nowIso(),
72
+ });
73
+ }
74
+ }
35
75
  return dir;
36
76
  }
37
77
  export function buildMissionSummary(input) {
@@ -0,0 +1,26 @@
1
+ export function parseGlmNarutoVerifierOutput(content) {
2
+ try {
3
+ const parsed = JSON.parse(stripJsonFences(content));
4
+ const issues = [];
5
+ if (parsed.schema !== 'sks.glm-naruto-verifier-output.v1')
6
+ issues.push('schema');
7
+ if (typeof parsed.ok !== 'boolean')
8
+ issues.push('ok');
9
+ if (!Array.isArray(parsed.issues) || parsed.issues.some((issue) => typeof issue !== 'string'))
10
+ issues.push('issues');
11
+ if (typeof parsed.risk_score !== 'number' || parsed.risk_score < 0 || parsed.risk_score > 1)
12
+ issues.push('risk_score');
13
+ if (typeof parsed.confidence !== 'number' || parsed.confidence < 0 || parsed.confidence > 1)
14
+ issues.push('confidence');
15
+ if (issues.length)
16
+ return { ok: false, issues: issues.map((issue) => `invalid_${issue}`) };
17
+ return { ok: true, output: parsed, issues: [] };
18
+ }
19
+ catch {
20
+ return { ok: false, issues: ['malformed_json'] };
21
+ }
22
+ }
23
+ function stripJsonFences(content) {
24
+ return content.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```\s*$/i, '').trim();
25
+ }
26
+ //# sourceMappingURL=glm-naruto-verifier-output.js.map
@@ -0,0 +1,42 @@
1
+ import path from 'node:path';
2
+ import { ensureDir, nowIso, writeJsonAtomic } from '../../../fsx.js';
3
+ export async function writeGlmNarutoWorkerArtifacts(input) {
4
+ const dir = path.join(input.root, '.sneakoscope', 'glm-naruto', input.missionId, 'workers', input.workerId);
5
+ await ensureDir(dir);
6
+ if (input.requestSummary) {
7
+ await writeJsonAtomic(path.join(dir, 'request-summary.json'), sanitizeArtifact({
8
+ schema: 'sks.glm-naruto-worker-request-summary.v1',
9
+ worker_id: input.workerId,
10
+ shard_id: input.shardId,
11
+ created_at: nowIso(),
12
+ ...input.requestSummary
13
+ }));
14
+ }
15
+ if (input.streamTrace)
16
+ await writeJsonAtomic(path.join(dir, 'stream-trace.json'), sanitizeArtifact(input.streamTrace));
17
+ if (input.patchEnvelope)
18
+ await writeJsonAtomic(path.join(dir, 'patch-envelope.json'), sanitizeArtifact(input.patchEnvelope));
19
+ if (input.gateResult)
20
+ await writeJsonAtomic(path.join(dir, 'gate-result.json'), sanitizeArtifact(input.gateResult));
21
+ if (input.termination) {
22
+ await writeJsonAtomic(path.join(dir, 'termination.json'), sanitizeArtifact({
23
+ schema: 'sks.glm-naruto-worker-termination.v1',
24
+ worker_id: input.workerId,
25
+ shard_id: input.shardId,
26
+ created_at: nowIso(),
27
+ ...input.termination
28
+ }));
29
+ }
30
+ return dir;
31
+ }
32
+ function sanitizeArtifact(value) {
33
+ return JSON.parse(JSON.stringify(value, (_key, raw) => {
34
+ if (typeof raw !== 'string')
35
+ return raw;
36
+ return raw
37
+ .replace(/Bearer\s+[A-Za-z0-9._~+/-]+/g, 'Bearer [REDACTED]')
38
+ .replace(/sk-or-[A-Za-z0-9_-]+/g, 'sk-or-[REDACTED]')
39
+ .replace(/sk-[A-Za-z0-9_-]{12,}/g, 'sk-[REDACTED]');
40
+ }));
41
+ }
42
+ //# sourceMappingURL=glm-naruto-worker-artifacts.js.map
@@ -1,8 +1,9 @@
1
1
  import { runPatchWorker } from './glm-naruto-worker-runtime.js';
2
- import { checkAndApplyGlmPatch } from '../glm-patch-apply.js';
3
- import { evaluateGlmSpeedGate } from '../glm-speed-gate.js';
4
2
  import { decideConcurrency } from './glm-naruto-concurrency-governor.js';
5
3
  import { planFileLeases } from './glm-naruto-file-lease.js';
4
+ import { evaluateGlmNarutoPatchCandidateGate } from './glm-naruto-patch-candidate-gate.js';
5
+ import { createPatchEnvelope } from './glm-naruto-patch-envelope.js';
6
+ import { writeGlmNarutoWorkerArtifacts } from './glm-naruto-worker-artifacts.js';
6
7
  export async function runPatchWorkerPool(input) {
7
8
  const envelopes = [];
8
9
  const traces = [];
@@ -34,6 +35,7 @@ export async function runPatchWorkerPool(input) {
34
35
  apiKey: input.apiKey,
35
36
  missionId: input.missionId,
36
37
  workerId,
38
+ root: input.cwd,
37
39
  shard: shardWithStrategy,
38
40
  contextSummary: input.contextSummary,
39
41
  timeoutMs: input.workerTimeoutMs
@@ -43,25 +45,42 @@ export async function runPatchWorkerPool(input) {
43
45
  const results = await Promise.allSettled(workerTasks);
44
46
  for (const result of results) {
45
47
  if (result.status === 'fulfilled' && result.value.ok && result.value.envelope) {
46
- const gate = evaluateGlmSpeedGate(result.value.envelope.patch);
48
+ const gate = await evaluateGlmNarutoPatchCandidateGate({
49
+ cwd: input.cwd,
50
+ envelope: result.value.envelope,
51
+ apply: false
52
+ });
47
53
  let envelope = result.value.envelope;
48
54
  if (gate.ok) {
49
- const applyCheck = await checkAndApplyGlmPatch({
50
- cwd: input.cwd,
51
- patch: envelope.patch,
52
- apply: false
55
+ envelope = createPatchEnvelope({
56
+ missionId: envelope.mission_id,
57
+ workerId: envelope.worker_id,
58
+ shardId: envelope.shard_id,
59
+ baseDigest: envelope.base_digest,
60
+ patch: gate.extracted_patch,
61
+ strategy: envelope.strategy,
62
+ reasoningEffort: envelope.reasoning_effort,
63
+ status: 'gate_passed',
64
+ warnings: envelope.warnings
53
65
  });
54
- envelope = applyCheck.ok
55
- ? { ...envelope, status: 'gate_passed' }
56
- : { ...envelope, status: 'gate_failed', blockers: [applyCheck.error.code] };
57
66
  }
58
67
  else {
59
68
  envelope = {
60
69
  ...envelope,
61
70
  status: 'gate_failed',
62
- blockers: gate.checks.filter((c) => !c.ok).map((c) => c.reason || c.id)
71
+ blockers: gate.blockers
63
72
  };
64
73
  }
74
+ await writeGlmNarutoWorkerArtifacts({
75
+ root: input.cwd,
76
+ missionId: input.missionId,
77
+ workerId: envelope.worker_id,
78
+ shardId: envelope.shard_id,
79
+ patchEnvelope: envelope,
80
+ gateResult: gate,
81
+ streamTrace: result.value.trace,
82
+ termination: { status: envelope.status, ok: gate.ok, blockers: envelope.blockers }
83
+ }).catch(() => undefined);
65
84
  envelopes.push(envelope);
66
85
  traces.push(result.value.trace);
67
86
  }