sneakoscope 6.0.3 → 6.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/README.md +29 -12
  2. package/config/codex-releases/{rust-v0.142.0.json → rust-v0.144.1.json} +6 -6
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/bin/fast-inline.js +18 -14
  6. package/dist/bin/sks-dispatch.js +2 -4
  7. package/dist/cli/args.js +2 -0
  8. package/dist/cli/command-manifest-lite.js +2 -3
  9. package/dist/cli/command-registry.js +3 -4
  10. package/dist/cli/help-fast.js +1 -1
  11. package/dist/cli/install-helpers.js +348 -763
  12. package/dist/cli/install-tool-helpers.js +287 -0
  13. package/dist/cli/router.js +91 -6
  14. package/dist/commands/codex-lb.js +188 -53
  15. package/dist/commands/codex.js +44 -5
  16. package/dist/commands/doctor.js +302 -46
  17. package/dist/commands/tmux.js +5 -1
  18. package/dist/commands/versioning.js +7 -0
  19. package/dist/commands/zellij-monitor-pane.js +2 -0
  20. package/dist/commands/zellij-viewport-pane.js +3 -1
  21. package/dist/commands/zellij.js +1 -1
  22. package/dist/config/skills-manifest.json +59 -59
  23. package/dist/core/agent-bridge/agent-manifest.js +48 -0
  24. package/dist/core/agents/agent-central-ledger.js +9 -0
  25. package/dist/core/agents/agent-cleanup-executor.js +334 -41
  26. package/dist/core/agents/agent-cleanup.js +20 -1
  27. package/dist/core/agents/agent-command-surface.js +3 -1
  28. package/dist/core/agents/agent-effort-policy.js +30 -38
  29. package/dist/core/agents/agent-janitor.js +9 -10
  30. package/dist/core/agents/agent-namespace-safety.js +56 -0
  31. package/dist/core/agents/agent-orchestrator.js +25 -3
  32. package/dist/core/agents/agent-output-validator.js +1 -1
  33. package/dist/core/agents/agent-plan.js +100 -21
  34. package/dist/core/agents/agent-role-config.js +43 -49
  35. package/dist/core/agents/agent-runner-codex-exec.js +18 -3
  36. package/dist/core/agents/agent-runner-process.js +1 -0
  37. package/dist/core/agents/agent-schema.js +3 -2
  38. package/dist/core/agents/fast-mode-policy.js +3 -5
  39. package/dist/core/agents/native-cli-session-swarm.js +1 -1
  40. package/dist/core/agents/native-worker-backend-router.js +2 -1
  41. package/dist/core/agents/work-partition/repo-inventory.js +68 -7
  42. package/dist/core/agents/zellij-right-lane-cockpit.js +2 -2
  43. package/dist/core/code-structure.js +5 -4
  44. package/dist/core/codex/agent-config-file-repair.js +94 -193
  45. package/dist/core/codex/codex-cli-update.js +723 -0
  46. package/dist/core/codex/codex-config-guard.js +38 -11
  47. package/dist/core/codex/codex-project-config-policy.js +3 -6
  48. package/dist/core/codex/codex-startup-config-postcheck.js +52 -42
  49. package/dist/core/codex-adapter.js +35 -22
  50. package/dist/core/codex-app/codex-agent-role-sync.js +13 -54
  51. package/dist/core/codex-app/codex-app-fast-ui-repair.js +51 -20
  52. package/dist/core/codex-app/codex-app-launcher.js +48 -17
  53. package/dist/core/codex-app/codex-app-restart.js +23 -2
  54. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +11 -3
  55. package/dist/core/codex-app/sks-menubar.js +136 -5
  56. package/dist/core/codex-app.js +35 -14
  57. package/dist/core/codex-compat/codex-hook-warning-detector.js +10 -1
  58. package/dist/core/codex-compat/codex-release-manifest.js +6 -6
  59. package/dist/core/codex-control/codex-0139-doctor-real-probe.js +7 -1
  60. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +29 -12
  61. package/dist/core/codex-control/codex-0139-probe-runner.js +103 -28
  62. package/dist/core/codex-control/codex-0139-real-probe-summary.js +7 -3
  63. package/dist/core/codex-control/codex-0139-real-probes.js +21 -6
  64. package/dist/core/codex-control/codex-0139-sandbox-real-probe.js +39 -13
  65. package/dist/core/codex-control/codex-0139-web-search-probe.js +28 -14
  66. package/dist/core/codex-control/{codex-0142-capability.js → codex-0144-capability.js} +22 -22
  67. package/dist/core/codex-control/codex-0144-collab-agent-real-probe.js +63 -0
  68. package/dist/core/codex-control/codex-lb-launch-recovery.js +278 -0
  69. package/dist/core/codex-control/codex-multi-agent-event-normalizer.js +5 -1
  70. package/dist/core/codex-control/codex-reliability-shield.js +93 -30
  71. package/dist/core/codex-control/codex-sdk-adapter.js +96 -13
  72. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  73. package/dist/core/codex-control/codex-sdk-sandbox-policy.js +6 -3
  74. package/dist/core/codex-control/codex-task-runner.js +62 -9
  75. package/dist/core/codex-control/python-codex-sdk-adapter.js +30 -3
  76. package/dist/core/codex-exec-output-schema.js +35 -6
  77. package/dist/core/codex-lb/codex-lb-env.js +30 -1
  78. package/dist/core/codex-lb/codex-lb-tool-catalog.js +478 -0
  79. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +291 -0
  80. package/dist/core/codex-native/codex-native-feature-broker.js +31 -38
  81. package/dist/core/codex-native/codex-native-feature-matrix.js +2 -2
  82. package/dist/core/codex-native/codex-native-repair-transaction.js +1 -1
  83. package/dist/core/codex-native/core-skill-manifest.js +8 -4
  84. package/dist/core/codex-native/native-capability-postcheck.js +16 -8
  85. package/dist/core/codex-native/native-capability-repair-matrix.js +76 -27
  86. package/dist/core/codex-plugins/codex-plugin-json.js +67 -21
  87. package/dist/core/codex-plugins/codex-plugin-repair.js +118 -0
  88. package/dist/core/codex-runtime/codex-desktop-config-policy.js +305 -0
  89. package/dist/core/commands/agent-bridge-command.js +23 -2
  90. package/dist/core/commands/agent-command.js +38 -5
  91. package/dist/core/commands/basic-cli.js +77 -30
  92. package/dist/core/commands/command-utils.js +1 -1
  93. package/dist/core/commands/gc-command.js +27 -4
  94. package/dist/core/commands/image-ux-review-command.js +55 -24
  95. package/dist/core/commands/mad-sks-command.js +182 -137
  96. package/dist/core/commands/naruto-command.js +643 -1153
  97. package/dist/core/commands/pipeline-command.js +1 -0
  98. package/dist/core/commands/research-command.js +293 -231
  99. package/dist/core/commands/run-command.js +100 -23
  100. package/dist/core/commands/team-command.js +2 -3
  101. package/dist/core/commands/team-legacy-observe-command.js +94 -359
  102. package/dist/core/commands/wiki-command.js +179 -83
  103. package/dist/core/computer-use-live-evidence.js +166 -3
  104. package/dist/core/computer-use-status.js +5 -2
  105. package/dist/core/daemon/sksd-hook-daemon-entrypoint.js +2 -2
  106. package/dist/core/daemon/sksd-hook-daemon.js +171 -28
  107. package/dist/core/daemon/sksd-hook-dispatch.js +2 -2
  108. package/dist/core/db-safety.js +2 -2
  109. package/dist/core/decision-lattice.js +6 -6
  110. package/dist/core/dfix/verification-selector.js +6 -4
  111. package/dist/core/doctor/browser-use-repair.js +54 -13
  112. package/dist/core/doctor/codex-0139-doctor.js +3 -2
  113. package/dist/core/doctor/codex-startup-config-repair.js +8 -3
  114. package/dist/core/doctor/computer-use-repair.js +28 -39
  115. package/dist/core/doctor/doctor-codex-startup-repair.js +24 -149
  116. package/dist/core/doctor/doctor-context7-repair.js +1 -1
  117. package/dist/core/doctor/doctor-dirty-planner.js +1 -1
  118. package/dist/core/doctor/doctor-native-capability-repair.js +74 -3
  119. package/dist/core/doctor/imagegen-repair.js +47 -12
  120. package/dist/core/doctor/legacy-global-hook-cleanup.js +179 -0
  121. package/dist/core/feature-fixture-executor.js +71 -7
  122. package/dist/core/feature-fixture-runner.js +53 -12
  123. package/dist/core/feature-fixtures.js +47 -14
  124. package/dist/core/feature-registry.js +53 -58
  125. package/dist/core/fsx.js +54 -6
  126. package/dist/core/harness-conflicts.js +19 -10
  127. package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +5 -4
  128. package/dist/core/hooks-runtime/hook-invocation-dedupe.js +147 -0
  129. package/dist/core/hooks-runtime/hook-io.js +14 -4
  130. package/dist/core/hooks-runtime/light-turn.js +70 -0
  131. package/dist/core/hooks-runtime/payload-signals.js +270 -0
  132. package/dist/core/hooks-runtime/team-digest.js +0 -1
  133. package/dist/core/hooks-runtime/tool-output-quarantine.js +93 -0
  134. package/dist/core/hooks-runtime.js +418 -250
  135. package/dist/core/image/image-artifact-path-contract.js +8 -6
  136. package/dist/core/image/image-artifact-registry.js +5 -1
  137. package/dist/core/image-ux-review/imagegen-adapter.js +5 -2
  138. package/dist/core/imagegen/imagegen-capability.js +10 -13
  139. package/dist/core/imagegen/require-imagegen.js +37 -11
  140. package/dist/core/init/skills.js +16 -30
  141. package/dist/core/init.js +144 -36
  142. package/dist/core/managed-assets/managed-assets-manifest.js +417 -14
  143. package/dist/core/mission.js +24 -1
  144. package/dist/core/naruto/naruto-work-graph.js +70 -4
  145. package/dist/core/ops/reporting.js +3 -0
  146. package/dist/core/pipeline-internals/runtime-core.js +578 -180
  147. package/dist/core/pipeline-internals/runtime-gates.js +191 -49
  148. package/dist/core/ppt/style-tokens.js +1 -1
  149. package/dist/core/ppt-review/index.js +1 -1
  150. package/dist/core/ppt-review/slide-exporter.js +23 -17
  151. package/dist/core/ppt-review/slide-imagegen-review.js +1 -1
  152. package/dist/core/ppt-review/slide-issue-extraction.js +4 -2
  153. package/dist/core/preflight/parallel-preflight-engine.js +50 -1
  154. package/dist/core/product-design-plugin.js +2 -2
  155. package/dist/core/proof/route-finalizer.js +1 -1
  156. package/dist/core/proof/route-proof-gate.js +7 -2
  157. package/dist/core/proof-field.js +3 -3
  158. package/dist/core/provider/model-router.js +38 -32
  159. package/dist/core/recallpulse/policy.js +12 -28
  160. package/dist/core/recallpulse.js +11 -6
  161. package/dist/core/release/gate-manifest.js +18 -12
  162. package/dist/core/release/gate-pack-manifest.js +1 -1
  163. package/dist/core/release/npm-pack-proof.js +247 -0
  164. package/dist/core/release/package-dist-snapshot.js +151 -0
  165. package/dist/core/release/package-size-budget.js +5 -0
  166. package/dist/core/release/release-authorization-snapshot.js +115 -0
  167. package/dist/core/release/release-gate-affected-selector.js +15 -3
  168. package/dist/core/release/release-gate-cache-v2.js +30 -0
  169. package/dist/core/release/release-gate-contract.js +161 -0
  170. package/dist/core/release/release-gate-dag.js +44 -3
  171. package/dist/core/release/release-gate-hermetic-env.js +28 -17
  172. package/dist/core/release/release-real-contract.js +496 -0
  173. package/dist/core/release-parallel-full-coverage.js +31 -143
  174. package/dist/core/research/claim-evidence-matrix.js +41 -6
  175. package/dist/core/research/experiment-plan.js +14 -10
  176. package/dist/core/research/falsification.js +9 -2
  177. package/dist/core/research/implementation-blueprint-densifier.js +82 -60
  178. package/dist/core/research/implementation-blueprint.js +32 -26
  179. package/dist/core/research/mock-result.js +122 -11
  180. package/dist/core/research/replication-pack.js +15 -8
  181. package/dist/core/research/research-adversarial-review.js +1068 -0
  182. package/dist/core/research/research-claim-builder.js +69 -17
  183. package/dist/core/research/research-claim-synthesizer.js +343 -0
  184. package/dist/core/research/research-cycle-runner.js +53 -3
  185. package/dist/core/research/research-falsification-runner.js +176 -0
  186. package/dist/core/research/research-final-reviewer.js +44 -125
  187. package/dist/core/research/research-realistic-report.js +14 -6
  188. package/dist/core/research/research-review-artifact-digest.js +66 -0
  189. package/dist/core/research/research-source-evidence.js +144 -0
  190. package/dist/core/research/research-source-layer-catalog.js +68 -0
  191. package/dist/core/research/research-source-ledger-merge.js +250 -12
  192. package/dist/core/research/research-source-shards.js +26 -70
  193. package/dist/core/research/research-stage-runner.js +237 -247
  194. package/dist/core/research/research-super-search.js +184 -0
  195. package/dist/core/research/research-synthesis-prompt.js +20 -1
  196. package/dist/core/research/research-synthesis-writer.js +86 -5
  197. package/dist/core/research/research-work-graph.js +25 -18
  198. package/dist/core/research/source-quality-report.js +21 -0
  199. package/dist/core/research.js +429 -280
  200. package/dist/core/retention/retention-budget.js +25 -6
  201. package/dist/core/retention.js +980 -101
  202. package/dist/core/routes/constants.js +1 -1
  203. package/dist/core/routes/dollar-manifest-lite.js +11 -11
  204. package/dist/core/routes/evidence.js +4 -4
  205. package/dist/core/routes/ppt-policy.js +1 -1
  206. package/dist/core/routes.js +92 -64
  207. package/dist/core/runtime/task-profile.js +66 -0
  208. package/dist/core/runtime/verification-budget.js +12 -0
  209. package/dist/core/secret-redaction.js +4 -0
  210. package/dist/core/security/high-risk-contracts.js +6 -6
  211. package/dist/core/stop-gate/stop-gate-check.js +49 -12
  212. package/dist/core/subagents/agent-catalog.js +215 -0
  213. package/dist/core/subagents/model-policy.js +118 -0
  214. package/dist/core/subagents/naruto-help-contract.js +40 -0
  215. package/dist/core/subagents/official-subagent-config.js +488 -0
  216. package/dist/core/subagents/official-subagent-preparation.js +251 -0
  217. package/dist/core/subagents/official-subagent-prompt.js +137 -0
  218. package/dist/core/subagents/official-subagent-runner.js +129 -0
  219. package/dist/core/subagents/subagent-evidence.js +663 -0
  220. package/dist/core/subagents/terminology.js +16 -0
  221. package/dist/core/subagents/thread-budget.js +21 -0
  222. package/dist/core/subagents/triwiki-attention.js +65 -0
  223. package/dist/core/super-search/runtime-helpers.js +173 -19
  224. package/dist/core/super-search/runtime.js +140 -15
  225. package/dist/core/super-search/source-records.js +49 -8
  226. package/dist/core/team-review-policy.js +15 -0
  227. package/dist/core/triwiki/code-pack.js +8 -2
  228. package/dist/core/triwiki/triwiki-cache-key.js +108 -29
  229. package/dist/core/triwiki/triwiki-module-card.js +1 -1
  230. package/dist/core/triwiki-attention.js +13 -6
  231. package/dist/core/triwiki-provenance.js +274 -0
  232. package/dist/core/triwiki-runtime.js +21 -7
  233. package/dist/core/triwiki-wrongness/wrongness-ledger.js +187 -61
  234. package/dist/core/triwiki-wrongness/wrongness-retrieval.js +11 -2
  235. package/dist/core/triwiki-wrongness/wrongness-trust-policy.js +2 -0
  236. package/dist/core/update-check.js +23 -15
  237. package/dist/core/version-manager.js +51 -1
  238. package/dist/core/version.js +1 -1
  239. package/dist/core/wiki-coordinate.js +128 -12
  240. package/dist/core/work-order-ledger.js +208 -10
  241. package/dist/core/zellij/zellij-command.js +12 -1
  242. package/dist/core/zellij/zellij-dashboard-pane.js +3 -3
  243. package/dist/core/zellij/zellij-fake-adapter.js +3 -1
  244. package/dist/core/zellij/zellij-launcher.js +51 -21
  245. package/dist/core/zellij/zellij-layout-builder.js +14 -5
  246. package/dist/core/zellij/zellij-official-subagent-activity.js +477 -0
  247. package/dist/core/zellij/zellij-official-subagent-telemetry.js +264 -0
  248. package/dist/core/zellij/zellij-pane-proof.js +32 -6
  249. package/dist/core/zellij/zellij-slot-pane-renderer.js +17 -5
  250. package/dist/core/zellij/zellij-slot-telemetry.js +15 -3
  251. package/dist/core/zellij/zellij-ui-mode.js +1 -1
  252. package/dist/core/zellij/zellij-viewport-binder.js +3 -0
  253. package/dist/core/zellij/zellij-worker-pane-manager.js +5 -3
  254. package/dist/scripts/agent-fast-mode-default-check.js +48 -29
  255. package/dist/scripts/agent-role-config-repair-check.js +34 -15
  256. package/dist/scripts/agent-wiki-context-proof-check.js +16 -2
  257. package/dist/scripts/all-feature-deep-completion-check.js +14 -4
  258. package/dist/scripts/architecture-guard-check.js +8 -1
  259. package/dist/scripts/build-dist.js +17 -5
  260. package/dist/scripts/canonical-test-runner.js +188 -0
  261. package/dist/scripts/check-command-module-budget.js +38 -16
  262. package/dist/scripts/check-legacy-free.js +1 -1
  263. package/dist/scripts/check-route-modularity.js +0 -1
  264. package/dist/scripts/check-ts-contracts.js +1 -1
  265. package/dist/scripts/codex-0139-interrupt-agent-check.js +5 -1
  266. package/dist/scripts/{codex-0142-app-server-v2-check.js → codex-0144-app-server-v2-check.js} +11 -10
  267. package/dist/scripts/{codex-0142-binary-identity-check.js → codex-0144-binary-identity-check.js} +4 -4
  268. package/dist/scripts/codex-0144-capability-check.js +21 -0
  269. package/dist/scripts/{codex-0139-real-probes-check.js → codex-0144-core-real-probes-check.js} +19 -5
  270. package/dist/scripts/{codex-0142-doctor-wiring-check.js → codex-0144-doctor-wiring-check.js} +6 -6
  271. package/dist/scripts/{codex-0142-manifest-check.js → codex-0144-manifest-check.js} +11 -3
  272. package/dist/scripts/{codex-0142-policy-check.js → codex-0144-policy-check.js} +2 -2
  273. package/dist/scripts/{codex-0142-thread-store-check.js → codex-0144-thread-store-check.js} +3 -3
  274. package/dist/scripts/codex-app-provider-model-ui-check.js +53 -19
  275. package/dist/scripts/codex-app-ui-preservation-check.js +35 -4
  276. package/dist/scripts/codex-control-all-pipelines-check.js +7 -3
  277. package/dist/scripts/codex-control-side-effect-scope-check.js +2 -2
  278. package/dist/scripts/codex-control-tool-output-continuity-audit-check.js +27 -0
  279. package/dist/scripts/codex-effort-auto-discovery-check.js +1 -1
  280. package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -0
  281. package/dist/scripts/{codex-lb-gpt55-fast-profile-check.js → codex-lb-gpt56-fast-profile-check.js} +56 -5
  282. package/dist/scripts/codex-model-metadata-check.js +1 -1
  283. package/dist/scripts/codex-native-agent-role-content-check.js +15 -7
  284. package/dist/scripts/codex-native-repair-transaction-check.js +3 -1
  285. package/dist/scripts/codex-sdk-all-pipelines-check.js +7 -4
  286. package/dist/scripts/codex-sdk-research-pipeline-check.js +30 -8
  287. package/dist/scripts/codex-sdk-sandbox-policy-check.js +5 -1
  288. package/dist/scripts/codex-sdk-version-compat-check.js +2 -1
  289. package/dist/scripts/db-route-materialization-check.js +33 -0
  290. package/dist/scripts/dfix-fixture-check.js +1 -1
  291. package/dist/scripts/docs-truthfulness-check.js +4 -2
  292. package/dist/scripts/doctor-codex-startup-repair-check.js +47 -25
  293. package/dist/scripts/doctor-context7-repair-check.js +2 -1
  294. package/dist/scripts/doctor-fix-production-blackbox.js +12 -2
  295. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +34 -7
  296. package/dist/scripts/doctor-imagegen-repair-check.js +14 -4
  297. package/dist/scripts/doctor-startup-config-repair-blackbox.js +9 -3
  298. package/dist/scripts/doctor-startup-config-repair-check.js +13 -3
  299. package/dist/scripts/install-update-preserves-config-check.js +7 -2
  300. package/dist/scripts/legacy-gate-inventory-check.js +7 -5
  301. package/dist/scripts/legacy-strong-inventory-check.js +7 -5
  302. package/dist/scripts/legacy-upgrade-matrix-check.js +4 -4
  303. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +173 -13
  304. package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +165 -6
  305. package/dist/scripts/managed-role-manifest-parity-check.js +5 -4
  306. package/dist/scripts/mutation-callsite-coverage-check.js +0 -11
  307. package/dist/scripts/naruto-codex-e2e-check.js +14 -7
  308. package/dist/scripts/naruto-shadow-clone-swarm-check.js +4 -201
  309. package/dist/scripts/npm-publish-performance-check.js +20 -12
  310. package/dist/scripts/official-subagent-workflow-check.js +145 -0
  311. package/dist/scripts/package-published-contract-check.js +6 -20
  312. package/dist/scripts/packlist-performance-check.js +17 -3
  313. package/dist/scripts/parallel-verification-engine-check.js +2 -2
  314. package/dist/scripts/postinstall-safe-side-effects-check.js +8 -3
  315. package/dist/scripts/ppt-no-mock-as-real-check.js +3 -1
  316. package/dist/scripts/prepublish-release-check-or-fast.js +23 -86
  317. package/dist/scripts/product-design-plugin-routing-check.js +1 -1
  318. package/dist/scripts/release-affected-selector-check.js +74 -1
  319. package/dist/scripts/release-check-stamp.js +324 -239
  320. package/dist/scripts/release-dag-full-coverage-check.js +7 -15
  321. package/dist/scripts/release-dynamic-presets-check.js +10 -1
  322. package/dist/scripts/release-gate-dag-runner-check.js +3 -3
  323. package/dist/scripts/release-gate-dag-runner.js +35 -11
  324. package/dist/scripts/release-gate-existence-audit.js +1 -2
  325. package/dist/scripts/release-gate-script-parity-check.js +1 -1
  326. package/dist/scripts/release-parallel-full-coverage-check.js +12 -6
  327. package/dist/scripts/release-parallel-speed-budget-check.js +18 -2
  328. package/dist/scripts/release-real-check.js +390 -157
  329. package/dist/scripts/release-registry-check.js +61 -16
  330. package/dist/scripts/release-runtime-truth-matrix-check.js +22 -0
  331. package/dist/scripts/research-blueprint-densifier-check.js +2 -2
  332. package/dist/scripts/research-real-cycle-no-legacy-final-md-check.js +11 -7
  333. package/dist/scripts/route-proof-artifact-structure-check.js +1 -0
  334. package/dist/scripts/search-visibility-gate-lib.js +1 -1
  335. package/dist/scripts/seo-geo-route-identity-check.js +30 -4
  336. package/dist/scripts/sizecheck.js +4 -4
  337. package/dist/scripts/sks-1-11-gate-lib.js +53 -6
  338. package/dist/scripts/sks-3-1-4-directive-check-lib.js +8 -2
  339. package/dist/scripts/sks-3-1-5-directive-check-lib.js +3 -2
  340. package/dist/scripts/sks-3-1-6-directive-check-lib.js +5 -3
  341. package/dist/scripts/sks-menubar-install-check.js +8 -0
  342. package/dist/scripts/sks-uninstall-regression-check.js +1 -1
  343. package/dist/scripts/sksd-daemon-check.js +16 -6
  344. package/dist/scripts/trust-fixture-check.js +32 -10
  345. package/dist/scripts/write-build-manifest.js +2 -1
  346. package/dist/scripts/wrongness-fixture-check.js +1 -1
  347. package/dist/scripts/zellij-layout-valid-check.js +36 -11
  348. package/dist/scripts/zellij-pane-proof-check.js +33 -2
  349. package/dist/scripts/zellij-real-session-cleanup-check.js +122 -2
  350. package/dist/scripts/zellij-real-session-launch-check.js +76 -5
  351. package/dist/scripts/zellij-slot-pane-renderer-check.js +4 -4
  352. package/dist/scripts/zellij-spawn-on-demand-layout-check.js +3 -3
  353. package/docs/demo.tape +1 -1
  354. package/infra-harness-gates.json +1486 -0
  355. package/package.json +32 -19
  356. package/release-gates.v2.json +4060 -0
  357. package/runtime-required-scripts.json +9 -0
  358. package/schemas/codex/{app-server-0.142 → app-server-0.144}/codex_app_server_protocol.v2.schemas.json +1146 -781
  359. package/schemas/codex/codex-0139-real-probe-result.schema.json +39 -5
  360. package/schemas/codex/ppt-slide-issue-ledger.schema.json +2 -1
  361. package/dist/commands/db.js +0 -6
  362. package/dist/core/codex-control/codex-0139-multi-agent-real-probe.js +0 -107
  363. package/dist/core/commands/db-command.js +0 -146
  364. package/dist/core/research/prompt.js +0 -15
  365. package/dist/scripts/agent-native-release-gate.js +0 -274
  366. package/dist/scripts/codex-0142-capability-check.js +0 -21
  367. package/dist/scripts/codex-control-tool-call-sequence-repair-check.js +0 -14
  368. package/dist/scripts/command-performance-scorecard-check.js +0 -204
  369. package/dist/scripts/config-managed-merge-callsite-coverage-check.js +0 -212
  370. package/dist/scripts/dollar-performance-scorecard-check.js +0 -257
  371. package/dist/scripts/gate-timing-check.js +0 -16
  372. package/dist/scripts/ops-maturity-scorecard-check.js +0 -12
  373. package/dist/scripts/performance-baseline-check.js +0 -12
  374. package/dist/scripts/performance-improvement-report-check.js +0 -58
  375. package/dist/scripts/release-parallel-check.js +0 -389
  376. package/dist/scripts/release-runner-efficiency-check.js +0 -15
  377. package/dist/scripts/retention-long-run-smoke-check.js +0 -299
  378. package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
