sneakoscope 1.21.2 → 1.21.4

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.
Files changed (44) hide show
  1. package/README.md +3 -3
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +6 -6
  8. package/dist/cli/install-helpers.d.ts +1 -1
  9. package/dist/cli/install-helpers.js +17 -8
  10. package/dist/commands/image-ux-review.d.ts +3 -0
  11. package/dist/commands/mad-sks.d.ts +1 -0
  12. package/dist/commands/ppt.d.ts +3 -0
  13. package/dist/core/agents/agent-gate.d.ts +1 -0
  14. package/dist/core/agents/agent-gate.js +14 -2
  15. package/dist/core/agents/agent-orchestrator.d.ts +3 -0
  16. package/dist/core/agents/agent-orchestrator.js +15 -1
  17. package/dist/core/agents/agent-proof-evidence.d.ts +2 -0
  18. package/dist/core/agents/agent-proof-evidence.js +3 -0
  19. package/dist/core/agents/agent-roster.d.ts +5 -0
  20. package/dist/core/agents/agent-roster.js +33 -7
  21. package/dist/core/agents/agent-schema.d.ts +1 -0
  22. package/dist/core/agents/route-collaboration-ledger.d.ts +3 -0
  23. package/dist/core/agents/zellij-lane-supervisor.js +7 -3
  24. package/dist/core/agents/zellij-right-lane-cockpit.js +1 -1
  25. package/dist/core/commands/fast-mode-command.d.ts +51 -0
  26. package/dist/core/commands/fast-mode-command.js +5 -0
  27. package/dist/core/commands/image-ux-review-command.d.ts +3 -0
  28. package/dist/core/commands/mad-sks-command.d.ts +1 -0
  29. package/dist/core/commands/naruto-command.js +44 -22
  30. package/dist/core/commands/ppt-command.d.ts +3 -0
  31. package/dist/core/commands/team-command.js +10 -7
  32. package/dist/core/fsx.d.ts +1 -1
  33. package/dist/core/fsx.js +1 -1
  34. package/dist/core/proof/auto-finalize.js +24 -20
  35. package/dist/core/version.d.ts +1 -1
  36. package/dist/core/version.js +1 -1
  37. package/dist/core/zellij/zellij-clipboard-config.d.ts +2 -0
  38. package/dist/core/zellij/zellij-clipboard-config.js +9 -7
  39. package/dist/core/zellij/zellij-lane-renderer.js +33 -3
  40. package/dist/core/zellij/zellij-launcher.d.ts +3 -0
  41. package/dist/core/zellij/zellij-launcher.js +1 -0
  42. package/dist/core/zellij/zellij-layout-builder.js +10 -2
  43. package/dist/scripts/release-parallel-check.js +1 -1
  44. package/package.json +4 -3
package/README.md CHANGED
@@ -16,11 +16,11 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
- SKS **1.21.2** fixes the `sks --mad` Zellij launch regression from 1.21.1: Zellij rejects `--copy-command` when it is paired with the OSC52-only `--copy-clipboard` flag, so SKS now passes only `--copy-command pbcopy` and `--copy-on-select true` on the launch CLI while keeping the generated clipboard config for attach-time behavior. It carries forward the 1.21.1 launch-speed and Codex legacy-profile fixes.
19
+ SKS **1.21.4** makes Fast mode state and Naruto clone activity directly visible in SKS Zellij lanes. The lane renderer now resolves the project-local `sks fast-mode on|off` preference even before worker scheduler artifacts arrive, and Naruto launches the right-side Zellij lane stack before clone scheduling starts so each clone slot can show live activity. Naruto's host-capacity model also no longer collapses `codex-exec` concurrency to one slot on capable Macs just because `freemem` is low; use `--concurrency` / `--target-active-slots` or `SKS_NARUTO_MAX_CONCURRENCY` for explicit operator control. SKS-launched interactive Codex panes also use `--no-alt-screen` by default so Mac trackpad/wheel gestures scroll the terminal conversation history instead of the prompt textarea/history; set `SKS_ZELLIJ_CODEX_ALT_SCREEN=1` before launch to opt back into alternate-screen mode. It carries forward the 1.21.3 Zellij clipboard, visual lane count, direct-publish stamp repair, and Codex Fast mode repair fixes.
20
20
 
21
21
  SKS **1.20.4** is a targeted `sks --mad` / codex-lb Zellij usability patch: when a background MAD Zellij session launches successfully, SKS now prints the exact `Attach with: ZELLIJ_SOCKET_DIR=... zellij attach ...` command so operators can enter the fresh session without manually reconstructing the socket namespace.
22
22
 
