sneakoscope 2.0.4 → 2.0.6

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 (120) hide show
  1. package/README.md +18 -11
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/build-manifest.json +78 -8
  8. package/dist/cli/install-helpers.js +23 -0
  9. package/dist/commands/codex-app.js +25 -3
  10. package/dist/commands/doctor.js +33 -4
  11. package/dist/commands/mad-sks.js +2 -2
  12. package/dist/core/agents/agent-orchestrator.js +22 -3
  13. package/dist/core/agents/agent-proof-evidence.js +59 -2
  14. package/dist/core/agents/agent-roster.js +35 -6
  15. package/dist/core/agents/agent-schema.js +1 -1
  16. package/dist/core/agents/agent-worker-pipeline.js +9 -1
  17. package/dist/core/agents/native-worker-backend-router.js +50 -10
  18. package/dist/core/agents/ollama-worker-config.js +164 -15
  19. package/dist/core/codex/codex-0-137-compat.js +119 -0
  20. package/dist/core/codex-app.js +124 -2
  21. package/dist/core/codex-control/codex-control-proof.js +4 -1
  22. package/dist/core/codex-control/codex-sdk-capability.js +1 -1
  23. package/dist/core/codex-control/codex-task-runner.js +329 -5
  24. package/dist/core/codex-control/python-codex-sdk-adapter.js +197 -0
  25. package/dist/core/codex-control/python-codex-sdk-event-translator.js +14 -0
  26. package/dist/core/commands/local-model-command.js +65 -19
  27. package/dist/core/commands/naruto-command.js +124 -8
  28. package/dist/core/commands/run-command.js +1 -1
  29. package/dist/core/doctor/doctor-readiness-matrix.js +21 -2
  30. package/dist/core/fsx.js +1 -1
  31. package/dist/core/hooks-runtime.js +2 -233
  32. package/dist/core/init.js +8 -8
  33. package/dist/core/local-llm/local-llm-backpressure.js +20 -0
  34. package/dist/core/local-llm/local-llm-capability.js +29 -0
  35. package/dist/core/local-llm/local-llm-client.js +100 -0
  36. package/dist/core/local-llm/local-llm-config.js +6 -1
  37. package/dist/core/local-llm/local-llm-context-cache.js +21 -0
  38. package/dist/core/local-llm/local-llm-control-adapter.js +101 -0
  39. package/dist/core/local-llm/local-llm-json-repair.js +52 -0
  40. package/dist/core/local-llm/local-llm-metrics.js +42 -0
  41. package/dist/core/local-llm/local-llm-ollama-client.js +67 -0
  42. package/dist/core/local-llm/local-llm-openai-compatible-client.js +30 -0
  43. package/dist/core/local-llm/local-llm-prompt-cache.js +12 -0
  44. package/dist/core/local-llm/local-llm-scheduler.js +29 -0
  45. package/dist/core/local-llm/local-llm-schema-enforcer.js +15 -0
  46. package/dist/core/local-llm/local-llm-smoke.js +83 -0
  47. package/dist/core/local-llm/local-llm-warmup.js +20 -0
  48. package/dist/core/local-llm/local-worker-eligibility.js +27 -0
  49. package/dist/core/naruto/hardware-capacity-probe.js +36 -0
  50. package/dist/core/naruto/naruto-active-pool.js +134 -0
  51. package/dist/core/naruto/naruto-backpressure.js +13 -0
  52. package/dist/core/naruto/naruto-concurrency-governor.js +65 -0
  53. package/dist/core/naruto/naruto-finalizer.js +18 -0
  54. package/dist/core/naruto/naruto-generation-scheduler.js +18 -0
  55. package/dist/core/naruto/naruto-gpt-final-pack.js +49 -0
  56. package/dist/core/naruto/naruto-parallel-patch-apply.js +95 -0
  57. package/dist/core/naruto/naruto-patch-transaction-batch.js +42 -0
  58. package/dist/core/naruto/naruto-role-policy.js +107 -0
  59. package/dist/core/naruto/naruto-verification-dag.js +42 -0
  60. package/dist/core/naruto/naruto-verification-pool.js +18 -0
  61. package/dist/core/naruto/naruto-work-graph.js +198 -0
  62. package/dist/core/naruto/naruto-work-item.js +40 -0
  63. package/dist/core/naruto/naruto-work-stealing.js +11 -0
  64. package/dist/core/naruto/resource-pressure-monitor.js +32 -0
  65. package/dist/core/pipeline/finalize-pipeline-result.js +58 -0
  66. package/dist/core/pipeline/gpt-final-required.js +12 -0
  67. package/dist/core/pipeline-internals/runtime-core.js +1 -1
  68. package/dist/core/ppt.js +31 -8
  69. package/dist/core/product-design-app-server.js +410 -0
  70. package/dist/core/product-design-plugin.js +139 -0
  71. package/dist/core/prompt/prompt-placeholder-guard.js +30 -0
  72. package/dist/core/router/capability-card.js +13 -0
  73. package/dist/core/router/route-cache.js +3 -0
  74. package/dist/core/router/ultra-router.js +2 -1
  75. package/dist/core/routes.js +12 -12
  76. package/dist/core/version.js +1 -1
  77. package/dist/core/zellij/zellij-lane-runtime.js +2 -2
  78. package/dist/core/zellij/zellij-naruto-dashboard.js +36 -0
  79. package/dist/core/zellij/zellij-worker-pane-manager.js +4 -4
  80. package/dist/scripts/blackbox-command-import-smoke.js +10 -1
  81. package/dist/scripts/check-package-boundary.js +12 -3
  82. package/dist/scripts/codex-0-137-compat-check.js +27 -0
  83. package/dist/scripts/codex-environment-scoped-approvals-check.js +10 -0
  84. package/dist/scripts/codex-plugin-list-json-check.js +8 -0
  85. package/dist/scripts/codex-thread-runtime-choice-check.js +10 -0
  86. package/dist/scripts/local-collab-all-pipelines-final-gpt-check.js +21 -0
  87. package/dist/scripts/local-llm-all-pipelines-check.js +11 -0
  88. package/dist/scripts/local-llm-cache-performance-check.js +10 -0
  89. package/dist/scripts/local-llm-capability-check.js +14 -0
  90. package/dist/scripts/local-llm-smoke-check.js +23 -0
  91. package/dist/scripts/local-llm-structured-output-check.js +11 -0
  92. package/dist/scripts/local-llm-throughput-check.js +10 -0
  93. package/dist/scripts/local-llm-tool-call-repair-check.js +10 -0
  94. package/dist/scripts/local-llm-warmup-check.js +11 -0
  95. package/dist/scripts/naruto-active-pool-check.js +39 -0
  96. package/dist/scripts/naruto-concurrency-governor-check.js +52 -0
  97. package/dist/scripts/naruto-gpt-final-pack-check.js +34 -0
  98. package/dist/scripts/naruto-parallel-patch-apply-check.js +41 -0
  99. package/dist/scripts/naruto-readonly-routing-check.js +116 -0
  100. package/dist/scripts/naruto-real-local-gpt-final-smoke.js +16 -0
  101. package/dist/scripts/naruto-role-distribution-check.js +23 -0
  102. package/dist/scripts/naruto-shadow-clone-swarm-check.js +13 -0
  103. package/dist/scripts/naruto-verification-pool-check.js +36 -0
  104. package/dist/scripts/naruto-work-graph-check.js +24 -0
  105. package/dist/scripts/naruto-zellij-massive-ui-check.js +23 -0
  106. package/dist/scripts/product-design-auto-install-check.js +119 -0
  107. package/dist/scripts/product-design-plugin-routing-check.js +101 -0
  108. package/dist/scripts/prompt-placeholder-guard-check.js +33 -0
  109. package/dist/scripts/python-codex-sdk-all-pipelines-check.js +47 -0
  110. package/dist/scripts/python-codex-sdk-capability-check.js +75 -0
  111. package/dist/scripts/python-codex-sdk-sandbox-policy-check.js +10 -0
  112. package/dist/scripts/python-codex-sdk-stream-bridge-check.js +12 -0
  113. package/dist/scripts/release-parallel-check.js +16 -2
  114. package/dist/scripts/release-provenance-check.js +21 -0
  115. package/dist/scripts/release-real-check.js +5 -0
  116. package/dist/scripts/zellij-worker-pane-manager-check.js +1 -1
  117. package/package.json +36 -4
  118. package/schemas/local-llm/local-model-config.schema.json +74 -0
  119. package/schemas/naruto/naruto-concurrency-governor.schema.json +21 -0
  120. package/schemas/naruto/naruto-work-graph.schema.json +22 -0