@@ -1,1232 +1,722 @@
1
- import fs from 'node:fs';
2
1
  import path from 'node:path';
3
2
  import { ui as cliUi } from '../../cli/cli-theme.js';
4
- import { createMission, findLatestMission, loadMission, setCurrent } from '../mission.js';
5
- import { createAndWriteWorkOrderLedgerForPrompt, closeWorkOrderLedgerForRouteResult } from '../work-order-ledger.js';
6
- import { nowIso, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
7
- import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
8
- import { classifyOllamaWorkerSlice } from '../agents/agent-runner-ollama.js';
9
- import { buildNarutoCloneRoster, systemSafeNarutoConcurrency } from '../agents/agent-roster.js';
10
- import { DEFAULT_NARUTO_CLONES, MAX_NARUTO_AGENT_COUNT } from '../agents/agent-schema.js';
11
- import { normalizeServiceTier } from '../agents/fast-mode-policy.js';
12
- import { resolveOllamaWorkerConfig } from '../agents/ollama-worker-config.js';
13
- import { attachZellijSessionInteractive, launchZellijLayout } from '../zellij/zellij-launcher.js';
14
- import { maybePromptZellijUpdateForLaunch } from '../zellij/zellij-update.js';
15
- import { buildNarutoWorkGraph } from '../naruto/naruto-work-graph.js';
16
- import { buildNarutoRoleDistribution } from '../naruto/naruto-role-policy.js';
17
- import { decideNarutoConcurrency } from '../naruto/naruto-concurrency-governor.js';
18
- import { runNarutoRealActivePool } from '../naruto/naruto-active-pool.js';
19
- import { collectActualNarutoWorker, killAllActiveNarutoWorkers, spawnActualNarutoWorker } from '../naruto/naruto-real-worker-runtime.js';
20
- import { runZellij } from '../zellij/zellij-command.js';
21
- import { allocateNarutoTasksToWorkers } from '../naruto/naruto-allocation-policy.js';
22
- import { rebalanceNarutoReadyWork } from '../naruto/naruto-rebalance-policy.js';
23
- import { buildNarutoVerificationDag } from '../naruto/naruto-verification-dag.js';
24
- import { runNarutoVerificationPool } from '../naruto/naruto-verification-pool.js';
25
- import { evaluateNarutoFinalizer } from '../naruto/naruto-finalizer.js';
26
- import { buildNarutoGptFinalPack } from '../naruto/naruto-gpt-final-pack.js';
27
- import { planNarutoZellijDashboard } from '../zellij/zellij-naruto-dashboard.js';
28
- import { checkPromptPlaceholders } from '../prompt/prompt-placeholder-guard.js';
29
- import { evaluateGitWorktreeCapability } from '../git/git-worktree-capability.js';
30
- import { buildNarutoRealWriteProof } from '../naruto/naruto-real-write-proof.js';
31
- import { buildRuntimeProofSummary, renderRuntimeProofSummary } from '../agents/runtime-proof-summary.js';
32
- import { writeCodex0138CapabilityArtifacts } from '../codex-control/codex-0138-capability.js';
33
- import { writeCodex0139CapabilityArtifacts } from '../codex-control/codex-0139-capability.js';
34
- import { writeFinalStopGate } from '../stop-gate/stop-gate-writer.js';
35
- import { extractNarutoPromptPaths } from '../naruto/naruto-task-hints.js';
36
- const NARUTO_RESULT_SCHEMA = 'sks.naruto-command-result.v1';
37
- const NARUTO_ROUTE = '$Naruto';
38
- // $Naruto — Shadow Clone Swarm (影分身 / Kage Bunshin no Jutsu).
39
- // A high-scale variant of the native agent orchestrator that fans out up to
40
- // MAX_NARUTO_AGENT_COUNT (100) identical clone sessions in parallel, reusing the
41
- // proven scheduler / work-queue / patch-swarm machinery (lease-based safe parallel
42
- // writes). The standard 20-agent ceiling is lifted only for this route.
3
+ import { createMission, findLatestMission, getOrCreateSessionMission, loadStateForSession, loadMission, sessionStateKey, setCurrent } from '../mission.js';
4
+ import { closeWorkOrderLedgerForRouteResult, createAndWriteWorkOrderLedgerForPrompt } from '../work-order-ledger.js';
5
+ import { appendJsonl, exists, nowIso, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
6
+ import { SUBAGENT_EVENT_LOG_FILENAME, SUBAGENT_PARENT_SUMMARY_FILENAME, bindTrustworthySubagentParentSummaryToRun, persistOrReuseTrustworthySubagentParentSummary, readSubagentEvents, writeSubagentEvidence } from '../subagents/subagent-evidence.js';
7
+ import { buildNarutoHelpResult } from '../subagents/naruto-help-contract.js';
8
+ import { withFileLock } from '../locks/file-lock.js';
9
+ import { resolveSubagentThreadBudget } from '../subagents/thread-budget.js';
10
+ import { codexAppSessionKey, detectCodexAppSession, runOfficialSubagentWorkflow } from '../subagents/official-subagent-runner.js';
11
+ import { NARUTO_GATE_FILENAME, NARUTO_RESULT_SCHEMA, NARUTO_SUMMARY_FILENAME, SUBAGENT_PLAN_FILENAME, buildNarutoGateResult, buildNarutoSummary, prepareOfficialSubagentMission, writeNarutoGate } from '../subagents/official-subagent-preparation.js';
12
+ import { recordOfficialSubagentParentOutcomesTelemetry } from '../zellij/zellij-official-subagent-telemetry.js';
13
+ export { buildNarutoGateResult } from '../subagents/official-subagent-preparation.js';
14
+ const LEGACY_FLAG_WARNING = 'SKS: --clones is deprecated; use --agents. Naruto now uses Codex subagents.';
15
+ const LEGACY_WORKERS_WARNING = 'SKS: naruto workers is deprecated; use naruto subagents.';
16
+ const REMOVED_LEGACY_SUBCOMMANDS = new Set(['dashboard']);
43
17
  export async function narutoCommand(commandOrArgs = 'naruto', maybeArgs = []) {
44
- const args = Array.isArray(commandOrArgs) ? commandOrArgs : maybeArgs;
45
- // Normal Naruto is a GPT-5.6-family-only route. Keep the historical GLM
46
- // implementation reachable only through the explicitly separate
47
- // `sks --mad --glm --naruto` global mode; a command-local --glm flag must
48
- // never bypass the Luna/Terra/Sol catalog and backend guards below.
49
- if (args.includes('--glm')) {
50
- const result = {
51
- schema: 'sks.naruto-command-result.v1',
52
- ok: false,
53
- mode: 'NARUTO',
54
- status: 'blocked',
55
- reason: 'naruto_gpt_5_6_family_only_glm_override_forbidden',
56
- blockers: ['naruto_gpt_5_6_family_only_glm_override_forbidden'],
57
- hint: 'Use normal sks naruto for Luna/Terra/Sol. The separate legacy GLM route requires: sks --mad --glm --naruto.'
58
- };
59
- process.exitCode = 1;
60
- if (args.includes('--json'))
61
- console.log(JSON.stringify(result, null, 2));
62
- else
63
- console.error(`$Naruto blocked: ${result.reason}. ${result.hint}`);
64
- return result;
65
- }
18
+ const args = Array.isArray(commandOrArgs) ? commandOrArgs.map(String) : maybeArgs.map(String);
19
+ if (args.some((arg) => arg === '--glm' || arg.startsWith('--glm=')))
20
+ return blockGlmOverride(args.includes('--json'));
66
21
  const parsed = parseNarutoArgs(args);
22
+ if (parsed.clonesAliasUsed)
23
+ console.warn(LEGACY_FLAG_WARNING);
24
+ if (parsed.workersAliasUsed)
25
+ console.warn(LEGACY_WORKERS_WARNING);
26
+ if (parsed.argumentErrors.length) {
27
+ return emit(parsed, argumentBlock(parsed.argumentErrors), () => {
28
+ console.error(`$Naruto argument error: ${parsed.argumentErrors.join(', ')}`);
29
+ }, true);
30
+ }
31
+ if (parsed.unsupportedLegacyFlags.length) {
32
+ return emit(parsed, legacyFlagBlock(parsed.unsupportedLegacyFlags), () => {
33
+ console.error('$Naruto uses only the Codex official subagent workflow. Legacy process, scheduler, pool, backend, and model flags were removed.');
34
+ }, true);
35
+ }
67
36
  if (!parsed.json)
68
- cliUi.banner(parsed.action === 'run' ? 'swarm' : `naruto ${parsed.action}`);
69
- if (!parsed.json && parsed.action !== 'run')
70
- cliUi.ok('naruto command surface ready');
37
+ cliUi.banner(parsed.action === 'run' ? 'naruto subagents' : `naruto ${parsed.action}`);
71
38
  if (parsed.action === 'help')
72
39
  return narutoHelp(parsed);
73
40
  if (parsed.action === 'status')
74
41
  return narutoStatus(parsed);
75
- if (parsed.action === 'dashboard')
76
- return narutoDashboard(parsed);
77
- if (parsed.action === 'workers')
78
- return narutoWorkers(parsed);
42
+ if (parsed.action === 'subagents')
43
+ return narutoSubagents(parsed);
79
44
  if (parsed.action === 'proof')
80
45
  return narutoProof(parsed);
81
- // Like the Codex CLI update prompt: check the installed zellij version and
82
- // offer an upgrade to the latest stable release before the live session
83
- // opens. Never blocks the run.
84
- if (!parsed.json && !parsed.mock && !parsed.noOpenZellij) {
85
- await maybePromptZellijUpdateForLaunch(args, { label: '$Naruto launch' }).catch(() => undefined);
86
- }
87
46
  return narutoRun(parsed);
88
47
  }
89
48
  async function narutoRun(parsed) {
90
- // 20차 P0-2: a real (non---mock) run must never be silently downgraded to
91
- // the fake Codex SDK adapter by a stray inherited env var (leaked shell
92
- // export, leftover CI job config, etc). Clear the escape hatches for this
93
- // process before any codex task executes; --mock runs and NODE_ENV=test
94
- // are unaffected since they set/rely on these independently downstream.
95
- if (!parsed.mock && process.env.NODE_ENV !== 'test') {
96
- delete process.env.SKS_CODEX_SDK_FAKE;
97
- delete process.env.SKS_CODEX_SDK_FIXTURE;
98
- }
99
49
  const root = await sksRoot();
100
- const writeCapable = parsed.readonly !== true && parsed.writeMode !== 'off';
101
- const patchEnvelopeBasePath = '.sneakoscope/naruto/patch-envelopes';
102
- const explicitPromptPaths = writeCapable ? extractNarutoPromptPaths(parsed.prompt) : [];
103
- // Keep an empty target list when the prompt does not name a concrete file.
104
- // buildNarutoWorkGraph then derives one patch-envelope path per work item;
105
- // passing the base directory as a target would collapse every write lease
106
- // onto the same path and force an avoidable serial merge bottleneck.
107
- const targetPaths = explicitPromptPaths;
108
- const fixtureLeaseTargetsAllowed = parsed.backend === 'fake' || parsed.mock;
109
- const placeholderGuard = checkPromptPlaceholders({
110
- prompt: parsed.prompt,
111
- writeCapable,
112
- targetPaths: writeCapable
113
- ? (targetPaths.length ? targetPaths : fixtureLeaseTargetsAllowed ? [patchEnvelopeBasePath] : [])
114
- : []
115
- });
116
- if (!placeholderGuard.ok) {
117
- process.exitCode = 1;
50
+ const appSession = detectCodexAppSession();
51
+ const sessionKey = appSession ? codexAppSessionKey() : null;
52
+ if (appSession && sessionKey) {
53
+ return withFileLock({
54
+ lockPath: path.join(root, '.sneakoscope', 'state', `naruto-session-${sessionStateKey(sessionKey)}.lock`),
55
+ timeoutMs: 20_000,
56
+ staleMs: 120_000
57
+ }, () => narutoRunTransaction(parsed, root, appSession, sessionKey));
58
+ }
59
+ return narutoRunTransaction(parsed, root, appSession, sessionKey);
60
+ }
61
+ async function narutoRunTransaction(parsed, root, appSession, sessionKey) {
62
+ const mission = await resolveRunMission(root, parsed, sessionKey);
63
+ if (!mission) {
118
64
  return emit(parsed, {
119
65
  schema: NARUTO_RESULT_SCHEMA,
120
66
  ok: false,
121
- mode: 'NARUTO',
122
- action: 'run',
123
67
  status: 'blocked',
124
- prompt_placeholder_guard: placeholderGuard,
125
- blockers: placeholderGuard.blockers,
126
- hint: placeholderGuard.blockers.includes('write_capable_prompt_target_paths_empty')
127
- ? 'Name the target source path(s), or run Naruto read-only discovery before starting a write-capable worker swarm.'
128
- : null
129
- }, () => {
130
- console.log('$Naruto blocked before work graph creation: unresolved prompt placeholder or empty write target path.');
131
- for (const blocker of placeholderGuard.blockers)
132
- console.log('- ' + blocker);
133
- });
68
+ blockers: [`naruto_mission_not_found:${parsed.missionId}`]
69
+ }, () => console.error(`Naruto mission not found: ${parsed.missionId}`), true);
134
70
  }
135
- const roster = buildNarutoCloneRoster({
136
- clones: parsed.clones,
137
- prompt: parsed.prompt,
138
- readonly: parsed.readonly,
139
- maxAgentCount: MAX_NARUTO_AGENT_COUNT
140
- });
141
- const mission = await createMission(root, { mode: 'naruto', prompt: parsed.prompt });
142
- await createAndWriteWorkOrderLedgerForPrompt(mission.dir, { missionId: mission.id, route: 'Naruto', prompt: parsed.prompt });
143
- await writeCodex0138CapabilityArtifacts(root, { missionId: mission.id }).catch(() => null);
144
- await writeCodex0139CapabilityArtifacts(root, { missionId: mission.id }).catch(() => null);
145
- const gitWorktreeCapability = writeCapable
146
- ? await evaluateGitWorktreeCapability({ root, missionId: mission.id })
147
- : null;
148
- const worktreePolicy = gitWorktreeCapability?.mode === 'git-worktree'
149
- ? {
150
- mode: 'git-worktree',
151
- required: true,
152
- main_repo_root: gitWorktreeCapability.detection.root,
153
- worktree_root: gitWorktreeCapability.root_resolution?.root || null,
154
- fallback_reason: null
155
- }
156
- : {
157
- mode: 'patch-envelope-only',
158
- required: false,
159
- main_repo_root: gitWorktreeCapability?.detection.root || null,
160
- worktree_root: null,
161
- fallback_reason: writeCapable ? (gitWorktreeCapability?.blockers.join(';') || 'not_git_repo_or_worktree_unavailable') : 'readonly_or_write_disabled'
162
- };
163
- // The clone roster is the full work fan-out; live concurrency is throttled to a
164
- // system-safe number so naruto never spawns the whole count at once unless an
165
- // explicit operator override asks for a higher target.
166
- const localWorker = await resolveNarutoLocalWorkerMode(parsed);
167
- const schedulerBackend = parsed.backend;
168
- const safe = systemSafeNarutoConcurrency({ backend: schedulerBackend });
169
- const baseWorkGraph = buildNarutoWorkGraph({
170
- prompt: parsed.prompt,
171
- requestedClones: roster.agent_count,
172
- totalWorkItems: parsed.workItems,
173
- honorExplicitTotalWorkItems: parsed.workItemsExplicit,
174
- readonly: parsed.readonly,
175
- writeCapable,
176
- targetPaths,
177
- leaseBasePath: patchEnvelopeBasePath,
178
- maxActiveWorkers: parsed.concurrency || safe.cap,
179
- worktreePolicy,
180
- tournament: parsed.tournament
181
- });
182
- const baseRoleDistribution = buildNarutoRoleDistribution(baseWorkGraph.work_items, { readonly: parsed.readonly });
183
- const allocationWorkers = buildNarutoAllocationWorkers(baseWorkGraph, baseRoleDistribution, roster);
184
- const allocationAssignments = allocateNarutoTasksToWorkers(baseWorkGraph.work_items, allocationWorkers);
185
- const workGraph = buildNarutoWorkGraph({
186
- prompt: parsed.prompt,
187
- requestedClones: roster.agent_count,
188
- totalWorkItems: parsed.workItems,
189
- honorExplicitTotalWorkItems: parsed.workItemsExplicit,
190
- readonly: parsed.readonly,
191
- writeCapable,
192
- targetPaths,
193
- leaseBasePath: patchEnvelopeBasePath,
194
- maxActiveWorkers: parsed.concurrency || safe.cap,
195
- worktreePolicy,
196
- allocationAssignments,
197
- tournament: parsed.tournament
198
- });
199
- const roleDistribution = buildNarutoRoleDistribution(workGraph.work_items, { readonly: parsed.readonly });
200
- const allocationPolicy = {
201
- schema: 'sks.naruto-allocation-policy.v1',
202
- generated_at: nowIso(),
203
- ok: allocationWorkers.length > 0 && allocationAssignments.length === workGraph.work_items.length,
204
- scoring_model: {
205
- same_primary_role: 18,
206
- declared_role: 12,
207
- same_path_lane: 12,
208
- overlap_each: 4,
209
- assigned_task_penalty_each: -4,
210
- write_conflict_penalty: -20,
211
- dependency_incomplete: '-Infinity'
212
- },
213
- workers: allocationWorkers,
214
- assignments: allocationAssignments.map((row) => ({
215
- task_id: row.id,
216
- owner: row.owner,
217
- score: Number.isFinite(row.allocation_score) ? row.allocation_score : '-Infinity',
218
- reason: row.allocation_reason,
219
- role: row.required_role,
220
- kind: row.kind,
221
- paths: row.hints.paths,
222
- domains: row.hints.domains,
223
- write_paths: row.hints.writePaths
224
- })),
225
- blockers: allocationWorkers.length ? [] : ['naruto_allocation_workers_missing']
226
- };
227
- const rebalanceDecisions = rebalanceNarutoReadyWork({
228
- tasks: workGraph.work_items.map((item) => ({ ...item, status: 'pending' })),
229
- workers: allocationWorkers.map((worker) => ({ ...worker, alive: true, state: 'idle' })),
230
- completedTaskIds: [],
231
- reclaimedTaskIds: []
232
- });
233
- const rebalancePolicy = {
234
- schema: 'sks.naruto-rebalance-policy.v1',
235
- generated_at: nowIso(),
236
- ok: true,
237
- trigger: 'idle_worker_ready_queue',
238
- decisions: rebalanceDecisions,
239
- blocked_by_dependency_count: workGraph.work_items.filter((item) => item.dependencies.length > 0).length,
240
- blockers: []
241
- };
242
- const governor = decideNarutoConcurrency({
243
- requestedClones: roster.agent_count,
244
- totalWorkItems: workGraph.total_work_items,
245
- pendingWorkQueueSize: workGraph.total_work_items,
246
- backend: schedulerBackend,
247
- parallelismMode: parsed.parallelism
248
- });
249
- const activeCap = Math.min(safe.cap, governor.safe_active_workers);
250
- const activeSlots = Math.max(1, Math.min(roster.agent_count, parsed.concurrency || activeCap, activeCap));
251
- const zellijVisiblePanes = Math.max(1, Math.min(activeSlots, governor.safe_zellij_visible_panes));
252
- const runPreRunSmoke = parsed.smoke === true || process.env.SKS_NARUTO_PRE_RUN_SMOKE === '1';
253
- const realActivePoolSmoke = runPreRunSmoke
254
- ? await runNarutoControlPlaneSmoke({
255
- root,
256
- missionId: mission.id,
257
- prompt: parsed.prompt,
258
- rosterCount: roster.agent_count,
259
- totalWorkItems: workGraph.total_work_items,
260
- patchEnvelopeBasePath,
261
- worktreePolicy,
262
- governor,
263
- activeSlots,
264
- zellijVisiblePanes
265
- })
266
- : {
267
- schema: 'sks.naruto-active-pool.v1',
268
- ok: false,
269
- status: 'skipped',
270
- runtime_source_of_truth: 'agent-orchestrator-scheduler',
271
- production_runtime_source_of_truth: 'agent-orchestrator-scheduler',
272
- fallback_reason: 'pre_run_smoke_never_owns_production_runtime',
273
- reason: 'pre_run_smoke_disabled_for_production',
274
- active_cap: 0,
275
- max_observed_active_workers: 0,
276
- average_active_workers: 0,
277
- active_pool_utilization: 0,
278
- refill_latency_ms_p95: 0,
279
- visible_workers: 0,
280
- headless_workers: 0,
281
- worker_lifecycle: [],
282
- smoke_graph_total_work_items: 0
283
- };
284
- const verificationCommand = resolveNarutoVerificationCommand(root);
285
- const verificationDag = buildNarutoVerificationDag(workGraph, { cwd: root, command: verificationCommand });
286
- const gptFinalPack = buildNarutoGptFinalPack({
287
- missionId: mission.id,
288
- graph: workGraph,
289
- roleDistribution,
290
- localLlmMetrics: localWorker,
291
- worktreePolicy,
292
- worktreeDiffs: []
293
- });
294
- const zellijDashboard = planNarutoZellijDashboard({
295
- targetActiveWorkers: activeSlots,
296
- visiblePaneCap: governor.safe_zellij_visible_panes,
297
- backpressure: governor.backpressure,
298
- roles: roleDistribution.work_item_roles.map((row) => row.role),
299
- backend: schedulerBackend,
300
- worktreePolicy
301
- });
302
- const ledgerRoot = path.join(mission.dir, 'agents');
303
- await writeNarutoArtifacts(ledgerRoot, {
304
- workGraph,
305
- roleDistribution,
306
- governor,
307
- realActivePool: realActivePoolSmoke,
308
- allocationPolicy,
309
- rebalancePolicy,
310
- verificationDag,
311
- gptFinalPack,
312
- zellijDashboard,
313
- placeholderGuard,
314
- gitWorktreeCapability
315
- });
316
- await writeJsonAtomic(path.join(mission.dir, 'naruto-gate.json'), {
317
- schema: 'sks.naruto-gate.v1',
318
- passed: false,
319
- mission_id: mission.id,
320
- clone_roster_built: true,
321
- clone_count: roster.agent_count,
322
- work_graph_ready: workGraph.ok === true,
323
- role_distribution_ready: roleDistribution.ok === true,
324
- allocation_ready: allocationPolicy.ok === true,
325
- rebalance_ready: rebalancePolicy.ok === true,
326
- concurrency_governor_ready: true,
327
- active_pool_ready: true,
328
- verification_dag_ready: verificationDag.configured,
329
- verification_dag_unconfigured_reason: verificationDag.unconfigured_reason,
330
- verification_dag_executed: false,
331
- gpt_final_pack_ready: Boolean(gptFinalPack?.schema),
332
- zellij_dashboard_ready: zellijDashboard.ok === true,
333
- native_agent_proof: false,
334
- final_arbiter_accepted: false,
335
- session_cleanup: false,
336
- blockers: [...(workGraph.blockers || []), ...(allocationPolicy.blockers || [])],
337
- updated_at: nowIso()
71
+ const { id, dir } = mission;
72
+ if (appSession && sessionKey) {
73
+ const pending = await readPendingAppNarutoRun(root, { id, dir }, sessionKey);
74
+ if (pending)
75
+ return emit(parsed, pending, () => renderRunResult(pending));
76
+ }
77
+ await createAndWriteWorkOrderLedgerForPrompt(dir, {
78
+ missionId: id,
79
+ route: 'Naruto',
80
+ prompt: parsed.prompt
338
81
  });
82
+ const preparation = await withFileLock({
83
+ lockPath: path.join(dir, '.naruto-preparation.lock'),
84
+ timeoutMs: 5_000,
85
+ staleMs: 60_000
86
+ }, () => prepareOfficialSubagentMission({
87
+ root,
88
+ dir,
89
+ missionId: id,
90
+ goal: parsed.prompt,
91
+ route: '$Naruto',
92
+ sessionScope: sessionKey,
93
+ ...(parsed.requestedSubagents === undefined ? {} : { requestedSubagents: parsed.requestedSubagents }),
94
+ requestedSubagentsExplicit: parsed.requestedSubagents !== undefined,
95
+ ...(parsed.maxThreads === undefined ? {} : { maxThreads: parsed.maxThreads }),
96
+ mode: 'naruto',
97
+ readOnly: parsed.readOnly,
98
+ preparationOnly: true
99
+ }));
100
+ const { plan, evidence: preparationEvidence, budget, verification, delegationPrompt, workflowRunId, configBlockers } = preparation;
339
101
  await setCurrent(root, {
340
- mission_id: mission.id,
102
+ mission_id: id,
341
103
  route: 'Naruto',
342
104
  route_command: '$Naruto',
343
105
  mode: 'NARUTO',
344
- phase: 'NARUTO_NATIVE_AGENT_INTAKE',
106
+ phase: 'NARUTO_DELEGATION_CONTEXT_READY',
345
107
  questions_allowed: false,
346
108
  implementation_allowed: true,
347
- context7_required: false,
348
- context7_verified: parsed.mock,
349
109
  subagents_required: true,
350
110
  subagents_verified: false,
351
- native_sessions_required: true,
111
+ native_sessions_required: false,
352
112
  native_sessions_verified: false,
353
- reflection_required: true,
354
- visible_progress_required: true,
355
- required_skills: ['naruto', 'pipeline-runner', 'prompt-pipeline', 'honest-mode'],
356
- stop_gate: 'naruto-gate.json',
357
- clone_count: roster.agent_count,
358
- target_active_slots: activeSlots,
359
- work_graph_ready: workGraph.ok === true,
360
- naruto_gate_file: 'naruto-gate.json',
113
+ agents_required: false,
114
+ requested_subagents: budget.requestedSubagents,
115
+ max_threads: budget.maxThreads,
116
+ max_depth: budget.maxDepth,
117
+ official_subagent_run_id: workflowRunId,
118
+ session_scope: sessionKey,
119
+ stop_gate: NARUTO_GATE_FILENAME,
120
+ naruto_gate_file: NARUTO_GATE_FILENAME,
361
121
  prompt: parsed.prompt
122
+ }, { sessionKey });
123
+ const run = await runOfficialSubagentWorkflow({
124
+ root,
125
+ prompt: delegationPrompt,
126
+ requestedSubagents: budget.requestedSubagents,
127
+ maxThreads: budget.maxThreads,
128
+ appSession,
129
+ missionId: id,
130
+ sessionKey
362
131
  });
363
- let liveZellij = null;
364
- if (!parsed.json) {
365
- console.log('$Naruto starting:');
366
- console.log(' clones requested: ' + roster.agent_count);
367
- console.log(' work items: ' + workGraph.total_work_items);
368
- console.log(' target active workers: ' + activeSlots);
369
- console.log(' visible panes: ' + zellijVisiblePanes);
370
- console.log(' headless workers: ' + Math.max(0, activeSlots - zellijVisiblePanes));
371
- console.log(' backend: ' + schedulerBackend);
372
- console.log(' parallelism mode: ' + parsed.parallelism);
373
- if (activeSlots < roster.agent_count)
374
- console.log(' cap reasons: ' + (governor.reasons.join(', ') || 'host safety cap'));
375
- // Backpressure used to throttle silently (50% when throttled, 25% when
376
- // saturated); always tell the operator when host pressure reduced workers.
377
- if (governor.backpressure !== 'normal')
378
- console.log(' backpressure: ' + governor.backpressure + ' — host resource pressure reduced active workers (memory/cpu/fd/disk thresholds)');
379
- if (parsed.parallelism !== 'safe' && activeSlots < 10)
380
- console.log(' warning: active workers below 10 in non-safe mode');
381
- }
382
- if (!parsed.json && !parsed.mock && !parsed.noOpenZellij) {
383
- liveZellij = await launchZellijLayout({
132
+ const completedPlan = await readJson(path.join(dir, SUBAGENT_PLAN_FILENAME), plan).catch(() => plan);
133
+ const finalBudget = resolveSubagentThreadBudget({
134
+ requested: Number(completedPlan?.requested_subagents || budget.requestedSubagents),
135
+ configuredMaxThreads: Number(completedPlan?.max_threads || budget.maxThreads)
136
+ });
137
+ const runBoundParentSummary = bindTrustworthySubagentParentSummaryToRun(run.parent_summary, workflowRunId);
138
+ const effectiveParentSummary = await persistOrReuseTrustworthySubagentParentSummary(dir, runBoundParentSummary, {
139
+ workflowStatus: run.status,
140
+ runId: workflowRunId
141
+ });
142
+ const evidence = await writeSubagentEvidence(dir, {
143
+ requestedSubagents: finalBudget.requestedSubagents,
144
+ parentSummary: effectiveParentSummary,
145
+ workflowStatus: run.status,
146
+ preparationOnly: appSession,
147
+ runId: workflowRunId,
148
+ additionalBlockers: configBlockers
149
+ });
150
+ if (!appSession) {
151
+ const parentTelemetry = await recordOfficialSubagentParentOutcomesTelemetry({
384
152
  root,
385
- missionId: mission.id,
386
- ledgerRoot,
387
- kind: 'naruto',
388
- slotCount: 0,
389
- dryRun: false,
390
- attach: false
153
+ routeMissionId: id,
154
+ parentSummary: effectiveParentSummary,
155
+ plan: completedPlan
156
+ }).catch(async (error) => {
157
+ await appendJsonl(path.join(dir, 'zellij-telemetry-warnings.jsonl'), {
158
+ ts: nowIso(),
159
+ warning: 'official_subagent_parent_outcome_telemetry_failed',
160
+ error: String(error?.message || error)
161
+ }).catch(() => undefined);
162
+ return null;
391
163
  });
392
- if (liveZellij?.ok && liveZellij.capability?.status === 'ok') {
393
- liveZellij.dashboard_pane = null;
394
- liveZellij.right_column_mode = 'spawn-on-first-worker';
395
- await writeJsonAtomic(path.join(mission.dir, 'zellij-initial-ui.json'), {
396
- schema: 'sks.zellij-initial-ui.v1',
397
- ok: true,
398
- mission_id: mission.id,
399
- session_name: liveZellij.session_name,
400
- initial_panes: 'main-only',
401
- dashboard_created: false,
402
- worker_panes_created: 0,
403
- right_column_mode: 'spawn-on-first-worker',
404
- visible_pane_cap: zellijVisiblePanes
405
- });
406
- 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));
407
- if (parsed.attach)
408
- attachZellijSessionInteractive(liveZellij.session_name, { cwd: process.cwd(), configPath: liveZellij.clipboard_config_path });
409
- }
410
- else if (liveZellij?.ok) {
411
- console.log('Zellij: optional live panes unavailable (' + ((liveZellij.warnings || []).join('; ') || liveZellij.capability?.status || 'unknown') + ')');
412
- }
413
- else {
414
- console.log('Zellij: blocked (' + Array.from(new Set(liveZellij?.blockers || [])).join('; ') + ')');
164
+ if (parentTelemetry?.blocker) {
165
+ await appendJsonl(path.join(dir, 'zellij-telemetry-warnings.jsonl'), {
166
+ ts: nowIso(),
167
+ warning: 'official_subagent_parent_outcome_telemetry_incomplete',
168
+ blocker: parentTelemetry.blocker,
169
+ failed_mission_ids: 'failed_mission_ids' in parentTelemetry ? parentTelemetry.failed_mission_ids : [],
170
+ skipped_thread_ids: 'skipped_thread_ids' in parentTelemetry ? parentTelemetry.skipped_thread_ids : []
171
+ }).catch(() => undefined);
415
172
  }
416
173
  }
417
- const result = await withNarutoSwarmInterruptCleanup({ missionId: mission.id, zellijSessionName: liveZellij?.session_name || null }, () => runNativeAgentOrchestrator({
418
- missionId: mission.id,
419
- prompt: parsed.prompt,
420
- route: NARUTO_ROUTE,
421
- routeCommand: 'sks naruto run',
422
- routeBlackboxKind: 'actual_naruto_command',
423
- roster,
424
- agents: roster.agent_count,
425
- concurrency: activeSlots,
426
- targetActiveSlots: activeSlots,
427
- visualLaneCount: zellijVisiblePanes,
428
- desiredWorkItemCount: workGraph.total_work_items,
429
- minimumWorkItems: workGraph.total_work_items,
430
- maxAgentCount: MAX_NARUTO_AGENT_COUNT,
431
- narutoMode: true,
432
- clones: roster.agent_count,
433
- backend: parsed.backend,
434
- backendExplicit: parsed.backendExplicit,
435
- noOllama: parsed.noOllama,
436
- ollamaEnabled: parsed.ollamaEnabled,
437
- ollamaModel: parsed.ollamaModel,
438
- ollamaBaseUrl: parsed.ollamaBaseUrl,
439
- mock: parsed.mock,
440
- real: parsed.real,
441
- readonly: parsed.readonly,
442
- zellijSessionName: liveZellij?.session_name || `sks-${mission.id}`,
443
- workerPlacement: parsed.json || parsed.noOpenZellij ? 'process' : 'zellij-pane',
444
- zellijPaneWorker: true,
445
- zellijVisiblePaneCap: zellijVisiblePanes,
446
- ...(parsed.fastMode === undefined ? {} : { fastMode: parsed.fastMode }),
447
- ...(parsed.serviceTier === undefined ? {} : { serviceTier: parsed.serviceTier }),
448
- noFast: parsed.noFast,
449
- writeMode: writeCapable ? parsed.writeMode || 'parallel' : 'off',
450
- applyPatches: parsed.applyPatches,
451
- dryRunPatches: parsed.dryRunPatches,
452
- maxWriteAgents: parsed.maxWriteAgents,
453
- gitWorktreePolicy: worktreePolicy,
454
- narutoWorkGraph: workGraph,
455
- narutoAllocationPolicy: allocationPolicy,
456
- narutoRebalancePolicy: rebalancePolicy,
457
- json: parsed.json
458
- }));
459
- const realWriteProof = await maybeWriteNarutoRealWriteProof(mission.dir, mission.id, parsed, result);
460
- const parallelRuntime = result.parallel_runtime_proof || null;
461
- const nativeProofOk = result.proof?.ok === true || result.proof?.status === 'passed';
462
- const finalAccepted = result.proof?.status === 'passed' || result.proof?.gpt_final_status === 'approved';
463
- // Real backends must actually prove parallel runtime; mock backends (no real
464
- // process fan-out to observe) and small rosters below the 16-worker proof
465
- // threshold are exempt. Previously this was inverted (`!parsed.mock`), which
466
- // skipped the proof precisely when it mattered most — real execution.
467
- const parallelRuntimeOk = parsed.mock || roster.agent_count < 16 || (parallelRuntime?.passed === true
468
- && Number(parallelRuntime.max_observed_active_workers || 0) >= Math.min(16, activeSlots));
469
- // Verification starts only after clone workers finish so two process pools do
470
- // not compete for CPU, memory, and disk at the same time.
471
- const verificationDagResult = verificationDag.configured && verificationDag.tasks.length > 0
472
- ? await runNarutoVerificationPool(verificationDag, governor, { cwd: root, logDir: path.join(mission.dir, 'agents', 'verification-logs') })
473
- : null;
474
- if (verificationDagResult)
475
- await writeJsonAtomic(path.join(mission.dir, 'naruto-verification-dag-result.json'), verificationDagResult);
476
- const verificationDagOk = !verificationDag.configured || verificationDagResult === null ? true : verificationDagResult.ok === true;
477
- const regressionProof = summarizeRegressionProof(workGraph, result);
478
- await writeJsonAtomic(path.join(mission.dir, 'regression-proof-summary.json'), regressionProof);
479
- const tddOk = !regressionProof.required || (regressionProof.regression_test_added && regressionProof.regression_test_failed_before_fix && regressionProof.regression_test_passed_after_fix);
480
- const narutoGateFullPassed = result.ok === true && nativeProofOk && finalAccepted && (realWriteProof ? realWriteProof.ok === true : true) && parallelRuntimeOk && tddOk && workGraph.ok === true && allocationPolicy.ok === true && verificationDagOk;
481
- const narutoGateFullBlockers = [...(result.proof?.blockers || []), ...(realWriteProof && !realWriteProof.ok ? realWriteProof.blockers : []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate']), ...(tddOk ? [] : ['tdd_evidence_missing']), ...(verificationDagOk ? [] : ['naruto_verification_dag_failed']), ...(workGraph.blockers || []), ...(allocationPolicy.blockers || [])];
482
- await writeJsonAtomic(path.join(mission.dir, 'naruto-gate.json'), {
483
- schema: 'sks.naruto-gate.v1',
484
- passed: narutoGateFullPassed,
485
- mission_id: mission.id,
486
- clone_roster_built: true,
487
- clone_count: roster.agent_count,
488
- work_graph_ready: workGraph.ok === true,
489
- role_distribution_ready: roleDistribution.ok === true,
490
- allocation_ready: allocationPolicy.ok === true,
491
- rebalance_ready: rebalancePolicy.ok === true,
492
- concurrency_governor_ready: true,
493
- active_pool_ready: true,
494
- verification_dag_ready: verificationDagOk,
495
- verification_dag_configured: verificationDag.configured,
496
- verification_dag_unconfigured_reason: verificationDag.unconfigured_reason,
497
- verification_dag_executed: verificationDagResult !== null,
498
- verification_dag_result: verificationDagResult ? 'naruto-verification-dag-result.json' : null,
499
- gpt_final_pack_ready: Boolean(gptFinalPack?.schema),
500
- zellij_dashboard_ready: zellijDashboard.ok === true,
501
- native_agent_proof: nativeProofOk,
502
- naruto_real_write_proof: realWriteProof ? 'naruto-real-write-proof.json' : null,
503
- parallel_runtime_proof: parallelRuntimeOk,
504
- regression_test_added: regressionProof.regression_test_added,
505
- regression_test_failed_before_fix: regressionProof.regression_test_failed_before_fix,
506
- regression_test_passed_after_fix: regressionProof.regression_test_passed_after_fix,
507
- regression_proof: 'regression-proof-summary.json',
508
- final_arbiter_accepted: finalAccepted,
509
- session_cleanup: result.proof?.all_sessions_closed === true || nativeProofOk,
510
- blockers: narutoGateFullBlockers,
511
- updated_at: nowIso()
512
- });
513
- await closeWorkOrderLedgerForRouteResult(mission.dir, { ok: narutoGateFullPassed, blockers: narutoGateFullBlockers });
514
- const clones = result.roster?.agent_count ?? roster.agent_count;
515
- const localWorkerSummary = summarizeNarutoLocalWorkerResult(localWorker, result);
516
- // Finalizer policy: when local LLM workers contributed patches, the GPT
517
- // final arbiter must have accepted before patches are considered final.
518
- const finalizer = evaluateNarutoFinalizer({
519
- localParticipated: Number(localWorkerSummary?.selected_worker_count || 0) > 0,
520
- gptFinalStatus: result.proof?.gpt_final_status || null,
521
- applyPatches: parsed.applyPatches
522
- });
523
- await writeJsonAtomic(path.join(mission.dir, 'naruto-finalizer.json'), {
524
- ...finalizer,
525
- generated_at: nowIso(),
526
- mission_id: mission.id
174
+ const passed = run.ok === true && evidence.ok === true && appSession === false;
175
+ const blockers = uniqueStrings([
176
+ ...(Array.isArray(evidence.blockers) ? evidence.blockers : []),
177
+ ...configBlockers,
178
+ ...(!appSession && run.ok !== true ? [`codex_parent_exit:${String(run.codex_exit_code ?? 'unknown')}`] : []),
179
+ ...(appSession ? ['official_subagent_execution_pending_in_current_parent'] : [])
180
+ ]);
181
+ const status = passed
182
+ ? 'completed'
183
+ : appSession
184
+ ? 'delegation_context_ready'
185
+ : run.ok === true
186
+ ? 'incomplete'
187
+ : 'blocked';
188
+ const summary = buildNarutoSummary({
189
+ missionId: id,
190
+ workflowRunId,
191
+ budget: finalBudget,
192
+ evidence,
193
+ verification,
194
+ status,
195
+ ok: passed,
196
+ parentSummary: effectiveParentSummary,
197
+ blockers,
198
+ appSession,
199
+ sessionKey
527
200
  });
528
- const summaryOk = result.ok === true && tddOk && (parsed.applyPatches === true ? finalizer.ok === true : finalizer.run_ok === true);
201
+ await writeJsonAtomic(path.join(dir, NARUTO_SUMMARY_FILENAME), summary);
202
+ await writeNarutoGate(dir, { missionId: id, workflowRunId, evidence, passed, blockers });
529
203
  await setCurrent(root, {
530
- mission_id: mission.id,
531
- route: 'Naruto',
532
- route_command: '$Naruto',
533
- mode: 'NARUTO',
534
- phase: summaryOk ? 'NARUTO_COMPLETE_OR_REVIEW' : 'NARUTO_BLOCKED',
535
- native_sessions_verified: nativeProofOk,
536
- subagents_verified: nativeProofOk,
537
- naruto_gate_file: 'naruto-gate.json',
538
- stop_gate: 'naruto-gate.json',
539
- prompt: parsed.prompt
540
- });
541
- // 4.0.9: Write canonical stop-gate artifacts for hook resolution.
542
- const narutoGatePassed = result.ok === true && nativeProofOk && finalAccepted && parallelRuntimeOk && tddOk;
543
- await writeFinalStopGate({
544
- root,
545
- missionId: mission.id,
546
- route: 'Naruto',
547
- routeCommand: '$Naruto',
548
- status: summaryOk ? 'passed' : 'blocked',
549
- terminal: summaryOk,
550
- terminalState: summaryOk ? 'completed' : 'blocked',
551
- evidence: {
552
- build_passed: summaryOk,
553
- tests_passed: summaryOk,
554
- route_evidence_passed: nativeProofOk && finalAccepted,
555
- native_session_split_evidence: nativeProofOk ? 'native_agent_proof' : null,
556
- ...(regressionProof.required ? {
557
- regression_test_added: regressionProof.regression_test_added,
558
- regression_test_failed_before_fix: regressionProof.regression_test_failed_before_fix,
559
- regression_test_passed_after_fix: regressionProof.regression_test_passed_after_fix,
560
- regression_proof_path: 'regression-proof-summary.json'
561
- } : { regression_proof_path: null }),
562
- },
563
- blockers: summaryOk ? [] : [...(result.proof?.blockers || []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate']), ...(tddOk ? [] : ['tdd_evidence_missing'])],
564
- nativeGateFile: 'naruto-gate.json',
565
- }).catch(() => null);
566
- const summary = {
204
+ mission_id: id,
205
+ official_subagent_run_id: workflowRunId,
206
+ session_scope: sessionKey,
207
+ phase: passed ? 'NARUTO_COMPLETE' : appSession ? 'NARUTO_DELEGATION_CONTEXT_READY' : 'NARUTO_BLOCKED',
208
+ subagents_verified: evidence.ok === true,
209
+ requested_subagents: finalBudget.requestedSubagents,
210
+ max_threads: finalBudget.maxThreads,
211
+ naruto_gate_passed: passed,
212
+ route_closed: false
213
+ }, { sessionKey });
214
+ if (!appSession) {
215
+ await closeWorkOrderLedgerForRouteResult(dir, { ok: passed, blockers });
216
+ if (!passed)
217
+ process.exitCode = 1;
218
+ }
219
+ const result = {
220
+ ...summary,
221
+ mission_id: id,
222
+ attached_to_pending_run: false,
223
+ additionalContext: appSession ? run.additionalContext : undefined,
224
+ artifacts: narutoArtifactLinks(evidence)
225
+ };
226
+ return emit(parsed, result, () => renderRunResult(result));
227
+ }
228
+ async function readPendingAppNarutoRun(root, mission, sessionKey) {
229
+ const [plan, evidence, summary, gate, state] = await Promise.all([
230
+ readJson(path.join(mission.dir, SUBAGENT_PLAN_FILENAME), null),
231
+ readJson(path.join(mission.dir, 'subagent-evidence.json'), null),
232
+ readJson(path.join(mission.dir, NARUTO_SUMMARY_FILENAME), null),
233
+ readJson(path.join(mission.dir, NARUTO_GATE_FILENAME), null),
234
+ loadStateForSession(root, sessionKey).catch(() => null)
235
+ ]);
236
+ const workflowRunId = String(plan?.workflow_run_id || '').trim();
237
+ const sessionMatches = state?._session_key === sessionStateKey(sessionKey);
238
+ const pending = Boolean(workflowRunId
239
+ && plan?.schema === 'sks.subagent-plan.v1'
240
+ && plan?.workflow === 'official_codex_subagent'
241
+ && plan?.mission_id === mission.id
242
+ && plan?.session_scope === sessionKey
243
+ && evidence?.run_id === workflowRunId
244
+ && evidence?.preparation_only === true
245
+ && evidence?.ok !== true
246
+ && summary?.workflow_run_id === workflowRunId
247
+ && summary?.mission_id === mission.id
248
+ && summary?.app_session === true
249
+ && summary?.session_scope === sessionKey
250
+ && summary?.status === 'delegation_context_ready'
251
+ && summary?.ok !== true
252
+ && summary?.completion_evidence !== true
253
+ && gate?.workflow_run_id === workflowRunId
254
+ && gate?.mission_id === mission.id
255
+ && gate?.passed !== true
256
+ && sessionMatches
257
+ && state?.mission_id === mission.id
258
+ && state?.official_subagent_run_id === workflowRunId
259
+ && state?.session_scope === sessionKey
260
+ && state?.route_closed !== true
261
+ && state?.phase === 'NARUTO_DELEGATION_CONTEXT_READY');
262
+ if (!pending)
263
+ return null;
264
+ return {
265
+ ...summary,
567
266
  schema: NARUTO_RESULT_SCHEMA,
568
- ok: summaryOk,
569
- mode: 'NARUTO',
570
- jutsu: 'kage_bunshin_no_jutsu',
571
- mission_id: result.mission_id,
572
- backend: result.backend,
573
- clones,
574
- max_clones: MAX_NARUTO_AGENT_COUNT,
575
- concurrency: result.target_active_slots ?? activeSlots,
576
- target_active_slots: result.target_active_slots ?? activeSlots,
577
- runtime_source_of_truth: 'agent-orchestrator-scheduler',
578
- pre_run_real_active_pool_source: runPreRunSmoke ? 'smoke_only' : 'skipped',
579
- concurrency_capped: clones > (result.target_active_slots ?? activeSlots),
580
- system: { cores: safe.cores, free_gb: safe.free_gb, safe_concurrency: safe.cap, heavy_backend: safe.heavy },
581
- work_graph: {
582
- total_work_items: workGraph.total_work_items,
583
- mixed_work_kinds: workGraph.mixed_work_kinds,
584
- write_allowed_count: workGraph.write_allowed_count,
585
- active_wave_count: workGraph.active_waves.length,
586
- parallel_write_wave_count: workGraph.active_waves.filter((wave) => wave.write_paths.length > 1).length,
587
- ok: workGraph.ok,
588
- worktree_policy: workGraph.worktree_policy
589
- },
590
- git_worktree: gitWorktreeCapability,
591
- role_distribution: roleDistribution,
592
- allocation_policy: allocationPolicy,
593
- rebalance_policy: rebalancePolicy,
594
- concurrency_governor: governor,
595
- active_pool: {
596
- ok: realActivePoolSmoke.ok,
597
- runtime_source_of_truth: realActivePoolSmoke.runtime_source_of_truth,
598
- production_runtime_source_of_truth: realActivePoolSmoke.production_runtime_source_of_truth,
599
- active_cap: realActivePoolSmoke.active_cap,
600
- max_observed_active_workers: realActivePoolSmoke.max_observed_active_workers,
601
- average_active_workers: realActivePoolSmoke.average_active_workers,
602
- active_pool_utilization: realActivePoolSmoke.active_pool_utilization,
603
- refill_latency_ms_p95: realActivePoolSmoke.refill_latency_ms_p95,
604
- visible_workers: realActivePoolSmoke.visible_workers,
605
- headless_workers: realActivePoolSmoke.headless_workers,
606
- worker_lifecycle_count: realActivePoolSmoke.worker_lifecycle.length,
607
- worker_lifecycle_sample: realActivePoolSmoke.worker_lifecycle.slice(0, 5)
608
- },
609
- parallel_runtime: parallelRuntime ? {
610
- proof_path: path.join(result.ledger_root || '', 'parallel-runtime-proof.json'),
611
- max_observed_active_workers: parallelRuntime.max_observed_active_workers,
612
- unique_worker_pids: parallelRuntime.unique_worker_pids,
613
- speedup_ratio: parallelRuntime.speedup_ratio,
614
- visible_panes: parallelRuntime.visible_panes,
615
- headless_workers: parallelRuntime.headless_workers,
616
- passed: parallelRuntime.passed
617
- } : null,
618
- parallel_write_policy: result.parallel_write_policy || null,
619
- local_worker: localWorkerSummary,
620
- fast_mode_policy: result.fast_mode_policy || null,
621
- fast_mode_propagation: result.fast_mode_propagation ? {
622
- ok: result.fast_mode_propagation.ok === true,
623
- fast_mode: result.fast_mode_propagation.fast_mode,
624
- service_tier: result.fast_mode_propagation.service_tier,
625
- worker_process_report_count: result.fast_mode_propagation.worker_process_report_count || 0,
626
- blockers: result.fast_mode_propagation.blockers || []
627
- } : null,
628
- finalizer,
629
- regression_proof: regressionProof,
630
- proof: result.proof?.status || 'missing',
631
- run: compactNarutoRunResult(result),
632
- zellij: null
267
+ ok: false,
268
+ completion_evidence: false,
269
+ status: 'delegation_context_ready',
270
+ workflow_run_id: workflowRunId,
271
+ mission_id: mission.id,
272
+ app_session: true,
273
+ session_scope: sessionKey,
274
+ attached_to_pending_run: true,
275
+ additionalContext: plan.delegation_prompt,
276
+ artifacts: narutoArtifactLinks(evidence)
633
277
  };
634
- summary.zellij = liveZellij;
635
- return emit(parsed, summary, () => {
636
- console.log('🍥 Shadow Clone Jutsu — Kage Bunshin no Jutsu');
637
- console.log('Mission: ' + result.mission_id);
638
- console.log('Clones: ' + summary.clones + ' / max ' + MAX_NARUTO_AGENT_COUNT + ', running ' + summary.target_active_slots + ' at a time' + (summary.concurrency_capped ? ` (throttled to host capacity: ${safe.cores} cores, ${safe.free_gb} GB free)` : ''));
639
- console.log('Backend: ' + result.backend);
640
- console.log('Roles: ' + roleDistribution.entries.map((entry) => `${entry.role}:${entry.count}`).join(', '));
641
- console.log('Proof: ' + summary.proof);
642
- if (!finalizer.ok)
643
- console.log('Finalizer: blocked — ' + finalizer.blockers.join(', '));
644
- if (summary.parallel_runtime) {
645
- console.log('$Naruto parallel proof:');
646
- console.log(' max active workers: ' + summary.parallel_runtime.max_observed_active_workers);
647
- console.log(' unique PIDs: ' + summary.parallel_runtime.unique_worker_pids);
648
- console.log(' speedup: ' + summary.parallel_runtime.speedup_ratio + 'x');
649
- console.log(' result: ' + (summary.parallel_runtime.passed ? 'passed' : 'blocked'));
650
- }
651
- if (summary.zellij?.ok && summary.zellij.capability?.status === 'ok')
652
- console.log('Zellij: prepared ' + zellijVisiblePanes + ' visible active clone lane(s) in ' + summary.zellij.session_name + '; dashboard tracks ' + Math.max(0, activeSlots - zellijVisiblePanes) + ' headless active worker(s)');
653
- else if (summary.zellij?.ok)
654
- console.log('Zellij: optional live panes unavailable (' + ((summary.zellij.warnings || []).join('; ') || summary.zellij.capability?.status || 'unknown') + ')');
655
- });
656
278
  }
657
- async function maybeWriteNarutoRealWriteProof(missionDir, missionId, parsed, result) {
658
- const enabled = parsed.backend === 'codex-sdk'
659
- && parsed.readonly !== true
660
- && parsed.applyPatches === true
661
- && (parsed.writeMode || 'parallel') === 'parallel';
662
- if (!enabled)
663
- return null;
664
- const ledgerRoot = path.join(missionDir, 'agents');
665
- const [queue, applyResults, proofEvidence] = await Promise.all([
666
- readJson(path.join(ledgerRoot, 'agent-patch-queue.json'), null),
667
- readJson(path.join(ledgerRoot, 'agent-patch-apply-results.json'), null),
668
- readJson(path.join(ledgerRoot, 'agent-proof-evidence.json'), null)
279
+ function narutoArtifactLinks(evidence) {
280
+ return {
281
+ plan: SUBAGENT_PLAN_FILENAME,
282
+ events: SUBAGENT_EVENT_LOG_FILENAME,
283
+ parent_summary: evidence?.parent_summary_trustworthy === true ? SUBAGENT_PARENT_SUMMARY_FILENAME : null,
284
+ evidence: 'subagent-evidence.json',
285
+ summary: NARUTO_SUMMARY_FILENAME,
286
+ gate: NARUTO_GATE_FILENAME
287
+ };
288
+ }
289
+ async function narutoStatus(parsed) {
290
+ const resolved = await resolveReadMission(parsed);
291
+ if (!resolved)
292
+ return missingMission(parsed, 'status');
293
+ const [plan, evidence, summary, gate] = await Promise.all([
294
+ readJson(path.join(resolved.dir, SUBAGENT_PLAN_FILENAME), null),
295
+ readJson(path.join(resolved.dir, 'subagent-evidence.json'), null),
296
+ readJson(path.join(resolved.dir, NARUTO_SUMMARY_FILENAME), null),
297
+ readJson(path.join(resolved.dir, NARUTO_GATE_FILENAME), null)
669
298
  ]);
670
- const applyRows = Array.isArray(applyResults?.results) ? applyResults.results : [];
671
- const entries = Array.isArray(queue?.entries) ? queue.entries : [];
672
- const applyByEntry = new Map(applyRows.map((row) => [String(row.entry_id || row.envelope_id || ''), row]));
673
- const patchEnvelopes = entries.map((entry) => {
674
- const applied = applyByEntry.get(String(entry.id || '')) || applyByEntry.get(String(entry.envelope?.envelope_id || '')) || null;
675
- return {
676
- envelope_id: String(entry.id || entry.envelope?.envelope_id || ''),
677
- agent_id: String(entry.agent_id || entry.envelope?.agent_id || ''),
678
- changed_files: uniqueRelValues(applied?.changed_files?.length ? applied.changed_files : entry.write_paths || entry.envelope?.operations?.map((operation) => operation.path) || []),
679
- applied: applied ? applied.ok !== false && String(entry.status || '') !== 'conflicted' && String(entry.status || '') !== 'rejected' : false
680
- };
681
- });
682
- const changedFiles = uniqueRelValues([
683
- ...applyRows.flatMap((row) => row.changed_files || []),
684
- ...patchEnvelopes.flatMap((envelope) => envelope.applied ? envelope.changed_files : [])
299
+ const result = {
300
+ schema: NARUTO_RESULT_SCHEMA,
301
+ ok: Boolean(plan || evidence || summary || gate),
302
+ action: 'status',
303
+ mission_id: resolved.id,
304
+ status: summary?.status || evidence?.status || 'prepared',
305
+ requested_subagents: plan?.requested_subagents ?? evidence?.requested_subagents ?? null,
306
+ max_threads: plan?.max_threads ?? null,
307
+ started_subagents: evidence?.started_threads ?? 0,
308
+ completed_subagents: evidence?.completed_threads ?? 0,
309
+ failed_subagents: evidence?.failed_threads ?? 0,
310
+ gate_passed: gate?.passed === true,
311
+ blockers: gate?.blockers || evidence?.blockers || []
312
+ };
313
+ return emit(parsed, result, () => renderStatusResult(result));
314
+ }
315
+ async function narutoSubagents(parsed) {
316
+ const resolved = await resolveReadMission(parsed);
317
+ if (!resolved)
318
+ return missingMission(parsed, 'subagents');
319
+ const [evidence, events] = await Promise.all([
320
+ readJson(path.join(resolved.dir, 'subagent-evidence.json'), null),
321
+ readSubagentEvents(resolved.dir)
685
322
  ]);
686
- const proof = buildNarutoRealWriteProof({
687
- missionId,
688
- changedFiles,
689
- workerIds: uniqueRelValues(entries.map((entry) => entry.slot_id || entry.session_id || entry.agent_id || entry.envelope?.slot_id || entry.envelope?.session_id || entry.envelope?.agent_id)),
690
- patchEnvelopes,
691
- parentMergeArtifact: 'agents/agent-patch-swarm-runtime.json',
692
- typecheck: {
693
- ok: proofEvidence?.patch_verification_ok === true,
694
- command: 'agents/agent-patch-verification-results.json'
695
- },
696
- cleanup: {
697
- ok: result?.cleanup?.ok === true || result?.proof?.all_sessions_closed === true
698
- },
699
- blockers: [
700
- ...(entries.length ? [] : ['patch_queue_entries_missing']),
701
- ...(applyRows.length ? [] : ['patch_apply_results_missing']),
702
- ...(result?.patch_swarm?.blockers || [])
703
- ]
704
- });
705
- await writeJsonAtomic(path.join(missionDir, 'naruto-real-write-proof.json'), proof);
706
- return proof;
707
- }
708
- function uniqueRelValues(values) {
709
- return [...new Set((Array.isArray(values) ? values : []).map((value) => String(value || '').replace(/\\/g, '/').replace(/^\.\/+/, '').trim()).filter(Boolean))].sort();
710
- }
711
- function compactNarutoRunResult(result) {
712
- return {
713
- schema: result?.schema || 'sks.agent-run.v1',
714
- ok: result?.ok === true,
715
- mission_id: result?.mission_id || null,
716
- route: result?.route || NARUTO_ROUTE,
717
- backend: result?.backend || null,
718
- parallel_write_policy: result?.parallel_write_policy || null,
719
- target_active_slots: result?.target_active_slots ?? null,
720
- proof: result?.proof ? {
721
- ok: result.proof.ok === true,
722
- status: result.proof.status || null,
723
- blockers: result.proof.blockers || []
724
- } : null,
725
- scheduler: result?.scheduler ? {
726
- state: {
727
- completed_count: result.scheduler.state?.completed_count ?? result.scheduler.completed_count ?? null,
728
- failed_count: result.scheduler.state?.failed_count ?? result.scheduler.failed_count ?? null,
729
- blocked_count: result.scheduler.state?.blocked_count ?? result.scheduler.blocked_count ?? null,
730
- max_observed_active_slots: result.scheduler.state?.max_observed_active_slots ?? result.scheduler.max_observed_active_slots ?? null
731
- }
732
- } : null,
733
- artifacts: {
734
- ledger_root: result?.ledger_root || null,
735
- proof: 'agent-proof-evidence.json',
736
- scheduler: 'agent-scheduler-state.json',
737
- native_cli_session_swarm: 'agent-native-cli-session-swarm.json',
738
- naruto_real_active_pool: 'naruto-real-active-pool.json'
739
- }
323
+ const result = {
324
+ schema: NARUTO_RESULT_SCHEMA,
325
+ ok: Boolean(evidence),
326
+ action: 'subagents',
327
+ mission_id: resolved.id,
328
+ requested_subagents: evidence?.requested_subagents ?? null,
329
+ started_subagents: evidence?.started_threads ?? 0,
330
+ completed_subagents: evidence?.completed_threads ?? 0,
331
+ failed_subagents: evidence?.failed_threads ?? 0,
332
+ started_thread_ids: evidence?.started_thread_ids || [],
333
+ completed_thread_ids: evidence?.completed_thread_ids || [],
334
+ failed_thread_ids: evidence?.failed_thread_ids || [],
335
+ events
740
336
  };
337
+ return emit(parsed, result, () => renderStatusResult(result));
741
338
  }
742
- async function runNarutoControlPlaneSmoke(input) {
743
- const smokeGraph = buildNarutoWorkGraph({
744
- prompt: input.prompt,
745
- requestedClones: Math.min(2, input.rosterCount),
746
- totalWorkItems: Math.min(2, input.totalWorkItems),
747
- readonly: true,
748
- writeCapable: false,
749
- leaseBasePath: input.patchEnvelopeBasePath,
750
- maxActiveWorkers: Math.min(2, input.activeSlots),
751
- worktreePolicy: {
752
- mode: 'patch-envelope-only',
753
- required: false,
754
- main_repo_root: input.worktreePolicy.main_repo_root,
755
- worktree_root: null,
756
- fallback_reason: 'pre_run_smoke_never_owns_production_runtime'
757
- }
758
- });
759
- const smokeWorktreePolicy = {
760
- mode: 'patch-envelope-only',
761
- required: false,
762
- main_repo_root: input.worktreePolicy.main_repo_root,
763
- worktree_root: null,
764
- fallback_reason: 'pre_run_smoke_never_owns_production_runtime'
339
+ async function narutoProof(parsed) {
340
+ const resolved = await resolveReadMission(parsed);
341
+ if (!resolved)
342
+ return missingMission(parsed, 'proof');
343
+ const [evidence, summary, gate] = await Promise.all([
344
+ readJson(path.join(resolved.dir, 'subagent-evidence.json'), null),
345
+ readJson(path.join(resolved.dir, NARUTO_SUMMARY_FILENAME), null),
346
+ readJson(path.join(resolved.dir, NARUTO_GATE_FILENAME), null)
347
+ ]);
348
+ const result = {
349
+ schema: NARUTO_RESULT_SCHEMA,
350
+ ok: gate?.passed === true && evidence?.ok === true,
351
+ action: 'proof',
352
+ mission_id: resolved.id,
353
+ workflow: 'official_codex_subagent',
354
+ evidence,
355
+ summary,
356
+ gate
765
357
  };
766
- const realActivePool = await runNarutoRealActivePool({
767
- graph: smokeGraph,
768
- governor: { ...input.governor, safe_active_workers: Math.min(2, input.activeSlots), safe_zellij_visible_panes: Math.min(1, input.zellijVisiblePanes) },
769
- spawnWorker: async (item, placement) => spawnActualNarutoWorker({
770
- root: input.root,
771
- missionId: input.missionId,
772
- item,
773
- placement,
774
- backend: 'fake',
775
- parentPrompt: input.prompt,
776
- worktreePolicy: smokeWorktreePolicy,
777
- zellijSessionName: `sks-${input.missionId}`,
778
- visiblePaneCap: input.zellijVisiblePanes
779
- }),
780
- collectWorker: async (handle) => collectActualNarutoWorker(handle),
781
- enqueueVerification: async () => undefined,
782
- updateDashboard: async () => undefined
358
+ return emit(parsed, result, () => {
359
+ console.log(`Naruto proof ${resolved.id}: ${result.ok ? 'passed' : 'incomplete'}`);
360
+ if (Array.isArray(gate?.blockers) && gate.blockers.length)
361
+ console.log(`Blockers: ${gate.blockers.join(', ')}`);
783
362
  });
784
- return {
785
- ...realActivePool,
786
- status: 'smoke_completed',
787
- runtime_source_of_truth: 'pre_run_smoke_only',
788
- production_runtime_source_of_truth: 'agent-orchestrator-scheduler',
789
- fallback_reason: 'pre_run_smoke_never_owns_production_runtime',
790
- smoke_graph_total_work_items: smokeGraph.total_work_items
791
- };
792
363
  }
793
- function buildNarutoAllocationWorkers(workGraph, roleDistribution, roster) {
794
- const workItems = Array.isArray(workGraph?.work_items) ? workGraph.work_items : [];
795
- const roleByWorkItem = new Map((roleDistribution?.work_item_roles || []).map((row) => [String(row.work_item_id), String(row.role || '')]));
796
- const rosterRows = Array.isArray(roster?.roster) ? roster.roster : [];
797
- const count = Math.max(1, Math.min(Number(roster?.agent_count || rosterRows.length || workItems.length || 1), Math.max(1, workItems.length || 1)));
798
- return Array.from({ length: count }, (_unused, index) => {
799
- const agent = rosterRows[index] || {};
800
- const item = workItems[index % Math.max(1, workItems.length)] || {};
801
- const role = String(agent.naruto_role || agent.role || roleByWorkItem.get(String(item.id || '')) || item.required_role || 'worker');
802
- return {
803
- id: String(agent.id || `clone-${String(index + 1).padStart(3, '0')}`),
804
- role,
805
- lane: narutoAllocationLane(item)
806
- };
364
+ function narutoHelp(parsed) {
365
+ const result = buildNarutoHelpResult();
366
+ return emit(parsed, result, () => {
367
+ console.log('$Naruto Codex official subagent workflow');
368
+ for (const line of result.usage)
369
+ console.log(` ${line}`);
370
+ console.log(`Parent: ${result.parent.model} / ${result.parent.model_reasoning_effort}`);
371
+ const worker = result.agents.worker;
372
+ const expert = result.agents.expert;
373
+ if (worker)
374
+ console.log(`Worker: ${worker.model} / ${worker.model_reasoning_effort}`);
375
+ if (expert)
376
+ console.log(`Expert: ${expert.model} / ${expert.model_reasoning_effort}`);
377
+ console.log(`Default children: ${result.default_requested_subagents}; use explicit --agents N for wider parallelism`);
378
+ console.log(`Nesting: max_depth=${result.max_depth}; subagents must not spawn subagents`);
379
+ console.log('Context: bounded TriWiki attention.use_first anchors with on-demand source hydration');
380
+ console.log('Evidence: SubagentStop is lifecycle-only; completion requires subagent-parent-summary.json with one structured outcome per thread.');
807
381
  });
808
382
  }
809
- function narutoAllocationLane(item) {
810
- const firstPath = String((item?.write_paths || item?.target_paths || item?.readonly_paths || [])[0] || '');
811
- const parts = firstPath.replace(/\\/g, '/').split('/').filter(Boolean);
812
- return parts.slice(0, Math.min(2, parts.length)).join('/') || null;
383
+ async function resolveRunMission(root, parsed, sessionKey = null) {
384
+ if (parsed.missionId && parsed.missionId !== 'latest') {
385
+ const loaded = await loadMission(root, parsed.missionId).catch(() => null);
386
+ return loaded ? { id: parsed.missionId, dir: loaded.dir } : null;
387
+ }
388
+ if (sessionKey) {
389
+ const resolved = await getOrCreateSessionMission(root, {
390
+ mode: 'naruto',
391
+ prompt: parsed.prompt,
392
+ sessionKey,
393
+ selectMissionId: (state) => {
394
+ const route = String(state?.route || state?.route_command || state?.mode || '').replace(/^\$/, '').toUpperCase();
395
+ const sessionMatches = state?._session_key === sessionStateKey(sessionKey);
396
+ return sessionMatches && state?.mission_id && state?.route_closed !== true && route === 'NARUTO'
397
+ ? String(state.mission_id)
398
+ : null;
399
+ }
400
+ });
401
+ return { id: resolved.id, dir: resolved.dir };
402
+ }
403
+ const created = await createMission(root, { mode: 'naruto', prompt: parsed.prompt, sessionKey });
404
+ return { id: created.id, dir: created.dir };
813
405
  }
814
- function summarizeNarutoLocalWorkerResult(localWorker, result) {
815
- const backendCounts = {};
816
- const rows = Array.isArray(result?.results) ? result.results : [];
817
- for (const row of rows) {
818
- const selected = String(row?.backend_router_report?.selected_backend || row?.backend || 'unknown');
819
- backendCounts[selected] = (backendCounts[selected] || 0) + 1;
406
+ async function resolveReadMission(parsed) {
407
+ const root = await sksRoot();
408
+ const explicitId = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : null;
409
+ const sessionKey = detectCodexAppSession() ? codexAppSessionKey() : null;
410
+ let id = explicitId;
411
+ if (!id && sessionKey) {
412
+ const state = await loadStateForSession(root, sessionKey).catch(() => null);
413
+ const route = String(state?.route || state?.route_command || state?.mode || '').replace(/^\$/, '').toUpperCase();
414
+ if (state?._session_key === sessionStateKey(sessionKey) && state?.route_closed !== true && route === 'NARUTO') {
415
+ id = String(state.mission_id || '') || null;
416
+ }
417
+ }
418
+ if (!id && !sessionKey)
419
+ id = await findLatestMission(root, { mode: 'naruto' });
420
+ if (!id)
421
+ return null;
422
+ const loaded = await loadMission(root, id).catch(() => null);
423
+ return loaded ? { root, id, dir: loaded.dir } : null;
424
+ }
425
+ export function parseNarutoArgs(args) {
426
+ const helpRequested = args.includes('--help') || args.includes('-h');
427
+ const validationArgs = [...args];
428
+ const normalized = helpRequested
429
+ ? ['help', ...(args.includes('--json') ? ['--json'] : [])]
430
+ : args;
431
+ const first = normalized[0] && !normalized[0].startsWith('-') ? normalized[0] : '';
432
+ const workersAliasUsed = first === 'workers';
433
+ const actionName = workersAliasUsed ? 'subagents' : first;
434
+ const actions = new Set(['run', 'status', 'subagents', 'proof', 'help']);
435
+ const action = (actions.has(actionName) ? actionName : 'run');
436
+ const explicitAction = actions.has(actionName) || workersAliasUsed;
437
+ const rest = explicitAction ? normalized.slice(1) : normalized;
438
+ const optionArgs = validationArgs.includes('--') ? validationArgs.slice(0, validationArgs.indexOf('--')) : validationArgs;
439
+ const agentsOption = optionValue(optionArgs, '--agents');
440
+ const clonesOption = optionValue(optionArgs, '--clones');
441
+ const maxThreadsOption = optionValue(optionArgs, '--max-threads');
442
+ const missionOption = optionValue(optionArgs, '--mission');
443
+ const missionIdOption = optionValue(optionArgs, '--mission-id');
444
+ const argumentErrors = uniqueStrings([
445
+ ...optionErrors('--agents', agentsOption, true),
446
+ ...optionErrors('--clones', clonesOption, true),
447
+ ...optionErrors('--max-threads', maxThreadsOption, true),
448
+ ...optionErrors('--mission', missionOption, false),
449
+ ...optionErrors('--mission-id', missionIdOption, false),
450
+ ...(agentsOption.present && clonesOption.present ? ['conflicting_options:--agents,--clones'] : []),
451
+ ...booleanOptionErrors(validationArgs),
452
+ ...unknownOptionErrors(validationArgs)
453
+ ]);
454
+ if (REMOVED_LEGACY_SUBCOMMANDS.has(String(first || '').toLowerCase())) {
455
+ argumentErrors.push(`removed_legacy_subcommand:${String(first).toLowerCase()}`);
456
+ }
457
+ const requestedSubagents = strictPositiveInteger(agentsOption.value ?? clonesOption.value);
458
+ const maxThreads = strictPositiveInteger(maxThreadsOption.value);
459
+ const missionFlag = missionOption.value ?? missionIdOption.value;
460
+ const positional = positionalValues(rest);
461
+ const positionalMission = action === 'status' || action === 'subagents' || action === 'proof'
462
+ ? positional.find((value) => value === 'latest' || /^M-/.test(value))
463
+ : undefined;
464
+ const prompt = action === 'run'
465
+ ? positional.join(' ').trim()
466
+ : '';
467
+ const positionalHead = String(positional[0] || '').toLowerCase();
468
+ const subcommandNames = new Set(['run', 'status', 'subagents', 'workers', 'proof', 'help']);
469
+ if (explicitAction && action === 'run' && REMOVED_LEGACY_SUBCOMMANDS.has(positionalHead)) {
470
+ argumentErrors.push(`removed_legacy_subcommand:${positionalHead}`);
471
+ }
472
+ else if (explicitAction && action === 'run' && subcommandNames.has(positionalHead)) {
473
+ argumentErrors.push(`misplaced_subcommand:${positionalHead}`);
820
474
  }
475
+ else if (!explicitAction && REMOVED_LEGACY_SUBCOMMANDS.has(positionalHead)) {
476
+ argumentErrors.push(`removed_legacy_subcommand:${positionalHead}`);
477
+ }
478
+ else if (!explicitAction && subcommandNames.has(positionalHead)) {
479
+ argumentErrors.push(`misplaced_subcommand:${positionalHead}`);
480
+ }
481
+ if (action !== 'run') {
482
+ let missionConsumed = false;
483
+ for (const value of positional) {
484
+ if (!missionConsumed && positionalMission !== undefined && value === positionalMission) {
485
+ missionConsumed = true;
486
+ continue;
487
+ }
488
+ const normalizedValue = String(value || '').toLowerCase();
489
+ if (REMOVED_LEGACY_SUBCOMMANDS.has(normalizedValue)) {
490
+ argumentErrors.push(`removed_legacy_subcommand:${normalizedValue}`);
491
+ }
492
+ else if (subcommandNames.has(normalizedValue)) {
493
+ argumentErrors.push(`misplaced_subcommand:${normalizedValue}`);
494
+ }
495
+ else {
496
+ argumentErrors.push(`unexpected_positional:${value}`);
497
+ }
498
+ }
499
+ }
500
+ if (action === 'run' && !prompt)
501
+ argumentErrors.push('empty_task');
821
502
  return {
822
- ...localWorker,
823
- selected_worker_count: (backendCounts['local-llm'] || 0) + (backendCounts.ollama || 0),
824
- backend_counts: backendCounts
503
+ action,
504
+ prompt,
505
+ requestedSubagents,
506
+ maxThreads,
507
+ missionId: String(missionFlag || positionalMission || 'latest'),
508
+ json: normalized.includes('--json'),
509
+ readOnly: normalized.includes('--readonly') || normalized.includes('--read-only'),
510
+ clonesAliasUsed: clonesOption.present,
511
+ workersAliasUsed,
512
+ unsupportedLegacyFlags: unsupportedLegacyFlags(validationArgs),
513
+ argumentErrors: uniqueStrings(argumentErrors)
825
514
  };
826
515
  }
827
- // Wraps the actual swarm execution so Ctrl-C (SIGINT) or SIGTERM during a
828
- // real Naruto run kills every active worker's process group and the live
829
- // zellij session instead of leaving them orphaned (20차 P1-5) — previously
830
- // nothing in the CLI entrypoint handled these signals at all, so Node's
831
- // default behavior (immediate exit, no cleanup) applied.
832
- async function withNarutoSwarmInterruptCleanup(input, fn) {
833
- let interrupted = false;
834
- const onSignal = (signal) => {
835
- if (interrupted)
836
- return;
837
- interrupted = true;
838
- killAllActiveNarutoWorkers(signal);
839
- const cleanup = input.zellijSessionName
840
- ? runZellij(['kill-session', input.zellijSessionName], { timeoutMs: 2500, optional: true }).catch(() => undefined)
841
- : Promise.resolve(undefined);
842
- cleanup.finally(() => process.exit(signal === 'SIGINT' ? 130 : 143));
843
- };
844
- process.on('SIGINT', onSignal);
845
- process.on('SIGTERM', onSignal);
846
- try {
847
- return await fn();
848
- }
849
- finally {
850
- process.off('SIGINT', onSignal);
851
- process.off('SIGTERM', onSignal);
516
+ function positionalValues(args) {
517
+ const valueFlags = new Set([
518
+ '--agents', '--clones', '--max-threads', '--mission', '--mission-id',
519
+ '--backend', '--concurrency', '--target-active-slots', '--work-items',
520
+ '--write-mode', '--max-write-agents', '--service-tier', '--messages',
521
+ '--parallelism', '--tournament', '--ollama-model', '--local-model-model',
522
+ '--ollama-base-url', '--local-model-base-url', '--scheduler', '--scheduler-mode',
523
+ '--pool', '--pool-size', '--model', '--parent-model', '--worker-model',
524
+ '--expert-model', '--agent-model', '--clone-model', '--reasoning-effort', '--engine'
525
+ ]);
526
+ const booleanFlags = new Set([
527
+ '--json', '--readonly', '--read-only', '--real', '--mock', '--no-open-zellij',
528
+ '--no-zellij', '--attach', '--smoke', '--apply-patches', '--dry-run-patches',
529
+ '--dry-run-patch', '--ollama', '--local-model', '--no-ollama', '--no-local-model',
530
+ '--parallel-write', '--fast', '--no-fast'
531
+ ]);
532
+ const result = [];
533
+ for (let index = 0; index < args.length; index += 1) {
534
+ const arg = args[index] || '';
535
+ if (arg === '--') {
536
+ result.push(...args.slice(index + 1));
537
+ break;
538
+ }
539
+ if (valueFlags.has(arg)) {
540
+ index += 1;
541
+ continue;
542
+ }
543
+ if ([...valueFlags].some((flag) => arg.startsWith(`${flag}=`)))
544
+ continue;
545
+ if (booleanFlags.has(arg))
546
+ continue;
547
+ if (!arg.startsWith('--'))
548
+ result.push(arg);
852
549
  }
550
+ return result;
853
551
  }
854
- // Scales the default clone count (and, via clones*2 below, the default
855
- // work-item count) to apparent task size instead of always requesting
856
- // DEFAULT_NARUTO_CLONES only the *default*; an explicit --clones/--agents
857
- // always wins over this (20차 P2-3). Broad-scope language or an unclear/
858
- // unmatched prompt keeps the full default rather than under-provisioning.
859
- function estimateNarutoDefaultCloneCount(prompt) {
860
- const text = String(prompt || '');
861
- const broadScope = /\b(entire|all files|whole codebase|across the codebase|comprehensive review|throughout the (?:repo|project|codebase))\b|전체|모든\s*파일|코드베이스\s*전체/i.test(text);
862
- if (broadScope || !text.trim())
863
- return DEFAULT_NARUTO_CLONES;
864
- const fileRefs = extractNarutoPromptPaths(text).length;
865
- const wordCount = text.trim().split(/\s+/).filter(Boolean).length;
866
- if (fileRefs <= 1 && wordCount <= 20)
867
- return 2;
868
- if (fileRefs <= 4 && wordCount <= 60)
869
- return 8;
870
- return DEFAULT_NARUTO_CLONES;
871
- }
872
- // Resolves the project's real verification command for the Naruto
873
- // verification DAG. Returns '' (unconfigured) rather than a fabricated
874
- // no-op when the project type can't be detected — see naruto-verification-dag.ts.
875
- function resolveNarutoVerificationCommand(root) {
876
- if (fs.existsSync(path.join(root, 'tsconfig.json'))) {
877
- return 'node node_modules/typescript/bin/tsc -p tsconfig.json --noEmit --incremental';
552
+ function unsupportedLegacyFlags(args) {
553
+ const optionArgs = args.includes('--') ? args.slice(0, args.indexOf('--')) : args;
554
+ const unsupportedBoolean = [
555
+ '--mock', '--ollama', '--local-model', '--no-ollama', '--no-local-model',
556
+ '--apply-patches', '--dry-run-patches', '--dry-run-patch', '--attach', '--smoke',
557
+ '--parallel-write', '--fast', '--no-fast', '--real', '--no-open-zellij',
558
+ '--no-zellij', '--incremental', '--mad'
559
+ ];
560
+ const unsupportedValues = [
561
+ '--backend', '--concurrency', '--target-active-slots', '--work-items', '--write-mode',
562
+ '--max-write-agents', '--service-tier', '--messages', '--parallelism', '--tournament',
563
+ '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url',
564
+ '--scheduler', '--scheduler-mode', '--pool', '--pool-size', '--model', '--parent-model',
565
+ '--worker-model', '--expert-model', '--agent-model', '--clone-model', '--reasoning-effort',
566
+ '--engine'
567
+ ];
568
+ const flags = unsupportedBoolean.filter((flag) => optionArgs.some((arg) => arg === flag || arg.startsWith(`${flag}=`)));
569
+ for (const flag of unsupportedValues) {
570
+ const option = optionValue(optionArgs, flag);
571
+ if (option.present)
572
+ flags.push(option.value === undefined ? flag : `${flag}=${option.value}`);
573
+ }
574
+ return uniqueStrings(flags);
575
+ }
576
+ function optionValue(args, name) {
577
+ const values = [];
578
+ for (let index = 0; index < args.length; index += 1) {
579
+ const arg = args[index] || '';
580
+ if (arg === name) {
581
+ const next = args[index + 1];
582
+ values.push(next && !next.startsWith('--') ? next : undefined);
583
+ continue;
584
+ }
585
+ if (arg.startsWith(`${name}=`))
586
+ values.push(arg.slice(name.length + 1) || undefined);
878
587
  }
879
- return '';
880
- }
881
- function summarizeRegressionProof(workGraph, result) {
882
- const bugfixItems = (Array.isArray(workGraph?.work_items) ? workGraph.work_items : []).filter((item) => String(item?.kind || '') === 'bugfix');
883
- const proofs = collectRegressionProofs(result).filter(validRegressionProof);
884
588
  return {
885
- schema: 'sks.regression-proof-summary.v1',
886
- generated_at: nowIso(),
887
- required: bugfixItems.length > 0,
888
- bugfix_work_item_count: bugfixItems.length,
889
- proof_count: proofs.length,
890
- regression_test_added: bugfixItems.length === 0 ? true : proofs.some((proof) => String(proof.test_file || '').trim().length > 0),
891
- regression_test_failed_before_fix: bugfixItems.length === 0 ? true : proofs.some((proof) => proof.failed_before === true),
892
- regression_test_passed_after_fix: bugfixItems.length === 0 ? true : proofs.some((proof) => proof.passed_after === true),
893
- test_files: [...new Set(proofs.map((proof) => String(proof.test_file || '')).filter(Boolean))],
894
- blockers: bugfixItems.length === 0 || proofs.length > 0 ? [] : ['tdd_evidence_missing']
589
+ present: values.length > 0,
590
+ value: values.at(-1),
591
+ missing: values.some((value) => value === undefined),
592
+ duplicate: values.length > 1
895
593
  };
896
594
  }
897
- function collectRegressionProofs(result) {
898
- const rows = Array.isArray(result?.results) ? result.results : [];
899
- return rows.flatMap((row) => [
900
- row?.regression_proof,
901
- ...(Array.isArray(row?.patch_envelopes) ? row.patch_envelopes.map((envelope) => envelope?.regression_proof) : [])
902
- ]).filter(Boolean);
595
+ function strictPositiveInteger(value) {
596
+ if (value === undefined || value === null || value === '')
597
+ return undefined;
598
+ if (!/^\d+$/.test(String(value)))
599
+ return undefined;
600
+ const parsed = Number(value);
601
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
602
+ }
603
+ function optionErrors(name, option, numeric) {
604
+ const errors = [];
605
+ if (option.missing)
606
+ errors.push(`missing_option_value:${name}`);
607
+ if (option.duplicate)
608
+ errors.push(`duplicate_option:${name}`);
609
+ if (numeric && option.present && option.value !== undefined && strictPositiveInteger(option.value) === undefined) {
610
+ errors.push(`invalid_positive_integer:${name}=${option.value}`);
611
+ }
612
+ return errors;
903
613
  }
904
- function validRegressionProof(proof) {
905
- return Boolean(proof && proof.failed_before === true && proof.passed_after === true && String(proof.test_file || '').trim());
614
+ function unknownOptionErrors(args) {
615
+ const canonical = new Set([
616
+ '--agents', '--clones', '--max-threads', '--mission', '--mission-id',
617
+ '--json', '--readonly', '--read-only', '--help', '-h', '--'
618
+ ]);
619
+ const legacy = new Set([
620
+ '--mock', '--ollama', '--local-model', '--no-ollama', '--no-local-model',
621
+ '--apply-patches', '--dry-run-patches', '--dry-run-patch', '--attach', '--smoke',
622
+ '--parallel-write', '--fast', '--no-fast', '--real', '--no-open-zellij',
623
+ '--no-zellij', '--incremental', '--mad', '--glm', '--backend', '--concurrency',
624
+ '--target-active-slots', '--work-items', '--write-mode', '--max-write-agents',
625
+ '--service-tier', '--messages', '--parallelism', '--tournament', '--ollama-model',
626
+ '--local-model-model', '--ollama-base-url', '--local-model-base-url', '--scheduler',
627
+ '--scheduler-mode', '--pool', '--pool-size', '--model', '--parent-model', '--worker-model',
628
+ '--expert-model', '--agent-model', '--clone-model', '--reasoning-effort', '--engine'
629
+ ]);
630
+ const errors = [];
631
+ const optionArgs = args.includes('--') ? args.slice(0, args.indexOf('--')) : args;
632
+ for (const arg of optionArgs) {
633
+ if (!arg.startsWith('-') || arg === '-')
634
+ continue;
635
+ const name = arg.includes('=') ? arg.slice(0, arg.indexOf('=')) : arg;
636
+ if (!canonical.has(name) && !legacy.has(name))
637
+ errors.push(`unsupported_argument:${name}`);
638
+ }
639
+ return errors;
640
+ }
641
+ function booleanOptionErrors(args) {
642
+ const booleanNames = new Set(['--json', '--readonly', '--read-only', '--help', '-h']);
643
+ const optionArgs = args.includes('--') ? args.slice(0, args.indexOf('--')) : args;
644
+ const errors = [];
645
+ for (const arg of optionArgs) {
646
+ if (!arg.includes('='))
647
+ continue;
648
+ const name = arg.slice(0, arg.indexOf('='));
649
+ if (booleanNames.has(name))
650
+ errors.push(`boolean_option_value_not_supported:${name}`);
651
+ }
652
+ return errors;
906
653
  }
907
- async function narutoStatus(parsed) {
908
- const root = await sksRoot();
909
- const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root, { mode: 'naruto' });
910
- if (!id)
911
- return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'status', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
912
- const { dir } = await loadMission(root, id);
913
- const proof = await readJson(path.join(dir, 'agents', 'agent-proof-evidence.json'), null);
914
- const scheduler = await readJson(path.join(dir, 'agents', 'agent-scheduler-state.json'), null);
915
- const roleDistribution = await readJson(path.join(dir, 'agents', 'naruto-role-distribution.json'), null);
916
- const workGraph = await readJson(path.join(dir, 'agents', 'naruto-work-graph.json'), null);
917
- const governor = await readJson(path.join(dir, 'agents', 'naruto-concurrency-governor.json'), null);
918
- const summary = {
654
+ function blockGlmOverride(json) {
655
+ const result = {
919
656
  schema: NARUTO_RESULT_SCHEMA,
920
- ok: proof !== null,
921
- action: 'status',
922
- mission_id: id,
923
- proof: proof?.status || 'missing',
924
- target_active_slots: scheduler?.target_active_slots ?? null,
925
- max_active_slots: scheduler?.max_active_slots ?? null,
926
- completed: scheduler?.completed_count ?? null,
927
- role_distribution: roleDistribution,
928
- work_graph: workGraph ? {
929
- total_work_items: workGraph.total_work_items,
930
- mixed_work_kinds: workGraph.mixed_work_kinds,
931
- write_allowed_count: workGraph.write_allowed_count,
932
- active_wave_count: Array.isArray(workGraph.active_waves) ? workGraph.active_waves.length : null,
933
- parallel_write_wave_count: Array.isArray(workGraph.active_waves) ? workGraph.active_waves.filter((wave) => Array.isArray(wave.write_paths) && wave.write_paths.length > 1).length : null
934
- } : null,
935
- concurrency_governor: governor
657
+ ok: false,
658
+ status: 'blocked',
659
+ reason: 'naruto_gpt_5_6_family_only_glm_override_forbidden',
660
+ blockers: ['naruto_gpt_5_6_family_only_glm_override_forbidden'],
661
+ hint: 'Use normal sks naruto for the official Codex subagent workflow. The separate legacy GLM route requires sks --mad --glm --naruto.'
936
662
  };
937
- return emit(parsed, summary, () => {
938
- console.log('🍥 Naruto mission: ' + id);
939
- console.log('Proof: ' + summary.proof);
940
- if (summary.target_active_slots !== null)
941
- console.log('Active clones: ' + summary.target_active_slots + ' / max ' + summary.max_active_slots);
942
- if (roleDistribution?.entries)
943
- console.log('Roles: ' + roleDistribution.entries.map((entry) => `${entry.role}:${entry.count}`).join(', '));
944
- });
663
+ process.exitCode = 1;
664
+ if (json)
665
+ console.log(JSON.stringify(result, null, 2));
666
+ else
667
+ console.error(`$Naruto blocked: ${result.reason}. ${result.hint}`);
668
+ return result;
945
669
  }
946
- async function narutoDashboard(parsed) {
947
- const root = await sksRoot();
948
- const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root, { mode: 'naruto' });
949
- if (!id)
950
- return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'dashboard', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
951
- const { dir } = await loadMission(root, id);
952
- const snapshot = await readJson(path.join(dir, 'zellij-dashboard-snapshot.json'), null);
953
- const rightColumnState = await readJson(path.join(dir, 'zellij-right-column-state.json'), null);
954
- const summary = {
670
+ function legacyFlagBlock(flags) {
671
+ return {
955
672
  schema: NARUTO_RESULT_SCHEMA,
956
- ok: Boolean(snapshot || rightColumnState),
957
- action: 'dashboard',
958
- mission_id: id,
959
- snapshot,
960
- right_column_state: rightColumnState,
961
- blockers: snapshot || rightColumnState ? [] : ['naruto_dashboard_missing']
673
+ ok: false,
674
+ status: 'blocked',
675
+ blockers: flags.map((flag) => `removed_legacy_process_flag:${flag}`),
676
+ hint: 'Naruto supports only the Codex official subagent workflow. Use --agents, --max-threads, --read-only, --mission, or --json.'
962
677
  };
963
- return emit(parsed, summary, () => {
964
- console.log('🍥 Naruto dashboard: ' + id);
965
- console.log('Right column: ' + (rightColumnState?.status || 'missing'));
966
- if (snapshot)
967
- console.log('Active/visible/headless/queue: ' + [snapshot.active_workers, snapshot.visible_panes, snapshot.headless_workers, snapshot.queue_depth].join('/'));
968
- });
969
678
  }
970
- async function narutoWorkers(parsed) {
971
- const root = await sksRoot();
972
- const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root, { mode: 'naruto' });
973
- if (!id)
974
- return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'workers', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
975
- const { dir } = await loadMission(root, id);
976
- const swarm = await readJson(path.join(dir, 'agents', 'agent-native-cli-session-swarm.json'), null);
977
- const state = await readJson(path.join(dir, 'zellij-right-column-state.json'), null);
978
- const records = Array.isArray(swarm?.records) ? swarm.records : [];
979
- const summary = {
679
+ function argumentBlock(errors) {
680
+ return {
980
681
  schema: NARUTO_RESULT_SCHEMA,
981
- ok: Boolean(swarm || state),
982
- action: 'workers',
983
- mission_id: id,
984
- active: records.filter((row) => row.status === 'running' || row.status === 'launching').length,
985
- completed: records.filter((row) => row.status === 'closed').length,
986
- failed: records.filter((row) => row.status === 'failed').length,
987
- visible_worker_panes: state?.visible_worker_panes || [],
988
- headless_workers: state?.headless_workers || [],
989
- records,
990
- blockers: swarm || state ? [] : ['naruto_worker_records_missing']
682
+ ok: false,
683
+ status: 'blocked',
684
+ reason: 'invalid_naruto_arguments',
685
+ blockers: errors.map((error) => `invalid_naruto_argument:${error}`),
686
+ hint: 'Provide a non-empty quoted task and valid positive integers for --agents/--max-threads. Use only official Naruto options.'
991
687
  };
992
- return emit(parsed, summary, () => {
993
- console.log('🍥 Naruto workers: ' + id);
994
- console.log(`Active ${summary.active} · completed ${summary.completed} · failed ${summary.failed} · visible ${summary.visible_worker_panes.length} · headless ${summary.headless_workers.length}`);
995
- });
996
688
  }
997
- async function narutoProof(parsed) {
998
- const root = await sksRoot();
999
- const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root, { mode: 'naruto' });
1000
- if (!id)
1001
- return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'proof', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
1002
- const summary = await buildRuntimeProofSummary(root, id, { maxMessages: parsed.messages });
1003
- return emit(parsed, { ...summary, action: 'proof' }, () => {
1004
- console.log(renderRuntimeProofSummary(summary));
1005
- });
1006
- }
1007
- async function narutoHelp(parsed) {
1008
- const help = {
689
+ function missingMission(parsed, action) {
690
+ return emit(parsed, {
1009
691
  schema: NARUTO_RESULT_SCHEMA,
1010
- ok: true,
1011
- action: 'help',
1012
- mode: 'NARUTO',
1013
- description: 'Shadow Clone Swarm: fan out up to ' + MAX_NARUTO_AGENT_COUNT + ' parallel clone sessions.',
1014
- usage: [
1015
- 'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake|ollama|local-llm] [--local-model|--ollama|--no-ollama] [--work-items N] [--tournament 2-4] [--write-mode parallel|serial|off] [--apply-patches] [--dry-run-patches] [--real] [--readonly] [--json]',
1016
- 'sks naruto status [--mission <id>] [--json]',
1017
- 'sks naruto proof latest [--messages 20] [--json]'
1018
- ],
1019
- defaults: { clones: DEFAULT_NARUTO_CLONES, max_clones: MAX_NARUTO_AGENT_COUNT, backend: 'codex-sdk' }
1020
- };
1021
- return emit(parsed, help, () => {
1022
- console.log('🍥 $Naruto — Shadow Clone Swarm (影分身)');
1023
- console.log(help.description);
1024
- for (const line of help.usage)
1025
- console.log(' ' + line);
1026
- });
1027
- }
1028
- function parseNarutoArgs(args = []) {
1029
- if (hasFlag(args, '--help') || hasFlag(args, '-h'))
1030
- args = ['help', ...args.filter((arg) => arg !== '--help' && arg !== '-h')];
1031
- const first = args[0] && !String(args[0]).startsWith('--') ? String(args[0]) : '';
1032
- const actions = new Set(['run', 'status', 'help', 'dashboard', 'workers', 'proof']);
1033
- const action = (actions.has(first) ? first : 'run');
1034
- const rest = action === first ? args.slice(1) : args;
1035
- const json = hasFlag(args, '--json');
1036
- // Computed early (normally derived further down) so an explicit --clones/
1037
- // --agents can still override this exactly as before, but the *default*
1038
- // scales with apparent task size instead of unconditionally requesting
1039
- // DEFAULT_NARUTO_CLONES for every run, including a one-line typo fix
1040
- // (20차 P2-3). --work-items defaults to clones*2 below, so this alone
1041
- // scales that down proportionally too.
1042
- const promptSizingValueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--max-write-agents', '--service-tier', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url', '--parallelism', '--messages', '--tournament']);
1043
- const promptForSizing = positionalArgs(rest, promptSizingValueFlags).join(' ').trim();
1044
- const requestedClones = Number(readOption(args, '--clones', readOption(args, '--agents', String(estimateNarutoDefaultCloneCount(promptForSizing)))));
1045
- const clones = clampClones(requestedClones);
1046
- const workItemsExplicit = hasOption(args, '--work-items');
1047
- const workItems = clampWorkItems(Number(readOption(args, '--work-items', clones * 2)), clones);
1048
- const concurrency = normalizeConcurrency(readOption(args, '--concurrency', readOption(args, '--target-active-slots', null)), clones);
1049
- const useOllamaProtocol = hasFlag(args, '--ollama');
1050
- const useLocalModel = hasFlag(args, '--local-model');
1051
- const useOllama = useOllamaProtocol || useLocalModel;
1052
- const noOllama = hasFlag(args, '--no-ollama') || hasFlag(args, '--no-local-model');
1053
- const backendExplicit = hasOption(args, '--backend') || useOllama || noOllama;
1054
- const defaultBackend = hasFlag(args, '--mock')
1055
- ? 'fake'
1056
- : useLocalModel && !noOllama
1057
- ? 'local-llm'
1058
- : useOllamaProtocol && !noOllama
1059
- ? 'ollama'
1060
- : 'codex-sdk';
1061
- const backend = String(readOption(args, '--backend', defaultBackend));
1062
- const mock = hasFlag(args, '--mock') || backend === 'fake';
1063
- const real = hasFlag(args, '--real');
1064
- const readonly = hasFlag(args, '--readonly') || hasFlag(args, '--read-only');
1065
- const writeModeRaw = String(readOption(args, '--write-mode', hasFlag(args, '--parallel-write') ? 'parallel' : '') || '');
1066
- const writeMode = (['proof-safe', 'parallel', 'serial', 'off'].includes(writeModeRaw) ? writeModeRaw : null);
1067
- const applyPatches = hasFlag(args, '--apply-patches');
1068
- const dryRunPatches = hasFlag(args, '--dry-run-patches') || hasFlag(args, '--dry-run-patch');
1069
- const maxWriteAgents = Math.max(0, Math.floor(Number(readOption(args, '--max-write-agents', '0')) || 0));
1070
- const explicitServiceTier = String(readOption(args, '--service-tier', '') || '');
1071
- const requestedServiceTier = normalizeServiceTier(explicitServiceTier, null) || undefined;
1072
- // Naruto clones always run in the fast service tier. The route-level skill and
1073
- // release docs explicitly treat --no-fast / standard as non-honored for clones.
1074
- const serviceTier = action === 'run' ? 'fast' : requestedServiceTier;
1075
- const fastMode = action === 'run'
1076
- ? true
1077
- : hasFlag(args, '--no-fast') || requestedServiceTier === 'standard'
1078
- ? false
1079
- : hasFlag(args, '--fast')
1080
- ? true
1081
- : undefined;
1082
- const noFast = action === 'run' ? false : hasFlag(args, '--no-fast');
1083
- const positionalMission = action === 'dashboard' || action === 'workers' || action === 'status' || action === 'proof'
1084
- ? positionalArgs(rest, new Set()).find((arg) => /^latest$|^M-/.test(arg))
1085
- : null;
1086
- const missionId = String(readOption(args, '--mission', readOption(args, '--mission-id', positionalMission || 'latest')));
1087
- const ollamaModel = String(readOption(args, '--ollama-model', readOption(args, '--local-model-model', '')) || '') || null;
1088
- const ollamaBaseUrl = String(readOption(args, '--ollama-base-url', readOption(args, '--local-model-base-url', '')) || '') || null;
1089
- const noOpenZellij = hasFlag(args, '--no-open-zellij') || hasFlag(args, '--no-zellij');
1090
- const attach = hasFlag(args, '--attach');
1091
- const smoke = hasFlag(args, '--smoke');
1092
- const parallelism = normalizeParallelism(readOption(args, '--parallelism', 'safe'));
1093
- const messages = normalizeMessages(readOption(args, '--messages', '8'));
1094
- const tournament = normalizeTournament(readOption(args, '--tournament', '0'));
1095
- const prompt = promptForSizing || 'Naruto shadow clone swarm run';
1096
- return { action, prompt, clones, workItems, workItemsExplicit, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, json, missionId, noOpenZellij, attach, smoke, parallelism, messages, tournament };
1097
- }
1098
- function normalizeParallelism(value) {
1099
- const text = String(value || 'safe').toLowerCase();
1100
- if (text === 'safe' || text === 'balanced' || text === 'extreme')
1101
- return text;
1102
- return 'safe';
1103
- }
1104
- function normalizeMessages(value) {
1105
- const parsed = Number(value);
1106
- return Math.max(0, Math.min(100, Math.floor(Number.isFinite(parsed) ? parsed : 8)));
1107
- }
1108
- function normalizeTournament(value) {
1109
- const parsed = Number(value);
1110
- if (!Number.isFinite(parsed) || parsed < 2)
1111
- return 0;
1112
- return Math.max(2, Math.min(4, Math.floor(parsed)));
1113
- }
1114
- async function writeNarutoArtifacts(ledgerRoot, artifacts) {
1115
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-work-graph.json'), artifacts.workGraph);
1116
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-role-distribution.json'), artifacts.roleDistribution);
1117
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-concurrency-governor.json'), artifacts.governor);
1118
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-active-pool.json'), {
1119
- schema: 'sks.naruto-active-pool.v1',
1120
- ok: null,
1121
- status: 'not_computed',
1122
- reason: 'simulated_active_pool_removed_from_production_path_20cha_p3_5',
1123
- runtime_source_of_truth: 'agent-orchestrator-scheduler',
1124
- see: 'naruto-real-active-pool.json'
1125
- });
1126
- if (artifacts.realActivePool)
1127
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-real-active-pool.json'), artifacts.realActivePool);
1128
- if (artifacts.allocationPolicy)
1129
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-allocation-policy.json'), artifacts.allocationPolicy);
1130
- if (artifacts.rebalancePolicy)
1131
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-rebalance-policy.json'), artifacts.rebalancePolicy);
1132
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-verification-dag.json'), artifacts.verificationDag);
1133
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-gpt-final-pack.json'), artifacts.gptFinalPack);
1134
- await writeJsonAtomic(path.join(ledgerRoot, 'naruto-zellij-dashboard.json'), artifacts.zellijDashboard);
1135
- await writeJsonAtomic(path.join(ledgerRoot, 'prompt-placeholder-guard.json'), artifacts.placeholderGuard);
1136
- if (artifacts.gitWorktreeCapability)
1137
- await writeJsonAtomic(path.join(ledgerRoot, 'git-worktree-capability.json'), artifacts.gitWorktreeCapability);
1138
- }
1139
- function clampClones(value) {
1140
- if (!Number.isFinite(value) || value < 1)
1141
- return DEFAULT_NARUTO_CLONES;
1142
- return Math.min(MAX_NARUTO_AGENT_COUNT, Math.floor(value));
1143
- }
1144
- function clampWorkItems(value, clones) {
1145
- if (!Number.isFinite(value) || value < 1)
1146
- return clones;
1147
- return Math.floor(value);
1148
- }
1149
- function normalizeConcurrency(value, clones) {
1150
- if (value == null || value === '')
1151
- return null;
1152
- const parsed = Number(value);
1153
- if (!Number.isFinite(parsed) || parsed < 1)
1154
- return null;
1155
- return Math.min(Math.floor(parsed), clones, MAX_NARUTO_AGENT_COUNT);
1156
- }
1157
- function hasFlag(args, flag) {
1158
- return args.includes(flag);
692
+ ok: false,
693
+ action,
694
+ status: 'missing_mission'
695
+ }, () => console.log('No Naruto mission found.'));
1159
696
  }
1160
- function readOption(args, name, fallback) {
1161
- const index = args.indexOf(name);
1162
- if (index >= 0 && args[index + 1] && !String(args[index + 1]).startsWith('--'))
1163
- return args[index + 1];
1164
- const prefixed = args.find((arg) => String(arg).startsWith(name + '='));
1165
- return prefixed ? prefixed.slice(name.length + 1) : fallback;
1166
- }
1167
- function hasOption(args, name) {
1168
- return args.includes(name) || args.some((arg) => String(arg).startsWith(name + '='));
1169
- }
1170
- function positionalArgs(args, valueFlags) {
1171
- const out = [];
1172
- for (let i = 0; i < args.length; i += 1) {
1173
- const arg = String(args[i]);
1174
- if (arg.startsWith('--')) {
1175
- if (valueFlags.has(arg) && args[i + 1] && !String(args[i + 1]).startsWith('--'))
1176
- i += 1;
1177
- continue;
1178
- }
1179
- out.push(arg);
1180
- }
1181
- return out;
1182
- }
1183
- function emit(parsed, result, text) {
1184
- if (parsed.json) {
697
+ function emit(parsed, result, human, failed = false) {
698
+ if (failed)
699
+ process.exitCode = 1;
700
+ if (parsed.json)
1185
701
  console.log(JSON.stringify(result, null, 2));
1186
- return result;
1187
- }
1188
- text();
702
+ else
703
+ human();
1189
704
  return result;
1190
705
  }
1191
- async function resolveNarutoLocalWorkerMode(parsed) {
1192
- const configInput = {
1193
- ollamaEnabled: parsed.ollamaEnabled,
1194
- model: parsed.ollamaModel,
1195
- baseUrl: parsed.ollamaBaseUrl
1196
- };
1197
- if (parsed.backend === 'ollama')
1198
- configInput.backend = 'ollama';
1199
- const config = await resolveOllamaWorkerConfig(configInput).catch(() => null);
1200
- const policy = classifyOllamaWorkerSlice({
1201
- id: 'naruto-local-worker-probe',
1202
- role: parsed.readonly ? 'collector' : 'implementer',
1203
- description: parsed.prompt,
1204
- write_paths: parsed.readonly ? [] : ['<lease-scoped-worker-path>']
1205
- }, { route: NARUTO_ROUTE, agent: { role: parsed.readonly ? 'collector' : 'implementer' } });
1206
- const localAutoSelectCandidate = parsed.backend === 'codex-sdk'
1207
- && parsed.backendExplicit !== true
1208
- && parsed.noOllama !== true
1209
- && config?.ok === true
1210
- && config.enabled === true
1211
- && policy.ok === true;
1212
- return {
1213
- schema: 'sks.naruto-local-worker-mode.v1',
1214
- enabled: config?.enabled === true,
1215
- provider: config?.provider || 'ollama',
1216
- model: config?.model || null,
1217
- requested_backend: parsed.backend,
1218
- backend_explicit: parsed.backendExplicit,
1219
- auto_select_eligible: false,
1220
- worker_only: true,
1221
- no_strategy_planning_design: true,
1222
- policy,
1223
- blockers: [
1224
- ...(config?.blockers || (config ? [] : ['ollama_worker_config_unavailable'])),
1225
- ...(policy.blockers || []),
1226
- ...(localAutoSelectCandidate ? ['naruto_gpt_5_6_family_only_local_auto_select_forbidden'] : []),
1227
- ...(parsed.backendExplicit ? ['backend_explicit'] : []),
1228
- ...(parsed.noOllama ? ['no_ollama_requested'] : [])
1229
- ]
1230
- };
706
+ function renderRunResult(result) {
707
+ console.log(`$Naruto ${result.status}: ${result.mission_id}`);
708
+ console.log(`Official subagents: requested ${result.requested_subagents}, max threads ${result.max_threads}`);
709
+ console.log(`Started/completed/failed: ${result.started_subagents}/${result.completed_subagents}/${result.failed_subagents}`);
710
+ if (result.status === 'delegation_context_ready')
711
+ console.log('Continue in the current Codex parent and wait for every requested subagent before summarizing.');
712
+ if (Array.isArray(result.blockers) && result.blockers.length)
713
+ console.log(`Blockers: ${result.blockers.join(', ')}`);
714
+ }
715
+ function renderStatusResult(result) {
716
+ console.log(`Naruto ${result.action || 'status'}: ${result.mission_id}`);
717
+ console.log(`Started/completed/failed: ${result.started_subagents || 0}/${result.completed_subagents || 0}/${result.failed_subagents || 0}`);
718
+ }
719
+ function uniqueStrings(values) {
720
+ return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
1231
721
  }
1232
722
  //# sourceMappingURL=naruto-command.js.map