sneakoscope 2.0.7 → 2.0.9

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 (78) hide show
  1. package/README.md +1 -1
  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 +44 -8
  8. package/dist/commands/zellij.js +144 -1
  9. package/dist/core/agents/agent-command-surface.js +4 -2
  10. package/dist/core/agents/agent-orchestrator.js +5 -2
  11. package/dist/core/agents/agent-patch-schema.js +4 -2
  12. package/dist/core/agents/native-cli-session-swarm.js +81 -9
  13. package/dist/core/commands/mad-sks-command.js +17 -1
  14. package/dist/core/commands/naruto-command.js +99 -7
  15. package/dist/core/fsx.js +1 -1
  16. package/dist/core/git/git-repo-detection.js +7 -0
  17. package/dist/core/git/git-worktree-cleanup.js +14 -3
  18. package/dist/core/git/git-worktree-diff.js +7 -2
  19. package/dist/core/git/git-worktree-manager.js +9 -2
  20. package/dist/core/git/git-worktree-patch-envelope.js +5 -5
  21. package/dist/core/naruto/naruto-active-pool.js +108 -0
  22. package/dist/core/naruto/naruto-concurrency-governor.js +16 -1
  23. package/dist/core/naruto/naruto-work-graph.js +2 -1
  24. package/dist/core/release/release-gate-cache-v2.js +117 -0
  25. package/dist/core/release/release-gate-dag.js +190 -0
  26. package/dist/core/release/release-gate-hermetic-env.js +32 -0
  27. package/dist/core/release/release-gate-node.js +62 -0
  28. package/dist/core/release/release-gate-report.js +11 -0
  29. package/dist/core/release/release-gate-resource-governor.js +54 -0
  30. package/dist/core/release/release-gate-scheduler.js +15 -0
  31. package/dist/core/version.js +1 -1
  32. package/dist/core/zellij/zellij-dashboard-pane.js +71 -0
  33. package/dist/core/zellij/zellij-dashboard-renderer.js +58 -0
  34. package/dist/core/zellij/zellij-launcher.js +3 -3
  35. package/dist/core/zellij/zellij-layout-builder.js +1 -1
  36. package/dist/core/zellij/zellij-right-column-layout-proof.js +42 -0
  37. package/dist/core/zellij/zellij-right-column-manager.js +245 -0
  38. package/dist/core/zellij/zellij-worker-pane-manager.js +180 -15
  39. package/dist/scripts/codex-sdk-release-review-pipeline-check.js +5 -5
  40. package/dist/scripts/doctor-fix-proves-codex-read-check.js +26 -5
  41. package/dist/scripts/git-worktree-diff-envelope-check.js +17 -0
  42. package/dist/scripts/git-worktree-dirty-lock-check.js +17 -0
  43. package/dist/scripts/git-worktree-dirty-main-detection-check.js +14 -0
  44. package/dist/scripts/git-worktree-integration-primary-check.js +22 -0
  45. package/dist/scripts/git-worktree-manifest-append-check.js +18 -0
  46. package/dist/scripts/git-worktree-untracked-diff-check.js +18 -0
  47. package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
  48. package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +2 -2
  49. package/dist/scripts/naruto-concurrency-governor-check.js +2 -1
  50. package/dist/scripts/naruto-extreme-parallelism-check.js +22 -0
  51. package/dist/scripts/naruto-real-active-pool-check.js +38 -0
  52. package/dist/scripts/naruto-work-graph-check.js +1 -1
  53. package/dist/scripts/naruto-worktree-coding-blackbox.js +29 -0
  54. package/dist/scripts/naruto-zellij-dynamic-right-column-check.js +21 -0
  55. package/dist/scripts/product-design-auto-install-check.js +3 -3
  56. package/dist/scripts/product-design-plugin-routing-check.js +3 -3
  57. package/dist/scripts/release-cache-glob-hashing-check.js +42 -0
  58. package/dist/scripts/release-dag-full-coverage-check.js +35 -0
  59. package/dist/scripts/release-gate-dag-runner-check.js +17 -0
  60. package/dist/scripts/release-gate-dag-runner.js +32 -0
  61. package/dist/scripts/release-gate-worker.js +10 -0
  62. package/dist/scripts/release-metadata-1-19-check.js +8 -2
  63. package/dist/scripts/release-parallel-speed-budget-check.js +79 -0
  64. package/dist/scripts/release-readiness-report.js +1 -1
  65. package/dist/scripts/release-stability-report-check.js +99 -0
  66. package/dist/scripts/zellij-dashboard-pane-check.js +70 -0
  67. package/dist/scripts/zellij-dashboard-watch.js +41 -0
  68. package/dist/scripts/zellij-developer-controls-check.js +20 -0
  69. package/dist/scripts/zellij-dynamic-pane-lifecycle-check.js +21 -0
  70. package/dist/scripts/zellij-initial-main-only-blackbox.js +28 -0
  71. package/dist/scripts/zellij-right-column-geometry-proof.js +29 -0
  72. package/dist/scripts/zellij-right-column-manager-check.js +22 -0
  73. package/dist/scripts/zellij-worker-pane-manager-check.js +2 -1
  74. package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +7 -6
  75. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +185 -0
  76. package/package.json +32 -5
  77. package/schemas/release/release-gate-node.schema.json +52 -0
  78. package/schemas/zellij/zellij-right-column-state.schema.json +41 -0
