sneakoscope 1.20.4 → 1.21.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.
Files changed (46) hide show
  1. package/README.md +2 -0
  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 +3 -3
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +3 -3
  8. package/dist/commands/mad-sks.d.ts +11 -1
  9. package/dist/core/agents/agent-codex-cockpit.d.ts +1 -1
  10. package/dist/core/agents/agent-command-surface.d.ts +1 -1
  11. package/dist/core/agents/agent-command-surface.js +2 -1
  12. package/dist/core/agents/agent-orchestrator.js +2 -2
  13. package/dist/core/agents/agent-plan.d.ts +1 -1
  14. package/dist/core/agents/agent-plan.js +1 -1
  15. package/dist/core/agents/fast-mode-policy.d.ts +5 -0
  16. package/dist/core/agents/fast-mode-policy.js +12 -1
  17. package/dist/core/codex/codex-cli-syntax-builder.d.ts +1 -1
  18. package/dist/core/codex/codex-cli-syntax-builder.js +11 -2
  19. package/dist/core/commands/fast-mode-command.js +4 -4
  20. package/dist/core/commands/mad-sks-command.d.ts +11 -1
  21. package/dist/core/commands/mad-sks-command.js +41 -3
  22. package/dist/core/commands/naruto-command.js +27 -2
  23. package/dist/core/commands/research-command.js +6 -3
  24. package/dist/core/commands/run-command.js +3 -3
  25. package/dist/core/commands/team-command.d.ts +7 -6
  26. package/dist/core/commands/team-command.js +60 -13
  27. package/dist/core/fsx.d.ts +1 -1
  28. package/dist/core/fsx.js +1 -1
  29. package/dist/core/hooks-runtime.js +4 -4
  30. package/dist/core/perf-bench.d.ts +3 -2
  31. package/dist/core/pipeline-internals/runtime-core.d.ts +6 -4
  32. package/dist/core/pipeline-internals/runtime-core.js +22 -21
  33. package/dist/core/pipeline-internals/runtime-gates.js +3 -3
  34. package/dist/core/routes.js +7 -7
  35. package/dist/core/team-dag.js +1 -1
  36. package/dist/core/team-live.js +4 -4
  37. package/dist/core/version-manager.js +32 -11
  38. package/dist/core/version.d.ts +1 -1
  39. package/dist/core/version.js +1 -1
  40. package/dist/core/zellij/zellij-launcher.d.ts +57 -4
  41. package/dist/core/zellij/zellij-launcher.js +57 -13
  42. package/dist/core/zellij/zellij-layout-builder.d.ts +6 -0
  43. package/dist/core/zellij/zellij-layout-builder.js +34 -2
  44. package/dist/core/zellij/zellij-pane-proof.d.ts +6 -0
  45. package/dist/core/zellij/zellij-pane-proof.js +20 -0
  46. package/package.json +1 -1
package/README.md CHANGED
@@ -16,6 +16,8 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
+ SKS **1.21.0** carries forward the `sks --mad` Zellij auto-attach fix and aligns the release metadata gates with explicit version bumps. Previous releases only *created* a detached background session and printed an `Attach with: ...` hint, so nothing opened in the operator's terminal. SKS now performs the follow-up foreground attach automatically when launched in an interactive TTY (using the same `ZELLIJ_SOCKET_DIR` namespace as the background session), and falls back to printing the manual attach command if attach fails. Auto-attach is skipped for `--json`, non-TTY/piped launches, when already inside a Zellij session, or with `--no-attach` / `SKS_NO_ZELLIJ_ATTACH=1`; `--attach` forces it.
20
+
19
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.
20
22
 
21
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.
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "1.20.4"
79
+ version = "1.21.0"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "1.20.4"
3
+ version = "1.21.0"
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.20.4"),
7
+ Some("--version") => println!("sks-rs 1.21.0"),
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.20.4",
5
- "source_digest": "491c4c03f8a989f3ea78aa47b16a937edbd54e653b71ef79b5ce767916df7395",
4
+ "package_version": "1.21.0",
5
+ "source_digest": "666db83626c95b8a8eae20389e647a2db96c4e22802cdf18ce0f7c142c9dbcaa",
6
6
  "source_file_count": 1750,
7
- "built_at_source_time": 1780281006481
7
+ "built_at_source_time": 1780297266324
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '1.20.4';
2
+ const FAST_PACKAGE_VERSION = '1.21.0';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schema": "sks.dist-build.v2",
3
- "version": "1.20.4",
4
- "package_version": "1.20.4",
3
+ "version": "1.21.0",
4
+ "package_version": "1.21.0",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
7
  "compiled_file_count": 1020,
