sneakoscope 8.3.2 → 8.6.0
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 +1 -1
- package/config/architecture-map-policy.v1.json +168 -0
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/commands/bridge.js +19 -1
- package/dist/commands/doctor-profile.js +1 -1
- package/dist/commands/doctor.js +97 -16
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/align/align-route.js +8 -2
- package/dist/core/align/code-navigation-align.js +88 -32
- package/dist/core/architecture-map-pipeline.js +173 -0
- package/dist/core/architecture-map-review.js +180 -0
- package/dist/core/codex/agent-config-file-repair.js +17 -3
- package/dist/core/codex/codex-config-guard.js +3 -1
- package/dist/core/codex/codex-feature-flags.js +10 -0
- package/dist/core/codex/codex-project-config-policy.js +54 -30
- package/dist/core/codex/codex-startup-config-postcheck.js +5 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +2 -1
- package/dist/core/codex-app/menubar/constants.js +1 -0
- package/dist/core/codex-hooks/codex-hook-state-writer.js +2 -0
- package/dist/core/codex-lb/codex-lb-env.js +25 -3
- package/dist/core/codex-lb/combined-catalog/builder.js +7 -2
- package/dist/core/codex-lb/combined-catalog/model-selection.js +71 -0
- package/dist/core/codex-lb/combined-catalog/normalize.js +20 -3
- package/dist/core/codex-lb/desktop-bridge/security.js +11 -8
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +75 -4
- package/dist/core/codex-lb/desktop-controller-v3.js +5 -1
- package/dist/core/codex-lb/request-route-resolver.js +21 -18
- package/dist/core/commands/triwiki-atlas-command.js +372 -0
- package/dist/core/commands/triwiki-command.js +12 -1
- package/dist/core/doctor/doctor-dirty-planner.js +16 -1
- package/dist/core/doctor/doctor-idempotence.js +11 -0
- package/dist/core/doctor/doctor-repair-postcheck.js +49 -0
- package/dist/core/doctor/doctor-transaction.js +47 -2
- package/dist/core/doctor/legacy-global-hook-cleanup.js +1 -1
- package/dist/core/fsx.js +14 -0
- package/dist/core/git-hygiene/git-policy.js +4 -0
- package/dist/core/git-hygiene/gitignore-writer.js +2 -0
- package/dist/core/gx-renderer.js +2 -1
- package/dist/core/init.js +40 -17
- package/dist/core/managed-paths.js +1 -0
- package/dist/core/perf/release-latency-slo.js +57 -11
- package/dist/core/pipeline-internals/pipeline-stage-builder.js +181 -0
- package/dist/core/pipeline-internals/runtime-core.js +18 -172
- package/dist/core/pipeline-internals/runtime-gates.js +12 -1
- package/dist/core/release/content-digest-cache.js +70 -0
- package/dist/core/release/gate-manifest.js +9 -0
- package/dist/core/release/main-push-guard.js +4 -3
- package/dist/core/release/package-dist-snapshot.js +18 -2
- package/dist/core/release/physical-release-gates.js +26 -3
- package/dist/core/release/release-authorization-snapshot.js +4 -0
- package/dist/core/release/release-gate-cache-v2.js +27 -3
- package/dist/core/release/release-gate-contract.js +2 -0
- package/dist/core/release/release-gate-dag.js +3 -0
- package/dist/core/release/release-inspection-memo.js +69 -0
- package/dist/core/release/release-pack-content-scanner.js +5 -0
- package/dist/core/release/release-pack-receipt.js +28 -18
- package/dist/core/release/release-pack-tarball.js +10 -0
- package/dist/core/release/release-upgrade-baseline.js +4 -0
- package/dist/core/routes.js +1 -1
- package/dist/core/safety/ssot-authority-inventory.js +114 -0
- package/dist/core/safety/ssot-guard.js +2 -44
- package/dist/core/subagents/agent-catalog.js +4 -2
- package/dist/core/subagents/official-subagent-config.js +92 -15
- package/dist/core/subagents/official-subagent-preparation.js +1 -0
- package/dist/core/triwiki/context-graph/architecture/baseline.js +81 -0
- package/dist/core/triwiki/context-graph/architecture/capsule.js +34 -0
- package/dist/core/triwiki/context-graph/architecture/contracts.js +67 -0
- package/dist/core/triwiki/context-graph/architecture/delta.js +149 -0
- package/dist/core/triwiki/context-graph/architecture/finding-factory.js +28 -0
- package/dist/core/triwiki/context-graph/architecture/findings.js +173 -0
- package/dist/core/triwiki/context-graph/architecture/fingerprint.js +75 -0
- package/dist/core/triwiki/context-graph/architecture/index.js +15 -0
- package/dist/core/triwiki/context-graph/architecture/input-bundle.js +66 -0
- package/dist/core/triwiki/context-graph/architecture/metrics.js +137 -0
- package/dist/core/triwiki/context-graph/architecture/policy.js +321 -0
- package/dist/core/triwiki/context-graph/architecture/slice.js +100 -0
- package/dist/core/triwiki/context-graph/architecture/ssot-analysis.js +59 -0
- package/dist/core/triwiki/context-graph/architecture/topology-overlay.js +106 -0
- package/dist/core/triwiki/context-graph/architecture/validation.js +145 -0
- package/dist/core/triwiki/context-graph/architecture/voxel-context.js +39 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +4 -0
- package/dist/core/triwiki/context-graph/projections/index.js +2 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/architecture-delta.js +68 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/ast.js +49 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/change-impact.js +43 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/contracts.js +5 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/escape.js +45 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/ids.js +73 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/index.js +113 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/manifest.js +37 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/module-dependency.js +21 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/ownership-workstream.js +21 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/project-topology.js +13 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/public-surface.js +17 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/reduce.js +44 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/risk-domains.js +23 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/runtime-control.js +20 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/selection-ledger.js +94 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/serializer.js +81 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/ssot-provenance.js +17 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/verification-coverage.js +17 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/view-builder-common.js +1 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/view-builder.js +173 -0
- package/dist/core/triwiki/context-graph/projections/mermaid/view-specs.js +33 -0
- package/dist/core/triwiki/context-graph/store/architecture-map-store.js +71 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +26 -1
- package/dist/core/triwiki/triwiki-proof-bank.js +82 -2
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +2 -2
- package/dist/native/sks-menubar/Sources/ProvidersModelExposure.swift +178 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +10 -1
- package/dist/scripts/architecture-map-check.js +299 -0
- package/dist/scripts/lib/ensure-dist-fresh.js +48 -1
- package/dist/scripts/release-metadata-check.js +2 -0
- package/package.json +6 -3
- package/release-gates.v2.json +204 -0
- package/schemas/architecture-baseline.v1.schema.json +47 -0
- package/schemas/architecture-map-manifest.v1.schema.json +40 -0
- package/schemas/architecture-map-policy.v1.schema.json +147 -0
- package/schemas/architecture-review.v1.schema.json +46 -0
- package/schemas/mermaid-projection.v1.schema.json +47 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { SksLruCache } from '../perf/lru-cache.js';
|
|
5
|
+
export const RELEASE_INSPECTION_MEMO_ENV = 'SKS_RELEASE_INSPECTION_MEMO';
|
|
6
|
+
const MAX_ENTRIES_PER_BUCKET = 64;
|
|
7
|
+
const buckets = new Map();
|
|
8
|
+
export function releaseInspectionMemoEnabled() {
|
|
9
|
+
return process.env[RELEASE_INSPECTION_MEMO_ENV] !== '0';
|
|
10
|
+
}
|
|
11
|
+
export function fileIdentity(file) {
|
|
12
|
+
try {
|
|
13
|
+
const stat = fs.lstatSync(file, { bigint: true });
|
|
14
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
15
|
+
return null;
|
|
16
|
+
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function toolIdentity(command) {
|
|
23
|
+
const searchPath = String(process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
24
|
+
for (const dir of searchPath) {
|
|
25
|
+
const candidate = path.join(dir, command);
|
|
26
|
+
let resolved;
|
|
27
|
+
try {
|
|
28
|
+
resolved = fs.realpathSync(candidate);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const identity = fileIdentity(resolved);
|
|
34
|
+
if (identity)
|
|
35
|
+
return `${candidate}>${resolved}#${identity}`;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
export function inspectionKey(...parts) {
|
|
40
|
+
if (parts.some((part) => part === null || part === undefined))
|
|
41
|
+
return null;
|
|
42
|
+
return crypto.createHash('sha256').update(parts.map(String).join('\0')).digest('hex');
|
|
43
|
+
}
|
|
44
|
+
export function memoizeReleaseInspection(bucket, key, compute, clone = defaultClone) {
|
|
45
|
+
if (!key || !releaseInspectionMemoEnabled())
|
|
46
|
+
return compute();
|
|
47
|
+
let cache = buckets.get(bucket);
|
|
48
|
+
if (!cache) {
|
|
49
|
+
cache = new SksLruCache(MAX_ENTRIES_PER_BUCKET);
|
|
50
|
+
buckets.set(bucket, cache);
|
|
51
|
+
}
|
|
52
|
+
const hit = cache.get(key);
|
|
53
|
+
if (hit !== null)
|
|
54
|
+
return clone(hit);
|
|
55
|
+
const value = compute();
|
|
56
|
+
cache.set(key, value);
|
|
57
|
+
return clone(value);
|
|
58
|
+
}
|
|
59
|
+
function defaultClone(value) {
|
|
60
|
+
if (value === null || typeof value !== 'object')
|
|
61
|
+
return value;
|
|
62
|
+
return structuredClone(value);
|
|
63
|
+
}
|
|
64
|
+
export function cloneBuffer(value) {
|
|
65
|
+
return Buffer.from(value);
|
|
66
|
+
}
|
|
67
|
+
export function clearReleaseInspectionMemo() {
|
|
68
|
+
buckets.clear();
|
|
69
|
+
}
|
|
@@ -3,7 +3,12 @@ import fs from 'node:fs';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { fileIdentity, inspectionKey, memoizeReleaseInspection, toolIdentity } from './release-inspection-memo.js';
|
|
6
7
|
export function scanTarballTextContents(input) {
|
|
8
|
+
const key = inspectionKey(input.temp_prefix, input.tarball, fileIdentity(input.tarball), toolIdentity('tar'), String(input.max_findings), input.include_allowlisted_count === true ? 'allowlisted' : 'plain', input.patterns.map((pattern) => `${pattern.kind}=${pattern.regex.source}/${pattern.regex.flags}`).join('\0'), String(input.allow_finding || 'no_allowlist'), String(input.finding_blocker));
|
|
9
|
+
return memoizeReleaseInspection('tarball-content-scan', key, () => computeTarballTextContents(input));
|
|
10
|
+
}
|
|
11
|
+
function computeTarballTextContents(input) {
|
|
7
12
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), input.temp_prefix));
|
|
8
13
|
const findings = [];
|
|
9
14
|
const blockers = [];
|
|
@@ -6,6 +6,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
import { tmpdir } from '../fsx.js';
|
|
7
7
|
import { DEFAULT_MAX_PACK_BYTES, DEFAULT_MAX_UNPACKED_BYTES } from './package-size-budget.js';
|
|
8
8
|
import { readCurrentNpmPackGateArtifacts } from './npm-pack-proof.js';
|
|
9
|
+
import { inspectionKey, memoizeReleaseInspection, toolIdentity } from './release-inspection-memo.js';
|
|
9
10
|
import { scanTarballTextContents } from './release-pack-content-scanner.js';
|
|
10
11
|
import { RELEASE_PACK_COMPARE_SCHEMA, RELEASE_PACK_RECEIPT_SCHEMA } from './release-pack-contract.js';
|
|
11
12
|
import { tarInventory, tarPackageJson, tarUnpackedBytes } from './release-pack-tarball.js';
|
|
@@ -298,12 +299,32 @@ export function inspectLocalReleaseSourceState(root) {
|
|
|
298
299
|
else if (String(fullStatus.stdout || '').split(/\r?\n/).some((line) => line.startsWith('?? '))) {
|
|
299
300
|
blockers.push('npm_pack_untracked_files_present');
|
|
300
301
|
}
|
|
302
|
+
const inventory = memoizeReleaseInspection('npm-pack-source-inventory', inspectionKey(path.resolve(root), head, toolIdentity('git'), toolIdentity('npm'), process.env.SKS_RELEASE_NPM_CACHE || '', String(trackedStatus.status), String(trackedStatus.stdout || ''), String(fullStatus.status), String(fullStatus.stdout || '')), () => packSourceInventory(root));
|
|
303
|
+
const trackedFiles = inventory.tracked_files ? new Set(inventory.tracked_files) : null;
|
|
304
|
+
if (!trackedFiles)
|
|
305
|
+
blockers.push('npm_pack_tracked_file_inventory_unavailable');
|
|
306
|
+
const packedFiles = inventory.packed_files;
|
|
307
|
+
if (!packedFiles)
|
|
308
|
+
blockers.push('npm_pack_dry_run_file_inventory_unavailable');
|
|
309
|
+
const packEligibleUntracked = trackedFiles && packedFiles
|
|
310
|
+
? unique(packedFiles.filter((relative) => !trackedFiles.has(relative) && !isGeneratedDistPackagePath(relative))).sort()
|
|
311
|
+
: [];
|
|
312
|
+
if (packEligibleUntracked.length > 0)
|
|
313
|
+
blockers.push('npm_pack_eligible_untracked_files_present');
|
|
314
|
+
return {
|
|
315
|
+
ok: blockers.length === 0,
|
|
316
|
+
head,
|
|
317
|
+
source_tree_sha256: sourceTreeSha256,
|
|
318
|
+
tracked_changes: trackedChanges,
|
|
319
|
+
pack_eligible_untracked: packEligibleUntracked,
|
|
320
|
+
blockers: unique(blockers)
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function packSourceInventory(root) {
|
|
301
324
|
const trackedFilesResult = spawnSync('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
302
325
|
const trackedFiles = trackedFilesResult.status === 0
|
|
303
|
-
?
|
|
326
|
+
? unique(String(trackedFilesResult.stdout || '').split('\0').filter(Boolean).map(normalizePath))
|
|
304
327
|
: null;
|
|
305
|
-
if (!trackedFiles)
|
|
306
|
-
blockers.push('npm_pack_tracked_file_inventory_unavailable');
|
|
307
328
|
const dryRun = spawnSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], {
|
|
308
329
|
cwd: root,
|
|
309
330
|
encoding: 'utf8',
|
|
@@ -323,21 +344,7 @@ export function inspectLocalReleaseSourceState(root) {
|
|
|
323
344
|
packedFiles = null;
|
|
324
345
|
}
|
|
325
346
|
}
|
|
326
|
-
|
|
327
|
-
blockers.push('npm_pack_dry_run_file_inventory_unavailable');
|
|
328
|
-
const packEligibleUntracked = trackedFiles && packedFiles
|
|
329
|
-
? unique(packedFiles.filter((relative) => !trackedFiles.has(relative) && !isGeneratedDistPackagePath(relative))).sort()
|
|
330
|
-
: [];
|
|
331
|
-
if (packEligibleUntracked.length > 0)
|
|
332
|
-
blockers.push('npm_pack_eligible_untracked_files_present');
|
|
333
|
-
return {
|
|
334
|
-
ok: blockers.length === 0,
|
|
335
|
-
head,
|
|
336
|
-
source_tree_sha256: sourceTreeSha256,
|
|
337
|
-
tracked_changes: trackedChanges,
|
|
338
|
-
pack_eligible_untracked: packEligibleUntracked,
|
|
339
|
-
blockers: unique(blockers)
|
|
340
|
-
};
|
|
347
|
+
return { tracked_files: trackedFiles, packed_files: packedFiles };
|
|
341
348
|
}
|
|
342
349
|
export function writeReleaseJson(file, value) {
|
|
343
350
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -536,6 +543,9 @@ function gitHead(root) {
|
|
|
536
543
|
return result.status === 0 ? String(result.stdout || '').trim() : '';
|
|
537
544
|
}
|
|
538
545
|
function gitTreeSha256(root, commit) {
|
|
546
|
+
return memoizeReleaseInspection('git-tree-sha256', inspectionKey(path.resolve(root), commit, toolIdentity('git')), () => computeGitTreeSha256(root, commit));
|
|
547
|
+
}
|
|
548
|
+
function computeGitTreeSha256(root, commit) {
|
|
539
549
|
if (!/^[a-f0-9]{40}$/i.test(commit))
|
|
540
550
|
return '';
|
|
541
551
|
const verified = spawnSync('git', ['rev-parse', '--verify', `${commit}^{commit}`], { cwd: root, encoding: 'utf8' });
|
|
@@ -2,7 +2,11 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { fileIdentity, inspectionKey, memoizeReleaseInspection, toolIdentity } from './release-inspection-memo.js';
|
|
5
6
|
export function tarInventory(tarball) {
|
|
7
|
+
return memoizeReleaseInspection('tar-inventory', inspectionKey(tarball, fileIdentity(tarball), toolIdentity('tar')), () => computeTarInventory(tarball));
|
|
8
|
+
}
|
|
9
|
+
function computeTarInventory(tarball) {
|
|
6
10
|
const result = spawnSync('tar', ['-tzf', tarball], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
7
11
|
if (result.status !== 0)
|
|
8
12
|
return { files: [], blockers: ['tarball_inventory_command_failed'] };
|
|
@@ -28,6 +32,9 @@ export function tarInventory(tarball) {
|
|
|
28
32
|
return { files, blockers };
|
|
29
33
|
}
|
|
30
34
|
export function tarPackageJson(tarball) {
|
|
35
|
+
return memoizeReleaseInspection('tar-package-json', inspectionKey(tarball, fileIdentity(tarball), toolIdentity('tar')), () => computeTarPackageJson(tarball));
|
|
36
|
+
}
|
|
37
|
+
function computeTarPackageJson(tarball) {
|
|
31
38
|
const result = spawnSync('tar', ['-xOzf', tarball, 'package/package.json'], { encoding: 'utf8', maxBuffer: 1024 * 1024 });
|
|
32
39
|
if (result.status !== 0)
|
|
33
40
|
return null;
|
|
@@ -40,6 +47,9 @@ export function tarPackageJson(tarball) {
|
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
49
|
export function tarUnpackedBytes(tarball) {
|
|
50
|
+
return memoizeReleaseInspection('tar-unpacked-bytes', inspectionKey(tarball, fileIdentity(tarball), toolIdentity('tar')), () => computeTarUnpackedBytes(tarball));
|
|
51
|
+
}
|
|
52
|
+
function computeTarUnpackedBytes(tarball) {
|
|
43
53
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-release-pack-unpacked-'));
|
|
44
54
|
try {
|
|
45
55
|
const result = spawnSync('tar', ['-xzf', tarball, '-C', temp], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
@@ -4,6 +4,10 @@ export const RELEASE_UPGRADE_BASELINE_SHA256 = '40a9e89f3a234dfcd32507ab7deeb950
|
|
|
4
4
|
export function releaseUpgradeProofFilename(targetVersion) {
|
|
5
5
|
return `upgrade-${RELEASE_UPGRADE_BASELINE_LABEL}-to-${targetVersion}.json`;
|
|
6
6
|
}
|
|
7
|
+
export function isExpectedReleaseUpgradeLaunchctlCall(call) {
|
|
8
|
+
return /^(?:unsetenv (?:CODEX_LB_API_KEY|OPENROUTER_API_KEY)|print|bootout (?:com\.sneakoscope\.sks-menubar|com\.sneakoscope\.telegram-hub))$/
|
|
9
|
+
.test(String(call));
|
|
10
|
+
}
|
|
7
11
|
export function releaseUpgradeExpectedVersion(stateName, targetVersion) {
|
|
8
12
|
return stateName.startsWith('target_') ? targetVersion : RELEASE_UPGRADE_BASELINE_VERSION;
|
|
9
13
|
}
|
package/dist/core/routes.js
CHANGED
|
@@ -647,7 +647,7 @@ export const COMMAND_CATALOG = [
|
|
|
647
647
|
{ name: 'check', usage: 'sks check --tier instant|affected|confidence|release|real-check [--sla 5m] [--changed-since auto] [--json]', description: 'Run build-once proof-bank checks: affected/confidence use incremental build and cached proof reuse; release keeps full clean proof for publish readiness.' },
|
|
648
648
|
{ name: 'task', usage: 'sks task run [--sla 5m] [--json]', description: 'Run the normal affected-scope, release-equivalent task verification path.' },
|
|
649
649
|
{ name: 'release', usage: 'sks release affected|full|background|stage [--json]', description: 'Run affected release proof, full release proof, or background release proof explicitly, or drive the staged npm publish up to the human approval step.' },
|
|
650
|
-
{ name: 'triwiki', usage: 'sks triwiki index|affected|proof-bank [--json]', description: 'Inspect TriWiki module cards, gate impact maps, affected graphs,
|
|
650
|
+
{ name: 'triwiki', usage: 'sks triwiki index|affected|proof-bank|graph-status|graph-lint|graph-query|atlas-status|atlas-lint|atlas-list|atlas-show|atlas-why [--json]', description: 'Inspect TriWiki module cards, gate impact maps, affected graphs, proof bank status, and Architecture Map views.' },
|
|
651
651
|
{ name: 'daemon', usage: 'sks daemon status|warm|stop [--json]', description: 'Inspect or warm the local SKS daemon cache state for build/proof reuse.' },
|
|
652
652
|
{ name: 'run', usage: 'sks run "task" [--visual|--research|--db] [--json]', description: 'Classify a plain-language task, materialize a mission, and route it through the SKS trust kernel.' },
|
|
653
653
|
{ name: 'plan', usage: 'sks plan "task" [--json]', description: 'Write a planning-only artifact under .sneakoscope/plans without editing code.' },
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const SKS_SSOT_AUTHORITY_INVENTORY = Object.freeze([
|
|
2
|
+
Object.freeze({
|
|
3
|
+
id: 'route_contract',
|
|
4
|
+
title: 'Route contract',
|
|
5
|
+
authorityKind: 'runtime_owner',
|
|
6
|
+
canonicalSources: Object.freeze([
|
|
7
|
+
'decision-contract.json, route prompt, and pipeline-plan.json'
|
|
8
|
+
]),
|
|
9
|
+
allowedWriters: Object.freeze(['pipeline plan writer', 'request intake']),
|
|
10
|
+
allowedReaders: Object.freeze(['naruto', 'workers', 'stop gate']),
|
|
11
|
+
derivedArtifacts: Object.freeze([
|
|
12
|
+
'subagent-plan.json',
|
|
13
|
+
'subagent-events.jsonl',
|
|
14
|
+
'worker inboxes'
|
|
15
|
+
]),
|
|
16
|
+
protected: true,
|
|
17
|
+
description: 'The sealed user objective, constraints, non-goals, and acceptance criteria define what code may be created.',
|
|
18
|
+
authority: 'The sealed user objective, constraints, non-goals, and acceptance criteria define what code may be created.',
|
|
19
|
+
rule: 'Do not implement behavior outside the sealed route contract; block with evidence if the requested path cannot be honored.'
|
|
20
|
+
}),
|
|
21
|
+
Object.freeze({
|
|
22
|
+
id: 'triwiki_context',
|
|
23
|
+
title: 'TriWiki context pack',
|
|
24
|
+
authorityKind: 'file',
|
|
25
|
+
canonicalSources: Object.freeze(['.sneakoscope/wiki/context-pack.json']),
|
|
26
|
+
allowedWriters: Object.freeze(['sks wiki pack', 'sks align run', 'triwiki refresh']),
|
|
27
|
+
allowedReaders: Object.freeze(['pipeline stages', 'naruto parent', 'honest mode']),
|
|
28
|
+
derivedArtifacts: Object.freeze([
|
|
29
|
+
'subagent-parent-summary.json',
|
|
30
|
+
'subagent-evidence.json',
|
|
31
|
+
'reflection.md'
|
|
32
|
+
]),
|
|
33
|
+
protected: true,
|
|
34
|
+
description: 'TriWiki is the bounded mission context SSOT and must be refreshed or packed, then validated before risky handoffs and final claims.',
|
|
35
|
+
authority: 'TriWiki is the bounded mission context SSOT and must be refreshed or packed, then validated before risky handoffs and final claims.',
|
|
36
|
+
rule: 'Use the latest coordinate+voxel overlay pack; coordinate-only legacy packs are invalid for pipeline decisions.'
|
|
37
|
+
}),
|
|
38
|
+
Object.freeze({
|
|
39
|
+
id: 'runtime_source',
|
|
40
|
+
title: 'TypeScript runtime source',
|
|
41
|
+
authorityKind: 'file',
|
|
42
|
+
canonicalSources: Object.freeze(['src/**/*.ts']),
|
|
43
|
+
allowedWriters: Object.freeze(['implementation workers under sealed route']),
|
|
44
|
+
allowedReaders: Object.freeze(['build', 'tests', 'release gates']),
|
|
45
|
+
derivedArtifacts: Object.freeze(['dist/**', 'dist/bin/sks.js']),
|
|
46
|
+
protected: true,
|
|
47
|
+
description: 'TypeScript source is the runtime SSOT.',
|
|
48
|
+
authority: 'TypeScript source is the runtime SSOT.',
|
|
49
|
+
rule: 'Edit source, rebuild derived output, and rely on runtime:ts-source-of-truth plus runtime:dist-parity.'
|
|
50
|
+
}),
|
|
51
|
+
Object.freeze({
|
|
52
|
+
id: 'generated_outputs',
|
|
53
|
+
title: 'Generated outputs',
|
|
54
|
+
authorityKind: 'generated_projection',
|
|
55
|
+
canonicalSources: Object.freeze([
|
|
56
|
+
'source generators, build scripts, and schema definitions'
|
|
57
|
+
]),
|
|
58
|
+
allowedWriters: Object.freeze(['generators', 'build scripts']),
|
|
59
|
+
allowedReaders: Object.freeze(['release', 'doctor', 'install']),
|
|
60
|
+
derivedArtifacts: Object.freeze([
|
|
61
|
+
'release-gates.v2.json',
|
|
62
|
+
'infra-harness-gates.json',
|
|
63
|
+
'.sneakoscope/reports/**',
|
|
64
|
+
'dist/build-manifest.json'
|
|
65
|
+
]),
|
|
66
|
+
protected: true,
|
|
67
|
+
description: 'Generated files are derived from their generator or schema owner.',
|
|
68
|
+
authority: 'Generated files are derived from their generator or schema owner.',
|
|
69
|
+
rule: 'Regenerate derived artifacts instead of hand-editing them as independent truth.'
|
|
70
|
+
}),
|
|
71
|
+
Object.freeze({
|
|
72
|
+
id: 'stack_current_docs',
|
|
73
|
+
title: 'Stack current docs',
|
|
74
|
+
authorityKind: 'file',
|
|
75
|
+
canonicalSources: Object.freeze(['.sneakoscope/memory/q2_facts/stack-current-docs.md']),
|
|
76
|
+
allowedWriters: Object.freeze(['Context7 hydration', 'docs refresh']),
|
|
77
|
+
allowedReaders: Object.freeze(['implementation', 'research']),
|
|
78
|
+
derivedArtifacts: Object.freeze(['implementation notes', 'route evidence']),
|
|
79
|
+
protected: false,
|
|
80
|
+
description: 'Current vendor or Context7 docs override model-memory defaults when stack versions or APIs change.',
|
|
81
|
+
authority: 'Current vendor or Context7 docs override model-memory defaults when stack versions or APIs change.',
|
|
82
|
+
rule: 'Fetch and record current docs before relying on external package, SDK, API, MCP, or generated-doc behavior that may have changed.'
|
|
83
|
+
}),
|
|
84
|
+
Object.freeze({
|
|
85
|
+
id: 'release_gate_manifest',
|
|
86
|
+
title: 'Release gate manifest',
|
|
87
|
+
authorityKind: 'manifest',
|
|
88
|
+
canonicalSources: Object.freeze([
|
|
89
|
+
'src/core/release/gate-manifest.ts and src/scripts/release-parallel-check.ts'
|
|
90
|
+
]),
|
|
91
|
+
allowedWriters: Object.freeze(['release maintainers via source edit']),
|
|
92
|
+
allowedReaders: Object.freeze(['release DAG', 'publish preflight']),
|
|
93
|
+
derivedArtifacts: Object.freeze([
|
|
94
|
+
'.sneakoscope/reports/release-gate-plan.json',
|
|
95
|
+
'.sneakoscope/reports/gate-policy-audit.json'
|
|
96
|
+
]),
|
|
97
|
+
protected: true,
|
|
98
|
+
description: 'Release gate selection and publish-required status live in the manifest plus the release DAG.',
|
|
99
|
+
authority: 'Release gate selection and publish-required status live in the manifest plus the release DAG.',
|
|
100
|
+
rule: 'Publish-blocking guard gates must appear in the DAG, manifest, and package scripts.'
|
|
101
|
+
})
|
|
102
|
+
]);
|
|
103
|
+
export function canonicalSsotAuthorityInventory() {
|
|
104
|
+
return SKS_SSOT_AUTHORITY_INVENTORY;
|
|
105
|
+
}
|
|
106
|
+
export function inventoryAsSsotGuardSources() {
|
|
107
|
+
return SKS_SSOT_AUTHORITY_INVENTORY.map((domain) => ({
|
|
108
|
+
id: domain.id,
|
|
109
|
+
source: domain.canonicalSources.join(', '),
|
|
110
|
+
authority: domain.authority,
|
|
111
|
+
derived: [...domain.derivedArtifacts],
|
|
112
|
+
rule: domain.rule
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nowIso } from '../fsx.js';
|
|
2
2
|
import { isRecord } from '../json/records.js';
|
|
3
|
+
import { inventoryAsSsotGuardSources } from './ssot-authority-inventory.js';
|
|
3
4
|
export const SSOT_GUARD_SCHEMA = 'sks.ssot-guard.v1';
|
|
4
5
|
export const SSOT_GUARD_ARTIFACT = 'ssot-guard.json';
|
|
5
6
|
export function buildSsotGuard(input = {}) {
|
|
@@ -34,50 +35,7 @@ export function buildSsotGuard(input = {}) {
|
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
export function canonicalSsotSources() {
|
|
37
|
-
return
|
|
38
|
-
{
|
|
39
|
-
id: 'route_contract',
|
|
40
|
-
source: 'decision-contract.json, route prompt, and pipeline-plan.json',
|
|
41
|
-
authority: 'The sealed user objective, constraints, non-goals, and acceptance criteria define what code may be created.',
|
|
42
|
-
derived: ['subagent-plan.json', 'subagent-events.jsonl', 'worker inboxes'],
|
|
43
|
-
rule: 'Do not implement behavior outside the sealed route contract; block with evidence if the requested path cannot be honored.'
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: 'triwiki_context',
|
|
47
|
-
source: '.sneakoscope/wiki/context-pack.json',
|
|
48
|
-
authority: 'TriWiki is the bounded mission context SSOT and must be refreshed or packed, then validated before risky handoffs and final claims.',
|
|
49
|
-
derived: ['subagent-parent-summary.json', 'subagent-evidence.json', 'reflection.md'],
|
|
50
|
-
rule: 'Use the latest coordinate+voxel overlay pack; coordinate-only legacy packs are invalid for pipeline decisions.'
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: 'runtime_source',
|
|
54
|
-
source: 'src/**/*.ts',
|
|
55
|
-
authority: 'TypeScript source is the runtime SSOT.',
|
|
56
|
-
derived: ['dist/**', 'dist/bin/sks.js'],
|
|
57
|
-
rule: 'Edit source, rebuild derived output, and rely on runtime:ts-source-of-truth plus runtime:dist-parity.'
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: 'generated_outputs',
|
|
61
|
-
source: 'source generators, build scripts, and schema definitions',
|
|
62
|
-
authority: 'Generated files are derived from their generator or schema owner.',
|
|
63
|
-
derived: ['release-gates.v2.json', 'infra-harness-gates.json', '.sneakoscope/reports/**', 'dist/build-manifest.json'],
|
|
64
|
-
rule: 'Regenerate derived artifacts instead of hand-editing them as independent truth.'
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
id: 'stack_current_docs',
|
|
68
|
-
source: '.sneakoscope/memory/q2_facts/stack-current-docs.md',
|
|
69
|
-
authority: 'Current vendor or Context7 docs override model-memory defaults when stack versions or APIs change.',
|
|
70
|
-
derived: ['implementation notes', 'route evidence'],
|
|
71
|
-
rule: 'Fetch and record current docs before relying on external package, SDK, API, MCP, or generated-doc behavior that may have changed.'
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
id: 'release_gate_manifest',
|
|
75
|
-
source: 'src/core/release/gate-manifest.ts and src/scripts/release-parallel-check.ts',
|
|
76
|
-
authority: 'Release gate selection and publish-required status live in the manifest plus the release DAG.',
|
|
77
|
-
derived: ['.sneakoscope/reports/release-gate-plan.json', '.sneakoscope/reports/gate-policy-audit.json'],
|
|
78
|
-
rule: 'Publish-blocking guard gates must appear in the DAG, manifest, and package scripts.'
|
|
79
|
-
}
|
|
80
|
-
];
|
|
38
|
+
return inventoryAsSsotGuardSources().map((row) => ({ ...row }));
|
|
81
39
|
}
|
|
82
40
|
export function validateSsotGuardArtifact(value) {
|
|
83
41
|
const issues = [];
|
|
@@ -100,7 +100,8 @@ export function officialSubagentFanoutPolicy(input = {}) {
|
|
|
100
100
|
...(input.suggestedRoles === undefined ? {} : { suggestedRoles: input.suggestedRoles }),
|
|
101
101
|
...(input.independentSliceCount === undefined ? {} : { independentSliceCount: input.independentSliceCount }),
|
|
102
102
|
...(input.maxThreads === undefined ? {} : { maxThreads: input.maxThreads }),
|
|
103
|
-
...(input.hardware === undefined ? {} : { hardware: input.hardware })
|
|
103
|
+
...(input.hardware === undefined ? {} : { hardware: input.hardware }),
|
|
104
|
+
...(input.requiresWrite === undefined ? {} : { requiresWrite: input.requiresWrite })
|
|
104
105
|
});
|
|
105
106
|
const requested = explicit ? explicitRequested : automatic.count;
|
|
106
107
|
const automaticReviewerCeiling = automatic.criticalMultiDomain
|
|
@@ -141,7 +142,8 @@ function automaticSubagentFanout(input) {
|
|
|
141
142
|
const critical = CRITICAL_RISK_RE.test(text);
|
|
142
143
|
const largeScale = LARGE_SCALE_WORK_RE.test(text);
|
|
143
144
|
const criticalMultiDomain = highRisk && critical && riskDomains.length >= 3;
|
|
144
|
-
const reviewerOnly = isReviewerOnlyFanout(input.suggestedRoles || [])
|
|
145
|
+
const reviewerOnly = isReviewerOnlyFanout(input.suggestedRoles || [])
|
|
146
|
+
&& input.requiresWrite !== true;
|
|
145
147
|
const massLane = decideSubagentModel({ description: text }).policy;
|
|
146
148
|
const massParallel = MASS_PARALLEL_WORK_RE.test(text)
|
|
147
149
|
&& !NON_CHEAP_MASS_ACTION_RE.test(text)
|
|
@@ -20,17 +20,23 @@ export const LEGACY_SKS_MAX_THREAD_VALUES = Object.freeze([4, 5, 6, 12]);
|
|
|
20
20
|
export const AGENTS_MAX_CONCURRENT_THREADS_KEY = 'max_concurrent_threads_per_session';
|
|
21
21
|
export const LEGACY_AGENTS_MAX_THREADS_KEY = 'max_threads';
|
|
22
22
|
export const LEGACY_AGENTS_JOB_MAX_RUNTIME_KEY = 'job_max_runtime_seconds';
|
|
23
|
+
export const SKS_MANAGED_CODEX_CONFIG_MARKER = '# SKS-MANAGED-CODEX-CONFIG';
|
|
23
24
|
export function mergeOfficialSubagentConfig(text = '', opts = {}) {
|
|
25
|
+
return mergeOfficialSubagentConfigResult(text, opts).text;
|
|
26
|
+
}
|
|
27
|
+
export function mergeOfficialSubagentConfigResult(text = '', opts = {}) {
|
|
24
28
|
const source = String(text || '');
|
|
25
|
-
if (!inspectToml(source).ok)
|
|
26
|
-
return source;
|
|
29
|
+
if (!inspectToml(source).ok) {
|
|
30
|
+
return { text: source, blockers: ['project_official_subagent_config_toml_parse_failed'] };
|
|
31
|
+
}
|
|
27
32
|
const inherited = parsedToml(opts.inheritedText || '');
|
|
28
33
|
const inheritedAgents = objectValue(inherited?.agents);
|
|
29
34
|
const inheritedFeatures = objectValue(inherited?.features);
|
|
30
35
|
let next = source.trimEnd();
|
|
31
36
|
if (!next.trim())
|
|
32
|
-
next =
|
|
37
|
+
next = SKS_MANAGED_CODEX_CONFIG_MARKER;
|
|
33
38
|
if (opts.sksOwned === true) {
|
|
39
|
+
next = stampManagedConfigMarker(next);
|
|
34
40
|
next = removeExactLegacyManagedAgentBlocks(next);
|
|
35
41
|
next = stripLegacyUnsupportedAgentKeys(next);
|
|
36
42
|
}
|
|
@@ -48,10 +54,12 @@ export function mergeOfficialSubagentConfig(text = '', opts = {}) {
|
|
|
48
54
|
next = mergeOfficialMultiAgentV2FeatureConfig(next, {
|
|
49
55
|
sksOwned: opts.sksOwned === true,
|
|
50
56
|
inheritedFeatures,
|
|
51
|
-
maxThreads: readAgentsMaxThreads(next) || targetMaxThreads
|
|
57
|
+
maxThreads: Math.min(readAgentsMaxThreads(next) || targetMaxThreads, HARD_NARUTO_MAX_THREADS)
|
|
52
58
|
});
|
|
53
59
|
const merged = ensureTrailingNewline(next);
|
|
54
|
-
|
|
60
|
+
if (inspectToml(merged).ok)
|
|
61
|
+
return { text: merged, blockers: [] };
|
|
62
|
+
return { text: source, blockers: ['official_subagent_config_merge_produced_invalid_toml'] };
|
|
55
63
|
}
|
|
56
64
|
export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
|
|
57
65
|
const source = String(text || '');
|
|
@@ -61,18 +69,21 @@ export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
|
|
|
61
69
|
const inheritedMaV2 = featureTomlObject(inheritedFeatures.multi_agent_v2);
|
|
62
70
|
let next = source.trimEnd();
|
|
63
71
|
if (!next.trim())
|
|
64
|
-
next =
|
|
65
|
-
if (
|
|
72
|
+
next = SKS_MANAGED_CODEX_CONFIG_MARKER;
|
|
73
|
+
if (hasUnmanageableMultiAgentV2Declaration(next))
|
|
74
|
+
return ensureTrailingNewline(next);
|
|
75
|
+
const booleanForm = readTomlTableBoolean(next, 'features', 'multi_agent_v2');
|
|
76
|
+
if (booleanForm !== null) {
|
|
66
77
|
next = removeTomlTableKey(next, 'features', 'multi_agent_v2');
|
|
67
78
|
}
|
|
68
79
|
const maxThreads = boundedManagedMaxThreads(opts.maxThreads, DEFAULT_OFFICIAL_SUBAGENT_MAX_THREADS);
|
|
69
80
|
const targetTotal = Math.max(1, maxThreads + 1);
|
|
70
81
|
if (!hasTomlTable(next, 'features.multi_agent_v2')) {
|
|
71
|
-
if (inheritedMaV2)
|
|
82
|
+
if (inheritedMaV2 && booleanForm === null)
|
|
72
83
|
return ensureTrailingNewline(next);
|
|
73
84
|
next = upsertTomlTable(next, 'features.multi_agent_v2', [
|
|
74
85
|
'[features.multi_agent_v2]',
|
|
75
|
-
|
|
86
|
+
`enabled = ${booleanForm === null ? true : booleanForm}`,
|
|
76
87
|
`max_concurrent_threads_per_session = ${targetTotal}`,
|
|
77
88
|
'expose_spawn_agent_model_overrides = true'
|
|
78
89
|
].join('\n'));
|
|
@@ -81,7 +92,7 @@ export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
|
|
|
81
92
|
if (opts.sksOwned === true) {
|
|
82
93
|
next = upsertTomlTableKey(next, 'features.multi_agent_v2', 'enabled = true');
|
|
83
94
|
const currentTotal = readTomlTableInteger(next, 'features.multi_agent_v2', 'max_concurrent_threads_per_session');
|
|
84
|
-
if (currentTotal ===
|
|
95
|
+
if (currentTotal === null || LEGACY_MANAGED_V2_TOTAL_THREADS.includes(currentTotal)) {
|
|
85
96
|
next = upsertTomlTableKey(next, 'features.multi_agent_v2', `max_concurrent_threads_per_session = ${targetTotal}`);
|
|
86
97
|
}
|
|
87
98
|
if (!hasTomlTableKey(next, 'features.multi_agent_v2', 'expose_spawn_agent_model_overrides')) {
|
|
@@ -105,6 +116,10 @@ export function mergeOfficialMultiAgentV2FeatureConfig(text = '', opts = {}) {
|
|
|
105
116
|
return ensureTrailingNewline(next);
|
|
106
117
|
}
|
|
107
118
|
const DEFAULT_NARUTO_LEGACY_TOTAL_THREADS = 13;
|
|
119
|
+
const LEGACY_MANAGED_V2_TOTAL_THREADS = Object.freeze([
|
|
120
|
+
...LEGACY_SKS_MAX_THREAD_VALUES.map((value) => value + 1),
|
|
121
|
+
DEFAULT_NARUTO_LEGACY_TOTAL_THREADS
|
|
122
|
+
]);
|
|
108
123
|
export async function readOfficialSubagentConfig(root, opts = {}) {
|
|
109
124
|
const projectConfigPath = path.resolve(opts.projectConfigPath || path.join(root, '.codex', 'config.toml'));
|
|
110
125
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
@@ -173,6 +188,9 @@ export function officialSubagentConfigWarnings(text = '', inheritedText = '') {
|
|
|
173
188
|
? [`official_subagent_max_depth_coerced_to_one:${maxDepth.value}:${maxDepth.source}`]
|
|
174
189
|
: []),
|
|
175
190
|
...capacityNormalizationWarnings(maxThreads, multiAgentV2),
|
|
191
|
+
...(hasUnmanageableMultiAgentV2Declaration(text)
|
|
192
|
+
? ['project_multi_agent_v2_declaration_form_unmanaged']
|
|
193
|
+
: []),
|
|
176
194
|
...project.legacyWarnings,
|
|
177
195
|
...inherited.legacyWarnings
|
|
178
196
|
];
|
|
@@ -315,6 +333,8 @@ export function officialSubagentConfigOwnershipProof(input = {}) {
|
|
|
315
333
|
}
|
|
316
334
|
if (hasExactManagedConfigMarker(text))
|
|
317
335
|
reasons.push('managed_marker_or_hash');
|
|
336
|
+
if (hasManagedAgentsConfigFingerprint(text))
|
|
337
|
+
reasons.push('managed_agents_config_fingerprint');
|
|
318
338
|
if (migrationReceiptProvesManagedMaxThreads(input.migrationReceipt)) {
|
|
319
339
|
reasons.push('migration_receipt:agents.max_concurrent_threads_per_session');
|
|
320
340
|
}
|
|
@@ -482,7 +502,38 @@ function featureTomlObject(value) {
|
|
|
482
502
|
return Object.keys(row).length ? row : null;
|
|
483
503
|
}
|
|
484
504
|
function hasExactManagedConfigMarker(text) {
|
|
485
|
-
return
|
|
505
|
+
return /^\s*(?:#\s*SKS-MANAGED-CODEX-CONFIG\b|#\s*SKS managed Codex config\b|#\s*sks_managed_(?:body_)?sha256\s*=\s*["'][a-f0-9]{64}["'])/mi.test(text);
|
|
506
|
+
}
|
|
507
|
+
export function stampSksManagedCodexConfigMarker(text) {
|
|
508
|
+
return stampManagedConfigMarker(text);
|
|
509
|
+
}
|
|
510
|
+
function stampManagedConfigMarker(text) {
|
|
511
|
+
const source = String(text || '');
|
|
512
|
+
if (hasExactManagedConfigMarker(source))
|
|
513
|
+
return source;
|
|
514
|
+
const body = source.replace(/^\n+/, '');
|
|
515
|
+
return body ? `${SKS_MANAGED_CODEX_CONFIG_MARKER}\n${body}` : SKS_MANAGED_CODEX_CONFIG_MARKER;
|
|
516
|
+
}
|
|
517
|
+
const MANAGED_AGENTS_FINGERPRINT_KEYS = Object.freeze([
|
|
518
|
+
'enabled',
|
|
519
|
+
'max_depth',
|
|
520
|
+
'interrupt_message',
|
|
521
|
+
'default_subagent_model',
|
|
522
|
+
'default_subagent_reasoning_effort'
|
|
523
|
+
]);
|
|
524
|
+
export function hasManagedAgentsConfigFingerprint(text) {
|
|
525
|
+
const parsed = parsedToml(text);
|
|
526
|
+
if (!parsed)
|
|
527
|
+
return false;
|
|
528
|
+
const agents = objectValue(parsed.agents);
|
|
529
|
+
if (!MANAGED_AGENTS_FINGERPRINT_KEYS.every((key) => hasOwn(agents, key)))
|
|
530
|
+
return false;
|
|
531
|
+
if (readAgentsMaxThreadsFromRecord(agents) === undefined)
|
|
532
|
+
return false;
|
|
533
|
+
if (agents.max_depth !== DEFAULT_OFFICIAL_SUBAGENT_MAX_DEPTH)
|
|
534
|
+
return false;
|
|
535
|
+
const multiAgentV2 = featureTomlObject(objectValue(parsed.features).multi_agent_v2);
|
|
536
|
+
return multiAgentV2 !== null && hasOwn(multiAgentV2, 'expose_spawn_agent_model_overrides');
|
|
486
537
|
}
|
|
487
538
|
function migrationReceiptProvesManagedMaxThreads(value) {
|
|
488
539
|
const root = objectValue(value);
|
|
@@ -676,11 +727,37 @@ function upsertDefaultUnlessInherited(text, inheritedAgents, key, line) {
|
|
|
676
727
|
}
|
|
677
728
|
function readTomlTableInteger(text, table, key) {
|
|
678
729
|
const line = tomlTableKeyLine(text, table, key);
|
|
679
|
-
const
|
|
680
|
-
|
|
730
|
+
const raw = line?.match(/^\s*[^=]+\s*=\s*([^#]+?)\s*(?:#.*)?$/)?.[1];
|
|
731
|
+
return parseTomlInteger(raw);
|
|
732
|
+
}
|
|
733
|
+
function parseTomlInteger(raw) {
|
|
734
|
+
const value = String(raw ?? '').trim().replace(/_/g, '');
|
|
735
|
+
const match = value.match(/^([+-]?)(?:0x([0-9a-fA-F]+)|0o([0-7]+)|0b([01]+)|([0-9]+))$/);
|
|
736
|
+
if (!match)
|
|
681
737
|
return null;
|
|
682
|
-
const
|
|
683
|
-
|
|
738
|
+
const magnitude = match[2] !== undefined
|
|
739
|
+
? Number.parseInt(match[2], 16)
|
|
740
|
+
: match[3] !== undefined
|
|
741
|
+
? Number.parseInt(match[3], 8)
|
|
742
|
+
: match[4] !== undefined
|
|
743
|
+
? Number.parseInt(match[4], 2)
|
|
744
|
+
: Number(match[5]);
|
|
745
|
+
const parsed = (match[1] === '-' ? -1 : 1) * magnitude;
|
|
746
|
+
return Number.isSafeInteger(parsed) ? parsed : null;
|
|
747
|
+
}
|
|
748
|
+
function hasUnmanageableMultiAgentV2Declaration(text) {
|
|
749
|
+
const parsed = parsedToml(text);
|
|
750
|
+
if (!parsed)
|
|
751
|
+
return false;
|
|
752
|
+
if (!hasOwn(objectValue(parsed.features), 'multi_agent_v2'))
|
|
753
|
+
return false;
|
|
754
|
+
return !hasTomlTable(text, 'features.multi_agent_v2')
|
|
755
|
+
&& readTomlTableBoolean(text, 'features', 'multi_agent_v2') === null;
|
|
756
|
+
}
|
|
757
|
+
function readTomlTableBoolean(text, table, key) {
|
|
758
|
+
const line = tomlTableKeyLine(text, table, key);
|
|
759
|
+
const match = line?.match(/^\s*[^=]+\s*=\s*(true|false)\s*(?:#.*)?$/);
|
|
760
|
+
return match?.[1] ? match[1] === 'true' : null;
|
|
684
761
|
}
|
|
685
762
|
function hasTomlTableKey(text, table, key) {
|
|
686
763
|
return tomlTableKeyLine(text, table, key) !== null;
|
|
@@ -107,6 +107,7 @@ async function prepareOfficialSubagentMissionLocked(input) {
|
|
|
107
107
|
taskProfile,
|
|
108
108
|
suggestedRoles: suggestedAgents,
|
|
109
109
|
goal,
|
|
110
|
+
requiresWrite: input.readOnly !== true,
|
|
110
111
|
maxThreads: input.maxThreads ?? officialConfig.maxThreads,
|
|
111
112
|
...(input.hardware ? { hardware: input.hardware } : {}),
|
|
112
113
|
...(slices.length > 0 ? { independentSliceCount: slices.length } : {})
|