sneakoscope 8.0.2 → 8.0.3

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 (122) hide show
  1. package/README.md +47 -23
  2. package/config/skills-hash-ledger.v1.json +1 -1
  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 +55 -13
  6. package/dist/bin/sks-dispatch.js +1 -1
  7. package/dist/cli/command-manifest-lite.js +4 -0
  8. package/dist/cli/command-registry.js +4 -0
  9. package/dist/cli/install-helpers-codex-lb-chain.js +187 -0
  10. package/dist/cli/install-helpers-codex-lb-config.js +74 -8
  11. package/dist/cli/install-helpers.js +89 -44
  12. package/dist/commands/codex-lb.js +117 -19
  13. package/dist/commands/doctor.js +697 -37
  14. package/dist/config/skills-manifest.json +3 -2
  15. package/dist/core/agents/agent-runner-codex-exec.js +5 -3
  16. package/dist/core/codex/codex-config-guard.js +238 -52
  17. package/dist/core/codex-adapter.js +5 -3
  18. package/dist/core/codex-app/menubar/constants.js +10 -1
  19. package/dist/core/codex-app/menubar/global-install.js +288 -0
  20. package/dist/core/codex-app/menubar/installer.js +79 -14
  21. package/dist/core/codex-app/menubar/launch-agent.js +267 -45
  22. package/dist/core/codex-app/menubar/paths.js +3 -0
  23. package/dist/core/codex-app/menubar/rollback.js +55 -7
  24. package/dist/core/codex-app/menubar/status.js +75 -4
  25. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +3 -1
  26. package/dist/core/codex-control/codex-0139-web-search-probe.js +3 -1
  27. package/dist/core/codex-control/codex-app-server-runtime-env.js +13 -4
  28. package/dist/core/codex-control/codex-app-server-v2-client.js +3 -3
  29. package/dist/core/codex-exec-output-schema.js +4 -1
  30. package/dist/core/codex-lb/codex-lb-env.js +63 -72
  31. package/dist/core/codex-lb/codex-lb-setup.js +15 -2
  32. package/dist/core/codex-lb/desktop-center-credentials.js +517 -58
  33. package/dist/core/codex-lb/desktop-controller.js +153 -24
  34. package/dist/core/codex-lb/routing-truth.js +327 -0
  35. package/dist/core/codex-native/core-skill-manifest.js +1 -1
  36. package/dist/core/commands/basic-cli.js +7 -1
  37. package/dist/core/commands/qa-loop-command.js +3 -1
  38. package/dist/core/commands/telegram-command.js +241 -0
  39. package/dist/core/config-adopt/config-adopt-command.js +35 -0
  40. package/dist/core/config-adopt/config-adopt.js +346 -0
  41. package/dist/core/config-adopt/index.js +3 -0
  42. package/dist/core/doctor/command-alias-cleanup.js +4 -1
  43. package/dist/core/doctor/current-project-guidance-nested.js +37 -6
  44. package/dist/core/doctor/current-project-guidance.js +11 -6
  45. package/dist/core/doctor/doctor-readiness-matrix.js +14 -0
  46. package/dist/core/doctor/retired-managed-residue-private.js +1 -1
  47. package/dist/core/doctor/sks-menubar-doctor.js +4 -0
  48. package/dist/core/feature-fixtures.js +8 -0
  49. package/dist/core/hooks-runtime/hook-context.js +9 -0
  50. package/dist/core/hooks-runtime/managed-skill-admission.js +54 -4
  51. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +69 -1
  52. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +2 -2
  53. package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +3 -3
  54. package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +43 -5
  55. package/dist/core/hooks-runtime/subagent-skill-availability.js +64 -30
  56. package/dist/core/hooks-runtime/subagent-stop-hook.js +15 -2
  57. package/dist/core/hooks-runtime.js +148 -4
  58. package/dist/core/imagegen/imagegen-capability.js +46 -0
  59. package/dist/core/imagegen/require-imagegen.js +2 -2
  60. package/dist/core/init/skills.js +17 -3
  61. package/dist/core/install/installed-package-smoke.js +8 -2
  62. package/dist/core/naruto/hardware-capacity-probe.js +9 -3
  63. package/dist/core/naruto/naruto-concurrency-governor.js +68 -24
  64. package/dist/core/perf/import-graph-budget.js +0 -1
  65. package/dist/core/product-design-app-server.js +13 -1
  66. package/dist/core/release/package-size-budget.js +2 -2
  67. package/dist/core/release/release-pack-receipt.js +1 -2
  68. package/dist/core/routes/dollar-manifest-lite.js +1 -1
  69. package/dist/core/routes.js +1 -1
  70. package/dist/core/security/private-credential-file.js +209 -0
  71. package/dist/core/subagents/agent-catalog.js +17 -32
  72. package/dist/core/subagents/naruto-help-contract.js +5 -5
  73. package/dist/core/subagents/official-subagent-config.js +16 -6
  74. package/dist/core/subagents/official-subagent-preparation.js +34 -15
  75. package/dist/core/subagents/official-subagent-prompt.js +4 -4
  76. package/dist/core/subagents/official-subagent-runner.js +27 -4
  77. package/dist/core/subagents/thread-budget.js +19 -12
  78. package/dist/core/subagents/wave-lifecycle.js +18 -6
  79. package/dist/core/telegram/access.js +54 -0
  80. package/dist/core/telegram/client.js +144 -0
  81. package/dist/core/telegram/confirmation.js +37 -0
  82. package/dist/core/telegram/doctor.js +151 -0
  83. package/dist/core/telegram/index.js +9 -0
  84. package/dist/core/telegram/keychain.js +265 -0
  85. package/dist/core/telegram/liveness.js +109 -0
  86. package/dist/core/telegram/poller.js +90 -0
  87. package/dist/core/telegram/redaction.js +15 -0
  88. package/dist/core/telegram/types.js +1 -0
  89. package/dist/core/update/update-diagnostics.js +52 -0
  90. package/dist/core/update/update-migration-state.js +5 -1
  91. package/dist/core/update/update-operation.js +2 -1
  92. package/dist/core/update/update-stage-diagnostics.js +38 -0
  93. package/dist/core/update/update-status.js +2 -9
  94. package/dist/core/update-check.js +147 -31
  95. package/dist/core/version.js +1 -1
  96. package/dist/core/zellij/zellij-launcher.js +23 -4
  97. package/dist/native/sks-menubar/Sources/AppDelegate.swift +13 -0
  98. package/dist/native/sks-menubar/Sources/AppIdentity.swift +7 -0
  99. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +3 -2
  100. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +115 -0
  101. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +17 -0
  102. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +190 -0
  103. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +100 -59
  104. package/dist/native/sks-menubar/Sources/SingletonInstanceGuard.swift +328 -0
  105. package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +320 -0
  106. package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +303 -0
  107. package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +238 -0
  108. package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +56 -0
  109. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  110. package/dist/native/sks-menubar/Sources/TelegramTransport.swift +474 -0
  111. package/dist/native/sks-menubar/Sources/main.swift +15 -1
  112. package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +4 -3
  113. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +129 -0
  114. package/dist/native/sks-menubar/Tests/SingletonInstanceGuardTests.swift +183 -0
  115. package/dist/native/sks-menubar/Tests/TelegramRuntimeSmokeTests.swift +267 -0
  116. package/dist/native/sks-menubar/Tests/TelegramRuntimeTests.swift +222 -0
  117. package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -1
  118. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +1 -1
  119. package/dist/scripts/current-command-surface-check.js +2 -4
  120. package/dist/scripts/official-subagent-workflow-check.js +1 -1
  121. package/dist/scripts/packlist-performance-check.js +1 -2
  122. package/package.json +1 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@ Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, mult
