sneakoscope 3.1.13 → 4.0.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 +28 -3
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +6 -13
- package/dist/commands/doctor.js +29 -2
- package/dist/commands/proof.js +8 -0
- package/dist/core/agents/agent-cleanup-executor.js +75 -1
- package/dist/core/agents/agent-command-surface.js +29 -4
- package/dist/core/agents/agent-orchestrator.js +25 -6
- package/dist/core/agents/native-cli-session-swarm.js +1 -1
- package/dist/core/build/build-once-runner.js +62 -0
- package/dist/core/codex/codex-config-readability.js +52 -38
- package/dist/core/commands/check-command.js +130 -0
- package/dist/core/commands/daemon-command.js +14 -0
- package/dist/core/commands/local-model-command.js +1 -1
- package/dist/core/commands/mad-sks-command.js +18 -1
- package/dist/core/commands/naruto-command.js +14 -5
- package/dist/core/commands/release-command.js +52 -0
- package/dist/core/commands/task-command.js +15 -0
- package/dist/core/commands/triwiki-command.js +38 -0
- package/dist/core/daemon/sksd-client.js +9 -0
- package/dist/core/daemon/sksd.js +55 -0
- package/dist/core/doctor/doctor-codex-startup-repair.js +3 -2
- package/dist/core/doctor/doctor-dirty-planner.js +30 -0
- package/dist/core/doctor/doctor-transaction.js +13 -0
- package/dist/core/feature-fixtures.js +1 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/init.js +7 -1
- package/dist/core/probes/probe-memoization.js +42 -0
- package/dist/core/release/extreme-parallel-scheduler.js +33 -0
- package/dist/core/release/gate-pack-manifest.js +118 -0
- package/dist/core/release/gate-pack-runner.js +113 -0
- package/dist/core/release/release-gate-cache-v2.js +73 -16
- package/dist/core/release/release-gate-dag.js +81 -2
- package/dist/core/release/resource-class-budget.js +22 -0
- package/dist/core/release/sla-scheduler.js +22 -0
- package/dist/core/routes.js +5 -0
- package/dist/core/triwiki/triwiki-affected-graph.js +56 -0
- package/dist/core/triwiki/triwiki-cache-key.js +221 -0
- package/dist/core/triwiki/triwiki-gate-impact-map.js +79 -0
- package/dist/core/triwiki/triwiki-module-card.js +37 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +132 -0
- package/dist/core/triwiki/triwiki-proof-card.js +42 -0
- package/dist/core/triwiki/triwiki-sla-certificate.js +30 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-worker-pane-manager.js +3 -2
- package/dist/scripts/fixtures/fake-codex-config-loader.js +12 -1
- package/dist/scripts/release-4000-required-gates.js +36 -0
- package/dist/scripts/release-gate-dag-runner.js +18 -0
- package/dist/scripts/release-speed-summary.js +9 -0
- package/package.json +43 -7
package/README.md
CHANGED
|
@@ -35,16 +35,41 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **
|
|
38
|
+
SKS **4.0.0** is the destructive TriWiki Parallel Turbo release. It moves default completion toward affected-scope, release-equivalent proof with reusable TriWiki proof cards, gate packs, SLA scheduling, build-once proof, probe memoization, and explicit legacy-runtime migration instead of silent fallback aliases.
|
|
39
39
|
|
|
40
|
-
What changed in
|
|
40
|
+
What changed in 4.0.0:
|
|
41
|
+
|
|
42
|
+
- **TriWiki proof bank.** Proof cards now bind reusable gate proof to input hash, implementation hash, package lock hash, environment allowlist, fixture version, and tool version.
|
|
43
|
+
- **Affected release-equivalent verification.** Changed files map to module cards, gate impact maps, and gate packs so small tasks can run fast without giving up scoped release confidence.
|
|
44
|
+
- **Parallel SLA planning.** Release gate packs, resource budgets, and SLA certificates expose the planned critical path for five-minute foreground verification.
|
|
45
|
+
- **New control surface.** `sks check`, `sks task`, `sks release`, `sks triwiki`, `sks proof bank status`, and `sks daemon` expose the fast proof pipeline.
|
|
46
|
+
- **No silent legacy fallback.** Compatibility aliases are removed; removed runtimes now use explicit migration notices.
|
|
47
|
+
|
|
48
|
+
SKS **3.1.16** was a launch-reliability patch on the 3.1.15 doctor-reliability release. It made `sks --mad` self-bootstrap a fresh project instead of dead-ending on a missing Codex config.
|
|
49
|
+
|
|
50
|
+
What changed in 3.1.16:
|
|
51
|
+
|
|
52
|
+
- **`sks --mad` bootstraps a fresh project.** When the only preflight blocker is a missing managed Codex config (`.codex/config.toml` absent), `sks --mad` now regenerates it — the `sks doctor --fix` equivalent — and re-runs the preflight, instead of blocking and making you run a separate command. An existing but unreadable/EPERM/parse-broken config still blocks and routes you to `sks doctor --fix`.
|
|
53
|
+
- **Missing-config diagnostics are honest.** A missing config no longer cascades into misleading `macos_acl_ls_le_failed` / `macos_flags_ls_lO_failed` / `spawned_child_read_failed` blockers from running file checks on a nonexistent path; the preflight reports only `missing_config` / `missing_codex_dir`.
|
|
54
|
+
|
|
55
|
+
SKS **3.1.15** was a doctor-reliability patch on the 3.1.14 production-hardening release. It ended the endless `sks doctor --fix` loop that kept reporting `codex_cli_config_toml_parse_error` / `cli_ready: no` on the very run that already repaired the config.
|
|
56
|
+
|
|
57
|
+
What changed in 3.1.15:
|
|
58
|
+
|
|
59
|
+
- **`sks doctor --fix` no longer loops on a config it already fixed.** The Codex config-load probe is re-run *after* the Context7/Supabase/startup MCP repairs land, so the readiness verdict reflects the repaired config instead of the stale pre-repair snapshot.
|
|
60
|
+
- **Context7 is seeded on the remote transport.** Managed setup writes `[mcp_servers.context7]` with the streamable-HTTP `url` instead of a local stdio `command`, so the project config never merges with a remote `url` in the global Codex config into the `url is not supported for stdio` error Codex 0.140 rejects.
|
|
61
|
+
- **The config-load operator action is accurate.** A `codex_cli_config_toml_parse_error` now points at both misplaced machine-local keys *and* the Context7/MCP stdio-vs-`url` transport conflict, instead of only suggesting a key hoist that does nothing for a transport conflict.
|
|
62
|
+
|
|
63
|
+
The 3.1.14 production-hardening surface for Codex 0.140 evidence, transactional `sks doctor --fix` repair, MCP readiness, native capability proof, and protected-secret rollback remains intact.
|
|
64
|
+
|
|
65
|
+
What changed in 3.1.14:
|
|
41
66
|
|
|
42
67
|
- **Codex 0.140 readiness carries evidence.** Capability reports now expose per-feature state and certainty, real usage parsing, goal attachment roundtrip proof, and usage-budget provenance for loop/Naruto runtime decisions.
|
|
43
68
|
- **Doctor repair is phase-based.** `sks doctor --fix` records phase durations, postchecks, optional manual readiness, and rollback evidence instead of collapsing repair work into a summary writer.
|
|
44
69
|
- **Startup and MCP repair are safer.** Managed agent TOML blocks are repaired without touching unrelated config, missing role files are regenerated from real managed templates, Context7 disabled servers stay disabled, and Supabase write scope is separated from read-only readiness.
|
|
45
70
|
- **Secret rollback is line-level when possible.** Protected key changes are restored without discarding unrelated operator edits, nested guard operations are recorded, and backup artifacts remain ignored.
|
|
46
71
|
- **Native capability proof is stricter.** Computer Use and Chrome/web review no longer become verified from environment variables outside explicit fixture/test modes.
|
|
47
|
-
- **Release metadata is aligned for 3.1.
|
|
72
|
+
- **Release metadata is aligned for 3.1.14.** Package, lockfile, CLI version constants, Rust helper metadata, README, changelog, docs, built output, and release stamp all point at the same release.
|
|
48
73
|
|
|
49
74
|
SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
|
|
50
75
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs
|
|
7
|
+
Some("--version") => println!("sks-rs 4.0.0"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
|
@@ -77,6 +77,11 @@ export const COMMANDS = {
|
|
|
77
77
|
help: entry('stable', 'Show SKS help', 'dist/commands/help.js', directCommand(() => import('../commands/help.js'), 'dist/commands/help.js')),
|
|
78
78
|
version: entry('stable', 'Show SKS version', 'dist/commands/version.js', directCommand(() => import('../commands/version.js'), 'dist/commands/version.js')),
|
|
79
79
|
commands: entry('stable', 'List SKS commands', 'dist/core/commands/basic-cli.js', basicArgs('commandsCommand')),
|
|
80
|
+
check: entry('stable', 'Run five-minute proof-bank affected checks', 'dist/core/commands/check-command.js', argsCommand(() => import('../core/commands/check-command.js'), 'checkCommand', 'dist/core/commands/check-command.js')),
|
|
81
|
+
task: entry('stable', 'Run an SLA-bounded SKS task check', 'dist/core/commands/task-command.js', argsCommand(() => import('../core/commands/task-command.js'), 'taskCommand', 'dist/core/commands/task-command.js')),
|
|
82
|
+
release: entry('stable', 'Run affected/full/background release gates', 'dist/core/commands/release-command.js', argsCommand(() => import('../core/commands/release-command.js'), 'releaseCommand', 'dist/core/commands/release-command.js')),
|
|
83
|
+
triwiki: entry('stable', 'Inspect TriWiki index, affected graph, and proof bank', 'dist/core/commands/triwiki-command.js', argsCommand(() => import('../core/commands/triwiki-command.js'), 'triwikiCommand', 'dist/core/commands/triwiki-command.js')),
|
|
84
|
+
daemon: entry('stable', 'Inspect or warm the local SKS daemon cache', 'dist/core/commands/daemon-command.js', argsCommand(() => import('../core/commands/daemon-command.js'), 'daemonCommand', 'dist/core/commands/daemon-command.js')),
|
|
80
85
|
run: entry('beta', 'Classify and execute a task through the SKS trust kernel', 'dist/core/commands/run-command.js', argsCommand(() => import('../core/commands/run-command.js'), 'runCommand', 'dist/core/commands/run-command.js')),
|
|
81
86
|
status: entry('stable', 'Show concise active mission and trust status', 'dist/core/commands/status-command.js', argsCommand(() => import('../core/commands/status-command.js'), 'statusCommand', 'dist/core/commands/status-command.js')),
|
|
82
87
|
root: entry('stable', 'Show active SKS root', 'dist/commands/root.js', directCommand(() => import('../commands/root.js'), 'dist/commands/root.js')),
|
|
@@ -159,19 +164,7 @@ export const COMMANDS = {
|
|
|
159
164
|
bench: entry('beta', 'Run core trust-kernel benchmark budgets', 'dist/core/commands/bench-command.js', argsCommand(() => import('../core/commands/bench-command.js'), 'benchCommand', 'dist/core/commands/bench-command.js'))
|
|
160
165
|
};
|
|
161
166
|
export const TYPED_COMMANDS = COMMANDS;
|
|
162
|
-
export const LEGACY_COMMAND_ALIASES = {
|
|
163
|
-
auth: 'codex-lb',
|
|
164
|
-
mad: 'mad-sks',
|
|
165
|
-
autoreview: 'auto-review',
|
|
166
|
-
dollars: 'dollar-commands',
|
|
167
|
-
'$': 'dollar-commands',
|
|
168
|
-
'ux-review': 'image-ux-review',
|
|
169
|
-
'visual-review': 'image-ux-review',
|
|
170
|
-
'ui-ux-review': 'image-ux-review',
|
|
171
|
-
cu: 'computer-use',
|
|
172
|
-
grok: 'xai',
|
|
173
|
-
memory: 'gc'
|
|
174
|
-
};
|
|
167
|
+
export const LEGACY_COMMAND_ALIASES = {};
|
|
175
168
|
export const COMMAND_ALIASES = {
|
|
176
169
|
...LEGACY_COMMAND_ALIASES,
|
|
177
170
|
'--help': 'help',
|
package/dist/commands/doctor.js
CHANGED
|
@@ -35,6 +35,7 @@ import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-re
|
|
|
35
35
|
import { repairContext7Mcp } from '../core/doctor/context7-mcp-repair.js';
|
|
36
36
|
import { repairSupabaseMcp } from '../core/doctor/supabase-mcp-repair.js';
|
|
37
37
|
import { runDoctorFixTransaction } from '../core/doctor/doctor-transaction.js';
|
|
38
|
+
import { planDoctorDirtyRepair } from '../core/doctor/doctor-dirty-planner.js';
|
|
38
39
|
import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js';
|
|
39
40
|
import { withSecretPreservationGuard } from '../core/config/config-migration-journal.js';
|
|
40
41
|
export async function run(_command, args = []) {
|
|
@@ -143,7 +144,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
143
144
|
const migrationJournal = flag(args, '--fix')
|
|
144
145
|
? await writeFixMigrationJournal(root, migrationPreFix, configRepair, setupRepair).catch(() => null)
|
|
145
146
|
: null;
|
|
146
|
-
|
|
147
|
+
let codexConfig = configRepair?.after || await inspectCodexConfigReadability(root, configProbeOpts);
|
|
147
148
|
const codexDoctor = await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--require-actual-codex') });
|
|
148
149
|
const codexDoctorDiff = compareCodexDoctorBridge(codexDoctorBefore, codexDoctor);
|
|
149
150
|
codexStartupRepair = mergeObservedCodexStartupWarnings(codexStartupRepair, codexDoctor);
|
|
@@ -284,9 +285,22 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
284
285
|
raw_secret_values_recorded: false
|
|
285
286
|
}))
|
|
286
287
|
: null;
|
|
288
|
+
const doctorDirtyPlan = doctorFix
|
|
289
|
+
? planDoctorDirtyRepair(root, [
|
|
290
|
+
'setup',
|
|
291
|
+
'codex_startup_repair',
|
|
292
|
+
'startup_config_repair',
|
|
293
|
+
'context7_repair',
|
|
294
|
+
'context7_mcp_repair',
|
|
295
|
+
'supabase_mcp_repair',
|
|
296
|
+
'command_alias_cleanup',
|
|
297
|
+
'native_capability_repair'
|
|
298
|
+
])
|
|
299
|
+
: null;
|
|
287
300
|
const doctorFixTransaction = doctorFix
|
|
288
301
|
? await runDoctorFixTransaction({
|
|
289
302
|
root,
|
|
303
|
+
dirtyPlan: doctorDirtyPlan,
|
|
290
304
|
phases: [
|
|
291
305
|
{
|
|
292
306
|
id: 'setup',
|
|
@@ -459,6 +473,18 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
459
473
|
blockers: [err?.message || String(err)],
|
|
460
474
|
warnings: []
|
|
461
475
|
}));
|
|
476
|
+
// Re-probe the Codex config AFTER the MCP transport repairs (Context7 remote
|
|
477
|
+
// migration, Supabase read-only, startup config) have landed. `repairCodexConfigEperm`
|
|
478
|
+
// ran its config-load probe ~before~ those repairs, so a config that those repairs
|
|
479
|
+
// fix in THIS run would otherwise keep `codexConfig.ok === false`, making the doctor
|
|
480
|
+
// report `cli_ready: no` / `codex_cli_config_toml_parse_error` on the very run that
|
|
481
|
+
// fixed it — the endless "rerun sks doctor --fix" loop. Only re-probe when the initial
|
|
482
|
+
// probe failed and we are in --fix mode, so healthy configs pay no extra probe cost.
|
|
483
|
+
if (doctorFix && codexConfig?.ok === false) {
|
|
484
|
+
const reinspected = await inspectCodexConfigReadability(root, configProbeOpts).catch(() => null);
|
|
485
|
+
if (reinspected)
|
|
486
|
+
codexConfig = reinspected;
|
|
487
|
+
}
|
|
462
488
|
const pkgBytes = await dirSize(root).catch(() => 0);
|
|
463
489
|
const ready = await writeDoctorReadinessMatrix(root, {
|
|
464
490
|
codex,
|
|
@@ -474,6 +500,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
474
500
|
context7_mcp_repair: context7McpRepair,
|
|
475
501
|
supabase_mcp_repair: supabaseMcpRepair,
|
|
476
502
|
doctor_fix_transaction: doctorFixTransaction,
|
|
503
|
+
doctor_dirty_plan: doctorDirtyPlan,
|
|
477
504
|
doctor_fix_postcheck: doctorFixPostcheck,
|
|
478
505
|
local_model: localModel,
|
|
479
506
|
agent_role_config: agentRoleConfigRepair,
|
|
@@ -540,7 +567,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
540
567
|
ready,
|
|
541
568
|
sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
|
|
542
569
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
543
|
-
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, doctor_transaction: doctorFixTransaction, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
|
|
570
|
+
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, doctor_transaction: doctorFixTransaction, doctor_dirty_plan: doctorDirtyPlan, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
|
|
544
571
|
};
|
|
545
572
|
if (flag(args, '--json')) {
|
|
546
573
|
printJson(result);
|
package/dist/commands/proof.js
CHANGED
|
@@ -9,10 +9,18 @@ import { finalizeRouteWithProof } from '../core/proof/route-finalizer.js';
|
|
|
9
9
|
import { renderProofMarkdown, writeCompletionProof } from '../core/proof/proof-writer.js';
|
|
10
10
|
import { validateCompletionProof } from '../core/proof/validation.js';
|
|
11
11
|
import { buildRuntimeProofSummary, renderRuntimeProofSummary } from '../core/agents/runtime-proof-summary.js';
|
|
12
|
+
import { summarizeTriWikiProofBank } from '../core/triwiki/triwiki-proof-bank.js';
|
|
12
13
|
export async function run(_command, args = []) {
|
|
13
14
|
const root = await projectRoot();
|
|
14
15
|
const action = args[0] || 'show';
|
|
15
16
|
const rest = args.slice(1);
|
|
17
|
+
if (action === 'bank' && rest[0] === 'status') {
|
|
18
|
+
const status = summarizeTriWikiProofBank(root);
|
|
19
|
+
if (flag(args, '--json'))
|
|
20
|
+
return printJson(status);
|
|
21
|
+
console.log(JSON.stringify(status, null, 2));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
16
24
|
if (action === 'latest' && !flag(args, '--completion')) {
|
|
17
25
|
const runtime = await tryRuntimeProofSummary(root);
|
|
18
26
|
if (runtime) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fsp from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { appendJsonl, exists, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { appendJsonl, exists, nowIso, packageRoot, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { drainZellijLaneSupervisor } from './zellij-lane-supervisor.js';
|
|
5
5
|
import { normalizeAgentSessionRows } from './agent-session-rows.js';
|
|
6
|
+
import { closeZellijPaneById } from '../zellij/zellij-worker-pane-manager.js';
|
|
6
7
|
export const AGENT_CLEANUP_PROOF_SCHEMA = 'sks.agent-cleanup-proof.v2';
|
|
7
8
|
export const AGENT_CLEANUP_ACTION_LEDGER_SCHEMA = 'sks.agent-cleanup-action-ledger.v1';
|
|
8
9
|
const TERMINAL_STATUSES = new Set(['closed', 'completed', 'done', 'failed', 'blocked', 'killed', 'timed_out']);
|
|
@@ -50,6 +51,7 @@ export async function runAgentCleanupExecutor(opts) {
|
|
|
50
51
|
killEscalation
|
|
51
52
|
}));
|
|
52
53
|
}
|
|
54
|
+
const seenZellijPaneIds = new Set();
|
|
53
55
|
const zellijReports = await listNamedFiles(path.join(agentRoot, 'sessions'), 'agent-zellij-report.json');
|
|
54
56
|
for (const file of zellijReports) {
|
|
55
57
|
const report = await readJson(file, null);
|
|
@@ -57,6 +59,7 @@ export async function runAgentCleanupExecutor(opts) {
|
|
|
57
59
|
const sessionId = String(report?.session_id || '');
|
|
58
60
|
if (!paneId)
|
|
59
61
|
continue;
|
|
62
|
+
seenZellijPaneIds.add(paneId);
|
|
60
63
|
if (!processReportInNamespace(report, projectHash)) {
|
|
61
64
|
actions.push({ kind: 'skip_foreign_namespace', target: paneId, status: 'skipped', reason: 'zellij_pane_outside_project_namespace' });
|
|
62
65
|
continue;
|
|
@@ -77,6 +80,77 @@ export async function runAgentCleanupExecutor(opts) {
|
|
|
77
80
|
}
|
|
78
81
|
}));
|
|
79
82
|
}
|
|
83
|
+
const workerPaneReports = await listNamedFiles(path.join(agentRoot, 'sessions'), 'zellij-worker-pane.json');
|
|
84
|
+
for (const file of workerPaneReports) {
|
|
85
|
+
const report = await readJson(file, null);
|
|
86
|
+
const paneId = String(report?.pane_id || '');
|
|
87
|
+
const sessionName = String(report?.session_name || '');
|
|
88
|
+
const sessionId = String(report?.session_id || '');
|
|
89
|
+
const status = String(report?.status || '');
|
|
90
|
+
if (!paneId || !sessionName || seenZellijPaneIds.has(paneId))
|
|
91
|
+
continue;
|
|
92
|
+
seenZellijPaneIds.add(paneId);
|
|
93
|
+
if (opts.missionId && report?.mission_id && String(report.mission_id) !== String(opts.missionId)) {
|
|
94
|
+
actions.push({ kind: 'skip_foreign_namespace', target: paneId, status: 'skipped', reason: 'zellij_pane_wrong_mission' });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const terminal = TERMINAL_STATUSES.has(status) || Boolean(report?.closed_at);
|
|
98
|
+
if (activeSessionIds.has(sessionId) && !terminal && opts.drain !== true) {
|
|
99
|
+
actions.push({ kind: 'skip_active_session', target: sessionId || paneId, status: 'skipped', reason: 'managed_zellij_worker_active' });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
actions.push(await applyAction({
|
|
103
|
+
kind: 'close_zellij_pane',
|
|
104
|
+
target: paneId,
|
|
105
|
+
reason: terminal ? 'managed_worker_zellij_pane_terminal' : 'managed_worker_zellij_pane_stale',
|
|
106
|
+
apply,
|
|
107
|
+
before: async () => ({ listed: true, pane_id: paneId, session_name: sessionName, source: path.relative(agentRoot, file), status }),
|
|
108
|
+
after: async () => ({ listed: false, pane_id: paneId, session_name: sessionName }),
|
|
109
|
+
run: async () => {
|
|
110
|
+
const close = await closeZellijPaneById(sessionName, paneId, packageRoot());
|
|
111
|
+
if (close && close.ok !== true)
|
|
112
|
+
throw new Error(close.blockers.join(',') || close.stderr_tail || 'zellij_pane_close_failed');
|
|
113
|
+
}
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
const rightColumnReports = await listNamedFiles(opts.missionDir, 'zellij-right-column-state.json');
|
|
117
|
+
for (const file of rightColumnReports) {
|
|
118
|
+
const report = await readJson(file, null);
|
|
119
|
+
const sessionName = String(report?.session_name || '');
|
|
120
|
+
if (!sessionName)
|
|
121
|
+
continue;
|
|
122
|
+
const visibleActive = (Array.isArray(report?.visible_worker_panes) ? report.visible_worker_panes : [])
|
|
123
|
+
.some((pane) => pane?.status === 'launching' || pane?.status === 'running');
|
|
124
|
+
const headlessActive = (Array.isArray(report?.headless_workers) ? report.headless_workers : [])
|
|
125
|
+
.some((row) => !row?.status || row.status === 'running');
|
|
126
|
+
const anchorPaneIds = [...new Set([
|
|
127
|
+
report?.slot_column_anchor_pane_id,
|
|
128
|
+
report?.right_anchor_pane_id,
|
|
129
|
+
report?.dashboard_pane_id
|
|
130
|
+
].map(String).filter((paneId) => paneId && paneId !== 'null' && paneId !== 'undefined'))];
|
|
131
|
+
for (const paneId of anchorPaneIds) {
|
|
132
|
+
if (seenZellijPaneIds.has(paneId))
|
|
133
|
+
continue;
|
|
134
|
+
seenZellijPaneIds.add(paneId);
|
|
135
|
+
if ((visibleActive || headlessActive) && opts.drain !== true) {
|
|
136
|
+
actions.push({ kind: 'skip_active_session', target: paneId, status: 'skipped', reason: 'zellij_right_column_anchor_active' });
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
actions.push(await applyAction({
|
|
140
|
+
kind: 'close_zellij_pane',
|
|
141
|
+
target: paneId,
|
|
142
|
+
reason: 'zellij_right_column_anchor_terminal',
|
|
143
|
+
apply,
|
|
144
|
+
before: async () => ({ listed: true, pane_id: paneId, session_name: sessionName, source: path.relative(opts.missionDir, file), visible_active: visibleActive, headless_active: headlessActive }),
|
|
145
|
+
after: async () => ({ listed: false, pane_id: paneId, session_name: sessionName }),
|
|
146
|
+
run: async () => {
|
|
147
|
+
const close = await closeZellijPaneById(sessionName, paneId, packageRoot());
|
|
148
|
+
if (close && close.ok !== true)
|
|
149
|
+
throw new Error(close.blockers.join(',') || close.stderr_tail || 'zellij_anchor_close_failed');
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
80
154
|
for (const dir of Array.isArray(namespace?.orphan_temp_dirs) ? namespace.orphan_temp_dirs.map(String) : []) {
|
|
81
155
|
if (!namespaceOwnsPath(dir, projectHash)) {
|
|
82
156
|
actions.push({ kind: 'skip_foreign_namespace', target: dir, status: 'skipped', reason: 'path_outside_project_namespace' });
|
|
@@ -12,10 +12,19 @@ export function parseAgentCommandArgs(command, args = []) {
|
|
|
12
12
|
const minimumWorkItems = Number(readOption(args, '--minimum-work-items', targetActiveSlots));
|
|
13
13
|
const maxQueueExpansion = Number(readOption(args, '--max-queue-expansion', 10));
|
|
14
14
|
const concurrency = Number(readOption(args, '--concurrency', Math.min(agents, 5)));
|
|
15
|
-
const
|
|
15
|
+
const useOllamaProtocol = hasFlag(args, '--ollama');
|
|
16
|
+
const useLocalModel = hasFlag(args, '--local-model');
|
|
17
|
+
const useOllama = useOllamaProtocol || useLocalModel;
|
|
16
18
|
const noOllama = hasFlag(args, '--no-ollama') || hasFlag(args, '--no-local-model');
|
|
17
|
-
const backendExplicit = hasOption(args, '--backend');
|
|
18
|
-
const
|
|
19
|
+
const backendExplicit = hasOption(args, '--backend') || useOllamaProtocol || useLocalModel || noOllama;
|
|
20
|
+
const defaultBackend = hasFlag(args, '--mock')
|
|
21
|
+
? 'fake'
|
|
22
|
+
: useLocalModel && !noOllama
|
|
23
|
+
? 'local-llm'
|
|
24
|
+
: useOllamaProtocol && !noOllama
|
|
25
|
+
? 'ollama'
|
|
26
|
+
: 'codex-sdk';
|
|
27
|
+
const backend = String(readOption(args, '--backend', defaultBackend));
|
|
19
28
|
const route = String(readOption(args, '--route', '$Agent'));
|
|
20
29
|
const mock = hasFlag(args, '--mock') || backend === 'fake';
|
|
21
30
|
const real = hasFlag(args, '--real');
|
|
@@ -34,7 +43,7 @@ export function parseAgentCommandArgs(command, args = []) {
|
|
|
34
43
|
const zellijSessionName = String(readOption(args, '--zellij-session-name', '') || '') || null;
|
|
35
44
|
const zellijPaneWorker = hasFlag(args, '--no-zellij-pane-worker') ? false : hasFlag(args, '--zellij-pane-worker') ? true : undefined;
|
|
36
45
|
const workerPlacement = String(readOption(args, '--worker-placement', zellijPaneWorker === true ? 'zellij-pane' : '') || '') || undefined;
|
|
37
|
-
const zellijVisiblePaneCap =
|
|
46
|
+
const zellijVisiblePaneCap = resolveZellijVisiblePaneCap(readOption(args, '--zellij-visible-pane-cap', process.env.SKS_ZELLIJ_VISIBLE_PANE_CAP || ''), hasOption(args, '--zellij-visible-pane-cap') || Boolean(process.env.SKS_ZELLIJ_VISIBLE_PANE_CAP));
|
|
38
47
|
const apply = hasFlag(args, '--apply');
|
|
39
48
|
const dryRun = hasFlag(args, '--dry-run') || hasFlag(args, '--dryrun');
|
|
40
49
|
const drain = hasFlag(args, '--drain');
|
|
@@ -52,6 +61,22 @@ export function parseAgentCommandArgs(command, args = []) {
|
|
|
52
61
|
const prompt = promptPositionals.join(' ').trim() || 'Native agent run';
|
|
53
62
|
return { command, action, prompt, route, agents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency, backend, backendExplicit, mock, real, readonly, profile, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, zellijSessionName, zellijPaneWorker, workerPlacement, zellijVisiblePaneCap, apply, dryRun, drain, staleMs, graceMs, killEscalation, json, missionId, lane, codexApp, patchEntryId };
|
|
54
63
|
}
|
|
64
|
+
export function resolveZellijVisiblePaneCap(value = '', explicit = false) {
|
|
65
|
+
const requested = Number(value);
|
|
66
|
+
if (explicit && Number.isFinite(requested) && requested >= 1)
|
|
67
|
+
return Math.max(1, Math.floor(requested));
|
|
68
|
+
const columns = Number(process.env.SKS_ZELLIJ_TERMINAL_COLUMNS || process.env.COLUMNS || process.stdout?.columns || 0);
|
|
69
|
+
const unknownFallback = Number(process.env.SKS_ZELLIJ_UNKNOWN_VISIBLE_PANE_CAP || 3);
|
|
70
|
+
if (!Number.isFinite(columns) || columns < 120) {
|
|
71
|
+
return Math.max(1, Math.floor(Number.isFinite(unknownFallback) ? unknownFallback : 3));
|
|
72
|
+
}
|
|
73
|
+
const reservedColumns = Number(process.env.SKS_ZELLIJ_RESERVED_COLUMNS || 108);
|
|
74
|
+
const minWorkerColumns = Number(process.env.SKS_ZELLIJ_MIN_WORKER_PANE_COLUMNS || 72);
|
|
75
|
+
const maxAutoVisible = Number(process.env.SKS_ZELLIJ_MAX_AUTO_VISIBLE_PANES || 8);
|
|
76
|
+
const available = Math.max(0, columns - Math.max(80, reservedColumns));
|
|
77
|
+
const computed = Math.floor(available / Math.max(40, minWorkerColumns));
|
|
78
|
+
return Math.max(1, Math.min(Math.max(1, Math.floor(maxAutoVisible || 8)), computed || Math.floor(unknownFallback) || 3));
|
|
79
|
+
}
|
|
55
80
|
function hasFlag(args, flag) {
|
|
56
81
|
return args.includes(flag);
|
|
57
82
|
}
|
|
@@ -1676,7 +1676,8 @@ async function runAgentByBackend(backend, agent, slice, opts) {
|
|
|
1676
1676
|
return runProcessAgent(agent, slice, opts);
|
|
1677
1677
|
if (backend === 'ollama')
|
|
1678
1678
|
return runOllamaAgent(agent, slice, opts);
|
|
1679
|
-
if (backend === 'codex-sdk' || backend === 'zellij') {
|
|
1679
|
+
if (backend === 'codex-sdk' || backend === 'zellij' || backend === 'local-llm') {
|
|
1680
|
+
const localPreferred = backend === 'local-llm';
|
|
1680
1681
|
const ledgerRoot = path.resolve(opts.agentRoot || opts.cwd || process.cwd());
|
|
1681
1682
|
const workerDir = path.join(ledgerRoot, 'codex-sdk-workers', String(agent.session_id || agent.id || 'agent'), String(slice?.id || 'slice'));
|
|
1682
1683
|
const writePaths = sdkWritePaths(slice, opts);
|
|
@@ -1703,6 +1704,9 @@ async function runAgentByBackend(backend, agent, slice, opts) {
|
|
|
1703
1704
|
user_confirmed_full_access: false,
|
|
1704
1705
|
mad_sks_authorized: opts.madSksAuthorized === true || process.env.SKS_MAD_SKS_ACTIVE === '1'
|
|
1705
1706
|
},
|
|
1707
|
+
backendPreference: localPreferred ? ['local-llm', 'codex-sdk'] : ['codex-sdk'],
|
|
1708
|
+
allowLocalLlm: localPreferred,
|
|
1709
|
+
...(localPreferred ? { localLlmPolicy: { mode: 'local_preferred', requiresGptFinal: true } } : {}),
|
|
1706
1710
|
mutationLedgerRoot: workerDir,
|
|
1707
1711
|
zellijPaneId: null
|
|
1708
1712
|
});
|
|
@@ -1710,9 +1714,11 @@ async function runAgentByBackend(backend, agent, slice, opts) {
|
|
|
1710
1714
|
const patchEnvelopes = normalizeDirectSdkPatchEnvelopes(sdkWorkerResult?.patch_envelopes || [], agent, opts, sdkTask.sdkThreadId);
|
|
1711
1715
|
const sdkReport = {
|
|
1712
1716
|
schema: 'sks.codex-sdk-worker-adapter.v1',
|
|
1713
|
-
backend: 'codex-sdk',
|
|
1717
|
+
backend: sdkTask.backend === 'local-llm' ? 'local-llm' : 'codex-sdk',
|
|
1718
|
+
backend_family: sdkTask.backend_family,
|
|
1714
1719
|
sdk_thread_id: sdkTask.sdkThreadId,
|
|
1715
1720
|
sdk_run_id: sdkTask.sdkRunId,
|
|
1721
|
+
local_llm_proof_path: sdkTask.localLlmProofPath || null,
|
|
1716
1722
|
stream_event_count: sdkTask.streamEventCount,
|
|
1717
1723
|
structured_output_valid: sdkTask.structuredOutputValid,
|
|
1718
1724
|
worker_result_path: path.relative(ledgerRoot, sdkTask.workerResultPath),
|
|
@@ -1723,18 +1729,31 @@ async function runAgentByBackend(backend, agent, slice, opts) {
|
|
|
1723
1729
|
};
|
|
1724
1730
|
return validateAgentWorkerResult({
|
|
1725
1731
|
...sdkWorkerResult,
|
|
1726
|
-
backend: 'codex-sdk',
|
|
1732
|
+
backend: sdkTask.backend === 'local-llm' ? 'local-llm' : 'codex-sdk',
|
|
1727
1733
|
patch_envelopes: patchEnvelopes,
|
|
1728
1734
|
...(patchEnvelopes.length ? {} : { no_patch_reason: buildDirectNoPatchReason(slice, opts) }),
|
|
1729
1735
|
codex_child_report: sdkReport,
|
|
1730
1736
|
codex_sdk_thread: sdkReport,
|
|
1731
1737
|
model_authored_patch_envelopes: patchEnvelopes.length > 0,
|
|
1732
1738
|
fixture_patch_envelopes: false,
|
|
1733
|
-
artifacts: [...new Set([
|
|
1739
|
+
artifacts: [...new Set([
|
|
1740
|
+
...(sdkWorkerResult?.artifacts || []),
|
|
1741
|
+
path.relative(ledgerRoot, sdkTask.workerResultPath),
|
|
1742
|
+
path.join(path.relative(ledgerRoot, workerDir), 'codex-control-proof.json'),
|
|
1743
|
+
path.join(path.relative(ledgerRoot, workerDir), 'codex-thread-registry.json'),
|
|
1744
|
+
path.join(path.relative(ledgerRoot, workerDir), sdkTask.backend === 'local-llm' ? 'local-llm-events.jsonl' : 'codex-sdk-events.jsonl'),
|
|
1745
|
+
...(sdkTask.localLlmProofPath ? [path.relative(ledgerRoot, sdkTask.localLlmProofPath)] : [])
|
|
1746
|
+
])],
|
|
1734
1747
|
blockers: [...(sdkWorkerResult?.blockers || []), ...sdkTask.blockers],
|
|
1735
1748
|
verification: {
|
|
1736
1749
|
status: sdkTask.ok ? 'passed' : 'failed',
|
|
1737
|
-
checks: [
|
|
1750
|
+
checks: [
|
|
1751
|
+
...(sdkWorkerResult?.verification?.checks || []),
|
|
1752
|
+
sdkTask.backend === 'local-llm' ? 'local-llm-control-plane' : 'codex-sdk-control-plane',
|
|
1753
|
+
sdkTask.backend === 'local-llm' ? 'local-llm-event-stream' : 'codex-sdk-event-stream',
|
|
1754
|
+
sdkTask.backend === 'local-llm' ? 'local-llm-structured-output' : 'codex-sdk-structured-output',
|
|
1755
|
+
...(sdkTask.backend === 'local-llm' ? ['gpt-final-required-before-acceptance'] : [])
|
|
1756
|
+
]
|
|
1738
1757
|
}
|
|
1739
1758
|
});
|
|
1740
1759
|
}
|
|
@@ -1753,7 +1772,7 @@ async function maybeSelectOllamaBackend(backend, agent, slice, opts) {
|
|
|
1753
1772
|
if (!config?.ok || config.enabled !== true)
|
|
1754
1773
|
return backend;
|
|
1755
1774
|
const policy = classifyOllamaWorkerSlice(slice, { route: opts.route, agent });
|
|
1756
|
-
return policy.ok ? '
|
|
1775
|
+
return policy.ok ? 'local-llm' : backend;
|
|
1757
1776
|
}
|
|
1758
1777
|
function buildDirectSdkWorkerPrompt(slice) {
|
|
1759
1778
|
const write = sdkWritePaths(slice, {});
|
|
@@ -66,7 +66,7 @@ class NativeCliSessionSwarmRecorder {
|
|
|
66
66
|
patch_envelope_path: patchRel,
|
|
67
67
|
service_tier: this.input.fastModePolicy.service_tier,
|
|
68
68
|
fast_mode: this.input.fastModePolicy.fast_mode,
|
|
69
|
-
ollama_enabled: ctx.opts.ollamaEnabled === true || this.input.backend === 'ollama',
|
|
69
|
+
ollama_enabled: ctx.opts.ollamaEnabled === true || this.input.backend === 'ollama' || this.input.backend === 'local-llm',
|
|
70
70
|
ollama_model: ctx.opts.ollamaModel || null,
|
|
71
71
|
ollama_base_url: ctx.opts.ollamaBaseUrl || null,
|
|
72
72
|
source_intelligence_refs: ctx.agent.source_intelligence_refs || null,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { computeTriWikiCacheKey } from '../triwiki/triwiki-cache-key.js';
|
|
5
|
+
export const BUILD_ONCE_PROOF_SCHEMA = 'sks.build-once-proof.v1';
|
|
6
|
+
export function runBuildOnce(input) {
|
|
7
|
+
const mode = input.mode || 'incremental';
|
|
8
|
+
const key = computeTriWikiCacheKey({
|
|
9
|
+
root: input.root,
|
|
10
|
+
id: `build-once:${mode}`,
|
|
11
|
+
inputs: ['src/**/*.ts', 'package.json', 'package-lock.json', 'tsconfig.json'],
|
|
12
|
+
implementationFiles: ['tsconfig.json'],
|
|
13
|
+
envAllowlist: ['NODE_ENV', 'CI'],
|
|
14
|
+
fixtureVersion: 'sks-4.0.0'
|
|
15
|
+
});
|
|
16
|
+
const existing = readBuildOnceProof(input.root);
|
|
17
|
+
if (!input.force && existing?.ok === true && existing.cache_key === key.key) {
|
|
18
|
+
return { ...existing, reused: true };
|
|
19
|
+
}
|
|
20
|
+
const started = Date.now();
|
|
21
|
+
const run = spawnSync('npm', ['run', mode === 'clean' ? 'build:clean' : 'build:incremental', '--silent'], {
|
|
22
|
+
cwd: input.root,
|
|
23
|
+
encoding: 'utf8',
|
|
24
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
25
|
+
env: { ...process.env, CI: process.env.CI || 'true' }
|
|
26
|
+
});
|
|
27
|
+
const proof = {
|
|
28
|
+
schema: BUILD_ONCE_PROOF_SCHEMA,
|
|
29
|
+
ok: run.status === 0,
|
|
30
|
+
mode,
|
|
31
|
+
cache_key: key.key,
|
|
32
|
+
created_at: new Date().toISOString(),
|
|
33
|
+
status: run.status,
|
|
34
|
+
duration_ms: Math.max(0, Date.now() - started),
|
|
35
|
+
reused: false,
|
|
36
|
+
blockers: run.status === 0 ? [] : [`build_failed:${run.status ?? 'signal'}`]
|
|
37
|
+
};
|
|
38
|
+
writeBuildOnceProof(input.root, proof);
|
|
39
|
+
return proof;
|
|
40
|
+
}
|
|
41
|
+
export function readBuildOnceProof(root) {
|
|
42
|
+
const file = buildProofPath(root);
|
|
43
|
+
try {
|
|
44
|
+
if (!fs.existsSync(file))
|
|
45
|
+
return null;
|
|
46
|
+
const json = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
47
|
+
return json.schema === BUILD_ONCE_PROOF_SCHEMA ? json : null;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function writeBuildOnceProof(root, proof) {
|
|
54
|
+
const file = buildProofPath(root);
|
|
55
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
56
|
+
fs.writeFileSync(file, `${JSON.stringify(proof, null, 2)}\n`);
|
|
57
|
+
return file;
|
|
58
|
+
}
|
|
59
|
+
function buildProofPath(root) {
|
|
60
|
+
return path.join(root, 'dist', '.sks-build-proof.json');
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=build-once-runner.js.map
|