23
- SKS **1.20.3** added the macOS Zellij launch fallback and project-local Fast mode control. SKS supplies a short per-user `ZELLIJ_SOCKET_DIR` by default, caps generated session names safely, records `*_command_with_env` attach commands, and classifies `IPC socket path is too long` as `zellij_socket_path_too_long` instead of a generic launch failure. It also adds `sks fast-mode on|off|status|clear`, `$Fast-On`, `$Fast-Off`, and `$Fast-Mode`; saved project preferences are used only when no explicit `--fast`, `--no-fast`, or `--service-tier` flag is present.
23
+ SKS **1.20.3** added the macOS Zellij launch fallback and project-local Fast mode control. SKS supplies a short per-user `ZELLIJ_SOCKET_DIR` by default, caps generated session names safely, records `*_command_with_env` attach commands, and classifies `IPC socket path is too long` as `zellij_socket_path_too_long` instead of a generic launch failure. It also adds `sks fast-mode on|off|status|clear`, `$Fast-On`, `$Fast-Off`, and `$Fast-Mode`; saved project preferences are used only when no explicit `--fast`, `--no-fast`, or `--service-tier` flag is present. In 1.21.3 and newer, the explicit `on` action also restores Codex App/CLI Fast mode defaults in `~/.codex/config.toml` when they were disabled.
24
24
 
25
25
  It carries forward the **1.20.2** stabilization layer: **Mutation Guard** routes genuinely-risky global/config/permission/package mutations through the Requested-Scope Contract + Mutation Ledger (`safety:mutation-callsite-coverage` fails any unguarded, unallowlisted risky call site); `release:check:dynamic:execute` is the real **caching gate runner** (schema v2, real/heavy gates deferred to `release:real-check`, dynamic-only cannot authorize publish); the **Core Skill** deployed snapshot is read by the route runtime and recorded in `agent-proof-evidence.json` (`selected_core_skill`), with promotions written to the mutation ledger; and `sks doctor` exposes an explicit **`zellij_readiness`** block (`zellij:doctor-readiness`). See `docs/dynamic-release-pipeline.md`.
26
26
 
@@ -341,7 +341,7 @@ sks team open-zellij latest
341
341
  sks team attach-zellij latest
