sneakoscope 4.0.5 → 4.0.7

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 CHANGED
@@ -35,16 +35,20 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **4.0.5** tunes only the GLM 5.2 MAD path: `sks --mad --glm` now defaults to an xhigh reasoning profile while recovering speed through compact GLM context, disabled default tools, streaming, request/schema caches, and redacted bench/trace artifacts. Ordinary `sks --mad`, Naruto/Team, and non-GLM Codex paths keep their existing defaults.
38
+ SKS **4.0.6** makes the GLM 5.2 MAD path bounded by default: `sks --mad --glm` now returns readiness/status and exits when no task is supplied, while task forms use a direct GLM-only speed path with loop guards, request timeouts, and deterministic patch gates. Ordinary `sks --mad`, Naruto/Team, and non-GLM Codex paths keep their existing defaults.
39
39
 
40
- What changed in 4.0.5:
40
+ What changed in 4.0.7:
41
41
 
42
- - **GLM-only xhigh speed profile.** `sks --mad --glm` keeps OpenRouter locked to `z-ai/glm-5.2`, uses `reasoning.effort: xhigh`, and bounds the default completion budget to the speed profile instead of changing global SKS reasoning defaults.
43
- - **Compact GLM request shape.** The GLM speed profile uses streaming, `tool_choice: none`, no fallback `models`, `provider.allow_fallbacks: false`, `provider.require_parameters: true`, and throughput/latency provider preferences.
44
- - **Opt-in GLM depth controls.** `--deep`, `--xhigh`, `--strict`, `--ttft`, and `--exact-provider` select explicit GLM profiles without affecting non-GLM routes.
45
- - **GLM speed infrastructure.** GLM-only context budgeting, encoded request cache, tool schema cache, model metadata cache, output envelope parsing, deterministic patch gating, latency traces, and `--bench` dry-run diagnostics are covered by tests.
46
- - **No GPT fallback panes.** GLM MAD keeps the existing GPT/codex-sdk native swarm disabled by default until a GLM worker backend exists, preserving the no-fallback guarantee.
47
- - **4.0.4 GLM launch proof remains.** Each GLM MAD launch still writes `mad-glm-launch.json` with provider/model/profile/wrapper evidence and keeps OpenRouter keys out of layout artifacts.
42
+ - **`--open` alias for interactive GLM launch.** `sks --mad --glm --open` now opens the GLM interactive Zellij runtime, equivalent to `sks --mad --glm --interactive`.
43
+
44
+ What changed in 4.0.6:
45
+
46
+ - **No default long-lived GLM launch.** Bare `sks --mad --glm` no longer falls through to MAD/Zellij; `--interactive`, `--open`, `--zellij`, or `session` is required for that path.
47
+ - **Fast GLM speed profile.** Speed mode keeps OpenRouter locked to `z-ai/glm-5.2`, disables GPT/model fallback, avoids high/xhigh reasoning by default, and uses `provider.require_parameters: false` with throughput-first routing.
48
+ - **Bounded direct task runs.** `sks --mad --glm run "task"` and `sks --mad --glm "task"` use a one-shot GLM speed run with max-turn, wall-clock, request-timeout, no-progress, repeated-output, and terminal-state guards.
49
+ - **Deterministic mutation gate.** GLM still returns patch envelopes; SKS parses the unified diff, blocks protected paths, runs `git apply --check`, and applies only after the gate passes.
50
+ - **OpenRouter speed plumbing.** Encoded request bodies are cached without Authorization headers, request timeout/abort is wired, streaming TTFT/usage capture is scaffolded, and synthetic `--bench` remains network-free by default.
51
+ - **Loop regression tests.** Routing, speed-profile, cache, loop-guard, patch-gate, and OpenRouter key handling are covered by targeted tests.
48
52
 
49
53
  SKS **3.1.16** was a launch-reliability patch on the 3.1.15 doctor-reliability release. It made `sks --mad` self-bootstrap a fresh project instead of dead-ending on a missing Codex config.
50
54
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "4.0.5"
79
+ version = "4.0.6"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "4.0.5"
3
+ version = "4.0.6"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -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.5"),
7
+ Some("--version") => println!("sks-rs 4.0.6"),
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '4.0.5';
2
+ const FAST_PACKAGE_VERSION = '4.0.6';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -1,8 +1,9 @@
1
- import { runMadGlmMode } from '../providers/glm/glm-mad-mode.js';
2
- import { flag } from '../../cli/args.js';
3
- import { madHighCommand } from './mad-sks-command.js';
1
+ import { flag, positionalArgs } from '../../cli/args.js';
4
2
  import { runGlmBench } from '../providers/glm/glm-bench.js';
5
3
  import { printJson } from '../../cli/output.js';
