sneakoscope 2.0.16 → 2.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +23 -30
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/cli/command-registry.js +1 -1
  8. package/dist/commands/doctor.js +39 -1
  9. package/dist/commands/proof.js +21 -0
  10. package/dist/commands/zellij-slot-pane.js +7 -1
  11. package/dist/core/agents/agent-effort-policy.js +7 -1
  12. package/dist/core/agents/agent-orchestrator.js +3 -1
  13. package/dist/core/agents/agent-scheduler.js +14 -1
  14. package/dist/core/agents/native-cli-session-swarm.js +11 -7
  15. package/dist/core/agents/native-cli-worker.js +56 -7
  16. package/dist/core/agents/parallel-runtime-proof.js +68 -9
  17. package/dist/core/agents/runtime-proof-summary.js +75 -0
  18. package/dist/core/codex-app/codex-app-handoff.js +77 -0
  19. package/dist/core/codex-control/codex-0138-capability.js +64 -0
  20. package/dist/core/codex-control/codex-model-capabilities.js +41 -0
  21. package/dist/core/codex-control/codex-sdk-config-policy.js +1 -1
  22. package/dist/core/codex-control/codex-task-runner.js +1 -1
  23. package/dist/core/codex-plugins/codex-plugin-json.js +152 -0
  24. package/dist/core/commands/mad-sks-command.js +4 -0
  25. package/dist/core/commands/naruto-command.js +20 -4
  26. package/dist/core/commands/qa-loop-command.js +111 -4
  27. package/dist/core/commands/team-command.js +6 -311
  28. package/dist/core/commands/team-legacy-observe-command.js +182 -0
  29. package/dist/core/db-safety.js +15 -0
  30. package/dist/core/doctor/codex-0138-doctor.js +104 -0
  31. package/dist/core/doctor/doctor-readiness-matrix.js +11 -0
  32. package/dist/core/effort-orchestrator.js +9 -0
  33. package/dist/core/feature-registry.js +4 -2
  34. package/dist/core/fsx.js +1 -1
  35. package/dist/core/hooks-runtime.js +38 -4
  36. package/dist/core/image/image-artifact-path-contract.js +99 -0
  37. package/dist/core/image-ux-review/imagegen-adapter.js +24 -3
  38. package/dist/core/init.js +1 -0
  39. package/dist/core/mad-db/mad-db-capability.js +9 -1
  40. package/dist/core/mad-db/mad-db-result-lifecycle.js +207 -0
  41. package/dist/core/mcp/mcp-plugin-inventory.js +29 -0
  42. package/dist/core/mcp/mcp-server-policy.js +24 -0
  43. package/dist/core/qa-loop/qa-loop-budget-policy.js +37 -0
  44. package/dist/core/qa-loop.js +28 -2
  45. package/dist/core/release/release-gate-affected-selector.js +47 -5
  46. package/dist/core/release/release-gate-dag.js +5 -1
  47. package/dist/core/release/release-gate-scheduler.js +2 -1
  48. package/dist/core/routes.js +3 -1
  49. package/dist/core/usage/codex-account-usage.js +78 -0
  50. package/dist/core/version.js +1 -1
  51. package/dist/core/zellij/zellij-slot-column-anchor.js +16 -7
  52. package/dist/core/zellij/zellij-slot-pane-renderer.js +92 -1
  53. package/dist/core/zellij/zellij-slot-telemetry.js +29 -6
  54. package/dist/core/zellij/zellij-ui-mode.js +12 -2
  55. package/dist/scripts/prepublish-release-check-or-fast.js +3 -3
  56. package/dist/scripts/release-gate-existence-audit.js +5 -1
  57. package/dist/scripts/release-speed-summary.js +22 -2
  58. package/package.json +38 -4
  59. package/schemas/agents/parallel-runtime-proof.schema.json +31 -0
  60. package/schemas/codex-app/codex-app-handoff.schema.json +20 -0
  61. package/schemas/codex-plugins/codex-plugin-inventory.schema.json +32 -0
  62. package/schemas/image/image-artifact-path-contract.schema.json +32 -0
  63. package/schemas/usage/codex-account-usage.schema.json +27 -0
package/README.md CHANGED
@@ -16,43 +16,34 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
16
16
 
17
17
  ## Current Release
18
18
 
19
- SKS **2.0.16** is the real parallelism closure release. It proves Naruto/agent runtime concurrency with PID, launch overlap, wall-clock speedup, active/headless worker, model-call, worktree allocation, and incremental Zellij telemetry evidence while keeping Naruto as the execution SSOT.
19
+ SKS **2.0.18** is the Codex 0.138 integration release: capability artifacts, Desktop `/app` handoff, plugin JSON inventory, image saved-path contracts, model-advertised effort order, account usage budget policy, and startup doctor checks.
20
20
 
21
21
  What changed:
22
22
 