342
342
  ```
343
343
 
344
- Interactive SKS sessions use Zellij layouts. By default SKS launches Codex in Fast service tier with `--model gpt-5.5`, `-c service_tier="fast"`, and the selected `model_reasoning_effort`. SKS always forces the model to `gpt-5.5`; `SKS_CODEX_MODEL` and `SKS_CODEX_FAST_HIGH=0` cannot downgrade or remove that model pin. You can still set `SKS_CODEX_REASONING` to change reasoning effort. Use `sks --mad --workspace <name>` for an explicit MAD session and `sks help` for CLI help.
344
+ Interactive SKS sessions use Zellij layouts. By default SKS launches Codex in Fast service tier with `--model gpt-5.5`, `-c service_tier="fast"`, the selected `model_reasoning_effort`, and `--no-alt-screen` for Zellij-backed interactive panes so terminal scrollback captures the conversation transcript. SKS always forces the model to `gpt-5.5`; `SKS_CODEX_MODEL` and `SKS_CODEX_FAST_HIGH=0` cannot downgrade or remove that model pin. You can still set `SKS_CODEX_REASONING` to change reasoning effort, and `SKS_ZELLIJ_CODEX_ALT_SCREEN=1` restores Codex's alternate-screen UI for the next launch. Use `sks --mad --workspace <name>` for an explicit MAD session and `sks help` for CLI help.
345
345
 
346
346
  Before opening the interactive runtime, SKS checks the installed Codex CLI against npm `@openai/codex@latest`. If a newer version exists, it asks `Y/n`; answering `y` updates automatically with `npm i -g @openai/codex@latest` and then opens the runtime with the updated Codex CLI.
347
347
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "1.21.2"
79
+ version = "1.21.4"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "1.21.2"
3
+ version = "1.21.4"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 1.21.2"),
7
+ Some("--version") => println!("sks-rs 1.21.4"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "1.21.2",
5
- "source_digest": "c2de07d5136a6b0feccf10d8b647ffc2fcbc322e738bafd6cea43efdd3da1225",
6
- "source_file_count": 1752,
7
- "built_at_source_time": 1780308857966
4
+ "package_version": "1.21.4",
5
+ "source_digest": "70780332db02a19044731adecdb06abcf7d9efdc8b128627be712ca33b3c6881",
6
+ "source_file_count": 1755,
7
+ "built_at_source_time": 1780318491834
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '1.21.2';
2
+ const FAST_PACKAGE_VERSION = '1.21.4';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schema": "sks.dist-build.v2",
3
- "version": "1.21.2",
4
- "package_version": "1.21.2",
3
+ "version": "1.21.4",
4
+ "package_version": "1.21.4",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
7
  "compiled_file_count": 1022,
8
8
  "compiled_js_count": 511,
9
9
  "compiled_dts_count": 511,
10
- "source_digest": "c2de07d5136a6b0feccf10d8b647ffc2fcbc322e738bafd6cea43efdd3da1225",
11
- "source_file_count": 1752,
12
- "source_files_hash": "6b8e7de7bcab2c6ddd83d31e15d7219e9d3f595813a1ec8173936208d3c877ef",
13
- "source_list_hash": "6b8e7de7bcab2c6ddd83d31e15d7219e9d3f595813a1ec8173936208d3c877ef",
10
+ "source_digest": "70780332db02a19044731adecdb06abcf7d9efdc8b128627be712ca33b3c6881",
11
+ "source_file_count": 1755,
12
+ "source_files_hash": "e6487058a1a0894c6b8a629b64319d45be1ad9a0658ab422a39969c1e39ad7e3",
13
+ "source_list_hash": "e6487058a1a0894c6b8a629b64319d45be1ad9a0658ab422a39969c1e39ad7e3",
14
14
  "src_mjs_runtime_files": 0,
15
15
  "dist_stamp_schema": "sks.dist-build-stamp.v1",
16
16
  "files": [
@@ -336,7 +336,7 @@ export declare function ensureGlobalCodexFastModeDuringInstall(opts?: any): Prom
336
336
  backup_path?: never;
337
337
  parse_smoke?: never;
338
338
  }>;
339
- export declare function normalizeCodexFastModeUiConfig(text?: any): string;
339
+ export declare function normalizeCodexFastModeUiConfig(text?: any, opts?: any): string;
340
340
  export declare function safeWriteCodexConfigToml(configPath: string, current: string, next: string, tag?: string): Promise<{
341
341
  ok: boolean;
342
342
  status: string;
@@ -1479,7 +1479,7 @@ export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
1479
1479
  return { status: 'unparseable_config_preserved', config_path: configPath, backup_path: backupPath, parse_smoke: currentSmoke };
1480
1480
  }
1481
1481
  }
1482
- const next = normalizeCodexFastModeUiConfig(current);
1482
+ const next = normalizeCodexFastModeUiConfig(current, { forceFastMode: opts.forceFastMode === true });
1483
1483
  if (next === ensureTrailingNewline(current))
1484
1484
  return { status: 'present', config_path: configPath };
1485
1485
  // Safety gate 2: never WRITE a config that would not parse.
@@ -1495,12 +1495,12 @@ export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
1495
1495
  return { status: 'failed', config_path: configPath, error: err.message };
1496
1496
  }
1497
1497
  }
1498
- export function normalizeCodexFastModeUiConfig(text = '') {
1498
+ export function normalizeCodexFastModeUiConfig(text = '', opts = {}) {
1499
1499
  // Run to a fixed point so a second install is a true no-op (idempotent). The per-pass
1500
1500
  // table/whitespace normalization converges within one extra pass.
1501
- return normalizeCodexFastModeUiConfigOnce(normalizeCodexFastModeUiConfigOnce(text));
1501
+ return normalizeCodexFastModeUiConfigOnce(normalizeCodexFastModeUiConfigOnce(text, opts), opts);
1502
1502
  }
1503
- function normalizeCodexFastModeUiConfigOnce(text = '') {
1503
+ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
1504
1504
  // Preserve user-owned top-level scalars (model / service_tier / model_reasoning_effort):
1505
1505
  // SKS only supplies a default when the user has not chosen one, and never strips the
1506
1506
  // user's own reasoning effort. SKS continues to manage its own namespaced tables below
@@ -1509,7 +1509,9 @@ function normalizeCodexFastModeUiConfigOnce(text = '') {
1509
1509
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
1510
1510
  next = removeTomlTableKey(next, 'features', 'codex_hooks');
1511
1511
  next = upsertTopLevelTomlStringIfAbsent(next, 'model', 'gpt-5.5');
1512
- next = upsertTopLevelTomlStringIfAbsent(next, 'service_tier', 'fast');
1512
+ next = opts.forceFastMode === true
1513
+ ? upsertTopLevelTomlString(next, 'service_tier', 'fast')
1514
+ : upsertTopLevelTomlStringIfAbsent(next, 'service_tier', 'fast');
1513
1515
  // Codex App feature flags / fast-mode UI / suppress-warning are SET-IF-ABSENT: a fresh
1514
1516
  // config still gets SKS's defaults, but SKS NEVER overrides (re-enables) a feature the
1515
1517
  // user disabled in the App, and never rejects-then-hides UI by forcing an unrecognized
@@ -1522,9 +1524,16 @@ function normalizeCodexFastModeUiConfigOnce(text = '') {
1522
1524
  'guardian_approval = true', 'tool_suggest = true', 'apps = true', 'plugins = true'
1523
1525
  ])
1524
1526
  next = upsertTomlTableKeyIfAbsent(next, 'features', featureLine);
1525
- next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'visible = true');
1526
- next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'enabled = true');
1527
- next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'default_profile = "sks-fast-high"');
1527
+ if (opts.forceFastMode === true) {
1528
+ next = upsertTomlTableKey(next, 'user.fast_mode', 'visible = true');
1529
+ next = upsertTomlTableKey(next, 'user.fast_mode', 'enabled = true');
1530
+ next = upsertTomlTableKey(next, 'user.fast_mode', 'default_profile = "sks-fast-high"');
1531
+ }
1532
+ else {
1533
+ next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'visible = true');
1534
+ next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'enabled = true');
1535
+ next = upsertTomlTableKeyIfAbsent(next, 'user.fast_mode', 'default_profile = "sks-fast-high"');
1536
+ }
1528
1537
  // Keep ONLY the sks-fast-high config-profile table: the Codex App fast-mode
1529
1538
  // (`[user.fast_mode] default_profile = "sks-fast-high"`) and the
1530
1539
  // codex-app:ui-preservation gate still expect it. The other SKS config profiles are
@@ -511,6 +511,7 @@ export declare function run(command: any, args?: any): Promise<void | {
511
511
  status: string;
512
512
  blockers: string[];
513
513
  };
514
+ visual_lane_count?: never;
514
515
  native_cli_session_proof?: never;
515
516
  no_subagent_scaling_policy?: never;
516
517
  fast_mode_policy?: never;
@@ -578,6 +579,7 @@ export declare function run(command: any, args?: any): Promise<void | {
578
579
  requested_work_items: number;
579
580
  actual_total_work_items: any;
580
581
  target_active_slots: number;
582
+ visual_lane_count: number;
581
583
  minimum_work_items: number;
582
584
  scheduler: {
583
585
  schema: string;
@@ -974,6 +976,7 @@ export declare function run(command: any, args?: any): Promise<void | {
974
976
  all_generations_closed: boolean;
975
977
  scheduler_state: string;
976
978
  target_active_slots: number;
979
+ visual_lane_count: number;
977
980
  requested_work_items: number;
978
981
  actual_total_work_items: number;
979
982
  minimum_work_items: number;
@@ -29,6 +29,7 @@ export declare function run(_command: any, args?: any): Promise<void | {
29
29
  zellij_socket_dir_source: import("../core/zellij/zellij-command.js").ZellijSocketDirSource;
30
30
  clipboard_config_path: string;
31
31
  clipboard_copy_command: string;
32
+ clipboard_mouse_mode: boolean;
32
33
  pane_proof_path: string;
33
34
  pane_proof: {
34
35
  schema: string;
@@ -360,6 +360,7 @@ export declare function run(command: any, args?: any): Promise<void | {
360
360
  status: string;
361
361
  blockers: string[];
362
362
  };
363
+ visual_lane_count?: never;
363
364
  native_cli_session_proof?: never;
364
365
  no_subagent_scaling_policy?: never;
365
366
  fast_mode_policy?: never;
@@ -427,6 +428,7 @@ export declare function run(command: any, args?: any): Promise<void | {
427
428
  requested_work_items: number;
428
429
  actual_total_work_items: any;
429
430
  target_active_slots: number;
431
+ visual_lane_count: number;
430
432
  minimum_work_items: number;
431
433
  scheduler: {
432
434
  schema: string;
@@ -823,6 +825,7 @@ export declare function run(command: any, args?: any): Promise<void | {
823
825
  all_generations_closed: boolean;
824
826
  scheduler_state: string;
825
827
  target_active_slots: number;
828
+ visual_lane_count: number;
826
829
  requested_work_items: number;
827
830
  actual_total_work_items: number;
828
831
  minimum_work_items: number;
@@ -6,6 +6,7 @@ export declare function readAgentGateStatus(root: string, missionId: string): Pr
6
6
  source: string;
7
7
  proof: any;
8
8
  gate: any;
9
+ expected_agent_count: number;
9
10
  all_sessions_closed: boolean;
10
11
  }>;
11
12
  //# sourceMappingURL=agent-gate.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { AGENT_INTAKE_STAGE_ID } from './agent-schema.js';
3
+ import { AGENT_INTAKE_STAGE_ID, DEFAULT_AGENT_COUNT } from './agent-schema.js';
4
4
  async function exists(file) {
5
5
  try {
6
6
  await fs.access(file);
@@ -22,8 +22,16 @@ export async function readAgentGateStatus(root, missionId) {
22
22
  const dir = path.join(root, '.sneakoscope', 'missions', missionId);
23
23
  const proofPath = path.join(dir, 'agents', 'agent-proof-evidence.json');
24
24
  const gatePath = path.join(dir, 'agent-gate.json');
25
+ const policyPath = path.join(dir, 'agents', 'agent-concurrency-policy.json');
26
+ const teamPlanPath = path.join(dir, 'team-plan.json');
25
27
  const proof = await readJson(proofPath, null);
26
28
  const gate = await readJson(gatePath, null);
29
+ const policy = await readJson(policyPath, null);
30
+ const teamPlan = await readJson(teamPlanPath, null);
31
+ const expectedAgentCount = Math.max(DEFAULT_AGENT_COUNT, Number(gate?.expected_agent_count ||
32
+ teamPlan?.bundle_size ||
33
+ policy?.agents ||
34
+ DEFAULT_AGENT_COUNT) || DEFAULT_AGENT_COUNT);
27
35
  const missing = [];
28
36
  if (!(await exists(proofPath)))
29
37
  missing.push('agents/agent-proof-evidence.json');
@@ -32,8 +40,11 @@ export async function readAgentGateStatus(root, missionId) {
32
40
  blockers.push('agent_proof_not_ok');
33
41
  if (proof?.status !== 'passed')
34
42
  blockers.push('agent_proof_status_not_passed');
35
- if (Number(proof?.agent_count || 0) < 5)
43
+ const agentCount = Number(proof?.agent_count || 0);
44
+ if (agentCount < DEFAULT_AGENT_COUNT)
36
45
  blockers.push('agent_count_below_5');
46
+ if (agentCount < expectedAgentCount)
47
+ blockers.push('agent_count_below_expected');
37
48
  if (proof?.no_overlap_ok !== true)
38
49
  blockers.push('agent_no_overlap_not_ok');
39
50
  if (proof?.ledger_hash_chain_ok !== true)
@@ -52,6 +63,7 @@ export async function readAgentGateStatus(root, missionId) {
52
63
  source: proofPath,
53
64
  proof,
54
65
  gate,
66
+ expected_agent_count: expectedAgentCount,
55
67
  all_sessions_closed: sessionsClosed
56
68
  };
57
69
  }
@@ -127,6 +127,7 @@ export declare function runNativeAgentOrchestrator(opts?: AgentRunOptions): Prom
127
127
  status: string;
128
128
  blockers: string[];
129
129
  };
130
+ visual_lane_count?: never;
130
131
  native_cli_session_proof?: never;
131
132
  no_subagent_scaling_policy?: never;
132
133
  fast_mode_policy?: never;
@@ -194,6 +195,7 @@ export declare function runNativeAgentOrchestrator(opts?: AgentRunOptions): Prom
194
195
  requested_work_items: number;
195
196
  actual_total_work_items: any;
196
197
  target_active_slots: number;
198
+ visual_lane_count: number;
197
199
  minimum_work_items: number;
198
200
  scheduler: {
199
201
  schema: string;
@@ -590,6 +592,7 @@ export declare function runNativeAgentOrchestrator(opts?: AgentRunOptions): Prom
590
592
  all_generations_closed: boolean;
591
593
  scheduler_state: string;
592
594
  target_active_slots: number;
595
+ visual_lane_count: number;
593
596
  requested_work_items: number;
594
597
  actual_total_work_items: number;
595
598
  minimum_work_items: number;
@@ -79,6 +79,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
79
79
  })
80
80
  }));
81
81
  const targetActiveSlots = normalizeTargetActiveSlots(opts.targetActiveSlots ?? opts.agents ?? roster.agent_count, maxAgentCount);
82
+ const visualLaneCount = normalizeVisualLaneCount(opts.visualLaneCount ?? opts.clones ?? opts.agents ?? roster.agent_count, roster.agent_count, maxAgentCount);
82
83
  const desiredWorkItemCount = normalizeDesiredWorkItemCount(opts.desiredWorkItemCount, opts.minimumWorkItems, targetActiveSlots);
83
84
  const minimumWorkItems = normalizeMinimumWorkItems(opts.minimumWorkItems, targetActiveSlots);
84
85
  const sourceIntelligence = await runSourceIntelligence({ root, missionDir: dir, route, query: prompt, offline: true, context7Available: true });
@@ -195,7 +196,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
195
196
  await writeIntelligentWorkGraphArtifacts(ledgerRoot, partition.intelligent_work_graph);
196
197
  await writeScoutPolicyArtifact(ledgerRoot);
197
198
  await writeZellijRightLaneCockpit(ledgerRoot, { missionId, sessionName: `sks-${missionId}`, agents: roster.roster });
198
- await initializeZellijLaneSupervisor(ledgerRoot, { missionId, sessionName: `sks-${missionId}`, targetActiveSlots, launchRealZellij: realZellij });
199
+ await initializeZellijLaneSupervisor(ledgerRoot, { missionId, sessionName: `sks-${missionId}`, targetActiveSlots: visualLaneCount, launchRealZellij: realZellij });
199
200
  await writeZellijPaneProof(root, { missionId, require: realZellijProofRequired, phase: 'initial', ledgerRoot });
200
201
  await writeAgentCodexCockpitArtifacts(dir, { missionId, projectHash: namespace.root_hash });
201
202
  await writeJsonAtomic(path.join(ledgerRoot, 'agent-no-overlap-proof.json'), partition.no_overlap_proof || { schema: 'sks.agent-no-overlap-proof.v1', ok: false, blockers: ['missing_no_overlap_proof'] });
@@ -209,6 +210,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
209
210
  concurrency: roster.concurrency,
210
211
  batch_count: 0,
211
212
  target_active_slots: targetActiveSlots,
213
+ visual_lane_count: visualLaneCount,
212
214
  desired_work_items: desiredWorkItemCount,
213
215
  minimum_work_items: minimumWorkItems,
214
216
  requested_work_items: desiredWorkItemCount,
@@ -404,6 +406,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
404
406
  minimumWorkItems,
405
407
  targetActiveSlots,
406
408
  realParallel: backend === 'codex-exec' && opts.mock !== true,
409
+ visualLaneCount,
407
410
  roster,
408
411
  partition,
409
412
  consensus,
@@ -442,6 +445,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
442
445
  requested_work_items: desiredWorkItemCount,
443
446
  actual_total_work_items: partition.task_graph?.total_work_items || partition.slices.length,
444
447
  target_active_slots: targetActiveSlots,
448
+ visual_lane_count: visualLaneCount,
445
449
  minimum_work_items: minimumWorkItems,
446
450
  scheduler,
447
451
  source_intelligence: sourceIntelligenceRef,
@@ -724,6 +728,16 @@ function normalizeMinimumWorkItems(value, targetActiveSlots) {
724
728
  return Math.max(1, Math.floor(targetActiveSlots));
725
729
  return Math.max(1, Math.floor(parsed));
726
730
  }
731
+ function normalizeVisualLaneCount(value, fallback, maxAgentCount) {
732
+ const parsed = Number(value);
733
+ const fallbackCount = Number(fallback);
734
+ const raw = Number.isFinite(parsed) && parsed > 0
735
+ ? parsed
736
+ : Number.isFinite(fallbackCount) && fallbackCount > 0
737
+ ? fallbackCount
738
+ : 1;
739
+ return Math.max(1, Math.min(maxAgentCount, Math.floor(raw)));
740
+ }
727
741
  function isWriteCapableRun(opts) {
728
742
  return opts.applyPatches === true || opts.dryRunPatches === true || (opts.writeMode !== undefined && opts.writeMode !== 'off');
729
743
  }
@@ -7,6 +7,7 @@ export declare function writeAgentProofEvidence(root: string, input: {
7
7
  requestedWorkItems?: number;
8
8
  minimumWorkItems?: number;
9
9
  targetActiveSlots?: number;
10
+ visualLaneCount?: number;
10
11
  realParallel?: boolean;
11
12
  roster?: any;
12
13
  partition?: any;
@@ -97,6 +98,7 @@ export declare function writeAgentProofEvidence(root: string, input: {
97
98
  all_generations_closed: boolean;
98
99
  scheduler_state: string;
99
100
  target_active_slots: number;
101
+ visual_lane_count: number;
100
102
  requested_work_items: number;
101
103
  actual_total_work_items: number;
102
104
  minimum_work_items: number;
@@ -47,6 +47,7 @@ export async function writeAgentProofEvidence(root, input) {
47
47
  const workQueueTotalWorkItems = Number(workQueue?.total_work_items || 0);
48
48
  const schedulerTotalWorkItems = Number(scheduler?.total_work_items || 0);
49
49
  const targetActiveSlots = Number(input.targetActiveSlots || scheduler?.target_active_slots || taskGraph?.target_active_slots || input.roster?.agent_count || 0);
50
+ const visualLaneCount = Number(input.visualLaneCount || zellijLanes?.lane_count || laneSupervisor?.lane_count || targetActiveSlots || 0);
50
51
  const minimumWorkItems = Number(input.minimumWorkItems || taskGraph?.minimum_work_items || targetActiveSlots || 0);
51
52
  const taskGraphMatchesCliOptions = Boolean(taskGraph) && requestedWorkItems === taskGraphTotalWorkItems && targetActiveSlots === Number(taskGraph.target_active_slots || 0);
52
53
  const workQueueMatchesTaskGraph = Boolean(workQueue && taskGraph) && workQueueTotalWorkItems === taskGraphTotalWorkItems;
@@ -111,6 +112,7 @@ export async function writeAgentProofEvidence(root, input) {
111
112
  ...(terminalCloseReportCount < generationCount ? ['terminal_close_report_count_below_generation_count'] : []),
112
113
  ...(slots && slots.all_slots_closed_after_drain !== true ? ['agent_worker_slots_not_closed_after_drain'] : []),
113
114
  ...(!laneSupervisor ? ['zellij_lane_supervisor_missing'] : []),
115
+ ...(laneSupervisor && visualLaneCount > 0 && Number(laneSupervisor.lane_count || 0) < visualLaneCount ? ['zellij_lane_count_below_visual_lane_count'] : []),
114
116
  ...(laneSupervisor && laneSupervisor.no_flicker_verified !== true ? ['zellij_lane_no_flicker_not_verified'] : []),
115
117
  ...(laneSupervisor && laneSupervisor.pane_survival_checked !== true ? ['zellij_lane_survival_not_checked'] : []),
116
118
  ...(laneSupervisor && Number(laneSupervisor.unexpected_close_count || 0) > 0 ? ['zellij_lane_unexpected_close_before_drain'] : []),
@@ -206,6 +208,7 @@ export async function writeAgentProofEvidence(root, input) {
206
208
  all_generations_closed: generations.ok,
207
209
  scheduler_state: 'agent-scheduler-state.json',
208
210
  target_active_slots: targetActiveSlots,
211
+ visual_lane_count: visualLaneCount,
209
212
  requested_work_items: requestedWorkItems,
210
213
  actual_total_work_items: taskGraphTotalWorkItems || schedulerTotalWorkItems,
211
214
  minimum_work_items: minimumWorkItems,
@@ -1,13 +1,18 @@
1
1
  import type { AgentPersona, AgentRosterEntry } from './agent-schema.js';
2
2
  export declare function systemSafeNarutoConcurrency(opts?: {
3
3
  backend?: string;
4
+ cores?: number;
5
+ freeBytes?: number;
6
+ totalBytes?: number;
4
7
  }): {
5
8
  cap: number;
6
9
  cores: number;
7
10
  free_gb: number;
11
+ total_gb: number;
8
12
  backend: string;
9
13
  heavy: boolean;
10
14
  override_applied: boolean;
15
+ memory_model: string;
11
16
  };
12
17
  export declare function normalizeAgentCount(value: unknown, fallback?: number, maxAgentCount?: number): number;
13
18
  export declare function normalizeAgentConcurrency(value: unknown, agents: number, maxAgentCount?: number): number;
@@ -7,22 +7,35 @@ import { buildAgentEffortPolicy, decideAgentEffort, decideNarutoCloneEffort } fr
7
7
  // host can safely sustain — derived from CPU cores and free memory, heavier-bounded for
8
8
  // real child-process backends (codex-exec/zellij/process) than for in-process (fake).
9
9
  export function systemSafeNarutoConcurrency(opts = {}) {
10
- const cores = Math.max(1, Number(os.cpus()?.length) || 4);
10
+ const cores = Math.max(1, Number(opts.cores ?? os.cpus()?.length) || 4);
11
11
  let freeBytes = 2 * 1024 * 1024 * 1024;
12
+ let totalBytes = 8 * 1024 * 1024 * 1024;
12
13
  try {
13
- freeBytes = os.freemem() || freeBytes;
14
+ freeBytes = Number(opts.freeBytes ?? os.freemem()) || freeBytes;
15
+ }
16
+ catch { /* keep fallback */ }
17
+ try {
18
+ totalBytes = Number(opts.totalBytes ?? os.totalmem()) || totalBytes;
14
19
  }
15
20
  catch { /* keep fallback */ }
16
21
  const freeGb = freeBytes / (1024 * 1024 * 1024);
22
+ const totalGb = totalBytes / (1024 * 1024 * 1024);
17
23
  const backend = String(opts.backend || 'codex-exec');
18
24
  const heavy = backend === 'codex-exec' || backend === 'zellij' || backend === 'process';
19
25
  let cap;
20
26
  if (heavy) {
21
- // Real codex children are heavy (a model call + process). Leave a core free and budget
22
- // ~0.6 GB per concurrent worker; clamp to a sane ceiling.
27
+ // Real codex children are heavier than fake workers, but macOS can report
28
+ // very low freemem while reclaimable memory is still available. Use a
29
+ // conservative total-memory floor so Naruto keeps meaningful parallelism
30
+ // instead of collapsing to one slot on otherwise capable machines.
23
31
  const byCpu = Math.max(1, cores - 1);
24
- const byMem = Math.max(1, Math.floor(freeGb / 0.6));
25
- cap = Math.min(byCpu, byMem, 16);
32
+ const gbPerWorker = positiveEnvNumber('SKS_NARUTO_GB_PER_WORKER', 0.6);
33
+ const reclaimableFloorGb = totalGb >= 16 ? 6 : totalGb >= 8 ? 3 : totalGb >= 4 ? 1.5 : freeGb;
34
+ const budgetGb = Math.max(freeGb, reclaimableFloorGb);
35
+ const byMem = Math.max(1, Math.floor(budgetGb / gbPerWorker));
36
+ const minParallelDefault = totalGb >= 16 ? 8 : totalGb >= 8 ? 4 : totalGb >= 4 ? 2 : 1;
37
+ const minParallel = Math.min(byCpu, Math.floor(positiveEnvNumber('SKS_NARUTO_MIN_CONCURRENCY', minParallelDefault)));
38
+ cap = Math.min(byCpu, Math.max(byMem, minParallel), 16);
26
39
  }
27
40
  else {
28
41
  // In-process / light workers can pack tighter.
@@ -32,7 +45,20 @@ export function systemSafeNarutoConcurrency(opts = {}) {
32
45
  if (Number.isFinite(override) && override >= 1)
33
46
  cap = Math.min(Math.floor(override), MAX_NARUTO_AGENT_COUNT);
34
47
  cap = Math.max(1, Math.min(cap, MAX_NARUTO_AGENT_COUNT));
35
- return { cap, cores, free_gb: Math.round(freeGb * 10) / 10, backend, heavy, override_applied: Number.isFinite(override) && override >= 1 };
48
+ return {
49
+ cap,
50
+ cores,
51
+ free_gb: Math.round(freeGb * 10) / 10,
52
+ total_gb: Math.round(totalGb * 10) / 10,
53
+ backend,
54
+ heavy,
55
+ override_applied: Number.isFinite(override) && override >= 1,
56
+ memory_model: heavy ? 'free_or_reclaimable_floor' : 'light_worker_cpu_bound'
57
+ };
58
+ }
59
+ function positiveEnvNumber(name, fallback) {
60
+ const parsed = Number(process.env[name]);
61
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
36
62
  }
37
63
  function resolveMaxAgentCount(value) {
38
64
  const parsed = Number(value);
@@ -96,6 +96,7 @@ export interface AgentRunOptions {
96
96
  noFast?: boolean;
97
97
  nativeCliSwarm?: boolean;
98
98
  maxAgentCount?: number;
99
+ visualLaneCount?: number;
99
100
  clones?: number;
100
101
  narutoMode?: boolean;
101
102
  }
@@ -186,6 +186,7 @@ export declare function writeRouteCollaborationArtifacts(root: string, opts: {
186
186
  status: string;
187
187
  blockers: string[];
188
188
  };
189
+ visual_lane_count?: never;
189
190
  native_cli_session_proof?: never;
190
191
  no_subagent_scaling_policy?: never;
191
192
  fast_mode_policy?: never;
@@ -253,6 +254,7 @@ export declare function writeRouteCollaborationArtifacts(root: string, opts: {
253
254
  requested_work_items: number;
254
255
  actual_total_work_items: any;
255
256
  target_active_slots: number;
257
+ visual_lane_count: number;
256
258
  minimum_work_items: number;
257
259
  scheduler: {
258
260
  schema: string;
@@ -649,6 +651,7 @@ export declare function writeRouteCollaborationArtifacts(root: string, opts: {
649
651
  all_generations_closed: boolean;
650
652
  scheduler_state: string;
651
653
  target_active_slots: number;
654
+ visual_lane_count: number;
652
655
  requested_work_items: number;
653
656
  actual_total_work_items: number;
654
657
  minimum_work_items: number;
@@ -34,9 +34,13 @@ export async function updateZellijLaneSupervisorFromSlots(root, input) {
34
34
  targetActiveSlots: Math.max(1, input.slots.length || input.state?.target_active_slots || 1)
35
35
  });
36
36
  }
37
- const laneBySlot = new Map(supervisor.lanes.map((lane) => [lane.slot_id, lane]));
38
- const lanes = input.slots.map((slot, index) => {
39
- const previous = laneBySlot.get(slot.slot_id) || createLane(input.missionId, supervisor.session_name, index + 1, nowIso());
37
+ const slotById = new Map(input.slots.map((slot) => [slot.slot_id, slot]));
38
+ const laneCount = Math.max(supervisor.lanes.length, input.slots.length);
39
+ const lanes = Array.from({ length: laneCount }, (_, index) => {
40
+ const previous = supervisor.lanes[index] || createLane(input.missionId, supervisor.session_name, index + 1, nowIso());
41
+ const slot = slotById.get(previous.slot_id);
42
+ if (!slot)
43
+ return previous;
40
44
  return {
41
45
  ...previous,
42
46
  current_session_id: slot.current_session_id,
@@ -4,7 +4,7 @@ export const ZELLIJ_RIGHT_LANE_LAYOUT_SCHEMA = 'sks.agent-zellij-right-lane-layo
4
4
  export const ZELLIJ_RIGHT_LANES_SCHEMA = 'sks.agent-zellij-right-lanes.v1';
5
5
  export function buildZellijRightLaneCockpit(input = {}) {
6
6
  const agents = input.slots || input.agents || [];
7
- const maxVisible = input.maxVisibleLanes || 20;
7
+ const maxVisible = input.maxVisibleLanes || Math.max(agents.length, 1);
8
8
  const lanes = agents.map((agent, index) => ({
9
9
  lane_index: index + 1,
10
10
  slot_id: String(agent.slot_id || agent.id || agent.agent_id || `slot-${String(index + 1).padStart(3, '0')}`),
@@ -12,6 +12,57 @@ export declare function fastModeCommand(args?: string[]): Promise<void | {
12
12
  fast_mode: boolean;
13
13
  service_tier: import("../agents/fast-mode-policy.js").AgentServiceTier;
14
14
  disabled_by: "none" | "no-fast" | "service-tier-standard" | "preference-standard";
15
+ codex_fast_mode_repair: {
16
+ status: string;
17
+ reason: string;
18
+ config_path?: never;
19
+ backup_path?: never;
20
+ parse_smoke?: never;
21
+ error?: never;
22
+ } | {
23
+ status: string;
24
+ config_path: any;
25
+ backup_path: string | null;
26
+ parse_smoke: {
27
+ ok: boolean;
28
+ unterminated_multiline_string: boolean;
29
+ invalid_table_header: string | null;
30
+ };
31
+ reason?: never;
32
+ error?: never;
33
+ } | {
34
+ status: string;
35
+ config_path: any;
36
+ reason?: never;
37
+ backup_path?: never;
38
+ parse_smoke?: never;
39
+ error?: never;
40
+ } | {
41
+ status: string;
42
+ config_path: any;
43
+ parse_smoke: {
44
+ ok: boolean;
45
+ unterminated_multiline_string: boolean;
46
+ invalid_table_header: string | null;
47
+ };
48
+ reason?: never;
49
+ backup_path?: never;
50
+ error?: never;
51
+ } | {
52
+ status: string;
53
+ config_path: any;
54
+ backup_path: string | null;
55
+ reason?: never;
56
+ parse_smoke?: never;
57
+ error?: never;
58
+ } | {
59
+ status: string;
60
+ config_path: any;
61
+ error: any;
62
+ reason?: never;
63
+ backup_path?: never;
64
+ parse_smoke?: never;
65
+ } | null;
15
66
  policy: import("../agents/fast-mode-policy.js").FastModePolicy;
16
67
  dollar_commands: {
17
68
  on: string;