sneakoscope 1.21.6 → 1.21.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,40 +16,26 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
- SKS **1.21.6** promotes OpenAI Codex CLI `rust-v0.136.0` as the current compatibility baseline. The release matrix tracks 0.136 session archive/unarchive, app-server `--stdio`, resumed-turn/status behavior, `CODEX_API_KEY` remote registration, short-lived remote-control tokens, elevated Windows sandbox setup, feature-gated image-generation extension support, ChatGPT auth refresh handling, command-safety hardening, sandbox cleanup, Bedrock region fallback, and rmcp 1.7.0 compatibility.
19
+ SKS **1.21.7** is a Zellij/Naruto runtime patch. Real Zellij workers now run as pane-bound native CLI sessions: SKS creates or targets the Zellij session, splits a named slot pane, launches the worker inside that pane, and reads `worker-result.json` plus heartbeat/log artifacts back in the parent scheduler.
20
20
 
21
- This release also tightens the day-to-day operator path:
21
+ What changed:
22
22
 
23
- - Zellij lane panes now surface the native worker session they are following, including per-slot worker status and stdout/stderr/heartbeat tails.
24
- - Native terminal drag-copy is the default in SKS-launched Zellij sessions; hover-pane mouse routing is opt-in with `SKS_ZELLIJ_MOUSE_MODE=1`.
25
- - Update prompts compare npm latest against an effective installed version that includes source, PATH, and global npm package metadata, so completed global updates stop being re-offered.
26
- - `sks doctor --fix` can remove duplicate global `sks`/`sneakoscope` npm installs while exempting the Sneakoscope source checkout.
23
+ - `--backend zellij --real` now uses real slot panes for worker sessions instead of only recording synthetic lane artifacts.
24
+ - Pane ids are reconciled from `zellij --session <name> action list-panes --json --all` when `new-pane` does not print one.
25
+ - The parent/worker transport is explicit: `worker-result.json`, `worker-heartbeat.jsonl`, `worker.stdout.log`, and `worker.stderr.log`.
26
+ - Zellij lane supervisor actions target the intended session with `--session`, so pane creation no longer depends on ambient terminal state.
27
27
 
28
- It carries forward the 0.135-era routing/readiness fixes: mixed frustration plus explicit implementation prompts still route to `$Team`, bare `context7 mcp` no longer implies `$DB`, Git Actions readiness uses the `codex remote-control` command/version capability, and repeated substantive prompts prepare fresh Team/Research-style native sessions. Zellij lane, Naruto, terminal scrollback, MAD cockpit, Goal bridge, and Fast profile fixes remain in place.
29
-
30
- 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.
31
-
32
- 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.
33
-
34
- 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`.
35
-
36
- SKS **1.20.1** introduces the **SKS Core Skill Engine** — a SkillOpt-style self-evolving skill layer — while locking the harness into a deploy-ready stable build. Skills are the agent's *external, versioned state* (Core Skill Cards): an optimizer proposes **bounded** add/delete/replace edits to a single skill document under a **textual edit budget**, edits are accepted **only on strict held-out improvement**, rejected edits are buffered so they are never retried, and the **deployed snapshot is immutable**. Critically, the optimizer runs only in training/evaluation — the **deployment/inference path reads the deployed snapshot and never makes an extra model call**, and a skill patch can never mutate code/config/package/global files.
37
-
38
- 1.20.1 also adds a **Requested-Scope Contract + Mutation Ledger** so SKS performs **no side effect the user did not request** (deny-by-default; global/destructive mutations require explicit `--yes`/env opt-in and a backup or no-op reason), and a **dynamic, risk-based release pipeline** (`release:gate-planner` builds `release-gates.json`; `release:check:dynamic` runs only P0 always-on gates plus gates whose files changed; `release:gate-budget` reports the slowest gates) so unrelated heavy gates are skipped during incremental checks while publish never skips a required gate.
39
-
40
- It carries forward the 1.19.x hardening unchanged: legacy 1.18.x/1.19.x→1.20.1 **zero-break upgrade** (user `model`/`service_tier`/`model_reasoning_effort` and user-disabled Codex App flags never overwritten; existing skill cards preserved), the **migration transaction journal** (`.sneakoscope/reports/migration-1.20.1-journal.jsonl`), **Zellij launch-command truth** + real-session **heartbeat-timeout blocker** + the redesigned **Zellij lane UI**, **packlist/publish-performance** gates, a **postinstall safe-side-effects** gate, and the **TS source-of-truth / Rust optional-accelerator** boundary (publish never compiles Rust). `sks zellij status`/`repair` inspects the Zellij runtime without auto-installing anything.
41
-
42
- Core release checks:
28
+ Quick checks:
43
29
 
44
30
  ```bash
