sneakoscope 10.3.5 → 10.3.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
@@ -16,7 +16,7 @@
16
16
  Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
17
17
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
18
18
 
19
- Current package: **SKS 10.3.5**. Install the latest stable release from npm.
19
+ Current package: **SKS 10.3.7**. Install the latest stable release from npm.
20
20
 
21
21
  [Quick start](#install-in-one-command) · [Commands](#everyday-commands) · [SKS Center](#sks-center-macos) · [Documentation](#documentation) · [Changelog](CHANGELOG.md)
22
22
 
@@ -195,7 +195,7 @@ also picks the pipeline for each prompt; an explicit SKS `<div align="center">
195
195
  Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
196
196
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
197
197
 
198
- Current package: **SKS 10.3.5**. Install the latest stable release from npm.
198
+ Current package: **SKS 10.3.7**. Install the latest stable release from npm.
199
199
 
200
200
  [Quick start](#install-in-one-command) · [Commands](#everyday-commands) · [SKS Center](#sks-center-macos) · [Documentation](#documentation) · [Changelog](CHANGELOG.md)
201
201
 
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "10.3.5"
262
+ version = "10.3.7"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "10.3.5"
3
+ version = "10.3.7"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "10.3.5",
3
+ "package_version": "10.3.7",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -302,6 +302,7 @@ function emptySkillLegacySurface(fix) {
302
302
  removed_other_harness_skill_count: 0,
303
303
  preserved_other_harness_skill_count: 0,
304
304
  preserved_user_skill_count: 0,
305
+ quarantined_sks_residue_count: 0,
305
306
  remaining_count: 0,
306
307
  preserved_clean_count: 0,
307
308
  error_count: 0,
@@ -309,6 +310,7 @@ function emptySkillLegacySurface(fix) {
309
310
  removed_other_harness_skills: [],
310
311
  preserved_other_harness_skills: [],
311
312
  preserved_user_skills: [],
313
+ quarantined_sks_residue: [],
312
314
  remaining: [],
313
315
  errors: [],
314
316
  cleanup_prompt_command: 'sks conflicts cleanup --yes'
@@ -1,13 +1,14 @@
1
1
  import fsp from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
- import { LEGACY_DOLLAR_SKILL_NAMES } from '../routes.js';
4
+ import { LEGACY_DOLLAR_SKILL_NAMES, routeByDollarCommand } from '../routes.js';
5
5
  import { legacyCoreSkillNames } from '../codex-native/core-skill-manifest.js';
6
6
  import { prefixKnownSksDollarReferences } from '../routes/dollar-prefix.js';
7
7
  import { inspectConfinedPath } from '../managed-path-safety.js';
8
8
  import { messageOf as errorMessage } from '../errors/message.js';
9
9
  import { collectNestedProjectRoots } from './current-project-guidance-nested.js';
10
10
  import { containsRetiredPublicSurface } from './current-project-guidance.js';
11
+ import { quarantineUserPath } from './retired-managed-residue-private.js';
11
12
  export const SKILL_LEGACY_SURFACE_SCHEMA = 'sks.skill-legacy-surface.v1';
12
13
  export const OTHER_HARNESS_SKILL_DIR_NAMES = ['omx', 'dcodex', 'omx-codex', 'dcodex-codex'];
13
14
  const TOKEN = 'A-Za-z0-9_.\\-';
@@ -111,6 +112,32 @@ const PREFIX_LEGACY_NAMES = Array.from(new Set([
111
112
  'prompt-pipeline',
112
113
  'pipeline-runner'
113
114
  ]));
115
+ async function isSksRouteSkillResidue(dir, name, text) {
116
+ if (!routeByDollarCommand(name) && !PREFIX_LEGACY_NAMES.includes(name))
117
+ return false;
118
+ const frontmatterName = /^---\n[\s\S]*?^name:\s*(.+?)\s*$[\s\S]*?^---/m.exec(text)?.[1];
119
+ if (frontmatterName !== name)
120
+ return false;
121
+ const files = [];
122
+ const walk = async (current, relative) => {
123
+ for (const entry of await fsp.readdir(current, { withFileTypes: true })) {
124
+ const rel = relative ? `${relative}/${entry.name}` : entry.name;
125
+ if (entry.isSymbolicLink())
126
+ return false;
127
+ if (entry.isDirectory()) {
128
+ if (!await walk(path.join(current, entry.name), rel))
129
+ return false;
130
+ }
131
+ else
132
+ files.push(rel);
133
+ }
134
+ return true;
135
+ };
136
+ if (!await walk(dir, ''))
137
+ return false;
138
+ return files.every((file) => SKS_SKILL_LAYOUT_FILES.has(file));
139
+ }
140
+ const SKS_SKILL_LAYOUT_FILES = new Set(['SKILL.md', 'agents/openai.yaml']);
114
141
  export function rewriteSkillLegacySurface(input) {
115
142
  let text = String(input || '');
116
143
  const hits = [];
@@ -173,6 +200,7 @@ export async function reconcileSkillLegacySurface(opts) {
173
200
  const removedOtherHarness = [];
174
201
  const preservedOtherHarness = [];
175
202
  const preservedUserSkills = [];
203
+ const quarantinedResidue = [];
176
204
  const remaining = [];
177
205
  const errors = [];
178
206
  let scanned = 0;
@@ -262,6 +290,21 @@ export async function reconcileSkillLegacySurface(opts) {
262
290
  remaining.push(display);
263
291
  continue;
264
292
  }
293
+ if (await isSksRouteSkillResidue(dir, row.name, before).catch(() => false)) {
294
+ if (!fix) {
295
+ remaining.push(display);
296
+ continue;
297
+ }
298
+ try {
299
+ await quarantineUserPath(ownerRoot, dir, path.join(ownerRoot, '.sneakoscope', 'quarantine', 'skill-legacy-surface'));
300
+ quarantinedResidue.push(display);
301
+ }
302
+ catch (error) {
303
+ errors.push(`${display}:${errorMessage(error)}`);
304
+ remaining.push(display);
305
+ }
306
+ continue;
307
+ }
265
308
  preservedUserSkills.push(display);
266
309
  remaining.push(display);
267
310
  }
@@ -275,6 +318,7 @@ export async function reconcileSkillLegacySurface(opts) {
275
318
  removed_other_harness_skill_count: removedOtherHarness.length,
276
319
  preserved_other_harness_skill_count: preservedOtherHarness.length,
277
320
  preserved_user_skill_count: preservedUserSkills.length,
321
+ quarantined_sks_residue_count: quarantinedResidue.length,
278
322
  remaining_count: remaining.length,
279
323
  preserved_clean_count: preservedClean,
280
324
  error_count: errors.length,
@@ -282,6 +326,7 @@ export async function reconcileSkillLegacySurface(opts) {
282
326
  removed_other_harness_skills: Array.from(new Set(removedOtherHarness)).sort(),
283
327
  preserved_other_harness_skills: Array.from(new Set(preservedOtherHarness)).sort(),
284
328
  preserved_user_skills: Array.from(new Set(preservedUserSkills)).sort(),
329
+ quarantined_sks_residue: Array.from(new Set(quarantinedResidue)).sort(),
285
330
  remaining: Array.from(new Set(remaining)).sort(),
286
331
  errors: Array.from(new Set(errors)).sort(),
287
332
  cleanup_prompt_command: 'sks conflicts cleanup --yes'
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import fsp from 'node:fs/promises';
3
3
  import os from 'node:os';
4
- import { exists, nowIso, PACKAGE_VERSION, readJson, readText, sha256, withScratchDir, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
+ import { exists, nowIso, PACKAGE_VERSION, packageRoot, readJson, readText, sha256, which, withScratchDir, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
5
5
  import { buildSksCoreSkillManifest, isCoreSkillName, legacyCoreSkillNames } from '../codex-native/core-skill-manifest.js';
6
6
  import { syncCoreSkillsIntegrity } from '../codex-native/core-skill-integrity.js';
7
7
  import { managedSkillGenerationLockPath, withManagedSkillGenerationLock } from '../codex-native/managed-skill-generation-lock.js';
@@ -682,11 +682,14 @@ async function reconcileSkillsUnlocked(opts) {
682
682
  if (installedMarker?.generated_by === 'sneakoscope') {
683
683
  const installedVersion = String(installedMarker?.version || '').trim();
684
684
  const versionOrder = compareSemVer(installedVersion, PACKAGE_VERSION);
685
- if (versionOrder === 1) {
685
+ const activeNewer = versionOrder === 1 ? await activeNewerSksOnPath(PACKAGE_VERSION) : null;
686
+ if (activeNewer) {
686
687
  report.ok = false;
687
- report.warnings.push(`managed_skill_generation_downgrade_refused:${installedVersion}:runtime_${PACKAGE_VERSION}`);
688
+ report.warnings.push(`managed_skill_generation_downgrade_refused:${installedVersion}:runtime_${PACKAGE_VERSION}:path_${activeNewer}`);
688
689
  return report;
689
690
  }
691
+ if (versionOrder === 1)
692
+ report.warnings.push(`managed_skill_generation_reclaimed_from_${installedVersion}`);
690
693
  const installedBuildTime = Number(installedMarker?.runtime_build_source_time);
691
694
  const currentBuildTime = await runtimeBuildSourceTime();
692
695
  const installedGeneration = String(installedMarker?.skill_generation_sha256 || '');
@@ -697,7 +700,8 @@ async function reconcileSkillsUnlocked(opts) {
697
700
  && Number.isFinite(installedBuildTime)
698
701
  && installedBuildTime > 0
699
702
  && currentBuildTime !== null
700
- && installedBuildTime > currentBuildTime) {
703
+ && installedBuildTime > currentBuildTime
704
+ && await activeNewerSksOnPath(PACKAGE_VERSION, installedBuildTime)) {
701
705
  report.ok = false;
702
706
  report.warnings.push(`managed_skill_same_version_older_build_refused:${currentBuildTime}:installed_${installedBuildTime}`);
703
707
  return report;
@@ -1384,3 +1388,29 @@ async function removeEmptyGeneratedParents(root, rel) {
1384
1388
  if (rel.startsWith('.codex/skills/'))
1385
1389
  await removeDirIfEmpty(path.join(root, '.codex', 'skills'));
1386
1390
  }
1391
+ async function activeNewerSksOnPath(runtimeVersion, sameVersionBuildTime = null) {
1392
+ const bin = await which('sks').catch(() => null);
1393
+ if (!bin)
1394
+ return null;
1395
+ let dir = path.dirname(await fsp.realpath(bin).catch(() => bin));
1396
+ for (let depth = 0; depth < 5; depth += 1) {
1397
+ const manifest = await readJson(path.join(dir, 'package.json'), null).catch(() => null);
1398
+ if (manifest?.name === 'sneakoscope' && typeof manifest.version === 'string') {
1399
+ const order = compareSemVer(manifest.version, runtimeVersion);
1400
+ if (order === 1)
1401
+ return manifest.version;
1402
+ if (order === 0 && sameVersionBuildTime !== null && path.resolve(dir) !== path.resolve(packageRoot())) {
1403
+ const stamp = await readJson(path.join(dir, 'dist', '.sks-build-stamp.json'), null).catch(() => null);
1404
+ const built = Number(stamp?.built_at_source_time);
1405
+ if (Number.isFinite(built) && built >= sameVersionBuildTime)
1406
+ return manifest.version;
1407
+ }
1408
+ return null;
1409
+ }
1410
+ const parent = path.dirname(dir);
1411
+ if (parent === dir)
1412
+ break;
1413
+ dir = parent;
1414
+ }
1415
+ return null;
1416
+ }
@@ -0,0 +1,58 @@
1
+ import { compareSemVer } from './semver.js';
2
+ export async function versionDownloadability(input) {
3
+ const env = input.env || process.env;
4
+ if (!probeEnabled(env))
5
+ return 'unknown';
6
+ const fetchImpl = input.fetchImpl || fetch;
7
+ const timeoutMs = input.timeoutMs ?? 4000;
8
+ const base = input.registry.replace(/\/+$/, '');
9
+ const name = input.packageName.replace('/', '%2F');
10
+ try {
11
+ const doc = await timed(fetchImpl, `${base}/${name}/${encodeURIComponent(input.version)}`, { headers: { accept: 'application/json' } }, timeoutMs);
12
+ if (doc.status === 404)
13
+ return 'not_yet_downloadable';
14
+ if (!doc.ok)
15
+ return 'unknown';
16
+ const tarball = (await doc.json().catch(() => null))?.dist?.tarball;
17
+ if (typeof tarball !== 'string' || !/^https?:\/\//.test(tarball))
18
+ return 'unknown';
19
+ const head = await timed(fetchImpl, tarball, { method: 'HEAD' }, timeoutMs);
20
+ if (head.ok)
21
+ return 'downloadable';
22
+ return head.status === 404 ? 'not_yet_downloadable' : 'unknown';
23
+ }
24
+ catch {
25
+ return 'unknown';
26
+ }
27
+ }
28
+ export async function offeredUpdateVersion(input) {
29
+ const { listed, current } = input;
30
+ if (!listed || (compareSemVer(listed, current) ?? 0) <= 0)
31
+ return { latest: listed, pending: null };
32
+ const state = await versionDownloadability({
33
+ registry: input.registry,
34
+ packageName: input.packageName,
35
+ version: listed,
36
+ ...(input.env ? { env: input.env } : {}),
37
+ ...(input.fetchImpl ? { fetchImpl: input.fetchImpl } : {})
38
+ });
39
+ return state === 'not_yet_downloadable' ? { latest: current, pending: listed } : { latest: listed, pending: null };
40
+ }
41
+ function probeEnabled(env) {
42
+ const flag = env.SKS_UPDATE_TARBALL_PROBE;
43
+ if (flag === '0')
44
+ return false;
45
+ if (flag === '1')
46
+ return true;
47
+ return !(env.NODE_TEST_CONTEXT || env.SKS_TEST_ISOLATION === '1');
48
+ }
49
+ async function timed(fetchImpl, url, init, timeoutMs) {
50
+ const controller = new AbortController();
51
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
52
+ try {
53
+ return await fetchImpl(url, { ...init, signal: controller.signal });
54
+ }
55
+ finally {
56
+ clearTimeout(timer);
57
+ }
58
+ }
@@ -4,6 +4,11 @@ import path from 'node:path';
4
4
  import { readJson, runProcess } from '../fsx.js';
5
5
  import { runPackageLocalDoctor } from './update-migration-state.js';
6
6
  import { compareSemVer, extractSemVer } from './semver.js';
7
+ export function isPublishPropagationError(text) {
8
+ return /\bE404\b|\bETARGET\b|No matching version found|404 Not Found - GET [^\s]+\.tgz/i.test(text);
9
+ }
10
+ export const PUBLISH_PROPAGATION_WAIT_MS = 6 * 60 * 1000;
11
+ const PUBLISH_PROPAGATION_RETRY_MS = 20 * 1000;
7
12
  export async function runTemporaryInstallSmoke(input) {
8
13
  const npmArgs = [];
9
14
  if (input.env.SKS_UPDATE_SKIP_TEMP_INSTALL_SMOKE === '1') {
@@ -19,21 +24,38 @@ export async function runTemporaryInstallSmoke(input) {
19
24
  entrypoint = path.resolve(fixtureEntrypoint);
20
25
  }
21
26
  else {
22
- npmArgs.push('install', '--prefix', temp, `${input.packageName}@${input.version}`, '--registry', input.registry, '--ignore-scripts', '--no-audit', '--no-fund');
23
- const install = await runProcess(input.npmBin, npmArgs, {
24
- cwd: temp,
25
- env: input.env,
26
- timeoutMs: input.timeoutMs ?? 3 * 60 * 1000,
27
- maxOutputBytes: input.maxOutputBytes ?? 128 * 1024
28
- }).catch((error) => ({
29
- code: 1,
30
- stdout: '',
31
- stderr: error instanceof Error ? error.message : String(error),
32
- timedOut: false
33
- }));
27
+ npmArgs.push('install', '--prefix', temp, `${input.packageName}@${input.version}`, '--registry', input.registry, '--ignore-scripts', '--no-audit', '--no-fund', '--prefer-online');
28
+ const waitBudget = publishWaitBudgetMs(input.env);
29
+ const sleep = input.sleep || ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
30
+ const started = Date.now();
31
+ let install;
32
+ for (;;) {
33
+ install = await runProcess(input.npmBin, npmArgs, {
34
+ cwd: temp,
35
+ env: input.env,
36
+ timeoutMs: input.timeoutMs ?? 3 * 60 * 1000,
37
+ maxOutputBytes: input.maxOutputBytes ?? 128 * 1024
38
+ }).catch((error) => ({
39
+ code: 1,
40
+ stdout: '',
41
+ stderr: error instanceof Error ? error.message : String(error),
42
+ timedOut: false
43
+ }));
44
+ const output = `${install.stderr || ''}\n${install.stdout || ''}`;
45
+ const waited = Date.now() - started;
46
+ if (install.code === 0 || !isPublishPropagationError(output) || waited >= waitBudget)
47
+ break;
48
+ input.onPublishWait?.(waited);
49
+ await sleep(Math.min(PUBLISH_PROPAGATION_RETRY_MS, waitBudget - waited));
50
+ }
34
51
  installCode = install.code;
52
+ const waitedMs = Date.now() - started;
35
53
  if (install.code !== 0) {
36
- return result(input, npmArgs, false, 'install_failed', installCode, null, null, null, null, String(install.stderr || install.stdout || 'temporary npm install failed').trim());
54
+ const output = String(install.stderr || install.stdout || 'temporary npm install failed').trim();
55
+ const pendingMessage = `${input.packageName}@${input.version} is published but npm has not finished distributing it (waited ${Math.round(waitedMs / 1000)}s). Your current version keeps working; run sks update again in a few minutes.`;
56
+ return isPublishPropagationError(output)
57
+ ? { ...result(input, npmArgs, false, 'not_yet_downloadable', installCode, null, null, null, null, pendingMessage), publish_wait_ms: waitedMs }
58
+ : { ...result(input, npmArgs, false, 'install_failed', installCode, null, null, null, null, output), publish_wait_ms: waitedMs };
37
59
  }
38
60
  const packageRoot = path.join(temp, 'node_modules', input.packageName);
39
61
  const manifest = await readJson(path.join(packageRoot, 'package.json'), null).catch(() => null);
@@ -89,6 +111,13 @@ function result(input, npmArgs, ok, status, installCode, entrypoint, manifestVer
89
111
  error
90
112
  };
91
113
  }
114
+ function publishWaitBudgetMs(env) {
115
+ const raw = env.SKS_UPDATE_PUBLISH_WAIT_MS;
116
+ if (raw === undefined || raw === '')
117
+ return PUBLISH_PROPAGATION_WAIT_MS;
118
+ const value = Number(raw);
119
+ return Number.isFinite(value) && value >= 0 ? value : PUBLISH_PROPAGATION_WAIT_MS;
120
+ }
92
121
  async function regularFile(file) {
93
122
  const stat = await fs.stat(file).catch(() => null);
94
123
  return Boolean(stat?.isFile());
@@ -12,6 +12,7 @@ import { compareSemVer, extractSemVer, parseSemVer } from './update/semver.js';
12
12
  import { countUpdates, emptyUpdateStatus, resolveSksUpdateStatus, UpdateStatusRefreshError } from './update/update-status.js';
13
13
  import { acquireUpdateOperationLock, authorizeUpdateRollback, buildUpdateNowCommand, buildUpdateRollbackCommand, canonicalUpdateRegistry, UpdateOperationRecorder, updateOperationLastInstallPath, updateReceiptHasConfirmedGlobalInstall } from './update/update-operation.js';
14
14
  import { runTemporaryInstallSmoke } from './update/temporary-install-smoke.js';
15
+ import { offeredUpdateVersion } from './update/registry-availability.js';
15
16
  import { npmGlobalInstallTargets, repairManagedPermissions } from './update/managed-permission-repair.js';
16
17
  import { updateStageFailureDiagnostics } from './update/update-stage-diagnostics.js';
17
18
  import { executableOnInjectedPath, inspectInstalledCliResolution } from './update/installed-cli-resolution.js';
@@ -150,7 +151,7 @@ async function runSksUpdateCheckLive(options = {}) {
150
151
  });
151
152
  }
152
153
  const result = await latestPromise;
153
- if (!result) {
154
+ if (!result || result.code !== 0) {
154
155
  return buildResult({
155
156
  packageName,
156
157
  current,
@@ -160,33 +161,14 @@ async function runSksUpdateCheckLive(options = {}) {
160
161
  npmBin,
161
162
  projectRoot: options.projectRoot,
162
163
  commandRegistry: options.registry,
163
- error: 'npm view failed'
164
+ error: result ? `${result.stderr || result.stdout || 'npm view failed'}`.trim() : 'npm view failed'
164
165
  });
165
166
  }
166
- if (result.code !== 0) {
167
- return buildResult({
168
- packageName,
169
- current,
170
- effective,
171
- latest: null,
172
- registry,
173
- npmBin,
174
- projectRoot: options.projectRoot,
175
- commandRegistry: options.registry,
176
- error: `${result.stderr || result.stdout || 'npm view failed'}`.trim()
177
- });
178
- }
179
- const latest = extractSemVer(String(result.stdout || '').trim().split(/\s+/).pop() || '');
180
- return buildResult({
181
- packageName,
182
- current,
183
- effective,
184
- latest,
185
- registry,
186
- npmBin,
187
- projectRoot: options.projectRoot,
188
- commandRegistry: options.registry
189
- });
167
+ const offered = await offeredUpdateVersion({ listed: extractSemVer(String(result.stdout || '').trim().split(/\s+/).pop() || ''), current, registry, packageName, env });
168
+ return {
169
+ ...buildResult({ packageName, current, effective, latest: offered.latest, registry, npmBin, projectRoot: options.projectRoot, commandRegistry: options.registry }),
170
+ pending_latest: offered.pending
171
+ };
190
172
  }
191
173
  function buildUpdateStatusSnapshot(input) {
192
174
  const currentParsed = parseSemVer(input.check.current);
@@ -823,7 +805,7 @@ async function runSksUpdateNowInternal(options, rollbackAuthorization) {
823
805
  registry,
824
806
  env: nestedProcessEnv,
825
807
  ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
826
- ...(options.maxOutputBytes === undefined ? {} : { maxOutputBytes: options.maxOutputBytes })
808
+ ...(options.maxOutputBytes === undefined ? {} : { maxOutputBytes: options.maxOutputBytes }), ...(machineOutput ? {} : { onPublishWait: (waited) => process.stderr.write(` npm is still publishing ${packageName}@${installVersion}; waiting (${Math.round(waited / 1000)}s)...\n`) })
827
809
  });
828
810
  stage('temporary_install_smoke', temporaryInstallSmoke.ok, temporaryInstallSmoke.status, {
829
811
  install_code: temporaryInstallSmoke.install_code,
@@ -846,7 +828,7 @@ async function runSksUpdateNowInternal(options, rollbackAuthorization) {
846
828
  projectRoot: projectReceiptRoot,
847
829
  registry,
848
830
  globalRoot,
849
- status: 'failed',
831
+ status: temporaryInstallSmoke.status === 'not_yet_downloadable' ? 'unavailable' : 'failed',
850
832
  ok: false,
851
833
  installCode: null,
852
834
  oldVersionDoctor,
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = '10.3.5';
1
+ export const PACKAGE_VERSION = '10.3.7';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "10.3.5",
4
+ "version": "10.3.7",
5
5
  "description": "Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",