sneakoscope 2.0.16 → 2.0.18

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 (63) hide show
  1. package/README.md +23 -30
  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/cli/command-registry.js +1 -1
  8. package/dist/commands/doctor.js +39 -1
  9. package/dist/commands/proof.js +21 -0
  10. package/dist/commands/zellij-slot-pane.js +7 -1
  11. package/dist/core/agents/agent-effort-policy.js +7 -1
  12. package/dist/core/agents/agent-orchestrator.js +3 -1
  13. package/dist/core/agents/agent-scheduler.js +14 -1
  14. package/dist/core/agents/native-cli-session-swarm.js +11 -7
  15. package/dist/core/agents/native-cli-worker.js +56 -7
  16. package/dist/core/agents/parallel-runtime-proof.js +68 -9
  17. package/dist/core/agents/runtime-proof-summary.js +75 -0
  18. package/dist/core/codex-app/codex-app-handoff.js +77 -0
  19. package/dist/core/codex-control/codex-0138-capability.js +64 -0
  20. package/dist/core/codex-control/codex-model-capabilities.js +41 -0
  21. package/dist/core/codex-control/codex-sdk-config-policy.js +1 -1
  22. package/dist/core/codex-control/codex-task-runner.js +1 -1
  23. package/dist/core/codex-plugins/codex-plugin-json.js +152 -0
  24. package/dist/core/commands/mad-sks-command.js +4 -0
  25. package/dist/core/commands/naruto-command.js +20 -4
  26. package/dist/core/commands/qa-loop-command.js +111 -4
  27. package/dist/core/commands/team-command.js +6 -311
  28. package/dist/core/commands/team-legacy-observe-command.js +182 -0
  29. package/dist/core/db-safety.js +15 -0
  30. package/dist/core/doctor/codex-0138-doctor.js +104 -0
  31. package/dist/core/doctor/doctor-readiness-matrix.js +11 -0
  32. package/dist/core/effort-orchestrator.js +9 -0
  33. package/dist/core/feature-registry.js +4 -2
  34. package/dist/core/fsx.js +1 -1
  35. package/dist/core/hooks-runtime.js +38 -4
  36. package/dist/core/image/image-artifact-path-contract.js +99 -0
  37. package/dist/core/image-ux-review/imagegen-adapter.js +24 -3
  38. package/dist/core/init.js +1 -0
  39. package/dist/core/mad-db/mad-db-capability.js +9 -1
  40. package/dist/core/mad-db/mad-db-result-lifecycle.js +207 -0
  41. package/dist/core/mcp/mcp-plugin-inventory.js +29 -0
  42. package/dist/core/mcp/mcp-server-policy.js +24 -0
  43. package/dist/core/qa-loop/qa-loop-budget-policy.js +37 -0
  44. package/dist/core/qa-loop.js +28 -2
  45. package/dist/core/release/release-gate-affected-selector.js +47 -5
  46. package/dist/core/release/release-gate-dag.js +5 -1
  47. package/dist/core/release/release-gate-scheduler.js +2 -1
  48. package/dist/core/routes.js +3 -1
  49. package/dist/core/usage/codex-account-usage.js +78 -0
  50. package/dist/core/version.js +1 -1
  51. package/dist/core/zellij/zellij-slot-column-anchor.js +16 -7
  52. package/dist/core/zellij/zellij-slot-pane-renderer.js +92 -1
  53. package/dist/core/zellij/zellij-slot-telemetry.js +29 -6
  54. package/dist/core/zellij/zellij-ui-mode.js +12 -2
  55. package/dist/scripts/prepublish-release-check-or-fast.js +3 -3
  56. package/dist/scripts/release-gate-existence-audit.js +5 -1
  57. package/dist/scripts/release-speed-summary.js +22 -2
  58. package/package.json +38 -4
  59. package/schemas/agents/parallel-runtime-proof.schema.json +31 -0
  60. package/schemas/codex-app/codex-app-handoff.schema.json +20 -0
  61. package/schemas/codex-plugins/codex-plugin-inventory.schema.json +32 -0
  62. package/schemas/image/image-artifact-path-contract.schema.json +32 -0
  63. package/schemas/usage/codex-account-usage.schema.json +27 -0
@@ -552,12 +552,14 @@ export const ROUTES = [
552
552
  route: 'explicit scoped permission-widening modifier',
553
553
  description: 'Explicit high-risk authorization modifier that can be combined with other $ commands to temporarily open approved target-project scopes such as files, shell, package installs, services, network, Computer Use/browser workflows, generated assets, file permissions, migrations, Supabase MCP DB writes, direct execute SQL, schema cleanup, and normal targeted DB writes for the active invocation, while preserving catastrophic wipe/all-row/project-management, credential-exfiltration, persistent security-weakening, and unrequested fallback safeguards.',
554
554
  requiredSkills: ['mad-sks', 'db-safety-guard', 'pipeline-runner', 'context7-docs', REFLECTION_SKILL_NAME, 'honest-mode'],
555
+ dollarAliases: ['$MAD-DB'],
556
+ appSkillAliases: ['mad-db'],
555
557
  lifecycle: ['explicit_invocation', 'auto_sealed_permission_scope', 'scoped_permission_override', 'catastrophic_guard', 'permission_deactivation', 'post_route_reflection', 'honest_mode'],
556
558
  context7Policy: 'required',
557
559
  reasoningPolicy: 'xhigh',
558
560
  stopGate: 'mad-sks-gate.json',
559
561
  cliEntrypoint: 'Codex App prompt route only: $MAD-SKS <task>',
560
- examples: ['$MAD-SKS $Team target project maintenance with package/service/file and DB scopes', '$DB Supabase 점검 $MAD-SKS']
562
+ examples: ['$MAD-SKS $Team target project maintenance with package/service/file and DB scopes', '$DB Supabase 점검 $MAD-SKS', '$MAD-DB enable one-cycle DB break-glass only after explicit ack']
561
563
  },
