sneakoscope 3.0.1 → 3.0.2
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 +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/core/agents/agent-message-bus.js +89 -2
- package/dist/core/agents/runtime-proof-summary.js +29 -1
- package/dist/core/commands/naruto-command.js +9 -4
- package/dist/core/fsx.js +1 -1
- package/dist/core/release/release-cache-key.js +128 -0
- package/dist/core/release/release-gate-cache-v2.js +6 -7
- package/dist/core/release/release-proof-truth.js +63 -0
- package/dist/core/safety/side-effect-runtime-report.js +19 -4
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-capability.js +41 -0
- package/dist/core/zellij/zellij-command.js +2 -1
- package/dist/core/zellij/zellij-update.js +28 -3
- package/dist/core/zellij/zellij-worker-pane-manager.js +116 -5
- package/dist/scripts/github-release-body-helper.js +43 -0
- package/dist/scripts/release-speed-summary.js +11 -0
- package/package.json +19 -2
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **3.0.
|
|
38
|
+
SKS **3.0.2** tracks Codex CLI `rust-v0.139.0` and hardens the 3.0.0 swarm release: capability detection for code-mode web search, preserved `oneOf`/`allOf` tool schemas, plugin marketplace `source`/cached catalog, the `-P` sandbox profile alias, the multi-agent v2 `interrupt_agent` rename, Zellij stacked-pane version gates, pane-lock concurrency proof, release cache safety fixtures, agent message proof summaries, and release proof source-truth artifacts. See [docs/codex-0.139-compat.md](docs/codex-0.139-compat.md).
|
|
39
39
|
|
|
40
40
|
SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
|
|
41
41
|
|
|
@@ -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 3.0.
|
|
7
|
+
Some("--version") => println!("sks-rs 3.0.2"),
|
|
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": "3.0.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "3.0.2",
|
|
5
|
+
"source_digest": "5d782324cdb5c0cb66ce53c5733ebdd754313abc22b579907de282270a0e6178",
|
|
6
|
+
"source_file_count": 2284,
|
|
7
|
+
"built_at_source_time": 1781100033829
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { appendJsonl, nowIso } from '../fsx.js';
|
|
3
4
|
import { appendAgentLedgerEvent } from './agent-central-ledger.js';
|
|
4
5
|
export async function appendAgentMessage(root, message) {
|
|
6
|
+
const eventType = normalizeAgentMessageEventType(message.event_type || message.type);
|
|
5
7
|
const entry = {
|
|
6
8
|
schema: 'sks.agent-message.v1',
|
|
9
|
+
ts: nowIso(),
|
|
10
|
+
mission_id: message.mission_id || inferMissionIdFromAgentRoot(root),
|
|
11
|
+
worker_id: message.worker_id || message.from,
|
|
12
|
+
slot_id: message.slot_id ?? message.from ?? null,
|
|
13
|
+
session_id: message.session_id || null,
|
|
14
|
+
level: message.level || (eventType === 'worker_failed' || eventType === 'blocker' ? 'error' : 'info'),
|
|
15
|
+
event_type: eventType,
|
|
16
|
+
message: message.body,
|
|
17
|
+
artifact_paths: message.artifact_paths || [],
|
|
7
18
|
from: message.from,
|
|
8
|
-
session_id: message.session_id,
|
|
9
19
|
to: message.to || 'orchestrator',
|
|
10
20
|
type: message.type || 'note',
|
|
11
21
|
body: message.body
|
|
@@ -14,4 +24,81 @@ export async function appendAgentMessage(root, message) {
|
|
|
14
24
|
await appendAgentLedgerEvent(root, { agent_id: message.from, session_id: message.session_id, event_type: 'message_appended', payload: { to: entry.to, type: entry.type } });
|
|
15
25
|
return entry;
|
|
16
26
|
}
|
|
27
|
+
export async function readAgentMessageBus(root, missionId, opts = {}) {
|
|
28
|
+
const file = agentMessageBusPath(root, missionId);
|
|
29
|
+
let text = '';
|
|
30
|
+
try {
|
|
31
|
+
text = await fs.readFile(file, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
const levels = new Set((opts.levels || []).map((level) => String(level)));
|
|
37
|
+
const rows = text.split(/\n+/)
|
|
38
|
+
.map((line) => line.trim())
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.map((line) => {
|
|
41
|
+
try {
|
|
42
|
+
return normalizeAgentMessageBusEntry(JSON.parse(line), missionId);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.filter((row) => Boolean(row))
|
|
49
|
+
.filter((row) => !levels.size || levels.has(row.level));
|
|
50
|
+
const max = Math.max(0, Math.floor(Number(opts.max || rows.length)));
|
|
51
|
+
return max > 0 ? rows.slice(-max) : rows;
|
|
52
|
+
}
|
|
53
|
+
export function agentMessageBusPath(root, missionId) {
|
|
54
|
+
const resolved = path.resolve(root);
|
|
55
|
+
if (path.basename(resolved) === 'agents')
|
|
56
|
+
return path.join(resolved, 'agent-messages.jsonl');
|
|
57
|
+
if (path.basename(resolved) === missionId)
|
|
58
|
+
return path.join(resolved, 'agents', 'agent-messages.jsonl');
|
|
59
|
+
return path.join(resolved, '.sneakoscope', 'missions', missionId, 'agents', 'agent-messages.jsonl');
|
|
60
|
+
}
|
|
61
|
+
function normalizeAgentMessageBusEntry(value, missionId) {
|
|
62
|
+
const eventType = normalizeAgentMessageEventType(value.event_type || value.type);
|
|
63
|
+
return {
|
|
64
|
+
schema: 'sks.agent-message.v1',
|
|
65
|
+
ts: String(value.ts || value.generated_at || nowIso()),
|
|
66
|
+
mission_id: String(value.mission_id || missionId),
|
|
67
|
+
worker_id: String(value.worker_id || value.from || value.slot_id || 'worker'),
|
|
68
|
+
slot_id: value.slot_id == null ? value.from == null ? null : String(value.from) : String(value.slot_id),
|
|
69
|
+
session_id: value.session_id == null ? null : String(value.session_id),
|
|
70
|
+
level: normalizeAgentMessageLevel(value.level, eventType),
|
|
71
|
+
event_type: eventType,
|
|
72
|
+
message: String(value.message || value.body || ''),
|
|
73
|
+
artifact_paths: Array.isArray(value.artifact_paths) ? value.artifact_paths.map(String) : [],
|
|
74
|
+
from: value.from == null ? undefined : String(value.from),
|
|
75
|
+
to: value.to == null ? undefined : String(value.to),
|
|
76
|
+
type: value.type == null ? undefined : String(value.type),
|
|
77
|
+
body: value.body == null ? undefined : String(value.body)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function normalizeAgentMessageEventType(value) {
|
|
81
|
+
const text = String(value || '').toLowerCase();
|
|
82
|
+
if (text === 'worker_completed' || text === 'completed' || text === 'done')
|
|
83
|
+
return 'worker_completed';
|
|
84
|
+
if (text === 'worker_failed' || text === 'failed' || text === 'error')
|
|
85
|
+
return 'worker_failed';
|
|
86
|
+
if (text === 'blocker')
|
|
87
|
+
return 'blocker';
|
|
88
|
+
if (text === 'handoff')
|
|
89
|
+
return 'handoff';
|
|
90
|
+
return 'status';
|
|
91
|
+
}
|
|
92
|
+
function normalizeAgentMessageLevel(value, eventType) {
|
|
93
|
+
const text = String(value || '').toLowerCase();
|
|
94
|
+
if (text === 'info' || text === 'warning' || text === 'error')
|
|
95
|
+
return text;
|
|
96
|
+
if (eventType === 'worker_failed' || eventType === 'blocker')
|
|
97
|
+
return 'error';
|
|
98
|
+
return 'info';
|
|
99
|
+
}
|
|
100
|
+
function inferMissionIdFromAgentRoot(root) {
|
|
101
|
+
const resolved = path.resolve(root);
|
|
102
|
+
return path.basename(resolved) === 'agents' ? path.basename(path.dirname(resolved)) : 'unknown';
|
|
103
|
+
}
|
|
17
104
|
//# sourceMappingURL=agent-message-bus.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { findLatestMission, missionDir } from '../mission.js';
|
|
3
3
|
import { readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { readAgentMessageBus } from './agent-message-bus.js';
|
|
4
5
|
export const RUNTIME_PROOF_SUMMARY_SCHEMA = 'sks.runtime-proof-summary.v1';
|
|
5
|
-
export async function buildRuntimeProofSummary(root, missionIdInput = 'latest') {
|
|
6
|
+
export async function buildRuntimeProofSummary(root, missionIdInput = 'latest', opts = {}) {
|
|
6
7
|
const missionId = missionIdInput === 'latest' ? await findLatestMission(root) : missionIdInput;
|
|
7
8
|
if (!missionId)
|
|
8
9
|
throw new Error('runtime_proof_summary_mission_missing');
|
|
@@ -13,6 +14,10 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest')
|
|
|
13
14
|
const swarm = await readJson(path.join(agentsDir, 'agent-native-cli-session-swarm.json'), null);
|
|
14
15
|
const telemetry = await readJson(path.join(dir, 'zellij', 'slot-telemetry.snapshot.json'), null);
|
|
15
16
|
const governor = await readJson(path.join(agentsDir, 'naruto-concurrency-governor.json'), null);
|
|
17
|
+
const messagesAll = await readAgentMessageBus(root, missionId, { max: 500 });
|
|
18
|
+
const recentMessages = await readAgentMessageBus(root, missionId, { max: opts.maxMessages || 8 });
|
|
19
|
+
const failedMessages = messagesAll.filter((row) => row.event_type === 'worker_failed');
|
|
20
|
+
const errorMessages = messagesAll.filter((row) => row.level === 'error');
|
|
16
21
|
const telemetryAgeMs = telemetry?.updated_at ? Math.max(0, Date.now() - Date.parse(telemetry.updated_at)) : Number.MAX_SAFE_INTEGER;
|
|
17
22
|
const visiblePanes = Number(parallel?.visible_panes ?? swarm?.zellij_pane_worker_sessions ?? telemetryVisiblePaneCount(telemetry) ?? 0);
|
|
18
23
|
const targetActive = Number(scheduler?.target_active_slots ?? parallel?.target_active_slots ?? swarm?.target_active_slots ?? governor?.target_active_slots ?? 0);
|
|
@@ -21,6 +26,7 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest')
|
|
|
21
26
|
...(!parallel ? ['parallel_runtime_proof_missing'] : []),
|
|
22
27
|
...(!scheduler ? ['agent_scheduler_state_missing'] : []),
|
|
23
28
|
...(parallel?.passed === false ? parallel.blockers || ['parallel_runtime_proof_failed'] : []),
|
|
29
|
+
...(errorMessages.length ? ['agent_message_bus_error_blockers'] : []),
|
|
24
30
|
...(telemetryAgeMs > 3000 ? ['zellij_telemetry_stale'] : [])
|
|
25
31
|
].map(String);
|
|
26
32
|
const summary = {
|
|
@@ -48,6 +54,13 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest')
|
|
|
48
54
|
largest_batch_size: Number(scheduler?.largest_batch_size || 0),
|
|
49
55
|
utilization: Number(scheduler?.scheduler_utilization || 0)
|
|
50
56
|
},
|
|
57
|
+
messages: {
|
|
58
|
+
recent: recentMessages,
|
|
59
|
+
completed_count: messagesAll.filter((row) => row.event_type === 'worker_completed').length,
|
|
60
|
+
failed_count: failedMessages.length,
|
|
61
|
+
warning_count: messagesAll.filter((row) => row.level === 'warning').length,
|
|
62
|
+
error_count: errorMessages.length
|
|
63
|
+
},
|
|
51
64
|
blockers
|
|
52
65
|
};
|
|
53
66
|
await writeJsonAtomic(path.join(agentsDir, 'runtime-proof-summary.json'), summary);
|
|
@@ -62,9 +75,24 @@ export function renderRuntimeProofSummary(summary) {
|
|
|
62
75
|
`Visible/headless: ${summary.ui.visible_panes} / ${summary.ui.headless_workers}`,
|
|
63
76
|
`Telemetry: ${summary.ui.stale ? `stale ${(summary.ui.telemetry_age_ms / 1000).toFixed(1)}s` : `fresh ${(summary.ui.telemetry_age_ms / 1000).toFixed(1)}s`}`,
|
|
64
77
|
`Model calls max: ${summary.model_calls.max_observed}`,
|
|
78
|
+
...(summary.messages.recent.length ? [
|
|
79
|
+
'Recent worker messages:',
|
|
80
|
+
...summary.messages.recent.map((row) => ` ${messageStatusLabel(row)} ${row.slot_id || row.worker_id}: ${row.message}`)
|
|
81
|
+
] : []),
|
|
65
82
|
...(summary.blockers.length ? [`Blockers: ${summary.blockers.join(', ')}`] : [])
|
|
66
83
|
].join('\n');
|
|
67
84
|
}
|
|
85
|
+
function messageStatusLabel(row) {
|
|
86
|
+
if (row.event_type === 'worker_completed')
|
|
87
|
+
return '[done]';
|
|
88
|
+
if (row.event_type === 'worker_failed')
|
|
89
|
+
return '[fail]';
|
|
90
|
+
if (row.level === 'warning')
|
|
91
|
+
return '[warn]';
|
|
92
|
+
if (row.level === 'error')
|
|
93
|
+
return '[err]';
|
|
94
|
+
return '[info]';
|
|
95
|
+
}
|
|
68
96
|
function telemetryVisiblePaneCount(snapshot) {
|
|
69
97
|
const slots = snapshot?.slots && typeof snapshot.slots === 'object' ? Object.values(snapshot.slots) : [];
|
|
70
98
|
return slots.filter((row) => row?.status && row.status !== 'headless').length;
|
|
@@ -741,7 +741,7 @@ async function narutoProof(parsed) {
|
|
|
741
741
|
const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
|
|
742
742
|
if (!id)
|
|
743
743
|
return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'proof', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
|
|
744
|
-
const summary = await buildRuntimeProofSummary(root, id);
|
|
744
|
+
const summary = await buildRuntimeProofSummary(root, id, { maxMessages: parsed.messages });
|
|
745
745
|
return emit(parsed, { ...summary, action: 'proof' }, () => {
|
|
746
746
|
console.log(renderRuntimeProofSummary(summary));
|
|
747
747
|
});
|
|
@@ -756,7 +756,7 @@ async function narutoHelp(parsed) {
|
|
|
756
756
|
usage: [
|
|
757
757
|
'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake|ollama] [--local-model|--no-ollama] [--work-items N] [--real] [--readonly] [--json]',
|
|
758
758
|
'sks naruto status [--mission <id>] [--json]',
|
|
759
|
-
'sks naruto proof latest [--json]'
|
|
759
|
+
'sks naruto proof latest [--messages 20] [--json]'
|
|
760
760
|
],
|
|
761
761
|
defaults: { clones: DEFAULT_NARUTO_CLONES, max_clones: MAX_NARUTO_AGENT_COUNT, backend: 'codex-sdk' }
|
|
762
762
|
};
|
|
@@ -798,9 +798,10 @@ function parseNarutoArgs(args = []) {
|
|
|
798
798
|
const attach = hasFlag(args, '--attach');
|
|
799
799
|
const smoke = hasFlag(args, '--smoke');
|
|
800
800
|
const parallelism = normalizeParallelism(readOption(args, '--parallelism', 'extreme'));
|
|
801
|
-
const
|
|
801
|
+
const messages = normalizeMessages(readOption(args, '--messages', '8'));
|
|
802
|
+
const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url', '--parallelism', '--messages']);
|
|
802
803
|
const prompt = positionalArgs(rest, valueFlags).join(' ').trim() || 'Naruto shadow clone swarm run';
|
|
803
|
-
return { action, prompt, clones, workItems, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, json, missionId, noOpenZellij, attach, smoke, parallelism };
|
|
804
|
+
return { action, prompt, clones, workItems, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, json, missionId, noOpenZellij, attach, smoke, parallelism, messages };
|
|
804
805
|
}
|
|
805
806
|
function normalizeParallelism(value) {
|
|
806
807
|
const text = String(value || 'extreme').toLowerCase();
|
|
@@ -808,6 +809,10 @@ function normalizeParallelism(value) {
|
|
|
808
809
|
return text;
|
|
809
810
|
return 'extreme';
|
|
810
811
|
}
|
|
812
|
+
function normalizeMessages(value) {
|
|
813
|
+
const parsed = Number(value);
|
|
814
|
+
return Math.max(0, Math.min(100, Math.floor(Number.isFinite(parsed) ? parsed : 8)));
|
|
815
|
+
}
|
|
811
816
|
async function writeNarutoArtifacts(ledgerRoot, artifacts) {
|
|
812
817
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-work-graph.json'), artifacts.workGraph);
|
|
813
818
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-role-distribution.json'), artifacts.roleDistribution);
|
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 = '3.0.
|
|
8
|
+
export const PACKAGE_VERSION = '3.0.2';
|
|
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() {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export function classifyReleaseCacheInputChange(input) {
|
|
2
|
+
if (input.before === input.after) {
|
|
3
|
+
return { neutralizable: true, reason: 'unchanged', behavior_affecting: false };
|
|
4
|
+
}
|
|
5
|
+
const before = normalizeReleaseCacheInputForBehavior(input.file, input.before);
|
|
6
|
+
const after = normalizeReleaseCacheInputForBehavior(input.file, input.after);
|
|
7
|
+
if (before === after) {
|
|
8
|
+
return {
|
|
9
|
+
neutralizable: true,
|
|
10
|
+
reason: neutralReason(input.file),
|
|
11
|
+
behavior_affecting: false
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
neutralizable: false,
|
|
16
|
+
reason: behaviorReason(input.file, input.before, input.after),
|
|
17
|
+
behavior_affecting: true
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function normalizeReleaseCacheInputForBehavior(file, text) {
|
|
21
|
+
const rel = normalizeRel(file);
|
|
22
|
+
if (rel === 'package.json')
|
|
23
|
+
return normalizePackageJson(text);
|
|
24
|
+
if (rel === 'package-lock.json')
|
|
25
|
+
return normalizePackageLock(text);
|
|
26
|
+
if (rel === 'src/core/version.ts' || rel === 'src/core/fsx.ts') {
|
|
27
|
+
return text.replace(/(PACKAGE_VERSION\s*=\s*['"])([^'"]+)(['"])/, '$1__SKS_RELEASE_VERSION__$3');
|
|
28
|
+
}
|
|
29
|
+
if (rel === 'src/bin/sks.ts') {
|
|
30
|
+
return text.replace(/(FAST_PACKAGE_VERSION\s*=\s*['"])([^'"]+)(['"])/, '$1__SKS_RELEASE_VERSION__$3');
|
|
31
|
+
}
|
|
32
|
+
if (rel === 'dist/build-manifest.json')
|
|
33
|
+
return normalizeBuildManifest(text);
|
|
34
|
+
return text;
|
|
35
|
+
}
|
|
36
|
+
function normalizePackageJson(text) {
|
|
37
|
+
return normalizeJson(text, (value) => {
|
|
38
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
39
|
+
value.version = '__SKS_RELEASE_VERSION__';
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function normalizePackageLock(text) {
|
|
45
|
+
return normalizeJson(text, (value) => {
|
|
46
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
47
|
+
value.version = '__SKS_RELEASE_VERSION__';
|
|
48
|
+
if (value.packages?.[''] && typeof value.packages[''] === 'object') {
|
|
49
|
+
value.packages[''].version = '__SKS_RELEASE_VERSION__';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function normalizeBuildManifest(text) {
|
|
56
|
+
return normalizeJson(text, (value) => {
|
|
57
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
58
|
+
if ('version' in value)
|
|
59
|
+
value.version = '__SKS_RELEASE_VERSION__';
|
|
60
|
+
if ('package_version' in value)
|
|
61
|
+
value.package_version = '__SKS_RELEASE_VERSION__';
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function normalizeJson(text, mutate) {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(text);
|
|
69
|
+
return stableJson(mutate(parsed));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return text;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function stableJson(value) {
|
|
76
|
+
if (Array.isArray(value))
|
|
77
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
78
|
+
if (value && typeof value === 'object') {
|
|
79
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
|
|
80
|
+
}
|
|
81
|
+
return JSON.stringify(value);
|
|
82
|
+
}
|
|
83
|
+
function neutralReason(file) {
|
|
84
|
+
const rel = normalizeRel(file);
|
|
85
|
+
if (rel === 'package.json')
|
|
86
|
+
return 'package_json_version_only';
|
|
87
|
+
if (rel === 'package-lock.json')
|
|
88
|
+
return 'package_lock_root_version_only';
|
|
89
|
+
if (rel === 'src/bin/sks.ts')
|
|
90
|
+
return 'fast_package_version_only';
|
|
91
|
+
if (rel === 'src/core/version.ts' || rel === 'src/core/fsx.ts')
|
|
92
|
+
return 'package_version_constant_only';
|
|
93
|
+
if (rel === 'dist/build-manifest.json')
|
|
94
|
+
return 'build_manifest_version_only';
|
|
95
|
+
return 'version_surface_only';
|
|
96
|
+
}
|
|
97
|
+
function behaviorReason(file, before, after) {
|
|
98
|
+
const rel = normalizeRel(file);
|
|
99
|
+
if (rel === 'package.json') {
|
|
100
|
+
const changed = changedTopLevelJsonKeys(before, after);
|
|
101
|
+
const behaviorKeys = changed.filter((key) => ['scripts', 'dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies', 'files', 'engines', 'publishConfig'].includes(key));
|
|
102
|
+
return behaviorKeys.length ? `package_json_behavior_keys:${behaviorKeys.join(',')}` : `package_json_non_version_keys:${changed.join(',') || 'unknown'}`;
|
|
103
|
+
}
|
|
104
|
+
if (rel === 'package-lock.json')
|
|
105
|
+
return 'package_lock_dependency_graph_changed';
|
|
106
|
+
if (rel === 'dist/build-manifest.json')
|
|
107
|
+
return 'build_manifest_artifact_hash_or_behavior_changed';
|
|
108
|
+
if (rel.startsWith('src/'))
|
|
109
|
+
return 'source_behavior_changed';
|
|
110
|
+
if (rel.startsWith('schemas/'))
|
|
111
|
+
return 'schema_behavior_changed';
|
|
112
|
+
return 'release_cache_input_behavior_changed';
|
|
113
|
+
}
|
|
114
|
+
function changedTopLevelJsonKeys(before, after) {
|
|
115
|
+
try {
|
|
116
|
+
const left = JSON.parse(before);
|
|
117
|
+
const right = JSON.parse(after);
|
|
118
|
+
const keys = [...new Set([...Object.keys(left || {}), ...Object.keys(right || {})])];
|
|
119
|
+
return keys.filter((key) => stableJson(left?.[key]) !== stableJson(right?.[key])).sort();
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function normalizeRel(file) {
|
|
126
|
+
return String(file || '').replace(/\\/g, '/').replace(/^\.?\//, '');
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=release-cache-key.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
|
+
import { normalizeReleaseCacheInputForBehavior } from './release-cache-key.js';
|
|
4
5
|
export const RELEASE_GATE_CACHE_V2_SCHEMA = 'sks.release-gate-cache.v2';
|
|
5
6
|
export function releaseGateCacheFile(root) {
|
|
6
7
|
return path.join(root, '.sneakoscope', 'reports', 'release-gates', 'cache-v2.json');
|
|
@@ -21,7 +22,8 @@ const VERSION_NEUTRAL_CACHE_FILES = new Set([
|
|
|
21
22
|
'package-lock.json',
|
|
22
23
|
'src/core/version.ts',
|
|
23
24
|
'src/core/fsx.ts',
|
|
24
|
-
'src/bin/sks.ts'
|
|
25
|
+
'src/bin/sks.ts',
|
|
26
|
+
'dist/build-manifest.json'
|
|
25
27
|
]);
|
|
26
28
|
export function releaseGateCacheKey(root, gate) {
|
|
27
29
|
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
@@ -54,14 +56,14 @@ export function releaseGateCacheKey(root, gate) {
|
|
|
54
56
|
const rel = path.relative(root, file);
|
|
55
57
|
hash.update(rel);
|
|
56
58
|
if (!versionSensitive && VERSION_NEUTRAL_CACHE_FILES.has(rel))
|
|
57
|
-
hashVersionNeutralFile(hash, file, releaseVersion);
|
|
59
|
+
hashVersionNeutralFile(hash, rel, file, releaseVersion);
|
|
58
60
|
else
|
|
59
61
|
hashFileIfPresent(hash, file);
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
return hash.digest('hex');
|
|
63
65
|
}
|
|
64
|
-
function hashVersionNeutralFile(hash, file, releaseVersion) {
|
|
66
|
+
function hashVersionNeutralFile(hash, rel, file, releaseVersion) {
|
|
65
67
|
if (!fs.existsSync(file) || !fs.statSync(file).isFile())
|
|
66
68
|
return;
|
|
67
69
|
const text = fs.readFileSync(file, 'utf8');
|
|
@@ -69,10 +71,7 @@ function hashVersionNeutralFile(hash, file, releaseVersion) {
|
|
|
69
71
|
hash.update(text);
|
|
70
72
|
return;
|
|
71
73
|
}
|
|
72
|
-
|
|
73
|
-
// version-only bump hashes identically. Any other content change in these
|
|
74
|
-
// files still alters the key.
|
|
75
|
-
hash.update(text.split(releaseVersion).join('__SKS_RELEASE_VERSION__'));
|
|
74
|
+
hash.update(normalizeReleaseCacheInputForBehavior(rel, text));
|
|
76
75
|
}
|
|
77
76
|
export function expandGlob(root, input) {
|
|
78
77
|
const absolute = path.join(root, input);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { nowIso, readJson, runProcess, sha256, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
export async function buildReleaseProofTruth(root) {
|
|
5
|
+
const pkg = await readJson(path.join(root, 'package.json'));
|
|
6
|
+
const gitCommit = await gitOutput(root, ['rev-parse', 'HEAD']);
|
|
7
|
+
const gitBranch = await gitOutput(root, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
8
|
+
const gitStatus = await gitOutput(root, ['status', '--porcelain']);
|
|
9
|
+
const packlist = await readNpmPacklist(root);
|
|
10
|
+
return {
|
|
11
|
+
schema: 'sks.release-proof-truth.v1',
|
|
12
|
+
generated_at: nowIso(),
|
|
13
|
+
package_version: String(pkg.version || ''),
|
|
14
|
+
git_commit_sha: gitCommit || null,
|
|
15
|
+
git_branch: gitBranch || null,
|
|
16
|
+
git_status_clean: gitStatus === '',
|
|
17
|
+
package_json_sha256: await shaFile(root, 'package.json'),
|
|
18
|
+
package_lock_sha256: await shaFile(root, 'package-lock.json'),
|
|
19
|
+
version_ts_sha256: await shaFile(root, 'src/core/version.ts'),
|
|
20
|
+
changelog_sha256: await shaFile(root, 'CHANGELOG.md'),
|
|
21
|
+
release_gates_sha256: await shaFile(root, 'release-gates.v2.json'),
|
|
22
|
+
...(packlist ? {
|
|
23
|
+
npm_packlist_count: packlist.count,
|
|
24
|
+
npm_packlist_bytes: packlist.bytes
|
|
25
|
+
} : {})
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function writeReleaseProofTruth(root) {
|
|
29
|
+
const truth = await buildReleaseProofTruth(root);
|
|
30
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'release-proof-truth.json'), truth);
|
|
31
|
+
await writeJsonAtomic(path.join(root, 'dist', 'release-proof-truth.json'), truth);
|
|
32
|
+
return truth;
|
|
33
|
+
}
|
|
34
|
+
async function shaFile(root, rel) {
|
|
35
|
+
return sha256(await fs.readFile(path.join(root, rel)));
|
|
36
|
+
}
|
|
37
|
+
async function gitOutput(root, args) {
|
|
38
|
+
const result = await runProcess('git', args, { cwd: root, timeoutMs: 10000, maxOutputBytes: 64 * 1024 }).catch(() => null);
|
|
39
|
+
if (!result || result.code !== 0)
|
|
40
|
+
return null;
|
|
41
|
+
return String(result.stdout || '').trim();
|
|
42
|
+
}
|
|
43
|
+
async function readNpmPacklist(root) {
|
|
44
|
+
const result = await runProcess('npm', ['pack', '--dry-run', '--json', '--ignore-scripts'], {
|
|
45
|
+
cwd: root,
|
|
46
|
+
timeoutMs: 60000,
|
|
47
|
+
maxOutputBytes: 1024 * 1024
|
|
48
|
+
}).catch(() => null);
|
|
49
|
+
if (!result || result.code !== 0)
|
|
50
|
+
return null;
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(String(result.stdout || '[]'));
|
|
53
|
+
const files = Array.isArray(parsed?.[0]?.files) ? parsed[0].files : [];
|
|
54
|
+
return {
|
|
55
|
+
count: files.length,
|
|
56
|
+
bytes: files.reduce((sum, file) => sum + Number(file.size || 0), 0)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=release-proof-truth.js.map
|
|
@@ -34,10 +34,18 @@ export async function buildSideEffectRuntimeReport(root) {
|
|
|
34
34
|
async function discoverLedgerPaths(root) {
|
|
35
35
|
const found = new Set();
|
|
36
36
|
await addIfExists(found, mutationLedgerPath(root));
|
|
37
|
-
await walkForLedgers(path.join(root, '.sneakoscope', 'missions'), found
|
|
37
|
+
await walkForLedgers(path.join(root, '.sneakoscope', 'missions'), found, {
|
|
38
|
+
depth: 0,
|
|
39
|
+
maxDepth: positiveInt(process.env.SKS_SIDE_EFFECT_LEDGER_SCAN_MAX_DEPTH, 6),
|
|
40
|
+
visitedDirs: 0,
|
|
41
|
+
maxDirs: positiveInt(process.env.SKS_SIDE_EFFECT_LEDGER_SCAN_MAX_DIRS, 20000)
|
|
42
|
+
});
|
|
38
43
|
return [...found].sort();
|
|
39
44
|
}
|
|
40
|
-
async function walkForLedgers(dir, found) {
|
|
45
|
+
async function walkForLedgers(dir, found, budget) {
|
|
46
|
+
if (budget.depth > budget.maxDepth || budget.visitedDirs >= budget.maxDirs)
|
|
47
|
+
return;
|
|
48
|
+
budget.visitedDirs += 1;
|
|
41
49
|
let entries;
|
|
42
50
|
try {
|
|
43
51
|
entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
@@ -47,12 +55,19 @@ async function walkForLedgers(dir, found) {
|
|
|
47
55
|
}
|
|
48
56
|
for (const entry of entries) {
|
|
49
57
|
const file = path.join(dir, entry.name);
|
|
50
|
-
if (entry.isDirectory())
|
|
51
|
-
await walkForLedgers(file, found);
|
|
58
|
+
if (entry.isDirectory() && !shouldSkipLedgerScanDir(entry.name))
|
|
59
|
+
await walkForLedgers(file, found, { ...budget, depth: budget.depth + 1 });
|
|
52
60
|
else if (entry.isFile() && entry.name === 'mutation-ledger.jsonl')
|
|
53
61
|
found.add(file);
|
|
54
62
|
}
|
|
55
63
|
}
|
|
64
|
+
function shouldSkipLedgerScanDir(name) {
|
|
65
|
+
return new Set(['node_modules', '.git', 'dist', 'vendor', '.next', 'coverage']).has(name);
|
|
66
|
+
}
|
|
67
|
+
function positiveInt(value, fallback) {
|
|
68
|
+
const parsed = Number(value);
|
|
69
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
|
|
70
|
+
}
|
|
56
71
|
async function addIfExists(found, file) {
|
|
57
72
|
try {
|
|
58
73
|
await fsp.access(file);
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '3.0.
|
|
1
|
+
export const PACKAGE_VERSION = '3.0.2';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -3,6 +3,47 @@ import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
|
3
3
|
import { compareVersionLike, parseZellijVersionText, runZellij } from './zellij-command.js';
|
|
4
4
|
export const ZELLIJ_CAPABILITY_SCHEMA = 'sks.zellij-capability.v1';
|
|
5
5
|
export const ZELLIJ_MIN_VERSION = '0.41.0';
|
|
6
|
+
export const ZELLIJ_STACKED_PANE_CAPABILITY_SCHEMA = 'sks.zellij-stacked-pane-capability.v1';
|
|
7
|
+
export const ZELLIJ_STACKED_PANE_MIN_VERSION = '0.43.0';
|
|
8
|
+
export function zellijSupportsStackedPanes(version) {
|
|
9
|
+
const parsed = parseZellijVersionText(version);
|
|
10
|
+
return Boolean(parsed && compareVersionLike(parsed, ZELLIJ_STACKED_PANE_MIN_VERSION) >= 0);
|
|
11
|
+
}
|
|
12
|
+
export function resolveZellijStackedPaneCapability(input = {}) {
|
|
13
|
+
const versionText = input.versionText == null ? null : String(input.versionText);
|
|
14
|
+
const parsedVersion = parseZellijVersionText(versionText);
|
|
15
|
+
const supports = zellijSupportsStackedPanes(parsedVersion);
|
|
16
|
+
const blockers = [...(input.blockers || [])].map(String);
|
|
17
|
+
const zellijMissing = blockers.includes('zellij_missing') || blockers.includes('zellij_missing_required');
|
|
18
|
+
if (!parsedVersion && !zellijMissing && input.ok === false)
|
|
19
|
+
blockers.push('zellij_version_unparsed');
|
|
20
|
+
return {
|
|
21
|
+
schema: ZELLIJ_STACKED_PANE_CAPABILITY_SCHEMA,
|
|
22
|
+
ok: blockers.length === 0 && supports,
|
|
23
|
+
zellij_bin: input.zellijBin === undefined ? 'zellij' : input.zellijBin,
|
|
24
|
+
version_text: versionText,
|
|
25
|
+
parsed_version: parsedVersion,
|
|
26
|
+
supports_stacked_panes: supports,
|
|
27
|
+
requires_update: Boolean(parsedVersion && !supports),
|
|
28
|
+
fallback_mode: supports ? 'native-stacked' : zellijMissing ? 'headless-only' : 'down-split-stack-emulation',
|
|
29
|
+
blockers
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function checkZellijStackedPaneCapability(opts = {}) {
|
|
33
|
+
const versionRun = await runZellij(['--version'], { optional: true, timeoutMs: 5000 });
|
|
34
|
+
const versionText = `${versionRun.stdout_tail}\n${versionRun.stderr_tail}`.trim();
|
|
35
|
+
const report = resolveZellijStackedPaneCapability({
|
|
36
|
+
ok: versionRun.ok,
|
|
37
|
+
zellijBin: 'zellij',
|
|
38
|
+
versionText,
|
|
39
|
+
blockers: versionRun.ok ? [] : versionRun.blockers
|
|
40
|
+
});
|
|
41
|
+
if (opts.writeReport !== false) {
|
|
42
|
+
const root = opts.root || process.cwd();
|
|
43
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'zellij-stacked-pane-capability.json'), report);
|
|
44
|
+
}
|
|
45
|
+
return report;
|
|
46
|
+
}
|
|
6
47
|
export async function checkZellijCapability(opts = {}) {
|
|
7
48
|
const requireZellij = opts.require === true || process.env.SKS_REQUIRE_ZELLIJ === '1';
|
|
8
49
|
const versionRun = await runZellij(['--version'], { optional: !requireZellij, timeoutMs: 5000 });
|
|
@@ -88,9 +88,10 @@ export function isZellijSocketPathTooLong(text) {
|
|
|
88
88
|
return /IPC socket path is too long|socket path is too long/i.test(String(text || ''));
|
|
89
89
|
}
|
|
90
90
|
export function parseZellijVersionText(text) {
|
|
91
|
-
const match = String(text || '').match(
|
|
91
|
+
const match = String(text || '').match(/(?:^|[^0-9A-Za-z])v?(\d+\.\d+\.\d+)(?:[-+][0-9A-Za-z.-]+)?(?:$|[^0-9A-Za-z])/i);
|
|
92
92
|
return match?.[1] ?? null;
|
|
93
93
|
}
|
|
94
|
+
export const parseZellijVersion = parseZellijVersionText;
|
|
94
95
|
export function compareVersionLike(a, b) {
|
|
95
96
|
const pa = versionParts(a);
|
|
96
97
|
const pb = versionParts(b);
|
|
@@ -8,6 +8,18 @@ import { createRequestedScopeContract } from '../safety/requested-scope-contract
|
|
|
8
8
|
import { checkZellijCapability } from './zellij-capability.js';
|
|
9
9
|
import { compareVersionLike, parseZellijVersionText } from './zellij-command.js';
|
|
10
10
|
export const ZELLIJ_UPDATE_NOTICE_SCHEMA = 'sks.zellij-update-notice.v1';
|
|
11
|
+
export function resolveZellijUpdatePromptMode(input) {
|
|
12
|
+
const env = input.env || process.env;
|
|
13
|
+
if (input.skipFlag === true || env.SKS_SKIP_ZELLIJ_UPDATE === '1')
|
|
14
|
+
return 'skip';
|
|
15
|
+
if (input.ci === true || env.CI === '1' || /^true$/i.test(String(env.CI || '')))
|
|
16
|
+
return 'nonblocking-notice';
|
|
17
|
+
if (input.noQuestion === true || env.SKS_NO_QUESTION === '1' || /^true$/i.test(String(env.SKS_NO_QUESTION || '')))
|
|
18
|
+
return 'nonblocking-notice';
|
|
19
|
+
if (input.headless === true)
|
|
20
|
+
return 'nonblocking-notice';
|
|
21
|
+
return 'interactive-prompt';
|
|
22
|
+
}
|
|
11
23
|
const ZELLIJ_RELEASES_API_PATH = '/repos/zellij-org/zellij/releases/latest';
|
|
12
24
|
export function zellijUpgradeCommandHint(missing = false) {
|
|
13
25
|
if (process.platform === 'darwin')
|
|
@@ -194,10 +206,19 @@ export async function upgradeZellijToLatest(input = {}) {
|
|
|
194
206
|
export async function maybePromptZellijUpdateForLaunch(args = [], opts = {}) {
|
|
195
207
|
const env = opts.env || process.env;
|
|
196
208
|
const list = (args || []).map((arg) => String(arg));
|
|
197
|
-
|
|
209
|
+
const mode = resolveZellijUpdatePromptMode({
|
|
210
|
+
env,
|
|
211
|
+
skipFlag: list.includes('--json') || list.includes('--skip-cli-tools') || list.includes('--skip-zellij-update'),
|
|
212
|
+
noQuestion: list.includes('--no-question') || list.includes('--no-questions'),
|
|
213
|
+
headless: !(process.stdin.isTTY && process.stdout.isTTY)
|
|
214
|
+
});
|
|
215
|
+
if (mode === 'skip') {
|
|
198
216
|
return { status: 'skipped', current: null, latest: null, command: null };
|
|
199
217
|
}
|
|
200
|
-
const
|
|
218
|
+
const noticeInput = { env };
|
|
219
|
+
if (opts.missionDir !== undefined)
|
|
220
|
+
noticeInput.missionDir = opts.missionDir;
|
|
221
|
+
const notice = await checkZellijUpdateNotice(noticeInput).catch(() => null);
|
|
201
222
|
if (!notice)
|
|
202
223
|
return { status: 'skipped', current: null, latest: null, command: null };
|
|
203
224
|
if (notice.zellij_missing) {
|
|
@@ -211,6 +232,10 @@ export async function maybePromptZellijUpdateForLaunch(args = [], opts = {}) {
|
|
|
211
232
|
}
|
|
212
233
|
const label = opts.label || 'Zellij launch';
|
|
213
234
|
const autoYes = list.includes('--yes') || list.includes('-y');
|
|
235
|
+
if (mode === 'nonblocking-notice') {
|
|
236
|
+
console.log(`Zellij update available: ${notice.current_version} -> ${notice.latest_version}. Run: ${notice.upgrade_command}`);
|
|
237
|
+
return { status: 'available', current: notice.current_version, latest: notice.latest_version, command: notice.upgrade_command };
|
|
238
|
+
}
|
|
214
239
|
if (!autoYes && !canAskYesNo(env)) {
|
|
215
240
|
console.log(`Zellij update available: ${notice.current_version} -> ${notice.latest_version}. Run: ${notice.upgrade_command}`);
|
|
216
241
|
return { status: 'available', current: notice.current_version, latest: notice.latest_version, command: notice.upgrade_command };
|
|
@@ -284,7 +309,7 @@ function githubLatestTag(timeoutMs) {
|
|
|
284
309
|
});
|
|
285
310
|
}
|
|
286
311
|
function canAskYesNo(env) {
|
|
287
|
-
return
|
|
312
|
+
return resolveZellijUpdatePromptMode({ env, headless: !(process.stdin.isTTY && process.stdout.isTTY) }) === 'interactive-prompt';
|
|
288
313
|
}
|
|
289
314
|
async function askYesNoDefaultYes(question) {
|
|
290
315
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { appendJsonl, ensureDir, nowIso, packageRoot, readJson, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { appendParallelRuntimeEvent } from '../agents/parallel-runtime-proof.js';
|
|
3
4
|
import { providerPaneLabel } from '../provider/provider-badge.js';
|
|
4
5
|
import { resolveProviderContext } from '../provider/provider-context.js';
|
|
6
|
+
import { checkZellijStackedPaneCapability } from './zellij-capability.js';
|
|
5
7
|
import { runZellij } from './zellij-command.js';
|
|
6
8
|
import { extractZellijPaneIdFromOutput } from './zellij-lane-runtime.js';
|
|
7
9
|
import { buildZellijSlotColumnAnchorCommand } from './zellij-slot-column-anchor.js';
|
|
8
10
|
import { closeWorkerInRightColumn, prepareWorkerInRightColumn, readRightColumnState, recordSlotColumnAnchorInRightColumn, recordWorkerPaneInRightColumn } from './zellij-right-column-manager.js';
|
|
9
11
|
export const ZELLIJ_WORKER_PANE_SCHEMA = 'sks.zellij-worker-pane.v1';
|
|
10
12
|
export const ZELLIJ_WORKER_PANE_EVENT_SCHEMA = 'sks.zellij-worker-pane-event.v1';
|
|
13
|
+
export const ZELLIJ_PANE_CREATION_LOCK_METRICS_SCHEMA = 'sks.zellij-pane-creation-lock-metrics.v1';
|
|
11
14
|
export function buildWorkerPaneName(slotId, generationIndex) {
|
|
12
15
|
return `${slotId}/gen-${Math.max(1, Math.floor(Number(generationIndex) || 1))}`;
|
|
13
16
|
}
|
|
@@ -77,12 +80,15 @@ export function buildWorkerPaneArtifact(input) {
|
|
|
77
80
|
worker_direction_applied: input.workerDirectionApplied || (directionApplied === 'down' ? 'down' : directionApplied === 'unknown' ? 'unknown' : 'not_applied'),
|
|
78
81
|
worker_stacked_requested: input.stackedRequested === true,
|
|
79
82
|
worker_stacked_applied: input.stackedApplied === true,
|
|
83
|
+
worker_stacked_fallback_mode: input.stackedFallbackMode || null,
|
|
84
|
+
worker_stacked_capability: input.stackedCapability || null,
|
|
80
85
|
slot_column_anchor_pane_id: input.slotColumnAnchorPaneId || null,
|
|
81
86
|
right_column: input.rightColumn || null,
|
|
82
87
|
sdk_thread_id: input.sdkThreadId || null,
|
|
83
88
|
sdk_run_id: input.sdkRunId || null,
|
|
84
89
|
stream_event_count: Number(input.streamEventCount || 0),
|
|
85
90
|
structured_output_valid: input.structuredOutputValid === true,
|
|
91
|
+
warnings: input.warnings || [],
|
|
86
92
|
blockers
|
|
87
93
|
};
|
|
88
94
|
}
|
|
@@ -163,7 +169,7 @@ export async function openWorkerPane(input) {
|
|
|
163
169
|
// --direction right — splitting the screen into N side-by-side columns
|
|
164
170
|
// (observed in agent-zellij-pane-launch-ledger: terminal_1..terminal_5 all
|
|
165
171
|
// recorded as separate slot_column_anchor_pane_id values in one mission).
|
|
166
|
-
return withZellijPaneCreationLock(input
|
|
172
|
+
return withZellijPaneCreationLock(input, async () => {
|
|
167
173
|
// Re-read the right-column state now that we hold the lock: a previously
|
|
168
174
|
// serialized worker may have created the anchor and/or its own pane.
|
|
169
175
|
const freshState = rightColumn
|
|
@@ -221,9 +227,23 @@ export async function openWorkerPane(input) {
|
|
|
221
227
|
// worker (`new-pane --stacked`, zellij >= 0.43) so the right column stays a
|
|
222
228
|
// clean vertical stack instead of fragmenting the screen. Opt out with
|
|
223
229
|
// SKS_ZELLIJ_WORKER_STACKED=0.
|
|
224
|
-
const
|
|
230
|
+
const stackIntent = process.env.SKS_ZELLIJ_WORKER_STACKED !== '0'
|
|
225
231
|
&& Boolean(lastVisibleWorkerPaneId)
|
|
226
232
|
&& focus?.ok === true;
|
|
233
|
+
const stackedCapability = stackIntent
|
|
234
|
+
? await checkZellijStackedPaneCapability({ writeReport: false }).catch((err) => ({
|
|
235
|
+
schema: 'sks.zellij-stacked-pane-capability.v1',
|
|
236
|
+
ok: false,
|
|
237
|
+
zellij_bin: 'zellij',
|
|
238
|
+
version_text: null,
|
|
239
|
+
parsed_version: null,
|
|
240
|
+
supports_stacked_panes: false,
|
|
241
|
+
requires_update: false,
|
|
242
|
+
fallback_mode: 'headless-only',
|
|
243
|
+
blockers: [`zellij_stacked_capability_check_failed:${err?.code || err?.message || String(err)}`]
|
|
244
|
+
}))
|
|
245
|
+
: null;
|
|
246
|
+
const stackRequested = stackIntent && stackedCapability?.supports_stacked_panes === true;
|
|
227
247
|
const newPaneArgs = stackRequested
|
|
228
248
|
? ['--session', input.sessionName, 'action', 'new-pane', '--stacked', '--name', paneName, '--', 'sh', '-lc', input.workerCommand]
|
|
229
249
|
: ['--session', input.sessionName, 'action', 'new-pane', '--direction', directionRequested, '--near-current-pane', '--name', paneName, '--', 'sh', '-lc', input.workerCommand];
|
|
@@ -236,6 +256,7 @@ export async function openWorkerPane(input) {
|
|
|
236
256
|
: null;
|
|
237
257
|
let stackApplied = Boolean(stackRequested && launch?.ok);
|
|
238
258
|
let directionApplied = launch?.ok ? directionRequested : 'not_applied';
|
|
259
|
+
let stackedRejectedFallback = false;
|
|
239
260
|
if (createSession.ok && launch && !launch.ok) {
|
|
240
261
|
const fallbackArgs = ['--session', input.sessionName, 'action', 'new-pane', '--direction', directionRequested, '--name', paneName, '--', 'sh', '-lc', input.workerCommand];
|
|
241
262
|
const fallback = await runZellij(fallbackArgs, {
|
|
@@ -246,6 +267,7 @@ export async function openWorkerPane(input) {
|
|
|
246
267
|
if (fallback.ok) {
|
|
247
268
|
launch = fallback;
|
|
248
269
|
stackApplied = false;
|
|
270
|
+
stackedRejectedFallback = stackRequested;
|
|
249
271
|
directionApplied = rightColumn ? 'down' : 'unknown';
|
|
250
272
|
}
|
|
251
273
|
}
|
|
@@ -268,6 +290,10 @@ export async function openWorkerPane(input) {
|
|
|
268
290
|
...(launch && !launch.ok ? launch.blockers.map((blocker) => `zellij_worker_pane_${blocker}`) : []),
|
|
269
291
|
...(launch?.ok && !isRealZellijWorkerPaneIdSource(paneIdSource) ? ['zellij_worker_pane_id_real_source_missing'] : [])
|
|
270
292
|
];
|
|
293
|
+
const warnings = [
|
|
294
|
+
...(stackIntent && stackedCapability && !stackedCapability.supports_stacked_panes ? [`zellij_stacked_pane_fallback:${stackedCapability.fallback_mode}`] : []),
|
|
295
|
+
...(stackedRejectedFallback ? ['zellij_stacked_pane_rejected_fallback_down'] : [])
|
|
296
|
+
];
|
|
271
297
|
const record = buildWorkerPaneArtifact({
|
|
272
298
|
...input,
|
|
273
299
|
paneId,
|
|
@@ -288,13 +314,16 @@ export async function openWorkerPane(input) {
|
|
|
288
314
|
columnCreationDirectionApplied,
|
|
289
315
|
workerDirectionRequested: 'down',
|
|
290
316
|
workerDirectionApplied: directionApplied === 'down' ? 'down' : directionApplied === 'unknown' ? 'unknown' : 'not_applied',
|
|
291
|
-
stackedRequested:
|
|
317
|
+
stackedRequested: stackIntent,
|
|
292
318
|
stackedApplied: stackApplied,
|
|
319
|
+
stackedFallbackMode: stackIntent ? (stackApplied ? 'native-stacked' : stackedCapability?.fallback_mode || 'down-split-stack-emulation') : null,
|
|
320
|
+
stackedCapability,
|
|
293
321
|
slotColumnAnchorPaneId,
|
|
294
322
|
rightColumn: rightColumn ? { mode: 'spawn-on-first-worker', focus_pane_id: focusPaneId, y_order: rightColumn.yOrder, slot_column_anchor_pane_id: slotColumnAnchorPaneId } : null,
|
|
295
323
|
status: blockers.length ? 'failed' : 'running',
|
|
296
324
|
providerContext,
|
|
297
325
|
serviceTier: input.serviceTier || providerContext.service_tier,
|
|
326
|
+
warnings,
|
|
298
327
|
blockers
|
|
299
328
|
});
|
|
300
329
|
await writeWorkerPaneArtifact(root, record);
|
|
@@ -311,8 +340,27 @@ export async function openWorkerPane(input) {
|
|
|
311
340
|
ok: record.ok,
|
|
312
341
|
pane_id: record.pane_id,
|
|
313
342
|
pane_id_source: record.pane_id_source,
|
|
343
|
+
worker_stacked_requested: record.worker_stacked_requested === true,
|
|
344
|
+
worker_stacked_applied: record.worker_stacked_applied === true,
|
|
345
|
+
worker_stacked_fallback_mode: record.worker_stacked_fallback_mode || null,
|
|
314
346
|
blockers
|
|
315
347
|
});
|
|
348
|
+
await appendParallelRuntimeEvent(root, input.missionId, {
|
|
349
|
+
event_type: 'zellij_pane_created',
|
|
350
|
+
slot_id: input.slotId,
|
|
351
|
+
generation_index: input.generationIndex,
|
|
352
|
+
session_id: input.sessionId,
|
|
353
|
+
pid: null,
|
|
354
|
+
backend: String(input.backend || 'zellij'),
|
|
355
|
+
placement: 'zellij-pane',
|
|
356
|
+
meta: {
|
|
357
|
+
ok: record.ok,
|
|
358
|
+
pane_id: record.pane_id,
|
|
359
|
+
worker_stacked_requested: record.worker_stacked_requested === true,
|
|
360
|
+
worker_stacked_applied: record.worker_stacked_applied === true,
|
|
361
|
+
worker_stacked_fallback_mode: record.worker_stacked_fallback_mode || null
|
|
362
|
+
}
|
|
363
|
+
}).catch(() => undefined);
|
|
316
364
|
await appendJsonl(path.join(root, 'agent-zellij-pane-launch-ledger.jsonl'), {
|
|
317
365
|
schema: 'sks.agent-zellij-pane-launch.v1',
|
|
318
366
|
generated_at: nowIso(),
|
|
@@ -338,6 +386,7 @@ export async function openWorkerPane(input) {
|
|
|
338
386
|
worker_direction_applied: record.worker_direction_applied,
|
|
339
387
|
worker_stacked_requested: record.worker_stacked_requested === true,
|
|
340
388
|
worker_stacked_applied: record.worker_stacked_applied === true,
|
|
389
|
+
worker_stacked_fallback_mode: record.worker_stacked_fallback_mode || null,
|
|
341
390
|
slot_column_anchor_pane_id: record.slot_column_anchor_pane_id || null,
|
|
342
391
|
command: record.command,
|
|
343
392
|
worker_artifact_dir: input.workerArtifactDir,
|
|
@@ -627,8 +676,20 @@ function sleep(ms) {
|
|
|
627
676
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
628
677
|
}
|
|
629
678
|
const zellijPaneCreationLocks = new Map();
|
|
630
|
-
async function withZellijPaneCreationLock(
|
|
631
|
-
const key = String(sessionName || 'default');
|
|
679
|
+
async function withZellijPaneCreationLock(input, fn) {
|
|
680
|
+
const key = String(input.sessionName || 'default');
|
|
681
|
+
const requestedAt = nowIso();
|
|
682
|
+
const requestedMs = Date.now();
|
|
683
|
+
await appendWorkerPaneEvent(path.resolve(input.root), 'zellij_pane_creation_lock_requested', input, {}).catch(() => undefined);
|
|
684
|
+
await appendParallelRuntimeEvent(path.resolve(input.root), input.missionId, {
|
|
685
|
+
event_type: 'zellij_pane_creation_lock_requested',
|
|
686
|
+
slot_id: input.slotId,
|
|
687
|
+
generation_index: input.generationIndex,
|
|
688
|
+
session_id: input.sessionId,
|
|
689
|
+
pid: null,
|
|
690
|
+
backend: String(input.backend || 'zellij'),
|
|
691
|
+
placement: 'zellij-pane'
|
|
692
|
+
}).catch(() => undefined);
|
|
632
693
|
const previous = zellijPaneCreationLocks.get(key) || Promise.resolve();
|
|
633
694
|
let release;
|
|
634
695
|
const current = new Promise((resolve) => {
|
|
@@ -636,15 +697,65 @@ async function withZellijPaneCreationLock(sessionName, fn) {
|
|
|
636
697
|
});
|
|
637
698
|
zellijPaneCreationLocks.set(key, previous.then(() => current, () => current));
|
|
638
699
|
await previous.catch(() => undefined);
|
|
700
|
+
const acquiredAt = nowIso();
|
|
701
|
+
const acquiredMs = Date.now();
|
|
702
|
+
await appendWorkerPaneEvent(path.resolve(input.root), 'zellij_pane_creation_lock_acquired', input, { wait_ms: acquiredMs - requestedMs }).catch(() => undefined);
|
|
703
|
+
await appendParallelRuntimeEvent(path.resolve(input.root), input.missionId, {
|
|
704
|
+
event_type: 'zellij_pane_creation_lock_acquired',
|
|
705
|
+
slot_id: input.slotId,
|
|
706
|
+
generation_index: input.generationIndex,
|
|
707
|
+
session_id: input.sessionId,
|
|
708
|
+
pid: null,
|
|
709
|
+
backend: String(input.backend || 'zellij'),
|
|
710
|
+
placement: 'zellij-pane',
|
|
711
|
+
meta: { wait_ms: acquiredMs - requestedMs }
|
|
712
|
+
}).catch(() => undefined);
|
|
639
713
|
try {
|
|
640
714
|
return await fn();
|
|
641
715
|
}
|
|
642
716
|
finally {
|
|
717
|
+
const releasedAt = nowIso();
|
|
718
|
+
const releasedMs = Date.now();
|
|
719
|
+
const metrics = {
|
|
720
|
+
schema: ZELLIJ_PANE_CREATION_LOCK_METRICS_SCHEMA,
|
|
721
|
+
mission_id: input.missionId,
|
|
722
|
+
session_name: input.sessionName,
|
|
723
|
+
slot_id: input.slotId,
|
|
724
|
+
generation_index: input.generationIndex,
|
|
725
|
+
requested_at: requestedAt,
|
|
726
|
+
acquired_at: acquiredAt,
|
|
727
|
+
released_at: releasedAt,
|
|
728
|
+
wait_ms: acquiredMs - requestedMs,
|
|
729
|
+
held_ms: releasedMs - acquiredMs
|
|
730
|
+
};
|
|
731
|
+
await appendJsonl(paneCreationLockMetricsPath(path.resolve(input.root), input.missionId), metrics).catch(() => undefined);
|
|
732
|
+
await appendWorkerPaneEvent(path.resolve(input.root), 'zellij_pane_creation_lock_released', input, { wait_ms: metrics.wait_ms, held_ms: metrics.held_ms }).catch(() => undefined);
|
|
733
|
+
await appendParallelRuntimeEvent(path.resolve(input.root), input.missionId, {
|
|
734
|
+
event_type: 'zellij_pane_creation_lock_released',
|
|
735
|
+
slot_id: input.slotId,
|
|
736
|
+
generation_index: input.generationIndex,
|
|
737
|
+
session_id: input.sessionId,
|
|
738
|
+
pid: null,
|
|
739
|
+
backend: String(input.backend || 'zellij'),
|
|
740
|
+
placement: 'zellij-pane',
|
|
741
|
+
meta: { wait_ms: metrics.wait_ms, held_ms: metrics.held_ms }
|
|
742
|
+
}).catch(() => undefined);
|
|
643
743
|
release();
|
|
644
744
|
if (zellijPaneCreationLocks.get(key) === current)
|
|
645
745
|
zellijPaneCreationLocks.delete(key);
|
|
646
746
|
}
|
|
647
747
|
}
|
|
748
|
+
function paneCreationLockMetricsPath(root, missionId) {
|
|
749
|
+
return path.join(missionArtifactRoot(root, missionId), 'zellij', 'pane-creation-lock-events.jsonl');
|
|
750
|
+
}
|
|
751
|
+
function missionArtifactRoot(root, missionId) {
|
|
752
|
+
const resolved = path.resolve(root);
|
|
753
|
+
if (path.basename(resolved) === 'agents')
|
|
754
|
+
return path.dirname(resolved);
|
|
755
|
+
if (path.basename(resolved) === missionId)
|
|
756
|
+
return resolved;
|
|
757
|
+
return path.join(resolved, '.sneakoscope', 'missions', missionId);
|
|
758
|
+
}
|
|
648
759
|
function normalizeExistingZellijSession(sessionName, result) {
|
|
649
760
|
if (result.ok)
|
|
650
761
|
return result;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { assertGate, root } from './sks-1-18-gate-lib.js';
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
8
|
+
const truth = readJson('.sneakoscope/release-proof-truth.json') || readJson('dist/release-proof-truth.json');
|
|
9
|
+
const changelog = fs.readFileSync(path.join(root, 'CHANGELOG.md'), 'utf8');
|
|
10
|
+
const latest = latestChangelogSection(changelog);
|
|
11
|
+
const body = [
|
|
12
|
+
`Version: ${pkg.version}`,
|
|
13
|
+
`Commit: ${truth?.git_commit_sha || 'unknown'}`,
|
|
14
|
+
`Packlist: ${truth?.npm_packlist_count ?? 'unknown'} files / ${truth?.npm_packlist_bytes ?? 'unknown'} bytes`,
|
|
15
|
+
'Release gates: passed',
|
|
16
|
+
'',
|
|
17
|
+
latest.body.trim()
|
|
18
|
+
].join('\n');
|
|
19
|
+
if (args.includes('--check')) {
|
|
20
|
+
assertGate(Boolean(truth && truth.schema === 'sks.release-proof-truth.v1'), 'release proof truth missing; run npm run release:proof-truth first');
|
|
21
|
+
assertGate(latest.version === pkg.version, 'latest changelog section must match package version', { latest: latest.version, package: pkg.version });
|
|
22
|
+
assertGate(body.includes(`Version: ${pkg.version}`) && body.includes('Commit:') && body.includes('Packlist:'), 'github release body helper missing source truth fields', { body });
|
|
23
|
+
}
|
|
24
|
+
console.log(body);
|
|
25
|
+
function readJson(rel) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(fs.readFileSync(path.join(root, rel), 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function latestChangelogSection(text) {
|
|
34
|
+
const matches = [...text.matchAll(/^## \[([^\]]+)\][^\n]*\n/gm)];
|
|
35
|
+
const first = matches.find((match) => /^[0-9]+\.[0-9]+\.[0-9]+$/.test(match[1]));
|
|
36
|
+
if (!first)
|
|
37
|
+
return { version: null, body: '' };
|
|
38
|
+
const second = matches.find((match) => (match.index || 0) > (first.index || 0));
|
|
39
|
+
const start = (first.index || 0) + first[0].length;
|
|
40
|
+
const end = second?.index || text.length;
|
|
41
|
+
return { version: first[1], body: text.slice(start, end) };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=github-release-body-helper.js.map
|
|
@@ -25,6 +25,17 @@ console.log(JSON.stringify({
|
|
|
25
25
|
skipped_gate_ids: summary?.skipped_by_affected || [],
|
|
26
26
|
cached: summary?.cached || 0,
|
|
27
27
|
cached_gates: summary?.cached_gates || [],
|
|
28
|
+
version_neutralized_inputs: [
|
|
29
|
+
'package.json:version',
|
|
30
|
+
'package-lock.json:root.version',
|
|
31
|
+
'src/core/version.ts:PACKAGE_VERSION',
|
|
32
|
+
'src/core/fsx.ts:PACKAGE_VERSION',
|
|
33
|
+
'src/bin/sks.ts:FAST_PACKAGE_VERSION',
|
|
34
|
+
'dist/build-manifest.json:version'
|
|
35
|
+
],
|
|
36
|
+
behavior_affecting_inputs: [],
|
|
37
|
+
cache_key_policy: 'version-neutral-safe-v1',
|
|
38
|
+
cache_message: 'Release cache: version-only changes neutralized for behavior gates. Version correctness gates still ran uncached.',
|
|
28
39
|
executed: summary?.executed_gates?.length || 0,
|
|
29
40
|
executed_gates: summary?.executed_gates || [],
|
|
30
41
|
wall_ms: summary?.wall_ms || 0,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "3.0.
|
|
4
|
+
"version": "3.0.2",
|
|
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",
|
|
@@ -701,7 +701,24 @@
|
|
|
701
701
|
"codex:model-metadata": "node ./dist/scripts/codex-model-metadata-check.js",
|
|
702
702
|
"codex:effort-auto-discovery": "node ./dist/scripts/codex-effort-auto-discovery-check.js",
|
|
703
703
|
"codex:account-usage-autodiscovery": "node ./dist/scripts/codex-account-usage-autodiscovery-check.js",
|
|
704
|
-
"codex:0138-feature-probes": "node ./dist/scripts/codex-0138-feature-probes-check.js"
|
|
704
|
+
"codex:0138-feature-probes": "node ./dist/scripts/codex-0138-feature-probes-check.js",
|
|
705
|
+
"zellij:stacked-version-parser": "node ./dist/scripts/zellij-stacked-version-parser-check.js",
|
|
706
|
+
"zellij:stacked-version-matrix": "node ./dist/scripts/zellij-stacked-version-matrix-check.js",
|
|
707
|
+
"zellij:stacked-capability-routing": "node ./dist/scripts/zellij-stacked-capability-routing-check.js",
|
|
708
|
+
"zellij:pane-creation-lock-metrics": "node ./dist/scripts/zellij-pane-creation-lock-metrics-check.js",
|
|
709
|
+
"zellij:pane-lock-does-not-block-worker": "node ./dist/scripts/zellij-pane-lock-does-not-block-worker-check.js",
|
|
710
|
+
"zellij:pane-lock-concurrency-blackbox": "node ./dist/scripts/zellij-pane-lock-concurrency-blackbox.js",
|
|
711
|
+
"release:cache-input-classifier": "node ./dist/scripts/release-cache-input-classifier-check.js",
|
|
712
|
+
"release:cache-version-neutral-fixtures": "node ./dist/scripts/release-cache-version-neutral-fixture-check.js",
|
|
713
|
+
"release:cache-neutralization-report": "node ./dist/scripts/release-cache-neutralization-report-check.js",
|
|
714
|
+
"agent:message-bus-reader": "node ./dist/scripts/agent-message-bus-reader-check.js",
|
|
715
|
+
"runtime:proof-summary-messages": "node ./dist/scripts/runtime-proof-summary-messages-check.js",
|
|
716
|
+
"naruto:proof-message-summary": "node ./dist/scripts/naruto-proof-message-summary-check.js",
|
|
717
|
+
"zellij:update-prompt-mode": "node ./dist/scripts/zellij-update-prompt-mode-check.js",
|
|
718
|
+
"zellij:update-prompt-safety": "node ./dist/scripts/zellij-update-prompt-safety-check.js",
|
|
719
|
+
"zellij:update-prompt-matrix": "node ./dist/scripts/zellij-update-prompt-matrix-check.js",
|
|
720
|
+
"release:proof-truth": "node ./dist/scripts/release-proof-truth-check.js",
|
|
721
|
+
"release:github-body-helper": "node ./dist/scripts/github-release-body-helper.js --check"
|
|
705
722
|
},
|
|
706
723
|
"keywords": [
|
|
707
724
|
"sneakoscope",
|