sneakoscope 2.0.2 → 2.0.5
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 +12 -8
- 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/build-manifest.json +86 -8
- package/dist/commands/doctor.js +14 -0
- package/dist/core/agents/agent-orchestrator.js +70 -4
- package/dist/core/agents/agent-patch-proof.js +5 -0
- package/dist/core/agents/agent-proof-evidence.js +61 -0
- package/dist/core/agents/agent-roster.js +35 -6
- package/dist/core/agents/agent-schema.js +1 -1
- package/dist/core/agents/native-worker-backend-router.js +31 -9
- package/dist/core/agents/ollama-worker-config.js +164 -15
- package/dist/core/codex/codex-0-137-compat.js +119 -0
- package/dist/core/codex-control/codex-control-proof.js +4 -1
- package/dist/core/codex-control/codex-fake-sdk-adapter.js +20 -0
- package/dist/core/codex-control/codex-output-schemas.js +5 -1
- package/dist/core/codex-control/codex-sdk-capability.js +1 -1
- package/dist/core/codex-control/codex-task-runner.js +329 -5
- package/dist/core/codex-control/gpt-final-arbiter.js +160 -0
- package/dist/core/codex-control/gpt-final-context-compressor.js +17 -0
- package/dist/core/codex-control/gpt-final-proof-pack.js +120 -0
- package/dist/core/codex-control/gpt-final-review-schema.js +71 -0
- package/dist/core/codex-control/python-codex-sdk-adapter.js +197 -0
- package/dist/core/codex-control/python-codex-sdk-event-translator.js +14 -0
- package/dist/core/commands/local-model-command.js +79 -18
- package/dist/core/commands/naruto-command.js +195 -12
- package/dist/core/commands/run-command.js +6 -2
- package/dist/core/doctor/doctor-readiness-matrix.js +34 -0
- package/dist/core/feature-fixtures.js +4 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/git-simple.js +143 -4
- package/dist/core/local-llm/local-collaboration-policy.js +93 -0
- package/dist/core/local-llm/local-llm-backpressure.js +20 -0
- package/dist/core/local-llm/local-llm-capability.js +29 -0
- package/dist/core/local-llm/local-llm-client.js +100 -0
- package/dist/core/local-llm/local-llm-config.js +20 -0
- package/dist/core/local-llm/local-llm-context-cache.js +21 -0
- package/dist/core/local-llm/local-llm-control-adapter.js +101 -0
- package/dist/core/local-llm/local-llm-json-repair.js +52 -0
- package/dist/core/local-llm/local-llm-metrics.js +42 -0
- package/dist/core/local-llm/local-llm-ollama-client.js +67 -0
- package/dist/core/local-llm/local-llm-openai-compatible-client.js +30 -0
- package/dist/core/local-llm/local-llm-prompt-cache.js +12 -0
- package/dist/core/local-llm/local-llm-scheduler.js +29 -0
- package/dist/core/local-llm/local-llm-schema-enforcer.js +15 -0
- package/dist/core/local-llm/local-llm-smoke.js +83 -0
- package/dist/core/local-llm/local-llm-warmup.js +20 -0
- package/dist/core/local-llm/local-worker-eligibility.js +27 -0
- package/dist/core/naruto/hardware-capacity-probe.js +36 -0
- package/dist/core/naruto/naruto-active-pool.js +118 -0
- package/dist/core/naruto/naruto-backpressure.js +13 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +65 -0
- package/dist/core/naruto/naruto-finalizer.js +18 -0
- package/dist/core/naruto/naruto-generation-scheduler.js +18 -0
- package/dist/core/naruto/naruto-gpt-final-pack.js +49 -0
- package/dist/core/naruto/naruto-parallel-patch-apply.js +95 -0
- package/dist/core/naruto/naruto-patch-transaction-batch.js +42 -0
- package/dist/core/naruto/naruto-role-policy.js +107 -0
- package/dist/core/naruto/naruto-verification-dag.js +42 -0
- package/dist/core/naruto/naruto-verification-pool.js +18 -0
- package/dist/core/naruto/naruto-work-graph.js +198 -0
- package/dist/core/naruto/naruto-work-item.js +40 -0
- package/dist/core/naruto/naruto-work-stealing.js +11 -0
- package/dist/core/naruto/resource-pressure-monitor.js +32 -0
- package/dist/core/pipeline/final-gpt-patch-stage.js +31 -0
- package/dist/core/pipeline/final-gpt-review-stage.js +5 -0
- package/dist/core/pipeline/finalize-pipeline-result.js +58 -0
- package/dist/core/pipeline/gpt-final-required.js +12 -0
- package/dist/core/prompt/prompt-placeholder-guard.js +30 -0
- package/dist/core/router/capability-card.js +13 -0
- package/dist/core/router/route-cache.js +3 -0
- package/dist/core/router/ultra-router.js +2 -1
- package/dist/core/routes.js +4 -4
- package/dist/core/safety/mutation-guard.js +2 -0
- package/dist/core/update-check.js +60 -25
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-lane-runtime.js +2 -2
- package/dist/core/zellij/zellij-naruto-dashboard.js +36 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +4 -4
- package/dist/scripts/blackbox-command-import-smoke.js +10 -1
- package/dist/scripts/check-package-boundary.js +12 -3
- package/dist/scripts/codex-0-137-compat-check.js +27 -0
- package/dist/scripts/codex-environment-scoped-approvals-check.js +10 -0
- package/dist/scripts/codex-plugin-list-json-check.js +8 -0
- package/dist/scripts/codex-sdk-team-naruto-agent-pipeline-check.js +2 -1
- package/dist/scripts/codex-thread-runtime-choice-check.js +10 -0
- package/dist/scripts/gpt-final-arbiter-check.js +63 -0
- package/dist/scripts/gpt-final-arbiter-performance-check.js +36 -0
- package/dist/scripts/local-collab-all-pipelines-final-gpt-check.js +21 -0
- package/dist/scripts/local-collab-gpt-final-availability-check.js +58 -0
- package/dist/scripts/local-collab-no-local-only-final-check.js +27 -0
- package/dist/scripts/local-collab-policy-check.js +17 -0
- package/dist/scripts/local-llm-all-pipelines-check.js +11 -0
- package/dist/scripts/local-llm-cache-performance-check.js +10 -0
- package/dist/scripts/local-llm-capability-check.js +14 -0
- package/dist/scripts/local-llm-smoke-check.js +23 -0
- package/dist/scripts/local-llm-structured-output-check.js +11 -0
- package/dist/scripts/local-llm-throughput-check.js +10 -0
- package/dist/scripts/local-llm-tool-call-repair-check.js +10 -0
- package/dist/scripts/local-llm-warmup-check.js +11 -0
- package/dist/scripts/naruto-active-pool-check.js +27 -0
- package/dist/scripts/naruto-concurrency-governor-check.js +52 -0
- package/dist/scripts/naruto-gpt-final-pack-check.js +34 -0
- package/dist/scripts/naruto-parallel-patch-apply-check.js +41 -0
- package/dist/scripts/naruto-real-local-gpt-final-smoke.js +16 -0
- package/dist/scripts/naruto-role-distribution-check.js +23 -0
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +6 -0
- package/dist/scripts/naruto-verification-pool-check.js +36 -0
- package/dist/scripts/naruto-work-graph-check.js +24 -0
- package/dist/scripts/naruto-zellij-massive-ui-check.js +23 -0
- package/dist/scripts/prompt-placeholder-guard-check.js +33 -0
- package/dist/scripts/python-codex-sdk-all-pipelines-check.js +47 -0
- package/dist/scripts/python-codex-sdk-capability-check.js +75 -0
- package/dist/scripts/python-codex-sdk-sandbox-policy-check.js +10 -0
- package/dist/scripts/python-codex-sdk-stream-bridge-check.js +12 -0
- package/dist/scripts/release-parallel-check.js +1 -1
- package/dist/scripts/release-real-check.js +5 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +1 -1
- package/package.json +38 -4
- package/schemas/local-llm/local-collaboration-policy.schema.json +57 -0
- package/schemas/local-llm/local-model-config.schema.json +74 -0
- package/schemas/naruto/naruto-concurrency-governor.schema.json +21 -0
- package/schemas/naruto/naruto-work-graph.schema.json +22 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ensureDir, nowIso, sha256, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { detectLocalLlmCapability } from './local-llm-capability.js';
|
|
5
|
+
import { callLocalLlmGenerate, localLlmTokensPerSecond } from './local-llm-client.js';
|
|
6
|
+
import { enforceLocalLlmJsonSchema } from './local-llm-schema-enforcer.js';
|
|
7
|
+
export const LOCAL_LLM_SMOKE_SCHEMA = 'sks.local-llm-smoke.v1';
|
|
8
|
+
export const localLlmSmokeSchema = {
|
|
9
|
+
type: 'object',
|
|
10
|
+
required: ['status', 'summary'],
|
|
11
|
+
properties: {
|
|
12
|
+
status: { type: 'string' },
|
|
13
|
+
summary: { type: 'string' }
|
|
14
|
+
},
|
|
15
|
+
additionalProperties: false
|
|
16
|
+
};
|
|
17
|
+
export async function runLocalLlmGenerationSmoke(config, opts = {}) {
|
|
18
|
+
const started = Date.now();
|
|
19
|
+
const prompt = opts.prompt || 'Return strict JSON: {"status":"ok","summary":"local smoke passed"}';
|
|
20
|
+
const schema = opts.schema || localLlmSmokeSchema;
|
|
21
|
+
const capability = await detectLocalLlmCapability(config);
|
|
22
|
+
const reportPath = expandHome(opts.reportPath || path.join(os.homedir(), '.sneakoscope', 'reports', 'local-llm-smoke.json'));
|
|
23
|
+
if (!capability.ok) {
|
|
24
|
+
const smoke = {
|
|
25
|
+
ok: false,
|
|
26
|
+
ran_at: nowIso(),
|
|
27
|
+
prompt_hash: sha256(prompt),
|
|
28
|
+
latency_ms: Math.max(0, Date.now() - started),
|
|
29
|
+
tokens_per_second: 0,
|
|
30
|
+
schema_valid: false,
|
|
31
|
+
result_path: reportPath,
|
|
32
|
+
status: 'blocked',
|
|
33
|
+
blockers: capability.blockers
|
|
34
|
+
};
|
|
35
|
+
await writeSmokeReport(reportPath, config, smoke, capability);
|
|
36
|
+
return smoke;
|
|
37
|
+
}
|
|
38
|
+
const request = {
|
|
39
|
+
model: config.model,
|
|
40
|
+
prompt,
|
|
41
|
+
stream: false,
|
|
42
|
+
format: 'json',
|
|
43
|
+
think: config.think,
|
|
44
|
+
keep_alive: config.keep_alive,
|
|
45
|
+
options: { temperature: 0 }
|
|
46
|
+
};
|
|
47
|
+
const response = await callLocalLlmGenerate({ ...config, timeout_ms: opts.timeoutMs || 20_000 }, request);
|
|
48
|
+
const latencyMs = Math.max(0, Date.now() - started);
|
|
49
|
+
const enforced = response.ok ? enforceLocalLlmJsonSchema(response.text, schema) : { ok: false, schema_valid: false, issues: [response.error] };
|
|
50
|
+
const smoke = {
|
|
51
|
+
ok: response.ok && enforced.ok,
|
|
52
|
+
ran_at: nowIso(),
|
|
53
|
+
prompt_hash: sha256(prompt),
|
|
54
|
+
latency_ms: latencyMs,
|
|
55
|
+
tokens_per_second: response.ok ? localLlmTokensPerSecond(response.data, response.text, latencyMs) : 0,
|
|
56
|
+
schema_valid: enforced.schema_valid === true,
|
|
57
|
+
result_path: reportPath,
|
|
58
|
+
status: response.ok && enforced.ok ? 'verified' : 'blocked',
|
|
59
|
+
blockers: [
|
|
60
|
+
...(response.ok ? [] : ['local_llm_generate_failed', response.error]),
|
|
61
|
+
...(enforced.ok ? [] : ['local_llm_smoke_schema_invalid', ...(enforced.issues || []).map(String)])
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
await writeSmokeReport(reportPath, config, smoke, capability, response.ok ? response.data : null);
|
|
65
|
+
return smoke;
|
|
66
|
+
}
|
|
67
|
+
async function writeSmokeReport(reportPath, config, smoke, capability, rawResponse = null) {
|
|
68
|
+
await ensureDir(path.dirname(reportPath));
|
|
69
|
+
await writeJsonAtomic(reportPath, {
|
|
70
|
+
schema: LOCAL_LLM_SMOKE_SCHEMA,
|
|
71
|
+
generated_at: nowIso(),
|
|
72
|
+
provider: config.provider,
|
|
73
|
+
model: config.model,
|
|
74
|
+
endpoint: config.base_url,
|
|
75
|
+
smoke,
|
|
76
|
+
capability,
|
|
77
|
+
raw_response: rawResponse
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function expandHome(value) {
|
|
81
|
+
return value.startsWith('~/') ? path.join(os.homedir(), value.slice(2)) : value;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=local-llm-smoke.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { nowIso } from '../fsx.js';
|
|
2
|
+
export function buildLocalLlmWarmupState(config, input) {
|
|
3
|
+
const ttlMs = Math.max(1, Number(input.ttlMs || 10 * 60 * 1000));
|
|
4
|
+
const warmedAt = nowIso();
|
|
5
|
+
return {
|
|
6
|
+
schema: 'sks.local-llm-warmup-state.v1',
|
|
7
|
+
ok: input.ok,
|
|
8
|
+
provider: config.provider,
|
|
9
|
+
model: config.model,
|
|
10
|
+
endpoint: config.base_url,
|
|
11
|
+
warmed_at: warmedAt,
|
|
12
|
+
expires_at: new Date(Date.parse(warmedAt) + ttlMs).toISOString(),
|
|
13
|
+
explicit_only: true,
|
|
14
|
+
postinstall_allowed: false,
|
|
15
|
+
release_check_real_warmup_allowed: false,
|
|
16
|
+
release_real_check_requires_env: 'SKS_REQUIRE_LOCAL_LLM=1',
|
|
17
|
+
reason: input.reason || null
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=local-llm-warmup.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function evaluateLocalWorkerEligibility(input, config) {
|
|
2
|
+
const prompt = String(input.prompt || '');
|
|
3
|
+
const writePaths = Array.isArray(input.requestedScopeContract?.write_paths) ? input.requestedScopeContract.write_paths.map(String) : [];
|
|
4
|
+
const forbidden = /\b(strategy|planning|architecture|design|final review|verification authority|safety authority|integration|database|migration|permission approval)\b/i.test(prompt);
|
|
5
|
+
const allowed = /\b(worker|patch|read-only|grep|qa|test|docstring|summary|refactor)\b/i.test(`${input.route} ${prompt}`);
|
|
6
|
+
const blockers = [
|
|
7
|
+
...(config.enabled ? [] : ['local_llm_disabled']),
|
|
8
|
+
...(config.status === 'verified' ? [] : [`local_llm_${config.status}`]),
|
|
9
|
+
...(input.tier === 'worker' ? [] : ['local_llm_worker_tier_only']),
|
|
10
|
+
...(input.sandboxPolicy === 'full-access' ? ['local_llm_full_access_blocked'] : []),
|
|
11
|
+
...(input.localLlmPolicy?.requiresGptFinal === false ? ['local_llm_requires_gpt_final_policy_missing'] : []),
|
|
12
|
+
...(forbidden ? ['local_llm_forbidden_task_class'] : []),
|
|
13
|
+
...(!allowed && !writePaths.length ? ['local_llm_task_not_eligible'] : [])
|
|
14
|
+
];
|
|
15
|
+
return {
|
|
16
|
+
schema: 'sks.local-worker-eligibility.v1',
|
|
17
|
+
ok: blockers.length === 0,
|
|
18
|
+
task_classes: {
|
|
19
|
+
allowed_detected: allowed,
|
|
20
|
+
forbidden_detected: forbidden,
|
|
21
|
+
write_path_count: writePaths.length
|
|
22
|
+
},
|
|
23
|
+
requires_gpt_final: true,
|
|
24
|
+
blockers
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=local-worker-eligibility.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
export function probeHardwareCapacity(input = {}) {
|
|
3
|
+
const memory = process.memoryUsage();
|
|
4
|
+
return {
|
|
5
|
+
schema: 'sks.naruto-hardware-capacity-probe.v1',
|
|
6
|
+
cpu_core_count: normalizePositiveInt(input.cores, os.cpus()?.length || 1),
|
|
7
|
+
current_load_average: input.loadAverage || os.loadavg(),
|
|
8
|
+
free_memory_bytes: normalizePositiveInt(input.freeMemoryBytes, os.freemem()),
|
|
9
|
+
total_memory_bytes: normalizePositiveInt(input.totalMemoryBytes, os.totalmem()),
|
|
10
|
+
node_heap_used_bytes: normalizePositiveInt(input.nodeHeapUsedBytes, memory.heapUsed),
|
|
11
|
+
node_heap_total_bytes: normalizePositiveInt(input.nodeHeapTotalBytes, memory.heapTotal),
|
|
12
|
+
process_count: normalizePositiveInt(input.processCount, 1),
|
|
13
|
+
file_descriptor_limit: normalizePositiveInt(input.fileDescriptorLimit, Number(process.env.SKS_NARUTO_FD_LIMIT) || 256),
|
|
14
|
+
zellij_pane_count: normalizeNonNegativeInt(input.zellijPaneCount, 0),
|
|
15
|
+
terminal_columns: normalizePositiveInt(input.terminalColumns, process.stdout.columns || 120),
|
|
16
|
+
terminal_rows: normalizePositiveInt(input.terminalRows, process.stdout.rows || 40),
|
|
17
|
+
local_llm_max_parallel_requests: normalizePositiveInt(input.localLlmMaxParallelRequests, Number(process.env.SKS_LOCAL_LLM_MAX_PARALLEL_REQUESTS) || 4),
|
|
18
|
+
remote_api_rate_limit_budget: normalizePositiveInt(input.remoteApiRateLimitBudget, Number(process.env.SKS_REMOTE_API_PARALLEL_BUDGET) || 12),
|
|
19
|
+
gpu_available: input.gpuAvailable === true,
|
|
20
|
+
gpu_vram_mb: normalizeNonNegativeInt(input.gpuVramMb, 0),
|
|
21
|
+
disk_io_pressure: Math.max(0, Math.min(1, Number(input.diskIoPressure ?? 0)))
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function normalizePositiveInt(value, fallback) {
|
|
25
|
+
const parsed = Number(value);
|
|
26
|
+
if (!Number.isFinite(parsed) || parsed < 1)
|
|
27
|
+
return Math.max(1, Math.floor(fallback));
|
|
28
|
+
return Math.floor(parsed);
|
|
29
|
+
}
|
|
30
|
+
function normalizeNonNegativeInt(value, fallback) {
|
|
31
|
+
const parsed = Number(value);
|
|
32
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
33
|
+
return Math.max(0, Math.floor(fallback));
|
|
34
|
+
return Math.floor(parsed);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=hardware-capacity-probe.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createNarutoGeneration, completeNarutoGeneration } from './naruto-generation-scheduler.js';
|
|
2
|
+
export function simulateNarutoActivePool(input) {
|
|
3
|
+
const safeActiveWorkers = Math.max(1, input.governor.safe_active_workers);
|
|
4
|
+
const retryLimit = Math.max(0, Math.floor(Number(input.retryLimit ?? 1)));
|
|
5
|
+
const failIds = new Set((input.failWorkItemIds || []).map(String));
|
|
6
|
+
const pending = [...input.graph.work_items];
|
|
7
|
+
const active = new Map();
|
|
8
|
+
const completed = new Set();
|
|
9
|
+
const failed = new Set();
|
|
10
|
+
const executed = new Map();
|
|
11
|
+
const timeline = [];
|
|
12
|
+
let generationIndex = 1;
|
|
13
|
+
let tick = 0;
|
|
14
|
+
let refillEvents = 0;
|
|
15
|
+
let maxObserved = 0;
|
|
16
|
+
let conflictItemsEnqueued = 0;
|
|
17
|
+
while (pending.length || active.size) {
|
|
18
|
+
let launched = 0;
|
|
19
|
+
for (;;) {
|
|
20
|
+
if (active.size >= safeActiveWorkers)
|
|
21
|
+
break;
|
|
22
|
+
const next = popRunnable(pending, completed, active);
|
|
23
|
+
if (!next)
|
|
24
|
+
break;
|
|
25
|
+
const generation = createNarutoGeneration(next, generationIndex, tick);
|
|
26
|
+
generationIndex += 1;
|
|
27
|
+
active.set(generation.generation_id, generation);
|
|
28
|
+
executed.set(next.id, (executed.get(next.id) || 0) + 1);
|
|
29
|
+
launched += 1;
|
|
30
|
+
}
|
|
31
|
+
if (launched)
|
|
32
|
+
refillEvents += launched;
|
|
33
|
+
maxObserved = Math.max(maxObserved, active.size);
|
|
34
|
+
timeline.push({ tick, active: active.size, pending: pending.length, completed: completed.size, event: launched ? 'refill' : 'wait' });
|
|
35
|
+
const done = [...active.values()].slice(0, Math.max(1, Math.ceil(active.size / 2)));
|
|
36
|
+
if (!done.length && pending.length)
|
|
37
|
+
break;
|
|
38
|
+
for (const generation of done) {
|
|
39
|
+
active.delete(generation.generation_id);
|
|
40
|
+
const shouldFail = failIds.has(generation.work_item_id) && (executed.get(generation.work_item_id) || 0) <= retryLimit;
|
|
41
|
+
completeNarutoGeneration(generation, tick + 1, shouldFail);
|
|
42
|
+
if (shouldFail) {
|
|
43
|
+
failed.add(generation.work_item_id);
|
|
44
|
+
conflictItemsEnqueued += 1;
|
|
45
|
+
pending.push(conflictResolutionFollowup(generation.work_item_id, input.graph.work_items.length + conflictItemsEnqueued));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
completed.add(generation.work_item_id);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
tick += 1;
|
|
52
|
+
if (tick > input.graph.work_items.length * 4 + 20)
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
const duplicateExecutionCount = [...executed.values()].filter((count) => count > 1).length;
|
|
56
|
+
const blockers = [
|
|
57
|
+
...(pending.length ? ['naruto_active_pool_pending_not_drained'] : []),
|
|
58
|
+
...(active.size ? ['naruto_active_pool_active_not_drained'] : []),
|
|
59
|
+
...(maxObserved > safeActiveWorkers ? ['naruto_active_pool_exceeded_safe_workers'] : []),
|
|
60
|
+
...(duplicateExecutionCount > conflictItemsEnqueued ? ['naruto_active_pool_duplicate_execution_without_retry'] : [])
|
|
61
|
+
];
|
|
62
|
+
return {
|
|
63
|
+
schema: 'sks.naruto-active-pool.v1',
|
|
64
|
+
ok: blockers.length === 0,
|
|
65
|
+
safe_active_workers: safeActiveWorkers,
|
|
66
|
+
total_work_items: input.graph.total_work_items,
|
|
67
|
+
completed_count: completed.size,
|
|
68
|
+
failed_count: failed.size,
|
|
69
|
+
refill_events: refillEvents,
|
|
70
|
+
max_observed_active_workers: maxObserved,
|
|
71
|
+
duplicate_execution_count: duplicateExecutionCount,
|
|
72
|
+
conflict_items_enqueued: conflictItemsEnqueued,
|
|
73
|
+
timeline,
|
|
74
|
+
blockers
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function popRunnable(pending, completed, active) {
|
|
78
|
+
const activeWorkIds = new Set([...active.values()].map((item) => item.work_item_id));
|
|
79
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
80
|
+
const item = pending[index];
|
|
81
|
+
if (!item)
|
|
82
|
+
continue;
|
|
83
|
+
if (activeWorkIds.has(item.id))
|
|
84
|
+
continue;
|
|
85
|
+
if (!item.dependencies.every((dep) => completed.has(dep)))
|
|
86
|
+
continue;
|
|
87
|
+
const writeConflict = [...active.values()].some((generation) => {
|
|
88
|
+
const activeItem = pending.find((candidate) => candidate.id === generation.work_item_id);
|
|
89
|
+
return activeItem?.write_paths.some((file) => item.write_paths.includes(file));
|
|
90
|
+
});
|
|
91
|
+
if (writeConflict)
|
|
92
|
+
continue;
|
|
93
|
+
pending.splice(index, 1);
|
|
94
|
+
return item;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function conflictResolutionFollowup(failedId, index) {
|
|
99
|
+
const id = `NW-CONFLICT-${String(index).padStart(4, '0')}`;
|
|
100
|
+
return {
|
|
101
|
+
id,
|
|
102
|
+
kind: 'conflict_resolution',
|
|
103
|
+
title: `Resolve failed work item ${failedId}`,
|
|
104
|
+
target_paths: [],
|
|
105
|
+
readonly_paths: [],
|
|
106
|
+
write_paths: [`.sneakoscope/naruto/conflicts/${id}.json`],
|
|
107
|
+
required_role: 'conflict_resolver',
|
|
108
|
+
write_allowed: true,
|
|
109
|
+
verification_required: true,
|
|
110
|
+
dependencies: [],
|
|
111
|
+
can_run_in_parallel_with: [],
|
|
112
|
+
conflicts_with: [],
|
|
113
|
+
estimated_cost: { tokens: 4000, latency_ms: 45000, cpu_weight: 1, memory_mb: 256, gpu_weight: 0 },
|
|
114
|
+
lease_requirements: [{ path: `.sneakoscope/naruto/conflicts/${id}.json`, kind: 'write' }],
|
|
115
|
+
acceptance: { requires_patch_envelope: true, requires_verification: true, requires_gpt_final: true }
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=naruto-active-pool.js.map
|
|
@@ -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
|