23
- - Zellij slot panes distinguish `slot_status_renderer` panes from Codex worker panes, and the first visible worker now stacks downward below the `SLOTS` anchor with real geometry proof available under `real-check`.
24
- - Naruto allocation owners now flow into work graph items, scheduler slices, queue ownership, and worker runtime proof; inactive owners are rebalanced and active write conflicts stay out of concurrent execution.
25
- - Naruto active-pool checks now exercise actual child-worker spawn/result collection paths while production source-of-truth stays with the agent orchestrator scheduler.
26
- - Worktree candidate output requires GPT Final approval before apply; GPT `modified` output replaces candidate patches and GPT `rejected` blocks apply.
27
- - Visible Zellij reservations are capped before pane launch so concurrent worker starts cannot over-open the right column.
28
- - Git worktree integration now proves the primary repo receives validated worktree diffs, with rollback hash evidence recorded around the apply step.
29
- - Agent role config repair detects stale generated role files and rewrites structured GPT-5.5-compatible configs atomically.
30
- - Release gates now include slot-only UI, compact slot renderer, headless overflow, role-config repair, worktree primary-runtime, real active-pool, extreme real parallelism, and real right-column geometry checks.
31
- - Release audit, dynamic selection, and stamp hashing use `release-gates.v2.json` as the manifest source of truth.
32
- - Git capability checks detect repo roots, Git dirs, worktree support, and safe cache roots while blocking in-repo worktree roots unless `SKS_ALLOW_IN_REPO_WORKTREES=1` is explicit.
33
- - Worker worktree allocation creates isolated branches/paths under `$SKS_WORKTREE_ROOT`, `$XDG_CACHE_HOME/sks/worktrees`, or `~/.cache/sks/worktrees`; main checkouts stay untouched until integration.
34
- - Product Design plugin readiness now checks both local and remote Codex App catalogs, auto-installs the remote plugin when needed, and records the installed/enabled skill surface.
35
- - UI/design/PPT runtime routes prefer Product Design for research, ideation, audit, design QA, prototype, URL-to-code, image-to-code, share, and user-context steps.
36
- - Naruto read-only runs force write mode off, propagate no-patch reasons through worker proof, and skip changed-file lease checks when no write-capable patch envelope exists.
37
- - `codex-sdk` is the default native agent backend for Team, QA, Research, Naruto, MAD-SKS, and direct agent runs, with every runtime task entering through `runCodexTask`.
38
- - Codex App UI snapshot, preservation, clobber guard, and doctor repair checks protect host-owned Fast UI/profile settings around `sks --mad`.
39
- - Provider context resolves `openai`, `codex-lb`, and `codex-app` with badge/fallback surfaces while avoiding private Codex App UI mutation.
40
- - UltraRouter writes `ultra-router-proof.json` decisions with tier, scores, hard filters, cache state, and cheapest-good-enough profile selection.
41
- - Reliability Shield writes `codex-reliability-shield.json` for empty-result retry, stream-idle blocking, tool-result repair, and no-CoT keepalive heartbeats.
42
- - Raw `codex exec` execution is removed from runtime fallback paths; explicit legacy requests are blocked with `legacy_codex_exec_runtime_removed`.
43
- - SDK runs write `codex-control-proof.json`, `codex-thread-registry.json`, `codex-sdk-events.jsonl`, and schema-validated worker results.
44
- - Zellij proof now links `pane_id`, `slot_id`, `generation_index`, `session_id`, `sdk_thread_id`, provider, and `service_tier`.
45
- - Production runtime scripts are TypeScript source under `src/scripts` and build to `dist/scripts`; Python remains optional diagnostics under `pytools`.
46
- - Release gates include `codex-control:*`, `ultra-router:*`, `codex-sdk:*`, Codex App Fast UI preservation, provider badges, Zellij spawn-on-demand, slot/pane binding, release truth, and real smoke checks.
47
- - Research synthesis is now evidence-bound in non-mock runs; deterministic report generation is mock/fallback only.
48
- - Research quality checks reject repeated paragraphs, template-like prose, low source density, low claim density, and thin implementation sections.
49
- - Research handoffs now include context, key claims, evidence summary, blueprint sections, parallel work items, acceptance tests, rollback, and source appendix for `$Team` or `$Naruto`.
23
+ - `sks doctor` now reports Codex 0.138 feature readiness, plugin JSON inventory, candidate-only remote MCP servers, unavailable app templates, and repairable plugin discovery cache state.
24
+ - QA-LOOP can write a Codex Desktop `/app` handoff artifact with `--app-handoff` or require it with `--app-handoff-required`; this never substitutes for Codex Chrome Extension web UI evidence.
25
+ - Zellij slot panes and the right-column anchor surface pending QA `/app` handoffs so desktop review is visible during long native-agent runs.
26
+ - Codex plugin detail JSON is normalized into `.sneakoscope/codex-plugin-inventory.json`, and plugin-provided remote MCP servers remain candidate-only until explicitly enabled under DB/Mad-DB safety policy.
27
+ - Imagegen and QA image flows write `image-artifact-path-contract.json` with exact saved file paths and follow-up edit hints.
28
+ - Effort routing now understands the fallback order `minimal < low < medium < high < xhigh`, records model capability, and escalates QA effort after repeated failures.
29
+ - Codex account token usage can be recorded from an app-server usage endpoint, and QA budget policy reduces remote concurrency near limits while preserving GPT final review.
30
+ - Naruto final pass status now depends on the parallel runtime proof, and Mad-DB post-tool lifecycle recording handles MCP `isError` failures.
50
31
 
51
32
  Quick checks:
52
33
 
