sneakoscope 4.0.11 → 4.0.12
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/README.md +7 -7
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/providers/glm/naruto/glm-naruto-apply-transaction.js +54 -8
- package/dist/core/providers/glm/naruto/glm-naruto-bench.js +74 -9
- package/dist/core/providers/glm/naruto/glm-naruto-command.js +6 -0
- package/dist/core/providers/glm/naruto/glm-naruto-final-seal.js +75 -0
- package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +41 -5
- package/dist/core/providers/glm/naruto/glm-naruto-targeted-checks.js +76 -0
- package/dist/core/providers/glm/naruto/glm-naruto-trace.js +50 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +61 -19
- package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +26 -4
- package/dist/core/providers/glm/naruto/glm-naruto-worker-scheduler.js +178 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worktree-worker.js +34 -4
- package/dist/core/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,15 +35,15 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **4.0.
|
|
38
|
+
SKS **4.0.12** seals GLM Naruto's production runtime path: worktree workers apply extracted unified diffs only, patch workers launch through a bounded adaptive scheduler, live bench compares true direct GLM against Naruto worker counts, final apply runs dirty-tree and targeted-check guards, and stop-gates reference a final seal artifact.
|
|
39
39
|
|
|
40
|
-
What changed in 4.0.
|
|
40
|
+
What changed in 4.0.12:
|
|
41
41
|
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
42
|
+
- **Extracted worktree patches.** `--worktree` parses `<sks_patch_candidate>` and records candidate/extracted patch hashes before any worker worktree apply.
|
|
43
|
+
- **Adaptive scheduler.** Patch workers use a finite launch queue with provider-health backpressure and retry-once handling for retryable 429/5xx/idle-timeout failures.
|
|
44
|
+
- **True direct-vs-Naruto bench.** `--bench --live --no-apply` compares direct GLM, Naruto 1, 4, 8, and 12 worker cases without fake zero metrics.
|
|
45
|
+
- **Transaction guards.** Final apply blocks dirty touched paths unless `--allow-dirty-apply` is explicit, runs targeted checks, and rolls back on validation failure by default.
|
|
46
|
+
- **Seal artifacts.** GLM Naruto writes `final-seal.json`, stop-gate final-seal evidence, `merge-rationale.md`, and `bench-report.md` for auditability.
|
|
47
47
|
|
|
48
48
|
What changed in 4.0.8:
|
|
49
49
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 4.0.
|
|
7
|
+
Some("--version") => println!("sks-rs 4.0.12"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '4.0.
|
|
8
|
+
export const PACKAGE_VERSION = '4.0.12';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -3,22 +3,28 @@ import path from 'node:path';
|
|
|
3
3
|
import { sha256, writeJsonAtomic, writeTextAtomic } from '../../../fsx.js';
|
|
4
4
|
import { parseUnifiedDiffPatch } from '../glm-patch-parser.js';
|
|
5
5
|
import { combineGlmNarutoPatches } from './glm-naruto-combined-patch.js';
|
|
6
|
+
import { runGlmNarutoTargetedChecks } from './glm-naruto-targeted-checks.js';
|
|
6
7
|
export async function runGlmNarutoApplyTransaction(input) {
|
|
7
8
|
const preStatus = await gitText(input.cwd, ['status', '--short']);
|
|
8
9
|
const preDiff = await gitText(input.cwd, ['diff', '--binary']);
|
|
9
10
|
const patch = combineGlmNarutoPatches(input.envelopes, input.selectedPatchIds);
|
|
10
11
|
const parsed = parseUnifiedDiffPatch(patch);
|
|
12
|
+
const dirtyTouchedPaths = await dirtyPaths(input.cwd, parsed.touchedPaths);
|
|
11
13
|
const patchPath = path.join(input.artifactDir, 'selected-combined.patch');
|
|
12
14
|
await writeTextAtomic(patchPath, patch);
|
|
13
15
|
const blockers = [];
|
|
14
16
|
let applyCheckPassed = false;
|
|
15
17
|
let applyPassed = false;
|
|
18
|
+
let targetedChecksPassed = null;
|
|
16
19
|
let rollbackAttempted = false;
|
|
17
20
|
let rollbackPassed = null;
|
|
18
21
|
let finalStatus = 'blocked';
|
|
19
22
|
if (!patch.trim()) {
|
|
20
23
|
blockers.push('combined_patch_empty');
|
|
21
24
|
}
|
|
25
|
+
else if (dirtyTouchedPaths.length > 0 && !input.allowDirtyApply) {
|
|
26
|
+
blockers.push(`dirty_touched_paths_before_apply:${dirtyTouchedPaths.join(',')}`);
|
|
27
|
+
}
|
|
22
28
|
else {
|
|
23
29
|
const checked = await gitApply(input.cwd, patch, ['apply', '--check', '--whitespace=nowarn', '-']);
|
|
24
30
|
applyCheckPassed = checked.code === 0;
|
|
@@ -28,16 +34,41 @@ export async function runGlmNarutoApplyTransaction(input) {
|
|
|
28
34
|
const applied = await gitApply(input.cwd, patch, ['apply', '--whitespace=nowarn', '-']);
|
|
29
35
|
applyPassed = applied.code === 0;
|
|
30
36
|
if (applyPassed) {
|
|
31
|
-
|
|
37
|
+
const targeted = await runGlmNarutoTargetedChecks({
|
|
38
|
+
cwd: input.cwd,
|
|
39
|
+
touchedPaths: parsed.touchedPaths,
|
|
40
|
+
artifactDir: input.artifactDir,
|
|
41
|
+
...(input.strictChecks !== undefined ? { strictChecks: input.strictChecks } : {})
|
|
42
|
+
});
|
|
43
|
+
targetedChecksPassed = targeted.ok;
|
|
44
|
+
if (targeted.ok) {
|
|
45
|
+
finalStatus = 'applied';
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
blockers.push(...targeted.blockers);
|
|
49
|
+
if (!input.noRollback) {
|
|
50
|
+
rollbackAttempted = true;
|
|
51
|
+
const rollback = await gitApply(input.cwd, patch, ['apply', '-R', '--whitespace=nowarn', '-']);
|
|
52
|
+
rollbackPassed = rollback.code === 0;
|
|
53
|
+
finalStatus = rollbackPassed ? 'rolled_back' : 'blocked';
|
|
54
|
+
if (!rollbackPassed)
|
|
55
|
+
blockers.push(rollback.stderr || rollback.stdout || 'rollback_reverse_patch_failed');
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
finalStatus = 'blocked';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
32
61
|
}
|
|
33
62
|
else {
|
|
34
63
|
blockers.push(applied.stderr || applied.stdout || 'git_apply_failed');
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
if (!input.noRollback) {
|
|
65
|
+
rollbackAttempted = true;
|
|
66
|
+
const rollback = await gitApply(input.cwd, patch, ['apply', '-R', '--whitespace=nowarn', '-']);
|
|
67
|
+
rollbackPassed = rollback.code === 0;
|
|
68
|
+
finalStatus = rollbackPassed ? 'rolled_back' : 'blocked';
|
|
69
|
+
if (!rollbackPassed)
|
|
70
|
+
blockers.push(rollback.stderr || rollback.stdout || 'rollback_reverse_patch_failed');
|
|
71
|
+
}
|
|
41
72
|
}
|
|
42
73
|
}
|
|
43
74
|
}
|
|
@@ -47,12 +78,15 @@ export async function runGlmNarutoApplyTransaction(input) {
|
|
|
47
78
|
mission_id: input.missionId,
|
|
48
79
|
selected_patch_ids: input.selectedPatchIds,
|
|
49
80
|
touched_paths: parsed.touchedPaths,
|
|
81
|
+
dirty_touched_paths_before_apply: dirtyTouchedPaths,
|
|
82
|
+
dirty_policy: input.allowDirtyApply ? 'allow' : 'block',
|
|
50
83
|
pre_status: preStatus,
|
|
51
84
|
pre_diff_sha256: sha256(preDiff),
|
|
85
|
+
post_diff_sha256: sha256(postDiff),
|
|
52
86
|
combined_patch_sha256: sha256(patch),
|
|
53
87
|
apply_check_passed: applyCheckPassed,
|
|
54
88
|
apply_passed: applyPassed,
|
|
55
|
-
targeted_checks_passed:
|
|
89
|
+
targeted_checks_passed: targetedChecksPassed,
|
|
56
90
|
rollback_attempted: rollbackAttempted,
|
|
57
91
|
rollback_passed: rollbackPassed,
|
|
58
92
|
final_status: finalStatus,
|
|
@@ -76,6 +110,18 @@ function gitText(cwd, args) {
|
|
|
76
110
|
child.on('close', () => resolve(stdout));
|
|
77
111
|
});
|
|
78
112
|
}
|
|
113
|
+
async function dirtyPaths(cwd, paths) {
|
|
114
|
+
if (paths.length === 0)
|
|
115
|
+
return [];
|
|
116
|
+
const status = await gitText(cwd, ['status', '--short', '--', ...paths]);
|
|
117
|
+
return status.split(/\r?\n/)
|
|
118
|
+
.map((line) => line.trimEnd())
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
.map((line) => {
|
|
121
|
+
const raw = line.slice(3).trim();
|
|
122
|
+
return raw.includes(' -> ') ? raw.split(' -> ').pop().trim() : raw;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
79
125
|
function gitApply(cwd, patch, args) {
|
|
80
126
|
return new Promise((resolve) => {
|
|
81
127
|
const child = spawn('git', [...args], { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import { nowIso } from '../../../fsx.js';
|
|
1
|
+
import { nowIso, writeTextAtomic } from '../../../fsx.js';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fsp from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
6
|
+
import { runGlmDirectSpeedRun } from '../glm-direct-run.js';
|
|
6
7
|
import { resolveOpenRouterApiKey } from '../../openrouter/openrouter-secret-store.js';
|
|
7
8
|
import { runGlmNarutoMission } from './glm-naruto-orchestrator.js';
|
|
8
9
|
import { summarizeGlmNarutoWorkerMetrics } from './glm-naruto-metrics.js';
|
|
9
|
-
export async function runGlmNarutoBench(root, args = []) {
|
|
10
|
+
export async function runGlmNarutoBench(root, args = [], deps = {}) {
|
|
10
11
|
const live = args.includes('--live');
|
|
11
12
|
const execute = args.includes('--execute');
|
|
12
13
|
const started = Date.now();
|
|
14
|
+
const runDirect = deps.runDirect ?? runGlmDirectSpeedRun;
|
|
15
|
+
const runNaruto = deps.runNaruto ?? runGlmNarutoMission;
|
|
13
16
|
if (execute && !live) {
|
|
14
17
|
return blocked(root, ['execute_requires_live_flag']);
|
|
15
18
|
}
|
|
16
19
|
if (!live) {
|
|
17
20
|
return {
|
|
18
21
|
schema: 'sks.glm-naruto-bench.v1',
|
|
19
|
-
version: '4.0.
|
|
22
|
+
version: '4.0.12',
|
|
20
23
|
generated_at: nowIso(),
|
|
21
24
|
status: 'dry_run',
|
|
22
25
|
model: GLM_52_OPENROUTER_MODEL,
|
|
@@ -39,9 +42,17 @@ export async function runGlmNarutoBench(root, args = []) {
|
|
|
39
42
|
await fsp.mkdir(path.join(fixture, 'src'), { recursive: true });
|
|
40
43
|
await fsp.writeFile(path.join(fixture, 'src', 'bench-target.ts'), 'export const value = 1;\n', 'utf8');
|
|
41
44
|
const cases = [];
|
|
45
|
+
const directStarted = Date.now();
|
|
46
|
+
const direct = await runDirect({
|
|
47
|
+
cwd: fixture,
|
|
48
|
+
task: 'Change src/bench-target.ts so value is 2. Return the smallest patch only.',
|
|
49
|
+
args: ['--bench', '--live', '--dry-run'],
|
|
50
|
+
dryRun: true
|
|
51
|
+
});
|
|
52
|
+
cases.push(directBenchCase(direct, Date.now() - directStarted));
|
|
42
53
|
for (const workers of [1, 4, 8, 12]) {
|
|
43
54
|
const caseStarted = Date.now();
|
|
44
|
-
const result = await
|
|
55
|
+
const result = await runNaruto({
|
|
45
56
|
cwd: fixture,
|
|
46
57
|
task: 'Change src/bench-target.ts so value is 2. Return the smallest patch only.',
|
|
47
58
|
args: ['--bench', '--live', '--no-apply'],
|
|
@@ -52,7 +63,8 @@ export async function runGlmNarutoBench(root, args = []) {
|
|
|
52
63
|
const traces = await readWorkerTraces(result.artifact_dir);
|
|
53
64
|
const metrics = summarizeGlmNarutoWorkerMetrics(traces);
|
|
54
65
|
cases.push({
|
|
55
|
-
name: workers === 1 ? '
|
|
66
|
+
name: `GLM Naruto ${workers} worker${workers === 1 ? '' : 's'}`,
|
|
67
|
+
kind: 'glm-naruto',
|
|
56
68
|
workers,
|
|
57
69
|
wall_clock_ms: Date.now() - caseStarted,
|
|
58
70
|
p50_ttft_ms: metrics.p50_ttft_ms,
|
|
@@ -60,19 +72,21 @@ export async function runGlmNarutoBench(root, args = []) {
|
|
|
60
72
|
p50_total_ms: metrics.p50_total_ms,
|
|
61
73
|
p90_total_ms: metrics.p90_total_ms,
|
|
62
74
|
candidate_count: result.patch_candidates,
|
|
63
|
-
gate_pass_rate: result.patch_candidates ? result.gate_passed_candidates / result.patch_candidates :
|
|
75
|
+
gate_pass_rate: result.patch_candidates ? result.gate_passed_candidates / result.patch_candidates : null,
|
|
64
76
|
verifier_pass_rate: metrics.verifier_pass_rate,
|
|
65
77
|
merge_success: result.mergeable_candidates > 0,
|
|
66
78
|
cached_tokens_sum: metrics.cached_tokens_sum,
|
|
67
79
|
cache_write_tokens_sum: metrics.cache_write_tokens_sum,
|
|
68
80
|
reasoning_tokens_sum: metrics.reasoning_tokens_sum,
|
|
81
|
+
metric_status: metrics.p50_total_ms === null && metrics.p50_ttft_ms === null ? 'unavailable' : 'measured',
|
|
69
82
|
workers_completed: metrics.workers_completed,
|
|
70
83
|
workers_failed: metrics.workers_failed
|
|
71
84
|
});
|
|
72
85
|
}
|
|
86
|
+
await writeBenchReport(root, cases).catch(() => undefined);
|
|
73
87
|
return {
|
|
74
88
|
schema: 'sks.glm-naruto-bench.v1',
|
|
75
|
-
version: '4.0.
|
|
89
|
+
version: '4.0.12',
|
|
76
90
|
generated_at: nowIso(),
|
|
77
91
|
status: 'live',
|
|
78
92
|
model: GLM_52_OPENROUTER_MODEL,
|
|
@@ -82,17 +96,48 @@ export async function runGlmNarutoBench(root, args = []) {
|
|
|
82
96
|
simulated_workers: Math.max(...cases.map((row) => row.workers)),
|
|
83
97
|
simulated_waves: cases.length,
|
|
84
98
|
simulated_patch_candidates: cases.reduce((sum, row) => sum + row.candidate_count, 0),
|
|
85
|
-
simulated_gate_passed: cases.reduce((sum, row) => sum + Math.round(row.candidate_count * row.gate_pass_rate), 0),
|
|
99
|
+
simulated_gate_passed: cases.reduce((sum, row) => sum + Math.round(row.candidate_count * (row.gate_pass_rate ?? 0)), 0),
|
|
86
100
|
simulated_mergeable: cases.filter((row) => row.merge_success).length,
|
|
87
101
|
wall_clock_ms: Date.now() - started
|
|
88
102
|
},
|
|
89
103
|
warnings: ['live_bench_no_apply_temp_repo']
|
|
90
104
|
};
|
|
91
105
|
}
|
|
106
|
+
async function writeBenchReport(root, cases) {
|
|
107
|
+
const rows = cases.map((row) => [
|
|
108
|
+
row.name,
|
|
109
|
+
row.kind,
|
|
110
|
+
String(row.workers),
|
|
111
|
+
String(row.wall_clock_ms),
|
|
112
|
+
String(row.p50_ttft_ms ?? 'unavailable'),
|
|
113
|
+
String(row.p90_ttft_ms ?? 'unavailable'),
|
|
114
|
+
String(row.p50_total_ms ?? 'unavailable'),
|
|
115
|
+
String(row.p90_total_ms ?? 'unavailable'),
|
|
116
|
+
String(row.gate_pass_rate ?? 'n/a'),
|
|
117
|
+
String(row.verifier_pass_rate ?? 'n/a'),
|
|
118
|
+
String(row.metric_status)
|
|
119
|
+
]);
|
|
120
|
+
const fastest = [...cases].sort((a, b) => a.wall_clock_ms - b.wall_clock_ms)[0] ?? null;
|
|
121
|
+
const md = [
|
|
122
|
+
'# GLM Naruto Bench Report',
|
|
123
|
+
'',
|
|
124
|
+
`Generated: ${nowIso()}`,
|
|
125
|
+
`Model: ${GLM_52_OPENROUTER_MODEL}`,
|
|
126
|
+
'',
|
|
127
|
+
'| Case | Kind | Workers | Wall ms | TTFT p50 | TTFT p90 | Total p50 | Total p90 | Gate pass | Verifier pass | Metric status |',
|
|
128
|
+
'| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |',
|
|
129
|
+
...rows.map((row) => `| ${row.join(' | ')} |`),
|
|
130
|
+
'',
|
|
131
|
+
`Fastest wall-clock case: ${fastest ? fastest.name : 'unavailable'}`,
|
|
132
|
+
'Missing usage metrics are reported as `unavailable` or `n/a`, never as fake zero.',
|
|
133
|
+
''
|
|
134
|
+
].join('\n');
|
|
135
|
+
await writeTextAtomic(path.join(root, '.sneakoscope', 'glm-naruto', 'bench-report.md'), md);
|
|
136
|
+
}
|
|
92
137
|
function blocked(root, warnings) {
|
|
93
138
|
return {
|
|
94
139
|
schema: 'sks.glm-naruto-bench.v1',
|
|
95
|
-
version: '4.0.
|
|
140
|
+
version: '4.0.12',
|
|
96
141
|
generated_at: nowIso(),
|
|
97
142
|
status: 'blocked',
|
|
98
143
|
model: GLM_52_OPENROUTER_MODEL,
|
|
@@ -108,6 +153,26 @@ function blocked(root, warnings) {
|
|
|
108
153
|
warnings
|
|
109
154
|
};
|
|
110
155
|
}
|
|
156
|
+
function directBenchCase(result, wallClockMs) {
|
|
157
|
+
return {
|
|
158
|
+
name: 'direct GLM speed path',
|
|
159
|
+
kind: 'direct-glm',
|
|
160
|
+
workers: 1,
|
|
161
|
+
wall_clock_ms: wallClockMs,
|
|
162
|
+
p50_ttft_ms: null,
|
|
163
|
+
p90_ttft_ms: null,
|
|
164
|
+
p50_total_ms: null,
|
|
165
|
+
p90_total_ms: null,
|
|
166
|
+
candidate_count: result.ok ? 1 : 0,
|
|
167
|
+
gate_pass_rate: result.ok ? 1 : null,
|
|
168
|
+
verifier_pass_rate: null,
|
|
169
|
+
merge_success: result.ok,
|
|
170
|
+
cached_tokens_sum: null,
|
|
171
|
+
cache_write_tokens_sum: null,
|
|
172
|
+
reasoning_tokens_sum: null,
|
|
173
|
+
metric_status: 'unavailable'
|
|
174
|
+
};
|
|
175
|
+
}
|
|
111
176
|
async function readWorkerTraces(artifactDir) {
|
|
112
177
|
if (!artifactDir)
|
|
113
178
|
return [];
|
|
@@ -46,6 +46,9 @@ export async function glmNarutoCommand(args = []) {
|
|
|
46
46
|
const allowPatchEnvelopeFallback = flag(args, '--allow-patch-envelope-fallback');
|
|
47
47
|
const noApply = flag(args, '--no-apply');
|
|
48
48
|
const skipVerifier = flag(args, '--skip-verifier');
|
|
49
|
+
const allowDirtyApply = flag(args, '--allow-dirty-apply');
|
|
50
|
+
const noRollback = flag(args, '--no-rollback');
|
|
51
|
+
const strictChecks = flag(args, '--strict-checks');
|
|
49
52
|
const mergeStrategy = readOption(args, '--merge-strategy', 'deterministic');
|
|
50
53
|
const result = await runGlmNarutoMission({
|
|
51
54
|
cwd: process.cwd(),
|
|
@@ -62,6 +65,9 @@ export async function glmNarutoCommand(args = []) {
|
|
|
62
65
|
cleanupWorktrees,
|
|
63
66
|
noApply: noApply || flag(args, '--dry-run'),
|
|
64
67
|
skipVerifier,
|
|
68
|
+
allowDirtyApply,
|
|
69
|
+
noRollback,
|
|
70
|
+
strictChecks,
|
|
65
71
|
mergeStrategy
|
|
66
72
|
});
|
|
67
73
|
if (flag(args, '--json')) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeJsonAtomic } from '../../../fsx.js';
|
|
3
|
+
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
4
|
+
export async function writeGlmNarutoFinalSeal(input) {
|
|
5
|
+
const mismatches = [
|
|
6
|
+
...input.envelopes.filter((env) => env.model !== GLM_52_OPENROUTER_MODEL || env.gpt_fallback_allowed !== false).map((env) => `envelope:${env.worker_id}`),
|
|
7
|
+
...input.traces.filter((trace) => trace.model !== GLM_52_OPENROUTER_MODEL).map((trace) => `trace:${trace.worker_id}`)
|
|
8
|
+
];
|
|
9
|
+
const isolationViolations = input.isolationPolicy.selected === 'blocked' ? input.isolationPolicy.blockers : [];
|
|
10
|
+
const status = finalSealStatus({
|
|
11
|
+
result: input.result,
|
|
12
|
+
secretOk: input.secretAudit.ok,
|
|
13
|
+
mismatches,
|
|
14
|
+
isolationViolations,
|
|
15
|
+
queueDrained: input.scheduler.queue_drained
|
|
16
|
+
});
|
|
17
|
+
const seal = {
|
|
18
|
+
schema: 'sks.glm-naruto-final-seal.v1',
|
|
19
|
+
mission_id: input.missionId,
|
|
20
|
+
status,
|
|
21
|
+
model_lock: {
|
|
22
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
23
|
+
gpt_fallback_allowed: false,
|
|
24
|
+
requests_checked: input.envelopes.length + input.traces.length,
|
|
25
|
+
mismatches
|
|
26
|
+
},
|
|
27
|
+
isolation: {
|
|
28
|
+
selected: input.isolationPolicy.selected,
|
|
29
|
+
workers_write_main_workspace: false,
|
|
30
|
+
violations: isolationViolations
|
|
31
|
+
},
|
|
32
|
+
scheduler: {
|
|
33
|
+
bounded: true,
|
|
34
|
+
max_observed_active_workers: input.scheduler.max_observed_active_workers,
|
|
35
|
+
queue_drained: input.scheduler.queue_drained,
|
|
36
|
+
backpressure_events: input.scheduler.backpressure_events
|
|
37
|
+
},
|
|
38
|
+
candidates: {
|
|
39
|
+
total: input.envelopes.length,
|
|
40
|
+
gate_passed: input.envelopes.filter((env) => env.status === 'gate_passed').length,
|
|
41
|
+
verifier_passed: input.envelopes.filter((env) => env.verification_passed === true).length,
|
|
42
|
+
selected: input.selectedPatchIds
|
|
43
|
+
},
|
|
44
|
+
apply: {
|
|
45
|
+
attempted: input.applyTransaction !== null,
|
|
46
|
+
transaction_path: input.applyTransaction ? path.join(input.artifactDir, 'apply-transaction.json') : null,
|
|
47
|
+
final_status: input.applyTransaction?.final_status ?? null,
|
|
48
|
+
rollback_attempted: input.applyTransaction?.rollback_attempted ?? false,
|
|
49
|
+
rollback_passed: input.applyTransaction?.rollback_passed ?? null
|
|
50
|
+
},
|
|
51
|
+
secret_audit: {
|
|
52
|
+
ok: input.secretAudit.ok,
|
|
53
|
+
findings: input.secretAudit.findings ?? []
|
|
54
|
+
},
|
|
55
|
+
stop_gate: {
|
|
56
|
+
path: input.stopGatePath,
|
|
57
|
+
passed: input.stopGatePassed
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const out = path.join(input.artifactDir, 'final-seal.json');
|
|
61
|
+
await writeJsonAtomic(out, seal);
|
|
62
|
+
return { seal, path: out, passed: seal.status === 'passed' };
|
|
63
|
+
}
|
|
64
|
+
function finalSealStatus(input) {
|
|
65
|
+
if (!input.secretOk || input.mismatches.length > 0 || input.isolationViolations.length > 0 || !input.queueDrained)
|
|
66
|
+
return 'blocked';
|
|
67
|
+
if (input.result.ok)
|
|
68
|
+
return 'passed';
|
|
69
|
+
if (input.result.status === 'partial_candidates')
|
|
70
|
+
return 'partial';
|
|
71
|
+
if (input.result.status === 'blocked')
|
|
72
|
+
return 'blocked';
|
|
73
|
+
return 'failed';
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=glm-naruto-final-seal.js.map
|
|
@@ -20,6 +20,7 @@ import { getGitHead, getGitRoot } from './glm-naruto-worktree-manager.js';
|
|
|
20
20
|
import { buildGlmNarutoCandidateScoreboard } from './glm-naruto-scoreboard.js';
|
|
21
21
|
import { runGlmNarutoApplyTransaction } from './glm-naruto-apply-transaction.js';
|
|
22
22
|
import { finalizeGlmNarutoTerminal } from './glm-naruto-terminal.js';
|
|
23
|
+
import { writeGlmNarutoFinalSeal } from './glm-naruto-final-seal.js';
|
|
23
24
|
import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
|
|
24
25
|
export async function runGlmNarutoMission(input) {
|
|
25
26
|
const missionId = input.missionId || `glm-naruto-${nowIso().replace(/[:.]/g, '-')}`;
|
|
@@ -94,7 +95,8 @@ export async function runGlmNarutoMission(input) {
|
|
|
94
95
|
strategies: strategyMap,
|
|
95
96
|
isolationMode: isolationPolicy.selected,
|
|
96
97
|
cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
|
|
97
|
-
baseCommit
|
|
98
|
+
baseCommit,
|
|
99
|
+
health: healthTracker
|
|
98
100
|
});
|
|
99
101
|
for (const trace of poolResult.traces) {
|
|
100
102
|
traceState = recordWorkerTrace(traceState, trace);
|
|
@@ -112,6 +114,7 @@ export async function runGlmNarutoMission(input) {
|
|
|
112
114
|
let envelopes = poolResult.envelopes;
|
|
113
115
|
let failedShardIds = poolResult.failedShardIds;
|
|
114
116
|
let repairWaves = 0;
|
|
117
|
+
let schedulerSummary = poolResult.schedulerSummary;
|
|
115
118
|
// Repair wave if needed
|
|
116
119
|
if (failedShardIds.length > 0 && repairWaves < GLM_NARUTO_LIMITS.max_repair_waves) {
|
|
117
120
|
const repairPlan = planRepairWave({
|
|
@@ -132,9 +135,15 @@ export async function runGlmNarutoMission(input) {
|
|
|
132
135
|
strategies: new Map(repairPlan.shardsToRepair.map((s) => [s.id, [s.strategy]])),
|
|
133
136
|
isolationMode: isolationPolicy.selected,
|
|
134
137
|
cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
|
|
135
|
-
baseCommit
|
|
138
|
+
baseCommit,
|
|
139
|
+
health: healthTracker
|
|
136
140
|
});
|
|
137
141
|
envelopes = [...envelopes, ...repairPool.envelopes];
|
|
142
|
+
schedulerSummary = {
|
|
143
|
+
max_observed_active_workers: Math.max(schedulerSummary.max_observed_active_workers, repairPool.schedulerSummary.max_observed_active_workers),
|
|
144
|
+
backpressure_events: schedulerSummary.backpressure_events + repairPool.schedulerSummary.backpressure_events,
|
|
145
|
+
queue_drained: schedulerSummary.queue_drained && repairPool.schedulerSummary.queue_drained
|
|
146
|
+
};
|
|
138
147
|
for (const trace of repairPool.traces) {
|
|
139
148
|
traceState = recordWorkerTrace(traceState, trace);
|
|
140
149
|
}
|
|
@@ -229,7 +238,10 @@ export async function runGlmNarutoMission(input) {
|
|
|
229
238
|
missionId,
|
|
230
239
|
envelopes,
|
|
231
240
|
selectedPatchIds: mergePlan.selected_patches,
|
|
232
|
-
artifactDir
|
|
241
|
+
artifactDir,
|
|
242
|
+
...(input.allowDirtyApply !== undefined ? { allowDirtyApply: input.allowDirtyApply } : {}),
|
|
243
|
+
...(input.noRollback !== undefined ? { noRollback: input.noRollback } : {}),
|
|
244
|
+
...(input.strictChecks !== undefined ? { strictChecks: input.strictChecks } : {})
|
|
233
245
|
});
|
|
234
246
|
applyTransaction = transactionResult.transaction;
|
|
235
247
|
appliedPatches = transactionResult.ok ? transactionResult.applied.length : 0;
|
|
@@ -300,13 +312,33 @@ export async function runGlmNarutoMission(input) {
|
|
|
300
312
|
findings: [`audit_failed:${err instanceof Error ? err.message : String(err)}`]
|
|
301
313
|
}));
|
|
302
314
|
await writeJsonAtomic(path.join(writtenArtifactDir, 'secret-audit.json'), secretAudit).catch(() => undefined);
|
|
315
|
+
const predictedStopGatePath = path.join(cwd, '.sneakoscope', 'missions', missionId, 'stop-gate.json');
|
|
316
|
+
const finalSeal = await writeGlmNarutoFinalSeal({
|
|
317
|
+
artifactDir: writtenArtifactDir,
|
|
318
|
+
missionId,
|
|
319
|
+
result,
|
|
320
|
+
envelopes,
|
|
321
|
+
traces: traceState.workerTraces,
|
|
322
|
+
isolationPolicy,
|
|
323
|
+
scheduler: schedulerSummary,
|
|
324
|
+
selectedPatchIds: mergePlan.selected_patches,
|
|
325
|
+
applyTransaction,
|
|
326
|
+
secretAudit,
|
|
327
|
+
stopGatePath: predictedStopGatePath,
|
|
328
|
+
stopGatePassed: result.ok && secretAudit.ok
|
|
329
|
+
}).catch((err) => ({
|
|
330
|
+
seal: null,
|
|
331
|
+
path: path.join(writtenArtifactDir, 'final-seal.json'),
|
|
332
|
+
passed: false,
|
|
333
|
+
error: err instanceof Error ? err.message : String(err)
|
|
334
|
+
}));
|
|
303
335
|
// 4.0.9: Write canonical stop-gate artifacts for hook resolution.
|
|
304
336
|
await writeFinalStopGate({
|
|
305
337
|
root: cwd,
|
|
306
338
|
missionId,
|
|
307
339
|
route: 'GLM_NARUTO',
|
|
308
340
|
routeCommand: '$Naruto',
|
|
309
|
-
status: result.ok && secretAudit.ok ? 'passed' : (terminalState === 'blocked' || !secretAudit.ok ? 'blocked' : 'failed'),
|
|
341
|
+
status: result.ok && secretAudit.ok && finalSeal.passed ? 'passed' : (terminalState === 'blocked' || !secretAudit.ok || !finalSeal.passed ? 'blocked' : 'failed'),
|
|
310
342
|
terminal: terminalState === 'completed' || terminalState === 'blocked',
|
|
311
343
|
terminalState,
|
|
312
344
|
evidence: {
|
|
@@ -316,12 +348,16 @@ export async function runGlmNarutoMission(input) {
|
|
|
316
348
|
per_worker_artifacts: true,
|
|
317
349
|
verifier_wave_run: verifierWaveRun,
|
|
318
350
|
model_guard_enforced: true,
|
|
351
|
+
final_seal_passed: finalSeal.passed,
|
|
352
|
+
final_seal_path: finalSeal.path,
|
|
319
353
|
proof_required: false,
|
|
320
354
|
proof_passed: true,
|
|
321
355
|
reflection_required: false,
|
|
322
356
|
reflection_passed: 'not_required',
|
|
323
357
|
},
|
|
324
|
-
blockers: secretAudit.ok
|
|
358
|
+
blockers: secretAudit.ok
|
|
359
|
+
? (finalSeal.passed ? (result.blockers || []) : [...(result.blockers || []), 'glm_naruto_final_seal_not_passed'])
|
|
360
|
+
: ['glm_naruto_secret_leak_detected'],
|
|
325
361
|
nativeGateFile: 'termination.json',
|
|
326
362
|
}).catch(() => null);
|
|
327
363
|
return { ...result, artifact_dir: writtenArtifactDir };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { exists, writeJsonAtomic } from '../../../fsx.js';
|
|
5
|
+
export async function runGlmNarutoTargetedChecks(input) {
|
|
6
|
+
const checks = [];
|
|
7
|
+
const touchedPaths = [...new Set(input.touchedPaths)].sort();
|
|
8
|
+
const diffCheck = await runProcess(input.cwd, ['git', 'diff', '--check']);
|
|
9
|
+
checks.push({
|
|
10
|
+
id: 'git_diff_check',
|
|
11
|
+
ok: diffCheck.code === 0,
|
|
12
|
+
command: ['git', 'diff', '--check'],
|
|
13
|
+
...(diffCheck.code === 0 ? {} : { message: diffCheck.stderr || diffCheck.stdout || 'git diff --check failed' })
|
|
14
|
+
});
|
|
15
|
+
for (const rel of touchedPaths.filter((file) => /\.(?:json)$/i.test(file))) {
|
|
16
|
+
const absolute = path.join(input.cwd, rel);
|
|
17
|
+
try {
|
|
18
|
+
JSON.parse(await fsp.readFile(absolute, 'utf8'));
|
|
19
|
+
checks.push({ id: 'json_parse', ok: true, path: rel });
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
checks.push({ id: 'json_parse', ok: false, path: rel, message: err instanceof Error ? err.message : String(err) });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const rel of touchedPaths.filter((file) => /\.(?:js|mjs|cjs)$/i.test(file))) {
|
|
26
|
+
const nodeCheck = await runProcess(input.cwd, ['node', '--check', rel]);
|
|
27
|
+
checks.push({
|
|
28
|
+
id: 'node_check',
|
|
29
|
+
ok: nodeCheck.code === 0,
|
|
30
|
+
path: rel,
|
|
31
|
+
command: ['node', '--check', rel],
|
|
32
|
+
...(nodeCheck.code === 0 ? {} : { message: nodeCheck.stderr || nodeCheck.stdout || 'node --check failed' })
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const tsTouched = touchedPaths.filter((file) => /\.(?:ts|tsx)$/i.test(file));
|
|
36
|
+
const tscThreshold = input.tscFileThreshold ?? 3;
|
|
37
|
+
if (tsTouched.length > 0 && (input.strictChecks || tsTouched.length <= tscThreshold)) {
|
|
38
|
+
const tscBin = path.join(input.cwd, 'node_modules', '.bin', 'tsc');
|
|
39
|
+
const tsconfig = path.join(input.cwd, 'tsconfig.json');
|
|
40
|
+
if (await exists(tscBin) && await exists(tsconfig)) {
|
|
41
|
+
const tsc = await runProcess(input.cwd, [tscBin, '-p', 'tsconfig.json', '--noEmit']);
|
|
42
|
+
checks.push({
|
|
43
|
+
id: 'tsc_no_emit',
|
|
44
|
+
ok: tsc.code === 0,
|
|
45
|
+
command: [tscBin, '-p', 'tsconfig.json', '--noEmit'],
|
|
46
|
+
...(tsc.code === 0 ? {} : { message: tsc.stderr || tsc.stdout || 'tsc --noEmit failed' })
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
checks.push({ id: 'tsc_no_emit', ok: true, skipped: true, message: 'tsc_or_tsconfig_unavailable' });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const blockers = checks.filter((check) => !check.ok).map((check) => `${check.id}${check.path ? `:${check.path}` : ''}`);
|
|
54
|
+
const result = {
|
|
55
|
+
schema: 'sks.glm-naruto-targeted-checks.v1',
|
|
56
|
+
ok: blockers.length === 0,
|
|
57
|
+
touched_paths: touchedPaths,
|
|
58
|
+
checks,
|
|
59
|
+
blockers
|
|
60
|
+
};
|
|
61
|
+
if (input.artifactDir)
|
|
62
|
+
await writeJsonAtomic(path.join(input.artifactDir, 'targeted-checks.json'), result);
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
function runProcess(cwd, command) {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const child = spawn(command[0], command.slice(1), { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
68
|
+
let stdout = '';
|
|
69
|
+
let stderr = '';
|
|
70
|
+
child.stdout.on('data', (chunk) => { stdout += String(chunk); });
|
|
71
|
+
child.stderr.on('data', (chunk) => { stderr += String(chunk); });
|
|
72
|
+
child.on('close', (code) => resolve({ code, stdout, stderr }));
|
|
73
|
+
child.on('error', (err) => resolve({ code: 1, stdout, stderr: err.message }));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=glm-naruto-targeted-checks.js.map
|
|
@@ -40,6 +40,14 @@ export async function writeMissionArtifacts(input) {
|
|
|
40
40
|
await writeJsonAtomic(path.join(dir, 'apply-result.json'), sanitizeArtifact(input.applyResult));
|
|
41
41
|
if (input.applyTransaction)
|
|
42
42
|
await writeJsonAtomic(path.join(dir, 'apply-transaction.json'), sanitizeArtifact(input.applyTransaction));
|
|
43
|
+
if (input.mergePlan) {
|
|
44
|
+
await writeTextAtomic(path.join(dir, 'merge-rationale.md'), renderMergeRationale({
|
|
45
|
+
mergePlan: input.mergePlan,
|
|
46
|
+
conflictGraph: input.conflictGraph,
|
|
47
|
+
candidateScoreboard: input.candidateScoreboard,
|
|
48
|
+
applyTransaction: input.applyTransaction
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
43
51
|
if (input.verificationSummary)
|
|
44
52
|
await writeJsonAtomic(path.join(dir, 'verification-summary.json'), sanitizeArtifact(input.verificationSummary));
|
|
45
53
|
if (input.missionResult)
|
|
@@ -85,6 +93,48 @@ export async function writeMissionArtifacts(input) {
|
|
|
85
93
|
}
|
|
86
94
|
return dir;
|
|
87
95
|
}
|
|
96
|
+
function renderMergeRationale(input) {
|
|
97
|
+
const plan = input.mergePlan;
|
|
98
|
+
const graph = input.conflictGraph;
|
|
99
|
+
const scoreboard = input.candidateScoreboard;
|
|
100
|
+
const tx = input.applyTransaction;
|
|
101
|
+
const selected = Array.isArray(plan?.selected_patches) ? plan.selected_patches : [];
|
|
102
|
+
const scores = Array.isArray(scoreboard?.scores) ? scoreboard.scores : [];
|
|
103
|
+
const edges = Array.isArray(graph?.edges) ? graph.edges : [];
|
|
104
|
+
const rejected = scores
|
|
105
|
+
.filter((score) => !selected.includes(score.patch_id))
|
|
106
|
+
.map((score) => `- ${score.patch_id}: score=${score.total_score}; disqualified=${Boolean(score.disqualified)}; reasons=${(score.disqualification_reasons || []).join(', ') || 'none'}`);
|
|
107
|
+
const selectedRows = selected.map((patchId) => {
|
|
108
|
+
const score = scores.find((row) => row.patch_id === patchId);
|
|
109
|
+
return `- ${patchId}: score=${score?.total_score ?? 'n/a'}; components=${JSON.stringify(score?.components ?? {})}`;
|
|
110
|
+
});
|
|
111
|
+
const conflictRows = edges.map((edge) => `- ${edge.left_patch_id} vs ${edge.right_patch_id}: ${edge.reason}`);
|
|
112
|
+
return [
|
|
113
|
+
'# GLM Naruto Merge Rationale',
|
|
114
|
+
'',
|
|
115
|
+
`Mission: ${plan?.mission_id ?? 'unknown'}`,
|
|
116
|
+
`Strategy: ${plan?.strategy ?? 'unknown'}`,
|
|
117
|
+
`Rationale: ${plan?.rationale ?? 'not_recorded'}`,
|
|
118
|
+
'',
|
|
119
|
+
'## Selected Patch IDs',
|
|
120
|
+
selectedRows.length ? selectedRows.join('\n') : '- none',
|
|
121
|
+
'',
|
|
122
|
+
'## Rejected Patch IDs',
|
|
123
|
+
rejected.length ? rejected.join('\n') : '- none',
|
|
124
|
+
'',
|
|
125
|
+
'## Conflicts',
|
|
126
|
+
conflictRows.length ? conflictRows.join('\n') : '- none',
|
|
127
|
+
'',
|
|
128
|
+
'## Apply Transaction',
|
|
129
|
+
`- final_status: ${tx?.final_status ?? 'not_attempted'}`,
|
|
130
|
+
`- apply_passed: ${tx?.apply_passed ?? false}`,
|
|
131
|
+
`- targeted_checks_passed: ${tx?.targeted_checks_passed ?? null}`,
|
|
132
|
+
`- rollback_attempted: ${tx?.rollback_attempted ?? false}`,
|
|
133
|
+
`- rollback_passed: ${tx?.rollback_passed ?? null}`,
|
|
134
|
+
`- blockers: ${(tx?.blockers || []).join(', ') || 'none'}`,
|
|
135
|
+
''
|
|
136
|
+
].join('\n');
|
|
137
|
+
}
|
|
88
138
|
function sanitizeArtifact(value) {
|
|
89
139
|
return JSON.parse(JSON.stringify(value, (key, raw) => {
|
|
90
140
|
if (isSecretLikeKey(key) && typeof raw === 'string' && raw.trim() && !isAllowedRedaction(raw))
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeJsonAtomic, writeTextAtomic } from '../../../fsx.js';
|
|
1
3
|
import { runPatchWorker } from './glm-naruto-worker-runtime.js';
|
|
2
4
|
import { decideConcurrency } from './glm-naruto-concurrency-governor.js';
|
|
3
|
-
import { planFileLeases } from './glm-naruto-file-lease.js';
|
|
4
5
|
import { evaluateGlmNarutoPatchCandidateGate } from './glm-naruto-patch-candidate-gate.js';
|
|
5
6
|
import { createPatchEnvelope } from './glm-naruto-patch-envelope.js';
|
|
6
7
|
import { writeGlmNarutoWorkerArtifacts } from './glm-naruto-worker-artifacts.js';
|
|
7
8
|
import { materializePatchViaWorktree } from './glm-naruto-worktree-worker.js';
|
|
9
|
+
import { createProviderHealthTracker } from '../../openrouter/openrouter-provider-health.js';
|
|
10
|
+
import { runGlmNarutoWorkerScheduler } from './glm-naruto-worker-scheduler.js';
|
|
8
11
|
export async function runPatchWorkerPool(input) {
|
|
9
12
|
const envelopes = [];
|
|
10
13
|
const traces = [];
|
|
11
14
|
const failedShardIds = [];
|
|
12
15
|
const concurrencyDecisions = [];
|
|
13
|
-
const shardPathMap = new Map();
|
|
14
|
-
for (const shard of input.shards) {
|
|
15
|
-
shardPathMap.set(shard.id, shard.target_paths);
|
|
16
|
-
}
|
|
17
|
-
const leases = planFileLeases(shardPathMap);
|
|
18
16
|
const mutableShards = input.shards.filter((s) => s.mutable);
|
|
19
17
|
const decision = decideConcurrency({
|
|
20
18
|
requestedClones: input.maxWorkers,
|
|
@@ -24,26 +22,38 @@ export async function runPatchWorkerPool(input) {
|
|
|
24
22
|
failureRate: 0,
|
|
25
23
|
operatorMax: input.maxWorkers
|
|
26
24
|
});
|
|
27
|
-
|
|
28
|
-
const workerTasks = [];
|
|
25
|
+
const jobs = [];
|
|
29
26
|
let workerIdx = 0;
|
|
30
27
|
for (const shard of mutableShards) {
|
|
31
28
|
const strategies = input.strategies.get(shard.id) || [shard.strategy];
|
|
32
29
|
for (const strategy of strategies) {
|
|
33
30
|
const workerId = `worker-${shard.id}-${strategy}-${workerIdx++}`;
|
|
34
31
|
const shardWithStrategy = { ...shard, strategy };
|
|
35
|
-
|
|
36
|
-
apiKey: input.apiKey,
|
|
37
|
-
missionId: input.missionId,
|
|
38
|
-
workerId,
|
|
39
|
-
root: input.cwd,
|
|
40
|
-
shard: shardWithStrategy,
|
|
41
|
-
contextSummary: input.contextSummary,
|
|
42
|
-
timeoutMs: input.workerTimeoutMs
|
|
43
|
-
}));
|
|
32
|
+
jobs.push({ worker_id: workerId, shard: shardWithStrategy, strategy });
|
|
44
33
|
}
|
|
45
34
|
}
|
|
46
|
-
const
|
|
35
|
+
const health = input.health ?? createProviderHealthTracker();
|
|
36
|
+
const schedulerResult = await runGlmNarutoWorkerScheduler({
|
|
37
|
+
jobs,
|
|
38
|
+
initial_active_workers: decision.target_active_workers,
|
|
39
|
+
max_active_workers: input.maxWorkers,
|
|
40
|
+
worker_timeout_ms: input.workerTimeoutMs,
|
|
41
|
+
health,
|
|
42
|
+
onDecision: (nextDecision) => {
|
|
43
|
+
concurrencyDecisions.push(nextDecision);
|
|
44
|
+
},
|
|
45
|
+
runJob: (job) => runPatchWorker({
|
|
46
|
+
apiKey: input.apiKey,
|
|
47
|
+
missionId: input.missionId,
|
|
48
|
+
workerId: job.worker_id,
|
|
49
|
+
root: input.cwd,
|
|
50
|
+
shard: job.shard,
|
|
51
|
+
contextSummary: input.contextSummary,
|
|
52
|
+
timeoutMs: input.workerTimeoutMs
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
await writeSchedulerArtifacts(input.cwd, input.missionId, schedulerResult).catch(() => undefined);
|
|
56
|
+
const results = schedulerResult.results;
|
|
47
57
|
for (const result of results) {
|
|
48
58
|
if (result.status === 'fulfilled' && result.value.ok && result.value.envelope) {
|
|
49
59
|
const isolationMode = input.isolationMode ?? 'patch-envelope-only';
|
|
@@ -65,6 +75,9 @@ export async function runPatchWorkerPool(input) {
|
|
|
65
75
|
worktree_path: worktree.lease?.path ?? null,
|
|
66
76
|
branch: worktree.lease?.branch ?? null,
|
|
67
77
|
base_commit: worktree.lease?.base_commit ?? input.baseCommit ?? null,
|
|
78
|
+
candidate_body_sha256: worktree.worktree?.candidate_body_sha256 ?? null,
|
|
79
|
+
extracted_patch_sha256: worktree.worktree?.extracted_patch_sha256 ?? null,
|
|
80
|
+
applied_patch_was_extracted: worktree.worktree?.applied_patch_was_extracted ?? false,
|
|
68
81
|
blockers: worktree.blockers
|
|
69
82
|
};
|
|
70
83
|
if (!worktree.ok) {
|
|
@@ -143,6 +156,35 @@ export async function runPatchWorkerPool(input) {
|
|
|
143
156
|
failedShardIds.push('unknown');
|
|
144
157
|
}
|
|
145
158
|
}
|
|
146
|
-
return {
|
|
159
|
+
return {
|
|
160
|
+
envelopes,
|
|
161
|
+
traces,
|
|
162
|
+
failedShardIds,
|
|
163
|
+
concurrencyDecisions,
|
|
164
|
+
schedulerSummary: {
|
|
165
|
+
max_observed_active_workers: schedulerResult.max_observed_active_workers,
|
|
166
|
+
backpressure_events: schedulerResult.backpressure_events,
|
|
167
|
+
queue_drained: true
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async function writeSchedulerArtifacts(root, missionId, schedulerResult) {
|
|
172
|
+
const dir = path.join(root, '.sneakoscope', 'glm-naruto', missionId);
|
|
173
|
+
await writeTextAtomic(path.join(dir, 'scheduler-decisions.jsonl'), schedulerResult.decisions.map((decision) => JSON.stringify(decision)).join('\n') + (schedulerResult.decisions.length ? '\n' : ''));
|
|
174
|
+
await writeJsonAtomic(path.join(dir, 'scheduler-summary.json'), {
|
|
175
|
+
schema: 'sks.glm-naruto-scheduler-summary.v1',
|
|
176
|
+
max_observed_active_workers: schedulerResult.max_observed_active_workers,
|
|
177
|
+
backpressure_events: schedulerResult.backpressure_events,
|
|
178
|
+
queue_drained: true,
|
|
179
|
+
result_count: schedulerResult.results.length,
|
|
180
|
+
decision_count: schedulerResult.decisions.length,
|
|
181
|
+
retry_count: schedulerResult.retry_events.length
|
|
182
|
+
});
|
|
183
|
+
if (schedulerResult.backpressure_records.length > 0) {
|
|
184
|
+
await writeTextAtomic(path.join(dir, 'provider-backpressure.jsonl'), schedulerResult.backpressure_records.map((row) => JSON.stringify(row)).join('\n') + '\n');
|
|
185
|
+
}
|
|
186
|
+
if (schedulerResult.retry_events.length > 0) {
|
|
187
|
+
await writeTextAtomic(path.join(dir, 'worker-retries.jsonl'), schedulerResult.retry_events.map((row) => JSON.stringify(row)).join('\n') + '\n');
|
|
188
|
+
}
|
|
147
189
|
}
|
|
148
190
|
//# sourceMappingURL=glm-naruto-worker-pool.js.map
|
|
@@ -94,6 +94,7 @@ export async function runPatchWorker(input) {
|
|
|
94
94
|
...(input.fetchImpl ? { fetchImpl: input.fetchImpl } : {})
|
|
95
95
|
});
|
|
96
96
|
if (!response.ok) {
|
|
97
|
+
const issue = classifyWorkerIssue(response.error);
|
|
97
98
|
const trace = { ...traceBase, total_ms: Date.now() - started, status: 'failed', chunk_count: 0, real_stream: false };
|
|
98
99
|
await writeGlmNarutoWorkerArtifacts({
|
|
99
100
|
root: artifactRoot,
|
|
@@ -107,7 +108,8 @@ export async function runPatchWorker(input) {
|
|
|
107
108
|
envelope: null,
|
|
108
109
|
trace,
|
|
109
110
|
ok: false,
|
|
110
|
-
error: response.error.code
|
|
111
|
+
error: response.error.code,
|
|
112
|
+
issue
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
115
|
const modelGuard = assertGlm52ActualModel(response.value.model);
|
|
@@ -126,7 +128,8 @@ export async function runPatchWorker(input) {
|
|
|
126
128
|
envelope: null,
|
|
127
129
|
trace,
|
|
128
130
|
ok: false,
|
|
129
|
-
error: `model_guard:${modelGuard.code}
|
|
131
|
+
error: `model_guard:${modelGuard.code}`,
|
|
132
|
+
issue: { code: `model_guard:${modelGuard.code}`, retryable: false }
|
|
130
133
|
};
|
|
131
134
|
}
|
|
132
135
|
const parsed = parsePatchCandidateOutput(response.value.content);
|
|
@@ -153,7 +156,8 @@ export async function runPatchWorker(input) {
|
|
|
153
156
|
envelope: null,
|
|
154
157
|
trace,
|
|
155
158
|
ok: false,
|
|
156
|
-
error: parsed.kind
|
|
159
|
+
error: parsed.kind,
|
|
160
|
+
issue: { code: parsed.kind, retryable: false }
|
|
157
161
|
};
|
|
158
162
|
}
|
|
159
163
|
const envelope = createPatchEnvelope({
|
|
@@ -196,6 +200,7 @@ export async function runPatchWorker(input) {
|
|
|
196
200
|
return { envelope, trace, ok: true };
|
|
197
201
|
}
|
|
198
202
|
catch (err) {
|
|
203
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
199
204
|
const trace = { ...traceBase, total_ms: Date.now() - started, status: 'failed', chunk_count: 0, real_stream: false };
|
|
200
205
|
await writeGlmNarutoWorkerArtifacts({
|
|
201
206
|
root: artifactRoot,
|
|
@@ -209,10 +214,27 @@ export async function runPatchWorker(input) {
|
|
|
209
214
|
envelope: null,
|
|
210
215
|
trace,
|
|
211
216
|
ok: false,
|
|
212
|
-
error:
|
|
217
|
+
error: message,
|
|
218
|
+
issue: classifyWorkerIssue({ code: message, message, severity: 'failed' })
|
|
213
219
|
};
|
|
214
220
|
}
|
|
215
221
|
}
|
|
222
|
+
function classifyWorkerIssue(issue) {
|
|
223
|
+
const code = String(issue.code || 'glm_worker_failed');
|
|
224
|
+
const status = typeof issue.status === 'number' ? issue.status : undefined;
|
|
225
|
+
const retryable = code === 'glm_openrouter_rate_limited'
|
|
226
|
+
|| status === 429
|
|
227
|
+
|| (typeof status === 'number' && status >= 500)
|
|
228
|
+
|| code === 'glm_openrouter_provider_unavailable'
|
|
229
|
+
|| code === 'glm_stream_idle_timeout'
|
|
230
|
+
|| code === 'glm_request_timeout';
|
|
231
|
+
return {
|
|
232
|
+
code,
|
|
233
|
+
retryable,
|
|
234
|
+
...(status !== undefined ? { provider_status: status } : {}),
|
|
235
|
+
retry_after_ms: null
|
|
236
|
+
};
|
|
237
|
+
}
|
|
216
238
|
export async function runVerifierWorker(input) {
|
|
217
239
|
const started = Date.now();
|
|
218
240
|
const sessionId = normalizeGlmNarutoSessionId(`sks-glm-naruto-verify-${input.missionId}-${input.workerId}`);
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { nowIso } from '../../../fsx.js';
|
|
2
|
+
import { decideConcurrency } from './glm-naruto-concurrency-governor.js';
|
|
3
|
+
export async function runGlmNarutoWorkerScheduler(input) {
|
|
4
|
+
const queue = input.jobs.map((job) => ({ job, attempt: 0 }));
|
|
5
|
+
const running = new Map();
|
|
6
|
+
const results = [];
|
|
7
|
+
const decisions = [];
|
|
8
|
+
const retryEvents = [];
|
|
9
|
+
const backpressureRecords = [];
|
|
10
|
+
const maxActive = Math.max(1, input.max_active_workers);
|
|
11
|
+
let targetActive = Math.max(1, Math.min(maxActive, input.initial_active_workers || 1));
|
|
12
|
+
let nextRunKey = 0;
|
|
13
|
+
let maxObservedActive = 0;
|
|
14
|
+
let backpressureEvents = 0;
|
|
15
|
+
let failureCount = 0;
|
|
16
|
+
let finishedCount = 0;
|
|
17
|
+
let pauseUntilMs = 0;
|
|
18
|
+
await recordDecision('initial');
|
|
19
|
+
while (queue.length > 0 || running.size > 0) {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
if (pauseUntilMs > now && running.size === 0) {
|
|
22
|
+
await sleep(Math.min(1_000, pauseUntilMs - now));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
while (queue.length > 0 && running.size < targetActive && Date.now() >= pauseUntilMs) {
|
|
26
|
+
const entry = queue.shift();
|
|
27
|
+
const key = nextRunKey++;
|
|
28
|
+
running.set(key, runTimedJob(key, entry, input));
|
|
29
|
+
maxObservedActive = Math.max(maxObservedActive, running.size);
|
|
30
|
+
}
|
|
31
|
+
if (running.size === 0) {
|
|
32
|
+
if (queue.length > 0) {
|
|
33
|
+
targetActive = Math.max(1, targetActive);
|
|
34
|
+
pauseUntilMs = Math.min(pauseUntilMs || Date.now(), Date.now());
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const completed = await Promise.race([...running.values()]);
|
|
39
|
+
running.delete(completed.key);
|
|
40
|
+
finishedCount++;
|
|
41
|
+
const issue = issueFromSettled(completed.settled);
|
|
42
|
+
if (completed.settled.status === 'rejected' || (completed.settled.status === 'fulfilled' && !completed.settled.value.ok)) {
|
|
43
|
+
failureCount++;
|
|
44
|
+
}
|
|
45
|
+
updateProviderHealth(input.health, completed.settled);
|
|
46
|
+
if (issue && shouldBackoff(issue)) {
|
|
47
|
+
backpressureEvents++;
|
|
48
|
+
const pauseMs = backoffMs(issue);
|
|
49
|
+
pauseUntilMs = Math.max(pauseUntilMs, Date.now() + pauseMs);
|
|
50
|
+
backpressureRecords.push({
|
|
51
|
+
schema: 'sks.glm-naruto-provider-backpressure.v1',
|
|
52
|
+
created_at: nowIso(),
|
|
53
|
+
worker_id: completed.entry.job.worker_id,
|
|
54
|
+
attempt: completed.entry.attempt,
|
|
55
|
+
code: issue.code,
|
|
56
|
+
provider_status: issue.provider_status ?? null,
|
|
57
|
+
retry_after_ms: issue.retry_after_ms ?? null,
|
|
58
|
+
pause_ms: pauseMs
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (shouldRetry(completed.entry, completed.settled, issue)) {
|
|
62
|
+
const retryEntry = { job: completed.entry.job, attempt: completed.entry.attempt + 1 };
|
|
63
|
+
retryEvents.push({
|
|
64
|
+
schema: 'sks.glm-naruto-worker-retry.v1',
|
|
65
|
+
created_at: nowIso(),
|
|
66
|
+
worker_id: completed.entry.job.worker_id,
|
|
67
|
+
shard_id: completed.entry.job.shard.id,
|
|
68
|
+
next_attempt: retryEntry.attempt,
|
|
69
|
+
code: issue?.code ?? 'worker_scheduler_rejected'
|
|
70
|
+
});
|
|
71
|
+
queue.push(retryEntry);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
results.push(completed.settled);
|
|
75
|
+
}
|
|
76
|
+
await recordDecision(issue?.code ?? 'worker_finished');
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
results,
|
|
80
|
+
decisions,
|
|
81
|
+
max_observed_active_workers: maxObservedActive,
|
|
82
|
+
backpressure_events: backpressureEvents,
|
|
83
|
+
retry_events: retryEvents,
|
|
84
|
+
backpressure_records: backpressureRecords
|
|
85
|
+
};
|
|
86
|
+
async function recordDecision(reasonSuffix) {
|
|
87
|
+
const health = input.health.getHealth();
|
|
88
|
+
const decision = decideConcurrency({
|
|
89
|
+
requestedClones: maxActive,
|
|
90
|
+
activeWorkers: targetActive,
|
|
91
|
+
rateLimited429: health?.count_429 ?? 0,
|
|
92
|
+
ttftP90Ms: health?.p90_ttft_ms ?? 0,
|
|
93
|
+
failureRate: finishedCount ? failureCount / finishedCount : 0,
|
|
94
|
+
operatorMax: maxActive
|
|
95
|
+
});
|
|
96
|
+
targetActive = Math.max(running.size > 0 ? 1 : 0, Math.min(maxActive, decision.target_active_workers));
|
|
97
|
+
if (targetActive === 0 && queue.length > 0 && running.size === 0)
|
|
98
|
+
targetActive = 1;
|
|
99
|
+
const recorded = { ...decision, reason: `${decision.reason}:${reasonSuffix}` };
|
|
100
|
+
decisions.push(recorded);
|
|
101
|
+
if (recorded.backpressure)
|
|
102
|
+
backpressureEvents++;
|
|
103
|
+
await input.onDecision(recorded);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function runTimedJob(key, entry, input) {
|
|
107
|
+
try {
|
|
108
|
+
const value = await withTimeout(input.runJob(entry.job), input.worker_timeout_ms);
|
|
109
|
+
return { key, entry, settled: { status: 'fulfilled', value } };
|
|
110
|
+
}
|
|
111
|
+
catch (reason) {
|
|
112
|
+
return { key, entry, settled: { status: 'rejected', reason } };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function updateProviderHealth(health, settled) {
|
|
116
|
+
if (settled.status !== 'fulfilled') {
|
|
117
|
+
health.record({ provider_slug: 'openrouter', model: 'z-ai/glm-5.2', count_5xx: 1, last_failure: nowIso() });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const issue = settled.value.issue;
|
|
121
|
+
const trace = settled.value.trace;
|
|
122
|
+
health.record({
|
|
123
|
+
provider_slug: trace.provider_slug || 'openrouter',
|
|
124
|
+
model: trace.model,
|
|
125
|
+
...(trace.ttft_ms !== null ? { p50_ttft_ms: trace.ttft_ms } : {}),
|
|
126
|
+
...(issue?.provider_status === 429 || issue?.code === 'glm_openrouter_rate_limited' ? { count_429: 1 } : {}),
|
|
127
|
+
...(typeof issue?.provider_status === 'number' && issue.provider_status >= 500 ? { count_5xx: 1 } : {}),
|
|
128
|
+
last_success: settled.value.ok ? nowIso() : null,
|
|
129
|
+
last_failure: settled.value.ok ? null : nowIso()
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function issueFromSettled(settled) {
|
|
133
|
+
if (settled.status === 'rejected') {
|
|
134
|
+
return { code: 'worker_scheduler_rejected', retryable: true, retry_after_ms: null };
|
|
135
|
+
}
|
|
136
|
+
return settled.value.issue ?? null;
|
|
137
|
+
}
|
|
138
|
+
function shouldRetry(entry, settled, issue) {
|
|
139
|
+
if (entry.attempt >= 1)
|
|
140
|
+
return false;
|
|
141
|
+
if (settled.status === 'rejected')
|
|
142
|
+
return true;
|
|
143
|
+
return settled.value.ok === false && issue?.retryable === true;
|
|
144
|
+
}
|
|
145
|
+
function shouldBackoff(issue) {
|
|
146
|
+
return issue.provider_status === 429
|
|
147
|
+
|| issue.code === 'glm_openrouter_rate_limited'
|
|
148
|
+
|| (typeof issue.provider_status === 'number' && issue.provider_status >= 500)
|
|
149
|
+
|| issue.code === 'glm_openrouter_provider_unavailable'
|
|
150
|
+
|| issue.code === 'glm_stream_idle_timeout'
|
|
151
|
+
|| issue.code === 'glm_request_timeout';
|
|
152
|
+
}
|
|
153
|
+
function backoffMs(issue) {
|
|
154
|
+
if (typeof issue.retry_after_ms === 'number' && Number.isFinite(issue.retry_after_ms) && issue.retry_after_ms > 0) {
|
|
155
|
+
return Math.min(30_000, issue.retry_after_ms);
|
|
156
|
+
}
|
|
157
|
+
if (issue.provider_status === 429 || issue.code === 'glm_openrouter_rate_limited')
|
|
158
|
+
return 1_000;
|
|
159
|
+
return 250;
|
|
160
|
+
}
|
|
161
|
+
function withTimeout(promise, timeoutMs) {
|
|
162
|
+
if (!timeoutMs || timeoutMs <= 0)
|
|
163
|
+
return promise;
|
|
164
|
+
let timer;
|
|
165
|
+
return Promise.race([
|
|
166
|
+
promise,
|
|
167
|
+
new Promise((_, reject) => {
|
|
168
|
+
timer = setTimeout(() => reject(new Error('worker_scheduler_timeout')), timeoutMs);
|
|
169
|
+
})
|
|
170
|
+
]).finally(() => {
|
|
171
|
+
if (timer)
|
|
172
|
+
clearTimeout(timer);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function sleep(ms) {
|
|
176
|
+
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=glm-naruto-worker-scheduler.js.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createPatchEnvelope } from './glm-naruto-patch-envelope.js';
|
|
2
|
+
import { sha256 } from '../../../fsx.js';
|
|
3
|
+
import { parseGlmNarutoPatchCandidate } from './glm-naruto-patch-candidate-parser.js';
|
|
2
4
|
import { applyPatchInWorktree, createGlmNarutoWorkerWorktree, diffWorktree } from './glm-naruto-worktree-manager.js';
|
|
3
5
|
import { cleanupGlmNarutoWorktree } from './glm-naruto-worktree-cleanup.js';
|
|
4
6
|
export async function materializePatchViaWorktree(input) {
|
|
@@ -10,10 +12,32 @@ export async function materializePatchViaWorktree(input) {
|
|
|
10
12
|
workerId: input.envelope.worker_id,
|
|
11
13
|
...(input.baseCommit !== undefined ? { baseCommit: input.baseCommit } : {})
|
|
12
14
|
});
|
|
13
|
-
const
|
|
15
|
+
const parsed = parseGlmNarutoPatchCandidate(input.envelope.patch);
|
|
16
|
+
const parseProof = {
|
|
17
|
+
candidate_body_sha256: sha256(input.envelope.patch),
|
|
18
|
+
extracted_patch_sha256: parsed.ok ? sha256(parsed.patch) : null,
|
|
19
|
+
applied_patch_was_extracted: false
|
|
20
|
+
};
|
|
21
|
+
if (!parsed.ok) {
|
|
22
|
+
await cleanupGlmNarutoWorktree({ repoRoot: input.repoRoot, missionId: input.missionId, lease, cleanup: input.cleanup });
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
envelope: { ...input.envelope, status: 'gate_failed', blockers: parsed.blockers },
|
|
26
|
+
lease,
|
|
27
|
+
blockers: parsed.blockers,
|
|
28
|
+
worktree: parseProof
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const applied = await applyPatchInWorktree(lease.path, parsed.patch);
|
|
14
32
|
if (!applied.ok) {
|
|
15
33
|
await cleanupGlmNarutoWorktree({ repoRoot: input.repoRoot, missionId: input.missionId, lease, cleanup: input.cleanup });
|
|
16
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
envelope: { ...input.envelope, status: 'gate_failed', blockers: ['worktree_patch_apply_failed'] },
|
|
37
|
+
lease,
|
|
38
|
+
blockers: ['worktree_patch_apply_failed'],
|
|
39
|
+
worktree: { ...parseProof, applied_patch_was_extracted: true }
|
|
40
|
+
};
|
|
17
41
|
}
|
|
18
42
|
const diff = await diffWorktree(lease.path);
|
|
19
43
|
const envelope = createPatchEnvelope({
|
|
@@ -21,14 +45,20 @@ export async function materializePatchViaWorktree(input) {
|
|
|
21
45
|
workerId: input.envelope.worker_id,
|
|
22
46
|
shardId: input.envelope.shard_id,
|
|
23
47
|
baseDigest: input.envelope.base_digest,
|
|
24
|
-
patch: diff,
|
|
48
|
+
patch: diff || parsed.patch,
|
|
25
49
|
strategy: input.envelope.strategy,
|
|
26
50
|
reasoningEffort: input.envelope.reasoning_effort,
|
|
27
51
|
status: input.envelope.status,
|
|
28
52
|
warnings: [...input.envelope.warnings, `worktree:${lease.path}`]
|
|
29
53
|
});
|
|
30
54
|
await cleanupGlmNarutoWorktree({ repoRoot: input.repoRoot, missionId: input.missionId, lease, cleanup: input.cleanup });
|
|
31
|
-
return {
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
envelope,
|
|
58
|
+
lease,
|
|
59
|
+
blockers: [],
|
|
60
|
+
worktree: { ...parseProof, applied_patch_was_extracted: true }
|
|
61
|
+
};
|
|
32
62
|
}
|
|
33
63
|
catch (err) {
|
|
34
64
|
if (lease)
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '4.0.
|
|
1
|
+
export const PACKAGE_VERSION = '4.0.12';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "4.0.
|
|
4
|
+
"version": "4.0.12",
|
|
5
5
|
"description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|