22
22
  Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
23
23
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
24
24
 
25
- This README documents package **SKS 8.0.2** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
25
+ This README documents package **SKS 8.0.3** — its own identity, read from `package.json` and subject to release-gate verification, not advice about what to install.
26
26
 
27
27
  Use the official latest stable SKS and Codex CLI releases. SKS stays version-agnostic: older hosts keep working where capabilities allow, while Menu Bar / Center induce updates to the latest stable build. Run `sks update-check` for what is installed and read the capability report for what is actually supported — feature availability is decided by capability probes, not by a version number printed in a document. It resolves managed SKS skills from the authoritative global install, preserves a runnable Naruto child slot when `max_threads=2`, and keeps Menu Bar repair transactional so stamped generations remain verifiable. Naruto uses stable opt-in multi-agent V2 when the host exposes it. Local code search is mode-separated (`sks search files|text|structure|symbol|context`); `context` is answered by the compiled TriWiki Context Graph with evidence paths rather than lexical guessing — see [docs/architecture/context-graph.md](docs/architecture/context-graph.md) and [docs/architecture/search-engine-target.md](docs/architecture/search-engine-target.md). See [CHANGELOG.md](CHANGELOG.md).
28
28
 
@@ -63,11 +63,25 @@ The SKS menu bar shows the installed Codex CLI version and latest known version.
63
63
 
64
64
  **Manage MCP Servers…** opens a native macOS manager for the global `~/.codex/config.toml`. It can add remote URL or local stdio servers, enable/disable existing entries, remove entries after confirmation, and refresh the current state. Mutations are lock-protected, backed up, TOML-validated, and written with mode `0600`; configured environment values and command arguments are never rendered in the list. Changes apply to new Codex sessions. The same plumbing is available through the canonical `sks mcp config list|get|add|edit|duplicate|enable|disable|remove|test|login|logout|backups|restore` surface for diagnostics and automation.
65
65
 
