sneakoscope 10.7.0 → 10.7.2
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 +27 -5
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/command-manifest-lite.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +55 -20
- package/dist/commands/bridge.js +14 -1
- package/dist/commands/codex.js +5 -1
- package/dist/commands/doctor-console.js +2 -0
- package/dist/commands/doctor.js +8 -9
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-role-config.js +6 -2
- package/dist/core/agents/sks-model-pin-hygiene.js +150 -0
- package/dist/core/codex/codex-config-guard.js +18 -13
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +7 -1
- package/dist/core/codex-app/menubar/constants.js +0 -1
- package/dist/core/codex-lb/bridge-contracts.js +5 -0
- package/dist/core/codex-lb/combined-catalog/builder.js +4 -2
- package/dist/core/codex-lb/combined-catalog/latest-generation.js +7 -34
- package/dist/core/codex-lb/combined-catalog/normalize.js +3 -1
- package/dist/core/codex-lb/combined-catalog/persistence.js +4 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/codex-lb-turn-state.js +35 -0
- package/dist/core/codex-lb/desktop-bridge/compaction-adapter.js +3 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +10 -4
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +138 -36
- package/dist/core/codex-lb/desktop-bridge/models-endpoint.js +62 -0
- package/dist/core/codex-lb/desktop-bridge/provider-handoff.js +88 -0
- package/dist/core/codex-lb/desktop-bridge/provider-request-compat.js +89 -0
- package/dist/core/codex-lb/desktop-bridge/rejection-log.js +61 -0
- package/dist/core/codex-lb/desktop-bridge/responses-websocket.js +108 -16
- package/dist/core/codex-lb/desktop-bridge/security.js +101 -55
- package/dist/core/codex-lb/desktop-bridge/server.js +15 -2
- package/dist/core/codex-lb/desktop-bridge/types.js +3 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +3 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +2 -6
- package/dist/core/codex-lb/desktop-bridge-migration.js +14 -2
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +125 -3
- package/dist/core/codex-lb/desktop-controller-v3/model-refresh.js +140 -0
- package/dist/core/codex-lb/desktop-controller-v3/openrouter-only-commands.js +5 -5
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +14 -11
- package/dist/core/codex-lb/desktop-controller-v3/status.js +2 -3
- package/dist/core/codex-lb/desktop-controller-v3.js +3 -1
- package/dist/core/codex-lb/desktop-service.js +51 -17
- package/dist/core/codex-lb/model-refresh-after.js +16 -0
- package/dist/core/codex-lb/request-route-resolver.js +1 -1
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +0 -1
- package/dist/core/commands/basic-cli.js +5 -0
- package/dist/core/commands/menubar-command.js +5 -1
- package/dist/core/doctor/desktop-bridge-catalog-repair.js +20 -17
- package/dist/core/hooks-runtime/jev-spawn-routing.js +1 -5
- package/dist/core/hooks-runtime/parent-orchestration-gate.js +6 -8
- package/dist/core/hooks-runtime.js +13 -9
- package/dist/core/init/skills.js +22 -3
- package/dist/core/provider/provider-context.js +4 -1
- package/dist/core/search/context.js +2 -2
- package/dist/core/subagents/model-tiers.js +19 -3
- package/dist/core/update/update-migration-state/desktop-bridge-catalog-repair-stage.js +4 -3
- package/dist/core/update/update-migration-state.js +10 -40
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +0 -1
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +32 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +15 -5
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +0 -2
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +3 -0
- package/package.json +2 -2
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +0 -49
|
@@ -335,17 +335,18 @@ async function hookUserPrompt(root, state, payload, noQuestion, sessionKey = nul
|
|
|
335
335
|
const submittedPrompt = stripVisibleDecisionAnswerBlocks(extractUserPrompt(payload));
|
|
336
336
|
let stagedMemory = null;
|
|
337
337
|
const disposition = jevPlan?.memoryDisposition;
|
|
338
|
-
|
|
338
|
+
const plan = jevPlan?.executionPlan || null;
|
|
339
|
+
if (state?.mission_id && (plan || state?.jev_execution_plan)) {
|
|
339
340
|
await setCurrent(root, {
|
|
340
|
-
jev_execution_plan:
|
|
341
|
-
jev_turn_binding: jevPlan
|
|
342
|
-
plan_id:
|
|
343
|
-
source_digest:
|
|
344
|
-
graph_digest:
|
|
345
|
-
policy_revision:
|
|
346
|
-
config_digest:
|
|
341
|
+
jev_execution_plan: plan,
|
|
342
|
+
jev_turn_binding: plan && jevPlan?.decisionBinding ? {
|
|
343
|
+
plan_id: plan.plan_id,
|
|
344
|
+
source_digest: plan.source_digest,
|
|
345
|
+
graph_digest: plan.graph_digest,
|
|
346
|
+
policy_revision: plan.policy_revision,
|
|
347
|
+
config_digest: plan.config_digest
|
|
347
348
|
} : null,
|
|
348
|
-
jev_turn_tier: jevPlan
|
|
349
|
+
jev_turn_tier: plan && jevPlan?.decision.tier ? { ...jevPlan.decision.tier } : null
|
|
349
350
|
}, { sessionKey, replace: false }).catch(() => null);
|
|
350
351
|
}
|
|
351
352
|
if (state?.mission_id && jevPlan?.decisionBinding && disposition
|
|
@@ -1135,6 +1136,9 @@ function clarificationPauseBlockReason(state = {}) {
|
|
|
1135
1136
|
}
|
|
1136
1137
|
async function hookStop(root, state, payload, noQuestion, sessionKey = null) {
|
|
1137
1138
|
const last = extractLastMessage(payload);
|
|
1139
|
+
if (state?.jev_execution_plan) {
|
|
1140
|
+
await setCurrent(root, { jev_execution_plan: null, jev_turn_binding: null, jev_turn_tier: null }, { sessionKey, replace: false }).catch(() => null);
|
|
1141
|
+
}
|
|
1138
1142
|
if (!noQuestion) {
|
|
1139
1143
|
const lightTurn = await consumeLightTurnStopBypass(root, { sessionKey, turnId: hookTurnId(payload) });
|
|
1140
1144
|
if (lightTurn.accepted)
|
package/dist/core/init/skills.js
CHANGED
|
@@ -262,6 +262,8 @@ async function withManagedSkillCleanupLocks(locks, index, run) {
|
|
|
262
262
|
return withManagedSkillGenerationLock(lock.ownerRoot, lock.scope, () => withManagedSkillCleanupLocks(locks, index + 1, run));
|
|
263
263
|
}
|
|
264
264
|
async function reconcileRemovedSkillTargets(targets, fix) {
|
|
265
|
+
const manifest = await loadSkillsManifest();
|
|
266
|
+
const currentNames = new Set(manifest.skills.map((skill) => canonicalSkillNameFromValue(skill.canonical_name)));
|
|
265
267
|
const detected = [];
|
|
266
268
|
const removed = [];
|
|
267
269
|
const quarantinedUserCollisions = [];
|
|
@@ -348,12 +350,22 @@ async function reconcileRemovedSkillTargets(targets, fix) {
|
|
|
348
350
|
const rowByName = new Map(rows.map((row) => [row.name, row]));
|
|
349
351
|
for (const row of rows) {
|
|
350
352
|
const name = canonicalSkillNameFromValue(row.name);
|
|
351
|
-
|
|
353
|
+
const retiredName = REMOVED_SKS_SKILL_NAME_SET.has(name);
|
|
354
|
+
if (!retiredName && (currentNames.has(name) || !row.isDirectory()))
|
|
352
355
|
continue;
|
|
353
356
|
const dir = path.join(targetDir, row.name);
|
|
354
357
|
const displayPath = displayRemovedSkillPath(target, dir);
|
|
355
358
|
let managed = false;
|
|
356
359
|
try {
|
|
360
|
+
if (!retiredName) {
|
|
361
|
+
const skillPath = path.join(dir, 'SKILL.md');
|
|
362
|
+
const inspected = await inspectConfinedPath(ownerRoot, skillPath);
|
|
363
|
+
if (!inspected.exists || inspected.leafSymlink || !inspected.stat?.isFile())
|
|
364
|
+
continue;
|
|
365
|
+
const text = await fsp.readFile(skillPath, 'utf8');
|
|
366
|
+
if (!MANAGED_SKILL_MARKER_RE.test(text) || FORGE_SKILL_MARKER_RE.test(text))
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
357
369
|
managed = await isManagedRemovedSkillPath(ownerRoot, dir);
|
|
358
370
|
}
|
|
359
371
|
catch (error) {
|
|
@@ -361,7 +373,7 @@ async function reconcileRemovedSkillTargets(targets, fix) {
|
|
|
361
373
|
remaining.push(displayPath);
|
|
362
374
|
continue;
|
|
363
375
|
}
|
|
364
|
-
if (!managed && target.managedOnly)
|
|
376
|
+
if (!managed && target.managedOnly && retiredName)
|
|
365
377
|
continue;
|
|
366
378
|
detected.push({
|
|
367
379
|
scope: target.scope,
|
|
@@ -737,6 +749,10 @@ async function reconcileSkillsUnlocked(opts) {
|
|
|
737
749
|
}, ...hostExtraRemovedSkillTargets(root));
|
|
738
750
|
}
|
|
739
751
|
const removedResidue = await reconcileRemovedSkillTargets(removedResidueTargets, opts.fix);
|
|
752
|
+
const quarantinedResiduePaths = new Set(removedResidue.quarantined_user_collisions);
|
|
753
|
+
report.quarantined_user_collisions.push(...removedResidue.detected
|
|
754
|
+
.filter((entry) => quarantinedResiduePaths.has(entry.path))
|
|
755
|
+
.map((entry) => entry.name));
|
|
740
756
|
report.retired_residue = {
|
|
741
757
|
detected_count: removedResidue.detected.length,
|
|
742
758
|
removed_count: removedResidue.removed.length,
|
|
@@ -813,7 +829,10 @@ async function reconcileSkillsUnlocked(opts) {
|
|
|
813
829
|
report.core_skill_integrity = install?.core_skill_integrity || { ok: true, installed_count: 0, restored_count: 0, user_collision_count: 0 };
|
|
814
830
|
report.quarantined_user_collisions.push(...(install?.quarantined_user_collisions || []));
|
|
815
831
|
report.quarantined_manifest_collisions = [...new Set(install?.quarantined_manifest_collisions || [])].sort();
|
|
816
|
-
report.removed_stale_generated_skills = currentSurfaceSkillPaths(
|
|
832
|
+
report.removed_stale_generated_skills = currentSurfaceSkillPaths([
|
|
833
|
+
...(install?.removed_stale_generated_skills || report.removed),
|
|
834
|
+
...removedResidue.removed.map((file) => file.replace(/^~\//, ''))
|
|
835
|
+
]);
|
|
817
836
|
report.removed_agent_skill_aliases = currentSurfaceSkillPaths(install?.removed_agent_skill_aliases || []);
|
|
818
837
|
report.removed_codex_skill_mirrors = currentSurfaceSkillPaths(install?.removed_codex_skill_mirrors || []);
|
|
819
838
|
report.removed = currentSurfaceSkillPaths(report.removed);
|
|
@@ -83,9 +83,12 @@ function selectedBridgeProvider(status) {
|
|
|
83
83
|
const selected = status?.routing.selected_route?.provider_id;
|
|
84
84
|
return (selected === 'codex-lb' || selected === 'openrouter' ? selected : null)
|
|
85
85
|
|| status?.routing.policy?.default_provider_id
|
|
86
|
-
|| status?.routing.session_pin?.provider_id
|
|
86
|
+
|| providerPinOf(status?.routing.session_pin?.provider_id)
|
|
87
87
|
|| null;
|
|
88
88
|
}
|
|
89
|
+
function providerPinOf(pinned) {
|
|
90
|
+
return pinned === 'codex-lb' || pinned === 'openrouter' ? pinned : null;
|
|
91
|
+
}
|
|
89
92
|
async function currentDesktopBridgeStatus(options) {
|
|
90
93
|
const controller = await import('../codex-lb/desktop-controller.js');
|
|
91
94
|
return controller.desktopBridgeStatusV3(options);
|
|
@@ -4,7 +4,7 @@ import { cacheGet, cacheSet } from './cache.js';
|
|
|
4
4
|
import { contextGraphMetaOf, emptyGraphMeta, firstProvenanceHash, omissionReasonsOf, overallConfidence, projectMatches } from './context-projection.js';
|
|
5
5
|
import { defaultSearchLimits, emptySkipped, SEARCH_PROVIDER_SCHEMA, SEARCH_SCHEMA_VERSION } from './types.js';
|
|
6
6
|
import { CONTEXT_GRAPH_CORRUPT_ERROR, CONTEXT_GRAPH_MISSING_ERROR, CONTEXT_GRAPH_REPAIR_COMMAND, CONTEXT_GRAPH_STALE_ERROR } from '../triwiki/context-graph/contracts.js';
|
|
7
|
-
import {
|
|
7
|
+
import { alignGraphExtractors } from '../triwiki/context-graph/extractors/index.js';
|
|
8
8
|
import { isWorkspaceRelativePosixPath } from '../triwiki/context-graph/paths.js';
|
|
9
9
|
import { CONTEXT_GRAPH_TRAVERSAL_CAPS, DEFAULT_CONTEXT_GRAPH_QUERY_PROFILE, isContextGraphQueryProfileName } from '../triwiki/context-graph/profiles.js';
|
|
10
10
|
import { changedPathKernelSeeds, changedPathSeeds, openWorkspaceContextIndex, queryWorkspaceContext, sharedContextIndexCache, workspaceContextFailureOf } from '../triwiki/context-graph/query/index.js';
|
|
@@ -132,7 +132,7 @@ async function preflightStatus(root, options) {
|
|
|
132
132
|
const cached = cacheGet(key);
|
|
133
133
|
if (cached)
|
|
134
134
|
return cached;
|
|
135
|
-
const status = await contextIndexFreshness(root, { extractors:
|
|
135
|
+
const status = await contextIndexFreshness(root, { extractors: alignGraphExtractors(), verifySources: false });
|
|
136
136
|
cacheSet(key, status, STATUS_MEMO_TTL_MS);
|
|
137
137
|
return status;
|
|
138
138
|
}
|
|
@@ -56,16 +56,32 @@ function readableRows(file) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
let catalogPathMemo = null;
|
|
59
|
+
const BRIDGE_BASE_URL_MARKER = '# sks-desktop-bridge-managed-base-url';
|
|
60
|
+
function activeBridgeCatalogPath(codexHome) {
|
|
61
|
+
try {
|
|
62
|
+
const pointer = JSON.parse(fs.readFileSync(path.join(codexHome, 'sks', 'sks-bridge-active-generation.json'), 'utf8'));
|
|
63
|
+
const bundle = path.resolve(codexHome, 'sks', String(pointer?.bundle_directory || ''));
|
|
64
|
+
const file = path.join(bundle, String(pointer?.catalog_filename || ''));
|
|
65
|
+
return bundle.startsWith(path.join(codexHome, 'sks', '.sks-bridge-generations') + path.sep) && path.dirname(file) === bundle ? file : null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
59
71
|
function configuredCatalogPath(input) {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
72
|
+
const codexHome = codexHomePath(input);
|
|
73
|
+
const configPath = path.join(codexHome, 'config.toml');
|
|
74
|
+
const key = `${fileKey(configPath)}|${fileKey(path.join(codexHome, 'sks', 'sks-bridge-active-generation.json'))}`;
|
|
62
75
|
if (catalogPathMemo?.key === key)
|
|
63
76
|
return catalogPathMemo.path;
|
|
64
77
|
let resolved = null;
|
|
65
78
|
try {
|
|
66
79
|
const stat = fs.statSync(configPath);
|
|
67
80
|
if (stat.isFile() && stat.size <= 4 * 1024 * 1024) {
|
|
68
|
-
const
|
|
81
|
+
const config = fs.readFileSync(configPath, 'utf8');
|
|
82
|
+
const value = readTopLevelTomlString(config, 'model_catalog_json')?.trim();
|
|
83
|
+
if (!value && config.split(/\r?\n/).some((line) => line.trim() === BRIDGE_BASE_URL_MARKER))
|
|
84
|
+
resolved = activeBridgeCatalogPath(codexHome);
|
|
69
85
|
if (value) {
|
|
70
86
|
const env = input.env || process.env;
|
|
71
87
|
const home = input.home || env.HOME || os.homedir();
|
|
@@ -17,17 +17,18 @@ export async function runDesktopBridgeCatalogRepairStage() {
|
|
|
17
17
|
if (!(await exists(desktopBridgeServicePaths(home).settings_path))) {
|
|
18
18
|
return skip('desktop_bridge_catalog_repair_no_managed_bridge');
|
|
19
19
|
}
|
|
20
|
-
const repair = await repairDoctorDesktopBridgeCatalog({ fix: true }).catch((error) => ({
|
|
20
|
+
const repair = await repairDoctorDesktopBridgeCatalog({ fix: true, reason: 'update' }).catch((error) => ({
|
|
21
21
|
ok: false,
|
|
22
22
|
repaired: false,
|
|
23
23
|
warnings: [],
|
|
24
24
|
blockers: [`desktop_bridge_catalog_repair_failed:${error?.message || String(error)}`]
|
|
25
25
|
}));
|
|
26
|
+
const refreshLine = typeof repair.model_refresh?.line === 'string' ? [repair.model_refresh.line] : [];
|
|
26
27
|
if (repair.ok) {
|
|
27
28
|
return {
|
|
28
29
|
ok: true,
|
|
29
30
|
status: 'ok',
|
|
30
|
-
actions: [repair.repaired ? 'desktop_bridge_catalog_repaired' : 'desktop_bridge_catalog_clean'],
|
|
31
|
+
actions: [repair.repaired ? 'desktop_bridge_catalog_repaired' : 'desktop_bridge_catalog_clean', ...refreshLine],
|
|
31
32
|
blockers: [],
|
|
32
33
|
warnings: repair.warnings || []
|
|
33
34
|
};
|
|
@@ -35,7 +36,7 @@ export async function runDesktopBridgeCatalogRepairStage() {
|
|
|
35
36
|
return {
|
|
36
37
|
ok: true,
|
|
37
38
|
status: 'ok',
|
|
38
|
-
actions:
|
|
39
|
+
actions: refreshLine,
|
|
39
40
|
blockers: [],
|
|
40
41
|
warnings: [
|
|
41
42
|
...(repair.blockers || []).map((blocker) => `desktop_bridge_catalog_repair_incomplete:${blocker}`),
|
|
@@ -16,7 +16,6 @@ import { runDesktopBridgeRestageStage } from './update-migration-state/desktop-b
|
|
|
16
16
|
import { runSessionStateSplitStage } from './update-migration-state/session-state-split.js';
|
|
17
17
|
import { runRetiredLocalDecisionStage } from './update-migration-state/retired-local-decision.js';
|
|
18
18
|
import { runConfigSyntaxRepairStage, runHookTrustRefreshStage, runOtherHarnessCleanupStage } from './update-migration-state/simple-stages.js';
|
|
19
|
-
import { compareSemVer } from './semver.js';
|
|
20
19
|
import { repairManagedPermissions } from './managed-permission-repair.js';
|
|
21
20
|
import { recordSksProject, runOtherProjectsMigrationStage, SKS_PROJECT_MARKERS } from './sks-project-registry.js';
|
|
22
21
|
export const UPDATE_MIGRATION_SCHEMA = 'sks.project-migration-receipt.v2';
|
|
@@ -265,27 +264,22 @@ export async function runUpdateRetentionCleanup(root, source = 'update-migration
|
|
|
265
264
|
const UPDATE_MIGRATION_STAGES = [
|
|
266
265
|
{
|
|
267
266
|
id: 'other-harness-cleanup',
|
|
268
|
-
min_from_version: '0.0.0',
|
|
269
267
|
run: runOtherHarnessCleanupStage
|
|
270
268
|
},
|
|
271
269
|
{
|
|
272
270
|
id: 'retired-local-decision',
|
|
273
|
-
min_from_version: '0.0.0',
|
|
274
271
|
run: () => runRetiredLocalDecisionStage()
|
|
275
272
|
},
|
|
276
273
|
{
|
|
277
274
|
id: 'skills-reconcile',
|
|
278
|
-
min_from_version: '0.0.0',
|
|
279
275
|
run: runSkillsReconcileStage
|
|
280
276
|
},
|
|
281
277
|
{
|
|
282
278
|
id: 'current-public-surface-reconcile',
|
|
283
|
-
min_from_version: '0.0.0',
|
|
284
279
|
run: runCurrentPublicSurfaceReconcileStage
|
|
285
280
|
},
|
|
286
281
|
{
|
|
287
282
|
id: 'naruto-execution-mode',
|
|
288
|
-
min_from_version: '0.0.0',
|
|
289
283
|
run: async () => {
|
|
290
284
|
const { writeNarutoExecutionMode } = await import('../subagents/naruto-execution-mode.js');
|
|
291
285
|
const initialMode = process.env.SKS_NARUTO_APP_SESSION === '1' ? 'current-session' : 'standalone';
|
|
@@ -295,42 +289,34 @@ const UPDATE_MIGRATION_STAGES = [
|
|
|
295
289
|
},
|
|
296
290
|
{
|
|
297
291
|
id: 'session-state-split',
|
|
298
|
-
min_from_version: '0.0.0',
|
|
299
292
|
run: runSessionStateSplitStage
|
|
300
293
|
},
|
|
301
294
|
{
|
|
302
295
|
id: 'menubar-retarget',
|
|
303
|
-
min_from_version: '0.0.0',
|
|
304
296
|
run: runMenubarRetargetStage
|
|
305
297
|
},
|
|
306
298
|
{
|
|
307
299
|
id: 'config-fastmode-normalize',
|
|
308
|
-
min_from_version: '0.0.0',
|
|
309
300
|
run: runConfigFastModeNormalizeStage
|
|
310
301
|
},
|
|
311
302
|
{
|
|
312
303
|
id: 'config-syntax-repair',
|
|
313
|
-
min_from_version: '0.0.0',
|
|
314
304
|
run: runConfigSyntaxRepairStage
|
|
315
305
|
},
|
|
316
306
|
{
|
|
317
307
|
id: 'hook-trust-refresh',
|
|
318
|
-
min_from_version: '0.0.0',
|
|
319
308
|
run: runHookTrustRefreshStage
|
|
320
309
|
},
|
|
321
310
|
{
|
|
322
311
|
id: 'receipt-rotation',
|
|
323
|
-
min_from_version: '0.0.0',
|
|
324
312
|
run: runReceiptRotationStage
|
|
325
313
|
},
|
|
326
314
|
{
|
|
327
315
|
id: 'desktop-bridge-restage',
|
|
328
|
-
min_from_version: '0.0.0',
|
|
329
316
|
run: runDesktopBridgeRestageStage
|
|
330
317
|
},
|
|
331
318
|
{
|
|
332
319
|
id: 'desktop-bridge-catalog-repair',
|
|
333
|
-
min_from_version: '0.0.0',
|
|
334
320
|
run: runDesktopBridgeCatalogRepairStage
|
|
335
321
|
}
|
|
336
322
|
];
|
|
@@ -340,26 +326,12 @@ export async function runUpdateMigrationStages(root, opts = {}) {
|
|
|
340
326
|
for (const stage of UPDATE_MIGRATION_STAGES) {
|
|
341
327
|
if (opts.only && !opts.only.has(stage.id))
|
|
342
328
|
continue;
|
|
343
|
-
if (!legacyStageApplies(fromVersion, stage.min_from_version)) {
|
|
344
|
-
runs.push({
|
|
345
|
-
schema: 'sks.update-migration-stage.v2',
|
|
346
|
-
id: stage.id,
|
|
347
|
-
ok: true,
|
|
348
|
-
status: 'skipped',
|
|
349
|
-
min_from_version: stage.min_from_version,
|
|
350
|
-
from_version: fromVersion,
|
|
351
|
-
actions: ['skipped_by_from_version'],
|
|
352
|
-
blockers: [],
|
|
353
|
-
warnings: []
|
|
354
|
-
});
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
329
|
try {
|
|
358
330
|
const result = await stage.run(root, fromVersion);
|
|
359
331
|
runs.push({
|
|
360
332
|
schema: 'sks.update-migration-stage.v2',
|
|
361
333
|
id: stage.id,
|
|
362
|
-
min_from_version:
|
|
334
|
+
min_from_version: '0.0.0',
|
|
363
335
|
from_version: fromVersion,
|
|
364
336
|
...result
|
|
365
337
|
});
|
|
@@ -370,7 +342,7 @@ export async function runUpdateMigrationStages(root, opts = {}) {
|
|
|
370
342
|
id: stage.id,
|
|
371
343
|
ok: false,
|
|
372
344
|
status: 'failed',
|
|
373
|
-
min_from_version:
|
|
345
|
+
min_from_version: '0.0.0',
|
|
374
346
|
from_version: fromVersion,
|
|
375
347
|
actions: [],
|
|
376
348
|
blockers: [err?.message || String(err)],
|
|
@@ -410,6 +382,7 @@ async function runSkillsReconcileStage(root) {
|
|
|
410
382
|
const runtimeRemaining = convergence.retired_runtime_scopes.reduce((sum, report) => sum + report.remaining_managed_artifact_count, 0);
|
|
411
383
|
const runtimeErrors = convergence.retired_runtime_scopes.reduce((sum, report) => sum + report.error_count, 0);
|
|
412
384
|
const blockers = [
|
|
385
|
+
...(convergence.ok ? [] : ['legacy_generation_convergence_failed']),
|
|
413
386
|
...skillReports.flatMap((report) => report.ok ? [] : [
|
|
414
387
|
`${report.scope}:${'error' in report ? report.error : 'failed'}`
|
|
415
388
|
]),
|
|
@@ -418,6 +391,12 @@ async function runSkillsReconcileStage(root) {
|
|
|
418
391
|
...(convergence.retired_agent_roles.ok ? [] : ['retired_agent_role_reconcile_failed']),
|
|
419
392
|
...(runtimeRemaining ? [`retired_runtime_residue_remaining:${runtimeRemaining}`] : []),
|
|
420
393
|
...(runtimeErrors ? [`retired_runtime_reconcile_failed:${runtimeErrors}`] : []),
|
|
394
|
+
...(convergence.runtime_data_gc.remaining_count
|
|
395
|
+
? [`legacy_runtime_data_remaining:${convergence.runtime_data_gc.remaining_count}`]
|
|
396
|
+
: []),
|
|
397
|
+
...(convergence.runtime_data_gc.error_count
|
|
398
|
+
? [`legacy_runtime_data_reconcile_failed:${convergence.runtime_data_gc.error_count}`]
|
|
399
|
+
: []),
|
|
421
400
|
...(convergence.managed_configs.remaining_count
|
|
422
401
|
? [`retired_config_residue_remaining:${convergence.managed_configs.remaining_count}`]
|
|
423
402
|
: []),
|
|
@@ -451,6 +430,7 @@ async function runSkillsReconcileStage(root) {
|
|
|
451
430
|
residue_remaining_count: globalRemaining
|
|
452
431
|
+ projectRemaining
|
|
453
432
|
+ runtimeRemaining
|
|
433
|
+
+ convergence.runtime_data_gc.remaining_count
|
|
454
434
|
+ convergence.managed_configs.remaining_count
|
|
455
435
|
}
|
|
456
436
|
};
|
|
@@ -521,16 +501,6 @@ async function runReceiptRotationStage(root) {
|
|
|
521
501
|
detail: { removed: removable.length }
|
|
522
502
|
};
|
|
523
503
|
}
|
|
524
|
-
function legacyStageApplies(fromVersion, minFromVersion) {
|
|
525
|
-
if (!fromVersion)
|
|
526
|
-
return true;
|
|
527
|
-
if (compareVersionLike(fromVersion, PACKAGE_VERSION) > 0)
|
|
528
|
-
return false;
|
|
529
|
-
return compareVersionLike(fromVersion, minFromVersion) >= 0;
|
|
530
|
-
}
|
|
531
|
-
function compareVersionLike(a, b) {
|
|
532
|
-
return compareSemVer(a, b) ?? 0;
|
|
533
|
-
}
|
|
534
504
|
export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
535
505
|
const env = input.env || process.env;
|
|
536
506
|
const command = input.command;
|
package/dist/core/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '10.7.
|
|
1
|
+
export const PACKAGE_VERSION = '10.7.2';
|
|
@@ -33,7 +33,6 @@ final class ControlCenterWindowController: NSWindowController, NSTableViewDataSo
|
|
|
33
33
|
.mcpServers: MCPServersViewController(processClient: processClient, operations: operations, notifications: notifications),
|
|
34
34
|
.providers: ProvidersViewController(processClient: processClient, operations: operations),
|
|
35
35
|
.subagentModels: SubagentModelsViewController(processClient: processClient, operations: operations),
|
|
36
|
-
.remoteCoding: RemoteCodingViewController(),
|
|
37
36
|
.localDecision: LocalDecisionViewController(processClient: processClient, operations: operations),
|
|
38
37
|
.imageGeneration: ImageGenerationViewController(processClient: processClient, operations: operations),
|
|
39
38
|
.diagnostics: DiagnosticsViewController(processClient: processClient, operations: operations),
|
|
@@ -134,3 +134,35 @@ enum OverviewSummary {
|
|
|
134
134
|
return value
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
/// What `sks bridge models refresh --json` reports, as SKS Center shows it.
|
|
139
|
+
/// Success is the payload's own `ok == true` at both levels; a result that only
|
|
140
|
+
/// carries a schema key is not one.
|
|
141
|
+
enum ModelRefreshSummary {
|
|
142
|
+
struct Outcome: Equatable {
|
|
143
|
+
let ok: Bool
|
|
144
|
+
let text: String
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static func outcome(fromJSONText text: String, at date: Date = Date()) -> Outcome {
|
|
148
|
+
outcome(fromJSON: jsonObject(text), at: date)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static func outcome(fromJSON object: [String: Any]?, at date: Date = Date()) -> Outcome {
|
|
152
|
+
let refresh = (object?["result"] as? [String: Any])?["model_refresh"] as? [String: Any]
|
|
153
|
+
guard object?["ok"] as? Bool == true, refresh?["ok"] as? Bool == true else {
|
|
154
|
+
return Outcome(ok: false, text: refresh?["line"] as? String ?? "Model list refresh failed. Run `sks bridge models refresh` to retry.")
|
|
155
|
+
}
|
|
156
|
+
let total = (refresh?["total"] as? NSNumber)?.intValue ?? 0
|
|
157
|
+
let formatter = DateFormatter()
|
|
158
|
+
formatter.dateFormat = "HH:mm"
|
|
159
|
+
let restart = refresh?["restart_codex_required"] as? Bool == true ? " · restart Codex to see it" : ""
|
|
160
|
+
return Outcome(ok: true, text: "Model list updated at \(formatter.string(from: date)) (\(total) models)\(restart)")
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private static func jsonObject(_ text: String) -> [String: Any]? {
|
|
164
|
+
let start = text.firstIndex(of: "{") ?? text.startIndex
|
|
165
|
+
guard let data = String(text[start...]).data(using: .utf8) else { return nil }
|
|
166
|
+
return (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -6,6 +6,7 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
6
6
|
private let status = NativeView.detail("Loading local SKS status…")
|
|
7
7
|
private let notificationInbox = NativeView.detail("Notifications: checking authorization…")
|
|
8
8
|
private let recoveryStatus = NativeView.detail("Progress recovery: no operation state loaded yet.")
|
|
9
|
+
private let modelListStatus = NativeView.detail("Model list: refreshed by Refresh, Run Doctor and updates.")
|
|
9
10
|
private let healthBadge = NativeView.badge("Checking local status", color: .systemBlue)
|
|
10
11
|
private let statusSpinner = NativeView.spinner(label: "Checking SKS Center status")
|
|
11
12
|
private let snapshotDetails = NativeView.detail("")
|
|
@@ -36,7 +37,6 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
36
37
|
updateCodexButton.setAccessibilityHelp("Update the operator Codex CLI to the preferred latest channel.")
|
|
37
38
|
let buttons = NativeView.row([refreshButton, doctorButton, updateCodexButton])
|
|
38
39
|
let shortcuts = NativeView.row([
|
|
39
|
-
NativeView.button("Remote Coding…", target: self, action: #selector(openRemoteCoding)),
|
|
40
40
|
connectionsShortcut(),
|
|
41
41
|
NativeView.button("Decisions…", target: self, action: #selector(openDecisions)),
|
|
42
42
|
NativeView.button("Updates…", target: self, action: #selector(openUpdates)),
|
|
@@ -50,7 +50,7 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
50
50
|
status.setAccessibilityIdentifier("sks-overview-next-action")
|
|
51
51
|
let healthCard = NativeView.card(
|
|
52
52
|
title: "System health", subtitle: "",
|
|
53
|
-
views: [NativeView.row([healthBadge, statusSpinner]), status, components, buttons]
|
|
53
|
+
views: [NativeView.row([healthBadge, statusSpinner]), status, modelListStatus, components, buttons]
|
|
54
54
|
)
|
|
55
55
|
recoveryCard = NativeView.card(
|
|
56
56
|
title: "Progress, pause & recovery", subtitle: "Review the current operation before retrying.",
|
|
@@ -72,7 +72,6 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
72
72
|
return button
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
@objc private func openRemoteCoding() { openSection?("Remote Coding") }
|
|
76
75
|
@objc private func openProviders() { openSection?("Providers") }
|
|
77
76
|
@objc private func openDecisions() { openSection?("Decisions") }
|
|
78
77
|
@objc private func openUpdates() { openSection?("Updates") }
|
|
@@ -113,7 +112,15 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
@objc private func refreshStatus() {
|
|
116
|
-
loadStatus(forceUpdateRefresh: true)
|
|
115
|
+
loadStatus(forceUpdateRefresh: true) { [weak self] in self?.refreshModelList() }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Refresh, Run Doctor and a Codex CLI update each end with a fresh model list.
|
|
119
|
+
private func refreshModelList() {
|
|
120
|
+
modelListStatus.stringValue = "Refreshing the model list…"
|
|
121
|
+
processClient.run(["bridge", "models", "refresh", "--json"], timeout: NativeView.longMutationTimeout) { [weak self] result in
|
|
122
|
+
self?.modelListStatus.stringValue = ModelRefreshSummary.outcome(fromJSONText: result.output).text
|
|
123
|
+
}
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
@objc private func updateCodexCLI() {
|
|
@@ -145,11 +152,12 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
145
152
|
self.status.stringValue = ok
|
|
146
153
|
? "Codex CLI update completed. Refreshing shared update status…"
|
|
147
154
|
: "Codex CLI update needs attention. Open Updates or Diagnostics for structured guidance."
|
|
155
|
+
if ok { self.refreshModelList() }
|
|
148
156
|
self.loadStatus(forceUpdateRefresh: true)
|
|
149
157
|
}
|
|
150
158
|
}
|
|
151
159
|
|
|
152
|
-
private func loadStatus(forceUpdateRefresh: Bool) {
|
|
160
|
+
private func loadStatus(forceUpdateRefresh: Bool, then completion: (() -> Void)? = nil) {
|
|
153
161
|
generation += 1
|
|
154
162
|
let requestGeneration = generation
|
|
155
163
|
refreshButton?.isEnabled = false
|
|
@@ -196,6 +204,7 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
196
204
|
self.refreshButton?.isEnabled = true
|
|
197
205
|
self.statusSpinner.stopAnimation(nil)
|
|
198
206
|
self.renderStatus(update: update, mcp: mcp, partial: false)
|
|
207
|
+
completion?()
|
|
199
208
|
}
|
|
200
209
|
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
|
|
201
210
|
guard let self = self,
|
|
@@ -232,6 +241,7 @@ final class OverviewViewController: NSViewController, ControlCenterPage {
|
|
|
232
241
|
if result.code == 0 {
|
|
233
242
|
NativeView.setBadge(self.healthBadge, text: "Doctor completed", color: .systemGreen)
|
|
234
243
|
self.status.stringValue = "Doctor completed. No blocking issue was reported."
|
|
244
|
+
self.refreshModelList()
|
|
235
245
|
} else {
|
|
236
246
|
NativeView.setBadge(self.healthBadge, text: "Doctor needs attention", color: .systemOrange)
|
|
237
247
|
self.status.stringValue = "Doctor found an issue. Open Diagnostics · \(NativeView.redactPreview(result.output))"
|
|
@@ -6,7 +6,6 @@ enum SidebarItem: String, CaseIterable {
|
|
|
6
6
|
case overview = "Overview"
|
|
7
7
|
case providers = "Providers"
|
|
8
8
|
case subagentModels = "Subagent Models"
|
|
9
|
-
case remoteCoding = "Remote Coding"
|
|
10
9
|
case updates = "Updates"
|
|
11
10
|
case mcpServers = "MCP Servers"
|
|
12
11
|
case localDecision = "Decisions"
|
|
@@ -25,7 +24,6 @@ enum SidebarItem: String, CaseIterable {
|
|
|
25
24
|
case .mcpServers: return "server.rack"
|
|
26
25
|
case .localDecision: return "cpu"
|
|
27
26
|
case .imageGeneration: return "photo"
|
|
28
|
-
case .remoteCoding: return "laptopcomputer.and.iphone"
|
|
29
27
|
case .diagnostics: return "stethoscope"
|
|
30
28
|
case .settings: return "gearshape"
|
|
31
29
|
}
|
|
@@ -231,6 +231,9 @@ final class UpdatesViewController: NSViewController, ControlCenterPage {
|
|
|
231
231
|
self.status.stringValue = "Review ready. Confirm to continue, or cancel to leave the current install unchanged."
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
|
+
if kind == "update", authoritativeState == .succeeded { // The update refreshed the model list; show what it holds now.
|
|
235
|
+
self.processClient.run(["bridge", "models", "refresh", "--json"], timeout: NativeView.mutationTimeout) { [weak self] refreshed in self?.status.stringValue = ModelRefreshSummary.outcome(fromJSONText: refreshed.output).text }
|
|
236
|
+
}
|
|
234
237
|
let updateOutput = kind == "update-status" ? result.output : nil
|
|
235
238
|
let approvalRequired = kind == "update-review" && result.code == 0
|
|
236
239
|
let updateAvailable = updateOutput != nil && NotificationCoordinator.updateIsAvailable(in: result.output)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "10.7.
|
|
4
|
+
"version": "10.7.2",
|
|
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",
|
|
@@ -201,6 +201,6 @@
|
|
|
201
201
|
"@types/node": "^24.19.2",
|
|
202
202
|
"@types/ws": "^8.18.2",
|
|
203
203
|
"jsdom": "30.1.3",
|
|
204
|
-
"mermaid": "12.
|
|
204
|
+
"mermaid": "12.2.0"
|
|
205
205
|
}
|
|
206
206
|
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import Cocoa
|
|
2
|
-
|
|
3
|
-
final class RemoteCodingViewController: NSViewController {
|
|
4
|
-
private static let websiteURL = URL(string: "https://paseo.sh/")!
|
|
5
|
-
private static let docsURL = URL(string: "https://paseo.sh/docs")!
|
|
6
|
-
|
|
7
|
-
override func loadView() {
|
|
8
|
-
let websiteButton = NativeView.button(
|
|
9
|
-
"Visit Paseo",
|
|
10
|
-
target: self,
|
|
11
|
-
action: #selector(openWebsite)
|
|
12
|
-
)
|
|
13
|
-
websiteButton.setAccessibilityLabel("Visit Paseo website")
|
|
14
|
-
websiteButton.setAccessibilityHelp("Open the independent Paseo website in your default browser.")
|
|
15
|
-
|
|
16
|
-
let docsButton = NativeView.button(
|
|
17
|
-
"Read Paseo Docs",
|
|
18
|
-
target: self,
|
|
19
|
-
action: #selector(openDocs)
|
|
20
|
-
)
|
|
21
|
-
docsButton.setAccessibilityLabel("Read Paseo documentation")
|
|
22
|
-
docsButton.setAccessibilityHelp("Open the official Paseo documentation in your default browser.")
|
|
23
|
-
|
|
24
|
-
let recommendation = NativeView.card(
|
|
25
|
-
title: "Paseo (recommended)",
|
|
26
|
-
subtitle: "Run Codex from your desktop, phone, web browser, or terminal.",
|
|
27
|
-
views: [
|
|
28
|
-
NativeView.row([websiteButton, docsButton]),
|
|
29
|
-
NativeView.detail("Paseo is a separate, open-source app. Follow its setup and security guidance to connect your devices.")
|
|
30
|
-
]
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
view = NativeView.page([
|
|
34
|
-
NativeView.title("Remote Coding"),
|
|
35
|
-
NativeView.detail(
|
|
36
|
-
"Continue coding from another device."
|
|
37
|
-
),
|
|
38
|
-
recommendation
|
|
39
|
-
])
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@objc private func openWebsite() {
|
|
43
|
-
NSWorkspace.shared.open(Self.websiteURL)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@objc private func openDocs() {
|
|
47
|
-
NSWorkspace.shared.open(Self.docsURL)
|
|
48
|
-
}
|
|
49
|
-
}
|