53
34
  ```bash
54
35
  npm run typecheck
55
36
  npm run build
37
+ npm run codex:0138-capability
38
+ npm run codex-sdk:version-compat
39
+ npm run codex-app:handoff
40
+ npm run codex-plugin:inventory
41
+ npm run qa-loop:app-handoff
42
+ npm run image:artifact-path-contract
43
+ npm run codex:effort-order
44
+ npm run codex:account-usage
45
+ npm run codex:0138-doctor
46
+ npm run doctor:codex-0138-fix
56
47
  npm run codex-control:capability
57
48
  npm run codex-control:structured-output
58
49
  npm run codex-control:event-stream-ledger
@@ -613,7 +604,9 @@ SKS_HERMES=1 sks status --json
613
604
 
614
605
  Use these inside Codex App or another agent prompt. They are prompt commands, not terminal commands.
615
606
 
616
- Common prompts: `$Team`, `$From-Chat-IMG`, `$with-local-llm-on`, `$with-local-llm-off`, `$DFix`, `$Answer`, `$SKS`, `$QA-LOOP`, `$PPT`, `$Computer-Use`/`$CU`, `$Goal`, `$Research`, `$AutoResearch`, `$DB`, `$MAD-SKS`, `$GX`, `$Wiki`, and `$Help`.
607
+ Common prompts: `$Team`, `$From-Chat-IMG`, `$with-local-llm-on`, `$with-local-llm-off`, `$DFix`, `$Answer`, `$SKS`, `$QA-LOOP`, `$PPT`, `$Computer-Use`/`$CU`, `$Goal`, `$Research`, `$AutoResearch`, `$DB`, `$MAD-SKS`, `$MAD-DB`, `$GX`, `$Wiki`, and `$Help`.
608
+
609
+ `$MAD-DB` is the prompt-visible Mad-DB alias for one-cycle DB break-glass work. It maps to the same guarded MAD-SKS permission route, while the terminal lifecycle remains `sks mad-db status|enable|revoke`; it is not a permanent DB unlock and catastrophic DB safeguards remain active.
617
610
 
618
611
  ## Common Workflows
619
612
 
@@ -759,7 +752,7 @@ npm run release:check
759
752
  npm run publish:dry
760
753
  ```
761
754
 
762
- `release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The DAG preserves the 1.18 baseline gates and adds Codex 0.136 compatibility, inherited Codex 0.135/0.134 runner truth, patch swarm runtime truth, transaction journaling, serial conflict rebase, strict strategy-to-patch proof, rollback command proof, Native CLI Session Swarm 5/10/20-process proof, Real Worker Backend Router proof, Codex child overlap proof, model-authored patch-envelope separation, Zellij layout/pane/screen/socket-dir proof, no-subagent-scaling proof, Fast mode default/worker/Codex/MAD propagation proof, Appshots attachment provenance, MCP runtime overlap evidence, task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route blackboxes, scheduler proof hardening, Source Intelligence propagation, Goal mode propagation checks, slot telemetry, update notice, MAD-DB, and Naruto SSOT gates. Broader live gates remain explicit scripts such as `release:real-check`; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. npm's `prepublishOnly` and `npm run publish:dry` both run `release:check:full`, verify the fresh stamp, and then run provenance/registry checks before the real or dry-run publish step.
755
+ `release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The DAG preserves the 1.18 baseline gates and adds Codex 0.136 compatibility, inherited Codex 0.135/0.134 runner truth, patch swarm runtime truth, transaction journaling, serial conflict rebase, strict strategy-to-patch proof, rollback command proof, Native CLI Session Swarm 5/10/20-process proof, Real Worker Backend Router proof, Codex child overlap proof, model-authored patch-envelope separation, Zellij layout/pane/screen/socket-dir proof, no-subagent-scaling proof, Fast mode default/worker/Codex/MAD propagation proof, Appshots attachment provenance, MCP runtime overlap evidence, task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route blackboxes, scheduler proof hardening, Source Intelligence propagation, Goal mode propagation checks, slot telemetry, update notice, MAD-DB, and Naruto SSOT gates. Broader live gates remain explicit scripts such as `release:real-check`; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. `npm run publish:dry` runs `release:check:full`, verifies the fresh stamp, and then performs provenance/registry and npm dry-run checks. npm's `prepublishOnly` uses `prepublish-release-check-or-fast` to accept that current stamp before the real publish; if the stamp is missing or stale, it runs `release:check:full` once before continuing.
763
756
 
764
757
  Version bumps are manual. Run `sks versioning bump` only when preparing release metadata; SKS will not create `.git/hooks/pre-commit` or auto-bump during ordinary commits.