66
- ### Remote coding: Orca (external option)
67
-
68
- SKS no longer provides a first-party Telegram coding bridge. If you want to monitor or steer coding work from another device, consider [Orca](https://github.com/stablyai/orca), an external MIT-licensed project that can launch Codex in a worktree. Orca is not bundled with SKS, supported by SKS, or required to use SKS; SKS adds no Orca dependency or configuration.
69
-
70
- Orca's mobile companion and Remote Orca Servers are beta. Their desktop/server runtime remains the source of truth and must keep running; remote access may require a private LAN or Tailscale path. See [the Orca migration and remote-coding guide](docs/orca-remote-coding.md) for scope, setup links, and former Telegram-user migration notes.
66
+ ### Remote coding: Telegram transport and Orca option
67
+
68
+ SKS 8.0.3 restores a first-party Telegram Bot API transport for the existing
69
+ typed remote-control contract. One bounded outbound `getUpdates` loop runs in
70
+ the existing Menu Bar process, so it adds no daemon, inbound port, or tunnel.
71
+ `sks telegram pair` enrolls an allowed chat through the existing secret store;
72
+ unpaired chats receive no response, free-form shell input is never accepted,
73
+ and destructive commands require actor-bound two-step confirmation and an audit
74
+ receipt. Doctor readiness requires a real `getMe` round trip plus live-poller
75
+ evidence. Release readiness additionally requires a real command/reply E2E from
76
+ a cellular network; configuration, fixtures, and local-network tests do not
77
+ satisfy that gate.
78
+
79
+ [Orca](https://github.com/stablyai/orca) remains an external MIT-licensed
80
+ option that can launch Codex in a worktree. It is not bundled with, supported
81
+ by, or required by SKS, and SKS adds no Orca dependency or automatic migration.
82
+ Orca's mobile companion and Remote Orca Servers are beta; the desktop/server
83
+ runtime remains the source of truth and remote access may require a private LAN
84
+ or Tailscale path. See [the Orca remote-coding guide](docs/orca-remote-coding.md).
71
85
 
72
86
  ## The Front Door
73
87
 
@@ -85,16 +99,25 @@ Orca's mobile companion and Remote Orca Servers are beta. Their desktop/server r
85
99
 
86
100
  `$sks-naruto` and `sks naruto run "task" --agents 8 --max-threads 12` use Codex official subagents. Standalone and Codex App tasks that request project-host database, spreadsheet, or render tools require the non-persistent `--trusted-project` flag after the operator reviews the checkout; an App session ID scopes evidence but does not grant trust. The parent is GPT-5.6 Sol Max. Tiny mechanical `worker` slices use Luna Max; ordinary UI, logic, backend, and native coding uses Sol High; review, testing, debugging, architecture, integration, security, database, research, release, and other judgment-sensitive work uses Sol Max; long-context scans and direct Computer Use, Browser/Chrome, or image-generation execution uses Terra Medium. Mixed execution/judgment work is split when possible, and unsplittable judgment defaults to Sol Max.
87
101
 
88
- Fresh SKS-owned project config enables Codex 0.145+ multi-agent V2 with `agents.max_concurrent_threads_per_session = 12`, `features.multi_agent_v2.max_concurrent_threads_per_session = 13`, `max_depth = 1`, and `interrupt_message = true`. Nested delegation remains forbidden. Explicit user limits are preserved, and larger requests run in waves.
89
-
90
- Naruto's automatic tiers are 4/6/8 children for ordinary work and 16 for
91
- mass cheap-model work, with automatic ceilings of 12 and 64 respectively.
92
- The absolute `--agents`/`--max-threads` frame ceiling is 256, and requests
93
- beyond current capacity reuse slots across waves. `SKS_NARUTO_REMOTE_API_PARALLEL_BUDGET`
94
- can lower the governor's remote API parallel-request budget. These are structural
95
- scheduling limits, not a recommended target or proof that 256 live agents were
96
- load-tested on the current host; CPU, memory, file descriptors, process/worktree
97
- capacity, ready work, and verifier capacity can select a much smaller active wave.
102
+ Fresh SKS-owned project config enables Codex 0.145+ multi-agent V2 with `agents.max_concurrent_threads_per_session = 256`, `features.multi_agent_v2.max_concurrent_threads_per_session = 257`, `max_depth = 1`, and `interrupt_message = true`. Nested delegation remains forbidden. Explicit user-owned limits are preserved, while legacy SKS-owned 12/13 defaults migrate to 256/257.
103
+
104
+ Naruto's automatic starting tiers are 4/6/8 children for ordinary work and 16
105
+ for mass cheap-model work. After decomposition, either lane may expand to the
106
+ SKS-owned ceiling of 256 independent useful children. Explicit
107
+ `--agents N` and `--max-threads N` values from 1 through 256 remain
108
+ authoritative instead of being reduced to those automatic tiers. A first wave
109
+ may reach 256 child slots only when independent ready work, disjoint ownership,
110
+ verifier/tool capacity, and the external Codex/session host all permit it;
111
+ otherwise Naruto records the exact active limiter and reuses returned capacity
112
+ in later waves.
113
+
114
+ `SKS_NARUTO_REMOTE_API_PARALLEL_BUDGET` declares a lower remote provider/API
115
+ parallel-request budget for the governor. It cannot raise a lower external-host
116
+ or session limit. The official Codex lane does not impose a local CPU/RAM or
117
+ unmeasured API-default clamp; it follows an explicit provider budget and a
118
+ measured host cap. The 256 ceiling is structural, not a recommendation or proof
119
+ that 256 live agents were load-tested on the current host. The four GPT-5.6
120
+ model profiles are routing lanes, not an agent-count limit.
98
121
 
99
122
  Gates are task-profile aware: greetings and answer-only turns create no mission gate; tiny work gets minimal verification; parallel work gets scoped ownership and verification; high-risk work keeps the full safety gates. `SubagentStart`/`SubagentStop` prove lifecycle only. Completion also requires `subagent-parent-summary.json` with one trustworthy structured outcome per thread, correlated with `subagent-events.jsonl` and `subagent-evidence.json`.
100
123
 
@@ -234,9 +257,9 @@ It shows the current quickstart flow: one-line install, `$sks-plan`, `sks review
234
257
  - Project memory: `sks memory build`
235
258
  - Codebase index/pack for LLM context: `sks wiki refresh --code`, `sks wiki validate --json` (code-pack freshness)
236
259
  - Native capability repair: `sks doctor --fix` (imagegen/Computer Use/Browser Use), `.sneakoscope/reports/native-capability-readiness.json`
237
- - codex-lb continuity: `sks codex-lb status --json` verifies the selected proxy's unauthenticated `/health` `X-App-Version`. Tool-heavy continuation requires a codex-lb deployment that reports the capability; use the official latest stable release. Older or unverified deployments block setup, doctor, and launch instead of silently falling back.
260
+ - codex-lb continuity: `sks codex-lb status --json` verifies the selected proxy's unauthenticated `/health` `X-App-Version`. Tool-heavy continuation requires a codex-lb deployment that reports the capability; use the official latest stable release. Older or unverified deployments block setup, doctor, and launch instead of silently falling back. A selected route is green only after one measured request records the destination host, gateway-key authentication class, measurement time, and latency; config presence alone is not routing proof.
238
261
  - Agent bridge for any agent system: `sks mcp-server`, `sks agent-bridge setup`, `SKS_AGENT_MODE=1` — see [docs/AGENT-BRIDGE.md](docs/AGENT-BRIDGE.md)
239
- - Release gates: `npm run release:check:affected` for ordinary change-aware verification and `npm run release:check:confidence` for the final local confidence pass.
262
+ - Release gates: `npm run release:check:affected` for ordinary change-aware verification and `npm run release:check:confidence` for the final local confidence pass. Evidence for the 8.0.3 physical checklist includes a successful 5,001-directory update scan, exactly one running Menu Bar process at the current package version, one measured real codex-lb request to the selected remote host, and one Telegram command/reply E2E over cellular.
240
263
  - Release preparation: typecheck, one clean build, focused tests, affected/confidence gates, then `npm publish --dry-run --json --registry https://registry.npmjs.org/ --tag latest --access public`. The dry-run does not publish; authorization remains a separate maintainer workflow.
241
264
  - Release readiness notes: [docs/release-readiness.md](docs/release-readiness.md) and [CHANGELOG.md](CHANGELOG.md)
242
265
  - Image generation review routes require Codex App `$imagegen`/`gpt-image-2` evidence with recorded output hashes; direct API fallback and mock fixtures do not satisfy full route gates.
@@ -248,15 +271,16 @@ It shows the current quickstart flow: one-line install, `$sks-plan`, `sks review
248
271
  - macOS optional: menu bar integration and `/usr/bin/open`
249
272
  - The menubar icon shows and hides itself automatically as the Codex desktop app launches/quits; set `quit_with_codex: true` in `~/.codex/sks-menubar/config.json` to have the menubar fully quit with Codex instead of just hiding (default `false`).
250
273
  - Native input dialogs (API keys, codex-lb setup) pass secrets to `sks` via `--api-key-stdin` instead of a visible Terminal window or process arguments.
251
- - Codex Desktop always keeps its real ChatGPT OAuth identity. codex-lb setup stores a separate gateway endpoint/key in an owner-only `0600` env file and never replaces `~/.codex/auth.json`; Keychain requests fail closed until a dedicated signed helper is available.
274
+ - Codex Desktop keeps `~/.codex/auth.json` byte-preserved. **Use codex-lb** atomically stores the provider definition and active top-level selection and resolves its separate gateway key from the owner-only `0600` env file; while selected, requests must not consume the stored ChatGPT OAuth identity. Turning codex-lb off returns to the official OAuth path. Keychain requests fail closed until a dedicated signed helper is available.
252
275
  - Explicit codex-lb setup/reconfiguration migrates the retired generic `sks-codex-lb` Keychain item only after the replacement owner-only store is verified, deletes only the exact legacy account, and proves absence by readback. If cleanup cannot be proved, the replacement store is retained and the provider key should be rotated.
253
- - Providers separates **Desktop Full Capability**, **CLI Provider**, and **Capability Matrix**. Desktop Full Capability keeps the built-in OpenAI provider and routes supported traffic through a loopback bridge; CLI Provider is selected only for the CLI process.
276
+ - Providers exposes atomic **Use codex-lb** / **Use ChatGPT OAuth** selection plus shared RoutingTruth. A green codex-lb state requires one measured request to the configured remote base URL; the Menu Bar and Doctor render the same host, authentication class, measurement time, and latency.
254
277
  - The retired dual-auth compatibility mode is detected only for safe migration. It cannot be activated or reported ready because it would require a global GUI secret.
255
- - Authentication and routing modes do not toggle Codex App's native model picker, Fast, image, Browser Use, Computer Use, voice, plugins/apps, or other built-in surfaces. Capability status describes evidence for the codex-lb data path, not permission to use the native feature.
278
+ - Authentication and routing modes do not toggle Codex App's native model picker, Fast, image, Browser Use, Computer Use, voice, plugins/apps, or other built-in surfaces. Capability status describes measured evidence for the codex-lb data path, not permission to use a native feature.
256
279
  - `sks update`, setup, repair, and routing changes preserve OAuth, user model/reasoning/Fast settings, unknown catalog fields, and unrelated provider configuration. Legacy shared-auth routing requires explicit `sks codex-lb migrate-legacy-desktop --restart-app`; without a real restart and post-restart identity check it cannot finalize a migration receipt.
257
280
  - The remote codex-lb service remains independently hosted and operated. SKS uses only the configured URL and gateway key; it does not deploy, restart, or change credentials on that host.
258
- - Full release proof blocks with `real_required_missing` until fresh trust-anchored evidence covers the real Desktop feature/lifecycle matrix and the separately hosted other-Mac runtime; fixtures and configuration cannot satisfy that boundary.
259
- - Update installs always rebuild the companion with the newly installed SKS package, preventing a previous-version updater from restoring a stale menu binary.
281
+ - Full release proof blocks with `real_required_missing` until fresh trust-anchored evidence covers the real Desktop feature/lifecycle matrix, one measured gateway-key request to the selected separately hosted codex-lb instance, and the other-Mac runtime; fixtures and configuration cannot satisfy that boundary.
282
+ - Update installs stop and verify every prior Menu Bar process before replacement, rebuild the companion from the newly installed SKS package, bootstrap it, and require exactly one running process whose version probe equals the current package version.
283
+ - Telegram remote control uses one bounded long-poller inside that same Menu Bar process. Release evidence must include a real cellular command/reply round trip; `getMe`, a local fixture, or a configured token alone cannot satisfy the E2E gate.
260
284
  - The menubar dropdown's `View Last Log` item opens the most recent background action's log file, so you don't need to keep a Terminal window open to see command output.
261
285
  - `Manage MCP Servers…` provides a resizable native table and add/remove/enable/disable controls for global Codex MCP configuration. Secret environment values and command arguments are accepted through native dialogs/stdin but omitted from list output and logs.
262
286
  - `sks menubar status --json` reports a `codex_sync` object with `bundle_id`, `codex_running`, and `icon_visible_expected` to show Codex-lifecycle detection state.
@@ -38,7 +38,7 @@
38
38
  { "canonical_name": "sks-init-deep", "trusted_sha256": ["a679fa4ba36e070de5753994f58595c4352302cc98d868ba70ff98305d15f451"] },
39
39
  { "canonical_name": "sks-loop", "trusted_sha256": ["e56f9c4d0f7227961779daf6806943f1926bb588ce21a965a813e8ad91cfd5c8"] },
40
40
  { "canonical_name": "sks-mad-sks", "trusted_sha256": ["03fb5063d19c3d48d004cd8e6e39bfe244016d7edacfe99e5dab087cbbfa6db9", "a3d0110ca97576130893311b3267621acb02c16ca9aea4b2da3e5300eea58158"] },
41
- { "canonical_name": "sks-naruto", "trusted_sha256": ["f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37", "49b5e3dcc1b537b49c2cf09e5b875cb16acb05c622b22798e265e64dacd8ac90"] },
41
+ { "canonical_name": "sks-naruto", "trusted_sha256": ["34222c72e4e1ead3584907f4d905444b7de820283ee7348018f110d5e6b04461", "f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37", "49b5e3dcc1b537b49c2cf09e5b875cb16acb05c622b22798e265e64dacd8ac90"] },
42
42
  { "canonical_name": "sks-performance-evaluator", "trusted_sha256": ["5b8c6cea19ffda8e434700297ed060fde3cfcfc9c98635402599e84756466db4", "c0bfd5a2e6c74813cb5dac9dadd4c7543ee768a48cf3e36b86b3d9306c2712c6"] },
43
43
  { "canonical_name": "sks-pipeline-runner", "trusted_sha256": ["020ba4da2f96bcda23cee4596b1e790816780178f1917dee748ff4b289cba0e5", "ffcbe0ea4b585e3d2886f1d9fa7a3b88706aaf42182fb177b2371b76beb0ddcd"] },
44
44
  { "canonical_name": "sks-plan", "trusted_sha256": ["7795867b38a5f169924125850f137c31995cf28000f74284839530a68ace156a", "b6f3b09230e9809acf4e13df2de6af3c75a8534e8890d6560664ebfc2e4072f8"] },
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "8.0.2"
262
+ version = "8.0.3"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "8.0.2"
3
+ version = "8.0.3"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -13,11 +13,7 @@ function rootJsonFastInline(fs, cwd = process.cwd()) {
13
13
  }
14
14
  async function doctorJsonFastInline(input = {}) {
15
15
  const startedAt = Date.now();
16
- const { inspectOAuthCallbackPortConflict, oauthCallbackDoctorGuidance } = await import('../core/codex/oauth-callback-port-diagnostic.js');
17
- const oauthCallbackPortDiagnostic = await inspectOAuthCallbackPortConflict({
18
- ...(input.run ? { run: input.run } : {})
19
- });
20
- const oauthCallbackOperatorActions = oauthCallbackDoctorGuidance(oauthCallbackPortDiagnostic);
16
+ const secretResolution = await inspectCodexLbSecretResolutionFastInline(input);
21
17
  const write = input.write || ((text) => process.stdout.write(text));
22
18
  write(`${JSON.stringify({
23
19
  schema: 'sks.doctor-status.v3',
@@ -59,12 +55,10 @@ async function doctorJsonFastInline(input = {}) {
59
55
  },
60
56
  doctor_fix_transaction: null,
61
57
  blockers: [],
62
- warnings: [
63
- 'fast_readonly_doctor_skipped_optional_deep_diagnostics',
64
- ...oauthCallbackPortDiagnostic.warnings
65
- ],
66
- operator_actions: oauthCallbackOperatorActions,
67
- oauth_callback_port_diagnostic: oauthCallbackPortDiagnostic
58
+ warnings: ['fast_readonly_doctor_skipped_optional_deep_diagnostics'],
59
+ codex_lb: {
60
+ secret_resolution: secretResolution
61
+ }
68
62
  }, null, 2)}\n`);
69
63
  }
70
64
  async function narutoHelpJsonFastInline() {
@@ -125,12 +119,60 @@ function findProjectRootSync(fs, start) {
125
119
  dir = parent;
126
120
  }
127
121
  }
122
+ async function inspectCodexLbSecretResolutionFastInline(input) {
123
+ const processEnv = input.processEnv || process.env;
124
+ const home = input.home || processEnv.HOME || processEnv.USERPROFILE || process.cwd();
125
+ const codexHome = joinPath(home, '.codex');
126
+ const envPath = joinPath(codexHome, 'sks-codex-lb.env');
127
+ const metadataPath = joinPath(codexHome, 'sks-codex-lb.json');
128
+ const [{ inspectConfinedPath }, { readPrivateCredentialFile }] = await Promise.all([
129
+ import('../core/managed-path-safety.js'),
130
+ import('../core/security/private-credential-file.js')
131
+ ]);
132
+ let envFilePresent = false;
133
+ let envFilePrivate = false;
134
+ try {
135
+ const inspected = await inspectConfinedPath(codexHome, envPath);
136
+ envFilePresent = inspected.exists;
137
+ const expectedUid = typeof process.getuid === 'function' ? process.getuid() : null;
138
+ envFilePrivate = inspected.exists
139
+ && !inspected.leafSymlink
140
+ && inspected.stat?.isFile() === true
141
+ && (inspected.stat.mode & 0o777) === 0o600
142
+ && (expectedUid === null || inspected.stat.uid === expectedUid);
143
+ }
144
+ catch {
145
+ envFilePresent = false;
146
+ }
147
+ let metadataValid = false;
148
+ if (envFilePrivate) {
149
+ try {
150
+ const snapshot = await readPrivateCredentialFile(codexHome, metadataPath, 'codex_lb_metadata', { maxBytes: 64 * 1024 });
151
+ const metadata = JSON.parse(snapshot.bytes.toString('utf8'));
152
+ metadataValid = metadata?.schema === 'sks.codex-lb-metadata.v1'
153
+ && /^[a-f0-9]{64}$/.test(String(metadata?.api_key?.sha256 || '').trim().toLowerCase())
154
+ && Boolean(String(metadata?.base_url || '').trim());
155
+ }
156
+ catch {
157
+ metadataValid = false;
158
+ }
159
+ }
160
+ const processKeyPresent = Boolean(String(processEnv.CODEX_LB_API_KEY || '').trim());
161
+ const source = envFilePrivate && metadataValid ? 'env-file' : processKeyPresent ? 'process.env' : 'missing';
162
+ return {
163
+ source,
164
+ path: source === 'env-file' || envFilePresent ? envPath : null,
165
+ prompt_risk: (input.platform || process.platform) === 'darwin'
166
+ ? 'unknown_keychain_not_probed'
167
+ : 'none'
168
+ };
169
+ }
128
170
  function fsInline() {
129
171
  return process.getBuiltinModule?.('node:fs') || require('node:fs');
130
172
  }
131
- function readJsonSyncInline(file) {
173
+ function readJsonSyncInline(file, fs = fsInline()) {
132
174
  try {
133
- return JSON.parse(fsInline().readFileSync(file, 'utf8'));
175
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
134
176
  }
135
177
  catch {
136
178
  return null;
@@ -13,7 +13,7 @@ async function runSks(args) {
13
13
  const { rootJsonFastInline } = await import('./fast-inline.js');
14
14
  rootJsonFastInline(fs);
15
15
  }
16
- else if (args[0] === 'doctor' && args.includes('--json') && !args.includes('--report-file') && !args.includes('--fix') && !args.includes('--full') && !args.includes('--capabilities') && !args.includes('--search')) {
16
+ else if (args[0] === 'doctor' && args.includes('--json') && !args.includes('--report-file') && !args.includes('--fix') && !args.includes('--full') && !args.includes('--capabilities') && !args.includes('--profile') && !args.includes('--search')) {
17
17
  const { doctorJsonFastInline } = await import('./fast-inline.js');
18
18
  await doctorJsonFastInline();
19
19
  }
@@ -17,6 +17,7 @@ const COMMAND_MANIFEST_LITE_BASE = [
17
17
  { name: 'update', summary: 'Inspect, review, apply, or roll back the global SKS update', maturity: 'stable' },
18
18
  { name: 'uninstall', summary: 'Uninstall SKS global skills, hooks, config, menu bar, and optional project residue', maturity: 'stable', allowedDuringActiveRoute: true },
19
19
  { name: 'update-check', summary: 'Show the shared SKS, Codex CLI, and Menu Bar update status', maturity: 'stable', readonly: true, skipMigrationGate: true, allowedDuringActiveRoute: true, diagnostic: true },
20
+ { name: 'config', summary: 'Adopt project Codex config into SKS management', maturity: 'stable', skipMigrationGate: true },
20
21
  { name: 'mcp', summary: 'Manage scoped Codex MCP configuration', maturity: 'beta', skipMigrationGate: true },
21
22
  { name: 'wizard', summary: 'Open setup wizard help', maturity: 'stable' },
22
23
  { name: 'usage', summary: 'Show focused usage topic', maturity: 'stable', readonly: true, allowedDuringActiveRoute: true, diagnostic: true },
@@ -37,6 +38,7 @@ const COMMAND_MANIFEST_LITE_BASE = [
37
38
  { name: 'codex-lb', summary: 'Inspect codex-lb status and circuit health', maturity: 'beta', skipMigrationGate: true },
38
39
  { name: 'menubar', summary: 'Inspect/install/restart/uninstall SKS menu bar', maturity: 'beta', skipMigrationGate: true, allowedDuringActiveRoute: true, diagnostic: true },
39
40
  { name: 'remote', summary: 'Inspect official Remote readiness and run the proof-aware SSH stdio worker', maturity: 'beta' },
41
+ { name: 'telegram', summary: 'Pair and inspect the Telegram remote-control transport', maturity: 'beta', skipMigrationGate: true },
40
42
  { name: 'hooks', summary: 'Explain and inspect Codex hooks', maturity: 'beta', skipMigrationGate: true },
41
43
  { name: 'zellij-lane', summary: 'Render a Zellij lane frame for SKS sessions', maturity: 'beta' },
42
44
  { name: 'zellij-slot-pane', summary: 'Render a compact Zellij worker slot pane', maturity: 'beta' },
@@ -116,6 +118,7 @@ const COMMAND_CONTRACT_OVERRIDES_LITE = {
116
118
  check: { risk: 'R1', latency: 'long' },
117
119
  'commit-and-push': { risk: 'R3' },
118
120
  'computer-use': { latency: 'long' },
121
+ config: { risk: 'R2', supportsJson: true, remoteAllowed: false, inputProfile: 'json-only' },
119
122
  dfix: { latency: 'long' },
120
123
  'dollar-commands': { risk: 'R2', latency: 'normal' },
121
124
  eval: { latency: 'long' },
@@ -165,6 +168,7 @@ const COMMAND_CONTRACT_OVERRIDES_LITE = {
165
168
  requiredCapabilities: ['proof.stop-gate']
166
169
  },
167
170
  task: { risk: 'R1', latency: 'long' },
171
+ telegram: { risk: 'R2', supportsJson: true, remoteAllowed: false, inputProfile: 'json-only' },
168
172
  trust: {
169
173
  risk: 'R0', latency: 'fast', supportsJson: true, remoteAllowed: true, inputProfile: 'trust', requiredCapabilities: ['proof.trust']
170
174
  },
@@ -165,6 +165,7 @@ const COMMAND_DEFINITIONS = {
165
165
  activeRoutePolicy: 'always'
166
166
  }),
167
167
  'update-check': readOnly(entry('stable', 'Show the shared SKS, Codex CLI, and Menu Bar update status', 'dist/core/commands/basic-cli.js', basicArgs('updateCheckCommand'))),
168
+ config: skipMigrationGate(entry('stable', 'Adopt project Codex config into SKS management', 'dist/core/config-adopt/index.js', subcommand(() => import('../core/config-adopt/index.js'), 'configCommand', 'dist/core/config-adopt/index.js', 'adopt'))),
168
169
  mcp: skipMigrationGate(entry('beta', 'Manage scoped Codex MCP configuration', 'dist/core/commands/mcp-config-command.js', argsCommand(() => import('../core/commands/mcp-config-command.js'), 'mcpConfigCommand', 'dist/core/commands/mcp-config-command.js'))),
169
170
  wizard: entry('stable', 'Open setup wizard help', 'dist/core/commands/basic-cli.js', basicNoArgs('quickstartCommand')),
170
171
  usage: readOnly(entry('stable', 'Show focused usage topic', 'dist/core/commands/basic-cli.js', basicArgs('usageCommand'))),
@@ -185,6 +186,7 @@ const COMMAND_DEFINITIONS = {
185
186
  'codex-lb': skipMigrationGate(entry('beta', 'Inspect codex-lb status and circuit health', 'dist/commands/codex-lb.js', directCommand(() => import('../commands/codex-lb.js'), 'dist/commands/codex-lb.js'))),
186
187
  menubar: activeRouteDiagnostic(entry('beta', 'Inspect/install/restart/uninstall SKS menu bar', 'dist/core/commands/menubar-command.js', subcommand(() => import('../core/commands/menubar-command.js'), 'menubarCommand', 'dist/core/commands/menubar-command.js', 'status'))),
187
188
  remote: entry('beta', 'Inspect official Remote readiness and run the proof-aware SSH stdio worker', 'dist/core/commands/remote-command.js', argsCommand(() => import('../core/commands/remote-command.js'), 'remoteCommand', 'dist/core/commands/remote-command.js')),
189
+ telegram: skipMigrationGate(entry('beta', 'Pair and inspect the Telegram remote-control transport', 'dist/core/commands/telegram-command.js', argsCommand(() => import('../core/commands/telegram-command.js'), 'telegramCommand', 'dist/core/commands/telegram-command.js'))),
188
190
  hooks: skipMigrationGate(entry('beta', 'Explain and inspect Codex hooks', 'dist/commands/hooks.js', directCommand(() => import('../commands/hooks.js'), 'dist/commands/hooks.js'))),
189
191
  'zellij-lane': entry('beta', 'Render a Zellij lane frame for SKS sessions', 'dist/commands/zellij-lane.js', directCommand(() => import('../commands/zellij-lane.js'), 'dist/commands/zellij-lane.js')),
190
192
  'zellij-slot-pane': entry('beta', 'Render a compact Zellij worker slot pane', 'dist/commands/zellij-slot-pane.js', directCommand(() => import('../commands/zellij-slot-pane.js'), 'dist/commands/zellij-slot-pane.js')),
@@ -260,6 +262,7 @@ const COMMANDS_WITH_LEGACY_CONTRACT_OVERRIDES = applyCommandContractOverrides(CO
260
262
  check: { risk: 'R1', latency: 'long' },
261
263
  'commit-and-push': { risk: 'R3' },
262
264
  'computer-use': { latency: 'long' },
265
+ config: { risk: 'R2', supportsJson: true, remoteAllowed: false, inputProfile: 'json-only' },
263
266
  dfix: { latency: 'long' },
264
267
  eval: { latency: 'long' },
265
268
  gates: {
@@ -307,6 +310,7 @@ const COMMANDS_WITH_LEGACY_CONTRACT_OVERRIDES = applyCommandContractOverrides(CO
307
310
  requiredCapabilities: ['proof.stop-gate']
308
311
  },
309
312
  task: { risk: 'R1', latency: 'long' },
313
+ telegram: { risk: 'R2', supportsJson: true, remoteAllowed: false, inputProfile: 'json-only' },
310
314
  trust: {
311
315
  risk: 'R0', latency: 'fast', supportsJson: true, remoteAllowed: true, inputProfile: 'trust', requiredCapabilities: ['proof.trust']
312
316
  },
@@ -204,6 +204,193 @@ async function fetchCodexLbResponse(fetchImpl, endpoint, apiKey, body, timeoutMs
204
204
  clearTimeout(timer);
205
205
  }
206
206
  }
207
+ const CODEX_LB_CONNECT_TEST_MAX_OUTPUT_TOKENS = 32;
208
+ const CODEX_LB_CONNECT_TEST_MAX_RESULT_CHARS = 256;
209
+ const CODEX_LB_CONNECT_TEST_MAX_ERROR_CHARS = 512;
210
+ const CODEX_LB_CONNECT_TEST_TIMEOUT_MS = 15_000;
211
+ function boundedText(value, maxChars) {
212
+ const text = String(value || '').trim();
213
+ return text.length <= maxChars ? text : text.slice(0, maxChars);
214
+ }
215
+ function codexLbCompletedResponse(json, events = []) {
216
+ if (json?.response && typeof json.response === 'object')
217
+ return json.response;
218
+ if (json?.data?.response && typeof json.data.response === 'object')
219
+ return json.data.response;
220
+ if (json?.data && typeof json.data === 'object')
221
+ return json.data;
222
+ if (json && typeof json === 'object')
223
+ return json;
224
+ for (let index = events.length - 1; index >= 0; index -= 1) {
225
+ const event = events[index];
226
+ if (event?.type === 'response.completed' && event?.response && typeof event.response === 'object') {
227
+ return event.response;
228
+ }
229
+ }
230
+ return null;
231
+ }
232
+ function codexLbOutputText(response, events = []) {
233
+ const parts = [];
234
+ for (const output of Array.isArray(response?.output) ? response.output : []) {
235
+ for (const content of Array.isArray(output?.content) ? output.content : []) {
236
+ if (content?.type === 'output_text' && typeof content.text === 'string' && content.text.trim()) {
237
+ parts.push(content.text.trim());
238
+ }
239
+ }
240
+ }
241
+ if (parts.length === 0) {
242
+ for (const event of events) {
243
+ if (event?.type === 'response.output_text.done' && typeof event.text === 'string' && event.text.trim()) {
244
+ parts.push(event.text.trim());
245
+ }
246
+ }
247
+ }
248
+ return parts.join('\n').trim();
249
+ }
250
+ function boundedCodexLbUsage(usage) {
251
+ const tokenCount = (value) => Number.isSafeInteger(value) && value >= 0 ? value : null;
252
+ return {
253
+ input_tokens: tokenCount(usage?.input_tokens),
254
+ output_tokens: tokenCount(usage?.output_tokens),
255
+ total_tokens: tokenCount(usage?.total_tokens),
256
+ reasoning_tokens: tokenCount(usage?.output_tokens_details?.reasoning_tokens)
257
+ };
258
+ }
259
+ function codexLbConnectTestFailure(status, extra = {}) {
260
+ return {
261
+ schema: 'sks.codex-lb-connect-test.v1',
262
+ ok: false,
263
+ status,
264
+ response_id: null,
265
+ model: extra.model || null,
266
+ latency_ms: extra.latency_ms ?? null,
267
+ usage: extra.usage || null,
268
+ result: null,
269
+ result_truncated: false,
270
+ http_status: extra.http_status ?? null,
271
+ blockers: Array.isArray(extra.blockers) ? extra.blockers : [status],
272
+ ...(extra.error ? { error: extra.error } : {})
273
+ };
274
+ }
275
+ export async function testCodexLbConnection(status = {}, opts = {}) {
276
+ const providerSelected = status?.selected === true || status?.provider?.selected === true;
277
+ if (opts.requireSelected === true && !providerSelected) {
278
+ return codexLbConnectTestFailure('provider_unselected', {
279
+ blockers: ['codex_lb_provider_not_selected']
280
+ });
281
+ }
282
+ const credentialBindingBlockers = Array.isArray(opts.credentialBindingBlockers)
283
+ ? opts.credentialBindingBlockers.map((value) => boundedText(value, 128)).filter(Boolean)
284
+ : [];
285
+ if (credentialBindingBlockers.length > 0) {
286
+ return codexLbConnectTestFailure('credential_binding_drift', { blockers: credentialBindingBlockers });
287
+ }
288
+ if (status.provider_base_url_matches_credential !== true) {
289
+ return codexLbConnectTestFailure('provider_base_url_mismatch', {
290
+ blockers: ['codex_lb_provider_base_url_mismatch']
291
+ });
292
+ }
293
+ if (status.provider_contract_ok !== true) {
294
+ return codexLbConnectTestFailure('provider_contract_drift', {
295
+ blockers: ['codex_lb_provider_contract_drift']
296
+ });
297
+ }
298
+ const baseUrl = opts.baseUrl || status.base_url;
299
+ const endpoint = codexLbResponsesEndpoint(baseUrl);
300
+ if (!endpoint)
301
+ return codexLbConnectTestFailure('missing_base_url');
302
+ const transportBlocker = codexLbBaseUrlSecurityBlocker(baseUrl);
303
+ if (transportBlocker)
304
+ return codexLbConnectTestFailure('transport_blocked', { blockers: [transportBlocker] });
305
+ const env = opts.env || process.env;
306
+ const home = opts.home || env.HOME || os.homedir();
307
+ const apiKey = opts.apiKey || parseCodexLbEnvKey(await readText(opts.envPath || status.env_path || codexLbEnvPath(home), ''));
308
+ if (!apiKey)
309
+ return codexLbConnectTestFailure('missing_env_key');
310
+ const model = String(opts.model || env.SKS_CODEX_MODEL || env.CODEX_MODEL || '').trim();
311
+ if (!model)
312
+ return codexLbConnectTestFailure('model_unselected');
313
+ if (model.length > 128)
314
+ return codexLbConnectTestFailure('model_invalid');
315
+ const fetchImpl = opts.fetch || globalThis.fetch;
316
+ if (typeof fetchImpl !== 'function')
317
+ return codexLbConnectTestFailure('fetch_unavailable', { model });
318
+ const timeoutMs = Number(opts.timeoutMs || env.SKS_CODEX_LB_CONNECT_TEST_TIMEOUT_MS || CODEX_LB_CONNECT_TEST_TIMEOUT_MS);
319
+ const body = {
320
+ model,
321
+ input: 'Reply OK.',
322
+ store: false,
323
+ reasoning: { effort: 'low' },
324
+ max_output_tokens: CODEX_LB_CONNECT_TEST_MAX_OUTPUT_TOKENS
325
+ };
326
+ const nowMs = typeof opts.nowMs === 'function' ? opts.nowMs : Date.now;
327
+ const startedAt = nowMs();
328
+ const fetched = await fetchCodexLbResponse(fetchImpl, endpoint, apiKey, body, timeoutMs);
329
+ const latencyMs = Math.max(0, Math.round(nowMs() - startedAt));
330
+ if (!fetched.ok) {
331
+ const error = redactSecretText(fetched.error_payload?.error?.message
332
+ || fetched.error_payload?.response?.error?.message
333
+ || fetched.text
334
+ || 'codex-lb Responses request failed', [apiKey]);
335
+ return codexLbConnectTestFailure('request_failed', {
336
+ model,
337
+ latency_ms: latencyMs,
338
+ http_status: fetched.status,
339
+ error: boundedText(error, CODEX_LB_CONNECT_TEST_MAX_ERROR_CHARS)
340
+ });
341
+ }
342
+ const response = codexLbCompletedResponse(fetched.json, fetched.events);
343
+ const responseId = boundedText(codexLbResponseId(response), 128);
344
+ const responseModel = boundedText(response?.model || model, 128);
345
+ const usage = boundedCodexLbUsage(response?.usage);
346
+ if (!responseId) {
347
+ return codexLbConnectTestFailure('missing_response_id', {
348
+ model: responseModel,
349
+ latency_ms: latencyMs,
350
+ http_status: fetched.status,
351
+ usage
352
+ });
353
+ }
354
+ if (response?.error) {
355
+ return codexLbConnectTestFailure('response_error', {
356
+ model: responseModel,
357
+ latency_ms: latencyMs,
358
+ http_status: fetched.status,
359
+ usage,
360
+ error: boundedText(redactSecretText(response.error?.message || response.error, [apiKey]), CODEX_LB_CONNECT_TEST_MAX_ERROR_CHARS)
361
+ });
362
+ }
363
+ if (response?.status !== 'completed') {
364
+ return codexLbConnectTestFailure('response_not_completed', {
365
+ model: responseModel,
366
+ latency_ms: latencyMs,
367
+ http_status: fetched.status,
368
+ usage
369
+ });
370
+ }
371
+ const outputText = codexLbOutputText(response, fetched.events);
372
+ if (!outputText) {
373
+ return codexLbConnectTestFailure('empty_result', {
374
+ model: responseModel,
375
+ latency_ms: latencyMs,
376
+ http_status: fetched.status,
377
+ usage
378
+ });
379
+ }
380
+ return {
381
+ schema: 'sks.codex-lb-connect-test.v1',
382
+ ok: true,
383
+ status: 'connected',
384
+ response_id: responseId,
385
+ model: responseModel,
386
+ latency_ms: latencyMs,
387
+ usage,
388
+ result: boundedText(outputText, CODEX_LB_CONNECT_TEST_MAX_RESULT_CHARS),
389
+ result_truncated: outputText.length > CODEX_LB_CONNECT_TEST_MAX_RESULT_CHARS,
390
+ http_status: fetched.status,
391
+ blockers: []
392
+ };
393
+ }
207
394
  export async function checkCodexLbResponseChain(status = {}, opts = {}) {
208
395
  const env = opts.env || process.env;
209
396
  if (!codexLbChainCheckEnabled(env) && !opts.force)