562
564
  {
563
565
  id: 'GX',
@@ -0,0 +1,78 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ export async function collectCodexAccountUsage() {
4
+ if (process.env.SKS_CODEX_ACCOUNT_USAGE_FAKE === '1') {
5
+ return {
6
+ schema: 'sks.codex-account-usage.v1',
7
+ generated_at: nowIso(),
8
+ ok: true,
9
+ source: 'fake',
10
+ account_id: 'fake-account',
11
+ token_usage: {
12
+ input_tokens: 1000,
13
+ output_tokens: 500,
14
+ total_tokens: 1500,
15
+ reset_at: null
16
+ },
17
+ usage_limit_tokens: 100000,
18
+ blockers: []
19
+ };
20
+ }
21
+ const url = String(process.env.SKS_CODEX_APP_SERVER_USAGE_URL || process.env.CODEX_APP_SERVER_USAGE_URL || '').trim();
22
+ if (!url)
23
+ return unavailable(['codex_app_server_usage_endpoint_unavailable']);
24
+ try {
25
+ const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
26
+ if (!response.ok)
27
+ return unavailable([`codex_app_server_usage_http_${response.status}`]);
28
+ const payload = await response.json();
29
+ return normalizeUsagePayload(payload, 'app-server');
30
+ }
31
+ catch (err) {
32
+ return unavailable([`codex_app_server_usage_fetch_failed:${err?.message || String(err)}`]);
33
+ }
34
+ }
35
+ export async function writeCodexAccountUsageArtifacts(root, input = {}) {
36
+ const snapshot = await collectCodexAccountUsage();
37
+ const rootArtifact = path.join(root, '.sneakoscope', 'codex-account-usage.json');
38
+ await writeJsonAtomic(rootArtifact, snapshot);
39
+ let missionArtifact = null;
40
+ if (input.missionId) {
41
+ missionArtifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-account-usage.json');
42
+ await writeJsonAtomic(missionArtifact, snapshot);
43
+ }
44
+ return { snapshot, root_artifact: rootArtifact, mission_artifact: missionArtifact };
45
+ }
46
+ function normalizeUsagePayload(payload, source) {
47
+ const usage = payload?.token_usage || payload?.usage || payload;
48
+ const input = Number(usage?.input_tokens || usage?.inputTokens || 0);
49
+ const output = Number(usage?.output_tokens || usage?.outputTokens || 0);
50
+ const total = Number(usage?.total_tokens || usage?.totalTokens || input + output);
51
+ return {
52
+ schema: 'sks.codex-account-usage.v1',
53
+ generated_at: nowIso(),
54
+ ok: true,
55
+ source,
56
+ account_id: payload?.account_id || payload?.accountId || null,
57
+ token_usage: {
58
+ input_tokens: Number.isFinite(input) ? input : 0,
59
+ output_tokens: Number.isFinite(output) ? output : 0,
60
+ total_tokens: Number.isFinite(total) ? total : 0,
61
+ reset_at: usage?.reset_at || usage?.resetAt || null
62
+ },
63
+ usage_limit_tokens: Number.isFinite(Number(payload?.usage_limit_tokens || payload?.usageLimitTokens)) ? Number(payload?.usage_limit_tokens || payload?.usageLimitTokens) : null,
64
+ blockers: []
65
+ };
66
+ }
67
+ function unavailable(blockers) {
68
+ return {
69
+ schema: 'sks.codex-account-usage.v1',
70
+ generated_at: nowIso(),
71
+ ok: true,
72
+ source: 'unavailable',
73
+ token_usage: null,
74
+ usage_limit_tokens: null,
75
+ blockers
76
+ };
77
+ }
78
+ //# sourceMappingURL=codex-account-usage.js.map
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '2.0.16';
1
+ export const PACKAGE_VERSION = '2.0.18';
2
2
  //# sourceMappingURL=version.js.map
@@ -10,18 +10,21 @@ export function renderZellijSlotColumnAnchor(input = {}) {
10
10
  const fail = nonNegativeInt(input.failedWorkers, 0);
11
11
  const update = input.updateAvailableVersion ? ` · update ${trimInline(input.updateAvailableVersion, 18)} available` : '';
12
12
  const madDb = input.madDbActive ? ' · MAD-DB ACTIVE' : '';
13
+ const appHandoff = input.qaAppHandoffPending ? ' · QA /app handoff pending' : '';
13
14
  const header = done || fail
14
- ? `SLOTS active ${active} · headless ${headless} · done ${done} · fail ${fail} · q ${queue}${update}${madDb}`
15
- : `SLOTS active ${active}/${visible} · headless ${headless} · q ${queue}${update}${madDb}`;
15
+ ? `SLOTS active ${active} · headless ${headless} · done ${done} · fail ${fail} · q ${queue}${update}${madDb}${appHandoff}`
16
+ : `SLOTS active ${active}/${visible} · headless ${headless} · q ${queue}${update}${madDb}${appHandoff}`;
16
17
  const workers = Array.isArray(input.workerRows) ? input.workerRows : [];
18
+ const handoffLine = input.qaAppHandoffPending ? `QA app handoff pending · ${trimInline(input.qaAppHandoffArtifact || 'qa-loop/app-handoff.json', 64)}` : null;
17
19
  if (!workers.length)
18
- return `${header}\nvisible slot panes stack below this anchor`;
20
+ return [header, handoffLine, 'visible slot panes stack below this anchor'].filter(Boolean).join('\n');
19
21
  const maxRows = Math.max(1, nonNegativeInt(input.maxWorkerRows, input.mode === 'full-debug' ? 24 : 12));
20
22
  const overflowRows = workers.filter((row) => row.placement === 'headless').slice(0, maxRows);
21
23
  const visibleRows = overflowRows.length ? overflowRows : workers.filter((row) => row.placement !== 'zellij-pane').slice(0, maxRows);
22
24
  const hidden = Math.max(0, workers.length - visibleRows.length);
23
25
  return [
24
26
  header,
27
+ handoffLine,
25
28
  `visible slot panes stack below this anchor`,
26
29
  ...visibleRows.map((row, index) => renderWorkerRow(row, index + 1)),
27
30
  ...(hidden && visibleRows.length ? [`+${hidden} worker${hidden === 1 ? '' : 's'} in dedicated panes or overflow`] : [])
@@ -33,8 +36,9 @@ export async function renderZellijSlotColumnAnchorFromArtifacts(input) {
33
36
  const telemetry = await readZellijSlotTelemetrySnapshot(root, input.missionId).catch(() => null);
34
37
  const updateNotice = await readJson(path.join(missionDir, 'update-notice.json'));
35
38
  const madDb = await readJson(path.join(missionDir, 'mad-db-capability.json'));
39
+ const appHandoff = await readJson(path.join(missionDir, 'qa-loop', 'app-handoff.json'));
36
40
  if (telemetry && Object.keys(telemetry.slots || {}).length) {
37
- return renderTelemetryAnchor(telemetry, updateNotice, madDb);
41
+ return renderTelemetryAnchor(telemetry, updateNotice, madDb, appHandoff);
38
42
  }
39
43
  const snapshot = await readJson(path.join(missionDir, 'zellij-dashboard-snapshot.json'));
40
44
  const rightColumn = await readJson(path.join(missionDir, 'zellij-right-column-state.json'));
@@ -50,20 +54,25 @@ export async function renderZellijSlotColumnAnchorFromArtifacts(input) {
50
54
  anchorInput.updateAvailableVersion = String(updateNotice.latest_version);
51
55
  if (isMadDbActive(madDb))
52
56
  anchorInput.madDbActive = true;
57
+ if (['pending', 'blocked_for_desktop_review'].includes(String(appHandoff?.status || ''))) {
58
+ anchorInput.qaAppHandoffPending = true;
59
+ anchorInput.qaAppHandoffArtifact = appHandoff?.artifact_path || 'qa-loop/app-handoff.json';
60
+ }
53
61
  if (input.mode !== undefined)
54
62
  anchorInput.mode = input.mode;
55
63
  return renderZellijSlotColumnAnchor(anchorInput);
56
64
  }
57
- function renderTelemetryAnchor(snapshot, updateNotice = null, madDbCapability = null) {
65
+ function renderTelemetryAnchor(snapshot, updateNotice = null, madDbCapability = null, appHandoff = null) {
58
66
  const updatedAt = Date.parse(snapshot.updated_at || '');
59
67
  const staleSeconds = Number.isFinite(updatedAt) ? Math.max(0, Math.round((Date.now() - updatedAt) / 1000)) : null;
60
68
  const counts = snapshot.counts || {};
61
69
  const active = Number(counts.running || 0) + Number(counts.verifying || 0);
62
70
  const update = updateNotice?.update_available && updateNotice?.latest_version ? ` · update ${trimInline(String(updateNotice.latest_version), 18)} available` : '';
63
71
  const madDb = isMadDbActive(madDbCapability) ? ' · MAD-DB ACTIVE' : '';
72
+ const qaHandoff = ['pending', 'blocked_for_desktop_review'].includes(String(appHandoff?.status || '')) ? ' · QA /app handoff pending' : '';
64
73
  if (staleSeconds != null && staleSeconds > 10)
65
- return `SLOTS telemetry stale ${staleSeconds}s · active ?${update}${madDb}`;
66
- return `SLOTS active ${active} · headless ${Number(counts.headless || 0)} · done ${Number(counts.completed || 0)} · fail ${Number(counts.failed || 0)} · q ${Number(counts.queued || 0)}${update}${madDb}`;
74
+ return `SLOTS telemetry stale ${staleSeconds}s · active ?${update}${madDb}${qaHandoff}`;
75
+ return `SLOTS active ${active} · headless ${Number(counts.headless || 0)} · done ${Number(counts.completed || 0)} · fail ${Number(counts.failed || 0)} · q ${Number(counts.queued || 0)}${update}${madDb}${qaHandoff}`;
67
76
  }
68
77
  function isMadDbActive(capability) {
69
78
  if (!capability || capability.enabled !== true || capability.consumed === true)
@@ -24,6 +24,7 @@ export function renderZellijSlotPane(input) {
24
24
  `doing: ${task}`,
25
25
  `files: ${trimInline(files.length ? files.join(', ') : 'no changed file yet', 78)}`,
26
26
  `patch: ${trimInline(input.patchStatus || 'queued', 24)} verify: ${trimInline(input.verifyStatus || 'queued', 24)}`,
27
+ input.qaAppHandoffPending ? `QA app handoff pending: ${trimInline(input.qaAppHandoffArtifact || 'qa-loop/app-handoff.json', 55)}` : null,
27
28
  ...events.map((event) => `event: ${trimInline(event, 78)}`),
28
29
  ...stdout.map((line) => `out: ${trimInline(line, 79)}`),
29
30
  ...stderr.map((line) => `err: ${trimInline(line, 79)}`)
@@ -31,21 +32,41 @@ export function renderZellijSlotPane(input) {
31
32
  return frameSlotPane(`LIVE SLOT ${input.slotId}`, rows.slice(0, Math.max(1, maxLines - 2)));
32
33
  }
33
34
  export async function renderZellijSlotPaneFromArtifacts(input) {
35
+ const artifactRender = await renderZellijSlotPaneFromArtifactDir(input).catch(() => null);
34
36
  if (input.missionId && input.missionId !== 'latest') {
35
37
  const telemetry = await tryRenderTelemetrySlotPane({
36
38
  artifactRoot: input.artifactRoot || input.artifactDir,
37
39
  missionId: input.missionId,
38
40
  slotId: input.slotId,
39
- generationIndex: input.generationIndex
41
+ generationIndex: input.generationIndex,
42
+ artifactRender
40
43
  });
41
44
  if (telemetry)
42
45
  return telemetry;
46
+ if (artifactRender)
47
+ return artifactRender;
43
48
  return [
44
49
  `${input.slotId} gen-${Math.max(1, Math.floor(Number(input.generationIndex) || 1))}`,
45
50
  'waiting for telemetry...',
46
51
  `mission ${input.missionId}`
47
52
  ].join('\n');
48
53
  }
54
+ if (artifactRender)
55
+ return artifactRender;
56
+ const fallbackInput = {
57
+ slotId: input.slotId,
58
+ generationIndex: input.generationIndex,
59
+ status: 'launching',
60
+ currentTask: 'waiting for worker intake',
61
+ mode: input.mode || 'compact-slots'
62
+ };
63
+ if (input.role !== undefined)
64
+ fallbackInput.role = input.role;
65
+ if (input.backend !== undefined)
66
+ fallbackInput.backend = input.backend;
67
+ return renderZellijSlotPane(fallbackInput);
68
+ }
69
+ async function renderZellijSlotPaneFromArtifactDir(input) {
49
70
  const artifactDir = path.resolve(input.artifactDir);
50
71
  const result = await readJson(path.join(artifactDir, 'worker-result.json'));
51
72
  const intake = await readJson(path.join(artifactDir, 'worker-intake.json'));
@@ -77,6 +98,9 @@ export async function renderZellijSlotPaneFromArtifacts(input) {
77
98
  ...(Array.isArray(intake?.input_files) ? intake.input_files : [])
78
99
  ]);
79
100
  const now = Date.now();
101
+ const qaAppHandoff = await readQaAppHandoffNearArtifactDir(artifactDir);
102
+ if (!result && !intake && !backendReport && !fastReport && !paneReport && !codexProof && !localProof && !heartbeatMtime && !eventRows.length)
103
+ return null;
80
104
  return renderZellijSlotPane({
81
105
  slotId: input.slotId,
82
106
  generationIndex: input.generationIndex,
@@ -133,9 +157,39 @@ export async function renderZellijSlotPaneFromArtifacts(input) {
133
157
  eventLines: eventRows.map(formatArtifactEvent).filter(Boolean),
134
158
  stdoutTail: await readTextTailLines(path.join(artifactDir, 'worker.stdout.log'), 2),
135
159
  stderrTail: await readTextTailLines(path.join(artifactDir, 'worker.stderr.log'), 1),
160
+ qaAppHandoffPending: ['pending', 'blocked_for_desktop_review'].includes(String(qaAppHandoff?.status || '')),
161
+ qaAppHandoffArtifact: qaAppHandoff?.artifact_path || null,
136
162
  mode: input.mode || 'compact-slots'
137
163
  });
138
164
  }
165
+ async function readQaAppHandoffNearArtifactDir(artifactDir) {
166
+ let current = path.resolve(artifactDir);
167
+ for (let i = 0; i < 8; i += 1) {
168
+ const candidate = path.join(current, 'qa-loop', 'app-handoff.json');
169
+ const json = await readJson(candidate);
170
+ if (json)
171
+ return json;
172
+ const next = path.dirname(current);
173
+ if (next === current)
174
+ break;
175
+ current = next;
176
+ }
177
+ return null;
178
+ }
179
+ export async function renderZellijSlotPaneStatusFromArtifacts(input) {
180
+ const snapshot = input.missionId && input.missionId !== 'latest'
181
+ ? await readZellijSlotTelemetrySnapshot(path.resolve(input.artifactRoot || input.artifactDir), input.missionId).catch(() => null)
182
+ : null;
183
+ const status = telemetryStatus(snapshot);
184
+ return {
185
+ schema: 'sks.zellij-slot-pane-status.v1',
186
+ mission_id: input.missionId || null,
187
+ slot_id: input.slotId,
188
+ generation_index: Math.max(1, Math.floor(Number(input.generationIndex) || 1)),
189
+ telemetry_stale: status.telemetry_stale,
190
+ telemetry_age_ms: status.telemetry_age_ms
191
+ };
192
+ }
139
193
  export function buildZellijSlotPaneCommand(input) {
140
194
  const args = [
141
195
  input.cliPath,
@@ -159,9 +213,14 @@ async function tryRenderTelemetrySlotPane(input) {
159
213
  const slot = findTelemetrySlot(snapshot, input.slotId, input.generationIndex);
160
214
  if (!slot)
161
215
  return null;
216
+ const staleRows = staleTelemetryRows(telemetryStatus(snapshot).telemetry_age_ms);
217
+ const fallbackRows = artifactFallbackRows(input.artifactRender);
218
+ const liveRows = staleRows.length || !slot.progress ? fallbackRows : [];
162
219
  if (slot.status === 'failed') {
163
220
  return [
164
221
  `${slot.slot_id} gen-${slot.generation_index} · FAILED`,
222
+ ...staleRows,
223
+ ...liveRows,
165
224
  `blocker: ${trimInline(slot.blockers[0] || 'worker_failed', 78)}`,
166
225
  `artifact: ${trimInline(slot.artifact_paths[slot.artifact_paths.length - 1] || '-', 78)}`
167
226
  ].join('\n');
@@ -169,6 +228,8 @@ async function tryRenderTelemetrySlotPane(input) {
169
228
  if (slot.status === 'completed' || slot.status === 'drained') {
170
229
  return [
171
230
  `${slot.slot_id} gen-${slot.generation_index} · done`,
231
+ ...staleRows,
232
+ ...liveRows,
172
233
  `artifacts ${slot.artifact_paths.length} · ${slot.latest_event_type === 'verification_passed' ? 'verify passed' : 'verify queued'}`,
173
234
  'closing in 3s'
174
235
  ].join('\n');
@@ -177,12 +238,25 @@ async function tryRenderTelemetrySlotPane(input) {
177
238
  const heartbeat = slot.latest_ts ? `${Math.max(0, Math.round((Date.now() - Date.parse(slot.latest_ts)) / 1000))}s` : '?';
178
239
  return [
179
240
  `${slot.slot_id} gen-${slot.generation_index} · ${trimInline(slot.role || 'worker', 28)}`,
241
+ ...staleRows,
242
+ ...liveRows,
180
243
  trimInline(backend, 78),
181
244
  `${slot.status}: ${trimInline(slot.task_title || 'worker task', 68)}`,
182
245
  `${formatTelemetryProgress(slot.progress)} · latest ${slot.latest_event_type} ${heartbeat}`,
183
246
  `${slot.latest_event_type === 'patch_candidate' ? 'patch candidate' : 'patch'}: ${slot.latest_event_type === 'patch_candidate' ? 'queued' : trimInline(slot.current_file || '-', 42)}`
184
247
  ].join('\n');
185
248
  }
249
+ function artifactFallbackRows(text) {
250
+ if (!text)
251
+ return [];
252
+ return String(text)
253
+ .split(/\r?\n/)
254
+ .map((line) => line.replace(/^\|\s?/, '').replace(/\s?\|$/, '').trim())
255
+ .filter((line) => /^(heartbeat|doing|files|event|out|err):\s+/i.test(line))
256
+ .filter((line) => !/unknown|waiting for worker intake|no changed file yet/i.test(line))
257
+ .slice(-4)
258
+ .map((line) => `live: ${trimInline(line, 72)}`);
259
+ }
186
260
  function findTelemetrySlot(snapshot, slotId, generationIndex) {
187
261
  const generation = Math.max(1, Math.floor(Number(generationIndex) || 1));
188
262
  return Object.values(snapshot.slots || {}).find((row) => row.slot_id === slotId && Number(row.generation_index) === generation) || null;
@@ -192,6 +266,23 @@ function formatTelemetryProgress(progress) {
192
266
  return 'progress ?';
193
267
  return `progress ${progress.done}/${progress.total}${progress.label ? ` ${trimInline(progress.label, 24)}` : ''}`;
194
268
  }
269
+ function telemetryStatus(snapshot) {
270
+ const parsed = snapshot?.updated_at ? Date.parse(snapshot.updated_at) : NaN;
271
+ const telemetryAgeMs = Number.isFinite(parsed) ? Math.max(0, Date.now() - parsed) : Number.MAX_SAFE_INTEGER;
272
+ return {
273
+ telemetry_stale: telemetryAgeMs > 3000,
274
+ telemetry_age_ms: telemetryAgeMs
275
+ };
276
+ }
277
+ function staleTelemetryRows(ageMs) {
278
+ if (!Number.isFinite(ageMs))
279
+ return ['telemetry stale; worker may still be running'];
280
+ if (ageMs > 10000)
281
+ return ['telemetry stale; worker may still be running'];
282
+ if (ageMs > 3000)
283
+ return [`telemetry stale ${(ageMs / 1000).toFixed(1)}s`];
284
+ return [];
285
+ }
195
286
  async function readJson(file) {
196
287
  try {
197
288
  return JSON.parse(await fs.promises.readFile(file, 'utf8'));
@@ -1,10 +1,12 @@
1
1
  import path from 'node:path';
2
2
  import fsp from 'node:fs/promises';
3
- import { appendJsonlBounded, ensureDir, nowIso, readJson, readText, writeJsonAtomic } from '../fsx.js';
3
+ import { appendJsonlBounded, ensureDir, nowIso, readJson, readText } from '../fsx.js';
4
4
  export const ZELLIJ_SLOT_TELEMETRY_EVENT_SCHEMA = 'sks.zellij-slot-telemetry-event.v1';
5
5
  export const ZELLIJ_SLOT_TELEMETRY_SNAPSHOT_SCHEMA = 'sks.zellij-slot-telemetry-snapshot.v1';
6
6
  const telemetrySnapshotCache = new Map();
7
7
  const telemetrySnapshotWriteCounts = new Map();
8
+ const telemetrySnapshotFlushCounts = new Map();
9
+ const telemetrySnapshotLastFlushMs = new Map();
8
10
  export function slotTelemetryEventPath(root, missionId) {
9
11
  return path.join(inferMissionDir(root, missionId), 'zellij', 'slot-telemetry.events.jsonl');
10
12
  }
@@ -60,6 +62,7 @@ export function applyTelemetryEventToSnapshot(snapshot, event) {
60
62
  schema: ZELLIJ_SLOT_TELEMETRY_SNAPSHOT_SCHEMA,
61
63
  mission_id: event.mission_id || snapshot.mission_id,
62
64
  updated_at: nowIso(),
65
+ flush_count: snapshot.flush_count || 0,
63
66
  slots,
64
67
  counts: countSlotTelemetry(slots)
65
68
  };
@@ -79,11 +82,11 @@ export async function rebuildZellijSlotTelemetrySnapshot(root, missionId) {
79
82
  schema: ZELLIJ_SLOT_TELEMETRY_SNAPSHOT_SCHEMA,
80
83
  mission_id: missionId,
81
84
  updated_at: nowIso(),
85
+ flush_count: 0,
82
86
  slots,
83
87
  counts: countSlotTelemetry(slots)
84
88
  };
85
- await writeJsonAtomic(slotTelemetrySnapshotPath(root, missionId), snapshot);
86
- telemetrySnapshotCache.set(slotTelemetrySnapshotPath(root, missionId), snapshot);
89
+ await writeTelemetrySnapshotFast(slotTelemetrySnapshotPath(root, missionId), snapshot);
87
90
  return snapshot;
88
91
  }
89
92
  async function readTelemetryEvents(file) {
@@ -210,17 +213,37 @@ function tail(value, max) {
210
213
  }
211
214
  async function writeTelemetrySnapshotFast(file, snapshot) {
212
215
  await ensureDir(path.dirname(file));
213
- await fsp.writeFile(file, `${JSON.stringify(snapshot)}\n`, 'utf8');
216
+ const flushCount = Number(telemetrySnapshotFlushCounts.get(file) || 0) + 1;
217
+ telemetrySnapshotFlushCounts.set(file, flushCount);
218
+ telemetrySnapshotLastFlushMs.set(file, Date.now());
219
+ const next = { ...snapshot, flush_count: flushCount };
220
+ telemetrySnapshotCache.set(file, next);
221
+ await fsp.writeFile(file, `${JSON.stringify(next)}\n`, 'utf8');
214
222
  }
215
223
  function shouldFlushTelemetrySnapshot(file, event) {
216
224
  const next = (telemetrySnapshotWriteCounts.get(file) || 0) + 1;
217
225
  telemetrySnapshotWriteCounts.set(file, next);
218
- return next === 1
219
- || next % 100 === 0
226
+ const now = Date.now();
227
+ const last = telemetrySnapshotLastFlushMs.get(file) || 0;
228
+ const parsedFlushMs = Number(process.env.SKS_ZELLIJ_SLOT_TELEMETRY_FLUSH_MS || 1000);
229
+ const parsedFlushEvery = Number(process.env.SKS_ZELLIJ_SLOT_TELEMETRY_FLUSH_EVERY_N || 100);
230
+ const flushMs = Math.max(250, Number.isFinite(parsedFlushMs) ? parsedFlushMs : 1000);
231
+ const flushEvery = Math.max(1, Number.isFinite(parsedFlushEvery) ? Math.floor(parsedFlushEvery) : 100);
232
+ const important = event.event_type === 'task_started'
233
+ || event.event_type === 'task_progress'
234
+ || event.event_type === 'artifact_written'
235
+ || event.event_type === 'patch_candidate'
220
236
  || event.event_type === 'worker_completed'
221
237
  || event.event_type === 'worker_failed'
222
238
  || event.status === 'completed'
223
239
  || event.status === 'failed';
240
+ const should = next === 1
241
+ || important
242
+ || now - last >= flushMs
243
+ || next % flushEvery === 0;
244
+ if (should)
245
+ telemetrySnapshotLastFlushMs.set(file, now);
246
+ return should;
224
247
  }
225
248
  function inferMissionDir(root, missionId) {
226
249
  const resolved = path.resolve(root);
@@ -1,16 +1,26 @@
1
1
  export function resolveZellijUiMode(args = [], env = process.env) {
2
+ return resolveExplicitZellijUiMode(args, env) || 'compact-slots';
3
+ }
4
+ export function resolveZellijWorkerPaneUiMode(args = [], env = process.env) {
5
+ return resolveExplicitZellijUiMode(args, env) || 'full-debug';
6
+ }
7
+ function resolveExplicitZellijUiMode(args = [], env = process.env) {
2
8
  const fromEnv = String(env.SKS_ZELLIJ_UI_MODE || '').trim();
9
+ if (fromEnv === 'compact-slots')
10
+ return 'compact-slots';
3
11
  if (fromEnv === 'full-debug')
4
12
  return 'full-debug';
5
13
  if (fromEnv === 'dashboard-plus-slots')
6
14
  return 'dashboard-plus-slots';
15
+ if (args.includes('--zellij-compact-slots'))
16
+ return 'compact-slots';
7
17
  if (args.includes('--zellij-dashboard'))
8
18
  return 'dashboard-plus-slots';
9
19
  if (args.includes('--zellij-full-debug'))
10
20
  return 'full-debug';
11
- return 'compact-slots';
21
+ return null;
12
22
  }
13
23
  export function zellijUiModeCreatesDashboard(mode) {
14
- return mode !== 'compact-slots';
24
+ return mode === 'dashboard-plus-slots';
15
25
  }
16
26
  //# sourceMappingURL=zellij-ui-mode.js.map
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // Fast path: accept a current release-check stamp.
6
6
  // Repair path: if the stamp is missing/stale, run the authoritative full
7
- // `release:check` once, then require the fast check to pass.
7
+ // `release:check:full` once, then require the fast check to pass.
8
8
  //
9
9
  // This keeps direct `npm publish` usable without weakening the publish gate:
10
10
  // stale stamp repair is the full release gate, not a synthetic stamp write.
@@ -37,7 +37,7 @@ function runReleaseCheck() {
37
37
  stdio: 'inherit'
38
38
  });
39
39
  }
40
- return spawnSync(npmCmd, ['run', 'release:check'], {
40
+ return spawnSync(npmCmd, ['run', 'release:check:full'], {
41
41
  cwd: process.cwd(),
42
42
  encoding: 'utf8',
43
43
  env: process.env,
@@ -82,7 +82,7 @@ function main() {
82
82
  console.error('Prepublish release-check auto-repair disabled by SKS_PREPUBLISH_RUN_RELEASE_CHECK_ON_STALE=0.');
83
83
  process.exit(first.status || 1);
84
84
  }
85
- console.error('Prepublish release stamp is stale or missing; running full `npm run release:check` before publish.');
85
+ console.error('Prepublish release stamp is stale or missing; running full `npm run release:check:full` before publish.');
86
86
  const releaseCheck = runReleaseCheck();
87
87
  if (releaseCheck.status !== 0)
88
88
  process.exit(releaseCheck.status || 1);
@@ -84,7 +84,11 @@ const required = [
84
84
  'git-collaboration:e2e'
85
85
  ];
86
86
  assertGate(releaseManifest.schema === 'sks.release-gates.v2', 'release gate manifest schema mismatch', { schema: releaseManifest.schema });
87
- assertGate(String(scripts['release:check'] || '').includes('release-gate-dag-runner') && String(scripts['release:check'] || '').includes('--preset release'), 'release:check must use the v2 DAG release preset', { release_check: scripts['release:check'] });
87
+ const releaseCheck = String(scripts['release:check'] || '');
88
+ const releaseCheckTarget = releaseCheck.includes('release:check:affected')
89
+ ? String(scripts['release:check:affected'] || '')
90
+ : releaseCheck;
91
+ assertGate(releaseCheckTarget.includes('release-gate-dag-runner') && /--preset\s+(?:release|affected)/.test(releaseCheckTarget), 'release:check must use the v2 DAG release/affected preset', { release_check: scripts['release:check'], resolved_release_check: releaseCheckTarget });
88
92
  assertGate(releaseGates.length > 0, 'release v2 manifest must include release preset gates', { gate_count: releaseGates.length });
89
93
  for (const name of required) {
90
94
  assertGate(Boolean(scripts[name]), `missing release gate script: ${name}`, { required });
@@ -9,19 +9,39 @@ const runs = fs.existsSync(reports)
9
9
  : [];
10
10
  const latest = runs.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0];
11
11
  const summary = latest ? JSON.parse(fs.readFileSync(latest, 'utf8')) : null;
12
+ const mode = summary?.affected_selection?.mode || summary?.selected_preset || (summary?.full === true ? 'full' : 'unknown');
13
+ const affectedMode = mode === 'affected';
14
+ const latestRuntimeProof = latestRuntimeProofSummaryPath();
12
15
  console.log(JSON.stringify({
13
16
  schema: 'sks.release-speed-summary.v1',
14
17
  ok: true,
15
18
  report: latest || null,
16
- mode: summary?.affected_selection?.mode || (summary?.full === true ? 'full' : 'unknown'),
19
+ mode,
20
+ affected_mode: affectedMode,
21
+ affected_mode_warning: affectedMode ? 'Affected mode: true. This is not a full publish gate. Run npm run release:check:full before publishing.' : null,
17
22
  selected_gates: summary?.selected_gates || 0,
23
+ selected_gate_ids: summary?.selected_gate_ids || [],
18
24
  skipped_by_affected: summary?.skipped_by_affected?.length || 0,
25
+ skipped_gate_ids: summary?.skipped_by_affected || [],
19
26
  cached: summary?.cached || 0,
27
+ cached_gates: summary?.cached_gates || [],
20
28
  executed: summary?.executed_gates?.length || 0,
29
+ executed_gates: summary?.executed_gates || [],
21
30
  wall_ms: summary?.wall_ms || 0,
22
31
  cpu_time_saved_ms: summary?.cpu_time_saved_ms || 0,
23
32
  parallelism_gain: summary?.parallelism_gain || 0,
24
33
  max_running: summary?.peak_running || summary?.max_running || 0,
25
- slowest_gates: summary?.slowest_gates || []
34
+ slowest_gates: summary?.slowest_gates || [],
35
+ proof_file_path: latestRuntimeProof
26
36
  }, null, 2));
37
+ function latestRuntimeProofSummaryPath() {
38
+ const missions = path.join(root, '.sneakoscope', 'missions');
39
+ if (!fs.existsSync(missions))
40
+ return null;
41
+ const files = fs.readdirSync(missions)
42
+ .map((name) => path.join(missions, name, 'agents', 'runtime-proof-summary.json'))
43
+ .filter((file) => fs.existsSync(file))
44
+ .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
45
+ return files[0] || null;
46
+ }
27
47
  //# sourceMappingURL=release-speed-summary.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "2.0.16",
4
+ "version": "2.0.18",
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",
@@ -325,7 +325,7 @@
325
325
  "publish:dry": "npm run release:metadata && npm run release:version-truth && npm run publish:packlist-performance && npm run release:check:full && node ./dist/scripts/check-publish-tag.js && node ./dist/scripts/release-check-stamp.js verify && npm run release:provenance -- --publish && npm run release:dist-freshness && node ./dist/scripts/release-registry-check.js --require-unpublished && npm --cache /tmp/sks-npm-cache publish --dry-run --ignore-scripts --registry https://registry.npmjs.org/ --access public",
326
326
  "publish:npm": "npm --cache /tmp/sks-npm-cache publish --registry https://registry.npmjs.org/ --access public",
327
327
  "prepack": "npm run build",
328
- "prepublishOnly": "npm run release:metadata && npm run release:version-truth && npm run release:dist-freshness && npm run publish:packlist-performance && npm run release:check:full && node ./dist/scripts/check-publish-tag.js && node ./dist/scripts/release-check-stamp.js verify && npm run release:provenance -- --publish && node ./dist/scripts/release-registry-check.js --require-unpublished --require-publish-auth",
328
+ "prepublishOnly": "npm run release:metadata && npm run release:version-truth && npm run release:dist-freshness && npm run publish:packlist-performance && node ./dist/scripts/prepublish-release-check-or-fast.js && node ./dist/scripts/check-publish-tag.js && node ./dist/scripts/release-check-stamp.js verify && npm run release:provenance -- --publish && node ./dist/scripts/release-registry-check.js --require-unpublished --require-publish-auth",
329
329
  "dist:check": "node ./dist/scripts/check-dist-runtime.js",
330
330
  "ux-review:run-wires-imagegen": "node ./dist/scripts/ux-review-run-wires-imagegen-check.js",
331
331
  "ux-review:extract-wires-real-extractor": "node ./dist/scripts/ux-review-extract-wires-real-extractor-check.js",
@@ -557,6 +557,29 @@
557
557
  "release:check:dag:explain": "node ./dist/scripts/release-gate-dag-runner.js --preset release --explain",
558
558
  "release:check:dag:no-cache": "node ./dist/scripts/release-gate-dag-runner.js --preset release --no-cache",
559
559
  "release:check:dag:fail-fast": "node ./dist/scripts/release-gate-dag-runner.js --preset release --fail-fast",
560
+ "codex:0138-capability": "node ./dist/scripts/codex-0138-capability-check.js",
561
+ "codex:0138-capability-artifact": "node ./dist/scripts/codex-0138-capability-artifact-check.js",
562
+ "codex-sdk:version-compat": "node ./dist/scripts/codex-sdk-version-compat-check.js",
563
+ "codex-app:handoff": "node ./dist/scripts/codex-app-handoff-check.js",
564
+ "qa-loop:app-handoff": "node ./dist/scripts/qa-loop-app-handoff-check.js",
565
+ "qa-loop:app-handoff-capability": "node ./dist/scripts/qa-loop-app-handoff-capability-check.js",
566
+ "qa-loop:app-handoff-cli": "node ./dist/scripts/qa-loop-app-handoff-cli-check.js",
567
+ "zellij:qa-app-handoff-status": "node ./dist/scripts/zellij-qa-app-handoff-status-check.js",
568
+ "codex-plugin:json": "node ./dist/scripts/codex-plugin-json-check.js",
569
+ "codex-plugin:inventory": "node ./dist/scripts/codex-plugin-inventory-check.js",
570
+ "mcp:plugin-inventory": "node ./dist/scripts/mcp-plugin-inventory-check.js",
571
+ "codex-plugin:app-template-policy": "node ./dist/scripts/codex-plugin-app-template-policy-check.js",
572
+ "image:artifact-path-contract": "node ./dist/scripts/image-artifact-path-contract-check.js",
573
+ "qa-loop:image-path-exposure": "node ./dist/scripts/qa-loop-image-path-exposure-check.js",
574
+ "image:generation-path-handoff": "node ./dist/scripts/image-generation-path-handoff-check.js",
575
+ "image:followup-edit-path": "node ./dist/scripts/image-followup-edit-path-check.js",
576
+ "codex:effort-order": "node ./dist/scripts/codex-effort-order-check.js",
577
+ "qa-loop:effort-escalation": "node ./dist/scripts/qa-loop-effort-escalation-check.js",
578
+ "codex:account-usage": "node ./dist/scripts/codex-account-usage-check.js",
579
+ "qa-loop:budget-policy": "node ./dist/scripts/qa-loop-budget-policy-check.js",
580
+ "naruto:parallel-gate-consistency": "node ./dist/scripts/naruto-parallel-gate-consistency-check.js",
581
+ "codex:0138-doctor": "node ./dist/scripts/codex-0138-doctor-check.js",
582
+ "doctor:codex-0138-fix": "node ./dist/scripts/doctor-codex-0138-fix-check.js",
560
583
  "release:dag-runner": "node ./dist/scripts/release-gate-dag-runner-check.js",
561
584
  "release:parallel-speed-budget": "node ./dist/scripts/release-parallel-speed-budget-check.js",
562
585
  "release:stability-report": "node ./dist/scripts/release-stability-report-check.js",
@@ -646,7 +669,18 @@
646
669
  "naruto:ssot-gate-aliases": "node ./dist/scripts/naruto-ssot-gate-aliases-check.js",
647
670
  "naruto:ssot-pipeline-default": "node ./dist/scripts/naruto-ssot-pipeline-default-check.js",
648
671
  "team:alias-to-naruto": "node ./dist/scripts/team-alias-to-naruto-check.js",
649
- "release:speed-summary:report": "node ./dist/scripts/release-speed-summary.js"
672
+ "release:speed-summary:report": "node ./dist/scripts/release-speed-summary.js",
673
+ "parallel:strict-pid-proof": "node ./dist/scripts/parallel-strict-pid-proof-check.js",
674
+ "parallel:missing-pid-rejection": "node ./dist/scripts/parallel-missing-pid-rejection-check.js",
675
+ "scheduler:utilization-integral": "node ./dist/scripts/scheduler-utilization-integral-check.js",
676
+ "scheduler:parallel-proof-consistency": "node ./dist/scripts/scheduler-parallel-proof-consistency-check.js",
677
+ "zellij:slot-telemetry-live-flush": "node ./dist/scripts/zellij-slot-telemetry-live-flush-check.js",
678
+ "zellij:slot-pane-stale-detection": "node ./dist/scripts/zellij-slot-pane-stale-detection-check.js",
679
+ "mad-db:lifecycle-hook-decision": "node ./dist/scripts/mad-db-lifecycle-hook-decision-check.js",
680
+ "mad-db:mcp-result-lifecycle": "node ./dist/scripts/mad-db-mcp-result-lifecycle-check.js",
681
+ "mad-db:operation-lifecycle-blackbox": "node ./dist/scripts/mad-db-operation-lifecycle-blackbox.js",
682
+ "runtime:proof-summary": "node ./dist/scripts/runtime-proof-summary-check.js",
683
+ "runtime:proof-summary-cli": "node ./dist/scripts/runtime-proof-summary-cli-check.js"
650
684
  },
651
685
  "keywords": [
652
686
  "sneakoscope",
@@ -700,7 +734,7 @@
700
734
  "license": "MIT",
701
735
  "dependencies": {
702
736
  "@modelcontextprotocol/sdk": "1.29.0",
703
- "@openai/codex-sdk": "0.137.0",
737
+ "@openai/codex-sdk": "^0.138.0",
704
738
  "figlet": "^1.11.0",
705
739
  "typescript": "^5.9.3"
706
740
  },