765
758
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "2.0.16"
79
+ version = "2.0.18"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "2.0.16"
3
+ version = "2.0.18"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 2.0.16"),
7
+ Some("--version") => println!("sks-rs 2.0.18"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "2.0.16",
5
- "source_digest": "cafc32cad87d3b6c7aeb0ec0e8e56258f830b35e71fd919440a8c1b95b78432a",
6
- "source_file_count": 2187,
7
- "built_at_source_time": 1780927200051
4
+ "package_version": "2.0.18",
5
+ "source_digest": "3c246288e22bf5f29b5ba20a2a05b3f15a6afb9e6d680c531f7e78ef996c8c33",
6
+ "source_file_count": 2238,
7
+ "built_at_source_time": 1780992531184
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '2.0.16';
2
+ const FAST_PACKAGE_VERSION = '2.0.18';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -119,7 +119,7 @@ export const COMMANDS = {
119
119
  commit: entry('stable', 'Create a simple git commit', 'dist/commands/commit.js', directCommand(() => import('../commands/commit.js'), 'dist/commands/commit.js')),
120
120
  'commit-and-push': entry('stable', 'Create a simple git commit and push', 'dist/commands/commit-and-push.js', directCommand(() => import('../commands/commit-and-push.js'), 'dist/commands/commit-and-push.js')),
121
121
  dfix: entry('stable', 'Run DFix diagnose/plan/patch/verify loop', 'dist/core/commands/dfix-command.js', commandArgsCommand(() => import('../core/commands/dfix-command.js'), 'dfixCommand', 'dist/core/commands/dfix-command.js')),
122
- team: entry('beta', 'Deprecated Team alias; create redirects to Naruto, observe legacy Team missions', 'dist/core/commands/team-command.js', argsCommand(() => import('../core/commands/team-command.js'), 'team', 'dist/core/commands/team-command.js')),
122
+ team: entry('beta', 'Deprecated alias. New execution redirects to Naruto; legacy observe/watch remains.', 'dist/core/commands/team-command.js', argsCommand(() => import('../core/commands/team-command.js'), 'team', 'dist/core/commands/team-command.js')),
123
123
  agent: entry('beta', 'Run native multi-session agent missions', 'dist/core/commands/agent-command.js', argsCommand(() => import('../core/commands/agent-command.js'), 'agentCommand', 'dist/core/commands/agent-command.js')),
124
124
  'with-local-llm': entry('beta', 'Enable or inspect local Ollama worker backend', 'dist/core/commands/local-model-command.js', argsCommand(() => import('../core/commands/local-model-command.js'), 'localModelCommand', 'dist/core/commands/local-model-command.js')),
125
125
  naruto: entry('labs', 'Run $Naruto shadow-clone swarm (up to 100 parallel sessions)', 'dist/core/commands/naruto-command.js', argsCommand(() => import('../core/commands/naruto-command.js'), 'narutoCommand', 'dist/core/commands/naruto-command.js')),
@@ -19,6 +19,10 @@ import { repairCodexAppFastUi } from '../core/codex-app/codex-app-fast-ui-repair
19
19
  import { resolveProviderContext } from '../core/provider/provider-context.js';
20
20
  import { readLocalModelConfig } from '../core/agents/ollama-worker-config.js';
21
21
  import { repairAgentRoleConfigs } from '../core/agents/agent-role-config.js';
22
+ import { writeCodex0138CapabilityArtifacts } from '../core/codex-control/codex-0138-capability.js';
23
+ import { runCodex0138Doctor } from '../core/doctor/codex-0138-doctor.js';
24
+ import { writeCodexPluginInventoryArtifacts, pluginAppTemplatePolicy } from '../core/codex-plugins/codex-plugin-json.js';
25
+ import { writeMcpPluginInventoryArtifacts } from '../core/mcp/mcp-plugin-inventory.js';
22
26
  export async function run(_command, args = []) {
23
27
  const doctorFix = flag(args, '--fix');
24
28
  let setupRepair = null;
@@ -167,6 +171,13 @@ export async function run(_command, args = []) {
167
171
  : null;
168
172
  const { detectImagegenCapability } = await import('../core/imagegen/imagegen-capability.js');
169
173
  const imagegen = await detectImagegenCapability({ codexBin: codexBin || undefined }).catch((err) => ({ ok: false, error: err.message, auth_readiness: null }));
174
+ const codex0138Capability = await writeCodex0138CapabilityArtifacts(root, { codexBin: codexBin || null }).catch((err) => ({ error: err?.message || String(err), report: null }));
175
+ const codex0138Doctor = await runCodex0138Doctor(root, { fix: doctorFix }).catch((err) => ({ schema: 'sks.codex-0138-doctor.v1', ok: false, error: err?.message || String(err), blockers: ['codex_0138_doctor_exception'], warnings: [] }));
176
+ const pluginInventory = await writeCodexPluginInventoryArtifacts(root).catch((err) => ({ error: err?.message || String(err), report: null, artifact: null }));
177
+ const pluginPolicy = pluginInventory?.report ? pluginAppTemplatePolicy(pluginInventory.report) : null;
178
+ const mcpPluginInventory = pluginInventory?.report
179
+ ? await writeMcpPluginInventoryArtifacts(root, { inventory: pluginInventory.report }).catch((err) => ({ error: err?.message || String(err), candidates: null }))
180
+ : null;
170
181
  const pkgBytes = await dirSize(root).catch(() => 0);
171
182
  const ready = await writeDoctorReadinessMatrix(root, {
172
183
  codex,
@@ -180,10 +191,14 @@ export async function run(_command, args = []) {
180
191
  agent_role_config: agentRoleConfigRepair,
181
192
  repair: configRepair,
182
193
  codex_app_ui: codexAppUi,
194
+ codex_0138_doctor: codex0138Doctor,
195
+ codex_plugin_inventory: pluginInventory?.report || null,
196
+ codex_plugin_app_template_policy: pluginPolicy,
183
197
  require_codex_cli_config_load: flag(args, '--fix') || flag(args, '--require-actual-codex'),
184
198
  operator_actions: [
185
199
  ...(codexConfig.operator_actions || []),
186
- ...(configRepair?.operator_actions || [])
200
+ ...(configRepair?.operator_actions || []),
201
+ ...(pluginPolicy?.doctor_warnings || [])
187
202
  ]
188
203
  });
189
204
  const zellijReadiness = buildZellijReadiness(root, zellij, ready);
@@ -211,6 +226,13 @@ export async function run(_command, args = []) {
211
226
  auth_readiness: imagegen.auth_readiness || null,
212
227
  codex_app_builtin_available: imagegen.codex_app?.available === true
213
228
  },
229
+ codex_0138: {
230
+ capability: codex0138Capability.report || null,
231
+ doctor: codex0138Doctor,
232
+ plugins: pluginInventory?.report || null,
233
+ plugin_app_template_policy: pluginPolicy,
234
+ mcp_plugin_inventory: mcpPluginInventory?.candidates || null
235
+ },
214
236
  ready,
215
237
  sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
216
238
  package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
@@ -262,6 +284,22 @@ export async function run(_command, args = []) {
262
284
  console.log(` - ${action}`);
263
285
  }
264
286
  }
287
+ const codex0138 = codex0138Capability.report || {};
288
+ console.log('Codex 0.138 features:');
289
+ console.log(` /app handoff: ${codex0138.supports_app_handoff ? 'ok' : 'unavailable'}`);
290
+ console.log(` plugin JSON: ${codex0138.supports_plugin_json ? 'ok' : 'unavailable'}`);
291
+ console.log(` image path exposure: ${codex0138.supports_image_path_exposure ? 'ok' : 'unavailable'}`);
292
+ console.log(` OAuth MCP pre-refresh: ${codex0138.supports_oauth_mcp_prerefresh ? 'ok' : 'unavailable'}`);
293
+ const plugins = pluginInventory?.report?.plugins || [];
294
+ const remoteMcpCount = plugins.flatMap((plugin) => plugin.remote_mcp_servers || []).length;
295
+ const unavailableTemplates = pluginPolicy?.unavailable_app_templates?.length || 0;
296
+ console.log(`Codex plugins: ${pluginInventory?.report ? 'ok' : 'warning'}`);
297
+ console.log(` Remote MCP servers: ${remoteMcpCount} candidates`);
298
+ console.log(` Unavailable app templates: ${unavailableTemplates}`);
299
+ for (const warning of pluginPolicy?.doctor_warnings || [])
300
+ console.log(` warning: ${warning}`);
301
+ if (codex0138Doctor?.fixed?.length)
302
+ console.log(` doctor --fix repaired: ${codex0138Doctor.fixed.join(', ')}`);
265
303
  console.log(`codex-lb: ${codexLb.ok ? 'ok' : `warning ${codexLb.circuit?.state || 'unknown'}`}`);
266
304
  if (localModel) {
267
305
  console.log('Local LLM:');
@@ -8,10 +8,20 @@ import { writeRouteCompletionProof } from '../core/proof/route-adapter.js';
8
8
  import { finalizeRouteWithProof } from '../core/proof/route-finalizer.js';
9
9
  import { renderProofMarkdown, writeCompletionProof } from '../core/proof/proof-writer.js';
10
10
  import { validateCompletionProof } from '../core/proof/validation.js';
11
+ import { buildRuntimeProofSummary, renderRuntimeProofSummary } from '../core/agents/runtime-proof-summary.js';
11
12
  export async function run(_command, args = []) {
12
13
  const root = await projectRoot();
13
14
  const action = args[0] || 'show';
14
15
  const rest = args.slice(1);
16
+ if (action === 'latest' && !flag(args, '--completion')) {
17
+ const runtime = await tryRuntimeProofSummary(root);
18
+ if (runtime) {
19
+ if (flag(args, '--json'))
20
+ return printJson(runtime);
21
+ console.log(renderRuntimeProofSummary(runtime));
22
+ return;
23
+ }
24
+ }
15
25
  if (action === 'show' || action === 'latest') {
16
26
  const proof = await withFreshSummaries(root, await readLatestProof(root));
17
27
  if (flag(args, '--json') || action === 'latest')
@@ -122,6 +132,17 @@ export async function run(_command, args = []) {
122
132
  console.error('Usage: sks proof show|latest|validate|route <mission-id|latest>|finalize <mission-id|latest> [--route route] [--strict] [--mock] [--json]|export --md|repair latest|smoke [--json]');
123
133
  process.exitCode = 1;
124
134
  }
135
+ async function tryRuntimeProofSummary(root) {
136
+ try {
137
+ const summary = await buildRuntimeProofSummary(root, 'latest');
138
+ if (summary.blockers.includes('parallel_runtime_proof_missing') && summary.blockers.includes('agent_scheduler_state_missing'))
139
+ return null;
140
+ return summary;
141
+ }
142
+ catch {
143
+ return null;
144
+ }
145
+ }
125
146
  async function withFreshSummaries(root, proof) {
126
147
  const evidence = await collectProofEvidence(root);
127
148
  return {
@@ -1,4 +1,4 @@
1
- import { renderZellijSlotPaneFromArtifacts } from '../core/zellij/zellij-slot-pane-renderer.js';
1
+ import { renderZellijSlotPaneFromArtifacts, renderZellijSlotPaneStatusFromArtifacts } from '../core/zellij/zellij-slot-pane-renderer.js';
2
2
  export async function run(_command = 'zellij-slot-pane', args = []) {
3
3
  const artifactDir = readOption(args, '--artifact-dir', process.cwd()) || process.cwd();
4
4
  const artifactRoot = readOption(args, '--artifact-root', artifactDir) || artifactDir;
@@ -9,7 +9,13 @@ export async function run(_command = 'zellij-slot-pane', args = []) {
9
9
  const role = readOption(args, '--role', null);
10
10
  const mode = readOption(args, '--mode', 'compact-slots');
11
11
  const watch = hasFlag(args, '--watch');
12
+ const json = hasFlag(args, '--json');
12
13
  const intervalMs = Math.max(250, Number(readOption(args, '--interval-ms', '1000') || 1000));
14
+ if (json) {
15
+ const status = await renderZellijSlotPaneStatusFromArtifacts({ artifactDir, artifactRoot, missionId, slotId, generationIndex });
16
+ console.log(JSON.stringify(status, null, 2));
17
+ return;
18
+ }
13
19
  for (;;) {
14
20
  const text = await renderZellijSlotPaneFromArtifacts({ artifactDir, artifactRoot, missionId, slotId, generationIndex, backend, role, mode });
15
21
  process.stdout.write('\x1Bc' + text + '\n');
@@ -1,3 +1,4 @@
1
+ import { codexModelEffortCapability } from '../codex-control/codex-model-capabilities.js';
1
2
  const XHIGH_SIGNAL_RE = /(frontier|autoresearch|novelty|hypothesis|falsif|forensic|from-chat-img|image\s*work\s*order|새로운\s*연구|가설|포렌식)/i;
2
3
  const HIGH_SIGNAL_RE = /(database|supabase|sql|migration|security|permission|mad|release|publish|deploy|architecture|policy|schema|hook|rollback|db|보안|배포|마이그레이션|데이터베이스|권한|릴리즈)/i;
3
4
  const MEDIUM_SIGNAL_RE = /(tmux|terminal|cli|tool(?:\s|-)?call|router|routing|orchestrat|pipeline|multi[-\s]?session|multi[-\s]?agent|lease|ledger|proof|검증|파이프라인|오케스트레이션|병렬|에이전트)/i;
@@ -26,6 +27,7 @@ export function decideAgentEffort(input = {}) {
26
27
  effort = 'high';
27
28
  reason = 'implementation_lane_capped_at_high';
28
29
  }
30
+ const modelCapability = codexModelEffortCapability({ defaultEffort: effort });
29
31
  return {
30
32
  schema: 'sks.agent-effort-decision.v1',
31
33
  policy_version: 1,
@@ -33,6 +35,7 @@ export function decideAgentEffort(input = {}) {
33
35
  role,
34
36
  reasoning_effort: effort,
35
37
  model_reasoning_effort: effort,
38
+ model_effort_capability: modelCapability,
36
39
  reasoning_profile: reasoningProfileName(effort),
37
40
  service_tier: 'fast',
38
41
  reason,
@@ -73,6 +76,7 @@ export function decideNarutoCloneEffort(input = {}) {
73
76
  const writes = !readonly || /write|edit|route-local|workspace|patch|integrat/i.test(writePolicy) || hasActionTool;
74
77
  const toolUse = writes || NARUTO_ACTION_TOOL_RE.test(prompt);
75
78
  const effort = toolUse ? 'medium' : 'low';
79
+ const modelCapability = codexModelEffortCapability({ defaultEffort: effort });
76
80
  return {
77
81
  schema: 'sks.agent-effort-decision.v1',
78
82
  policy_version: 1,
@@ -80,6 +84,7 @@ export function decideNarutoCloneEffort(input = {}) {
80
84
  role,
81
85
  reasoning_effort: effort,
82
86
  model_reasoning_effort: effort,
87
+ model_effort_capability: modelCapability,
83
88
  reasoning_profile: reasoningProfileName(effort),
84
89
  service_tier: 'fast',
85
90
  reason: toolUse ? 'naruto_tool_use_medium' : 'naruto_simple_no_tool_low',
@@ -107,7 +112,8 @@ export function buildAgentEffortPolicy(roster = {}) {
107
112
  policy_version: 1,
108
113
  dynamic: true,
109
114
  service_tier: 'fast',
110
- allowed_efforts: ['low', 'medium', 'high', 'xhigh'],
115
+ allowed_efforts: codexModelEffortCapability().advertised_efforts,
116
+ model_effort_capability: codexModelEffortCapability(),
111
117
  max_agents: roster.max_agents || 20,
112
118
  agent_count: roster.agent_count || decisions.length,
113
119
  concurrency: roster.concurrency || decisions.length,
@@ -476,7 +476,9 @@ export async function runNativeAgentOrchestrator(opts = {}) {
476
476
  targetActiveSlots,
477
477
  visiblePanes: visualLaneCount,
478
478
  expectedWorkerRuntimeMs: targetActiveSlots >= 10 ? 8000 : targetActiveSlots >= 2 ? 2000 : 25,
479
- minActiveWorkers: Math.min(targetActiveSlots, desiredWorkItemCount)
479
+ minActiveWorkers: Math.min(targetActiveSlots, desiredWorkItemCount),
480
+ proofMode: opts.mock === true ? 'mock-process' : 'production',
481
+ requireWorkerPids: opts.nativeCliSwarm !== false && targetActiveSlots >= 16
480
482
  });
481
483
  const results = scheduler.results;
482
484
  const nativeCliSessionProof = await writeNativeCliSessionProof(ledgerRoot, {
@@ -21,6 +21,8 @@ export async function runAgentScheduler(input) {
21
21
  const active = new Map();
22
22
  const results = [];
23
23
  const schedulerStartedAt = Date.now();
24
+ let lastUtilizationUpdateMs = schedulerStartedAt;
25
+ let activeSlotTimeMs = 0;
24
26
  let batchCounter = 0;
25
27
  let batchLaunchSpanTotalMs = 0;
26
28
  let batchDispatchInProgress = false;
@@ -43,6 +45,7 @@ export async function runAgentScheduler(input) {
43
45
  if (!entry)
44
46
  continue;
45
47
  const activeCountBeforeClose = active.size;
48
+ accumulateActiveSlotTime();
46
49
  active.delete(settled.session_id);
47
50
  const resultStatus = settled.result?.status === 'done' ? 'completed' : settled.result?.status === 'blocked' ? 'blocked' : 'failed';
48
51
  completeWorkItem(queue, entry.work_item_id, settled.session_id, resultStatus, settled.error || null);
@@ -70,6 +73,7 @@ export async function runAgentScheduler(input) {
70
73
  const pendingAfterClose = pendingWorkItems(queue).length;
71
74
  if (pendingAfterClose > 0)
72
75
  state.expected_backfill_count += 1;
76
+ updateUtilizationMetrics();
73
77
  await writeAll(input.root, state, slots, queue, active, {
74
78
  event_type: 'session_completed',
75
79
  session_id: settled.session_id,
@@ -85,6 +89,7 @@ export async function runAgentScheduler(input) {
85
89
  closed_at_ms: Date.now()
86
90
  } : null);
87
91
  }
92
+ updateUtilizationMetrics();
88
93
  state.status = 'draining';
89
94
  await writeAll(input.root, state, slots, queue, active, { event_type: 'scheduler_draining' }, input.onSchedulerEvent);
90
95
  slots = closeWorkerSlotsAfterDrain(slots);
@@ -204,6 +209,7 @@ export async function runAgentScheduler(input) {
204
209
  error: err instanceof Error ? err.message : String(err),
205
210
  terminal_close_report_path: path.join(generation.artifact_dir, 'agent-terminal-close-report.json')
206
211
  }));
212
+ accumulateActiveSlotTime();
207
213
  active.set(generation.session_id, { slot_id: slot.slot_id, work_item_id: workItem.id, session_id: generation.session_id, promise });
208
214
  }
209
215
  await appendAgentWorkQueueEvent(input.root, 'batch_work_items_dispatched', {
@@ -317,11 +323,18 @@ export async function runAgentScheduler(input) {
317
323
  return launches;
318
324
  }
319
325
  function updateUtilizationMetrics() {
326
+ accumulateActiveSlotTime();
320
327
  state.wall_time_ms = Math.max(0, Date.now() - schedulerStartedAt);
321
- state.active_slot_time_ms = Math.max(state.active_slot_time_ms, state.completed_count * state.wall_time_ms);
328
+ state.active_slot_time_ms = activeSlotTimeMs;
322
329
  const denominator = Math.max(1, state.wall_time_ms * targetActiveSlots);
323
330
  state.scheduler_utilization = Number(Math.min(1, state.active_slot_time_ms / denominator).toFixed(3));
324
331
  }
332
+ function accumulateActiveSlotTime() {
333
+ const now = Date.now();
334
+ const delta = Math.max(0, now - lastUtilizationUpdateMs);
335
+ activeSlotTimeMs += active.size * delta;
336
+ lastUtilizationUpdateMs = now;
337
+ }
325
338
  }
326
339
  export function normalizeTargetActiveSlots(value, maxActiveSlots = MAX_AGENT_COUNT) {
327
340
  const cap = Number.isFinite(Number(maxActiveSlots)) && Number(maxActiveSlots) >= 1 ? Math.floor(Number(maxActiveSlots)) : MAX_AGENT_COUNT;
@@ -8,7 +8,7 @@ import { closeWorkerPane, openWorkerPane } from '../zellij/zellij-worker-pane-ma
8
8
  import { closeWorkerInRightColumn, recordHeadlessWorkerInRightColumn } from '../zellij/zellij-right-column-manager.js';
9
9
  import { resolveProviderContext } from '../provider/provider-context.js';
10
10
  import { buildZellijSlotPaneCommand } from '../zellij/zellij-slot-pane-renderer.js';
11
- import { resolveZellijUiMode } from '../zellij/zellij-ui-mode.js';
11
+ import { resolveZellijWorkerPaneUiMode } from '../zellij/zellij-ui-mode.js';
12
12
  import { appendZellijSlotTelemetry } from '../zellij/zellij-slot-telemetry.js';
13
13
  import { appendParallelRuntimeEvent } from './parallel-runtime-proof.js';
14
14
  export const NATIVE_CLI_SESSION_SWARM_SCHEMA = 'sks.agent-native-cli-session-swarm.v1';
@@ -278,7 +278,8 @@ class NativeCliSessionSwarmRecorder {
278
278
  route: this.input.route,
279
279
  serviceTier: this.input.fastModePolicy.service_tier
280
280
  });
281
- const uiMode = resolveZellijUiMode(Array.isArray(input.ctx.opts.args) ? input.ctx.opts.args : [], process.env);
281
+ const uiMode = resolveZellijWorkerPaneUiMode(Array.isArray(input.ctx.opts.args) ? input.ctx.opts.args : [], process.env);
282
+ const liveWorkerPane = uiMode !== 'compact-slots';
282
283
  const workerEnv = {
283
284
  ...(input.ctx.opts.env || {}),
284
285
  ...fastModeEnv(this.input.fastModePolicy),
@@ -300,7 +301,7 @@ class NativeCliSessionSwarmRecorder {
300
301
  artifacts: [path.join(input.workerDirRel, 'worker-intake.json'), input.heartbeatRel, input.resultRel],
301
302
  logTail: `zellij=${sessionName}`
302
303
  });
303
- const workerCommand = uiMode === 'full-debug'
304
+ const workerCommand = liveWorkerPane
304
305
  ? buildPaneWorkerCommand({
305
306
  args: input.args,
306
307
  stdoutPath: path.join(this.root, input.stdoutRel),
@@ -332,7 +333,7 @@ class NativeCliSessionSwarmRecorder {
332
333
  mode: uiMode,
333
334
  watch: true
334
335
  });
335
- const processRun = uiMode === 'full-debug'
336
+ const processRun = liveWorkerPane
336
337
  ? null
337
338
  : await this.spawnCompactSlotWorkerProcess({
338
339
  args: input.args,
@@ -405,7 +406,7 @@ class NativeCliSessionSwarmRecorder {
405
406
  const zellijRequired = process.env.SKS_REQUIRE_ZELLIJ === '1';
406
407
  const launchBlockers = zellijRequired ? paneRecord.blockers || [] : [];
407
408
  const launchWarnings = zellijRequired ? [] : paneRecord.blockers || [];
408
- input.record.command_line = ['zellij', '--session', sessionName, 'action', 'new-pane', '--direction', paneRecord.direction_applied, '--name', paneRecord.pane_name, '--', 'sh', '-lc', uiMode === 'full-debug' ? '<native-cli-worker-command>' : '<zellij-slot-pane-renderer-command>'];
409
+ input.record.command_line = ['zellij', '--session', sessionName, 'action', 'new-pane', '--direction', paneRecord.direction_applied, '--name', paneRecord.pane_name, '--', 'sh', '-lc', liveWorkerPane ? '<native-cli-worker-command>' : '<zellij-slot-pane-renderer-command>'];
409
410
  input.record.zellij_session_name = sessionName;
410
411
  input.record.zellij_pane_id = paneRecord.pane_id || null;
411
412
  input.record.zellij_pane_id_source = paneRecord.pane_id_source;
@@ -419,7 +420,7 @@ class NativeCliSessionSwarmRecorder {
419
420
  input.record.provider_context = paneRecord.provider_context;
420
421
  input.record.worktree = worktree;
421
422
  input.record.zellij_ui_mode = uiMode;
422
- input.record.slot_visualization = uiMode === 'full-debug' ? 'worker-command-pane' : 'zellij-slot-pane-renderer';
423
+ input.record.slot_visualization = liveWorkerPane ? 'worker-command-pane' : 'zellij-slot-pane-renderer';
423
424
  input.record.status = launchBlockers.length ? 'failed' : 'running';
424
425
  input.record.blockers = launchBlockers;
425
426
  input.record.warnings = [...(input.record.warnings || []), ...launchWarnings];
@@ -741,7 +742,10 @@ export function buildPaneWorkerCommand(input) {
741
742
  const holdMs = Math.max(0, Number(process.env.SKS_ZELLIJ_WORKER_PANE_HOLD_MS || 1500));
742
743
  const hold = holdMs > 0 ? `sleep ${shellQuote(String(Math.min(30, holdMs / 1000)))}` : ':';
743
744
  const header = input.header ? `printf '%s\\n' ${shellQuote(input.header)} | tee -a ${shellQuote(input.stdoutPath)};` : '';
744
- return `${envPrefix.join(' ')} ${header} ${command} >> ${shellQuote(input.stdoutPath)} 2>> ${shellQuote(input.stderrPath)}; code=$?; ${heartbeat}; ${hold}; exit $code`.trim();
745
+ const exitPath = `${input.heartbeatPath}.exit`;
746
+ const visibleCommand = `(${command}; printf '%s' "$?" > ${shellQuote(exitPath)}) 2>&1 | tee -a ${shellQuote(input.stdoutPath)}`;
747
+ const readExit = `code=$(cat ${shellQuote(exitPath)} 2>/dev/null || printf '1'); rm -f ${shellQuote(exitPath)}`;
748
+ return `${envPrefix.join(' ')} ${header} ${visibleCommand}; ${readExit}; ${heartbeat}; ${hold}; exit $code`.trim();
745
749
  }
746
750
  function buildPaneWorkerHeader(input) {
747
751
  return [
@@ -126,18 +126,33 @@ export async function runNativeCliWorker(input = {}) {
126
126
  });
127
127
  await writeJsonAtomic(path.join(workerDir, 'worker-recursion-guard.json'), guard);
128
128
  let noPatchReason = null;
129
- const routed = await runNativeWorkerBackendRouter({
129
+ const progressTelemetry = startWorkerProgressTelemetry({
130
130
  agentRoot,
131
- workerDirRel,
132
- resultRel,
133
- patchRel,
131
+ heartbeatRel,
132
+ intake,
134
133
  agent,
135
134
  slice,
136
- intake: { ...intake, ...input },
137
135
  backend,
138
- fastModePolicy: policy,
139
- guard
136
+ serviceTier: policy.service_tier
140
137
  });
138
+ let routed;
139
+ try {
140
+ routed = await runNativeWorkerBackendRouter({
141
+ agentRoot,
142
+ workerDirRel,
143
+ resultRel,
144
+ patchRel,
145
+ agent,
146
+ slice,
147
+ intake: { ...intake, ...input },
148
+ backend,
149
+ fastModePolicy: policy,
150
+ guard
151
+ });
152
+ }
153
+ finally {
154
+ progressTelemetry.stop();
155
+ }
141
156
  const patchEnvelopes = routed.patchEnvelopes;
142
157
  if (patchEnvelopes.length) {
143
158
  await writeJsonAtomic(path.resolve(agentRoot, patchRel), {
@@ -330,6 +345,39 @@ export async function runNativeCliWorker(input = {}) {
330
345
  function delay(ms) {
331
346
  return new Promise((resolve) => setTimeout(resolve, ms));
332
347
  }
348
+ function startWorkerProgressTelemetry(input) {
349
+ const parsed = Number(process.env.SKS_ZELLIJ_WORKER_PROGRESS_MS || 2000);
350
+ const intervalMs = Math.max(500, Number.isFinite(parsed) ? Math.floor(parsed) : 2000);
351
+ let tick = 0;
352
+ const timer = setInterval(() => {
353
+ tick += 1;
354
+ const progress = { done: tick, total: 0, label: 'backend running' };
355
+ appendJsonl(path.resolve(input.agentRoot, input.heartbeatRel), {
356
+ schema: 'sks.native-cli-worker-heartbeat.v1',
357
+ ts: nowIso(),
358
+ event: 'progress',
359
+ pid: process.pid,
360
+ session_id: input.agent.session_id,
361
+ slot_id: input.agent.slot_id || null,
362
+ generation_index: input.agent.generation_index || null,
363
+ progress
364
+ }).catch(() => undefined);
365
+ workerTelemetry(input.agentRoot, input.intake, input.agent, input.slice, {
366
+ eventType: 'task_progress',
367
+ status: 'running',
368
+ backend: input.backend,
369
+ serviceTier: input.serviceTier,
370
+ artifacts: [input.heartbeatRel],
371
+ progress,
372
+ logTail: `backend running ${tick}`
373
+ }).catch(() => undefined);
374
+ }, intervalMs);
375
+ return {
376
+ stop() {
377
+ clearInterval(timer);
378
+ }
379
+ };
380
+ }
333
381
  function parseNativeCliWorkerArgs(args) {
334
382
  return {
335
383
  intake: readOption(args, '--intake', ''),
@@ -389,6 +437,7 @@ async function workerTelemetry(agentRoot, intake, agent, slice, input) {
389
437
  worktree_path: agent.worktree?.path || slice.worktree?.path || intake.worktree?.path || null,
390
438
  task_title: String(slice.description || slice.title || slice.id || 'worker task'),
391
439
  current_file: firstString([slice.write_paths?.[0], slice.readonly_paths?.[0], slice.input_files?.[0]]) || null,
440
+ ...(input.progress ? { progress: input.progress } : {}),
392
441
  artifact_paths: input.artifacts || [],
393
442
  log_tail: input.logTail || '',
394
443
  blockers: input.blockers || []