8
8
  "compiled_js_count": 510,
9
9
  "compiled_dts_count": 510,
10
- "source_digest": "491c4c03f8a989f3ea78aa47b16a937edbd54e653b71ef79b5ce767916df7395",
10
+ "source_digest": "666db83626c95b8a8eae20389e647a2db96c4e22802cdf18ce0f7c142c9dbcaa",
11
11
  "source_file_count": 1750,
12
12
  "source_files_hash": "de93b15a72352d00481b958333ed964bf083563809e3beddfe4e64046720df75",
13
13
  "source_list_hash": "de93b15a72352d00481b958333ed964bf083563809e3beddfe4e64046720df75",
@@ -2,7 +2,7 @@ export declare function run(_command: any, args?: any): Promise<void | {
2
2
  schema: string;
3
3
  generated_at: string;
4
4
  ok: boolean;
5
- kind: "agent" | "team" | "mad";
5
+ kind: "agent" | "team" | "mad" | "naruto";
6
6
  mission_id: string;
7
7
  session_name: string;
8
8
  root: string;
@@ -10,6 +10,13 @@ export declare function run(_command: any, args?: any): Promise<void | {
10
10
  ledger_root: string;
11
11
  layout_path: string;
12
12
  layout_artifact: string;
13
+ main_pane_kind: "codex_interactive" | "status_shell";
14
+ codex_pane: {
15
+ enabled: boolean;
16
+ args: string[];
17
+ launch_env_keys: string[];
18
+ bin: string;
19
+ };
13
20
  command: string[];
14
21
  launch_command: string[];
15
22
  launch_command_with_env: string;
@@ -17,6 +24,7 @@ export declare function run(_command: any, args?: any): Promise<void | {
17
24
  background_command_with_env: string;
18
25
  attach_command: string;
19
26
  attach_command_with_env: string;
27
+ attach_requested: boolean;
20
28
  zellij_socket_dir: string | null;
21
29
  zellij_socket_dir_source: import("../core/zellij/zellij-command.js").ZellijSocketDirSource;
22
30
  pane_proof_path: string;
@@ -32,6 +40,8 @@ export declare function run(_command: any, args?: any): Promise<void | {
32
40
  main_pane: any;
33
41
  lane_panes: any[];
34
42
  expected_lane_count: number;
43
+ expected_main_command_includes: string | null;
44
+ main_command_ok: boolean;
35
45
  lane_count_ok: boolean;
36
46
  geometry_distinct: boolean | null;
37
47
  panes: any[];
@@ -5,7 +5,7 @@ export declare const AGENT_LIVE_SUMMARY_JSON = "agent-live-summary.json";
5
5
  export declare const AGENT_PROGRESS_TIMELINE_MD = "agent-progress-timeline.md";
6
6
  export declare const AGENT_CODEX_COCKPIT_EVENTS = "agent-codex-cockpit-events.jsonl";
7
7
  export type CodexCockpitHookPayload = {
8
- hook_event_name: 'SubagentStart' | 'SubagentStop';
8
+ hook_event_name: 'NativeSessionStart' | 'NativeSessionStop' | 'SubagentStart' | 'SubagentStop';
9
9
  agent_id?: string;
10
10
  agent_type?: string;
11
11
  session_id?: string;
@@ -19,7 +19,7 @@ export declare function parseAgentCommandArgs(command: string, args?: string[]):
19
19
  dryRunPatches: boolean;
20
20
  maxWriteAgents: number;
21
21
  fastMode: boolean | undefined;
22
- serviceTier: string | undefined;
22
+ serviceTier: import("./fast-mode-policy.js").AgentServiceTier | undefined;
23
23
  noFast: boolean;
24
24
  apply: boolean;
25
25
  dryRun: boolean;
@@ -1,4 +1,5 @@
1
1
  import { DEFAULT_AGENT_COUNT } from './agent-schema.js';
2
+ import { normalizeServiceTier } from './fast-mode-policy.js';
2
3
  export function parseAgentCommandArgs(command, args = []) {
3
4
  const first = args[0] && !String(args[0]).startsWith('--') ? String(args[0]) : '';
4
5
  const actions = new Set(['run', 'worker', 'status', 'plan', 'spawn', 'watch', 'dashboard', 'cockpit', 'lane', 'board', 'ledger', 'collect', 'consensus', 'close', 'cleanup', 'proof', 'explain', 'rollback-patches']);
@@ -22,7 +23,7 @@ export function parseAgentCommandArgs(command, args = []) {
22
23
  const dryRunPatches = hasFlag(args, '--dry-run-patches') || hasFlag(args, '--dryrun-patches');
23
24
  const maxWriteAgents = Number(readOption(args, '--max-write-agents', Math.max(1, Math.min(concurrency, agents))));
24
25
  const explicitServiceTier = String(readOption(args, '--service-tier', '') || '');
25
- const serviceTier = explicitServiceTier === 'standard' || explicitServiceTier === 'fast' ? explicitServiceTier : undefined;
26
+ const serviceTier = normalizeServiceTier(explicitServiceTier, null) || undefined;
26
27
  const fastMode = hasFlag(args, '--no-fast') || serviceTier === 'standard' ? false : hasFlag(args, '--fast') ? true : undefined;
27
28
  const noFast = hasFlag(args, '--no-fast');
28
29
  const apply = hasFlag(args, '--apply');
@@ -264,7 +264,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
264
264
  await openAgentSession(ledgerRoot, agent);
265
265
  await heartbeatAgentSession(ledgerRoot, agent);
266
266
  await appendAgentCodexCockpitHookEvent(dir, {
267
- hook_event_name: 'SubagentStart',
267
+ hook_event_name: 'NativeSessionStart',
268
268
  agent_id: agent.id,
269
269
  agent_type: agent.role || agent.persona_id || 'agent',
270
270
  session_id: agent.session_id,
@@ -310,7 +310,7 @@ export async function runNativeAgentOrchestrator(opts = {}) {
310
310
  await completeAgentSession(ledgerRoot, agent);
311
311
  await closeAgentSession(ledgerRoot, agent, result.status === 'done' ? 'closed' : result.status);
312
312
  await appendAgentCodexCockpitHookEvent(dir, {
313
- hook_event_name: 'SubagentStop',
313
+ hook_event_name: 'NativeSessionStop',
314
314
  agent_id: agent.id,
315
315
  agent_type: agent.role || agent.persona_id || 'agent',
316
316
  session_id: agent.session_id,
@@ -11,7 +11,7 @@ export declare function agentPipelineStage(policy?: any): {
11
11
  id: string;
12
12
  goal: string;
13
13
  agent_count: number;
14
- max_parallel_subagents: number;
14
+ max_parallel_native_sessions: number;
15
15
  backend: string;
16
16
  read_only: boolean;
17
17
  write_policy: string;
@@ -33,7 +33,7 @@ export function agentPipelineStage(policy = {}) {
33
33
  id: AGENT_INTAKE_STAGE_ID,
34
34
  goal: 'Run native multi-session agent intake with non-overlapping leases and proof evidence.',
35
35
  agent_count: required ? Number(policy.agent_count || DEFAULT_AGENT_COUNT) : 0,
36
- max_parallel_subagents: Number(policy.agent_count || DEFAULT_AGENT_COUNT),
36
+ max_parallel_native_sessions: Number(policy.agent_count || DEFAULT_AGENT_COUNT),
37
37
  backend: 'native-agent-kernel',
38
38
  read_only: true,
39
39
  write_policy: 'read-only analysis; parent-owned integration',
@@ -2,12 +2,14 @@ export declare const FAST_MODE_POLICY_SCHEMA = "sks.fast-mode-policy.v1";
2
2
  export declare const FAST_MODE_PROPAGATION_PROOF_SCHEMA = "sks.fast-mode-propagation-proof.v1";
3
3
  export declare const FAST_MODE_PREFERENCE_SCHEMA = "sks.fast-mode-preference.v1";
4
4
  export type AgentServiceTier = 'fast' | 'standard';
5
+ export type CodexDesktopServiceTier = 'priority' | 'default';
5
6
  export type FastModePreferenceMode = AgentServiceTier;
6
7
  export interface FastModePolicy {
7
8
  schema: typeof FAST_MODE_POLICY_SCHEMA;
8
9
  generated_at: string;
9
10
  fast_mode: boolean;
10
11
  service_tier: AgentServiceTier;
12
+ codex_desktop_service_tier: CodexDesktopServiceTier;
11
13
  default_fast_mode: true;
12
14
  disabled_by: 'none' | 'no-fast' | 'service-tier-standard' | 'preference-standard';
13
15
  explicit_fast: boolean;
@@ -23,6 +25,7 @@ export interface FastModePreference {
23
25
  mode: FastModePreferenceMode;
24
26
  fast_mode: boolean;
25
27
  service_tier: AgentServiceTier;
28
+ codex_desktop_service_tier: CodexDesktopServiceTier;
26
29
  source: string;
27
30
  }
28
31
  export declare function resolveFastModePolicy(input?: any): FastModePolicy;
@@ -65,4 +68,6 @@ export declare function writeFastModePropagationProof(root: string, input?: {
65
68
  artifacts: string[];
66
69
  blockers: string[];
67
70
  }>;
71
+ export declare function normalizeServiceTier(value: unknown, fallback?: AgentServiceTier | null): AgentServiceTier | null;
72
+ export declare function codexDesktopServiceTier(tier: AgentServiceTier): CodexDesktopServiceTier;
68
73
  //# sourceMappingURL=fast-mode-policy.d.ts.map
@@ -24,6 +24,7 @@ export function resolveFastModePolicy(input = {}) {
24
24
  generated_at: nowIso(),
25
25
  fast_mode: serviceTier === 'fast',
26
26
  service_tier: serviceTier,
27
+ codex_desktop_service_tier: codexDesktopServiceTier(serviceTier),
27
28
  default_fast_mode: true,
28
29
  disabled_by: explicitNoFast ? 'no-fast' : explicitTier === 'standard' ? 'service-tier-standard' : preference?.mode === 'standard' ? 'preference-standard' : 'none',
29
30
  explicit_fast: explicitFast,
@@ -69,6 +70,7 @@ export async function writeFastModePreference(root = process.cwd(), mode, source
69
70
  mode: normalized,
70
71
  fast_mode: normalized === 'fast',
71
72
  service_tier: normalized,
73
+ codex_desktop_service_tier: codexDesktopServiceTier(normalized),
72
74
  source
73
75
  };
74
76
  await writeJsonAtomic(file, preference);
@@ -84,6 +86,7 @@ export function fastModeEnv(policy) {
84
86
  return {
85
87
  SKS_FAST_MODE: policy.fast_mode ? '1' : '0',
86
88
  SKS_SERVICE_TIER: policy.service_tier,
89
+ SKS_CODEX_DESKTOP_SERVICE_TIER: policy.codex_desktop_service_tier,
87
90
  SKS_REASONING_PROFILE_SUFFIX: policy.fast_mode ? 'fast' : 'standard'
88
91
  };
89
92
  }
@@ -164,12 +167,19 @@ export async function writeFastModePropagationProof(root, input = { policy: reso
164
167
  await writeJsonAtomic(path.join(root, 'fast-mode-propagation-proof.json'), report);
165
168
  return report;
166
169
  }
167
- function normalizeServiceTier(value, fallback = 'fast') {
170
+ export function normalizeServiceTier(value, fallback = 'fast') {
168
171
  const text = String(value || '').toLowerCase();
169
172
  if (text === 'fast' || text === 'standard')
170
173
  return text;
174
+ if (text === 'priority')
175
+ return 'fast';
176
+ if (text === 'default')
177
+ return 'standard';
171
178
  return fallback;
172
179
  }
180
+ export function codexDesktopServiceTier(tier) {
181
+ return tier === 'fast' ? 'priority' : 'default';
182
+ }
173
183
  function normalizeFastModePreference(parsed, file) {
174
184
  const mode = normalizeServiceTier(parsed?.mode ?? parsed?.service_tier, 'fast') || 'fast';
175
185
  return {
@@ -178,6 +188,7 @@ function normalizeFastModePreference(parsed, file) {
178
188
  mode,
179
189
  fast_mode: mode === 'fast',
180
190
  service_tier: mode,
191
+ codex_desktop_service_tier: codexDesktopServiceTier(mode),
181
192
  source: typeof parsed?.source === 'string' ? parsed.source : 'unknown',
182
193
  path: file
183
194
  };
@@ -1,5 +1,5 @@
1
1
  export type CodexSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
2
- export type CodexServiceTier = 'fast' | 'standard' | 'default' | 'flex' | 'auto';
2
+ export type CodexServiceTier = 'fast' | 'standard';
3
3
  export type BuildCodexExecArgsOptions = {
4
4
  json?: boolean;
5
5
  outputSchema?: string | null;
@@ -34,9 +34,18 @@ export function buildCodexExecArgs(opts) {
34
34
  args.push('--dangerously-bypass-approvals-and-sandbox');
35
35
  else if (opts.sandbox)
36
36
  args.push('--sandbox', opts.sandbox);
37
- if (opts.serviceTier)
38
- args.push('-c', `service_tier=${opts.serviceTier}`);
37
+ const serviceTier = normalizeCodexServiceTier(opts.serviceTier);
38
+ if (serviceTier)
39
+ args.push('-c', `service_tier=${serviceTier}`);
39
40
  args.push(opts.prompt);
40
41
  return args;
41
42
  }
43
+ function normalizeCodexServiceTier(value) {
44
+ const text = String(value || '').toLowerCase();
45
+ if (text === 'fast' || text === 'priority')
46
+ return 'fast';
47
+ if (text === 'standard' || text === 'default')
48
+ return 'standard';
49
+ return null;
50
+ }
42
51
  //# sourceMappingURL=codex-cli-syntax-builder.js.map
@@ -57,17 +57,17 @@ export async function fastModeCommand(args = []) {
57
57
  else if (action === 'clear')
58
58
  console.log(`Cleared: ${removed ? 'yes' : 'already default'}`);
59
59
  else if (!preference)
60
- console.log('Preference: default fast');
60
+ console.log('Preference: implicit fast');
61
61
  console.log('Dollar: $Fast-On | $Fast-Off | $Fast-Mode');
62
62
  return result;
63
63
  }
64
64
  function normalizeFastModeAction(value) {
65
65
  const text = String(value || 'status').toLowerCase();
66
- if (['on', 'enable', 'enabled', 'fast'].includes(text))
66
+ if (['on', 'enable', 'enabled', 'fast', 'priority'].includes(text))
67
67
  return 'on';
68
- if (['off', 'disable', 'disabled', 'standard', 'slow'].includes(text))
68
+ if (['off', 'disable', 'disabled', 'standard', 'slow', 'default'].includes(text))
69
69
  return 'off';
70
- if (['clear', 'default', 'reset'].includes(text))
70
+ if (['clear', 'reset'].includes(text))
71
71
  return 'clear';
72
72
  return 'status';
73
73
  }
@@ -2,7 +2,7 @@ export declare function madHighCommand(args?: any, deps?: any): Promise<void | {
2
2
  schema: string;
3
3
  generated_at: string;
4
4
  ok: boolean;
5
- kind: "agent" | "team" | "mad";
5
+ kind: "agent" | "team" | "mad" | "naruto";
6
6
  mission_id: string;
7
7
  session_name: string;
8
8
  root: string;
@@ -10,6 +10,13 @@ export declare function madHighCommand(args?: any, deps?: any): Promise<void | {
10
10
  ledger_root: string;
11
11
  layout_path: string;
12
12
  layout_artifact: string;
13
+ main_pane_kind: "codex_interactive" | "status_shell";
14
+ codex_pane: {
15
+ enabled: boolean;
16
+ args: string[];
17
+ launch_env_keys: string[];
18
+ bin: string;
19
+ };
13
20
  command: string[];
14
21
  launch_command: string[];
15
22
  launch_command_with_env: string;
@@ -17,6 +24,7 @@ export declare function madHighCommand(args?: any, deps?: any): Promise<void | {
17
24
  background_command_with_env: string;
18
25
  attach_command: string;
19
26
  attach_command_with_env: string;
27
+ attach_requested: boolean;
20
28
  zellij_socket_dir: string | null;
21
29
  zellij_socket_dir_source: import("../zellij/zellij-command.js").ZellijSocketDirSource;
22
30
  pane_proof_path: string;
@@ -32,6 +40,8 @@ export declare function madHighCommand(args?: any, deps?: any): Promise<void | {
32
40
  main_pane: any;
33
41
  lane_panes: any[];
34
42
  expected_lane_count: number;
43
+ expected_main_command_includes: string | null;
44
+ main_command_ok: boolean;
35
45
  lane_count_ok: boolean;
36
46
  geometry_distinct: boolean | null;
37
47
  panes: any[];
@@ -4,7 +4,7 @@ import { initProject } from '../init.js';
4
4
  import { createMission, setCurrent } from '../mission.js';
5
5
  import { enableMadHighProfile, madHighProfileName } from '../auto-review.js';
6
6
  import { permissionGateSummary } from '../permission-gates.js';
7
- import { launchMadZellijUi, sanitizeZellijSessionName } from '../zellij/zellij-launcher.js';
7
+ import { attachZellijSessionInteractive, launchMadZellijUi, sanitizeZellijSessionName } from '../zellij/zellij-launcher.js';
8
8
  import { createMadSksAuthorizationManifest, validateMadSksAuthorizationManifest } from '../mad-sks/authorization-manifest.js';
9
9
  import { createMadSksAuditLedger, madSksAuditAction, writeMadSksAuditLedger } from '../mad-sks/audit-ledger.js';
10
10
  import { compareProtectedCoreSnapshots, evaluateMadSksWrite, resolveProtectedCore, snapshotProtectedCore } from '../mad-sks/immutable-harness-guard.js';
@@ -79,12 +79,48 @@ export async function madHighCommand(args = [], deps = {}) {
79
79
  const launchOpts = codexLbImmediateLaunchOpts(cleanArgs, launchLb, { codexArgs: profile.launch_args, conciseBlockers: true, madSksEnv, launchEnv: madSksEnv });
80
80
  const workspace = readOption(cleanArgs, '--workspace', readOption(cleanArgs, '--session', launchOpts.session || `sks-mad-${sanitizeZellijSessionName(process.cwd())}`));
81
81
  const launch = await launchMadZellijUi([...cleanArgs, '--workspace', workspace], { ...launchOpts, missionId: madLaunch.mission_id, root: madLaunch.root, cwd: process.cwd(), ledgerRoot: path.join(madLaunch.dir, 'agents'), requireZellij: process.env.SKS_REQUIRE_ZELLIJ === '1' });
82
- if (!launch.ok)
82
+ if (!launch.ok) {
83
83
  console.log(`MAD Zellij action: ${formatMadZellijAction(launch)}`);
84
- else if (launch.attach_command_with_env)
84
+ return launch;
85
+ }
86
+ // The launcher only creates a detached background session. In an interactive
87
+ // terminal, immediately attach so the session actually opens for the user
88
+ // instead of leaving them to copy/paste the attach command by hand.
89
+ if (shouldAutoAttachZellij(args)) {
90
+ console.log(`Opening Zellij session: ${launch.session_name} (detach with Ctrl+q, re-attach later with: ${launch.attach_command_with_env})`);
91
+ const attached = attachZellijSessionInteractive(launch.session_name, { cwd: process.cwd() });
92
+ if (!attached.ok) {
93
+ console.log(`Could not open the Zellij session automatically${attached.error ? ` (${attached.error})` : ''}.`);
94
+ if (launch.attach_command_with_env)
95
+ console.log(`Attach with: ${launch.attach_command_with_env}`);
96
+ }
97
+ return launch;
98
+ }
99
+ if (launch.attach_command_with_env)
85
100
  console.log(`Attach with: ${launch.attach_command_with_env}`);
86
101
  return launch;
87
102
  }
103
+ // Decide whether to take over the current terminal with a foreground Zellij
104
+ // attach. We only do this for genuinely interactive launches; piped, JSON,
105
+ // non-TTY, or already-inside-Zellij invocations keep the previous behaviour of
106
+ // printing a manual "Attach with:" hint. Use --no-attach (or
107
+ // SKS_NO_ZELLIJ_ATTACH=1) to force the background-only behaviour, and --attach
108
+ // to force attaching even without a detected TTY.
109
+ function shouldAutoAttachZellij(args) {
110
+ const list = (args || []).map((arg) => String(arg));
111
+ if (list.includes('--no-attach'))
112
+ return false;
113
+ if (list.includes('--json'))
114
+ return false;
115
+ if (process.env.SKS_NO_ZELLIJ_ATTACH === '1')
116
+ return false;
117
+ // Nested attach is rejected by Zellij when already inside a session.
118
+ if (process.env.ZELLIJ)
119
+ return false;
120
+ if (list.includes('--attach'))
121
+ return true;
122
+ return Boolean(process.stdout.isTTY && process.stdin.isTTY);
123
+ }
88
124
  function formatMadZellijAction(launch) {
89
125
  const blockers = launch.blockers?.join(', ') || launch.warnings?.join(', ') || 'check Zellij installation';
90
126
  const details = [
@@ -189,6 +225,8 @@ function madLaunchOnlyFlags() {
189
225
  '--MAD',
190
226
  '--mad-sks',
191
227
  '--high',
228
+ '--attach',
229
+ '--no-attach',
192
230
  '--no-auto-install-zellij',
193
231
  '--allow-system',
194
232
  '--allow-db-write',
@@ -4,6 +4,7 @@ import { readJson, sksRoot } from '../fsx.js';
4
4
  import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
5
5
  import { buildNarutoCloneRoster, systemSafeNarutoConcurrency } from '../agents/agent-roster.js';
6
6
  import { DEFAULT_NARUTO_CLONES, MAX_NARUTO_AGENT_COUNT } from '../agents/agent-schema.js';
7
+ import { attachZellijSessionInteractive, launchZellijLayout } from '../zellij/zellij-launcher.js';
7
8
  const NARUTO_RESULT_SCHEMA = 'sks.naruto-command-result.v1';
8
9
  const NARUTO_ROUTE = '$Naruto';
9
10
  // $Naruto — Shadow Clone Swarm (影分身 / Kage Bunshin no Jutsu).
@@ -21,6 +22,7 @@ export async function narutoCommand(commandOrArgs = 'naruto', maybeArgs = []) {
21
22
  return narutoRun(parsed);
22
23
  }
23
24
  async function narutoRun(parsed) {
25
+ const root = await sksRoot();
24
26
  const roster = buildNarutoCloneRoster({
25
27
  clones: parsed.clones,
26
28
  prompt: parsed.prompt,
@@ -70,14 +72,35 @@ async function narutoRun(parsed) {
70
72
  concurrency_capped: clones > (result.target_active_slots ?? activeSlots),
71
73
  system: { cores: safe.cores, free_gb: safe.free_gb, safe_concurrency: safe.cap, heavy_backend: safe.heavy },
72
74
  proof: result.proof?.status || 'missing',
73
- run: result
75
+ run: result,
76
+ zellij: null
74
77
  };
78
+ if (!parsed.json && !parsed.mock && !parsed.noOpenZellij) {
79
+ const ledgerRoot = result.ledger_root
80
+ ? path.join(root, result.ledger_root)
81
+ : path.join(root, '.sneakoscope', 'missions', result.mission_id, 'agents');
82
+ summary.zellij = await launchZellijLayout({
83
+ root,
84
+ missionId: result.mission_id,
85
+ ledgerRoot,
86
+ kind: 'naruto',
87
+ slotCount: summary.target_active_slots,
88
+ dryRun: false,
89
+ attach: false
90
+ });
91
+ if (summary.zellij?.ok && summary.zellij.capability?.status === 'ok' && parsed.attach)
92
+ attachZellijSessionInteractive(summary.zellij.session_name, { cwd: process.cwd() });
93
+ }
75
94
  return emit(parsed, summary, () => {
76
95
  console.log('🍥 Shadow Clone Jutsu — Kage Bunshin no Jutsu');
77
96
  console.log('Mission: ' + result.mission_id);
78
97
  console.log('Clones: ' + summary.clones + ' / max ' + MAX_NARUTO_AGENT_COUNT + ', running ' + summary.target_active_slots + ' at a time' + (summary.concurrency_capped ? ` (throttled to host capacity: ${safe.cores} cores, ${safe.free_gb} GB free)` : ''));
79
98
  console.log('Backend: ' + result.backend);
80
99
  console.log('Proof: ' + summary.proof);
100
+ if (summary.zellij?.ok && summary.zellij.capability?.status === 'ok')
101
+ console.log('Zellij: prepared ' + summary.target_active_slots + ' native session lane(s) in ' + summary.zellij.session_name);
102
+ else if (summary.zellij?.ok)
103
+ console.log('Zellij: optional live panes unavailable (' + ((summary.zellij.warnings || []).join('; ') || summary.zellij.capability?.status || 'unknown') + ')');
81
104
  });
82
105
  }
83
106
  async function narutoStatus(parsed) {
@@ -141,9 +164,11 @@ function parseNarutoArgs(args = []) {
141
164
  const writeModeRaw = String(readOption(args, '--write-mode', hasFlag(args, '--parallel-write') ? 'parallel' : '') || '');
142
165
  const writeMode = (['proof-safe', 'parallel', 'serial', 'off'].includes(writeModeRaw) ? writeModeRaw : null);
143
166
  const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', 'latest')));
167
+ const noOpenZellij = hasFlag(args, '--no-open-zellij') || hasFlag(args, '--no-zellij');
168
+ const attach = hasFlag(args, '--attach');
144
169
  const valueFlags = new Set(['--clones', '--agents', '--work-items', '--backend', '--write-mode', '--mission', '--mission-id']);
145
170
  const prompt = positionalArgs(rest, valueFlags).join(' ').trim() || 'Naruto shadow clone swarm run';
146
- return { action, prompt, clones, workItems, backend, mock, real, readonly, writeMode, json, missionId };
171
+ return { action, prompt, clones, workItems, backend, mock, real, readonly, writeMode, json, missionId, noOpenZellij, attach };
147
172
  }
148
173
  function clampClones(value) {
149
174
  if (!Number.isFinite(value) || value < 1)
@@ -53,7 +53,8 @@ async function researchPrepare(args) {
53
53
  task: prompt,
54
54
  required_skills: route.requiredSkills,
55
55
  context7_required: context7Required,
56
- subagents_required: routeRequiresSubagents(route, prompt),
56
+ subagents_required: false,
57
+ native_sessions_required: routeRequiresSubagents(route, prompt),
57
58
  reflection_required: reflectionRequiredForRoute(route),
58
59
  original_stop_gate: route.stopGate,
59
60
  stop_gate: route.stopGate,
@@ -72,8 +73,10 @@ async function researchPrepare(args) {
72
73
  implementation_allowed: false,
73
74
  context7_required: context7Required,
74
75
  context7_verified: false,
75
- subagents_required: routeRequiresSubagents(route, prompt),
76
- subagents_verified: false,
76
+ subagents_required: false,
77
+ subagents_verified: true,
78
+ native_sessions_required: routeRequiresSubagents(route, prompt),
79
+ native_sessions_verified: false,
77
80
  reflection_required: reflectionRequiredForRoute(route),
78
81
  visible_progress_required: true,
79
82
  context_tracking: 'triwiki',
@@ -364,11 +364,11 @@ function fastModeActionFromPrompt(prompt = '') {
364
364
  .trimStart()
365
365
  .toLowerCase();
366
366
  const token = afterRoute.match(/^[^\s?!.,;:()"'`]+/)?.[0] || '';
367
- if (['off', 'disable', 'disabled', 'standard', 'slow', '끄기', '꺼', '꺼줘'].includes(token) || token.startsWith('끄') || token.startsWith('꺼'))
367
+ if (['off', 'disable', 'disabled', 'standard', 'default', 'slow', '끄기', '꺼', '꺼줘'].includes(token) || token.startsWith('끄') || token.startsWith('꺼'))
368
368
  return 'off';
369
- if (['on', 'enable', 'enabled', 'fast', '켜기', '켜', '켜줘'].includes(token) || token.startsWith('켜'))
369
+ if (['on', 'enable', 'enabled', 'fast', 'priority', '켜기', '켜', '켜줘'].includes(token) || token.startsWith('켜'))
370
370
  return 'on';
371
- if (['clear', 'reset', 'default', '초기화', '기본'].includes(token) || token.startsWith('초기화'))
371
+ if (['clear', 'reset', '초기화', '기본'].includes(token) || token.startsWith('초기화'))
372
372
  return 'clear';
373
373
  return 'status';
374
374
  }
@@ -20,6 +20,7 @@ export declare function buildTeamPlan(id: any, prompt: any, opts?: any): {
20
20
  prompt: any;
21
21
  agent_session_count: number;
22
22
  default_agent_session_count: number;
23
+ target_active_slots: any;
23
24
  role_counts: Record<string, number>;
24
25
  session_policy: string;
25
26
  review_policy: {
@@ -119,7 +120,7 @@ export declare function buildTeamPlan(id: any, prompt: any, opts?: any): {
119
120
  goal: string;
120
121
  agents: string[];
121
122
  output: string;
122
- max_parallel_subagents?: never;
123
+ max_parallel_native_sessions?: never;
123
124
  write_policy?: never;
124
125
  commands?: never;
125
126
  min_reviewer_lanes?: never;
@@ -127,7 +128,7 @@ export declare function buildTeamPlan(id: any, prompt: any, opts?: any): {
127
128
  id: string;
128
129
  goal: string;
129
130
  agents: any[];
130
- max_parallel_subagents: number;
131
+ max_parallel_native_sessions: any;
131
132
  write_policy: string;
132
133
  output: string;
133
134
  commands?: never;
@@ -138,14 +139,14 @@ export declare function buildTeamPlan(id: any, prompt: any, opts?: any): {
138
139
  agents: string[];
139
140
  commands: string[];
140
141
  output: string;
141
- max_parallel_subagents?: never;
142
+ max_parallel_native_sessions?: never;
142
143
  write_policy?: never;
143
144
  min_reviewer_lanes?: never;
144
145
  } | {
145
146
  id: string;
146
147
  goal: string;
147
148
  agents: any[];
148
- max_parallel_subagents: number;
149
+ max_parallel_native_sessions: any;
149
150
  write_policy: string;
150
151
  output?: never;
151
152
  commands?: never;
@@ -155,7 +156,7 @@ export declare function buildTeamPlan(id: any, prompt: any, opts?: any): {
155
156
  goal: string;
156
157
  agents: string[];
157
158
  output?: never;
158
- max_parallel_subagents?: never;
159
+ max_parallel_native_sessions?: never;
159
160
  write_policy?: never;
160
161
  commands?: never;
161
162
  min_reviewer_lanes?: never;
@@ -165,7 +166,7 @@ export declare function buildTeamPlan(id: any, prompt: any, opts?: any): {
165
166
  agents: any[];
166
167
  min_reviewer_lanes: number;
167
168
  output?: never;
168
- max_parallel_subagents?: never;
169
+ max_parallel_native_sessions?: never;
169
170
  write_policy?: never;
170
171
  commands?: never;
171
172
  })[];