sneakoscope 4.0.1 → 4.0.3
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/cli/command-registry.js +1 -0
- package/dist/cli/global-mode-router.js +25 -0
- package/dist/cli/router.js +12 -0
- package/dist/commands/codex-app.js +10 -1
- package/dist/commands/codex.js +15 -1
- package/dist/core/build/build-once-runner.js +49 -16
- package/dist/core/codex-app/glm-model-profile.js +2 -0
- package/dist/core/codex-app/glm-profile-installer.js +61 -0
- package/dist/core/codex-app/glm-profile-schema.js +24 -0
- package/dist/core/codex-control/codex-0141-capability.js +95 -0
- package/dist/core/commands/daemon-command.js +1 -1
- package/dist/core/commands/glm-command.js +5 -0
- package/dist/core/daemon/sksd-client.js +7 -2
- package/dist/core/daemon/sksd-ipc.js +13 -3
- package/dist/core/daemon/sksd.js +62 -3
- package/dist/core/doctor/doctor-dirty-planner.js +97 -7
- package/dist/core/doctor/doctor-transaction.js +21 -5
- package/dist/core/fsx.js +1 -1
- package/dist/core/providers/glm/glm-52-profile.js +30 -0
- package/dist/core/providers/glm/glm-52-request.js +34 -0
- package/dist/core/providers/glm/glm-52-response-guard.js +34 -0
- package/dist/core/providers/glm/glm-52-settings.js +26 -0
- package/dist/core/providers/glm/glm-mad-mode.js +242 -0
- package/dist/core/providers/openrouter/openrouter-client.js +44 -0
- package/dist/core/providers/openrouter/openrouter-error.js +37 -0
- package/dist/core/providers/openrouter/openrouter-secret-store.js +113 -0
- package/dist/core/providers/openrouter/openrouter-types.js +2 -0
- package/dist/core/release/extreme-parallel-scheduler.js +124 -11
- package/dist/core/release/gate-pack-assertion.js +58 -0
- package/dist/core/release/gate-pack-fixture-cache.js +38 -2
- package/dist/core/release/gate-pack-manifest.js +2 -2
- package/dist/core/release/gate-pack-runner.js +9 -93
- package/dist/core/release/release-gate-cache-v2.js +71 -0
- package/dist/core/release/release-gate-dag.js +50 -5
- package/dist/core/release/release-gate-node.js +2 -0
- package/dist/core/release/release-gate-resource-governor.js +2 -0
- package/dist/core/release/resource-class-budget.js +1 -0
- package/dist/core/results.js +2 -0
- package/dist/core/secret-redaction.js +4 -0
- package/dist/core/security/redact-secrets.js +15 -0
- package/dist/core/triwiki/triwiki-affected-graph.js +35 -3
- package/dist/core/triwiki/triwiki-gate-impact-map.js +2 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +12 -2
- package/dist/core/triwiki/triwiki-sla-certificate.js +3 -0
- package/dist/core/version.js +1 -1
- package/dist/scripts/release-4002-required-gates.js +14 -0
- package/dist/scripts/release-gate-dag-runner.js +2 -1
- package/dist/scripts/release-gate-existence-audit.js +1 -2
- package/package.json +16 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { hashJson } from '../triwiki/triwiki-cache-key.js';
|
|
4
|
+
import { triWikiProofBankDir } from '../triwiki/triwiki-proof-bank.js';
|
|
4
5
|
export const DOCTOR_DIRTY_PLAN_SCHEMA = 'sks.doctor-dirty-plan.v1';
|
|
5
6
|
export function planDoctorDirtyRepair(root, phaseIds) {
|
|
6
7
|
const phases = phaseIds.map((id) => {
|
|
@@ -13,6 +14,9 @@ export function planDoctorDirtyRepair(root, phaseIds) {
|
|
|
13
14
|
if (markerState.input_hash !== inputHash) {
|
|
14
15
|
return { id, status: 'dirty', reason: 'input_hash_changed', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired };
|
|
15
16
|
}
|
|
17
|
+
if (markerState.proof_id && !proofExists(root, markerState.proof_id)) {
|
|
18
|
+
return { id, status: 'dirty', reason: 'clean_proof_missing', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: postcheckRequired };
|
|
19
|
+
}
|
|
16
20
|
if (postcheckRequired && !markerState.postcheck_passed) {
|
|
17
21
|
return { id, status: 'dirty', reason: 'postcheck_required', input_hash: inputHash, last_clean_proof_id: markerState.proof_id, postcheck_required: true };
|
|
18
22
|
}
|
|
@@ -23,15 +27,17 @@ export function planDoctorDirtyRepair(root, phaseIds) {
|
|
|
23
27
|
root,
|
|
24
28
|
phases,
|
|
25
29
|
dirty_count: phases.filter((phase) => phase.status === 'dirty').length,
|
|
26
|
-
clean_count: phases.filter((phase) => phase.status === 'clean').length
|
|
30
|
+
clean_count: phases.filter((phase) => phase.status === 'clean').length,
|
|
31
|
+
semantic_dirty_plan_path: dirtyPlanPath(root)
|
|
27
32
|
};
|
|
28
33
|
writeDirtyPlan(root, plan);
|
|
29
34
|
return plan;
|
|
30
35
|
}
|
|
31
|
-
export function markDoctorPhaseClean(root, id) {
|
|
36
|
+
export function markDoctorPhaseClean(root, id, proofId = `doctor-${id}-${Date.now()}`, postcheckPassed = true) {
|
|
32
37
|
const file = markerPath(root, id);
|
|
33
38
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
34
|
-
fs.writeFileSync(file, `${JSON.stringify({ schema: 'sks.doctor-dirty-clean-proof.v1', proof_id:
|
|
39
|
+
fs.writeFileSync(file, `${JSON.stringify({ schema: 'sks.doctor-dirty-clean-proof.v1', proof_id: proofId, cleaned_at: new Date().toISOString(), input_hash: phaseInputHash(root, id), postcheck_passed: postcheckPassed }, null, 2)}\n`);
|
|
40
|
+
return proofId;
|
|
35
41
|
}
|
|
36
42
|
export function isDoctorPhaseClean(plan, id) {
|
|
37
43
|
return plan?.phases.find((phase) => phase.id === id)?.status === 'clean';
|
|
@@ -58,10 +64,9 @@ function phaseInputHash(root, id) {
|
|
|
58
64
|
const file = path.join(root, rel);
|
|
59
65
|
if (!fs.existsSync(file))
|
|
60
66
|
return { rel, hash: 'missing' };
|
|
61
|
-
|
|
62
|
-
return { rel, hash: stat.isDirectory() ? `dir:${stat.mtimeMs}` : hashJson({ size: stat.size, mtimeMs: stat.mtimeMs, text: stat.size < 512_000 ? fs.readFileSync(file, 'utf8') : '' }) };
|
|
67
|
+
return hashSemanticPath(root, rel);
|
|
63
68
|
});
|
|
64
|
-
return hashJson({ id, files, env: phaseEnvPresence(id) });
|
|
69
|
+
return hashJson({ id, files, env: phaseEnvPresence(id), semantic_state: phaseSemanticState(root, id) });
|
|
65
70
|
}
|
|
66
71
|
function phaseInputFiles(id) {
|
|
67
72
|
if (id.includes('zellij'))
|
|
@@ -84,12 +89,97 @@ function phaseEnvPresence(id) {
|
|
|
84
89
|
const keys = id.includes('supabase') ? ['SUPABASE_ACCESS_TOKEN'] : id.includes('context7') ? ['CONTEXT7_API_KEY'] : [];
|
|
85
90
|
return Object.fromEntries(keys.map((key) => [key, process.env[key] !== undefined]));
|
|
86
91
|
}
|
|
92
|
+
function phaseSemanticState(root, id) {
|
|
93
|
+
const config = readTextIfSmall(path.join(root, '.codex', 'config.toml'));
|
|
94
|
+
return {
|
|
95
|
+
zellij_capability_present: id.includes('zellij') ? fs.existsSync(path.join(root, 'src', 'core', 'zellij')) : undefined,
|
|
96
|
+
context7_transport: id.includes('context7') ? parseMcpTransport(config, 'context7') : undefined,
|
|
97
|
+
startup_config_targets: id.includes('startup') ? parseConfigTargets(config) : undefined,
|
|
98
|
+
supabase_env_present: id.includes('supabase') ? process.env.SUPABASE_ACCESS_TOKEN !== undefined : undefined,
|
|
99
|
+
skill_registry_hash: id.includes('skill') ? hashSemanticPath(root, '.agents/skills').hash : undefined,
|
|
100
|
+
native_capability_hash: id.includes('native') ? hashSemanticPath(root, 'src/core/codex-native').hash : undefined,
|
|
101
|
+
secret_fingerprint_hash: id.includes('secret') ? hashJson({ has_allowlist: fs.existsSync(path.join(root, 'safety-mutation-allowlist.json')) }) : undefined
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function hashSemanticPath(root, rel) {
|
|
105
|
+
const absolute = path.join(root, rel);
|
|
106
|
+
if (!fs.existsSync(absolute))
|
|
107
|
+
return { rel, hash: 'missing' };
|
|
108
|
+
const stat = fs.lstatSync(absolute);
|
|
109
|
+
if (stat.isDirectory()) {
|
|
110
|
+
const records = walkFiles(absolute).map((file) => {
|
|
111
|
+
const fileStat = fs.lstatSync(file);
|
|
112
|
+
const relative = path.relative(root, file).replace(/\\/g, '/');
|
|
113
|
+
return fileStat.isSymbolicLink()
|
|
114
|
+
? { path: relative, mode: 'symlink', target: fs.readlinkSync(file) }
|
|
115
|
+
: { path: relative, mode: 'file', hash: hashFile(file), size: fileStat.size };
|
|
116
|
+
});
|
|
117
|
+
return { rel, hash: hashJson(records) };
|
|
118
|
+
}
|
|
119
|
+
if (stat.isSymbolicLink())
|
|
120
|
+
return { rel, hash: hashJson({ mode: 'symlink', target: fs.readlinkSync(absolute) }) };
|
|
121
|
+
return { rel, hash: hashFile(absolute) };
|
|
122
|
+
}
|
|
123
|
+
function walkFiles(dir) {
|
|
124
|
+
const out = [];
|
|
125
|
+
const stack = [dir];
|
|
126
|
+
while (stack.length) {
|
|
127
|
+
const current = stack.pop();
|
|
128
|
+
if (!current)
|
|
129
|
+
continue;
|
|
130
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
131
|
+
const absolute = path.join(current, entry.name);
|
|
132
|
+
if (entry.isDirectory())
|
|
133
|
+
stack.push(absolute);
|
|
134
|
+
else if (entry.isFile() || entry.isSymbolicLink())
|
|
135
|
+
out.push(absolute);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return out.sort();
|
|
139
|
+
}
|
|
140
|
+
function hashFile(file) {
|
|
141
|
+
return hashJson({ text: fs.readFileSync(file, 'utf8') });
|
|
142
|
+
}
|
|
143
|
+
function readTextIfSmall(file) {
|
|
144
|
+
try {
|
|
145
|
+
const stat = fs.statSync(file);
|
|
146
|
+
if (stat.size > 512_000)
|
|
147
|
+
return '';
|
|
148
|
+
return fs.readFileSync(file, 'utf8');
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return '';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function parseMcpTransport(text, name) {
|
|
155
|
+
const block = text.match(new RegExp(`\\[mcp_servers\\.${name}\\]([\\s\\S]*?)(?:\\n\\[|$)`));
|
|
156
|
+
if (!block?.[1])
|
|
157
|
+
return null;
|
|
158
|
+
const transport = block[1].match(/transport\s*=\s*["']?([^"'\n]+)["']?/);
|
|
159
|
+
const command = block[1].match(/command\s*=\s*["']?([^"'\n]+)["']?/);
|
|
160
|
+
return transport?.[1]?.trim() || (command ? 'stdio' : 'unknown');
|
|
161
|
+
}
|
|
162
|
+
function parseConfigTargets(text) {
|
|
163
|
+
return [...text.matchAll(/config_file\s*=\s*["']([^"']+)["']/g)].map((match) => match[1] || '').filter(Boolean).sort();
|
|
164
|
+
}
|
|
165
|
+
function proofExists(root, proofId) {
|
|
166
|
+
const transaction = path.join(root, '.sneakoscope', 'reports', 'doctor-fix-transaction.json');
|
|
167
|
+
if (fs.existsSync(transaction) && fs.readFileSync(transaction, 'utf8').includes(proofId))
|
|
168
|
+
return true;
|
|
169
|
+
const bank = triWikiProofBankDir(root);
|
|
170
|
+
if (!fs.existsSync(bank))
|
|
171
|
+
return false;
|
|
172
|
+
return walkFiles(bank).some((file) => path.basename(file) === `${proofId}.json` || fs.readFileSync(file, 'utf8').includes(proofId));
|
|
173
|
+
}
|
|
87
174
|
function phaseRequiresPostcheck(id) {
|
|
88
175
|
return /zellij|context7|startup|supabase|native|secret/i.test(id);
|
|
89
176
|
}
|
|
90
177
|
function writeDirtyPlan(root, plan) {
|
|
91
|
-
const file =
|
|
178
|
+
const file = dirtyPlanPath(root);
|
|
92
179
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
93
180
|
fs.writeFileSync(file, `${JSON.stringify(plan, null, 2)}\n`);
|
|
94
181
|
}
|
|
182
|
+
function dirtyPlanPath(root) {
|
|
183
|
+
return path.join(root, '.sneakoscope', 'reports', 'doctor-dirty-plan.json');
|
|
184
|
+
}
|
|
95
185
|
//# sourceMappingURL=doctor-dirty-planner.js.map
|
|
@@ -4,6 +4,7 @@ import { isDoctorPhaseClean, markDoctorPhaseClean } from './doctor-dirty-planner
|
|
|
4
4
|
export async function runDoctorFixTransaction(input) {
|
|
5
5
|
const startedAt = nowIso();
|
|
6
6
|
const phases = [];
|
|
7
|
+
const proofIdsUsed = [];
|
|
7
8
|
let rollbackPerformed = false;
|
|
8
9
|
for (const definition of input.phases) {
|
|
9
10
|
const phaseStarted = nowIso();
|
|
@@ -20,10 +21,13 @@ export async function runDoctorFixTransaction(input) {
|
|
|
20
21
|
started_at: phaseStarted
|
|
21
22
|
};
|
|
22
23
|
if (isDoctorPhaseClean(input.dirtyPlan, definition.id)) {
|
|
24
|
+
const proofId = input.dirtyPlan?.phases.find((row) => row.id === definition.id)?.last_clean_proof_id;
|
|
25
|
+
if (proofId)
|
|
26
|
+
proofIdsUsed.push(proofId);
|
|
23
27
|
phases.push({
|
|
24
28
|
...phase,
|
|
25
29
|
ok: true,
|
|
26
|
-
warnings: [
|
|
30
|
+
warnings: [`dirty_plan_skipped_clean_phase${proofId ? `:${proofId}` : ''}`],
|
|
27
31
|
completed_at: nowIso(),
|
|
28
32
|
duration_ms: Math.max(0, Date.now() - startedMs)
|
|
29
33
|
});
|
|
@@ -59,8 +63,11 @@ export async function runDoctorFixTransaction(input) {
|
|
|
59
63
|
}
|
|
60
64
|
phase.completed_at = phase.completed_at || nowIso();
|
|
61
65
|
phase.duration_ms = phase.duration_ms ?? Math.max(0, Date.now() - startedMs);
|
|
62
|
-
if (phase.ok)
|
|
63
|
-
|
|
66
|
+
if (phase.ok) {
|
|
67
|
+
const proofId = `doctor-${definition.id}-${Date.now()}`;
|
|
68
|
+
markDoctorPhaseClean(input.root, definition.id, proofId, true);
|
|
69
|
+
proofIdsUsed.push(proofId);
|
|
70
|
+
}
|
|
64
71
|
phases.push(phase);
|
|
65
72
|
}
|
|
66
73
|
const writeInput = {
|
|
@@ -71,7 +78,11 @@ export async function runDoctorFixTransaction(input) {
|
|
|
71
78
|
};
|
|
72
79
|
if (input.reportPath !== undefined)
|
|
73
80
|
writeInput.reportPath = input.reportPath;
|
|
74
|
-
return writeDoctorFixTransaction(
|
|
81
|
+
return writeDoctorFixTransaction({
|
|
82
|
+
...writeInput,
|
|
83
|
+
dirtyPlan: input.dirtyPlan || null,
|
|
84
|
+
proofIdsUsed
|
|
85
|
+
});
|
|
75
86
|
}
|
|
76
87
|
export async function writeDoctorFixTransaction(input) {
|
|
77
88
|
const root = path.resolve(input.root);
|
|
@@ -99,7 +110,12 @@ export async function writeDoctorFixTransaction(input) {
|
|
|
99
110
|
phases,
|
|
100
111
|
postcheck_ok: postcheckOk,
|
|
101
112
|
rollback_performed: input.rollbackPerformed === true,
|
|
102
|
-
raw_secret_values_recorded: false
|
|
113
|
+
raw_secret_values_recorded: false,
|
|
114
|
+
skipped_clean_phases: phases.filter((phase) => phase.warnings.some((warning) => warning.startsWith('dirty_plan_skipped_clean_phase'))).map((phase) => phase.id),
|
|
115
|
+
dirty_phases: input.dirtyPlan?.phases.filter((phase) => phase.status === 'dirty').map((phase) => phase.id) || phases.filter((phase) => !phase.warnings.some((warning) => warning.startsWith('dirty_plan_skipped_clean_phase'))).map((phase) => phase.id),
|
|
116
|
+
proof_ids_used: [...new Set(input.proofIdsUsed || [])].sort(),
|
|
117
|
+
saved_ms_estimate: phases.filter((phase) => phase.warnings.some((warning) => warning.startsWith('dirty_plan_skipped_clean_phase'))).length * 1000,
|
|
118
|
+
semantic_dirty_plan_path: input.dirtyPlan?.semantic_dirty_plan_path || null
|
|
103
119
|
};
|
|
104
120
|
if (input.reportPath !== null)
|
|
105
121
|
await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'doctor-fix-transaction.json'), report).catch(() => undefined);
|
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.3';
|
|
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() {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GLM_52_DEFAULT_REQUEST_SETTINGS, GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE } from './glm-52-settings.js';
|
|
2
|
+
export const GLM_CODEX_APP_PROFILE_ID = 'sks/glm-5.2-mad';
|
|
3
|
+
export const GLM_CODEX_APP_PROFILE_LABEL = 'GLM 5.2 (MAD / OpenRouter)';
|
|
4
|
+
export function buildGlmCodexAppModelProfile() {
|
|
5
|
+
return {
|
|
6
|
+
schema: 'sks.codex-app-model-profile.v1',
|
|
7
|
+
id: GLM_CODEX_APP_PROFILE_ID,
|
|
8
|
+
label: GLM_CODEX_APP_PROFILE_LABEL,
|
|
9
|
+
provider: 'openrouter',
|
|
10
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
11
|
+
mode: GLM_MAD_MODE,
|
|
12
|
+
strictModelLock: true,
|
|
13
|
+
gptFallbackAllowed: false,
|
|
14
|
+
requiresSecret: 'openrouter-api-key',
|
|
15
|
+
defaultSettings: {
|
|
16
|
+
temperature: GLM_52_DEFAULT_REQUEST_SETTINGS.temperature,
|
|
17
|
+
top_p: GLM_52_DEFAULT_REQUEST_SETTINGS.top_p,
|
|
18
|
+
reasoning_effort: 'high',
|
|
19
|
+
tool_choice: 'auto',
|
|
20
|
+
parallel_tool_calls: false
|
|
21
|
+
},
|
|
22
|
+
codexCompatibility: {
|
|
23
|
+
target: 'rust-v0.141.0',
|
|
24
|
+
selectedExecutorPluginMcp: 'defer-to-codex-native',
|
|
25
|
+
duplicateAppMcpDeclarations: 'dedupe-by-codex',
|
|
26
|
+
cwdShellPathSemantics: 'preserve-codex-native'
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=glm-52-profile.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { GLM_52_DEFAULT_REQUEST_SETTINGS, GLM_52_OPENROUTER_MODEL, clampGlm52MaxTokens } from './glm-52-settings.js';
|
|
2
|
+
export function buildGlm52Request(input) {
|
|
3
|
+
const request = {
|
|
4
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
5
|
+
messages: input.messages,
|
|
6
|
+
stream: input.stream ?? GLM_52_DEFAULT_REQUEST_SETTINGS.stream,
|
|
7
|
+
temperature: GLM_52_DEFAULT_REQUEST_SETTINGS.temperature,
|
|
8
|
+
top_p: GLM_52_DEFAULT_REQUEST_SETTINGS.top_p,
|
|
9
|
+
reasoning: { effort: input.reasoningEffort ?? 'high' },
|
|
10
|
+
max_tokens: clampGlm52MaxTokens(input.maxTokens),
|
|
11
|
+
tool_choice: input.toolChoice ?? 'auto',
|
|
12
|
+
parallel_tool_calls: input.parallelToolCalls ?? false,
|
|
13
|
+
provider: {
|
|
14
|
+
allow_fallbacks: false,
|
|
15
|
+
require_parameters: true,
|
|
16
|
+
sort: input.providerSort ?? 'throughput'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
...request,
|
|
21
|
+
...(input.tools ? { tools: input.tools } : {}),
|
|
22
|
+
...(input.responseFormat ? { response_format: input.responseFormat } : {})
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function buildGlm52KeyValidationRequest() {
|
|
26
|
+
return buildGlm52Request({
|
|
27
|
+
messages: [{ role: 'user', content: 'Reply with OK.' }],
|
|
28
|
+
stream: false,
|
|
29
|
+
maxTokens: 1,
|
|
30
|
+
toolChoice: 'none',
|
|
31
|
+
parallelToolCalls: false
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=glm-52-request.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { GLM_52_OPENROUTER_MODEL } from './glm-52-settings.js';
|
|
2
|
+
export function assertGlm52ActualModel(responseModel) {
|
|
3
|
+
if (!responseModel) {
|
|
4
|
+
return {
|
|
5
|
+
ok: false,
|
|
6
|
+
code: 'glm_model_missing',
|
|
7
|
+
requestedModel: GLM_52_OPENROUTER_MODEL,
|
|
8
|
+
strictModelLock: true,
|
|
9
|
+
gptFallbackAllowed: false
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const normalized = responseModel.toLowerCase();
|
|
13
|
+
if (normalized === GLM_52_OPENROUTER_MODEL ||
|
|
14
|
+
normalized.startsWith(`${GLM_52_OPENROUTER_MODEL}-`) ||
|
|
15
|
+
normalized.includes('glm-5.2')) {
|
|
16
|
+
return {
|
|
17
|
+
ok: true,
|
|
18
|
+
code: 'ok',
|
|
19
|
+
actualModel: responseModel,
|
|
20
|
+
requestedModel: GLM_52_OPENROUTER_MODEL,
|
|
21
|
+
strictModelLock: true,
|
|
22
|
+
gptFallbackAllowed: false
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
code: 'glm_model_mismatch',
|
|
28
|
+
actualModel: responseModel,
|
|
29
|
+
requestedModel: GLM_52_OPENROUTER_MODEL,
|
|
30
|
+
strictModelLock: true,
|
|
31
|
+
gptFallbackAllowed: false
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=glm-52-response-guard.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { OPENROUTER_CHAT_COMPLETIONS_URL } from '../openrouter/openrouter-types.js';
|
|
2
|
+
export const GLM_52_OPENROUTER_MODEL = 'z-ai/glm-5.2';
|
|
3
|
+
export const GLM_MAD_MODE = 'mad-glm';
|
|
4
|
+
export const GLM_52_MAX_TOKENS_DEFAULT = 32768;
|
|
5
|
+
export const GLM_52_MAX_TOKENS_LONG = 65536;
|
|
6
|
+
export const GLM_52_MAX_TOKENS_XLONG = 131072;
|
|
7
|
+
export const GLM_52_TOP_PROVIDER_MAX_COMPLETION_TOKENS = 262144;
|
|
8
|
+
export const GLM_52_DEFAULT_REQUEST_SETTINGS = {
|
|
9
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
10
|
+
temperature: 1,
|
|
11
|
+
top_p: 0.95,
|
|
12
|
+
reasoning_effort: 'high',
|
|
13
|
+
stream: true,
|
|
14
|
+
provider: {
|
|
15
|
+
allow_fallbacks: false,
|
|
16
|
+
require_parameters: true
|
|
17
|
+
},
|
|
18
|
+
tool_choice: 'auto',
|
|
19
|
+
parallel_tool_calls: false,
|
|
20
|
+
max_tokens: GLM_52_MAX_TOKENS_DEFAULT
|
|
21
|
+
};
|
|
22
|
+
export function clampGlm52MaxTokens(value) {
|
|
23
|
+
const numeric = Number.isFinite(value) ? Math.floor(Number(value)) : GLM_52_MAX_TOKENS_DEFAULT;
|
|
24
|
+
return Math.max(1, Math.min(numeric, GLM_52_TOP_PROVIDER_MAX_COMPLETION_TOKENS));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=glm-52-settings.js.map
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import readline from 'node:readline/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
4
|
+
import { printJson } from '../../../cli/output.js';
|
|
5
|
+
import { flag } from '../../../cli/args.js';
|
|
6
|
+
import { nowIso, writeJsonAtomic } from '../../fsx.js';
|
|
7
|
+
import {} from '../openrouter/openrouter-types.js';
|
|
8
|
+
import { sendOpenRouterChatCompletion } from '../openrouter/openrouter-client.js';
|
|
9
|
+
import { resolveOpenRouterApiKey, writeStoredOpenRouterKey } from '../openrouter/openrouter-secret-store.js';
|
|
10
|
+
import { redactOpenRouterKey } from '../../security/redact-secrets.js';
|
|
11
|
+
import { buildGlmCodexAppModelProfile } from './glm-52-profile.js';
|
|
12
|
+
import { buildGlm52KeyValidationRequest, buildGlm52Request } from './glm-52-request.js';
|
|
13
|
+
import { assertGlm52ActualModel } from './glm-52-response-guard.js';
|
|
14
|
+
import { GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE, OPENROUTER_CHAT_COMPLETIONS_URL } from './glm-52-settings.js';
|
|
15
|
+
export async function runMadGlmMode(args = [], adapters = {}) {
|
|
16
|
+
const runtime = buildDefaultAdapters(adapters);
|
|
17
|
+
const repair = flag(args, '--repair');
|
|
18
|
+
const noSaveKey = flag(args, '--no-save-key');
|
|
19
|
+
const skipValidation = flag(args, '--skip-validation');
|
|
20
|
+
const json = flag(args, '--json');
|
|
21
|
+
const profile = buildGlmCodexAppModelProfile();
|
|
22
|
+
let result;
|
|
23
|
+
if (repair) {
|
|
24
|
+
const key = await runtime.promptSecret('OpenRouter API key is required for GLM 5.2 mode.\nEnter OpenRouter API key: ');
|
|
25
|
+
if (!key) {
|
|
26
|
+
result = baseResult({
|
|
27
|
+
status: 'blocked',
|
|
28
|
+
blockers: ['glm_key_prompt_cancelled'],
|
|
29
|
+
warnings: []
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (!noSaveKey)
|
|
34
|
+
await runtime.writeSecret(key);
|
|
35
|
+
const validation = skipValidation
|
|
36
|
+
? { ok: true, value: validationValue(null) }
|
|
37
|
+
: await runtime.validateOpenRouterKey(key);
|
|
38
|
+
result = validation.ok
|
|
39
|
+
? baseResult({
|
|
40
|
+
status: 'ready',
|
|
41
|
+
...(validation.value.actual_model ? { actual_model: validation.value.actual_model } : {}),
|
|
42
|
+
openrouter_key_source: noSaveKey ? 'prompt' : 'user-secret-store',
|
|
43
|
+
key_preview: redactOpenRouterKey(key),
|
|
44
|
+
blockers: [],
|
|
45
|
+
warnings: noSaveKey ? ['openrouter_key_not_saved'] : []
|
|
46
|
+
})
|
|
47
|
+
: baseResult({
|
|
48
|
+
status: 'blocked',
|
|
49
|
+
openrouter_key_source: noSaveKey ? 'prompt' : 'user-secret-store',
|
|
50
|
+
key_preview: redactOpenRouterKey(key),
|
|
51
|
+
blockers: [validation.error.code],
|
|
52
|
+
warnings: []
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const resolved = await resolveOpenRouterApiKey({ env: runtime.env });
|
|
58
|
+
if (!resolved.key && process.stdin.isTTY) {
|
|
59
|
+
const key = await runtime.promptSecret('OpenRouter API key is required for GLM 5.2 mode.\nEnter OpenRouter API key: ');
|
|
60
|
+
if (!key) {
|
|
61
|
+
result = baseResult({ status: 'blocked', blockers: ['glm_key_prompt_cancelled'], warnings: [] });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const save = noSaveKey ? false : await runtime.promptConfirm('Save this key for future SKS GLM runs? [Y/n] ', true);
|
|
65
|
+
if (save)
|
|
66
|
+
await runtime.writeSecret(key);
|
|
67
|
+
result = baseResult({
|
|
68
|
+
status: 'ready',
|
|
69
|
+
openrouter_key_source: save ? 'user-secret-store' : 'prompt',
|
|
70
|
+
key_preview: redactOpenRouterKey(key),
|
|
71
|
+
blockers: [],
|
|
72
|
+
warnings: save ? [] : ['openrouter_key_not_saved']
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (!resolved.key) {
|
|
77
|
+
result = baseResult({
|
|
78
|
+
status: 'blocked',
|
|
79
|
+
blockers: resolved.blockers,
|
|
80
|
+
warnings: ['set_OPENROUTER_API_KEY_or_run_sks_--mad_--glm_--repair']
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
result = baseResult({
|
|
85
|
+
status: 'ready',
|
|
86
|
+
...(resolved.source ? { openrouter_key_source: resolved.source } : {}),
|
|
87
|
+
key_preview: resolved.key_preview,
|
|
88
|
+
blockers: [],
|
|
89
|
+
warnings: resolved.warnings
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
await writeGlmModeArtifacts(runtime.cwd, result, profile, runtime.nowIso()).catch(() => undefined);
|
|
94
|
+
if (json)
|
|
95
|
+
printJson(result);
|
|
96
|
+
else
|
|
97
|
+
printHumanGlmResult(result, runtime.log);
|
|
98
|
+
if (!result.ok)
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
function baseResult(input) {
|
|
103
|
+
const result = {
|
|
104
|
+
schema: 'sks.glm-mode-result.v1',
|
|
105
|
+
ok: input.blockers.length === 0 && input.status !== 'failed',
|
|
106
|
+
status: input.status,
|
|
107
|
+
mode: GLM_MAD_MODE,
|
|
108
|
+
provider: 'openrouter',
|
|
109
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
110
|
+
requested_model: GLM_52_OPENROUTER_MODEL,
|
|
111
|
+
strict_model_lock: true,
|
|
112
|
+
gpt_fallback_allowed: false,
|
|
113
|
+
codex_app_profile_id: 'sks/glm-5.2-mad',
|
|
114
|
+
blockers: input.blockers,
|
|
115
|
+
warnings: input.warnings
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
...result,
|
|
119
|
+
...(input.actual_model ? { actual_model: input.actual_model } : {}),
|
|
120
|
+
...(input.openrouter_key_source ? { openrouter_key_source: input.openrouter_key_source } : {}),
|
|
121
|
+
...(input.key_preview !== undefined ? { key_preview: input.key_preview } : {})
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function buildDefaultAdapters(overrides) {
|
|
125
|
+
return {
|
|
126
|
+
nowIso: overrides.nowIso || nowIso,
|
|
127
|
+
env: overrides.env || process.env,
|
|
128
|
+
cwd: overrides.cwd || process.cwd(),
|
|
129
|
+
promptSecret: overrides.promptSecret || promptLine,
|
|
130
|
+
promptConfirm: overrides.promptConfirm || promptConfirmLine,
|
|
131
|
+
writeSecret: overrides.writeSecret || (async (value) => {
|
|
132
|
+
await writeStoredOpenRouterKey(value);
|
|
133
|
+
}),
|
|
134
|
+
validateOpenRouterKey: overrides.validateOpenRouterKey || validateOpenRouterKey,
|
|
135
|
+
sendOpenRouterRequest: overrides.sendOpenRouterRequest || (async (request, key) => sendOpenRouterChatCompletion({ request, apiKey: key })),
|
|
136
|
+
log: overrides.log || ((message) => console.log(message))
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function validateOpenRouterKey(key) {
|
|
140
|
+
const response = await sendOpenRouterChatCompletion({
|
|
141
|
+
apiKey: key,
|
|
142
|
+
request: buildGlm52KeyValidationRequest()
|
|
143
|
+
});
|
|
144
|
+
if (!response.ok)
|
|
145
|
+
return response;
|
|
146
|
+
const guard = assertGlm52ActualModel(response.value.model);
|
|
147
|
+
if (!guard.ok) {
|
|
148
|
+
return {
|
|
149
|
+
ok: false,
|
|
150
|
+
error: {
|
|
151
|
+
code: guard.code,
|
|
152
|
+
message: 'GLM model lock violated.',
|
|
153
|
+
severity: 'blocked'
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return { ok: true, value: validationValue(response.value.model || null) };
|
|
158
|
+
}
|
|
159
|
+
function validationValue(actualModel) {
|
|
160
|
+
return {
|
|
161
|
+
schema: 'sks.openrouter-key-validation.v1',
|
|
162
|
+
ok: true,
|
|
163
|
+
requested_model: GLM_52_OPENROUTER_MODEL,
|
|
164
|
+
actual_model: actualModel,
|
|
165
|
+
strict_model_lock: true,
|
|
166
|
+
gpt_fallback_allowed: false
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
async function writeGlmModeArtifacts(cwd, result, profile, generatedAt) {
|
|
170
|
+
const dir = path.join(cwd, '.sneakoscope', 'glm');
|
|
171
|
+
await writeJsonAtomic(path.join(dir, 'mad-glm-session.json'), {
|
|
172
|
+
schema: 'sks.glm-mad-session.v1',
|
|
173
|
+
generated_at: generatedAt,
|
|
174
|
+
result,
|
|
175
|
+
profile_id: profile.id
|
|
176
|
+
});
|
|
177
|
+
await writeJsonAtomic(path.join(dir, 'openrouter-request-summary.json'), {
|
|
178
|
+
schema: 'sks.openrouter-request-summary.v1',
|
|
179
|
+
generated_at: generatedAt,
|
|
180
|
+
endpoint: OPENROUTER_CHAT_COMPLETIONS_URL,
|
|
181
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
182
|
+
temperature: 1,
|
|
183
|
+
top_p: 0.95,
|
|
184
|
+
reasoning_effort: 'high',
|
|
185
|
+
stream: true,
|
|
186
|
+
provider_allow_fallbacks: false,
|
|
187
|
+
require_parameters: true,
|
|
188
|
+
key_source: result.openrouter_key_source || null,
|
|
189
|
+
key_preview: result.key_preview || null
|
|
190
|
+
});
|
|
191
|
+
await writeJsonAtomic(path.join(dir, 'model-guard.json'), {
|
|
192
|
+
schema: 'sks.glm-model-guard.v1',
|
|
193
|
+
generated_at: generatedAt,
|
|
194
|
+
requested_model: GLM_52_OPENROUTER_MODEL,
|
|
195
|
+
actual_model: result.actual_model || null,
|
|
196
|
+
accepted: result.actual_model ? assertGlm52ActualModel(result.actual_model).ok : result.ok,
|
|
197
|
+
strict_model_lock: true,
|
|
198
|
+
gpt_fallback_allowed: false,
|
|
199
|
+
blockers: result.blockers
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function printHumanGlmResult(result, log) {
|
|
203
|
+
log(`GLM 5.2 MAD mode: ${result.ok ? result.status : 'blocked'}`);
|
|
204
|
+
log(`Model: ${result.model}`);
|
|
205
|
+
log(`GPT fallback: ${result.gpt_fallback_allowed ? 'allowed' : 'blocked'}`);
|
|
206
|
+
if (result.openrouter_key_source)
|
|
207
|
+
log(`OpenRouter key: ${result.openrouter_key_source} ${result.key_preview || ''}`.trim());
|
|
208
|
+
for (const blocker of result.blockers)
|
|
209
|
+
log(`- blocker: ${blocker}`);
|
|
210
|
+
for (const warning of result.warnings)
|
|
211
|
+
log(`- warning: ${warning}`);
|
|
212
|
+
}
|
|
213
|
+
async function promptLine(prompt) {
|
|
214
|
+
if (!process.stdin.isTTY)
|
|
215
|
+
return null;
|
|
216
|
+
const rl = readline.createInterface({ input, output });
|
|
217
|
+
try {
|
|
218
|
+
const answer = await rl.question(prompt);
|
|
219
|
+
return answer.trim() || null;
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
rl.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function promptConfirmLine(prompt, defaultYes) {
|
|
226
|
+
if (!process.stdin.isTTY)
|
|
227
|
+
return defaultYes;
|
|
228
|
+
const answer = await promptLine(prompt);
|
|
229
|
+
if (!answer)
|
|
230
|
+
return defaultYes;
|
|
231
|
+
return !/^n(o)?$/i.test(answer);
|
|
232
|
+
}
|
|
233
|
+
export function buildGlmModeDryRunRequest() {
|
|
234
|
+
return buildGlm52Request({
|
|
235
|
+
messages: [{ role: 'user', content: 'SKS GLM dry run.' }],
|
|
236
|
+
stream: false,
|
|
237
|
+
maxTokens: 1,
|
|
238
|
+
toolChoice: 'none',
|
|
239
|
+
parallelToolCalls: false
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=glm-mad-mode.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { OPENROUTER_CHAT_COMPLETIONS_URL } from './openrouter-types.js';
|
|
2
|
+
import { invalidOpenRouterResponseIssue, normalizeOpenRouterError } from './openrouter-error.js';
|
|
3
|
+
import { redactOpenRouterString } from '../../security/redact-secrets.js';
|
|
4
|
+
export async function sendOpenRouterChatCompletion(input) {
|
|
5
|
+
try {
|
|
6
|
+
const doFetch = input.fetchImpl || fetch;
|
|
7
|
+
const response = await doFetch(input.endpoint || OPENROUTER_CHAT_COMPLETIONS_URL, {
|
|
8
|
+
method: 'POST',
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: `Bearer ${input.apiKey}`,
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
'X-OpenRouter-Title': 'Sneakoscope-Codex'
|
|
13
|
+
},
|
|
14
|
+
body: JSON.stringify(input.request)
|
|
15
|
+
});
|
|
16
|
+
const text = await response.text();
|
|
17
|
+
if (!response.ok)
|
|
18
|
+
return { ok: false, error: normalizeOpenRouterError(response.status, text) };
|
|
19
|
+
return parseOpenRouterResponse(text);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
error: {
|
|
25
|
+
code: 'glm_openrouter_request_failed',
|
|
26
|
+
message: redactOpenRouterString(err instanceof Error ? err.message : String(err)),
|
|
27
|
+
severity: 'failed'
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function parseOpenRouterResponse(text) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(text);
|
|
35
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
36
|
+
return { ok: false, error: invalidOpenRouterResponseIssue('OpenRouter response was not an object.', text) };
|
|
37
|
+
}
|
|
38
|
+
return { ok: true, value: parsed };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return { ok: false, error: invalidOpenRouterResponseIssue('OpenRouter response was not valid JSON.', text) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=openrouter-client.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { redactOpenRouterString } from '../../security/redact-secrets.js';
|
|
2
|
+
export function normalizeOpenRouterError(status, body) {
|
|
3
|
+
const code = status === 401 || status === 403
|
|
4
|
+
? 'glm_openrouter_unauthorized'
|
|
5
|
+
: status === 429
|
|
6
|
+
? 'glm_openrouter_rate_limited'
|
|
7
|
+
: status >= 500
|
|
8
|
+
? 'glm_openrouter_provider_unavailable'
|
|
9
|
+
: 'glm_openrouter_request_failed';
|
|
10
|
+
return {
|
|
11
|
+
code,
|
|
12
|
+
message: statusMessage(code),
|
|
13
|
+
severity: status >= 500 ? 'failed' : 'blocked',
|
|
14
|
+
status,
|
|
15
|
+
redacted_body_tail: redactOpenRouterString(body).slice(-2000)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function invalidOpenRouterResponseIssue(message, body) {
|
|
19
|
+
const issue = {
|
|
20
|
+
code: 'glm_openrouter_invalid_response',
|
|
21
|
+
message,
|
|
22
|
+
severity: 'failed'
|
|
23
|
+
};
|
|
24
|
+
return body ? { ...issue, redacted_body_tail: redactOpenRouterString(body).slice(-2000) } : issue;
|
|
25
|
+
}
|
|
26
|
+
function statusMessage(code) {
|
|
27
|
+
if (code === 'glm_openrouter_unauthorized')
|
|
28
|
+
return 'OpenRouter rejected the GLM API key.';
|
|
29
|
+
if (code === 'glm_openrouter_rate_limited')
|
|
30
|
+
return 'OpenRouter rate limited the GLM request.';
|
|
31
|
+
if (code === 'glm_openrouter_provider_unavailable')
|
|
32
|
+
return 'OpenRouter provider is unavailable for GLM 5.2.';
|
|
33
|
+
if (code === 'glm_openrouter_invalid_response')
|
|
34
|
+
return 'OpenRouter returned an invalid response.';
|
|
35
|
+
return 'OpenRouter request failed.';
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=openrouter-error.js.map
|