@@ -0,0 +1,71 @@
1
+ import path from 'node:path';
2
+ import { ensureDir, nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { runZellij } from './zellij-command.js';
4
+ import { extractZellijPaneIdFromOutput } from './zellij-lane-runtime.js';
5
+ import { buildZellijDashboardSnapshot } from './zellij-dashboard-renderer.js';
6
+ export const ZELLIJ_DASHBOARD_PANE_SCHEMA = 'sks.zellij-dashboard-pane.v1';
7
+ export async function openZellijDashboardPane(input) {
8
+ const root = path.resolve(input.root);
9
+ const cwd = input.cwd || root;
10
+ const paneTitle = `dashboard · ${input.missionId}`;
11
+ const dashboardDir = path.join(root, '.sneakoscope', 'missions', input.missionId);
12
+ await ensureDir(dashboardDir);
13
+ const snapshot = buildZellijDashboardSnapshot({ ...(input.snapshot || {}), mission_id: input.missionId });
14
+ const snapshotPath = path.join(dashboardDir, 'zellij-dashboard-snapshot.json');
15
+ await writeJsonAtomic(snapshotPath, snapshot);
16
+ const watchScript = path.join(root, 'dist', 'scripts', 'zellij-dashboard-watch.js');
17
+ const command = `${shellQuote(process.execPath)} ${shellQuote(watchScript)} --snapshot ${shellQuote(snapshotPath)} --interval-ms 1000`;
18
+ const createSession = await runZellij(['attach', '--create-background', input.sessionName], { cwd, timeoutMs: 5000, optional: true });
19
+ const launch = await runZellij(['--session', input.sessionName, 'action', 'new-pane', '--direction', 'right', '--name', paneTitle, '--', 'sh', '-lc', command], {
20
+ cwd,
21
+ timeoutMs: 5000,
22
+ optional: false
23
+ });
24
+ const stdoutPaneId = launch.ok ? extractZellijPaneIdFromOutput(launch.stdout_tail) : null;
25
+ const listed = await runZellij(['--session', input.sessionName, 'action', 'list-panes', '--json', '--all'], { cwd, timeoutMs: 5000, optional: true });
26
+ const rows = parseRows(listed.stdout_tail);
27
+ const pane = stdoutPaneId ? null : rows.find((row) => String(row.title || row.name || '').includes(paneTitle));
28
+ const paneId = stdoutPaneId || pane?.pane_id || pane?.paneId || pane?.id || null;
29
+ const dump = await runZellij(['--session', input.sessionName, 'action', 'dump-screen'], { cwd, timeoutMs: 5000, optional: true });
30
+ const source = stdoutPaneId ? 'zellij_dashboard_new_pane_stdout' : paneId ? 'zellij_dashboard_list_panes' : 'zellij_dashboard_missing';
31
+ const blockers = [
32
+ ...(createSession.ok || /Session already exists/i.test(createSession.stderr_tail || '') ? [] : createSession.blockers.map((blocker) => `zellij_dashboard_session_${blocker}`)),
33
+ ...(launch.ok ? [] : launch.blockers.map((blocker) => `zellij_dashboard_pane_${blocker}`)),
34
+ ...(paneId ? [] : ['zellij_dashboard_pane_id_missing'])
35
+ ];
36
+ const record = {
37
+ schema: ZELLIJ_DASHBOARD_PANE_SCHEMA,
38
+ generated_at: nowIso(),
39
+ ok: blockers.length === 0,
40
+ mission_id: input.missionId,
41
+ session_name: input.sessionName,
42
+ pane_title: paneTitle,
43
+ pane_id: paneId == null ? null : String(paneId),
44
+ pane_id_source: source,
45
+ pane_kind: 'dashboard',
46
+ worker_pane: false,
47
+ command,
48
+ launch,
49
+ list_panes: listed,
50
+ dump_screen: dump,
51
+ snapshot,
52
+ blockers
53
+ };
54
+ await writeJsonAtomic(path.join(dashboardDir, 'zellij-dashboard-pane.json'), record);
55
+ return record;
56
+ }
57
+ function parseRows(text) {
58
+ if (!String(text || '').trim())
59
+ return [];
60
+ try {
61
+ const parsed = JSON.parse(String(text));
62
+ return Array.isArray(parsed) ? parsed : Array.isArray(parsed?.panes) ? parsed.panes : [];
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ }
68
+ function shellQuote(value) {
69
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
70
+ }
71
+ //# sourceMappingURL=zellij-dashboard-pane.js.map
@@ -0,0 +1,58 @@
1
+ import { nowIso } from '../fsx.js';
2
+ export const ZELLIJ_DASHBOARD_RENDER_SCHEMA = 'sks.zellij-dashboard-render.v1';
3
+ export function buildZellijDashboardSnapshot(input) {
4
+ return {
5
+ schema: ZELLIJ_DASHBOARD_RENDER_SCHEMA,
6
+ generated_at: nowIso(),
7
+ mission_id: input.mission_id,
8
+ mode: input.mode || 'naruto',
9
+ route: input.route || input.mode || '$Naruto',
10
+ provider: input.provider || 'unknown',
11
+ service_tier: input.service_tier || 'fast',
12
+ backpressure: input.backpressure || 'normal',
13
+ backend_counts: input.backend_counts || { 'codex-sdk': 1 },
14
+ placement_counts: input.placement_counts || { 'zellij-pane': 1 },
15
+ active_workers: Number(input.active_workers || 0),
16
+ visible_panes: Number(input.visible_panes || 0),
17
+ headless_workers: Number(input.headless_workers || 0),
18
+ queue_depth: Number(input.queue_depth || 0),
19
+ worktrees: input.worktrees || { active: 0, completed: 0, retained: 0 },
20
+ local_llm: input.local_llm || { tps: 0, queue: 0 },
21
+ gpt_final_status: input.gpt_final_status || 'not_started',
22
+ gate_progress: input.gate_progress || 'not_release',
23
+ patch_verify: input.patch_verify || {
24
+ patches: 0,
25
+ gpt_approved: 0,
26
+ conflicts: 0,
27
+ verification_running: 0,
28
+ verification_passed: 0,
29
+ verification_failed: 0
30
+ },
31
+ workers: input.workers || [],
32
+ latest_blockers: input.latest_blockers || []
33
+ };
34
+ }
35
+ export function renderZellijDashboardText(snapshot) {
36
+ const backendCounts = Object.entries(snapshot.backend_counts).map(([key, value]) => `${key}=${value}`).join(' ');
37
+ const placementCounts = Object.entries(snapshot.placement_counts).map(([key, value]) => `${key}=${value}`).join(' ');
38
+ return [
39
+ 'SKS Dashboard',
40
+ `Mission: ${snapshot.mission_id}`,
41
+ `Route / mode: ${snapshot.route} / ${snapshot.mode} / ${snapshot.service_tier}`,
42
+ `Provider: ${snapshot.provider}`,
43
+ `Backend counts: ${backendCounts || 'none'}`,
44
+ `Placement counts: ${placementCounts || 'none'}`,
45
+ `Active / visible / headless / queued: ${snapshot.active_workers}/${snapshot.visible_panes}/${snapshot.headless_workers}/${snapshot.queue_depth}`,
46
+ `Backpressure: ${snapshot.backpressure}`,
47
+ `Worktrees active/completed/retained: ${snapshot.worktrees.active}/${snapshot.worktrees.completed}/${snapshot.worktrees.retained}`,
48
+ `Local LLM TPS / queue: ${snapshot.local_llm.tps}/${snapshot.local_llm.queue}`,
49
+ `GPT final status: ${snapshot.gpt_final_status}`,
50
+ `Gate progress: ${snapshot.gate_progress}`,
51
+ `Patch / verify: patches ${snapshot.patch_verify.patches} · approved ${snapshot.patch_verify.gpt_approved} · conflicts ${snapshot.patch_verify.conflicts} · verify ${snapshot.patch_verify.verification_running}/${snapshot.patch_verify.verification_passed}/${snapshot.patch_verify.verification_failed}`,
52
+ 'Workers:',
53
+ ...(snapshot.workers.length ? snapshot.workers.slice(0, 12).map((worker) => `${worker.slot_id} gen-${worker.generation_index} ${worker.role} ${worker.backend}/${worker.provider}/${worker.service_tier} WT:${worker.worktree_id || '-'} ${worker.status} ${worker.current_file || ''} ${worker.latest_heartbeat || ''}`) : ['none']),
54
+ `Latest blockers: ${snapshot.latest_blockers.length ? snapshot.latest_blockers.join(', ') : 'none'}`,
55
+ 'Controls: q detach | /stop selected | /focus slot | /logs'
56
+ ].join('\n');
57
+ }
58
+ //# sourceMappingURL=zellij-dashboard-renderer.js.map
@@ -19,7 +19,7 @@ export async function launchZellijLayout(opts = {}) {
19
19
  ledgerRoot,
20
20
  cwd: opts.cwd || root,
21
21
  kind: opts.kind || 'agent',
22
- slotCount: opts.slotCount || 1,
22
+ slotCount: opts.slotCount ?? 1,
23
23
  title: `SKS ${opts.kind || 'agent'} ${missionId}`,
24
24
  codexArgs: opts.codexArgs || [],
25
25
  launchEnv: opts.launchEnv || {}
@@ -124,7 +124,7 @@ export async function launchMadZellijUi(args = [], opts = {}) {
124
124
  const launchOpts = {
125
125
  ...opts,
126
126
  kind: 'mad',
127
- slotCount: opts.slotCount || 1
127
+ slotCount: opts.slotCount ?? 1
128
128
  };
129
129
  const resolvedSession = session || opts.session;
130
130
  if (resolvedSession)
@@ -135,7 +135,7 @@ export async function launchTeamZellijView(opts = {}) {
135
135
  return launchZellijLayout({
136
136
  ...opts,
137
137
  kind: 'team',
138
- slotCount: opts.slotCount || 5
138
+ slotCount: opts.slotCount ?? 5
139
139
  });
140
140
  }
141
141
  /**
@@ -3,7 +3,7 @@ import { ensureDir, nowIso, packageRoot, writeTextAtomic } from '../fsx.js';
3
3
  import { writeZellijLaneRuntimeManifest } from './zellij-lane-runtime.js';
4
4
  export const ZELLIJ_LAYOUT_SCHEMA = 'sks.zellij-layout.v1';
5
5
  export function buildZellijLayoutKdl(input) {
6
- const slotCount = Math.max(1, Number(input.slotCount || 1));
6
+ const slotCount = Math.max(0, Number(input.slotCount ?? 1));
7
7
  const sessionName = input.sessionName || `sks-${input.missionId}`;
8
8
  const cwd = path.resolve(input.cwd || process.cwd());
9
9
  const ledgerRoot = path.resolve(input.ledgerRoot);
@@ -0,0 +1,42 @@
1
+ export function evaluateZellijRightColumnGeometry(input) {
2
+ const tolerance = Math.max(0, Number(input.tolerance ?? 2));
3
+ const main = input.panes.find((pane) => pane.role === 'main') || null;
4
+ const dashboard = input.panes.find((pane) => pane.role === 'dashboard') || null;
5
+ const workers = input.panes.filter((pane) => pane.role === 'worker');
6
+ const workerGeometries = workers.map((pane) => pane.geometry).filter(Boolean);
7
+ const baseX = workerGeometries[0]?.x;
8
+ const sameRightX = baseX == null ? null : workerGeometries.every((geometry) => geometry.x != null && Math.abs(geometry.x - baseX) <= tolerance);
9
+ const rightOfMain = !main?.geometry ? null : workerGeometries.every((geometry) => {
10
+ if (geometry.x == null || main.geometry?.x == null || main.geometry?.width == null)
11
+ return false;
12
+ return geometry.x >= main.geometry.x + main.geometry.width - tolerance;
13
+ });
14
+ const increasingY = workerGeometries.every((geometry, index) => {
15
+ if (index === 0)
16
+ return true;
17
+ const previous = workerGeometries[index - 1];
18
+ return geometry.y != null && previous?.y != null && geometry.y > previous.y;
19
+ });
20
+ const visibleCapOk = workers.length <= Math.max(0, Math.floor(Number(input.visiblePaneCap || 0)));
21
+ const blockers = [
22
+ ...(!main ? ['right_column_main_pane_missing'] : []),
23
+ ...(!dashboard ? ['right_column_dashboard_missing'] : []),
24
+ ...(sameRightX === false ? ['right_column_worker_x_range_mismatch'] : []),
25
+ ...(rightOfMain === false ? ['right_column_workers_not_right_of_main'] : []),
26
+ ...(increasingY === false ? ['right_column_workers_not_stacked_down'] : []),
27
+ ...(visibleCapOk ? [] : ['right_column_visible_cap_exceeded'])
28
+ ];
29
+ return {
30
+ schema: 'sks.zellij-right-column-layout-proof.v1',
31
+ ok: blockers.length === 0,
32
+ main_pane_id: main?.pane_id || null,
33
+ dashboard_pane_id: dashboard?.pane_id || null,
34
+ worker_pane_ids: workers.map((pane) => pane.pane_id),
35
+ same_right_x: sameRightX,
36
+ right_of_main: rightOfMain,
37
+ increasing_y: increasingY,
38
+ visible_cap_ok: visibleCapOk,
39
+ blockers
40
+ };
41
+ }
42
+ //# sourceMappingURL=zellij-right-column-layout-proof.js.map
@@ -0,0 +1,245 @@
1
+ import path from 'node:path';
2
+ import { appendJsonl, ensureDir, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
3
+ import { openZellijDashboardPane } from './zellij-dashboard-pane.js';
4
+ export const ZELLIJ_RIGHT_COLUMN_STATE_SCHEMA = 'sks.zellij-right-column-state.v1';
5
+ export async function ensureRightColumn(input) {
6
+ const paths = resolveRightColumnPaths(input.root, input.missionId, input.projectRoot);
7
+ await ensureDir(paths.missionDir);
8
+ const existing = await readRightColumnState(input.root, input.missionId, input.projectRoot);
9
+ if (existing?.status === 'active' && existing.dashboard_pane_id) {
10
+ return writeRightColumnState(paths.statePath, { ...existing, updated_at: nowIso() });
11
+ }
12
+ const creating = await writeRightColumnState(paths.statePath, {
13
+ schema: ZELLIJ_RIGHT_COLUMN_STATE_SCHEMA,
14
+ generated_at: nowIso(),
15
+ updated_at: nowIso(),
16
+ mission_id: input.missionId,
17
+ session_name: input.sessionName,
18
+ status: 'creating',
19
+ dashboard_pane_id: null,
20
+ right_anchor_pane_id: null,
21
+ visible_worker_panes: [],
22
+ headless_workers: [],
23
+ blockers: []
24
+ });
25
+ await appendRightColumnEvent(paths.eventsPath, 'right_column_creating', creating, {});
26
+ const dashboard = await openZellijDashboardPane({
27
+ root: paths.projectRoot,
28
+ missionId: input.missionId,
29
+ sessionName: input.sessionName,
30
+ cwd: input.cwd || paths.projectRoot,
31
+ snapshot: {
32
+ ...input.dashboardSnapshot,
33
+ mission_id: input.missionId,
34
+ gate_progress: input.dashboardSnapshot.gate_progress || 'right-column:first-worker'
35
+ }
36
+ }).catch((err) => ({
37
+ ok: false,
38
+ pane_id: null,
39
+ blockers: [`zellij_dashboard_exception:${err?.message || String(err)}`]
40
+ }));
41
+ const blockers = Array.isArray(dashboard.blockers) ? dashboard.blockers : [];
42
+ const active = await writeRightColumnState(paths.statePath, {
43
+ ...creating,
44
+ updated_at: nowIso(),
45
+ status: blockers.length ? 'creating' : 'active',
46
+ dashboard_pane_id: dashboard.pane_id ? String(dashboard.pane_id) : null,
47
+ right_anchor_pane_id: dashboard.pane_id ? String(dashboard.pane_id) : null,
48
+ blockers
49
+ });
50
+ await appendRightColumnEvent(paths.eventsPath, 'right_column_created', active, { ok: active.status === 'active' });
51
+ await appendRightColumnEvent(paths.eventsPath, 'dashboard_pane_created', active, { pane_id: active.dashboard_pane_id, blockers });
52
+ return active;
53
+ }
54
+ export async function prepareWorkerInRightColumn(input) {
55
+ const paths = resolveRightColumnPaths(input.root, input.missionId, input.projectRoot);
56
+ const state = await ensureRightColumn({
57
+ root: input.root,
58
+ ...(input.projectRoot ? { projectRoot: input.projectRoot } : {}),
59
+ missionId: input.missionId,
60
+ sessionName: input.sessionName,
61
+ cwd: input.cwd,
62
+ dashboardSnapshot: input.dashboardSnapshot
63
+ });
64
+ const activeVisible = state.visible_worker_panes.filter((pane) => pane.status === 'launching' || pane.status === 'running');
65
+ const cap = Math.max(1, Math.floor(Number(input.visiblePaneCap || 1)));
66
+ if (activeVisible.length >= cap) {
67
+ const next = await recordHeadlessWorkerInRightColumn({
68
+ root: input.root,
69
+ ...(input.projectRoot ? { projectRoot: input.projectRoot } : {}),
70
+ missionId: input.missionId,
71
+ sessionName: input.sessionName,
72
+ slotId: input.worker.slotId,
73
+ generationIndex: input.worker.generationIndex,
74
+ reason: `visible_pane_cap:${cap}`
75
+ });
76
+ return { state: next, placement: 'headless', focusPaneId: null, yOrder: null };
77
+ }
78
+ const lastVisible = activeVisible[activeVisible.length - 1];
79
+ const focusPaneId = lastVisible?.pane_id || state.right_anchor_pane_id || state.dashboard_pane_id || null;
80
+ const yOrder = Math.max(1, ...state.visible_worker_panes.map((pane) => Number(pane.y_order || 0) + 1));
81
+ const next = await writeRightColumnState(paths.statePath, {
82
+ ...state,
83
+ updated_at: nowIso(),
84
+ right_anchor_pane_id: focusPaneId,
85
+ visible_worker_panes: [
86
+ ...state.visible_worker_panes,
87
+ {
88
+ pane_id: null,
89
+ slot_id: input.worker.slotId,
90
+ generation_index: input.worker.generationIndex,
91
+ y_order: yOrder,
92
+ status: 'launching'
93
+ }
94
+ ]
95
+ });
96
+ await appendRightColumnEvent(paths.eventsPath, 'scheduler_slot_reserved', next, {
97
+ slot_id: input.worker.slotId,
98
+ generation_index: input.worker.generationIndex,
99
+ y_order: yOrder
100
+ });
101
+ return { state: next, placement: 'zellij-pane', focusPaneId, yOrder };
102
+ }
103
+ export async function recordWorkerPaneInRightColumn(input) {
104
+ const paths = resolveRightColumnPaths(input.root, input.missionId, input.projectRoot);
105
+ const state = await readRightColumnState(input.root, input.missionId, input.projectRoot);
106
+ if (!state)
107
+ return null;
108
+ const yOrder = Math.max(1, Math.floor(Number(input.yOrder || 0))) || Math.max(1, ...state.visible_worker_panes.map((pane) => pane.y_order + 1));
109
+ const slotId = input.record.slot_id;
110
+ const generationIndex = input.record.generation_index;
111
+ const rows = state.visible_worker_panes.filter((pane) => !(pane.slot_id === slotId && pane.generation_index === generationIndex && pane.pane_id == null));
112
+ rows.push({
113
+ pane_id: input.record.pane_id,
114
+ slot_id: slotId,
115
+ generation_index: generationIndex,
116
+ y_order: yOrder,
117
+ status: input.record.status === 'failed' ? 'failed' : 'running'
118
+ });
119
+ const next = await writeRightColumnState(paths.statePath, {
120
+ ...state,
121
+ updated_at: nowIso(),
122
+ right_anchor_pane_id: input.record.pane_id || state.right_anchor_pane_id,
123
+ visible_worker_panes: rows.sort((a, b) => a.y_order - b.y_order),
124
+ blockers: [...new Set([...state.blockers, ...(input.record.blockers || [])])]
125
+ });
126
+ await appendRightColumnEvent(paths.eventsPath, 'worker_pane_created', next, {
127
+ slot_id: slotId,
128
+ generation_index: generationIndex,
129
+ pane_id: input.record.pane_id,
130
+ y_order: yOrder,
131
+ direction_applied: input.record.direction_applied,
132
+ blockers: input.record.blockers
133
+ });
134
+ return next;
135
+ }
136
+ export async function recordHeadlessWorkerInRightColumn(input) {
137
+ const paths = resolveRightColumnPaths(input.root, input.missionId, input.projectRoot);
138
+ const state = await readRightColumnState(input.root, input.missionId, input.projectRoot) || {
139
+ schema: ZELLIJ_RIGHT_COLUMN_STATE_SCHEMA,
140
+ generated_at: nowIso(),
141
+ updated_at: nowIso(),
142
+ mission_id: input.missionId,
143
+ session_name: input.sessionName,
144
+ status: 'absent',
145
+ dashboard_pane_id: null,
146
+ right_anchor_pane_id: null,
147
+ visible_worker_panes: [],
148
+ headless_workers: [],
149
+ blockers: []
150
+ };
151
+ const headless = [
152
+ ...state.headless_workers.filter((row) => !(row.slot_id === input.slotId && row.generation_index === input.generationIndex)),
153
+ { slot_id: input.slotId, generation_index: input.generationIndex, reason: input.reason }
154
+ ];
155
+ const next = await writeRightColumnState(paths.statePath, { ...state, updated_at: nowIso(), headless_workers: headless });
156
+ await appendRightColumnEvent(paths.eventsPath, 'worker_headless_overflow', next, {
157
+ slot_id: input.slotId,
158
+ generation_index: input.generationIndex,
159
+ reason: input.reason
160
+ });
161
+ return next;
162
+ }
163
+ export async function closeWorkerInRightColumn(input) {
164
+ const paths = resolveRightColumnPaths(input.root, input.missionId, input.projectRoot);
165
+ const state = await readRightColumnState(input.root, input.missionId, input.projectRoot);
166
+ if (!state)
167
+ return null;
168
+ const panes = state.visible_worker_panes.map((pane) => {
169
+ const same = pane.slot_id === input.slotId && pane.generation_index === input.generationIndex;
170
+ return same ? { ...pane, pane_id: input.paneId || pane.pane_id, status: input.status } : pane;
171
+ });
172
+ const visibleStillActive = panes.filter((pane) => pane.status === 'launching' || pane.status === 'running');
173
+ const next = await writeRightColumnState(paths.statePath, {
174
+ ...state,
175
+ updated_at: nowIso(),
176
+ status: visibleStillActive.length || state.headless_workers.length ? 'active' : 'draining',
177
+ right_anchor_pane_id: visibleStillActive[visibleStillActive.length - 1]?.pane_id || state.dashboard_pane_id,
178
+ visible_worker_panes: panes
179
+ });
180
+ await appendRightColumnEvent(paths.eventsPath, 'worker_pane_drained', next, {
181
+ slot_id: input.slotId,
182
+ generation_index: input.generationIndex,
183
+ pane_id: input.paneId,
184
+ status: input.status
185
+ });
186
+ return next;
187
+ }
188
+ export async function openWorkerInRightColumn(input) {
189
+ const prepared = await prepareWorkerInRightColumn({
190
+ root: input.root,
191
+ missionId: input.state.mission_id,
192
+ sessionName: input.state.session_name,
193
+ cwd: input.worker.cwd || input.root,
194
+ worker: { slotId: input.worker.slotId, generationIndex: input.worker.generationIndex },
195
+ visiblePaneCap: input.visiblePaneCap,
196
+ dashboardSnapshot: { mission_id: input.state.mission_id }
197
+ });
198
+ return { state: prepared.state, pane: null, placement: prepared.placement };
199
+ }
200
+ export async function readRightColumnState(root, missionId, projectRoot) {
201
+ const paths = resolveRightColumnPaths(root, missionId, projectRoot);
202
+ return readJson(paths.statePath, null);
203
+ }
204
+ export function resolveRightColumnPaths(root, missionId, projectRoot) {
205
+ const resolvedRoot = path.resolve(root);
206
+ const resolvedProjectRoot = projectRoot ? path.resolve(projectRoot) : inferProjectRoot(resolvedRoot, missionId);
207
+ const missionDir = inferMissionDir(resolvedRoot, missionId) || path.join(resolvedProjectRoot, '.sneakoscope', 'missions', missionId);
208
+ return {
209
+ projectRoot: resolvedProjectRoot,
210
+ missionDir,
211
+ statePath: path.join(missionDir, 'zellij-right-column-state.json'),
212
+ eventsPath: path.join(missionDir, 'zellij-right-column-events.jsonl')
213
+ };
214
+ }
215
+ async function writeRightColumnState(file, state) {
216
+ await writeJsonAtomic(file, state);
217
+ return state;
218
+ }
219
+ async function appendRightColumnEvent(file, eventType, state, payload) {
220
+ await appendJsonl(file, {
221
+ schema: 'sks.zellij-right-column-event.v1',
222
+ ts: nowIso(),
223
+ event_type: eventType,
224
+ mission_id: state.mission_id,
225
+ session_name: state.session_name,
226
+ ...payload
227
+ });
228
+ }
229
+ function inferMissionDir(root, missionId) {
230
+ if (path.basename(root) === 'agents' && path.basename(path.dirname(root)) === missionId)
231
+ return path.dirname(root);
232
+ if (path.basename(root) === missionId && path.basename(path.dirname(root)) === 'missions')
233
+ return root;
234
+ return null;
235
+ }
236
+ function inferProjectRoot(root, missionId) {
237
+ if (path.basename(root) === 'agents' && path.basename(path.dirname(root)) === missionId) {
238
+ return path.dirname(path.dirname(path.dirname(path.dirname(root))));
239
+ }
240
+ if (path.basename(root) === missionId && path.basename(path.dirname(root)) === 'missions') {
241
+ return path.dirname(path.dirname(path.dirname(root)));
242
+ }
243
+ return root;
244
+ }
245
+ //# sourceMappingURL=zellij-right-column-manager.js.map