45
- npm run codex:0.136-compat
31
+ npm run typecheck
32
+ npm run build
33
+ npm run agent:zellij-runtime
34
+ npm run zellij:layout-valid
46
35
  npm run zellij:lane-renderer
47
- npm run mad-sks:zellij-launch
48
- npm run release:readiness
49
- npm run release:check
50
36
  ```
51
37
 
52
- Detailed release history lives in [CHANGELOG.md](CHANGELOG.md); every version-facing change should be recorded there before release. Current release gate status lives in [docs/release-readiness.md](docs/release-readiness.md).
38
+ Broader release checks still live behind `npm run release:check`. Detailed release history is in [CHANGELOG.md](CHANGELOG.md), and release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
53
39
 
54
40
  ## Parallelism, UX, And Integrations
55
41
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "1.21.6"
79
+ version = "1.21.7"
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.6"
3
+ version = "1.21.7"
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.6"),
7
+ Some("--version") => println!("sks-rs 1.21.7"),
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.6",
5
- "source_digest": "3577af7555600bd9748b9c3252bf18a727db23c92740e7a06dc778b467aa1495",
4
+ "package_version": "1.21.7",
5
+ "source_digest": "2c5c05c578c385f3e186f4e94b7387c6122093fbba7235ad2c009d4803e1d6b4",
6
6
  "source_file_count": 1771,
7
- "built_at_source_time": 1780391165608
7
+ "built_at_source_time": 1780395828537
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.6';
2
+ const FAST_PACKAGE_VERSION = '1.21.7';
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.21.6",
4
- "package_version": "1.21.6",
3
+ "version": "1.21.7",
4
+ "package_version": "1.21.7",
5
5
  "typescript": true,
6
6
  "mjs_runtime_files": 0,
7
7
  "compiled_file_count": 1032,
8
8
  "compiled_js_count": 516,
9
9
  "compiled_dts_count": 516,
10
- "source_digest": "3577af7555600bd9748b9c3252bf18a727db23c92740e7a06dc778b467aa1495",
10
+ "source_digest": "2c5c05c578c385f3e186f4e94b7387c6122093fbba7235ad2c009d4803e1d6b4",
11
11
  "source_file_count": 1771,
12
12
  "source_files_hash": "1f79c92a7165654f5ae190c1719e7e8ec89337ed8f265c7a68242ef613848f19",
13
13
  "source_list_hash": "1f79c92a7165654f5ae190c1719e7e8ec89337ed8f265c7a68242ef613848f19",
@@ -15,6 +15,7 @@ declare class NativeCliSessionSwarmRecorder {
15
15
  private active;
16
16
  private maxObserved;
17
17
  private writeLock;
18
+ private nextPaneToken;
18
19
  constructor(root: string, input: {
19
20
  missionId: string;
20
21
  requestedAgents: number;
@@ -29,6 +30,7 @@ declare class NativeCliSessionSwarmRecorder {
29
30
  slice: any;
30
31
  opts: any;
31
32
  }): Promise<import("./agent-schema.js").AgentRunnerResult>;
33
+ private launchWorkerInZellijPane;
32
34
  finalize(): Promise<{
33
35
  schema: string;
34
36
  generated_at: string;
@@ -37,6 +39,7 @@ declare class NativeCliSessionSwarmRecorder {
37
39
  route: string;
38
40
  backend: string;
39
41
  scaling_primitive: string;
42
+ zellij_pane_worker_sessions: number;
40
43
  requested_agents: number;
41
44
  target_active_slots: number;
42
45
  spawned_worker_process_count: number;
@@ -60,5 +63,12 @@ declare class NativeCliSessionSwarmRecorder {
60
63
  private persist;
61
64
  private summary;
62
65
  }
66
+ export declare function buildPaneWorkerCommand(input: {
67
+ args: string[];
68
+ stdoutPath: string;
69
+ stderrPath: string;
70
+ heartbeatPath: string;
71
+ env: Record<string, unknown>;
72
+ }): string;
63
73
  export {};
64
74
  //# sourceMappingURL=native-cli-session-swarm.d.ts.map
@@ -1,9 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import { spawn } from 'node:child_process';
3
3
  import path from 'node:path';
4
- import { ensureDir, exists, nowIso, packageRoot, readJson, writeJsonAtomic } from '../fsx.js';
4
+ import { appendJsonl, ensureDir, exists, nowIso, packageRoot, readJson, writeJsonAtomic } from '../fsx.js';
5
5
  import { fastModeEnv } from './fast-mode-policy.js';
6
6
  import { validateAgentWorkerResult } from './agent-worker-pipeline.js';
7
+ import { runZellij } from '../zellij/zellij-command.js';
8
+ import { extractZellijPaneIdFromOutput, recordZellijLanePaneId } from '../zellij/zellij-lane-runtime.js';
7
9
  export const NATIVE_CLI_SESSION_SWARM_SCHEMA = 'sks.agent-native-cli-session-swarm.v1';
8
10
  export function createNativeCliSessionSwarmRecorder(root, input) {
9
11
  return new NativeCliSessionSwarmRecorder(root, input);
@@ -15,6 +17,7 @@ class NativeCliSessionSwarmRecorder {
15
17
  active = new Set();
16
18
  maxObserved = 0;
17
19
  writeLock = Promise.resolve();
20
+ nextPaneToken = -1;
18
21
  constructor(root, input) {
19
22
  this.root = root;
20
23
  this.input = input;
@@ -87,6 +90,20 @@ class NativeCliSessionSwarmRecorder {
87
90
  };
88
91
  const stdout = fs.createWriteStream(path.join(this.root, stdoutRel), { flags: 'a' });
89
92
  const stderr = fs.createWriteStream(path.join(this.root, stderrRel), { flags: 'a' });
93
+ if (this.input.backend === 'zellij' && ctx.opts.real === true && ctx.opts.zellijPaneWorker !== false) {
94
+ stdout.end();
95
+ stderr.end();
96
+ return this.launchWorkerInZellijPane({
97
+ ctx,
98
+ record,
99
+ args,
100
+ resultRel,
101
+ stdoutRel,
102
+ stderrRel,
103
+ heartbeatRel,
104
+ workerDirRel
105
+ });
106
+ }
90
107
  const child = spawn(process.execPath, args, {
91
108
  cwd: ctx.opts.cwd || packageRoot(),
92
109
  env: {
@@ -154,6 +171,159 @@ class NativeCliSessionSwarmRecorder {
154
171
  await this.record(record);
155
172
  return result;
156
173
  }
174
+ async launchWorkerInZellijPane(input) {
175
+ const sessionName = String(input.ctx.opts.zellijSessionName || (this.input.missionId ? `sks-${this.input.missionId}` : 'sks-agent-runtime'));
176
+ const slotId = String(input.ctx.agent.slot_id || input.ctx.agent.id || 'slot-001');
177
+ const activeToken = this.nextPaneToken--;
178
+ this.active.add(activeToken);
179
+ this.maxObserved = Math.max(this.maxObserved, this.active.size);
180
+ const workerCommand = buildPaneWorkerCommand({
181
+ args: input.args,
182
+ stdoutPath: path.join(this.root, input.stdoutRel),
183
+ stderrPath: path.join(this.root, input.stderrRel),
184
+ heartbeatPath: path.join(this.root, input.heartbeatRel),
185
+ env: {
186
+ ...(input.ctx.opts.env || {}),
187
+ ...fastModeEnv(this.input.fastModePolicy),
188
+ SKS_AGENT_WORKER: '1',
189
+ SKS_PIPELINE_MODE: 'agent-worker',
190
+ SKS_DISABLE_ROUTE_RECURSION: '1',
191
+ SKS_PARENT_MISSION_ID: this.input.missionId,
192
+ SKS_AGENT_SESSION_ID: String(input.ctx.agent.session_id || ''),
193
+ SKS_AGENT_SLOT_ID: slotId,
194
+ SKS_AGENT_GENERATION_INDEX: String(input.ctx.agent.generation_index || 1),
195
+ SKS_ZELLIJ_WORKER_PANE: '1',
196
+ SKS_ZELLIJ_SESSION_NAME: sessionName
197
+ }
198
+ });
199
+ const createSession = await runZellij(['attach', '--create-background', sessionName], {
200
+ cwd: input.ctx.opts.cwd || packageRoot(),
201
+ timeoutMs: 5000,
202
+ optional: false
203
+ });
204
+ const launch = createSession.ok
205
+ ? await runZellij(['--session', sessionName, 'action', 'new-pane', '--name', slotId, '--', 'sh', '-lc', workerCommand], {
206
+ cwd: input.ctx.opts.cwd || packageRoot(),
207
+ timeoutMs: 5000,
208
+ optional: false
209
+ })
210
+ : null;
211
+ const stdoutPaneId = launch?.ok ? extractZellijPaneIdFromOutput(launch.stdout_tail) : null;
212
+ const reconciledPane = stdoutPaneId ? null : launch?.ok ? await reconcileZellijWorkerPaneId(sessionName, slotId, path.join(this.root, input.resultRel), input.ctx.opts.cwd || packageRoot()) : null;
213
+ const paneId = stdoutPaneId || reconciledPane?.pane_id || null;
214
+ const paneIdSource = stdoutPaneId ? 'zellij_worker_new_pane_stdout' : reconciledPane?.pane_id ? 'zellij_worker_list_panes' : launch?.ok ? 'zellij_worker_pane_stdout_missing' : 'zellij_worker_pane_launch_failed';
215
+ const launchBlockers = [
216
+ ...(createSession.ok ? [] : createSession.blockers.map((blocker) => `zellij_worker_session_${blocker}`)),
217
+ ...(launch && !launch.ok ? launch.blockers.map((blocker) => `zellij_worker_pane_${blocker}`) : [])
218
+ ];
219
+ input.record.command_line = ['zellij', '--session', sessionName, 'action', 'new-pane', '--name', slotId, '--', 'sh', '-lc', '<native-cli-worker-command>'];
220
+ input.record.zellij_session_name = sessionName;
221
+ input.record.zellij_pane_id = paneId || null;
222
+ input.record.zellij_pane_id_source = paneIdSource;
223
+ input.record.zellij_create_session = createSession;
224
+ input.record.zellij_launch = launch;
225
+ input.record.scaling_primitive = 'native_cli_process_in_zellij_pane';
226
+ input.record.status = launchBlockers.length ? 'failed' : 'running';
227
+ input.record.blockers = launchBlockers;
228
+ await this.record(input.record);
229
+ await appendJsonl(path.join(this.root, 'agent-zellij-pane-launch-ledger.jsonl'), {
230
+ schema: 'sks.agent-zellij-pane-launch.v1',
231
+ generated_at: nowIso(),
232
+ launch_mode: launch?.ok ? 'real_zellij_worker_pane_session' : 'real_zellij_worker_pane_failed',
233
+ agent_id: input.ctx.agent.id,
234
+ slot_id: slotId,
235
+ generation_index: input.ctx.agent.generation_index || null,
236
+ session_id: input.ctx.agent.session_id,
237
+ session_name: sessionName,
238
+ pane_id: paneId || `zellij-pane-${slotId}`,
239
+ pane_id_source: paneIdSource,
240
+ command: '<native-cli-worker-command>',
241
+ worker_artifact_dir: input.workerDirRel,
242
+ worker_result_path: input.resultRel,
243
+ parent_child_transport: 'worker-result-json-and-heartbeat',
244
+ persistent_slot_lane: false,
245
+ blockers: launchBlockers
246
+ });
247
+ await recordZellijLanePaneId(this.root, {
248
+ slotId,
249
+ paneId: paneId || `zellij-pane-${slotId}`,
250
+ source: paneIdSource,
251
+ sessionName,
252
+ command: '<native-cli-worker-command>'
253
+ });
254
+ await writeJsonAtomic(path.join(this.root, input.workerDirRel, 'zellij-worker-pane-launch.json'), {
255
+ schema: 'sks.zellij-worker-pane-launch.v1',
256
+ generated_at: nowIso(),
257
+ ok: launchBlockers.length === 0,
258
+ session_name: sessionName,
259
+ pane_id: paneId || null,
260
+ pane_id_source: paneIdSource,
261
+ slot_id: slotId,
262
+ worker_artifact_dir: input.workerDirRel,
263
+ result_path: input.resultRel,
264
+ stdout_log: input.stdoutRel,
265
+ stderr_log: input.stderrRel,
266
+ parent_child_transport: 'worker-result-json-and-heartbeat',
267
+ create_session: createSession,
268
+ launch,
269
+ pane_reconciliation: reconciledPane,
270
+ blockers: launchBlockers
271
+ });
272
+ if (launchBlockers.length) {
273
+ this.active.delete(activeToken);
274
+ input.record.closed_at = nowIso();
275
+ input.record.status = 'failed';
276
+ await this.record(input.record);
277
+ return validateAgentWorkerResult({
278
+ mission_id: this.input.missionId,
279
+ agent_id: input.ctx.agent.id,
280
+ session_id: input.ctx.agent.session_id,
281
+ persona_id: input.ctx.agent.persona_id || input.ctx.agent.id,
282
+ task_slice_id: input.ctx.slice?.id || '',
283
+ status: 'failed',
284
+ backend: this.input.backend,
285
+ summary: 'Zellij worker pane launch failed.',
286
+ artifacts: [input.stdoutRel, input.stderrRel, path.join(input.workerDirRel, 'zellij-worker-pane-launch.json')],
287
+ blockers: launchBlockers,
288
+ unverified: [],
289
+ writes: [],
290
+ source_intelligence_refs: input.ctx.agent.source_intelligence_refs || null,
291
+ goal_mode_ref: input.ctx.agent.goal_mode_ref || null
292
+ });
293
+ }
294
+ const parsed = await waitForWorkerResult(path.join(this.root, input.resultRel), Number(process.env.SKS_ZELLIJ_WORKER_RESULT_TIMEOUT_MS || 120000));
295
+ this.active.delete(activeToken);
296
+ input.record.closed_at = nowIso();
297
+ const workerProcessReport = await readJson(path.join(this.root, input.workerDirRel, 'worker-process-report.json'), null).catch(() => null);
298
+ input.record.pid = Number(workerProcessReport?.pid) || null;
299
+ input.record.process_id = input.record.pid;
300
+ input.record.exit_code = parsed ? (parsed.status === 'done' ? 0 : 1) : 1;
301
+ input.record.status = parsed?.status === 'done' ? 'closed' : 'failed';
302
+ input.record.blockers = parsed ? parsed.blockers || [] : ['zellij_worker_result_timeout'];
303
+ await this.record(input.record);
304
+ if (!parsed) {
305
+ return validateAgentWorkerResult({
306
+ mission_id: this.input.missionId,
307
+ agent_id: input.ctx.agent.id,
308
+ session_id: input.ctx.agent.session_id,
309
+ persona_id: input.ctx.agent.persona_id || input.ctx.agent.id,
310
+ task_slice_id: input.ctx.slice?.id || '',
311
+ status: 'failed',
312
+ backend: this.input.backend,
313
+ summary: 'Zellij pane worker result timed out.',
314
+ artifacts: [input.stdoutRel, input.stderrRel, path.join(input.workerDirRel, 'zellij-worker-pane-launch.json')],
315
+ blockers: ['zellij_worker_result_timeout'],
316
+ unverified: [],
317
+ writes: [],
318
+ source_intelligence_refs: input.ctx.agent.source_intelligence_refs || null,
319
+ goal_mode_ref: input.ctx.agent.goal_mode_ref || null
320
+ });
321
+ }
322
+ return validateAgentWorkerResult({
323
+ ...parsed,
324
+ artifacts: [...new Set([...(Array.isArray(parsed.artifacts) ? parsed.artifacts : []), input.stdoutRel, input.stderrRel, path.join(input.workerDirRel, 'zellij-worker-pane-launch.json')])]
325
+ });
326
+ }
157
327
  async finalize() {
158
328
  await this.persist();
159
329
  return this.summary();
@@ -183,6 +353,7 @@ class NativeCliSessionSwarmRecorder {
183
353
  route: this.input.route,
184
354
  backend: this.input.backend,
185
355
  scaling_primitive: 'native_cli_process',
356
+ zellij_pane_worker_sessions: this.records.filter((row) => row.scaling_primitive === 'native_cli_process_in_zellij_pane').length,
186
357
  requested_agents: this.input.requestedAgents,
187
358
  target_active_slots: this.input.targetActiveSlots,
188
359
  spawned_worker_process_count: this.records.length,
@@ -204,6 +375,69 @@ class NativeCliSessionSwarmRecorder {
204
375
  };
205
376
  }
206
377
  }
378
+ export function buildPaneWorkerCommand(input) {
379
+ const envPrefix = Object.entries(input.env)
380
+ .filter(([key, value]) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) && value != null)
381
+ .map(([key, value]) => `${key}=${shellQuote(String(value))}`)
382
+ .sort();
383
+ const command = [shellQuote(process.execPath), ...input.args.map(shellQuote)].join(' ');
384
+ const heartbeat = `printf '%s\\n' ${shellQuote(JSON.stringify({ schema: 'sks.zellij-worker-pane-event.v1', event: 'worker_command_exited' }))} >> ${shellQuote(input.heartbeatPath)}`;
385
+ const holdMs = Math.max(0, Number(process.env.SKS_ZELLIJ_WORKER_PANE_HOLD_MS || 1500));
386
+ const hold = holdMs > 0 ? `sleep ${shellQuote(String(Math.min(30, holdMs / 1000)))}` : ':';
387
+ return `${envPrefix.join(' ')} ${command} > ${shellQuote(input.stdoutPath)} 2> ${shellQuote(input.stderrPath)}; code=$?; ${heartbeat}; ${hold}; exit $code`.trim();
388
+ }
389
+ async function waitForWorkerResult(file, timeoutMs) {
390
+ const deadline = Date.now() + Math.max(1000, timeoutMs);
391
+ while (Date.now() < deadline) {
392
+ const result = await readJson(file, null).catch(() => null);
393
+ if (result)
394
+ return result;
395
+ await new Promise((resolve) => setTimeout(resolve, 250));
396
+ }
397
+ return null;
398
+ }
399
+ async function reconcileZellijWorkerPaneId(sessionName, slotId, resultPath, cwd) {
400
+ const listed = await runZellij(['--session', sessionName, 'action', 'list-panes', '--json', '--all'], {
401
+ cwd,
402
+ timeoutMs: 5000,
403
+ optional: true
404
+ });
405
+ const rows = parsePaneRows(listed.stdout_tail);
406
+ const pane = rows.find((row) => {
407
+ const title = String(row.title || row.name || row.pane_name || '');
408
+ const command = String(row.terminal_command || row.command || row.command_line || row.running_command || '');
409
+ const exited = row.exited === true || row.is_exited === true || row.exit_status != null;
410
+ return !exited && title === slotId && (command.includes(resultPath) || command.includes('SKS_ZELLIJ_WORKER_PANE'));
411
+ }) || rows.find((row) => {
412
+ const title = String(row.title || row.name || row.pane_name || '');
413
+ const exited = row.exited === true || row.is_exited === true || row.exit_status != null;
414
+ return !exited && title === slotId;
415
+ });
416
+ const paneId = pane?.pane_id ?? pane?.paneId ?? pane?.id ?? null;
417
+ return {
418
+ schema: 'sks.zellij-worker-pane-reconciliation.v1',
419
+ ok: Boolean(paneId),
420
+ pane_id: paneId == null ? null : String(paneId),
421
+ listed_count: rows.length,
422
+ command: listed,
423
+ blockers: paneId == null ? ['zellij_worker_pane_id_not_reconciled'] : []
424
+ };
425
+ }
426
+ function parsePaneRows(text) {
427
+ if (!String(text || '').trim())
428
+ return [];
429
+ try {
430
+ const parsed = JSON.parse(String(text));
431
+ if (Array.isArray(parsed))
432
+ return parsed;
433
+ if (Array.isArray(parsed?.panes))
434
+ return parsed.panes;
435
+ return [];
436
+ }
437
+ catch {
438
+ return [];
439
+ }
440
+ }
207
441
  async function resolveWorkerCliPath() {
208
442
  if (process.env.SKS_NATIVE_WORKER_CLI)
209
443
  return process.env.SKS_NATIVE_WORKER_CLI;
@@ -215,4 +449,7 @@ async function resolveWorkerCliPath() {
215
449
  function redactWorkerArgs(args) {
216
450
  return args.map((arg, index) => index > 0 && args[index - 1] === '--intake' ? '<worker-intake.json>' : arg);
217
451
  }
452
+ function shellQuote(value) {
453
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
454
+ }
218
455
  //# sourceMappingURL=native-cli-session-swarm.js.map
@@ -230,7 +230,7 @@ async function createSupervisorLanes(root, missionId, sessionName, targetActiveS
230
230
  }
231
231
  async function launchPersistentSlotLane(root, lane, missionId) {
232
232
  const command = persistentLaneCommandForRoot(root, missionId, lane.slot_id, lane.runtime.session_name);
233
- const launch = await runZellij(['action', 'new-pane', '--name', lane.slot_id, '--', 'sh', '-lc', command], { cwd: root, timeoutMs: 5000, optional: true });
233
+ const launch = await runZellij(['--session', lane.runtime.session_name, 'action', 'new-pane', '--name', lane.slot_id, '--', 'sh', '-lc', command], { cwd: root, timeoutMs: 5000, optional: true });
234
234
  const paneId = launch.ok ? extractZellijPaneIdFromOutput(launch.stdout_tail) : null;
235
235
  const nextPaneId = paneId || lane.pane_id;
236
236
  const paneIdSource = paneId ? 'zellij_new_pane_stdout' : launch.ok ? 'synthetic_fallback_new_pane_stdout_missing' : lane.pane_id_source;
@@ -1,4 +1,4 @@
1
- export declare const PACKAGE_VERSION = "1.21.6";
1
+ export declare const PACKAGE_VERSION = "1.21.7";
2
2
  export declare const DEFAULT_PROCESS_TAIL_BYTES: number;
3
3
  export declare const DEFAULT_PROCESS_TIMEOUT_MS: number;
4
4
  export interface RunProcessOptions {
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '1.21.6';
8
+ export const PACKAGE_VERSION = '1.21.7';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -1,2 +1,2 @@
1
- export declare const PACKAGE_VERSION = "1.21.6";
1
+ export declare const PACKAGE_VERSION = "1.21.7";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '1.21.6';
1
+ export const PACKAGE_VERSION = '1.21.7';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "1.21.6",
4
+ "version": "1.21.7",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",