@@ -0,0 +1,13 @@
1
+ export function applyNarutoBackpressure(requestedActiveWorkers, pressure) {
2
+ const requested = Math.max(1, Math.floor(requestedActiveWorkers));
3
+ const multiplier = pressure.state === 'saturated' ? 0.25 : pressure.state === 'throttled' ? 0.5 : 1;
4
+ const adjusted = Math.max(1, Math.floor(requested * multiplier));
5
+ return {
6
+ schema: 'sks.naruto-backpressure.v1',
7
+ requested_active_workers: requested,
8
+ adjusted_active_workers: adjusted,
9
+ backpressure: pressure.state,
10
+ reasons: pressure.reasons
11
+ };
12
+ }
13
+ //# sourceMappingURL=naruto-backpressure.js.map
@@ -0,0 +1,65 @@
1
+ import { probeHardwareCapacity } from './hardware-capacity-probe.js';
2
+ import { applyNarutoBackpressure } from './naruto-backpressure.js';
3
+ import { monitorNarutoResourcePressure } from './resource-pressure-monitor.js';
4
+ export function decideNarutoConcurrency(input = {}) {
5
+ const requestedClones = normalizePositiveInt(input.requestedClones, 12);
6
+ const totalWorkItems = normalizePositiveInt(input.totalWorkItems, requestedClones);
7
+ const pending = normalizeNonNegativeInt(input.pendingWorkQueueSize, totalWorkItems);
8
+ const leaseConflicts = normalizeNonNegativeInt(input.activeLeaseConflicts, 0);
9
+ const hardware = probeHardwareCapacity(input.hardware || {});
10
+ const zellijVisiblePaneCap = normalizePositiveInt(input.zellijVisiblePaneCap, Math.min(12, Math.max(4, Math.floor(hardware.terminal_rows / 3))));
11
+ const backend = String(input.backend || 'codex-sdk');
12
+ const freeGb = hardware.free_memory_bytes / (1024 * 1024 * 1024);
13
+ const totalGb = hardware.total_memory_bytes / (1024 * 1024 * 1024);
14
+ const reclaimableFloorGb = totalGb >= 32 ? 16 : totalGb >= 16 ? 8 : totalGb >= 8 ? 4 : Math.max(1, freeGb);
15
+ const memoryBudgetGb = Math.max(freeGb, reclaimableFloorGb);
16
+ const heavy = backend === 'codex-sdk' || backend === 'zellij' || backend === 'process' || backend === 'ollama';
17
+ const gbPerWorker = heavy ? Number(process.env.SKS_NARUTO_GB_PER_WORKER || 0.25) : Number(process.env.SKS_NARUTO_LIGHT_GB_PER_WORKER || 0.1);
18
+ const memoryCap = Math.max(1, Math.floor(memoryBudgetGb / Math.max(0.05, gbPerWorker)));
19
+ const fdCap = Math.max(1, Math.floor((hardware.file_descriptor_limit - hardware.process_count) / 6));
20
+ const localLlmParallel = Math.max(1, Math.min(4, hardware.local_llm_max_parallel_requests));
21
+ const remoteCodexParallel = Math.max(1, Math.min(hardware.remote_api_rate_limit_budget, requestedClones));
22
+ const queueCap = Math.max(1, Math.min(requestedClones, pending || totalWorkItems));
23
+ const leaseCap = Math.max(1, requestedClones - leaseConflicts);
24
+ const rawSafe = Math.max(1, Math.min(requestedClones, totalWorkItems, memoryCap, fdCap, remoteCodexParallel, queueCap, leaseCap, 100));
25
+ const pressure = monitorNarutoResourcePressure(hardware, { activeWorkers: rawSafe, zellijVisiblePaneCap });
26
+ const backpressure = applyNarutoBackpressure(rawSafe, pressure);
27
+ const safeActiveWorkers = Math.max(1, Math.min(rawSafe, backpressure.adjusted_active_workers));
28
+ const safeVisible = Math.min(safeActiveWorkers, zellijVisiblePaneCap);
29
+ const reasons = [
30
+ ...(memoryCap < requestedClones ? ['memory_cap'] : []),
31
+ ...(fdCap < requestedClones ? ['file_descriptor_budget'] : []),
32
+ ...(remoteCodexParallel < requestedClones ? ['remote_api_rate_limit_budget'] : []),
33
+ ...(localLlmParallel <= 4 ? ['local_llm_max_parallel_requests'] : []),
34
+ ...(safeVisible < safeActiveWorkers ? ['zellij_ui_pane_budget'] : []),
35
+ ...(leaseConflicts > 0 ? ['active_lease_conflicts'] : []),
36
+ ...pressure.reasons
37
+ ];
38
+ return {
39
+ schema: 'sks.naruto-concurrency-governor.v1',
40
+ requested_clones: requestedClones,
41
+ total_work_items: totalWorkItems,
42
+ safe_active_workers: safeActiveWorkers,
43
+ safe_zellij_visible_panes: safeVisible,
44
+ headless_workers: Math.max(0, safeActiveWorkers - safeVisible),
45
+ local_llm_parallel: localLlmParallel,
46
+ remote_codex_parallel: remoteCodexParallel,
47
+ verification_parallel: Math.max(1, Math.min(hardware.cpu_core_count * 2, safeActiveWorkers, 16)),
48
+ reasons: [...new Set(reasons)],
49
+ backpressure: backpressure.backpressure,
50
+ hardware
51
+ };
52
+ }
53
+ function normalizePositiveInt(value, fallback) {
54
+ const parsed = Number(value);
55
+ if (!Number.isFinite(parsed) || parsed < 1)
56
+ return Math.max(1, Math.floor(fallback));
57
+ return Math.floor(parsed);
58
+ }
59
+ function normalizeNonNegativeInt(value, fallback) {
60
+ const parsed = Number(value);
61
+ if (!Number.isFinite(parsed) || parsed < 0)
62
+ return Math.max(0, Math.floor(fallback));
63
+ return Math.floor(parsed);
64
+ }
65
+ //# sourceMappingURL=naruto-concurrency-governor.js.map
@@ -0,0 +1,18 @@
1
+ export function evaluateNarutoFinalizer(input = {}) {
2
+ const localParticipated = input.localParticipated === true;
3
+ const gptFinalRequired = localParticipated;
4
+ const gptFinalAccepted = input.gptFinalStatus === 'approved' || input.gptFinalStatus === 'modified';
5
+ const blockers = [
6
+ ...(gptFinalRequired && !gptFinalAccepted ? ['naruto_local_worker_output_needs_gpt_final_arbiter'] : [])
7
+ ];
8
+ return {
9
+ schema: 'sks.naruto-finalizer.v1',
10
+ local_participated: localParticipated,
11
+ gpt_final_required: gptFinalRequired,
12
+ final_status: blockers.length ? 'blocked' : input.applyPatches === true ? 'accepted' : 'draft',
13
+ final_patch_source: gptFinalRequired ? 'gpt_final_arbiter' : 'deterministic_no_local',
14
+ blockers,
15
+ ok: blockers.length === 0
16
+ };
17
+ }
18
+ //# sourceMappingURL=naruto-finalizer.js.map
@@ -0,0 +1,18 @@
1
+ export function createNarutoGeneration(workItem, index, tick) {
2
+ return {
3
+ generation_id: `NG-${String(index).padStart(6, '0')}`,
4
+ work_item_id: workItem.id,
5
+ role: workItem.required_role,
6
+ status: 'active',
7
+ started_tick: tick,
8
+ completed_tick: null
9
+ };
10
+ }
11
+ export function completeNarutoGeneration(generation, tick, failed = false) {
12
+ return {
13
+ ...generation,
14
+ status: failed ? 'failed' : 'completed',
15
+ completed_tick: tick
16
+ };
17
+ }
18
+ //# sourceMappingURL=naruto-generation-scheduler.js.map
@@ -0,0 +1,49 @@
1
+ export function buildNarutoGptFinalPack(input) {
2
+ const maxPatchEnvelopes = Math.max(1, Math.floor(Number(input.maxPatchEnvelopes || 100)));
3
+ const maxLogs = Math.max(1, Math.floor(Number(input.maxLogs || 12)));
4
+ return {
5
+ schema: 'sks.naruto-gpt-final-pack.v1',
6
+ mission_id: input.missionId,
7
+ route: '$Naruto',
8
+ work_graph_summary: {
9
+ total_work_items: input.graph.total_work_items,
10
+ mixed_work_kinds: input.graph.mixed_work_kinds,
11
+ write_allowed_count: input.graph.write_allowed_count
12
+ },
13
+ role_distribution: input.roleDistribution,
14
+ changed_files: [...new Set((input.changedFiles || []).map(String))],
15
+ patch_envelopes: (input.patchEnvelopes || []).slice(0, maxPatchEnvelopes).map(redactSecrets),
16
+ verification_results: (input.verificationResults || []).slice(0, 200).map(redactSecrets),
17
+ failed_shards: (input.failedShards || []).slice(0, 100).map(redactSecrets),
18
+ conflict_map: (input.conflictMap || []).slice(0, 100).map(redactSecrets),
19
+ rollback_plan: redactSecrets(input.rollbackPlan || { status: 'not_required' }),
20
+ side_effect_report: redactSecrets(input.sideEffectReport || { status: 'not_recorded' }),
21
+ local_llm_metrics: redactSecrets(input.localLlmMetrics || { participated: false }),
22
+ representative_logs: (input.logs || []).slice(0, maxLogs).map((log) => redactSecretText(log).slice(-4000)),
23
+ bounded: true,
24
+ secrets_redacted: true
25
+ };
26
+ }
27
+ export function redactSecrets(value) {
28
+ if (typeof value === 'string')
29
+ return redactSecretText(value);
30
+ if (Array.isArray(value))
31
+ return value.map(redactSecrets);
32
+ if (value && typeof value === 'object') {
33
+ const out = {};
34
+ for (const [key, item] of Object.entries(value)) {
35
+ if (/token|secret|password|api[_-]?key|authorization/i.test(key))
36
+ out[key] = '[REDACTED]';
37
+ else
38
+ out[key] = redactSecrets(item);
39
+ }
40
+ return out;
41
+ }
42
+ return value;
43
+ }
44
+ function redactSecretText(value) {
45
+ return String(value)
46
+ .replace(/sk-[A-Za-z0-9_-]{16,}/g, 'sk-[REDACTED]')
47
+ .replace(/(api[_-]?key|authorization|token|password)\s*[:=]\s*\S+/gi, '$1=[REDACTED]');
48
+ }
49
+ //# sourceMappingURL=naruto-gpt-final-pack.js.map
@@ -0,0 +1,95 @@
1
+ import path from 'node:path';
2
+ import { normalizeAgentPatchEnvelope, validateAgentPatchEnvelope } from '../agents/agent-patch-schema.js';
3
+ import { ensureDir, readText, sha256, writeTextAtomic } from '../fsx.js';
4
+ import { envelopeIdFor, planNarutoPatchTransactionBatches } from './naruto-patch-transaction-batch.js';
5
+ export async function applyNarutoPatchEnvelopes(root, rawEnvelopes, opts = {}) {
6
+ const dryRun = opts.dryRun === true;
7
+ const envelopes = rawEnvelopes.map(normalizeAgentPatchEnvelope);
8
+ const plan = planNarutoPatchTransactionBatches(envelopes);
9
+ const results = [];
10
+ for (const batch of plan.batches) {
11
+ const batchEnvelopes = envelopes.filter((envelope) => batch.envelope_ids.includes(envelopeIdFor(envelope)));
12
+ const batchResults = await Promise.all(batchEnvelopes.map((envelope) => applyEnvelope(root, envelope, dryRun)));
13
+ results.push(...batchResults);
14
+ }
15
+ const blockers = [
16
+ ...plan.conflicts.map((conflict) => `naruto_patch_conflict:${conflict.envelope_id}`),
17
+ ...results.flatMap((result) => result.blockers)
18
+ ];
19
+ return {
20
+ schema: 'sks.naruto-parallel-patch-apply.v1',
21
+ ok: blockers.length === 0,
22
+ dry_run: dryRun,
23
+ batch_count: plan.batches.length,
24
+ parallel_apply_count: plan.batches.filter((batch) => batch.envelope_ids.length > 1).length,
25
+ conflicts: plan.conflicts,
26
+ results,
27
+ blockers
28
+ };
29
+ }
30
+ async function applyEnvelope(root, envelope, dryRun) {
31
+ const validation = validateAgentPatchEnvelope(envelope);
32
+ const beforeHashes = {};
33
+ const afterHashes = {};
34
+ const rollback = [];
35
+ const changedFiles = [];
36
+ const blockers = [...validation.violations];
37
+ if (!validation.ok) {
38
+ return { envelope_id: envelopeIdFor(envelope), ok: false, changed_files: [], before_hashes: {}, after_hashes: {}, rollback: [], blockers };
39
+ }
40
+ for (const operation of envelope.operations) {
41
+ const target = resolvePatchPath(root, operation.path);
42
+ const before = await readText(target, '');
43
+ beforeHashes[operation.path] = sha256(String(before));
44
+ rollback.push({ path: operation.path, content: String(before) });
45
+ const after = applyOperation(String(before), operation);
46
+ afterHashes[operation.path] = sha256(after);
47
+ if (after !== before) {
48
+ changedFiles.push(operation.path);
49
+ if (!dryRun) {
50
+ await ensureDir(path.dirname(target));
51
+ await writeTextAtomic(target, after);
52
+ }
53
+ }
54
+ }
55
+ return {
56
+ envelope_id: envelopeIdFor(envelope),
57
+ ok: blockers.length === 0,
58
+ changed_files: changedFiles,
59
+ before_hashes: beforeHashes,
60
+ after_hashes: afterHashes,
61
+ rollback,
62
+ blockers
63
+ };
64
+ }
65
+ export async function rollbackNarutoPatchResult(root, result) {
66
+ const restored = [];
67
+ const blockers = [];
68
+ for (const entry of result.rollback) {
69
+ try {
70
+ const target = resolvePatchPath(root, entry.path);
71
+ await ensureDir(path.dirname(target));
72
+ await writeTextAtomic(target, entry.content);
73
+ restored.push(entry.path);
74
+ }
75
+ catch (error) {
76
+ blockers.push(error instanceof Error ? error.message : String(error));
77
+ }
78
+ }
79
+ return { ok: blockers.length === 0, restored, blockers };
80
+ }
81
+ function applyOperation(before, operation) {
82
+ if (operation.op === 'write')
83
+ return String(operation.content || '');
84
+ if (operation.op === 'replace')
85
+ return before.replace(String(operation.search || ''), String(operation.replace || ''));
86
+ return before;
87
+ }
88
+ function resolvePatchPath(root, relativePath) {
89
+ const resolved = path.resolve(root, relativePath);
90
+ const base = path.resolve(root);
91
+ if (resolved !== base && !resolved.startsWith(base + path.sep))
92
+ throw new Error(`patch path escapes root: ${relativePath}`);
93
+ return resolved;
94
+ }
95
+ //# sourceMappingURL=naruto-parallel-patch-apply.js.map
@@ -0,0 +1,42 @@
1
+ export function planNarutoPatchTransactionBatches(envelopes) {
2
+ const batches = [];
3
+ const conflicts = [];
4
+ for (const envelope of envelopes) {
5
+ const envelopeId = envelopeIdFor(envelope);
6
+ const paths = envelope.operations.map((operation) => operation.path);
7
+ let placed = false;
8
+ for (const batch of batches) {
9
+ const overlaps = paths.filter((file) => batch.write_paths.includes(file));
10
+ if (overlaps.length)
11
+ continue;
12
+ batch.envelope_ids.push(envelopeId);
13
+ batch.write_paths.push(...paths);
14
+ batch.write_paths = [...new Set(batch.write_paths)];
15
+ placed = true;
16
+ break;
17
+ }
18
+ if (!placed) {
19
+ const previousConflicts = envelopes
20
+ .filter((candidate) => envelopeIdFor(candidate) !== envelopeId)
21
+ .filter((candidate) => candidate.operations.some((operation) => paths.includes(operation.path)))
22
+ .map(envelopeIdFor);
23
+ if (previousConflicts.length)
24
+ conflicts.push({ envelope_id: envelopeId, conflicts_with: previousConflicts, write_paths: paths });
25
+ batches.push({
26
+ batch_id: `NPB-${String(batches.length + 1).padStart(4, '0')}`,
27
+ envelope_ids: [envelopeId],
28
+ write_paths: [...new Set(paths)],
29
+ parallel_apply_allowed: true
30
+ });
31
+ }
32
+ }
33
+ return {
34
+ schema: 'sks.naruto-patch-transaction-batch.v1',
35
+ batches,
36
+ conflicts
37
+ };
38
+ }
39
+ export function envelopeIdFor(envelope) {
40
+ return `${envelope.agent_id}:${envelope.session_id}:${envelope.task_slice_id || envelope.generation_index}`;
41
+ }
42
+ //# sourceMappingURL=naruto-patch-transaction-batch.js.map
@@ -0,0 +1,107 @@
1
+ export const NARUTO_WRITE_ROLES = new Set([
2
+ 'implementer',
3
+ 'modifier',
4
+ 'test_writer',
5
+ 'conflict_resolver',
6
+ 'rollback_planner',
7
+ 'integrator'
8
+ ]);
9
+ export const NARUTO_IMPLEMENTATION_LIKE_ROLES = new Set([
10
+ 'implementer',
11
+ 'modifier',
12
+ 'test_writer',
13
+ 'conflict_resolver'
14
+ ]);
15
+ export function narutoRoleAllowsWrite(role) {
16
+ return NARUTO_WRITE_ROLES.has(role);
17
+ }
18
+ export function mapWorkKindToNarutoRole(kind) {
19
+ switch (kind) {
20
+ case 'implementation':
21
+ return 'implementer';
22
+ case 'code_modification':
23
+ case 'refactor':
24
+ case 'patch_rebase':
25
+ return 'modifier';
26
+ case 'test_generation':
27
+ return 'test_writer';
28
+ case 'test_execution':
29
+ case 'verification':
30
+ case 'ux_review':
31
+ case 'ppt_review':
32
+ case 'image_review':
33
+ return 'verifier';
34
+ case 'research':
35
+ return 'researcher';
36
+ case 'documentation':
37
+ return 'modifier';
38
+ case 'conflict_resolution':
39
+ return 'conflict_resolver';
40
+ case 'rollback_preparation':
41
+ return 'rollback_planner';
42
+ case 'integration_support':
43
+ return 'integrator';
44
+ case 'final_review_input_pack':
45
+ return 'gpt_final_arbiter';
46
+ }
47
+ }
48
+ export function mapNarutoRoleToAgentRole(role) {
49
+ switch (role) {
50
+ case 'implementer':
51
+ case 'modifier':
52
+ case 'test_writer':
53
+ return 'implementer';
54
+ case 'researcher':
55
+ return 'research';
56
+ case 'conflict_resolver':
57
+ case 'rollback_planner':
58
+ case 'integrator':
59
+ case 'gpt_final_arbiter':
60
+ return 'integrator';
61
+ case 'verifier':
62
+ return 'verifier';
63
+ }
64
+ }
65
+ export function buildNarutoRoleDistribution(workItems, opts = {}) {
66
+ const readonly = opts.readonly === true;
67
+ const counts = new Map();
68
+ const workItemRoles = workItems.map((item) => {
69
+ const role = mapWorkKindToNarutoRole(item.kind);
70
+ counts.set(role, (counts.get(role) || 0) + 1);
71
+ return {
72
+ work_item_id: item.id,
73
+ kind: item.kind,
74
+ role,
75
+ write_allowed: item.write_allowed && narutoRoleAllowsWrite(role)
76
+ };
77
+ });
78
+ const entries = [...counts.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([role, count]) => ({
79
+ role,
80
+ count,
81
+ write_allowed: narutoRoleAllowsWrite(role)
82
+ }));
83
+ const implementationLikeWorkers = workItemRoles.filter((row) => NARUTO_IMPLEMENTATION_LIKE_ROLES.has(row.role)).length;
84
+ const totalWorkers = workItems.length;
85
+ const implementationLikeRatio = totalWorkers ? implementationLikeWorkers / totalWorkers : 0;
86
+ const verifierOnly = totalWorkers > 0 && workItemRoles.every((row) => row.role === 'verifier');
87
+ const writeAllowedCount = workItemRoles.filter((row) => row.write_allowed).length;
88
+ const blockers = [
89
+ ...(totalWorkers === 0 ? ['naruto_role_distribution_empty'] : []),
90
+ ...(!readonly && verifierOnly ? ['naruto_default_must_not_be_verifier_only'] : []),
91
+ ...(!readonly && implementationLikeRatio < 0.4 ? ['naruto_write_capable_route_requires_40_percent_implementation_like_roles'] : []),
92
+ ...(!readonly && writeAllowedCount === 0 ? ['naruto_write_capable_route_requires_write_roles'] : [])
93
+ ];
94
+ return {
95
+ schema: 'sks.naruto-role-distribution.v1',
96
+ readonly,
97
+ total_workers: totalWorkers,
98
+ implementation_like_workers: implementationLikeWorkers,
99
+ implementation_like_ratio: Math.round(implementationLikeRatio * 1000) / 1000,
100
+ verifier_only: verifierOnly,
101
+ entries,
102
+ work_item_roles: workItemRoles,
103
+ ok: blockers.length === 0,
104
+ blockers
105
+ };
106
+ }
107
+ //# sourceMappingURL=naruto-role-policy.js.map
@@ -0,0 +1,42 @@
1
+ import { buildVerificationDag } from '../verification/verification-dag.js';
2
+ const NODE_KIND_CYCLE = [
3
+ 'typecheck_shard',
4
+ 'unit_test_shard',
5
+ 'route_gate_shard',
6
+ 'lint_static_scan_shard',
7
+ 'schema_validation_shard',
8
+ 'patch_specific_test_shard',
9
+ 'docs_changelog_check',
10
+ 'side_effect_check',
11
+ 'mutation_ledger_check',
12
+ 'zellij_proof_check',
13
+ 'local_llm_structured_output_check'
14
+ ];
15
+ export function buildNarutoVerificationDag(graph, input = {}) {
16
+ const command = input.command || 'node -e "process.exit(0)"';
17
+ const verifiableItems = graph.work_items.filter((item) => item.verification_required);
18
+ const taskIdByWorkItemId = new Map(verifiableItems.map((item, index) => [item.id, `NV-${String(index + 1).padStart(6, '0')}`]));
19
+ const tasks = verifiableItems
20
+ .map((item, index) => {
21
+ const kind = NODE_KIND_CYCLE[index % NODE_KIND_CYCLE.length] || 'route_gate_shard';
22
+ const taskId = taskIdByWorkItemId.get(item.id) || `NV-${String(index + 1).padStart(6, '0')}`;
23
+ return {
24
+ id: taskId,
25
+ command,
26
+ ...(input.cwd ? { cwd: input.cwd } : {}),
27
+ inputs: [...item.target_paths],
28
+ outputs: [`.sneakoscope/reports/naruto-verification/${item.id}.json`],
29
+ dependencies: item.dependencies.map((dep) => taskIdByWorkItemId.get(dep)).filter((dep) => typeof dep === 'string' && dep !== taskId),
30
+ timeout_ms: 60000,
31
+ read_only: true
32
+ };
33
+ });
34
+ const dag = buildVerificationDag(tasks);
35
+ return {
36
+ ...dag,
37
+ naruto_schema: 'sks.naruto-verification-dag.v1',
38
+ node_kinds: NODE_KIND_CYCLE,
39
+ starts_when_dependencies_ready: true
40
+ };
41
+ }
42
+ //# sourceMappingURL=naruto-verification-dag.js.map
@@ -0,0 +1,18 @@
1
+ import { runVerificationDag } from '../verification/verification-worker-pool.js';
2
+ export async function runNarutoVerificationPool(dag, governor, opts = {}) {
3
+ const safeConcurrency = Math.max(1, governor.verification_parallel);
4
+ const result = await runVerificationDag(dag, {
5
+ ...(opts.cwd ? { cwd: opts.cwd } : {}),
6
+ ...(opts.logDir ? { logDir: opts.logDir } : {}),
7
+ concurrency: safeConcurrency
8
+ });
9
+ return {
10
+ ...result,
11
+ naruto_schema: 'sks.naruto-verification-pool.v1',
12
+ safe_concurrency: safeConcurrency,
13
+ cpu_heavy_cap_respected: safeConcurrency <= Math.max(1, governor.hardware.cpu_core_count * 2),
14
+ io_heavy_cap_respected: governor.backpressure !== 'saturated',
15
+ api_rate_cap_respected: safeConcurrency <= Math.max(1, governor.remote_codex_parallel)
16
+ };
17
+ }
18
+ //# sourceMappingURL=naruto-verification-pool.js.map
@@ -0,0 +1,198 @@
1
+ import { mapWorkKindToNarutoRole } from './naruto-role-policy.js';
2
+ import { isNarutoWriteKind, normalizeNarutoPath } from './naruto-work-item.js';
3
+ const WRITE_CAPABLE_KIND_CYCLE = [
4
+ 'implementation',
5
+ 'code_modification',
6
+ 'test_generation',
7
+ 'verification',
8
+ 'research',
9
+ 'documentation',
10
+ 'refactor',
11
+ 'test_execution',
12
+ 'conflict_resolution',
13
+ 'patch_rebase',
14
+ 'rollback_preparation',
15
+ 'integration_support',
16
+ 'final_review_input_pack'
17
+ ];
18
+ const READONLY_KIND_CYCLE = [
19
+ 'verification',
20
+ 'research',
21
+ 'test_execution',
22
+ 'ux_review',
23
+ 'ppt_review',
24
+ 'image_review',
25
+ 'final_review_input_pack'
26
+ ];
27
+ export function buildNarutoWorkGraph(input = {}) {
28
+ const requestedClones = normalizePositiveInt(input.requestedClones, 12);
29
+ const readonly = input.readonly === true;
30
+ const writeCapable = input.writeCapable !== false && !readonly;
31
+ const totalWorkItems = Math.max(requestedClones, normalizePositiveInt(input.totalWorkItems, requestedClones));
32
+ const kindCycle = writeCapable ? WRITE_CAPABLE_KIND_CYCLE : READONLY_KIND_CYCLE;
33
+ const basePath = normalizeNarutoPath(input.leaseBasePath || '.sneakoscope/naruto/patch-envelopes');
34
+ const targetPaths = normalizePaths(input.targetPaths || []);
35
+ const readonlyPaths = normalizePaths(input.readonlyPaths || []);
36
+ const workItems = [];
37
+ for (let index = 0; index < totalWorkItems; index += 1) {
38
+ const id = `NW-${String(index + 1).padStart(6, '0')}`;
39
+ const kind = kindCycle[index % kindCycle.length] || 'verification';
40
+ const kindWrites = writeCapable && isNarutoWriteKind(kind);
41
+ const selectedTarget = targetPaths.length ? targetPaths[index % targetPaths.length] || targetPaths[0] || '' : `${basePath}/${id}.json`;
42
+ const writePaths = kindWrites ? [selectedTarget].filter(Boolean) : [];
43
+ const readPaths = readonlyPaths.length ? readonlyPaths : targetPaths.filter((item) => !writePaths.includes(item));
44
+ const leaseRequirements = [
45
+ ...writePaths.map((file) => ({ path: file, kind: 'write' })),
46
+ ...readPaths.map((file) => ({ path: file, kind: 'read' }))
47
+ ];
48
+ workItems.push({
49
+ id,
50
+ kind,
51
+ title: titleForKind(kind, id),
52
+ target_paths: [...new Set([...(writePaths.length ? writePaths : [selectedTarget].filter(Boolean)), ...readPaths])],
53
+ readonly_paths: readPaths,
54
+ write_paths: writePaths,
55
+ required_role: mapWorkKindToNarutoRole(kind),
56
+ write_allowed: writePaths.length > 0,
57
+ verification_required: kind !== 'research' && kind !== 'final_review_input_pack',
58
+ dependencies: dependenciesForKind(kind, workItems),
59
+ can_run_in_parallel_with: [],
60
+ conflicts_with: [],
61
+ estimated_cost: estimateCost(kind),
62
+ lease_requirements: leaseRequirements,
63
+ acceptance: {
64
+ requires_patch_envelope: writePaths.length > 0,
65
+ requires_verification: kind !== 'research' && kind !== 'final_review_input_pack',
66
+ requires_gpt_final: writePaths.length > 0 || kind === 'final_review_input_pack'
67
+ }
68
+ });
69
+ }
70
+ const activeWaves = planNarutoWorkWaves(workItems, Math.max(1, normalizePositiveInt(input.maxActiveWorkers, requestedClones)));
71
+ annotateParallelCompatibility(workItems, activeWaves);
72
+ const mixedWorkKinds = [...new Set(workItems.map((item) => item.kind))];
73
+ const writeAllowedCount = workItems.filter((item) => item.write_allowed).length;
74
+ const blockers = [
75
+ ...(mixedWorkKinds.length < 2 ? ['naruto_work_graph_not_mixed'] : []),
76
+ ...(!readonly && writeAllowedCount === 0 ? ['naruto_write_capable_graph_missing_write_items'] : []),
77
+ ...(workItems.length < requestedClones ? ['naruto_work_graph_below_requested_clones'] : []),
78
+ ...activeWaves.flatMap((wave) => wave.conflict_count > 0 ? [`naruto_wave_write_conflict:${wave.wave_id}`] : [])
79
+ ];
80
+ return {
81
+ schema: 'sks.naruto-work-graph.v1',
82
+ route: '$Naruto',
83
+ requested_clones: requestedClones,
84
+ total_work_items: workItems.length,
85
+ readonly,
86
+ write_capable: writeCapable,
87
+ work_items: workItems,
88
+ active_waves: activeWaves,
89
+ mixed_work_kinds: mixedWorkKinds,
90
+ write_allowed_count: writeAllowedCount,
91
+ ok: blockers.length === 0,
92
+ blockers
93
+ };
94
+ }
95
+ export function validateNarutoWorkGraph(graph) {
96
+ const ids = new Set();
97
+ const blockers = [...graph.blockers];
98
+ for (const item of graph.work_items) {
99
+ if (ids.has(item.id))
100
+ blockers.push(`duplicate_work_item:${item.id}`);
101
+ ids.add(item.id);
102
+ for (const dep of item.dependencies) {
103
+ if (!ids.has(dep) && !graph.work_items.some((candidate) => candidate.id === dep))
104
+ blockers.push(`missing_dependency:${item.id}:${dep}`);
105
+ }
106
+ if (item.write_allowed && item.acceptance.requires_patch_envelope !== true)
107
+ blockers.push(`write_item_missing_patch_envelope_acceptance:${item.id}`);
108
+ }
109
+ if (graph.total_work_items !== graph.work_items.length)
110
+ blockers.push('naruto_work_graph_count_mismatch');
111
+ if (!graph.readonly && graph.write_allowed_count === 0)
112
+ blockers.push('naruto_write_capable_graph_missing_write_items');
113
+ return { ok: blockers.length === 0, blockers: [...new Set(blockers)] };
114
+ }
115
+ export function planNarutoWorkWaves(items, maxActiveWorkers) {
116
+ const pending = [...items];
117
+ const waves = [];
118
+ while (pending.length) {
119
+ const waveItems = [];
120
+ const writePaths = new Set();
121
+ for (let index = 0; index < pending.length && waveItems.length < maxActiveWorkers;) {
122
+ const item = pending[index];
123
+ if (!item) {
124
+ index += 1;
125
+ continue;
126
+ }
127
+ const conflicts = item.write_paths.some((file) => writePaths.has(file));
128
+ if (conflicts) {
129
+ index += 1;
130
+ continue;
131
+ }
132
+ waveItems.push(item);
133
+ for (const file of item.write_paths)
134
+ writePaths.add(file);
135
+ pending.splice(index, 1);
136
+ }
137
+ if (!waveItems.length) {
138
+ const item = pending.shift();
139
+ if (item)
140
+ waveItems.push(item);
141
+ }
142
+ const paths = waveItems.flatMap((item) => item.write_paths);
143
+ waves.push({
144
+ wave_id: `NWAVE-${String(waves.length + 1).padStart(4, '0')}`,
145
+ work_item_ids: waveItems.map((item) => item.id),
146
+ write_paths: [...new Set(paths)],
147
+ conflict_count: paths.length - new Set(paths).size
148
+ });
149
+ }
150
+ return waves;
151
+ }
152
+ function annotateParallelCompatibility(items, waves) {
153
+ const byId = new Map(items.map((item) => [item.id, item]));
154
+ for (const wave of waves) {
155
+ for (const id of wave.work_item_ids) {
156
+ const item = byId.get(id);
157
+ if (!item)
158
+ continue;
159
+ item.can_run_in_parallel_with = wave.work_item_ids.filter((other) => other !== id);
160
+ item.conflicts_with = items
161
+ .filter((other) => other.id !== id && other.write_paths.some((file) => item.write_paths.includes(file)))
162
+ .map((other) => other.id);
163
+ }
164
+ }
165
+ }
166
+ function dependenciesForKind(kind, previous) {
167
+ if (kind === 'verification' || kind === 'test_execution') {
168
+ const candidate = [...previous].reverse().find((item) => item.write_allowed);
169
+ return candidate ? [candidate.id] : [];
170
+ }
171
+ if (kind === 'final_review_input_pack') {
172
+ return previous.slice(-3).map((item) => item.id);
173
+ }
174
+ return [];
175
+ }
176
+ function estimateCost(kind) {
177
+ const heavy = kind === 'implementation' || kind === 'code_modification' || kind === 'refactor' || kind === 'conflict_resolution';
178
+ return {
179
+ tokens: heavy ? 8000 : 3000,
180
+ latency_ms: heavy ? 90000 : 30000,
181
+ cpu_weight: kind === 'test_execution' || kind === 'verification' ? 2 : 1,
182
+ memory_mb: heavy ? 512 : 256,
183
+ gpu_weight: 0
184
+ };
185
+ }
186
+ function titleForKind(kind, id) {
187
+ return `${id} ${kind.replace(/_/g, ' ')}`;
188
+ }
189
+ function normalizePositiveInt(value, fallback) {
190
+ const parsed = Number(value);
191
+ if (!Number.isFinite(parsed) || parsed < 1)
192
+ return Math.max(1, Math.floor(fallback));
193
+ return Math.floor(parsed);
194
+ }
195
+ function normalizePaths(paths) {
196
+ return [...new Set(paths.map(normalizeNarutoPath).filter(Boolean))];
197
+ }
198
+ //# sourceMappingURL=naruto-work-graph.js.map