4
+ import { runGlmDirectSpeedRun } from '../providers/glm/glm-direct-run.js';
5
+ import { runGlmReadinessAndExit } from '../providers/glm/glm-readiness.js';
6
+ import { runGlmInteractiveLaunch } from '../providers/glm/glm-interactive-launch.js';
6
7
  export async function glmCommand(args = []) {
7
8
  if (flag(args, '--bench')) {
8
9
  const result = await runGlmBench(process.cwd(), args);
@@ -16,9 +17,39 @@ export async function glmCommand(args = []) {
16
17
  console.log(`GLM bench: dry-run p50=${result.summary.speed_p50_total_ms}ms ratio=${result.summary.speed_vs_deep_ratio}`);
17
18
  return result;
18
19
  }
19
- const result = await runMadGlmMode(args);
20
- if (!result.ok || flag(args, '--repair') || flag(args, '--json'))
21
- return result;
22
- return madHighCommand(['--glm', ...args], { glmReadiness: result, glmArgs: args });
20
+ const task = extractGlmTask(args);
21
+ const interactive = flag(args, '--interactive') || flag(args, '--open') || flag(args, '--zellij') || positionalArgs(args)[0] === 'session';
22
+ if (interactive) {
23
+ const readiness = await runGlmReadinessAndExit(args);
24
+ if (!readiness.ok)
25
+ return readiness;
26
+ return runGlmInteractiveLaunch(args, readiness);
27
+ }
28
+ if (!task || flag(args, '--repair') || flag(args, '--status')) {
29
+ return runGlmReadinessAndExit(args);
30
+ }
31
+ const result = await runGlmDirectSpeedRun({
32
+ cwd: process.cwd(),
33
+ task,
34
+ args,
35
+ dryRun: flag(args, '--dry-run')
36
+ });
37
+ if (flag(args, '--json'))
38
+ printJson(result);
39
+ else if (result.ok)
40
+ console.log(`GLM direct run completed: ${result.termination_reason}`);
41
+ else
42
+ console.error(`GLM direct run ${result.status}: ${result.blockers.join(', ') || result.termination_reason}`);
43
+ if (!result.ok)
44
+ process.exitCode = 1;
45
+ return result;
46
+ }
47
+ function extractGlmTask(args) {
48
+ const positional = positionalArgs(args).map(String);
49
+ if (positional[0] === 'run')
50
+ return positional.slice(1).join(' ').trim() || null;
51
+ if (positional[0] === 'session')
52
+ return null;
53
+ return positional.join(' ').trim() || null;
23
54
  }
24
55
  //# sourceMappingURL=glm-command.js.map
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.5';
8
+ export const PACKAGE_VERSION = '4.0.6';
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() {
@@ -1,7 +1,7 @@
1
1
  import { GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE } from './glm-52-settings.js';
2
2
  import { profileFromConst } from './glm-profile-resolver.js';
3
3
  export const GLM_CODEX_APP_PROFILE_ID = 'sks/glm-5.2-mad';
4
- export const GLM_CODEX_APP_PROFILE_LABEL = 'GLM 5.2 (MAD XHigh Speed / OpenRouter)';
4
+ export const GLM_CODEX_APP_PROFILE_LABEL = 'GLM 5.2 (MAD Speed / OpenRouter)';
5
5
  export function buildGlmCodexAppModelProfile() {
6
6
  const speed = profileFromConst('speed');
7
7
  return {
@@ -18,7 +18,7 @@ export function buildGlmCodexAppModelProfile() {
18
18
  defaultSettings: {
19
19
  temperature: speed.temperature,
20
20
  top_p: speed.top_p,
21
- reasoning_effort: 'xhigh',
21
+ reasoning_effort: speed.reasoning_effort || null,
22
22
  tool_choice: speed.tool_choice,
23
23
  parallel_tool_calls: speed.parallel_tool_calls,
24
24
  max_tokens: speed.max_tokens,
@@ -1,5 +1,5 @@
1
1
  import { GLM_52_DEFAULT_REQUEST_SETTINGS, GLM_52_OPENROUTER_MODEL, clampGlm52MaxTokens } from './glm-52-settings.js';
2
- import { buildDeepReasoningConfig } from './glm-reasoning-policy.js';
2
+ import { buildDeepReasoningConfig, buildFastReasoningConfig } from './glm-reasoning-policy.js';
3
3
  import { profileFromConst, resolveGlmProfileFromArgs } from './glm-profile-resolver.js';
4
4
  export function buildGlm52Request(input) {
5
5
  const profile = resolveInputProfile(input.profile, input.args, input.reasoningEffort);
@@ -8,8 +8,11 @@ export function buildGlm52Request(input) {
8
8
  }
9
9
  const strictOrDeepEffort = profile.reasoning_effort || (input.reasoningEffort === 'high' || input.reasoningEffort === 'xhigh' ? input.reasoningEffort : undefined);
10
10
  const reasoning = profile.name === 'speed'
11
- ? buildDeepReasoningConfig('xhigh')
11
+ ? buildFastReasoningConfig(input.reasoningMeta)
12
12
  : buildDeepReasoningConfig(strictOrDeepEffort || 'high');
13
+ if (profile.name === 'speed' && (reasoning.effort === 'high' || reasoning.effort === 'xhigh')) {
14
+ throw new Error(`GLM speed profile invariant violated: forbidden reasoning effort ${reasoning.effort}`);
15
+ }
13
16
  const request = {
14
17
  model: GLM_52_OPENROUTER_MODEL,
15
18
  messages: input.messages,
@@ -21,7 +21,7 @@ export const GLM_SPEED_PROFILE = {
21
21
  stream: true,
22
22
  provider: {
23
23
  allow_fallbacks: false,
24
- require_parameters: true,
24
+ require_parameters: false,
25
25
  sort: 'throughput',
26
26
  preferred_min_throughput: { p50: 80, p90: 40 },
27
27
  preferred_max_latency: { p50: 2, p90: 5 }
@@ -29,8 +29,8 @@ export const GLM_SPEED_PROFILE = {
29
29
  tool_choice: 'none',
30
30
  parallel_tool_calls: false,
31
31
  max_tokens: GLM_52_MAX_TOKENS_SPEED,
32
- reasoning_effort: 'xhigh',
33
- reasoning_default: 'xhigh-speed-optimized'
32
+ reasoning_effort: null,
33
+ reasoning_default: 'off-or-minimal-speed'
34
34
  };
35
35
  export const GLM_DEEP_PROFILE = {
36
36
  model: GLM_52_OPENROUTER_MODEL,
@@ -9,11 +9,12 @@ const SYNTHETIC_CASES = Object.freeze([
9
9
  benchCase('simple config edit', 'config_edit', 390, 930)
10
10
  ]);
11
11
  export async function runGlmBench(root, args = []) {
12
+ const live = args.includes('--live');
12
13
  const execute = args.includes('--execute');
13
- if (execute) {
14
+ if (execute && !live) {
14
15
  const blocked = {
15
16
  schema: 'sks.glm-bench-result.v1',
16
- version: '4.0.5',
17
+ version: '4.0.6',
17
18
  generated_at: nowIso(),
18
19
  status: 'blocked',
19
20
  dry_run: true,
@@ -28,11 +29,47 @@ export async function runGlmBench(root, args = []) {
28
29
  await writeJsonAtomic(path.join(root, '.sneakoscope', 'glm', 'bench-blocked.json'), blocked);
29
30
  return blocked;
30
31
  }
32
+ if (live) {
33
+ const blocked = {
34
+ schema: 'sks.glm-bench-result.v1',
35
+ version: '4.0.6',
36
+ generated_at: nowIso(),
37
+ status: 'blocked',
38
+ dry_run: false,
39
+ cases: [],
40
+ summary: {
41
+ speed_p50_total_ms: 0,
42
+ speed_p90_total_ms: 0,
43
+ speed_p50_ttft_ms: null
44
+ },
45
+ warnings: ['live_openrouter_bench_requires_explicit_network_runner_not_enabled_in_this_build']
46
+ };
47
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'glm', 'bench-live-blocked.json'), blocked);
48
+ return blocked;
49
+ }
50
+ if (execute) {
51
+ const blocked = {
52
+ schema: 'sks.glm-bench-result.v1',
53
+ version: '4.0.6',
54
+ generated_at: nowIso(),
55
+ status: 'blocked',
56
+ dry_run: true,
57
+ cases: [],
58
+ summary: {
59
+ speed_p50_total_ms: 0,
60
+ speed_p90_total_ms: 0,
61
+ speed_p50_ttft_ms: null
62
+ },
63
+ warnings: ['execute_requested_without_live_flag_uses_no_network_dry_run_policy']
64
+ };
65
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'glm', 'bench-blocked.json'), blocked);
66
+ return blocked;
67
+ }
31
68
  const speedTotals = SYNTHETIC_CASES.map((row) => row.speed.total_ms);
32
69
  const deepTotals = SYNTHETIC_CASES.map((row) => row.deep.total_ms);
33
70
  const result = {
34
71
  schema: 'sks.glm-bench-result.v1',
35
- version: '4.0.5',
72
+ version: '4.0.6',
36
73
  generated_at: nowIso(),
37
74
  status: 'dry_run',
38
75
  dry_run: true,
@@ -0,0 +1,140 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { nowIso, writeJsonAtomic } from '../../fsx.js';
4
+ import { resolveOpenRouterApiKey } from '../openrouter/openrouter-secret-store.js';
5
+ import { sendOpenRouterChatCompletionStream } from '../openrouter/openrouter-stream.js';
6
+ import { assertGlm52ActualModel } from './glm-52-response-guard.js';
7
+ import { GLM_52_OPENROUTER_MODEL } from './glm-52-settings.js';
8
+ import { buildGlm52Request } from './glm-52-request.js';
9
+ import { buildGlmSpeedContext } from './glm-speed-context.js';
10
+ import { parseGlmSpeedOutput } from './glm-speed-output-parser.js';
11
+ import { evaluateGlmSpeedGate } from './glm-speed-gate.js';
12
+ import { checkAndApplyGlmPatch } from './glm-patch-apply.js';
13
+ import { createGlmRunController, writeGlmRunArtifacts } from './glm-run-controller.js';
14
+ import { GLM_SPEED_LIMITS } from './glm-run-timeout.js';
15
+ import { recordGlmLoopIteration } from './glm-loop-guard.js';
16
+ export async function runGlmDirectSpeedRun(input) {
17
+ if (process.env.SKS_GLM_WRAPPER_ACTIVE === '1') {
18
+ return blocked('glm-recursive-blocked', input.task, 'glm_recursive_launch_blocked', ['glm_recursive_launch_blocked']);
19
+ }
20
+ const controller = createGlmRunController({ limits: GLM_SPEED_LIMITS });
21
+ controller.transition('preflight');
22
+ const key = await resolveOpenRouterApiKey({ env: process.env });
23
+ if (!key.key) {
24
+ const termination = controller.terminate('blocked', 'glm_patch_gate_failed', key.blockers, [
25
+ 'set_OPENROUTER_API_KEY_or_run_sks_--mad_--glm_--repair'
26
+ ]);
27
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination });
28
+ return result('blocked', controller.state().run_id, input.task, termination.reason, artifactDir, [], key.blockers, termination.warnings);
29
+ }
30
+ controller.transition('context');
31
+ const gitStatus = await readGitStatus(input.cwd);
32
+ const context = await buildGlmSpeedContext({
33
+ cwd: input.cwd,
34
+ task: input.task,
35
+ mentionedPaths: extractMentionedPaths(input.task),
36
+ readFile: async (file) => fs.readFile(file, 'utf8').catch(() => null),
37
+ ...(gitStatus ? { gitStatus } : {})
38
+ });
39
+ controller.transition('request');
40
+ const request = buildGlm52Request({
41
+ profile: 'speed',
42
+ messages: [
43
+ { role: 'system', content: 'Return only <sks_patch>, <sks_need_context>, or <sks_blocked>. Use unified diff for patches.' },
44
+ { role: 'user', content: JSON.stringify({ task: input.task, context }) }
45
+ ],
46
+ maxTokens: 4096
47
+ });
48
+ const response = await sendOpenRouterChatCompletionStream({
49
+ apiKey: key.key,
50
+ request: {
51
+ ...request,
52
+ session_id: `sks-${controller.state().run_id}`
53
+ },
54
+ timeoutMs: GLM_SPEED_LIMITS.request_timeout_ms
55
+ });
56
+ if (!response.ok) {
57
+ const reason = response.error.code === 'glm_request_timeout' ? 'glm_request_timeout' : 'glm_patch_gate_failed';
58
+ const termination = controller.terminate(reason === 'glm_request_timeout' ? 'timeout' : 'failed', reason, [response.error.code]);
59
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, contextOmissions: context.omitted });
60
+ return result(reason === 'glm_request_timeout' ? 'timeout' : 'failed', controller.state().run_id, input.task, termination.reason, artifactDir, [], [response.error.code], []);
61
+ }
62
+ controller.transition('model_guard');
63
+ const modelGuard = assertGlm52ActualModel(response.value.model || GLM_52_OPENROUTER_MODEL);
64
+ if (!modelGuard.ok) {
65
+ const termination = controller.terminate('blocked', 'glm_model_mismatch', [modelGuard.code]);
66
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, contextOmissions: context.omitted });
67
+ return result('blocked', controller.state().run_id, input.task, termination.reason, artifactDir, [], [modelGuard.code], []);
68
+ }
69
+ controller.transition('parse_output');
70
+ const parsed = parseGlmSpeedOutput(response.value.content);
71
+ const guard = recordGlmLoopIteration({
72
+ state: controller.state(),
73
+ limits: GLM_SPEED_LIMITS,
74
+ output: response.value.content,
75
+ madeProgress: parsed.kind === 'patch',
76
+ nowIso: nowIso()
77
+ });
78
+ if (!guard.ok) {
79
+ const termination = controller.terminate('blocked', guard.reason === 'glm_loop_repeated_output' ? 'glm_loop_repeated_output' : 'glm_loop_no_progress', [guard.reason || 'glm_loop_blocked']);
80
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, loopGuard: guard, contextOmissions: context.omitted });
81
+ return result('blocked', controller.state().run_id, input.task, termination.reason, artifactDir, [], termination.blockers, []);
82
+ }
83
+ if (parsed.kind === 'blocked' || parsed.kind === 'need_context' || parsed.kind === 'malformed') {
84
+ const termination = controller.terminate('blocked', parsed.kind === 'malformed' ? 'glm_loop_no_progress' : 'completed_noop', [parsed.reason || parsed.kind]);
85
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, loopGuard: guard, contextOmissions: context.omitted });
86
+ return result('blocked', controller.state().run_id, input.task, termination.reason, artifactDir, [], termination.blockers, []);
87
+ }
88
+ controller.transition('patch_gate');
89
+ const gate = evaluateGlmSpeedGate(response.value.content);
90
+ if (!gate.ok) {
91
+ const termination = controller.terminate('blocked', 'glm_patch_gate_failed', gate.checks.filter((row) => !row.ok).map((row) => row.reason || row.id));
92
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, loopGuard: guard, contextOmissions: context.omitted });
93
+ return result('blocked', controller.state().run_id, input.task, termination.reason, artifactDir, [], termination.blockers, []);
94
+ }
95
+ controller.transition('apply_patch');
96
+ const applied = await checkAndApplyGlmPatch({ cwd: input.cwd, patch: parsed.content, apply: !input.dryRun });
97
+ if (!applied.ok) {
98
+ const termination = controller.terminate('blocked', 'glm_patch_gate_failed', [applied.error.code]);
99
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, loopGuard: guard, contextOmissions: context.omitted });
100
+ return result('blocked', controller.state().run_id, input.task, termination.reason, artifactDir, [], termination.blockers, []);
101
+ }
102
+ controller.transition('verify');
103
+ const termination = controller.terminate('completed', input.dryRun ? 'completed_noop' : 'completed_patch_applied');
104
+ const artifactDir = await writeGlmRunArtifacts({ cwd: input.cwd, state: controller.state(), termination, loopGuard: guard, contextOmissions: context.omitted });
105
+ await writeJsonAtomic(path.join(artifactDir, 'direct-run.json'), { request_model: request.model, stream: true, gate, applied: applied.value });
106
+ return result('completed', controller.state().run_id, input.task, termination.reason, artifactDir, applied.value.touchedPaths, [], []);
107
+ }
108
+ function result(status, runId, task, terminationReason, artifactDir, touchedPaths, blockers, warnings) {
109
+ return {
110
+ schema: 'sks.glm-direct-run-result.v1',
111
+ ok: status === 'completed',
112
+ status,
113
+ run_id: runId,
114
+ task,
115
+ model: GLM_52_OPENROUTER_MODEL,
116
+ gpt_fallback_allowed: false,
117
+ termination_reason: terminationReason,
118
+ ...(artifactDir ? { artifact_dir: artifactDir } : {}),
119
+ touched_paths: touchedPaths,
120
+ blockers,
121
+ warnings
122
+ };
123
+ }
124
+ function blocked(runId, task, reason, blockers) {
125
+ return result('blocked', runId, task, reason, undefined, [], blockers, []);
126
+ }
127
+ async function readGitStatus(cwd) {
128
+ const { spawn } = await import('node:child_process');
129
+ return new Promise((resolve) => {
130
+ const child = spawn('git', ['status', '--short'], { cwd, stdio: ['ignore', 'pipe', 'ignore'] });
131
+ let stdout = '';
132
+ child.stdout.on('data', (chunk) => { stdout += String(chunk); });
133
+ child.on('close', () => resolve(stdout.trim() || undefined));
134
+ });
135
+ }
136
+ function extractMentionedPaths(task) {
137
+ const matches = task.match(/(?:^|\s|[`"'])([A-Za-z0-9_.-]+\/[A-Za-z0-9_./-]+\.[A-Za-z0-9]+)(?:\s|[`"']|$)/g) || [];
138
+ return [...new Set(matches.map((value) => value.trim().replace(/^[`"']|[`"']$/g, '')))];
139
+ }
140
+ //# sourceMappingURL=glm-direct-run.js.map
@@ -0,0 +1,5 @@
1
+ import { madHighCommand } from '../../commands/mad-sks-command.js';
2
+ export async function runGlmInteractiveLaunch(args, readiness) {
3
+ return madHighCommand(['--glm', '--no-swarm', ...args], { glmReadiness: readiness, glmArgs: args });
4
+ }
5
+ //# sourceMappingURL=glm-interactive-launch.js.map
@@ -3,7 +3,7 @@ import { nowIso, writeJsonAtomic } from '../../fsx.js';
3
3
  export function createEmptyGlmLatencyTrace(mode) {
4
4
  return {
5
5
  schema: 'sks.glm-latency-trace.v1',
6
- version: '4.0.5',
6
+ version: '4.0.6',
7
7
  mode,
8
8
  total_ms: 0,
9
9
  preflight_ms: 0,
@@ -0,0 +1,31 @@
1
+ import crypto from 'node:crypto';
2
+ import { isGlmTerminalPhase } from './glm-run-state.js';
3
+ export function recordGlmLoopIteration(input) {
4
+ if (isGlmTerminalPhase(input.state.phase) || input.state.terminal) {
5
+ return { ok: false, reason: 'terminal_state', state: input.state };
6
+ }
7
+ const outputDigest = input.output ? digestNormalizedOutput(input.output) : input.state.last_output_digest;
8
+ const repeated = Boolean(outputDigest && outputDigest === input.state.last_output_digest);
9
+ const nextState = {
10
+ ...input.state,
11
+ updated_at: input.nowIso,
12
+ turn_count: input.state.turn_count + 1,
13
+ no_progress_count: input.madeProgress ? 0 : input.state.no_progress_count + 1,
14
+ repeated_output_count: repeated ? input.state.repeated_output_count + 1 : 0,
15
+ ...(outputDigest ? { last_output_digest: outputDigest } : {})
16
+ };
17
+ if (nextState.turn_count > input.limits.max_turns) {
18
+ return { ok: false, reason: 'glm_loop_max_turns', state: nextState };
19
+ }
20
+ if (nextState.repeated_output_count >= input.limits.max_repeated_output) {
21
+ return { ok: false, reason: 'glm_loop_repeated_output', state: nextState };
22
+ }
23
+ if (nextState.no_progress_count > input.limits.max_no_progress_iterations) {
24
+ return { ok: false, reason: 'glm_loop_no_progress', state: nextState };
25
+ }
26
+ return { ok: true, state: nextState };
27
+ }
28
+ export function digestNormalizedOutput(output) {
29
+ return crypto.createHash('sha256').update(output.replace(/\s+/g, ' ').trim()).digest('hex');
30
+ }
31
+ //# sourceMappingURL=glm-loop-guard.js.map
@@ -123,6 +123,6 @@ function codexReasoningEffortForProfile(profile) {
123
123
  return 'xhigh';
124
124
  if (profile.name === 'deep' || profile.name === 'strict')
125
125
  return 'high';
126
- return 'xhigh';
126
+ return 'low';
127
127
  }
128
128
  //# sourceMappingURL=glm-mad-launch.js.map
@@ -204,7 +204,7 @@ async function writeGlmModeArtifacts(cwd, result, profile, selectedProfile, gene
204
204
  profile: selectedProfile.name,
205
205
  temperature: selectedProfile.temperature,
206
206
  top_p: selectedProfile.top_p,
207
- reasoning_effort: selectedProfile.reasoning_effort || 'xhigh',
207
+ reasoning_effort: selectedProfile.reasoning_effort || null,
208
208
  max_tokens: selectedProfile.max_tokens,
209
209
  tool_choice: selectedProfile.tool_choice,
210
210
  parallel_tool_calls: selectedProfile.parallel_tool_calls,
@@ -0,0 +1,58 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { parseUnifiedDiffPatch } from './glm-patch-parser.js';
3
+ const PROTECTED_PATH = /(^|\/)(\.github|dist|node_modules)(\/|$)/;
4
+ export async function checkAndApplyGlmPatch(input) {
5
+ const parsed = parseUnifiedDiffPatch(input.patch);
6
+ if (parsed.empty) {
7
+ return issue('glm_patch_empty', 'GLM output did not contain a non-empty unified diff.');
8
+ }
9
+ const blockedPath = parsed.touchedPaths.find((file) => PROTECTED_PATH.test(file));
10
+ if (blockedPath) {
11
+ return issue('glm_patch_protected_path', `GLM patch touched protected path: ${blockedPath}`);
12
+ }
13
+ const check = await runGitApply(input.cwd, input.patch, ['apply', '--check', '--whitespace=nowarn', '-']);
14
+ if (check.code !== 0) {
15
+ return issue('glm_patch_gate_failed', check.stderr || check.stdout || 'git apply --check failed.');
16
+ }
17
+ if (!input.apply) {
18
+ return {
19
+ ok: true,
20
+ value: {
21
+ checked: true,
22
+ applied: false,
23
+ touchedPaths: parsed.touchedPaths,
24
+ stdout: check.stdout,
25
+ stderr: check.stderr
26
+ }
27
+ };
28
+ }
29
+ const applied = await runGitApply(input.cwd, input.patch, ['apply', '--whitespace=nowarn', '-']);
30
+ if (applied.code !== 0) {
31
+ return issue('glm_patch_apply_failed', applied.stderr || applied.stdout || 'git apply failed.');
32
+ }
33
+ return {
34
+ ok: true,
35
+ value: {
36
+ checked: true,
37
+ applied: true,
38
+ touchedPaths: parsed.touchedPaths,
39
+ stdout: applied.stdout,
40
+ stderr: applied.stderr
41
+ }
42
+ };
43
+ }
44
+ function issue(code, message) {
45
+ return { ok: false, error: { code, message, severity: 'blocked' } };
46
+ }
47
+ function runGitApply(cwd, patch, args) {
48
+ return new Promise((resolve) => {
49
+ const child = spawn('git', [...args], { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
50
+ let stdout = '';
51
+ let stderr = '';
52
+ child.stdout.on('data', (chunk) => { stdout += String(chunk); });
53
+ child.stderr.on('data', (chunk) => { stderr += String(chunk); });
54
+ child.on('close', (code) => resolve({ code, stdout, stderr }));
55
+ child.stdin.end(patch);
56
+ });
57
+ }
58
+ //# sourceMappingURL=glm-patch-apply.js.map
@@ -0,0 +1,19 @@
1
+ export function parseUnifiedDiffPatch(patch) {
2
+ const touched = new Set();
3
+ for (const line of patch.split(/\r?\n/)) {
4
+ const diff = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
5
+ if (diff?.[1])
6
+ touched.add(diff[1]);
7
+ if (diff?.[2])
8
+ touched.add(diff[2]);
9
+ const file = line.match(/^(?:---|\+\+\+) [ab]\/(.+)$/);
10
+ if (file?.[1] && file[1] !== '/dev/null')
11
+ touched.add(file[1]);
12
+ }
13
+ return {
14
+ patch,
15
+ touchedPaths: [...touched],
16
+ empty: !patch.trim() || !/^diff --git /m.test(patch)
17
+ };
18
+ }
19
+ //# sourceMappingURL=glm-patch-parser.js.map
@@ -0,0 +1,5 @@
1
+ import { runMadGlmMode } from './glm-mad-mode.js';
2
+ export async function runGlmReadinessAndExit(args = []) {
3
+ return runMadGlmMode(args);
4
+ }
5
+ //# sourceMappingURL=glm-readiness.js.map
@@ -6,18 +6,31 @@ export function createGlmEncodedRequestCache(maxEntries = 128) {
6
6
  export function encodeGlmRequestWithCache(request, cache = defaultEncodedRequestCache) {
7
7
  const key = digestRequestForCache(request);
8
8
  const hit = cache.get(key);
9
- if (hit)
10
- return { entry: hit, cacheHit: true };
11
9
  const body = JSON.stringify(request);
10
+ if (hit)
11
+ return { body: hit.bodyStored ? hit.body : body, entry: hit, cacheHit: true };
12
+ if (containsSecretLikeContent(body)) {
13
+ const entry = {
14
+ key,
15
+ body: '',
16
+ bodySha256: crypto.createHash('sha256').update(body).digest('hex'),
17
+ byteLength: Buffer.byteLength(body),
18
+ createdAt: Date.now(),
19
+ bodyStored: false,
20
+ skippedReason: 'secret_like_request_body_not_cached'
21
+ };
22
+ return { body, entry, cacheHit: false };
23
+ }
12
24
  const entry = {
13
25
  key,
26
+ body,
14
27
  bodySha256: crypto.createHash('sha256').update(body).digest('hex'),
15
28
  byteLength: Buffer.byteLength(body),
16
29
  createdAt: Date.now(),
17
- bodyStored: false
30
+ bodyStored: true
18
31
  };
19
32
  cache.set(key, entry);
20
- return { entry, cacheHit: false };
33
+ return { body, entry, cacheHit: false };
21
34
  }
22
35
  export function digestRequestForCache(request) {
23
36
  const safe = {
@@ -31,10 +44,14 @@ export function digestRequestForCache(request) {
31
44
  top_p: request.top_p || null,
32
45
  tool_choice: request.tool_choice || null,
33
46
  parallel_tool_calls: request.parallel_tool_calls || null,
34
- reasoning: request.reasoning || null
47
+ reasoning: request.reasoning || null,
48
+ session_id: request.session_id || null
35
49
  };
36
50
  return crypto.createHash('sha256').update(stableStringify(safe)).digest('hex');
37
51
  }
52
+ function containsSecretLikeContent(body) {
53
+ return /\b(?:Bearer\s+[A-Za-z0-9._~+/-]+|sk-(?:or-)?[A-Za-z0-9_-]{12,}|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)\b/.test(body);
54
+ }
38
55
  function stableStringify(value) {
39
56
  if (!value || typeof value !== 'object')
40
57
  return JSON.stringify(value);
@@ -0,0 +1,66 @@
1
+ import path from 'node:path';
2
+ import { nowIso, randomId, writeJsonAtomic } from '../../fsx.js';
3
+ import { GLM_SPEED_LIMITS } from './glm-run-timeout.js';
4
+ import { isGlmTerminalPhase } from './glm-run-state.js';
5
+ export function createGlmRunController(input = {}) {
6
+ const clock = input.now || nowIso;
7
+ const startedAt = clock();
8
+ const startedMs = Date.now();
9
+ let state = {
10
+ run_id: input.runId || `glm-${startedAt.replace(/[:.]/g, '-')}-${randomId(6)}`,
11
+ ...(input.missionId ? { mission_id: input.missionId } : {}),
12
+ phase: 'idle',
13
+ started_at: startedAt,
14
+ updated_at: startedAt,
15
+ turn_count: 0,
16
+ tool_round_count: 0,
17
+ no_progress_count: 0,
18
+ repeated_output_count: 0,
19
+ terminal: false
20
+ };
21
+ const limits = input.limits || GLM_SPEED_LIMITS;
22
+ return {
23
+ state: () => state,
24
+ transition: (phase, reason) => {
25
+ if (state.terminal || isGlmTerminalPhase(state.phase))
26
+ return state;
27
+ state = {
28
+ ...state,
29
+ phase,
30
+ updated_at: clock(),
31
+ terminal: isGlmTerminalPhase(phase),
32
+ ...(reason ? { terminal_reason: reason } : {})
33
+ };
34
+ return state;
35
+ },
36
+ terminate: (phase, reason, blockers = [], warnings = []) => {
37
+ state = {
38
+ ...state,
39
+ phase,
40
+ updated_at: clock(),
41
+ terminal: true,
42
+ terminal_reason: reason
43
+ };
44
+ return {
45
+ schema: 'sks.glm-run-termination.v1',
46
+ run_id: state.run_id,
47
+ terminal: true,
48
+ phase,
49
+ reason,
50
+ turn_count: state.turn_count,
51
+ wall_clock_ms: Math.min(Date.now() - startedMs, limits.max_wall_clock_ms),
52
+ blockers,
53
+ warnings
54
+ };
55
+ }
56
+ };
57
+ }
58
+ export async function writeGlmRunArtifacts(input) {
59
+ const dir = path.join(input.cwd, '.sneakoscope', 'glm', 'runs', input.state.run_id);
60
+ await writeJsonAtomic(path.join(dir, 'run-state.json'), input.state);
61
+ await writeJsonAtomic(path.join(dir, 'termination.json'), input.termination);
62
+ await writeJsonAtomic(path.join(dir, 'loop-guard.json'), input.loopGuard || { schema: 'sks.glm-loop-guard.v1', ok: true });
63
+ await writeJsonAtomic(path.join(dir, 'context-omissions.json'), input.contextOmissions || { omitted: [] });
64
+ return dir;
65
+ }
66
+ //# sourceMappingURL=glm-run-controller.js.map
@@ -0,0 +1,11 @@
1
+ export const GLM_TERMINAL_PHASES = new Set([
2
+ 'completed',
3
+ 'blocked',
4
+ 'failed',
5
+ 'cancelled',
6
+ 'timeout'
7
+ ]);
8
+ export function isGlmTerminalPhase(phase) {
9
+ return GLM_TERMINAL_PHASES.has(phase);
10
+ }
11
+ //# sourceMappingURL=glm-run-state.js.map
@@ -0,0 +1,31 @@
1
+ export const GLM_SPEED_LIMITS = {
2
+ max_turns: 2,
3
+ max_tool_rounds: 0,
4
+ max_wall_clock_ms: 90_000,
5
+ request_timeout_ms: 45_000,
6
+ idle_timeout_ms: 15_000,
7
+ max_no_progress_iterations: 1,
8
+ max_repeated_output: 1,
9
+ max_patch_retries: 1,
10
+ max_context_requests: 1
11
+ };
12
+ export const GLM_DEEP_LIMITS = {
13
+ max_turns: 4,
14
+ max_tool_rounds: 4,
15
+ max_wall_clock_ms: 240_000,
16
+ request_timeout_ms: 120_000,
17
+ idle_timeout_ms: 30_000,
18
+ max_no_progress_iterations: 2,
19
+ max_repeated_output: 2,
20
+ max_patch_retries: 2,
21
+ max_context_requests: 2
22
+ };
23
+ export function createRequestAbortController(timeoutMs) {
24
+ const controller = new AbortController();
25
+ const timeout = setTimeout(() => controller.abort(), Math.max(1, timeoutMs));
26
+ return {
27
+ controller,
28
+ clear: () => clearTimeout(timeout)
29
+ };
30
+ }
31
+ //# sourceMappingURL=glm-run-timeout.js.map
@@ -1,24 +1,44 @@
1
1
  import { OPENROUTER_CHAT_COMPLETIONS_URL } from './openrouter-types.js';
2
2
  import { invalidOpenRouterResponseIssue, normalizeOpenRouterError } from './openrouter-error.js';
3
3
  import { redactOpenRouterString } from '../../security/redact-secrets.js';
4
+ import { encodeGlmRequestWithCache } from '../glm/glm-request-cache.js';
4
5
  export async function sendOpenRouterChatCompletion(input) {
5
6
  try {
6
7
  const doFetch = input.fetchImpl || fetch;
8
+ const encoded = encodeGlmRequestWithCache(input.request);
9
+ const controller = input.timeoutMs ? new AbortController() : null;
10
+ const timeout = controller
11
+ ? setTimeout(() => controller.abort(), Math.max(1, input.timeoutMs || 0))
12
+ : null;
13
+ const signal = input.signal || controller?.signal;
7
14
  const response = await doFetch(input.endpoint || OPENROUTER_CHAT_COMPLETIONS_URL, {
8
15
  method: 'POST',
16
+ ...(signal ? { signal } : {}),
9
17
  headers: {
10
18
  Authorization: `Bearer ${input.apiKey}`,
11
19
  'Content-Type': 'application/json',
12
20
  'X-OpenRouter-Title': 'Sneakoscope-Codex'
13
21
  },
14
- body: JSON.stringify(input.request)
22
+ body: encoded.body
15
23
  });
24
+ if (timeout)
25
+ clearTimeout(timeout);
16
26
  const text = await response.text();
17
27
  if (!response.ok)
18
28
  return { ok: false, error: normalizeOpenRouterError(response.status, text) };
19
29
  return parseOpenRouterResponse(text);
20
30
  }
21
31
  catch (err) {
32
+ if (err instanceof Error && err.name === 'AbortError') {
33
+ return {
34
+ ok: false,
35
+ error: {
36
+ code: 'glm_request_timeout',
37
+ message: `OpenRouter request aborted after ${input.timeoutMs || 'external'}ms.`,
38
+ severity: 'failed'
39
+ }
40
+ };
41
+ }
22
42
  return {
23
43
  ok: false,
24
44
  error: {
@@ -0,0 +1,94 @@
1
+ import { redactOpenRouterString } from '../../security/redact-secrets.js';
2
+ import { OPENROUTER_CHAT_COMPLETIONS_URL } from './openrouter-types.js';
3
+ import { normalizeOpenRouterError } from './openrouter-error.js';
4
+ import { encodeGlmRequestWithCache } from '../glm/glm-request-cache.js';
5
+ export async function sendOpenRouterChatCompletionStream(input) {
6
+ const started = Date.now();
7
+ const controller = input.timeoutMs ? new AbortController() : null;
8
+ const timeout = controller ? setTimeout(() => controller.abort(), Math.max(1, input.timeoutMs || 0)) : null;
9
+ try {
10
+ const request = { ...input.request, stream: true };
11
+ const encoded = encodeGlmRequestWithCache(request);
12
+ const signal = input.signal || controller?.signal;
13
+ const response = await (input.fetchImpl || fetch)(OPENROUTER_CHAT_COMPLETIONS_URL, {
14
+ method: 'POST',
15
+ ...(signal ? { signal } : {}),
16
+ headers: {
17
+ Authorization: `Bearer ${input.apiKey}`,
18
+ 'Content-Type': 'application/json',
19
+ 'X-OpenRouter-Title': 'Sneakoscope-Codex'
20
+ },
21
+ body: encoded.body
22
+ });
23
+ if (timeout)
24
+ clearTimeout(timeout);
25
+ const text = await response.text();
26
+ if (!response.ok)
27
+ return { ok: false, error: normalizeOpenRouterError(response.status, text) };
28
+ return { ok: true, value: parseOpenRouterStreamText(text, started) };
29
+ }
30
+ catch (err) {
31
+ if (timeout)
32
+ clearTimeout(timeout);
33
+ if (err instanceof Error && err.name === 'AbortError') {
34
+ return {
35
+ ok: false,
36
+ error: {
37
+ code: 'glm_request_timeout',
38
+ message: `OpenRouter stream aborted after ${input.timeoutMs || 'external'}ms.`,
39
+ severity: 'failed'
40
+ }
41
+ };
42
+ }
43
+ return {
44
+ ok: false,
45
+ error: {
46
+ code: 'glm_openrouter_stream_failed',
47
+ message: redactOpenRouterString(err instanceof Error ? err.message : String(err)),
48
+ severity: 'failed'
49
+ }
50
+ };
51
+ }
52
+ }
53
+ export function parseOpenRouterStreamText(text, startedAtMs = Date.now()) {
54
+ const events = [];
55
+ let content = '';
56
+ let model;
57
+ let usage;
58
+ let ttft = null;
59
+ for (const line of text.split(/\r?\n/)) {
60
+ if (!line.startsWith('data:'))
61
+ continue;
62
+ const data = line.slice('data:'.length).trim();
63
+ if (!data || data === '[DONE]')
64
+ continue;
65
+ try {
66
+ const raw = JSON.parse(data);
67
+ const delta = raw?.choices?.[0]?.delta?.content;
68
+ if (typeof raw?.model === 'string')
69
+ model = raw.model;
70
+ if (raw?.usage)
71
+ usage = raw.usage;
72
+ if (typeof delta === 'string' && delta) {
73
+ if (ttft === null)
74
+ ttft = Math.max(0, Date.now() - startedAtMs);
75
+ content += delta;
76
+ events.push({ type: 'chunk', content_delta: delta, ...(model ? { model } : {}), raw });
77
+ }
78
+ }
79
+ catch {
80
+ events.push({ type: 'error', raw: data });
81
+ }
82
+ }
83
+ events.push({ type: 'done', ...(model ? { model } : {}), ...(usage ? { usage } : {}) });
84
+ return {
85
+ content,
86
+ ...(model ? { model } : {}),
87
+ ...(usage ? { usage } : {}),
88
+ ttft_ms: ttft,
89
+ total_ms: Math.max(0, Date.now() - startedAtMs),
90
+ chunk_count: events.filter((event) => event.type === 'chunk').length,
91
+ events
92
+ };
93
+ }
94
+ //# sourceMappingURL=openrouter-stream.js.map
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '4.0.5';
1
+ export const PACKAGE_VERSION = '4.0.6';
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.5",
4
+ "version": "4.0.7",
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",