sneakoscope 4.0.6 → 4.0.8
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 +6 -2
- 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/commands/glm-command.js +9 -2
- package/dist/core/fsx.js +1 -1
- package/dist/core/providers/glm/glm-bench.js +4 -4
- package/dist/core/providers/glm/glm-latency-trace.js +1 -1
- package/dist/core/providers/glm/glm-request-cache.js +10 -2
- package/dist/core/providers/glm/naruto/glm-naruto-artifacts.js +2 -0
- package/dist/core/providers/glm/naruto/glm-naruto-bench.js +68 -0
- package/dist/core/providers/glm/naruto/glm-naruto-budget.js +45 -0
- package/dist/core/providers/glm/naruto/glm-naruto-command.js +97 -0
- package/dist/core/providers/glm/naruto/glm-naruto-concurrency-governor.js +37 -0
- package/dist/core/providers/glm/naruto/glm-naruto-conflict-graph.js +74 -0
- package/dist/core/providers/glm/naruto/glm-naruto-decomposer.js +99 -0
- package/dist/core/providers/glm/naruto/glm-naruto-file-lease.js +23 -0
- package/dist/core/providers/glm/naruto/glm-naruto-finalizer.js +22 -0
- package/dist/core/providers/glm/naruto/glm-naruto-judge.js +84 -0
- package/dist/core/providers/glm/naruto/glm-naruto-merge-planner.js +57 -0
- package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +224 -0
- package/dist/core/providers/glm/naruto/glm-naruto-patch-envelope.js +55 -0
- package/dist/core/providers/glm/naruto/glm-naruto-quorum.js +37 -0
- package/dist/core/providers/glm/naruto/glm-naruto-rate-limiter.js +18 -0
- package/dist/core/providers/glm/naruto/glm-naruto-repair-wave.js +21 -0
- package/dist/core/providers/glm/naruto/glm-naruto-shard-planner.js +32 -0
- package/dist/core/providers/glm/naruto/glm-naruto-trace.js +51 -0
- package/dist/core/providers/glm/naruto/glm-naruto-types.js +37 -0
- package/dist/core/providers/glm/naruto/glm-naruto-work-graph.js +2 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +79 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +196 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker.js +2 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worktree.js +48 -0
- package/dist/core/providers/openrouter/openrouter-provider-health.js +46 -0
- package/dist/core/providers/openrouter/openrouter-secret-store.js +33 -0
- package/dist/core/providers/openrouter/openrouter-stream.js +73 -5
- package/dist/core/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { buildGlm52Request } from '../glm-52-request.js';
|
|
2
|
+
import { sendOpenRouterChatCompletionStream } from '../../openrouter/openrouter-stream.js';
|
|
3
|
+
import { assertGlm52ActualModel } from '../glm-52-response-guard.js';
|
|
4
|
+
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
5
|
+
export async function runGlmJudge(input) {
|
|
6
|
+
const validEnvelopes = input.envelopes.filter((e) => e.status === 'gate_passed');
|
|
7
|
+
if (validEnvelopes.length === 0) {
|
|
8
|
+
return {
|
|
9
|
+
schema: 'sks.glm-naruto-judge.v1',
|
|
10
|
+
ranked_patch_ids: [],
|
|
11
|
+
reject_patch_ids: [],
|
|
12
|
+
mergeable_sets: [],
|
|
13
|
+
risks: ['no_gate_passed_candidates'],
|
|
14
|
+
requires_repair_wave: false
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const systemPrompt = `You are a GLM Naruto judge. Rank patch candidates by quality. Output strict JSON with schema: {"ranked_patch_ids":["id1","id2"],"reject_patch_ids":["id3"],"mergeable_sets":[["id1","id2"]],"risks":[],"requires_repair_wave":false}. Model: ${GLM_52_OPENROUTER_MODEL}. No GPT fallback.`;
|
|
18
|
+
const candidateDescriptions = validEnvelopes.map((e) => ({
|
|
19
|
+
patch_id: e.worker_id,
|
|
20
|
+
shard_id: e.shard_id,
|
|
21
|
+
target_paths: e.target_paths,
|
|
22
|
+
patch_sha256: e.patch_sha256.slice(0, 12),
|
|
23
|
+
strategy: e.strategy,
|
|
24
|
+
patch_size: e.patch.length
|
|
25
|
+
}));
|
|
26
|
+
const userContent = JSON.stringify({
|
|
27
|
+
mission_id: input.missionId,
|
|
28
|
+
candidates: candidateDescriptions,
|
|
29
|
+
instruction: 'Rank by: gate pass, minimal diff, correct target paths, no protected paths. Return mergeable non-conflicting sets.'
|
|
30
|
+
});
|
|
31
|
+
const messages = [
|
|
32
|
+
{ role: 'system', content: systemPrompt },
|
|
33
|
+
{ role: 'user', content: userContent }
|
|
34
|
+
];
|
|
35
|
+
const request = buildGlm52Request({
|
|
36
|
+
profile: 'deep',
|
|
37
|
+
messages,
|
|
38
|
+
maxTokens: 8192,
|
|
39
|
+
reasoningEffort: 'high',
|
|
40
|
+
toolChoice: 'none',
|
|
41
|
+
parallelToolCalls: false
|
|
42
|
+
});
|
|
43
|
+
const response = await sendOpenRouterChatCompletionStream({
|
|
44
|
+
apiKey: input.apiKey,
|
|
45
|
+
request: { ...request, session_id: `sks-glm-naruto-judge-${input.missionId}` },
|
|
46
|
+
timeoutMs: input.timeoutMs || 120_000
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
return fallbackJudgeResult(validEnvelopes, [`judge_request_failed:${response.error.code}`]);
|
|
50
|
+
}
|
|
51
|
+
const modelGuard = assertGlm52ActualModel(response.value.model || GLM_52_OPENROUTER_MODEL);
|
|
52
|
+
if (!modelGuard.ok) {
|
|
53
|
+
return fallbackJudgeResult(validEnvelopes, [`judge_model_guard:${modelGuard.code}`]);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const jsonMatch = response.value.content.match(/\{[\s\S]*\}/);
|
|
57
|
+
if (!jsonMatch)
|
|
58
|
+
return fallbackJudgeResult(validEnvelopes, ['judge_no_json_output']);
|
|
59
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
60
|
+
return {
|
|
61
|
+
schema: 'sks.glm-naruto-judge.v1',
|
|
62
|
+
ranked_patch_ids: Array.isArray(parsed.ranked_patch_ids) ? parsed.ranked_patch_ids : [],
|
|
63
|
+
reject_patch_ids: Array.isArray(parsed.reject_patch_ids) ? parsed.reject_patch_ids : [],
|
|
64
|
+
mergeable_sets: Array.isArray(parsed.mergeable_sets) ? parsed.mergeable_sets : [],
|
|
65
|
+
risks: Array.isArray(parsed.risks) ? parsed.risks : [],
|
|
66
|
+
requires_repair_wave: Boolean(parsed.requires_repair_wave)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return fallbackJudgeResult(validEnvelopes, ['judge_json_parse_failed']);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function fallbackJudgeResult(envelopes, risks) {
|
|
74
|
+
const sorted = [...envelopes].sort((a, b) => a.patch.length - b.patch.length);
|
|
75
|
+
return {
|
|
76
|
+
schema: 'sks.glm-naruto-judge.v1',
|
|
77
|
+
ranked_patch_ids: sorted.map((e) => e.worker_id),
|
|
78
|
+
reject_patch_ids: [],
|
|
79
|
+
mergeable_sets: sorted.length > 0 ? [[sorted[0].worker_id]] : [],
|
|
80
|
+
risks,
|
|
81
|
+
requires_repair_wave: false
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=glm-naruto-judge.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getNonConflictingSets } from './glm-naruto-conflict-graph.js';
|
|
2
|
+
export function planMerge(input) {
|
|
3
|
+
const passedNodes = input.graph.nodes.filter((n) => n.gate_passed);
|
|
4
|
+
const nonConflictingSets = getNonConflictingSets(input.graph);
|
|
5
|
+
const candidates = nonConflictingSets.map((patchIds) => {
|
|
6
|
+
const nodes = passedNodes.filter((n) => patchIds.includes(n.patch_id));
|
|
7
|
+
const totalScore = nodes.reduce((sum, n) => sum + n.score, 0);
|
|
8
|
+
return { patch_ids: patchIds, total_score: totalScore, conflict_free: true };
|
|
9
|
+
});
|
|
10
|
+
candidates.sort((a, b) => b.total_score - a.total_score);
|
|
11
|
+
let selected = [];
|
|
12
|
+
let rationale = '';
|
|
13
|
+
if (input.strategy === 'judge' && input.judgeRanking && input.judgeRanking.length > 0) {
|
|
14
|
+
const ranked = input.judgeRanking.filter((id) => passedNodes.some((n) => n.patch_id === id));
|
|
15
|
+
const bestSet = candidates.find((set) => set.patch_ids.every((id) => ranked.includes(id))) || candidates[0];
|
|
16
|
+
selected = bestSet ? bestSet.patch_ids : [];
|
|
17
|
+
rationale = 'judge_ranked_deterministic_gated';
|
|
18
|
+
}
|
|
19
|
+
else if (input.strategy === 'quorum') {
|
|
20
|
+
const quorumMap = new Map();
|
|
21
|
+
for (const node of passedNodes) {
|
|
22
|
+
const key = node.shard_id;
|
|
23
|
+
quorumMap.set(key, (quorumMap.get(key) || 0) + 1);
|
|
24
|
+
}
|
|
25
|
+
const bestSet = candidates[0];
|
|
26
|
+
selected = bestSet ? bestSet.patch_ids : [];
|
|
27
|
+
rationale = 'quorum_consensus_deterministic_gated';
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const bestSet = candidates[0];
|
|
31
|
+
selected = bestSet ? bestSet.patch_ids : [];
|
|
32
|
+
rationale = 'highest_score_non_conflicting';
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
schema: 'sks.glm-naruto-merge-plan.v1',
|
|
36
|
+
mission_id: input.missionId,
|
|
37
|
+
strategy: input.strategy,
|
|
38
|
+
selected_patches: selected,
|
|
39
|
+
candidates,
|
|
40
|
+
rationale
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function scoreCandidate(input) {
|
|
44
|
+
let score = 0;
|
|
45
|
+
if (input.node.gate_passed)
|
|
46
|
+
score += 100;
|
|
47
|
+
score -= input.patchSize / 100;
|
|
48
|
+
score -= input.touchedPathsCount * 5;
|
|
49
|
+
if (input.protectedPath)
|
|
50
|
+
score -= 200;
|
|
51
|
+
if (input.testFailure)
|
|
52
|
+
score -= 50;
|
|
53
|
+
if (input.judgeRank !== null && input.judgeRank !== undefined)
|
|
54
|
+
score += Math.max(0, 50 - input.judgeRank * 10);
|
|
55
|
+
return Math.round(score);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=glm-naruto-merge-planner.js.map
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { nowIso, writeJsonAtomic } from '../../../fsx.js';
|
|
3
|
+
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
4
|
+
import { resolveOpenRouterApiKey } from '../../openrouter/openrouter-secret-store.js';
|
|
5
|
+
import { checkAndApplyGlmPatch } from '../glm-patch-apply.js';
|
|
6
|
+
import { decomposeTask, validateWorkGraph } from './glm-naruto-decomposer.js';
|
|
7
|
+
import { planShardCandidates, computeInitialLaneMix } from './glm-naruto-shard-planner.js';
|
|
8
|
+
import { runPatchWorkerPool } from './glm-naruto-worker-pool.js';
|
|
9
|
+
import { buildConflictGraph } from './glm-naruto-conflict-graph.js';
|
|
10
|
+
import { planMerge } from './glm-naruto-merge-planner.js';
|
|
11
|
+
import { finalizeMergePlan } from './glm-naruto-finalizer.js';
|
|
12
|
+
import { planRepairWave } from './glm-naruto-repair-wave.js';
|
|
13
|
+
import { createBudget, checkBudget, recordRequest } from './glm-naruto-budget.js';
|
|
14
|
+
import { createProviderHealthTracker } from '../../openrouter/openrouter-provider-health.js';
|
|
15
|
+
import { createMissionTrace, recordWorkerTrace, writeMissionArtifacts, buildMissionSummary } from './glm-naruto-trace.js';
|
|
16
|
+
import { runGlmJudge } from './glm-naruto-judge.js';
|
|
17
|
+
import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
|
|
18
|
+
export async function runGlmNarutoMission(input) {
|
|
19
|
+
const missionId = input.missionId || `glm-naruto-${nowIso().replace(/[:.]/g, '-')}`;
|
|
20
|
+
const cwd = input.cwd;
|
|
21
|
+
const startedMs = Date.now();
|
|
22
|
+
const key = await resolveOpenRouterApiKey({ env: process.env });
|
|
23
|
+
if (!key.key) {
|
|
24
|
+
return missionResult(missionId, input.task, 'blocked', 'glm_missing_openrouter_key', 0, startedMs, [], [], ['glm_missing_openrouter_key'], []);
|
|
25
|
+
}
|
|
26
|
+
const mentionedPaths = extractMentionedPaths(input.task);
|
|
27
|
+
const gitStatus = await readGitStatus(cwd);
|
|
28
|
+
const graph = decomposeTask({
|
|
29
|
+
missionId,
|
|
30
|
+
task: input.task,
|
|
31
|
+
gitStatus,
|
|
32
|
+
mentionedPaths
|
|
33
|
+
});
|
|
34
|
+
const isVerifyOnly = input.task.trim().toLowerCase().startsWith('verify');
|
|
35
|
+
const validation = validateWorkGraph(graph, isVerifyOnly);
|
|
36
|
+
if (!validation.ok) {
|
|
37
|
+
return missionResult(missionId, input.task, 'blocked', validation.reason || 'invalid_work_graph', 0, startedMs, [], [], [validation.reason || 'invalid_work_graph'], []);
|
|
38
|
+
}
|
|
39
|
+
const budget = createBudget(missionId, input.deep || false);
|
|
40
|
+
const budgetCheck = checkBudget(budget);
|
|
41
|
+
if (!budgetCheck.ok) {
|
|
42
|
+
return missionResult(missionId, input.task, 'budget_exhausted', budgetCheck.reason, 0, startedMs, [], [], [budgetCheck.reason], []);
|
|
43
|
+
}
|
|
44
|
+
const laneMix = computeInitialLaneMix(graph);
|
|
45
|
+
const strategies = planShardCandidates(graph);
|
|
46
|
+
const strategyMap = new Map();
|
|
47
|
+
for (const entry of strategies) {
|
|
48
|
+
strategyMap.set(entry.shard.id, entry.strategies);
|
|
49
|
+
}
|
|
50
|
+
const healthTracker = createProviderHealthTracker();
|
|
51
|
+
let traceState = createMissionTrace(missionId);
|
|
52
|
+
// Wave 1: parallel patch candidate generation
|
|
53
|
+
const poolResult = await runPatchWorkerPool({
|
|
54
|
+
apiKey: key.key,
|
|
55
|
+
missionId,
|
|
56
|
+
cwd,
|
|
57
|
+
shards: graph.shards,
|
|
58
|
+
contextSummary: JSON.stringify({ task: input.task, git_status: gitStatus || '' }),
|
|
59
|
+
maxWorkers: input.maxWorkers || laneMix.patch_workers,
|
|
60
|
+
workerTimeoutMs: GLM_NARUTO_LIMITS.max_worker_runtime_ms,
|
|
61
|
+
strategies: strategyMap
|
|
62
|
+
});
|
|
63
|
+
for (const trace of poolResult.traces) {
|
|
64
|
+
traceState = recordWorkerTrace(traceState, trace);
|
|
65
|
+
}
|
|
66
|
+
healthTracker.record({ provider_slug: 'openrouter', model: GLM_52_OPENROUTER_MODEL, count_429: 0, count_5xx: 0 });
|
|
67
|
+
let envelopes = poolResult.envelopes;
|
|
68
|
+
let failedShardIds = poolResult.failedShardIds;
|
|
69
|
+
let repairWaves = 0;
|
|
70
|
+
// Repair wave if needed
|
|
71
|
+
if (failedShardIds.length > 0 && repairWaves < GLM_NARUTO_LIMITS.max_repair_waves) {
|
|
72
|
+
const repairPlan = planRepairWave({
|
|
73
|
+
failedEnvelopes: envelopes.filter((e) => e.status === 'gate_failed'),
|
|
74
|
+
shards: graph.shards,
|
|
75
|
+
repairWaveCount: repairWaves
|
|
76
|
+
});
|
|
77
|
+
if (repairPlan.canRepair && repairPlan.shardsToRepair.length > 0) {
|
|
78
|
+
repairWaves++;
|
|
79
|
+
const repairPool = await runPatchWorkerPool({
|
|
80
|
+
apiKey: key.key,
|
|
81
|
+
missionId,
|
|
82
|
+
cwd,
|
|
83
|
+
shards: repairPlan.shardsToRepair,
|
|
84
|
+
contextSummary: JSON.stringify({ task: input.task, repair: true }),
|
|
85
|
+
maxWorkers: input.maxWorkers || 3,
|
|
86
|
+
workerTimeoutMs: GLM_NARUTO_LIMITS.max_worker_runtime_ms,
|
|
87
|
+
strategies: new Map(repairPlan.shardsToRepair.map((s) => [s.id, [s.strategy]]))
|
|
88
|
+
});
|
|
89
|
+
envelopes = [...envelopes, ...repairPool.envelopes];
|
|
90
|
+
for (const trace of repairPool.traces) {
|
|
91
|
+
traceState = recordWorkerTrace(traceState, trace);
|
|
92
|
+
}
|
|
93
|
+
failedShardIds = [...failedShardIds, ...repairPool.failedShardIds];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Build conflict graph and merge plan
|
|
97
|
+
const passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
|
|
98
|
+
const nodes = passedEnvelopes.map((env) => ({
|
|
99
|
+
patch_id: env.worker_id,
|
|
100
|
+
shard_id: env.shard_id,
|
|
101
|
+
target_paths: env.target_paths,
|
|
102
|
+
score: Math.max(0, 100 - Math.floor(env.patch.length / 100)),
|
|
103
|
+
gate_passed: true,
|
|
104
|
+
patch_sha256: env.patch_sha256
|
|
105
|
+
}));
|
|
106
|
+
const conflictGraph = buildConflictGraph(passedEnvelopes, nodes);
|
|
107
|
+
let judgeResult = null;
|
|
108
|
+
if (input.useJudge && passedEnvelopes.length > 1) {
|
|
109
|
+
judgeResult = await runGlmJudge({
|
|
110
|
+
apiKey: key.key,
|
|
111
|
+
missionId,
|
|
112
|
+
envelopes: passedEnvelopes,
|
|
113
|
+
timeoutMs: 120_000
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const mergePlan = finalizeMergePlan({
|
|
117
|
+
missionId,
|
|
118
|
+
envelopes: passedEnvelopes,
|
|
119
|
+
...(judgeResult ? { judgeResult } : {}),
|
|
120
|
+
useJudge: input.useJudge || false,
|
|
121
|
+
xhighFinalizer: input.xhighFinalizer || false
|
|
122
|
+
});
|
|
123
|
+
// Apply winning merge plan
|
|
124
|
+
let appliedPatches = 0;
|
|
125
|
+
let applyResult = null;
|
|
126
|
+
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 };
|
|
137
|
+
}
|
|
138
|
+
const terminalState = appliedPatches > 0 ? 'completed' : passedEnvelopes.length > 0 ? 'partial_candidates' : 'blocked';
|
|
139
|
+
const terminationReason = appliedPatches > 0 ? 'completed_merge_applied' : passedEnvelopes.length > 0 ? 'partial_no_apply' : 'no_gate_passed_candidates';
|
|
140
|
+
const summary = buildMissionSummary({
|
|
141
|
+
missionId,
|
|
142
|
+
startedMs,
|
|
143
|
+
workerTraces: traceState.workerTraces,
|
|
144
|
+
patchCandidates: envelopes.length,
|
|
145
|
+
gatePassed: passedEnvelopes.length,
|
|
146
|
+
mergeable: mergePlan.candidates.length,
|
|
147
|
+
appliedPatches,
|
|
148
|
+
failedShards: failedShardIds.length,
|
|
149
|
+
repairWaves
|
|
150
|
+
});
|
|
151
|
+
const result = {
|
|
152
|
+
schema: 'sks.glm-naruto-mission-result.v1',
|
|
153
|
+
ok: terminalState === 'completed',
|
|
154
|
+
status: terminalState,
|
|
155
|
+
mission_id: missionId,
|
|
156
|
+
task: input.task,
|
|
157
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
158
|
+
gpt_fallback_allowed: false,
|
|
159
|
+
termination_reason: terminationReason,
|
|
160
|
+
workers_started: summary.workers_started,
|
|
161
|
+
workers_completed: summary.workers_completed,
|
|
162
|
+
patch_candidates: summary.patch_candidates,
|
|
163
|
+
gate_passed_candidates: summary.gate_passed_candidates,
|
|
164
|
+
mergeable_candidates: summary.mergeable_candidates,
|
|
165
|
+
applied_patches: summary.applied_patches,
|
|
166
|
+
failed_shards: summary.failed_shards,
|
|
167
|
+
repair_waves: summary.repair_waves,
|
|
168
|
+
budget_used_ms: summary.budget_used_ms,
|
|
169
|
+
blockers: terminalState === 'blocked' ? ['no_gate_passed_candidates'] : [],
|
|
170
|
+
warnings: []
|
|
171
|
+
};
|
|
172
|
+
const artifactDir = await writeMissionArtifacts({
|
|
173
|
+
root: cwd,
|
|
174
|
+
missionId,
|
|
175
|
+
workGraph: graph,
|
|
176
|
+
conflictGraph,
|
|
177
|
+
mergePlan,
|
|
178
|
+
...(judgeResult ? { judgeResult } : {}),
|
|
179
|
+
workerTraces: traceState.workerTraces,
|
|
180
|
+
providerHealth: healthTracker.snapshot(),
|
|
181
|
+
termination: { schema: 'sks.glm-naruto-termination.v1', mission_id: missionId, terminal_state: terminalState, reason: terminationReason, wall_clock_ms: summary.wall_clock_ms },
|
|
182
|
+
...(applyResult ? { applyResult: { ...applyResult, schema: 'sks.glm-naruto-apply-result.v1' } } : {}),
|
|
183
|
+
verificationSummary: { schema: 'sks.glm-naruto-verification.v1', verified: passedEnvelopes.length, total: envelopes.length },
|
|
184
|
+
missionResult: result
|
|
185
|
+
});
|
|
186
|
+
return { ...result, artifact_dir: artifactDir };
|
|
187
|
+
}
|
|
188
|
+
function missionResult(missionId, task, status, reason, patchCandidates, startedMs, envelopes, traces, blockers, warnings) {
|
|
189
|
+
return {
|
|
190
|
+
schema: 'sks.glm-naruto-mission-result.v1',
|
|
191
|
+
ok: status === 'completed',
|
|
192
|
+
status,
|
|
193
|
+
mission_id: missionId,
|
|
194
|
+
task,
|
|
195
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
196
|
+
gpt_fallback_allowed: false,
|
|
197
|
+
termination_reason: reason,
|
|
198
|
+
workers_started: traces.length,
|
|
199
|
+
workers_completed: traces.filter((t) => t.status === 'completed').length,
|
|
200
|
+
patch_candidates: envelopes.length,
|
|
201
|
+
gate_passed_candidates: envelopes.filter((e) => e.status === 'gate_passed').length,
|
|
202
|
+
mergeable_candidates: 0,
|
|
203
|
+
applied_patches: 0,
|
|
204
|
+
failed_shards: 0,
|
|
205
|
+
repair_waves: 0,
|
|
206
|
+
budget_used_ms: Date.now() - startedMs,
|
|
207
|
+
blockers,
|
|
208
|
+
warnings
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async function readGitStatus(cwd) {
|
|
212
|
+
const { spawn } = await import('node:child_process');
|
|
213
|
+
return new Promise((resolve) => {
|
|
214
|
+
const child = spawn('git', ['status', '--short'], { cwd, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
215
|
+
let stdout = '';
|
|
216
|
+
child.stdout.on('data', (chunk) => { stdout += String(chunk); });
|
|
217
|
+
child.on('close', () => resolve(stdout.trim() || undefined));
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function extractMentionedPaths(task) {
|
|
221
|
+
const matches = task.match(/(?:^|\s|[`"'])([A-Za-z0-9_.-]+\/[A-Za-z0-9_./-]+\.[A-Za-z0-9]+)(?:\s|[`"']|$)/g) || [];
|
|
222
|
+
return [...new Set(matches.map((value) => value.trim().replace(/^[`"']|[`"']$/g, '')))];
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=glm-naruto-orchestrator.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { nowIso } from '../../../fsx.js';
|
|
3
|
+
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
4
|
+
import { parseUnifiedDiffPatch } from '../glm-patch-parser.js';
|
|
5
|
+
export function createPatchEnvelope(input) {
|
|
6
|
+
const parsed = parseUnifiedDiffPatch(input.patch);
|
|
7
|
+
return {
|
|
8
|
+
schema: 'sks.glm-naruto-patch-envelope.v1',
|
|
9
|
+
mission_id: input.missionId,
|
|
10
|
+
worker_id: input.workerId,
|
|
11
|
+
shard_id: input.shardId,
|
|
12
|
+
base_digest: input.baseDigest,
|
|
13
|
+
target_paths: parsed.touchedPaths,
|
|
14
|
+
patch: input.patch,
|
|
15
|
+
patch_sha256: crypto.createHash('sha256').update(input.patch).digest('hex'),
|
|
16
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
17
|
+
provider: 'openrouter',
|
|
18
|
+
reasoning_effort: input.reasoningEffort,
|
|
19
|
+
gpt_fallback_allowed: false,
|
|
20
|
+
generated_at: nowIso(),
|
|
21
|
+
status: input.status || 'candidate',
|
|
22
|
+
blockers: input.blockers || [],
|
|
23
|
+
warnings: input.warnings || [],
|
|
24
|
+
strategy: input.strategy
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function normalizePatchForDigest(patch) {
|
|
28
|
+
return patch.replace(/\s+/g, ' ').trim();
|
|
29
|
+
}
|
|
30
|
+
export function digestPatch(patch) {
|
|
31
|
+
return crypto.createHash('sha256').update(normalizePatchForDigest(patch)).digest('hex');
|
|
32
|
+
}
|
|
33
|
+
export function parsePatchCandidateOutput(text) {
|
|
34
|
+
const patchStart = text.indexOf('<sks_patch_candidate>');
|
|
35
|
+
const patchEnd = text.indexOf('</sks_patch_candidate>');
|
|
36
|
+
if (patchStart >= 0 && patchEnd > patchStart) {
|
|
37
|
+
return { kind: 'patch', content: text.slice(patchStart + '<sks_patch_candidate>'.length, patchEnd).trim() };
|
|
38
|
+
}
|
|
39
|
+
const needStart = text.indexOf('<sks_need_context>');
|
|
40
|
+
const needEnd = text.indexOf('</sks_need_context>');
|
|
41
|
+
if (needStart >= 0 && needEnd > needStart) {
|
|
42
|
+
const body = text.slice(needStart + '<sks_need_context>'.length, needEnd).trim();
|
|
43
|
+
const paths = body.split(/\r?\n/).map((line) => line.match(/^\s*-\s*(.+?)\s*$/)?.[1]).filter((v) => Boolean(v));
|
|
44
|
+
return { kind: 'need_context', content: body, paths };
|
|
45
|
+
}
|
|
46
|
+
const blockedStart = text.indexOf('<sks_blocked>');
|
|
47
|
+
const blockedEnd = text.indexOf('</sks_blocked>');
|
|
48
|
+
if (blockedStart >= 0 && blockedEnd > blockedStart) {
|
|
49
|
+
const body = text.slice(blockedStart + '<sks_blocked>'.length, blockedEnd).trim();
|
|
50
|
+
const reason = body.match(/reason:\s*(.+)/i)?.[1]?.trim() || body;
|
|
51
|
+
return { kind: 'blocked', content: body, reason };
|
|
52
|
+
}
|
|
53
|
+
return { kind: 'malformed', content: text.trim(), reason: 'missing_glm_naruto_output_envelope' };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=glm-naruto-patch-envelope.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { digestPatch } from './glm-naruto-patch-envelope.js';
|
|
2
|
+
export function evaluateQuorum(envelopes) {
|
|
3
|
+
const byShard = new Map();
|
|
4
|
+
for (const env of envelopes) {
|
|
5
|
+
if (env.status !== 'gate_passed' && env.status !== 'candidate')
|
|
6
|
+
continue;
|
|
7
|
+
const list = byShard.get(env.shard_id) || [];
|
|
8
|
+
list.push(env);
|
|
9
|
+
byShard.set(env.shard_id, list);
|
|
10
|
+
}
|
|
11
|
+
const results = [];
|
|
12
|
+
for (const [shardId, envs] of byShard) {
|
|
13
|
+
const digestCounts = new Map();
|
|
14
|
+
for (const env of envs) {
|
|
15
|
+
const digest = digestPatch(env.patch);
|
|
16
|
+
const existing = digestCounts.get(digest) || { count: 0, patches: [] };
|
|
17
|
+
existing.count++;
|
|
18
|
+
existing.patches.push(env.patch);
|
|
19
|
+
digestCounts.set(digest, existing);
|
|
20
|
+
}
|
|
21
|
+
let best = null;
|
|
22
|
+
for (const [digest, info] of digestCounts) {
|
|
23
|
+
if (!best || info.count > best.count) {
|
|
24
|
+
best = { count: info.count, patches: info.patches, digest };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
results.push({
|
|
28
|
+
shardId,
|
|
29
|
+
consensusDigest: best ? best.digest : null,
|
|
30
|
+
voteCount: best ? best.count : 0,
|
|
31
|
+
totalCandidates: envs.length,
|
|
32
|
+
consensusPatches: best ? best.patches : []
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=glm-naruto-quorum.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function createRateLimitState() {
|
|
2
|
+
return { rateLimited: false, retryAfterMs: 0, consecutive429: 0, last429At: null };
|
|
3
|
+
}
|
|
4
|
+
export function handleRateLimit(state, retryAfterMs) {
|
|
5
|
+
return {
|
|
6
|
+
rateLimited: true,
|
|
7
|
+
retryAfterMs,
|
|
8
|
+
consecutive429: state.consecutive429 + 1,
|
|
9
|
+
last429At: Date.now()
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function clearRateLimit(state) {
|
|
13
|
+
return { rateLimited: false, retryAfterMs: 0, consecutive429: 0, last429At: state.last429At };
|
|
14
|
+
}
|
|
15
|
+
export function shouldBackoff(state) {
|
|
16
|
+
return state.consecutive429 > 3 || (state.last429At !== null && Date.now() - state.last429At < state.retryAfterMs);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=glm-naruto-rate-limiter.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
|
|
2
|
+
export function planRepairWave(input) {
|
|
3
|
+
if (input.repairWaveCount >= GLM_NARUTO_LIMITS.max_repair_waves) {
|
|
4
|
+
return { shardsToRepair: [], canRepair: false, reason: 'max_repair_waves_reached' };
|
|
5
|
+
}
|
|
6
|
+
const failedShardIds = new Set(input.failedEnvelopes.map((e) => e.shard_id));
|
|
7
|
+
const shardsToRepair = input.shards.filter((s) => failedShardIds.has(s.id));
|
|
8
|
+
if (shardsToRepair.length === 0) {
|
|
9
|
+
return { shardsToRepair: [], canRepair: false, reason: 'no_failed_shards_to_repair' };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
shardsToRepair: shardsToRepair.map((s) => ({
|
|
13
|
+
...s,
|
|
14
|
+
strategy: 'defensive_fix',
|
|
15
|
+
patches_per_shard: 1
|
|
16
|
+
})),
|
|
17
|
+
canRepair: true,
|
|
18
|
+
reason: 'repair_wave_planned'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=glm-naruto-repair-wave.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { NARUTO_PATCH_STRATEGIES, GLM_NARUTO_DEFAULTS } from './glm-naruto-types.js';
|
|
2
|
+
export function planShardCandidates(graph) {
|
|
3
|
+
return graph.shards
|
|
4
|
+
.filter((shard) => shard.mutable)
|
|
5
|
+
.map((shard) => {
|
|
6
|
+
const strategies = assignStrategies(shard);
|
|
7
|
+
return {
|
|
8
|
+
shard,
|
|
9
|
+
strategies,
|
|
10
|
+
candidate_count: shard.patches_per_shard
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function assignStrategies(shard) {
|
|
15
|
+
const base = Math.max(0, NARUTO_PATCH_STRATEGIES.indexOf(shard.strategy));
|
|
16
|
+
const result = [shard.strategy];
|
|
17
|
+
for (let i = 1; i < shard.patches_per_shard && i < NARUTO_PATCH_STRATEGIES.length; i++) {
|
|
18
|
+
const next = NARUTO_PATCH_STRATEGIES[(base + i) % NARUTO_PATCH_STRATEGIES.length] || 'minimal_patch';
|
|
19
|
+
if (!result.includes(next))
|
|
20
|
+
result.push(next);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
export function computeInitialLaneMix(graph) {
|
|
25
|
+
const mutable = graph.mutable_shards.length;
|
|
26
|
+
const total = Math.max(mutable, GLM_NARUTO_DEFAULTS.safe_active_start);
|
|
27
|
+
const patchWorkers = Math.ceil(total * GLM_NARUTO_DEFAULTS.patch_worker_ratio);
|
|
28
|
+
const scouts = Math.max(0, Math.floor(total * GLM_NARUTO_DEFAULTS.scout_ratio));
|
|
29
|
+
const verifiers = Math.max(1, total - patchWorkers - scouts);
|
|
30
|
+
return { patch_workers: patchWorkers, scouts, verifiers };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=glm-naruto-shard-planner.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { nowIso, writeJsonAtomic } from '../../../fsx.js';
|
|
3
|
+
export function createMissionTrace(missionId) {
|
|
4
|
+
return {
|
|
5
|
+
missionId,
|
|
6
|
+
startedMs: Date.now(),
|
|
7
|
+
workerTraces: []
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function recordWorkerTrace(state, trace) {
|
|
11
|
+
return { ...state, workerTraces: [...state.workerTraces, trace] };
|
|
12
|
+
}
|
|
13
|
+
export async function writeMissionArtifacts(input) {
|
|
14
|
+
const dir = path.join(input.root, '.sneakoscope', 'glm-naruto', input.missionId);
|
|
15
|
+
if (input.workGraph)
|
|
16
|
+
await writeJsonAtomic(path.join(dir, 'work-graph.json'), input.workGraph);
|
|
17
|
+
if (input.conflictGraph)
|
|
18
|
+
await writeJsonAtomic(path.join(dir, 'conflict-graph.json'), input.conflictGraph);
|
|
19
|
+
if (input.mergePlan)
|
|
20
|
+
await writeJsonAtomic(path.join(dir, 'final-merge-plan.json'), input.mergePlan);
|
|
21
|
+
if (input.judgeResult)
|
|
22
|
+
await writeJsonAtomic(path.join(dir, 'judge-result.json'), input.judgeResult);
|
|
23
|
+
if (input.workerTraces.length > 0)
|
|
24
|
+
await writeJsonAtomic(path.join(dir, 'worker-traces.json'), input.workerTraces);
|
|
25
|
+
if (input.providerHealth)
|
|
26
|
+
await writeJsonAtomic(path.join(dir, 'provider-health.json'), input.providerHealth);
|
|
27
|
+
if (input.termination)
|
|
28
|
+
await writeJsonAtomic(path.join(dir, 'termination.json'), input.termination);
|
|
29
|
+
if (input.applyResult)
|
|
30
|
+
await writeJsonAtomic(path.join(dir, 'apply-result.json'), input.applyResult);
|
|
31
|
+
if (input.verificationSummary)
|
|
32
|
+
await writeJsonAtomic(path.join(dir, 'verification-summary.json'), input.verificationSummary);
|
|
33
|
+
if (input.missionResult)
|
|
34
|
+
await writeJsonAtomic(path.join(dir, 'mission-result.json'), input.missionResult);
|
|
35
|
+
return dir;
|
|
36
|
+
}
|
|
37
|
+
export function buildMissionSummary(input) {
|
|
38
|
+
return {
|
|
39
|
+
wall_clock_ms: Date.now() - input.startedMs,
|
|
40
|
+
workers_started: input.workerTraces.length,
|
|
41
|
+
workers_completed: input.workerTraces.filter((t) => t.status === 'completed').length,
|
|
42
|
+
patch_candidates: input.patchCandidates,
|
|
43
|
+
gate_passed_candidates: input.gatePassed,
|
|
44
|
+
mergeable_candidates: input.mergeable,
|
|
45
|
+
applied_patches: input.appliedPatches,
|
|
46
|
+
failed_shards: input.failedShards,
|
|
47
|
+
repair_waves: input.repairWaves,
|
|
48
|
+
budget_used_ms: Date.now() - input.startedMs
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=glm-naruto-trace.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
2
|
+
export const GLM_NARUTO_LIMITS = {
|
|
3
|
+
max_waves_speed: 3,
|
|
4
|
+
max_waves_deep: 5,
|
|
5
|
+
max_wall_clock_ms: 300_000,
|
|
6
|
+
max_worker_runtime_ms: 90_000,
|
|
7
|
+
max_total_requests: 128,
|
|
8
|
+
max_requests_per_shard: 4,
|
|
9
|
+
max_no_progress_waves: 1,
|
|
10
|
+
max_repeated_patch_digest: 1,
|
|
11
|
+
max_repair_waves: 1,
|
|
12
|
+
max_merge_attempts: 2
|
|
13
|
+
};
|
|
14
|
+
export const GLM_NARUTO_DEFAULTS = {
|
|
15
|
+
default_clones: 12,
|
|
16
|
+
safe_active_start: 6,
|
|
17
|
+
max_clones: 64,
|
|
18
|
+
patch_worker_ratio: 0.70,
|
|
19
|
+
scout_ratio: 0.10,
|
|
20
|
+
verifier_ratio: 0.20,
|
|
21
|
+
default_patches_per_shard: 2,
|
|
22
|
+
critical_patches_per_shard: 3,
|
|
23
|
+
default_max_tokens: 4096,
|
|
24
|
+
judge_max_tokens: 8192,
|
|
25
|
+
patch_temperature: 0.25,
|
|
26
|
+
patch_top_p: 0.85,
|
|
27
|
+
judge_temperature: 0.1,
|
|
28
|
+
judge_top_p: 0.8
|
|
29
|
+
};
|
|
30
|
+
export const NARUTO_PATCH_STRATEGIES = [
|
|
31
|
+
'minimal_patch',
|
|
32
|
+
'test_first_fix',
|
|
33
|
+
'type_safe_fix',
|
|
34
|
+
'refactor_local',
|
|
35
|
+
'defensive_fix'
|
|
36
|
+
];
|
|
37
|
+
//# sourceMappingURL=glm-naruto-types.js.map
|