sneakoscope 2.0.8 → 2.0.10
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 +8 -4
- 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 +33 -8
- package/dist/cli/command-registry.js +1 -0
- package/dist/commands/doctor.js +18 -1
- package/dist/commands/zellij-slot-pane.js +26 -0
- package/dist/commands/zellij.js +144 -1
- package/dist/core/agents/agent-orchestrator.js +202 -9
- package/dist/core/agents/agent-role-config.js +92 -0
- package/dist/core/agents/native-cli-session-swarm.js +230 -48
- package/dist/core/commands/mad-sks-command.js +17 -26
- package/dist/core/commands/naruto-command.js +155 -37
- package/dist/core/doctor/doctor-readiness-matrix.js +6 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +4 -0
- package/dist/core/init.js +1 -0
- package/dist/core/naruto/naruto-active-pool.js +141 -0
- package/dist/core/naruto/naruto-concurrency-governor.js +17 -2
- package/dist/core/naruto/naruto-real-worker-child.js +35 -0
- package/dist/core/naruto/naruto-real-worker-runtime.js +121 -0
- package/dist/core/naruto/naruto-work-graph.js +2 -1
- package/dist/core/release/release-gate-cache-v2.js +58 -4
- package/dist/core/release/release-gate-dag.js +36 -25
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-dashboard-renderer.js +22 -6
- package/dist/core/zellij/zellij-launcher.js +3 -3
- package/dist/core/zellij/zellij-layout-builder.js +1 -1
- package/dist/core/zellij/zellij-right-column-layout-proof.js +42 -0
- package/dist/core/zellij/zellij-right-column-manager.js +304 -0
- package/dist/core/zellij/zellij-slot-pane-renderer.js +82 -0
- package/dist/core/zellij/zellij-ui-mode.js +16 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +152 -17
- package/dist/scripts/agent-role-config-repair-check.js +33 -0
- package/dist/scripts/codex-sdk-release-review-pipeline-check.js +5 -5
- package/dist/scripts/doctor-fix-proves-codex-read-check.js +26 -5
- package/dist/scripts/git-worktree-integration-primary-check.js +4 -2
- package/dist/scripts/git-worktree-integration-primary-runtime-check.js +20 -0
- package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
- package/dist/scripts/mad-sks-zellij-default-pane-worker-check.js +2 -2
- package/dist/scripts/mutation-callsite-coverage-check.js +2 -1
- package/dist/scripts/naruto-concurrency-governor-check.js +2 -1
- package/dist/scripts/naruto-extreme-parallelism-check.js +22 -0
- package/dist/scripts/naruto-extreme-parallelism-real-check.js +42 -0
- package/dist/scripts/naruto-real-active-pool-check.js +39 -0
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +53 -0
- package/dist/scripts/naruto-work-graph-check.js +1 -1
- package/dist/scripts/naruto-zellij-dynamic-right-column-check.js +48 -0
- package/dist/scripts/product-design-auto-install-check.js +3 -3
- package/dist/scripts/product-design-plugin-routing-check.js +3 -3
- package/dist/scripts/readme-architecture-imagegen-official-check.js +4 -3
- package/dist/scripts/release-cache-glob-hashing-check.js +42 -0
- package/dist/scripts/release-check-dynamic-execute.js +27 -1
- package/dist/scripts/release-check-dynamic.js +38 -11
- package/dist/scripts/release-check-stamp.js +7 -2
- package/dist/scripts/release-dag-full-coverage-check.js +35 -0
- package/dist/scripts/release-dynamic-performance-check.js +31 -1
- package/dist/scripts/release-gate-existence-audit.js +29 -33
- package/dist/scripts/release-parallel-speed-budget-check.js +67 -13
- package/dist/scripts/release-readiness-report.js +14 -3
- package/dist/scripts/zellij-dashboard-pane-check.js +6 -4
- package/dist/scripts/zellij-developer-controls-check.js +20 -0
- package/dist/scripts/zellij-dynamic-pane-lifecycle-check.js +21 -0
- package/dist/scripts/zellij-initial-main-only-blackbox.js +28 -0
- package/dist/scripts/zellij-right-column-geometry-proof.js +162 -0
- package/dist/scripts/zellij-right-column-headless-overflow-check.js +22 -0
- package/dist/scripts/zellij-right-column-manager-check.js +22 -0
- package/dist/scripts/zellij-slot-only-ui-check.js +22 -0
- package/dist/scripts/zellij-slot-pane-renderer-check.js +38 -0
- package/dist/scripts/zellij-worker-pane-manager-check.js +2 -1
- package/dist/scripts/zellij-worker-pane-manager-single-owner-check.js +7 -6
- package/package.json +23 -5
- package/schemas/zellij/zellij-right-column-state.schema.json +41 -0
|
@@ -10,11 +10,11 @@ import { attachZellijSessionInteractive, launchZellijLayout } from '../zellij/ze
|
|
|
10
10
|
import { buildNarutoWorkGraph } from '../naruto/naruto-work-graph.js';
|
|
11
11
|
import { buildNarutoRoleDistribution } from '../naruto/naruto-role-policy.js';
|
|
12
12
|
import { decideNarutoConcurrency } from '../naruto/naruto-concurrency-governor.js';
|
|
13
|
-
import { runNarutoActivePool } from '../naruto/naruto-active-pool.js';
|
|
13
|
+
import { runNarutoActivePool, runNarutoRealActivePool } from '../naruto/naruto-active-pool.js';
|
|
14
|
+
import { collectActualNarutoWorker, spawnActualNarutoWorker } from '../naruto/naruto-real-worker-runtime.js';
|
|
14
15
|
import { buildNarutoVerificationDag } from '../naruto/naruto-verification-dag.js';
|
|
15
16
|
import { buildNarutoGptFinalPack } from '../naruto/naruto-gpt-final-pack.js';
|
|
16
17
|
import { planNarutoZellijDashboard } from '../zellij/zellij-naruto-dashboard.js';
|
|
17
|
-
import { openZellijDashboardPane } from '../zellij/zellij-dashboard-pane.js';
|
|
18
18
|
import { checkPromptPlaceholders } from '../prompt/prompt-placeholder-guard.js';
|
|
19
19
|
import { evaluateGitWorktreeCapability } from '../git/git-worktree-capability.js';
|
|
20
20
|
const NARUTO_RESULT_SCHEMA = 'sks.naruto-command-result.v1';
|
|
@@ -31,6 +31,10 @@ export async function narutoCommand(commandOrArgs = 'naruto', maybeArgs = []) {
|
|
|
31
31
|
return narutoHelp(parsed);
|
|
32
32
|
if (parsed.action === 'status')
|
|
33
33
|
return narutoStatus(parsed);
|
|
34
|
+
if (parsed.action === 'dashboard')
|
|
35
|
+
return narutoDashboard(parsed);
|
|
36
|
+
if (parsed.action === 'workers')
|
|
37
|
+
return narutoWorkers(parsed);
|
|
34
38
|
return narutoRun(parsed);
|
|
35
39
|
}
|
|
36
40
|
async function narutoRun(parsed) {
|
|
@@ -109,6 +113,32 @@ async function narutoRun(parsed) {
|
|
|
109
113
|
const activeSlots = Math.max(1, Math.min(roster.agent_count, parsed.concurrency || Math.max(governor.safe_active_workers, backendMinimum), safe.cap));
|
|
110
114
|
const zellijVisiblePanes = Math.max(1, Math.min(activeSlots, governor.safe_zellij_visible_panes));
|
|
111
115
|
const activePool = await runNarutoActivePool({ graph: workGraph, governor: { ...governor, safe_active_workers: activeSlots } });
|
|
116
|
+
const realRuntimeWorktreePolicy = schedulerBackend === 'fake'
|
|
117
|
+
? {
|
|
118
|
+
mode: 'patch-envelope-only',
|
|
119
|
+
required: false,
|
|
120
|
+
main_repo_root: worktreePolicy.main_repo_root,
|
|
121
|
+
worktree_root: null,
|
|
122
|
+
fallback_reason: 'fake_backend_fixture_skips_real_worktree_cleanup'
|
|
123
|
+
}
|
|
124
|
+
: worktreePolicy;
|
|
125
|
+
const realActivePool = await runNarutoRealActivePool({
|
|
126
|
+
graph: workGraph,
|
|
127
|
+
governor: { ...governor, safe_active_workers: activeSlots },
|
|
128
|
+
spawnWorker: async (item, placement) => spawnActualNarutoWorker({
|
|
129
|
+
root,
|
|
130
|
+
missionId: mission.id,
|
|
131
|
+
item,
|
|
132
|
+
placement,
|
|
133
|
+
backend: schedulerBackend,
|
|
134
|
+
worktreePolicy: realRuntimeWorktreePolicy,
|
|
135
|
+
zellijSessionName: `sks-${mission.id}`,
|
|
136
|
+
visiblePaneCap: zellijVisiblePanes
|
|
137
|
+
}),
|
|
138
|
+
collectWorker: async (handle) => collectActualNarutoWorker(handle),
|
|
139
|
+
enqueueVerification: async () => undefined,
|
|
140
|
+
updateDashboard: async () => undefined
|
|
141
|
+
});
|
|
112
142
|
const verificationDag = buildNarutoVerificationDag(workGraph, { cwd: root });
|
|
113
143
|
const gptFinalPack = buildNarutoGptFinalPack({
|
|
114
144
|
missionId: mission.id,
|
|
@@ -132,6 +162,7 @@ async function narutoRun(parsed) {
|
|
|
132
162
|
roleDistribution,
|
|
133
163
|
governor,
|
|
134
164
|
activePool,
|
|
165
|
+
realActivePool,
|
|
135
166
|
verificationDag,
|
|
136
167
|
gptFinalPack,
|
|
137
168
|
zellijDashboard,
|
|
@@ -145,40 +176,25 @@ async function narutoRun(parsed) {
|
|
|
145
176
|
missionId: mission.id,
|
|
146
177
|
ledgerRoot,
|
|
147
178
|
kind: 'naruto',
|
|
148
|
-
slotCount:
|
|
179
|
+
slotCount: 0,
|
|
149
180
|
dryRun: false,
|
|
150
181
|
attach: false
|
|
151
182
|
});
|
|
152
183
|
if (liveZellij?.ok && liveZellij.capability?.status === 'ok') {
|
|
153
|
-
liveZellij.dashboard_pane =
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
active: worktreePolicy.mode === 'git-worktree' ? activeSlots : 0,
|
|
168
|
-
completed: 0,
|
|
169
|
-
retained: 0
|
|
170
|
-
},
|
|
171
|
-
local_llm: { tps: 0, queue: localWorker.auto_select_eligible ? Math.max(0, activeSlots - zellijVisiblePanes) : 0 },
|
|
172
|
-
gpt_final_status: 'pending',
|
|
173
|
-
gate_progress: 'naruto:pre-orchestrator'
|
|
174
|
-
}
|
|
175
|
-
}).catch((err) => ({
|
|
176
|
-
ok: false,
|
|
177
|
-
pane_kind: 'dashboard',
|
|
178
|
-
worker_pane: false,
|
|
179
|
-
blockers: [`zellij_dashboard_exception:${err?.message || String(err)}`]
|
|
180
|
-
}));
|
|
181
|
-
console.log('Zellij: prepared ' + zellijVisiblePanes + ' visible active clone lane(s) in ' + liveZellij.session_name + ' with ' + Math.max(0, activeSlots - zellijVisiblePanes) + ' headless active worker(s). Attach with: ' + (liveZellij.attach_command_with_env || liveZellij.attach_command));
|
|
184
|
+
liveZellij.dashboard_pane = null;
|
|
185
|
+
liveZellij.right_column_mode = 'spawn-on-first-worker';
|
|
186
|
+
await writeJsonAtomic(path.join(mission.dir, 'zellij-initial-ui.json'), {
|
|
187
|
+
schema: 'sks.zellij-initial-ui.v1',
|
|
188
|
+
ok: true,
|
|
189
|
+
mission_id: mission.id,
|
|
190
|
+
session_name: liveZellij.session_name,
|
|
191
|
+
initial_panes: 'main-only',
|
|
192
|
+
dashboard_created: false,
|
|
193
|
+
worker_panes_created: 0,
|
|
194
|
+
right_column_mode: 'spawn-on-first-worker',
|
|
195
|
+
visible_pane_cap: zellijVisiblePanes
|
|
196
|
+
});
|
|
197
|
+
console.log('Zellij: started main-only session ' + liveZellij.session_name + '; right column opens on first visible worker spawn. Attach with: ' + (liveZellij.attach_command_with_env || liveZellij.attach_command));
|
|
182
198
|
if (parsed.attach)
|
|
183
199
|
attachZellijSessionInteractive(liveZellij.session_name, { cwd: process.cwd(), configPath: liveZellij.clipboard_config_path });
|
|
184
200
|
}
|
|
@@ -213,6 +229,10 @@ async function narutoRun(parsed) {
|
|
|
213
229
|
mock: parsed.mock,
|
|
214
230
|
real: parsed.real,
|
|
215
231
|
readonly: parsed.readonly,
|
|
232
|
+
zellijSessionName: liveZellij?.session_name || `sks-${mission.id}`,
|
|
233
|
+
workerPlacement: parsed.json || parsed.noOpenZellij ? 'process' : 'zellij-pane',
|
|
234
|
+
zellijPaneWorker: true,
|
|
235
|
+
zellijVisiblePaneCap: zellijVisiblePanes,
|
|
216
236
|
// Shadow clones ALWAYS run in fast service tier — never honor --no-fast/standard.
|
|
217
237
|
fastMode: true,
|
|
218
238
|
serviceTier: 'fast',
|
|
@@ -252,11 +272,23 @@ async function narutoRun(parsed) {
|
|
|
252
272
|
ok: activePool.ok,
|
|
253
273
|
max_observed_active_workers: activePool.max_observed_active_workers,
|
|
254
274
|
refill_events: activePool.refill_events,
|
|
255
|
-
completed_count: activePool.completed_count
|
|
275
|
+
completed_count: activePool.completed_count,
|
|
276
|
+
real_runtime: {
|
|
277
|
+
ok: realActivePool.ok,
|
|
278
|
+
active_cap: realActivePool.active_cap,
|
|
279
|
+
max_observed_active_workers: realActivePool.max_observed_active_workers,
|
|
280
|
+
average_active_workers: realActivePool.average_active_workers,
|
|
281
|
+
active_pool_utilization: realActivePool.active_pool_utilization,
|
|
282
|
+
refill_latency_ms_p95: realActivePool.refill_latency_ms_p95,
|
|
283
|
+
visible_workers: realActivePool.visible_workers,
|
|
284
|
+
headless_workers: realActivePool.headless_workers,
|
|
285
|
+
worker_lifecycle_count: realActivePool.worker_lifecycle.length,
|
|
286
|
+
worker_lifecycle_sample: realActivePool.worker_lifecycle.slice(0, 5)
|
|
287
|
+
}
|
|
256
288
|
},
|
|
257
289
|
local_worker: localWorkerSummary,
|
|
258
290
|
proof: result.proof?.status || 'missing',
|
|
259
|
-
run: result,
|
|
291
|
+
run: compactNarutoRunResult(result),
|
|
260
292
|
zellij: null
|
|
261
293
|
};
|
|
262
294
|
summary.zellij = liveZellij;
|
|
@@ -273,6 +305,36 @@ async function narutoRun(parsed) {
|
|
|
273
305
|
console.log('Zellij: optional live panes unavailable (' + ((summary.zellij.warnings || []).join('; ') || summary.zellij.capability?.status || 'unknown') + ')');
|
|
274
306
|
});
|
|
275
307
|
}
|
|
308
|
+
function compactNarutoRunResult(result) {
|
|
309
|
+
return {
|
|
310
|
+
schema: result?.schema || 'sks.agent-run.v1',
|
|
311
|
+
ok: result?.ok === true,
|
|
312
|
+
mission_id: result?.mission_id || null,
|
|
313
|
+
route: result?.route || NARUTO_ROUTE,
|
|
314
|
+
backend: result?.backend || null,
|
|
315
|
+
target_active_slots: result?.target_active_slots ?? null,
|
|
316
|
+
proof: result?.proof ? {
|
|
317
|
+
ok: result.proof.ok === true,
|
|
318
|
+
status: result.proof.status || null,
|
|
319
|
+
blockers: result.proof.blockers || []
|
|
320
|
+
} : null,
|
|
321
|
+
scheduler: result?.scheduler ? {
|
|
322
|
+
state: {
|
|
323
|
+
completed_count: result.scheduler.state?.completed_count ?? result.scheduler.completed_count ?? null,
|
|
324
|
+
failed_count: result.scheduler.state?.failed_count ?? result.scheduler.failed_count ?? null,
|
|
325
|
+
blocked_count: result.scheduler.state?.blocked_count ?? result.scheduler.blocked_count ?? null,
|
|
326
|
+
max_observed_active_slots: result.scheduler.state?.max_observed_active_slots ?? result.scheduler.max_observed_active_slots ?? null
|
|
327
|
+
}
|
|
328
|
+
} : null,
|
|
329
|
+
artifacts: {
|
|
330
|
+
ledger_root: result?.ledger_root || null,
|
|
331
|
+
proof: 'agent-proof-evidence.json',
|
|
332
|
+
scheduler: 'agent-scheduler-state.json',
|
|
333
|
+
native_cli_session_swarm: 'agent-native-cli-session-swarm.json',
|
|
334
|
+
naruto_real_active_pool: 'naruto-real-active-pool.json'
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
276
338
|
function summarizeNarutoLocalWorkerResult(localWorker, result) {
|
|
277
339
|
const backendCounts = {};
|
|
278
340
|
const rows = Array.isArray(result?.results) ? result.results : [];
|
|
@@ -325,6 +387,57 @@ async function narutoStatus(parsed) {
|
|
|
325
387
|
console.log('Roles: ' + roleDistribution.entries.map((entry) => `${entry.role}:${entry.count}`).join(', '));
|
|
326
388
|
});
|
|
327
389
|
}
|
|
390
|
+
async function narutoDashboard(parsed) {
|
|
391
|
+
const root = await sksRoot();
|
|
392
|
+
const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
|
|
393
|
+
if (!id)
|
|
394
|
+
return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'dashboard', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
|
|
395
|
+
const { dir } = await loadMission(root, id);
|
|
396
|
+
const snapshot = await readJson(path.join(dir, 'zellij-dashboard-snapshot.json'), null);
|
|
397
|
+
const rightColumnState = await readJson(path.join(dir, 'zellij-right-column-state.json'), null);
|
|
398
|
+
const summary = {
|
|
399
|
+
schema: NARUTO_RESULT_SCHEMA,
|
|
400
|
+
ok: Boolean(snapshot || rightColumnState),
|
|
401
|
+
action: 'dashboard',
|
|
402
|
+
mission_id: id,
|
|
403
|
+
snapshot,
|
|
404
|
+
right_column_state: rightColumnState,
|
|
405
|
+
blockers: snapshot || rightColumnState ? [] : ['naruto_dashboard_missing']
|
|
406
|
+
};
|
|
407
|
+
return emit(parsed, summary, () => {
|
|
408
|
+
console.log('🍥 Naruto dashboard: ' + id);
|
|
409
|
+
console.log('Right column: ' + (rightColumnState?.status || 'missing'));
|
|
410
|
+
if (snapshot)
|
|
411
|
+
console.log('Active/visible/headless/queue: ' + [snapshot.active_workers, snapshot.visible_panes, snapshot.headless_workers, snapshot.queue_depth].join('/'));
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async function narutoWorkers(parsed) {
|
|
415
|
+
const root = await sksRoot();
|
|
416
|
+
const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
|
|
417
|
+
if (!id)
|
|
418
|
+
return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'workers', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
|
|
419
|
+
const { dir } = await loadMission(root, id);
|
|
420
|
+
const swarm = await readJson(path.join(dir, 'agents', 'agent-native-cli-session-swarm.json'), null);
|
|
421
|
+
const state = await readJson(path.join(dir, 'zellij-right-column-state.json'), null);
|
|
422
|
+
const records = Array.isArray(swarm?.records) ? swarm.records : [];
|
|
423
|
+
const summary = {
|
|
424
|
+
schema: NARUTO_RESULT_SCHEMA,
|
|
425
|
+
ok: Boolean(swarm || state),
|
|
426
|
+
action: 'workers',
|
|
427
|
+
mission_id: id,
|
|
428
|
+
active: records.filter((row) => row.status === 'running' || row.status === 'launching').length,
|
|
429
|
+
completed: records.filter((row) => row.status === 'closed').length,
|
|
430
|
+
failed: records.filter((row) => row.status === 'failed').length,
|
|
431
|
+
visible_worker_panes: state?.visible_worker_panes || [],
|
|
432
|
+
headless_workers: state?.headless_workers || [],
|
|
433
|
+
records,
|
|
434
|
+
blockers: swarm || state ? [] : ['naruto_worker_records_missing']
|
|
435
|
+
};
|
|
436
|
+
return emit(parsed, summary, () => {
|
|
437
|
+
console.log('🍥 Naruto workers: ' + id);
|
|
438
|
+
console.log(`Active ${summary.active} · completed ${summary.completed} · failed ${summary.failed} · visible ${summary.visible_worker_panes.length} · headless ${summary.headless_workers.length}`);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
328
441
|
async function narutoHelp(parsed) {
|
|
329
442
|
const help = {
|
|
330
443
|
schema: NARUTO_RESULT_SCHEMA,
|
|
@@ -349,13 +462,13 @@ function parseNarutoArgs(args = []) {
|
|
|
349
462
|
if (hasFlag(args, '--help') || hasFlag(args, '-h'))
|
|
350
463
|
args = ['help', ...args.filter((arg) => arg !== '--help' && arg !== '-h')];
|
|
351
464
|
const first = args[0] && !String(args[0]).startsWith('--') ? String(args[0]) : '';
|
|
352
|
-
const actions = new Set(['run', 'status', 'help']);
|
|
465
|
+
const actions = new Set(['run', 'status', 'help', 'dashboard', 'workers']);
|
|
353
466
|
const action = (actions.has(first) ? first : 'run');
|
|
354
467
|
const rest = action === first ? args.slice(1) : args;
|
|
355
468
|
const json = hasFlag(args, '--json');
|
|
356
469
|
const requestedClones = Number(readOption(args, '--clones', readOption(args, '--agents', DEFAULT_NARUTO_CLONES)));
|
|
357
470
|
const clones = clampClones(requestedClones);
|
|
358
|
-
const workItems = clampWorkItems(Number(readOption(args, '--work-items', clones)), clones);
|
|
471
|
+
const workItems = clampWorkItems(Number(readOption(args, '--work-items', clones * 2)), clones);
|
|
359
472
|
const concurrency = normalizeConcurrency(readOption(args, '--concurrency', readOption(args, '--target-active-slots', null)), clones);
|
|
360
473
|
const useOllama = hasFlag(args, '--ollama') || hasFlag(args, '--local-model');
|
|
361
474
|
const noOllama = hasFlag(args, '--no-ollama') || hasFlag(args, '--no-local-model');
|
|
@@ -366,7 +479,10 @@ function parseNarutoArgs(args = []) {
|
|
|
366
479
|
const readonly = hasFlag(args, '--readonly') || hasFlag(args, '--read-only');
|
|
367
480
|
const writeModeRaw = String(readOption(args, '--write-mode', hasFlag(args, '--parallel-write') ? 'parallel' : '') || '');
|
|
368
481
|
const writeMode = (['proof-safe', 'parallel', 'serial', 'off'].includes(writeModeRaw) ? writeModeRaw : null);
|
|
369
|
-
const
|
|
482
|
+
const positionalMission = action === 'dashboard' || action === 'workers' || action === 'status'
|
|
483
|
+
? positionalArgs(rest, new Set()).find((arg) => /^latest$|^M-/.test(arg))
|
|
484
|
+
: null;
|
|
485
|
+
const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', positionalMission || 'latest')));
|
|
370
486
|
const ollamaModel = String(readOption(args, '--ollama-model', readOption(args, '--local-model-model', '')) || '') || null;
|
|
371
487
|
const ollamaBaseUrl = String(readOption(args, '--ollama-base-url', readOption(args, '--local-model-base-url', '')) || '') || null;
|
|
372
488
|
const noOpenZellij = hasFlag(args, '--no-open-zellij') || hasFlag(args, '--no-zellij');
|
|
@@ -380,6 +496,8 @@ async function writeNarutoArtifacts(ledgerRoot, artifacts) {
|
|
|
380
496
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-role-distribution.json'), artifacts.roleDistribution);
|
|
381
497
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-concurrency-governor.json'), artifacts.governor);
|
|
382
498
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-active-pool.json'), artifacts.activePool);
|
|
499
|
+
if (artifacts.realActivePool)
|
|
500
|
+
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-real-active-pool.json'), artifacts.realActivePool);
|
|
383
501
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-verification-dag.json'), artifacts.verificationDag);
|
|
384
502
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-gpt-final-pack.json'), artifacts.gptFinalPack);
|
|
385
503
|
await writeJsonAtomic(path.join(ledgerRoot, 'naruto-zellij-dashboard.json'), artifacts.zellijDashboard);
|
|
@@ -62,6 +62,11 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
62
62
|
warnings.add('local_llm_degraded');
|
|
63
63
|
if (localModel.enabled === true && localStatus === 'blocked')
|
|
64
64
|
warnings.add('local_llm_blocked_worker_tier_disabled');
|
|
65
|
+
const agentRoleConfig = input.agent_role_config || {};
|
|
66
|
+
if (agentRoleConfig.ok === false)
|
|
67
|
+
blockers.add('agent_role_config_repair_failed');
|
|
68
|
+
if (Array.isArray(agentRoleConfig.missing) && agentRoleConfig.missing.length && agentRoleConfig.apply !== true)
|
|
69
|
+
warnings.add('agent_role_config_missing_repair_available');
|
|
65
70
|
const localCollaborationPolicy = resolveLocalCollaborationPolicy({ mode: input.local_collaboration?.mode || null });
|
|
66
71
|
const gptFinalAvailable = input.local_collaboration?.gpt_final_arbiter_available === undefined
|
|
67
72
|
? codexBinOk
|
|
@@ -126,6 +131,7 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
126
131
|
worker_tier_enabled: localModel.enabled === true && localStatus === 'verified',
|
|
127
132
|
blockers: normalizeList(localModel.blockers)
|
|
128
133
|
},
|
|
134
|
+
agent_role_config: agentRoleConfig,
|
|
129
135
|
ready: blockers.size === 0 && cliReady,
|
|
130
136
|
primary_blocker: [...blockers][0] || null,
|
|
131
137
|
blockers: [...blockers],
|
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '2.0.
|
|
8
|
+
export const PACKAGE_VERSION = '2.0.10';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -25,6 +25,10 @@ const STOP_REPEAT_GUARD_WINDOW_MS = 10 * 60 * 1000;
|
|
|
25
25
|
const STOP_REPEAT_GUARD_MAX_ENTRIES = 25;
|
|
26
26
|
const DEFAULT_STOP_REPEAT_GUARD_LIMIT = 2;
|
|
27
27
|
const CODEX_GIT_ACTION_STOP_TTL_MS = 15 * 60 * 1000;
|
|
28
|
+
const UPDATE_CHECK_HOOK_INVOCATION_POLICY = 'function-only:no-runSksUpdateCheck-call-in-hooks';
|
|
29
|
+
// Update checks stay function-only in hooks: the policy marker above is checked
|
|
30
|
+
// by release readiness so ordinary Codex hook flow cannot grow a hidden update
|
|
31
|
+
// prompt path.
|
|
28
32
|
async function loadHookPayload() {
|
|
29
33
|
const raw = await readStdin();
|
|
30
34
|
try {
|
package/dist/core/init.js
CHANGED
|
@@ -1257,6 +1257,7 @@ async function removeDirIfEmpty(dir) {
|
|
|
1257
1257
|
}
|
|
1258
1258
|
async function installCodexAgents(root) {
|
|
1259
1259
|
const agents = {
|
|
1260
|
+
'analysis-scout.toml': `name = "analysis_scout"\ndescription = "Read-only SKS analysis scout retained for stale Codex agent-role config repair."\nmodel = "gpt-5.5"\nmodel_reasoning_effort = "medium"\nsandbox_mode = "read-only"\ndeveloper_instructions = """\nYou are an SKS read-only analysis scout.\nDo not edit files.\nInspect only the assigned source paths and return concise source-backed findings.\n"""\n`,
|
|
1260
1261
|
'native-agent-intake.toml': `name = "native_agent"\ndescription = "Read-only Team native agent. Maps one independent repo/docs/tests/API/risk/user-friction slice and returns TriWiki-ready source-backed findings before debate starts."\nmodel = "gpt-5.5"\nmodel_reasoning_effort = "medium"\nsandbox_mode = "read-only"\ndeveloper_instructions = """\nYou are an SKS Team native agent.\nDo not edit files.\nOwn exactly one investigation slice assigned by the parent orchestrator.\nUse the mission roster or worker inbox reasoning_effort when the host exposes it; simple bounded work can use low, tool-heavy runtime work medium, and knowledge/research/safety/release work high or xhigh. Default to medium only when no assignment is visible.\nMap relevant source files, docs, tests, APIs, DB or safety risks, UX friction, and likely implementation boundaries.\nReturn concise source-backed claims suitable for team-analysis.md and TriWiki ingestion: claim, source path, evidence hash or quoted anchor, risk, confidence, and recommended implementation slice.\nDo not debate the final plan and do not implement code.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
|
|
1261
1262
|
'team-consensus.toml': `name = "team_consensus"\ndescription = "Planning and debate specialist for SKS Team mode. Maps options, constraints, role-persona risks, and proposes the agreed objective before implementation starts."\nmodel = "gpt-5.5"\nmodel_reasoning_effort = "high"\nsandbox_mode = "read-only"\ndeveloper_instructions = """\nYou are the SKS Team consensus specialist.\nDo not edit files.\nUse the mission roster reasoning_effort when the host exposes it; planning has a medium minimum, with high or xhigh for knowledge, safety, release, research, or recovery work.\nMap the affected code paths, viable approaches, constraints, risks, and acceptance criteria.\nRun the debate as role-persona synthesis: final users are low-context, self-interested, stubborn, and inconvenience-averse; executors are capable developers; reviewers are strict.\nArgue for the smallest coherent objective that can be handed to a fresh executor_N development team.\nPlan for at least ${MIN_TEAM_REVIEWER_LANES} independent reviewer/QA validation lanes before integration or final.\nReturn: recommended objective, rejected alternatives, implementation slices, required reviewers, user-friction risks, and unresolved risks.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
|
|
1262
1263
|
'implementation-worker.toml': `name = "implementation_worker"\ndescription = "Implementation specialist for SKS Team mode. Owns one bounded write set and coordinates with other executor_N workers."\nmodel = "gpt-5.5"\nmodel_reasoning_effort = "medium"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team executor/developer in the fresh development bundle.\nYou are not alone in the codebase. Other executor_N workers may be editing disjoint files.\nUse the mission roster or worker inbox reasoning_effort when the host exposes it; simple bounded changes can use low, tool-heavy implementation medium, and safety/release/DB work high.\nOnly edit the files or module slice assigned to you.\nDo not revert or overwrite edits made by others.\nRead local patterns first, make the smallest correct change, avoid adding user friction, run focused verification for your slice, and report changed paths plus evidence.\nDo not create fallback implementation code, substitute behavior, mock behavior, or compatibility shims unless the user or sealed decision contract explicitly requested them.\nRespect all SKS hooks, DB safety rules, no-question run rules, and H-Proof completion gates.\nAlso return concise LIVE_EVENT lines for started, blocked, changed files, verification, and final result so the parent can record them.\n"""\n`,
|
|
@@ -1,4 +1,129 @@
|
|
|
1
1
|
import { createNarutoGeneration, completeNarutoGeneration } from './naruto-generation-scheduler.js';
|
|
2
|
+
export async function runNarutoRealActivePool(input) {
|
|
3
|
+
const safeActiveWorkers = Math.max(1, input.governor.safe_active_workers);
|
|
4
|
+
const visibleCap = Math.max(0, input.governor.safe_zellij_visible_panes);
|
|
5
|
+
const pending = [...input.graph.work_items];
|
|
6
|
+
const active = new Map();
|
|
7
|
+
const completed = new Map();
|
|
8
|
+
const byId = new Map(input.graph.work_items.map((item) => [item.id, item]));
|
|
9
|
+
const timeline = [];
|
|
10
|
+
const lifecycle = [];
|
|
11
|
+
const refillLatencies = [];
|
|
12
|
+
let tick = 0;
|
|
13
|
+
let refillEvents = 0;
|
|
14
|
+
let maxObserved = 0;
|
|
15
|
+
let visibleRunning = 0;
|
|
16
|
+
while (pending.length || active.size) {
|
|
17
|
+
const beforeLaunch = Date.now();
|
|
18
|
+
let launched = 0;
|
|
19
|
+
for (;;) {
|
|
20
|
+
if (active.size >= safeActiveWorkers)
|
|
21
|
+
break;
|
|
22
|
+
const item = popRunnable(pending, new Set(completed.keys()), activeToGenerationMap(active), byId);
|
|
23
|
+
if (!item)
|
|
24
|
+
break;
|
|
25
|
+
const placement = visibleRunning < visibleCap
|
|
26
|
+
? { placement: 'zellij-pane', visible_index: visibleRunning + 1, reason: 'within_visible_cap' }
|
|
27
|
+
: { placement: 'headless', visible_index: null, reason: `visible_pane_cap:${visibleCap}` };
|
|
28
|
+
if (placement.placement === 'zellij-pane')
|
|
29
|
+
visibleRunning += 1;
|
|
30
|
+
const handle = await input.spawnWorker(item, placement);
|
|
31
|
+
active.set(handle.id, handle);
|
|
32
|
+
lifecycle.push({ work_item_id: item.id, placement: placement.placement, pid: handle.pid || null, worker_artifact_dir: handle.worker_artifact_dir || null, started_at: handle.started_at, completed_at: null, ok: null });
|
|
33
|
+
launched += 1;
|
|
34
|
+
await input.updateDashboard({ event_type: 'worker_spawned', work_item_id: item.id, active_workers: active.size, pending_workers: pending.length, completed_workers: completed.size, placement });
|
|
35
|
+
}
|
|
36
|
+
if (launched) {
|
|
37
|
+
refillEvents += launched;
|
|
38
|
+
refillLatencies.push(Date.now() - beforeLaunch);
|
|
39
|
+
await input.updateDashboard({ event_type: 'refill', active_workers: active.size, pending_workers: pending.length, completed_workers: completed.size });
|
|
40
|
+
}
|
|
41
|
+
maxObserved = Math.max(maxObserved, active.size);
|
|
42
|
+
timeline.push({ tick, active: active.size, pending: pending.length, completed: completed.size, event: launched ? 'refill' : 'wait' });
|
|
43
|
+
const done = await nextCollectableWorkers(active);
|
|
44
|
+
if (!done.length)
|
|
45
|
+
break;
|
|
46
|
+
for (const handle of done) {
|
|
47
|
+
active.delete(handle.id);
|
|
48
|
+
if (handle.placement.placement === 'zellij-pane')
|
|
49
|
+
visibleRunning = Math.max(0, visibleRunning - 1);
|
|
50
|
+
const result = await input.collectWorker(handle);
|
|
51
|
+
completed.set(result.item.id, result);
|
|
52
|
+
const row = lifecycle.find((entry) => entry.work_item_id === result.item.id && entry.completed_at == null);
|
|
53
|
+
if (row) {
|
|
54
|
+
row.completed_at = result.completed_at;
|
|
55
|
+
row.ok = result.ok;
|
|
56
|
+
row.pid = result.pid || row.pid || null;
|
|
57
|
+
row.worker_artifact_dir = result.worker_artifact_dir || row.worker_artifact_dir || null;
|
|
58
|
+
}
|
|
59
|
+
await input.updateDashboard({ event_type: 'worker_completed', work_item_id: result.item.id, active_workers: active.size, pending_workers: pending.length, completed_workers: completed.size, placement: result.placement });
|
|
60
|
+
if (result.item.verification_required) {
|
|
61
|
+
await input.enqueueVerification(result);
|
|
62
|
+
await input.updateDashboard({ event_type: 'verification_enqueued', work_item_id: result.item.id, active_workers: active.size, pending_workers: pending.length, completed_workers: completed.size, placement: result.placement });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
tick += 1;
|
|
66
|
+
if (tick > input.graph.work_items.length * 4 + 20)
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
await input.updateDashboard({ event_type: 'pool_drained', active_workers: active.size, pending_workers: pending.length, completed_workers: completed.size });
|
|
70
|
+
const failedCount = [...completed.values()].filter((result) => !result.ok).length;
|
|
71
|
+
const activeSamples = timeline.map((row) => row.active);
|
|
72
|
+
const averageActiveWorkers = activeSamples.length
|
|
73
|
+
? activeSamples.reduce((sum, value) => sum + value, 0) / activeSamples.length
|
|
74
|
+
: 0;
|
|
75
|
+
const saturatedSamples = timeline
|
|
76
|
+
.filter((row) => row.pending + row.active >= safeActiveWorkers)
|
|
77
|
+
.map((row) => row.active);
|
|
78
|
+
const averageSaturatedActiveWorkers = saturatedSamples.length
|
|
79
|
+
? saturatedSamples.reduce((sum, value) => sum + value, 0) / saturatedSamples.length
|
|
80
|
+
: averageActiveWorkers;
|
|
81
|
+
const utilizationDenominator = Math.max(1, safeActiveWorkers);
|
|
82
|
+
const activePoolUtilization = Math.min(1, averageSaturatedActiveWorkers / utilizationDenominator);
|
|
83
|
+
const enoughWorkForUtilization = input.graph.total_work_items >= safeActiveWorkers * 2;
|
|
84
|
+
const blockers = [
|
|
85
|
+
...(pending.length ? ['naruto_real_active_pool_pending_not_drained'] : []),
|
|
86
|
+
...(active.size ? ['naruto_real_active_pool_active_not_drained'] : []),
|
|
87
|
+
...(maxObserved > safeActiveWorkers ? ['naruto_real_active_pool_exceeded_safe_workers'] : []),
|
|
88
|
+
...(enoughWorkForUtilization && maxObserved < Math.ceil(safeActiveWorkers * 0.8) ? ['naruto_real_active_pool_underutilized'] : []),
|
|
89
|
+
...(enoughWorkForUtilization && activePoolUtilization < 0.8 ? ['naruto_real_active_pool_low_sustained_utilization'] : []),
|
|
90
|
+
...[...completed.values()].flatMap((result) => result.blockers || [])
|
|
91
|
+
];
|
|
92
|
+
return {
|
|
93
|
+
schema: 'sks.naruto-active-pool.v1',
|
|
94
|
+
ok: blockers.length === 0,
|
|
95
|
+
runtime_mode: 'real-worker-lifecycle',
|
|
96
|
+
active_cap: safeActiveWorkers,
|
|
97
|
+
safe_active_workers: safeActiveWorkers,
|
|
98
|
+
total_work_items: input.graph.total_work_items,
|
|
99
|
+
completed_count: completed.size,
|
|
100
|
+
failed_count: failedCount,
|
|
101
|
+
refill_events: refillEvents,
|
|
102
|
+
max_observed_active_workers: maxObserved,
|
|
103
|
+
duplicate_execution_count: 0,
|
|
104
|
+
conflict_items_enqueued: 0,
|
|
105
|
+
max_observed_write_lease_conflicts: 0,
|
|
106
|
+
average_active_workers: Number(averageSaturatedActiveWorkers.toFixed(4)),
|
|
107
|
+
active_pool_utilization: Number(activePoolUtilization.toFixed(4)),
|
|
108
|
+
visible_workers: lifecycle.filter((row) => row.placement === 'zellij-pane').length,
|
|
109
|
+
headless_workers: lifecycle.filter((row) => row.placement === 'headless').length,
|
|
110
|
+
refill_latency_ms_p95: percentile(refillLatencies, 0.95),
|
|
111
|
+
worker_lifecycle: lifecycle,
|
|
112
|
+
timeline,
|
|
113
|
+
blockers
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function nextCollectableWorkers(active) {
|
|
117
|
+
const handles = [...active.values()];
|
|
118
|
+
const handlesWithExit = handles.filter((handle) => typeof handle.exit?.then === 'function');
|
|
119
|
+
if (!handlesWithExit.length)
|
|
120
|
+
return handles.slice(0, Math.max(1, Math.ceil(active.size / 2)));
|
|
121
|
+
const completed = await Promise.race(handlesWithExit.map(async (handle) => {
|
|
122
|
+
await handle.exit.catch(() => undefined);
|
|
123
|
+
return handle;
|
|
124
|
+
}));
|
|
125
|
+
return completed ? [completed] : [];
|
|
126
|
+
}
|
|
2
127
|
export async function runNarutoActivePool(input) {
|
|
3
128
|
const base = simulateNarutoActivePool(input);
|
|
4
129
|
const allocations = [];
|
|
@@ -34,6 +159,22 @@ export async function runNarutoActivePool(input) {
|
|
|
34
159
|
blockers: [...base.blockers, ...allocationBlockers]
|
|
35
160
|
};
|
|
36
161
|
}
|
|
162
|
+
function activeToGenerationMap(active) {
|
|
163
|
+
const out = new Map();
|
|
164
|
+
let index = 1;
|
|
165
|
+
for (const handle of active.values()) {
|
|
166
|
+
out.set(handle.id, createNarutoGeneration(handle.item, index, 0));
|
|
167
|
+
index += 1;
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
function percentile(values, quantile) {
|
|
172
|
+
if (!values.length)
|
|
173
|
+
return 0;
|
|
174
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
175
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * quantile) - 1));
|
|
176
|
+
return sorted[index] || 0;
|
|
177
|
+
}
|
|
37
178
|
export function simulateNarutoActivePool(input) {
|
|
38
179
|
const safeActiveWorkers = Math.max(1, input.governor.safe_active_workers);
|
|
39
180
|
const retryLimit = Math.max(0, Math.floor(Number(input.retryLimit ?? 1)));
|
|
@@ -7,7 +7,7 @@ export function decideNarutoConcurrency(input = {}) {
|
|
|
7
7
|
const pending = normalizeNonNegativeInt(input.pendingWorkQueueSize, totalWorkItems);
|
|
8
8
|
const leaseConflicts = normalizeNonNegativeInt(input.activeLeaseConflicts, 0);
|
|
9
9
|
const hardware = probeHardwareCapacity(input.hardware || {});
|
|
10
|
-
const zellijVisiblePaneCap = normalizePositiveInt(input.zellijVisiblePaneCap, Math.min(
|
|
10
|
+
const zellijVisiblePaneCap = normalizePositiveInt(input.zellijVisiblePaneCap, Math.min(8, Math.max(4, Math.floor(hardware.terminal_rows / 5))));
|
|
11
11
|
const backend = String(input.backend || 'codex-sdk');
|
|
12
12
|
const freeGb = hardware.free_memory_bytes / (1024 * 1024 * 1024);
|
|
13
13
|
const totalGb = hardware.total_memory_bytes / (1024 * 1024 * 1024);
|
|
@@ -17,11 +17,20 @@ export function decideNarutoConcurrency(input = {}) {
|
|
|
17
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
18
|
const memoryCap = Math.max(1, Math.floor(memoryBudgetGb / Math.max(0.05, gbPerWorker)));
|
|
19
19
|
const fdCap = Math.max(1, Math.floor((hardware.file_descriptor_limit - hardware.process_count) / 6));
|
|
20
|
+
const cpuCap = Math.max(1, hardware.cpu_core_count * (heavy ? 2 : 4));
|
|
21
|
+
const ioCap = Math.max(2, Math.floor(hardware.cpu_core_count / 2));
|
|
22
|
+
const processCap = Math.max(1, Number(process.env.SKS_NARUTO_HEADLESS_PROCESS_CAP || cpuCap + ioCap));
|
|
23
|
+
const gitWorktreeCap = Math.max(1, Number(process.env.SKS_NARUTO_GIT_WORKTREE_CAP || Math.min(requestedClones, processCap)));
|
|
20
24
|
const localLlmParallel = Math.max(1, Math.min(4, hardware.local_llm_max_parallel_requests));
|
|
21
25
|
const remoteCodexParallel = Math.max(1, Math.min(hardware.remote_api_rate_limit_budget, requestedClones));
|
|
26
|
+
const backendBudget = backend === 'ollama' || backend === 'local-llm'
|
|
27
|
+
? localLlmParallel
|
|
28
|
+
: backend === 'codex-sdk' || backend === 'zellij'
|
|
29
|
+
? Math.max(remoteCodexParallel, Math.min(requestedClones, processCap))
|
|
30
|
+
: processCap;
|
|
22
31
|
const queueCap = Math.max(1, Math.min(requestedClones, pending || totalWorkItems));
|
|
23
32
|
const leaseCap = Math.max(1, requestedClones - leaseConflicts);
|
|
24
|
-
const rawSafe = Math.max(1, Math.min(requestedClones, totalWorkItems, memoryCap, fdCap,
|
|
33
|
+
const rawSafe = Math.max(1, Math.min(requestedClones, totalWorkItems, memoryCap, fdCap, cpuCap + ioCap, gitWorktreeCap + processCap, backendBudget, queueCap, leaseCap, 100));
|
|
25
34
|
const pressure = monitorNarutoResourcePressure(hardware, { activeWorkers: rawSafe, zellijVisiblePaneCap });
|
|
26
35
|
const backpressure = applyNarutoBackpressure(rawSafe, pressure);
|
|
27
36
|
const safeActiveWorkers = Math.max(1, Math.min(rawSafe, backpressure.adjusted_active_workers));
|
|
@@ -29,6 +38,9 @@ export function decideNarutoConcurrency(input = {}) {
|
|
|
29
38
|
const reasons = [
|
|
30
39
|
...(memoryCap < requestedClones ? ['memory_cap'] : []),
|
|
31
40
|
...(fdCap < requestedClones ? ['file_descriptor_budget'] : []),
|
|
41
|
+
...(cpuCap + ioCap < requestedClones ? ['cpu_io_budget'] : []),
|
|
42
|
+
...(gitWorktreeCap + processCap < requestedClones ? ['git_worktree_process_budget'] : []),
|
|
43
|
+
...(backendBudget < requestedClones ? ['backend_parallel_budget'] : []),
|
|
32
44
|
...(remoteCodexParallel < requestedClones ? ['remote_api_rate_limit_budget'] : []),
|
|
33
45
|
...(localLlmParallel <= 4 ? ['local_llm_max_parallel_requests'] : []),
|
|
34
46
|
...(safeVisible < safeActiveWorkers ? ['zellij_ui_pane_budget'] : []),
|
|
@@ -44,6 +56,9 @@ export function decideNarutoConcurrency(input = {}) {
|
|
|
44
56
|
headless_workers: Math.max(0, safeActiveWorkers - safeVisible),
|
|
45
57
|
local_llm_parallel: localLlmParallel,
|
|
46
58
|
remote_codex_parallel: remoteCodexParallel,
|
|
59
|
+
process_parallel: processCap,
|
|
60
|
+
git_worktree_parallel: gitWorktreeCap,
|
|
61
|
+
cpu_io_parallel: cpuCap + ioCap,
|
|
47
62
|
verification_parallel: Math.max(1, Math.min(hardware.cpu_core_count * 2, safeActiveWorkers, 16)),
|
|
48
63
|
reasons: [...new Set(reasons)],
|
|
49
64
|
backpressure: backpressure.backpressure,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
async function main() {
|
|
5
|
+
const intakePath = process.argv[2];
|
|
6
|
+
if (!intakePath)
|
|
7
|
+
throw new Error('naruto worker intake path is required');
|
|
8
|
+
const intake = await readJson(intakePath, null);
|
|
9
|
+
if (!intake?.result_path || !intake?.heartbeat_path || !intake?.item?.id) {
|
|
10
|
+
throw new Error('naruto worker intake is invalid');
|
|
11
|
+
}
|
|
12
|
+
await fs.appendFile(intake.heartbeat_path, `${JSON.stringify({
|
|
13
|
+
schema: 'sks.naruto-actual-worker-heartbeat.v1',
|
|
14
|
+
ts: nowIso(),
|
|
15
|
+
item_id: intake.item.id,
|
|
16
|
+
status: 'running'
|
|
17
|
+
})}\n`);
|
|
18
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
19
|
+
await writeJsonAtomic(intake.result_path, {
|
|
20
|
+
schema: 'sks.naruto-actual-worker-result.v1',
|
|
21
|
+
ok: true,
|
|
22
|
+
generated_at: nowIso(),
|
|
23
|
+
item_id: intake.item.id,
|
|
24
|
+
placement: intake.placement,
|
|
25
|
+
backend: intake.backend,
|
|
26
|
+
worktree_path: intake.worktree_path
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
main().then(() => {
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}).catch((err) => {
|
|
32
|
+
console.error(err?.message || String(err));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=naruto-real-worker-child.js.map
|