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
@@ -1,6 +1,7 @@
1
1
  import os from 'node:os';
2
2
  import path from 'node:path';
3
- import { projectRoot, exists, formatBytes, nowIso, writeJsonAtomic } from '../core/fsx.js';
3
+ import { createHash } from 'node:crypto';
4
+ import { projectRoot, exists, formatBytes, nowIso, readText, writeJsonAtomic } from '../core/fsx.js';
4
5
  import { flag } from '../cli/args.js';
5
6
  import { printJson } from '../cli/output.js';
6
7
  import { ui as cliUi } from '../cli/cli-theme.js';
@@ -10,6 +11,15 @@ import { codexAppIntegrationStatus } from '../core/codex-app.js';
10
11
  import { codexLbMetrics, readCodexLbCircuit } from '../core/codex-lb-circuit.js';
11
12
  import { codexLbStatus } from '../cli/install-helpers.js';
12
13
  import { codexLbToolOutputRecoveryOverrideAcknowledged } from '../core/codex-lb/codex-lb-tool-output-recovery.js';
14
+ import { codexLbEnvPath, loadCodexLbEnv } from '../core/codex-lb/codex-lb-env.js';
15
+ import { codexLbLegacyKeychainMigrationStampPath, inspectDesktopCenterLaunchCredentials, inspectCodexLbLegacyKeychainMigration, repairCodexLbLegacyKeychainMigration, syncDesktopCenterLaunchCredentials } from '../core/codex-lb/desktop-center-credentials.js';
16
+ import { codexLbRoutingTruthIsActive, measureAndWriteCodexLbRoutingTruth, readCodexLbRoutingTruthReceipt } from '../core/codex-lb/routing-truth.js';
17
+ import { removeCodexLbOrphanManagedMarkers } from '../cli/install-helpers-codex-lb-config.js';
18
+ import { safeWriteCodexConfigToml } from '../core/codex-runtime/codex-desktop-config-policy.js';
19
+ import { probeTelegram, telegramSelfHealAction } from '../core/telegram/doctor.js';
20
+ import { TelegramClient } from '../core/telegram/client.js';
21
+ import { resolveTelegramBotToken } from '../core/telegram/keychain.js';
22
+ import { telegramLivenessPath } from '../core/telegram/liveness.js';
13
23
  import { normalizeInstallScope } from '../core/init.js';
14
24
  import { inspectCodexConfigReadability } from '../core/codex/codex-config-readability.js';
15
25
  import { inspectOAuthCallbackPortConflict, oauthCallbackDoctorGuidance } from '../core/codex/oauth-callback-port-diagnostic.js';
@@ -27,13 +37,18 @@ import { withSecretPreservationGuard } from '../core/config/config-migration-jou
27
37
  import { reconcileDoctorSkills } from '../core/doctor/doctor-skill-reconcile.js';
28
38
  import { buildSksMenuBarDoctorPostcheck } from '../core/doctor/sks-menubar-doctor.js';
29
39
  import { isUpdateMigrationReceiptCurrent, projectUpdateMigrationReceiptPath, writeProjectUpdateMigrationReceipt } from '../core/update/update-migration-state.js';
30
- import { inspectSksMenuBarStatus, installSksMenuBar, sksMenuBarRestartDeferred } from '../core/codex-app/menubar/index.js';
40
+ import { inspectSksMenuBarStatus, installSksMenuBar, sksMenuBarPaths, sksMenuBarRestartDeferred } from '../core/codex-app/menubar/index.js';
41
+ import { restartLaunchAgent } from '../core/codex-app/menubar/launch-agent.js';
31
42
  import { sweepSksTempDirs } from '../core/retention.js';
32
43
  import { detectImagegenCapability } from '../core/imagegen/imagegen-capability.js';
33
44
  import { CURRENT_CODEX_RELEASE_MANIFEST } from '../core/codex-compat/codex-release-manifest.js';
34
45
  import { formatHarnessConflictReport, scanHarnessConflicts } from '../core/harness-conflicts.js';
35
- import { doctorArgWarnings, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs } from './doctor-profile.js';
36
- export { doctorArgWarnings, doctorMenuBarInstallPolicy, doctorProfileFromArgs } from './doctor-profile.js';
46
+ import { doctorArgWarnings as baseDoctorArgWarnings, doctorMenuBarInstallPolicy, doctorPhaseIdsForProfile, doctorProfileFromArgs } from './doctor-profile.js';
47
+ export { doctorMenuBarInstallPolicy, doctorProfileFromArgs } from './doctor-profile.js';
48
+ const CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG = '--retry-codex-lb-keychain-migration';
49
+ export function doctorArgWarnings(args = []) {
50
+ return baseDoctorArgWarnings(args.filter((arg) => arg !== CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG));
51
+ }
37
52
  export function deferCommandAliasCleanupToMigrationReceipt(result) {
38
53
  const observedBlockers = Array.isArray(result?.blockers)
39
54
  ? result.blockers.map(String).filter(Boolean)
@@ -53,6 +68,196 @@ export function deferCommandAliasCleanupToMigrationReceipt(result) {
53
68
  ]
54
69
  };
55
70
  }
71
+ export async function inspectDoctorCodexLbSecretResolution(input = {}, deps = {}) {
72
+ const processEnv = input.processEnv || process.env;
73
+ const home = path.resolve(input.home || processEnv.HOME || os.homedir());
74
+ const suppliedMigrationOptions = input.migrationOptions || {};
75
+ const migrationOptions = {
76
+ ...suppliedMigrationOptions,
77
+ home,
78
+ env: suppliedMigrationOptions.env || processEnv,
79
+ processEnv: suppliedMigrationOptions.processEnv || processEnv,
80
+ ...(input.baseUrl ? { baseUrl: input.baseUrl } : {}),
81
+ ...(input.forceRetry === true ? { forceRetry: true } : {})
82
+ };
83
+ if (processEnv.SKS_TEST_FORBID_REAL_HOME === '1' && suppliedMigrationOptions.platform === undefined) {
84
+ migrationOptions.platform = 'linux';
85
+ }
86
+ const mode = input.fix === true ? 'repair' : 'inspect';
87
+ const reconcile = mode === 'repair'
88
+ ? deps.repairImpl || repairCodexLbLegacyKeychainMigration
89
+ : deps.inspectImpl || inspectCodexLbLegacyKeychainMigration;
90
+ let migration;
91
+ try {
92
+ migration = await reconcile(migrationOptions);
93
+ }
94
+ catch (error) {
95
+ migration = {
96
+ schema: 'sks.codex-lb-legacy-keychain-reconciliation.v1',
97
+ ok: false,
98
+ status: `${mode}_failed`,
99
+ mode,
100
+ env_key_valid: false,
101
+ keychain_item_present: null,
102
+ prompt_risk: 'none',
103
+ attempted: false,
104
+ stamp_path: codexLbLegacyKeychainMigrationStampPath(home),
105
+ stamp_outcome: null,
106
+ keychain_deleted: false,
107
+ keychain_cleared: [],
108
+ blockers: [`codex_lb_legacy_keychain_${mode}_failed:${error instanceof Error ? error.message : String(error)}`]
109
+ };
110
+ }
111
+ const envPath = String(suppliedMigrationOptions.envPath || codexLbEnvPath(home));
112
+ let loaded = null;
113
+ let resolutionError = null;
114
+ try {
115
+ loaded = await (deps.loadEnvImpl || loadCodexLbEnv)({
116
+ home,
117
+ processEnv,
118
+ envPath,
119
+ ...(suppliedMigrationOptions.metadataPath ? { metadataPath: suppliedMigrationOptions.metadataPath } : {})
120
+ });
121
+ }
122
+ catch (error) {
123
+ resolutionError = error instanceof Error ? error.message : String(error);
124
+ }
125
+ const source = String(loaded?.source || 'missing');
126
+ const unsafeEnvFile = Array.isArray(loaded?.blockers)
127
+ && loaded.blockers.some((blocker) => String(blocker).startsWith('codex_lb_env_file_'));
128
+ const resolutionPath = source === 'env-file' || unsafeEnvFile
129
+ ? String(loaded?.env_paths?.[0] || envPath)
130
+ : null;
131
+ const operatorActions = [];
132
+ if (mode === 'inspect' && migration.prompt_risk === 'one_time_on_repair') {
133
+ operatorActions.push('Run `sks doctor --fix` to perform the one-time legacy Keychain transfer.');
134
+ }
135
+ if (mode === 'repair'
136
+ && migration.ok === false
137
+ && (migration.attempted === true || migration.status === 'already_attempted')) {
138
+ operatorActions.push(`Run \`sks doctor --fix ${CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG}\` to explicitly retry the one-time Keychain transfer.`);
139
+ }
140
+ return {
141
+ ok: migration.ok !== false && resolutionError === null,
142
+ secret_resolution: {
143
+ source,
144
+ path: resolutionPath,
145
+ prompt_risk: migration.prompt_risk || 'none'
146
+ },
147
+ legacy_keychain_migration: {
148
+ ...migration,
149
+ operator_actions: operatorActions,
150
+ ...(resolutionError ? { resolution_error: resolutionError } : {})
151
+ }
152
+ };
153
+ }
154
+ export async function inspectDoctorCodexLbDivergence(input = {}, deps = {}) {
155
+ const processEnv = input.processEnv || process.env;
156
+ const home = path.resolve(input.home || processEnv.HOME || os.homedir());
157
+ const status = input.providerStatus || {};
158
+ const configPath = String(status.config_path || path.join(home, '.codex', 'config.toml'));
159
+ const configText = await (deps.readTextImpl || readText)(configPath, '');
160
+ const orphan = removeCodexLbOrphanManagedMarkers(configText);
161
+ let orphanRepair = {
162
+ schema: 'sks.codex-lb-orphan-managed-marker-repair.v1',
163
+ attempted: false,
164
+ changed: false,
165
+ markers: orphan.orphan_markers,
166
+ receipt: null
167
+ };
168
+ if (input.fix === true && orphan.changed) {
169
+ const write = deps.writeConfigImpl || safeWriteCodexConfigToml;
170
+ const receipt = await write(configPath, configText, orphan.text, 'doctor-codex-lb-orphan-marker');
171
+ orphanRepair = {
172
+ ...orphanRepair,
173
+ attempted: true,
174
+ changed: receipt?.ok !== false,
175
+ receipt
176
+ };
177
+ }
178
+ const load = deps.loadEnvImpl || loadCodexLbEnv;
179
+ const canonical = await load({
180
+ home,
181
+ processEnv: {},
182
+ envPath: String(status.env_path || codexLbEnvPath(home))
183
+ }).catch(() => null);
184
+ const ambientKey = String(processEnv.CODEX_LB_API_KEY || '').trim();
185
+ const canonicalKey = String(canonical?.secret_api_key || '').trim();
186
+ const sha256 = (value) => value
187
+ ? createHash('sha256').update(value).digest('hex')
188
+ : null;
189
+ const ambientHash = sha256(ambientKey);
190
+ const canonicalHash = sha256(canonicalKey);
191
+ const staleAmbient = Boolean(ambientHash && canonicalHash && ambientHash !== canonicalHash);
192
+ const bridgeActive = status.desktop_mode === 'desktop-native-bridge';
193
+ const definedButNotSelected = status.provider_configured === true
194
+ && status.selected !== true
195
+ && !bridgeActive
196
+ && status.desktop_mode !== 'desktop-dual-auth-compat';
197
+ const mode = status.selected === true
198
+ ? 'cli-provider'
199
+ : bridgeActive
200
+ ? 'desktop-native-bridge'
201
+ : 'disabled';
202
+ const inspectLaunch = deps.inspectLaunchImpl || inspectDesktopCenterLaunchCredentials;
203
+ const syncLaunch = deps.syncLaunchImpl || syncDesktopCenterLaunchCredentials;
204
+ const launchOptions = {
205
+ mode,
206
+ home,
207
+ ...(input.launchctlBin ? { launchctlBin: input.launchctlBin } : {}),
208
+ ...(input.platform ? { platform: input.platform } : {}),
209
+ ...(input.forceLaunchctl ? { force: true } : {}),
210
+ ...(input.runProcessImpl ? { runProcessImpl: input.runProcessImpl } : {})
211
+ };
212
+ const launchBefore = await inspectLaunch(launchOptions);
213
+ const launchRepair = input.fix === true && launchBefore.ok === false
214
+ ? await syncLaunch({ ...launchOptions, skipPurge: true })
215
+ : null;
216
+ const launchAfter = launchRepair ? await inspectLaunch(launchOptions) : launchBefore;
217
+ const warnings = [
218
+ ...(definedButNotSelected
219
+ ? ['codex_lb_defined_but_not_selected']
220
+ : []),
221
+ ...(staleAmbient ? ['codex_lb_stale_ambient_key'] : []),
222
+ ...(orphan.orphan_markers.length > 0
223
+ ? [`codex_lb_orphan_managed_markers:${orphan.orphan_markers.join(',')}`]
224
+ : [])
225
+ ];
226
+ const blockers = [
227
+ ...(launchAfter.ok === false ? launchAfter.blockers || ['codex_lb_launchd_selection_state_mismatch'] : []),
228
+ ...(input.fix === true && orphan.changed && orphanRepair.changed !== true
229
+ ? ['codex_lb_orphan_marker_repair_failed']
230
+ : [])
231
+ ];
232
+ return {
233
+ schema: 'sks.doctor-codex-lb-divergence.v1',
234
+ ok: blockers.length === 0,
235
+ warnings,
236
+ blockers,
237
+ operator_actions: [
238
+ ...(definedButNotSelected
239
+ ? ['Run `sks codex-lb use-cli` to select the stored provider.']
240
+ : []),
241
+ ...(staleAmbient
242
+ ? ['Remove or update the stale shell export: `unset CODEX_LB_API_KEY`; the canonical key remains in ~/.codex/sks-codex-lb.env.']
243
+ : []),
244
+ ...(launchAfter.operator_actions || [])
245
+ ],
246
+ ambient_key: {
247
+ present: Boolean(ambientKey),
248
+ sha256: ambientHash,
249
+ matches_canonical: ambientHash && canonicalHash ? ambientHash === canonicalHash : null
250
+ },
251
+ canonical_key: {
252
+ present: Boolean(canonicalKey),
253
+ sha256: canonicalHash,
254
+ path: String(status.env_path || codexLbEnvPath(home))
255
+ },
256
+ orphan_markers: orphan.orphan_markers,
257
+ orphan_marker_repair: orphanRepair,
258
+ launchd: { before: launchBefore, repair: launchRepair, after: launchAfter }
259
+ };
260
+ }
56
261
  export async function run(_command, args = [], deps = {}) {
57
262
  const root = await projectRoot();
58
263
  if (flag(args, '--search')) {
@@ -102,9 +307,9 @@ export async function run(_command, args = [], deps = {}) {
102
307
  : root;
103
308
  return withSecretPreservationGuard(guardRoot, 'doctor-fix', async () => (globalOnly
104
309
  ? runDoctorGlobalOnlyFix(args, root, deps)
105
- : runDoctor(args, root, doctorFix)));
310
+ : runDoctor(args, root, doctorFix, deps)));
106
311
  }
107
- return runDoctor(args, root, doctorFix);
312
+ return runDoctor(args, root, doctorFix, deps);
108
313
  }
109
314
  export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
110
315
  const startedAtMs = Date.now();
@@ -122,7 +327,29 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
122
327
  const doctorEnv = deps.env || (deps.home
123
328
  ? { ...process.env, HOME: home }
124
329
  : process.env);
330
+ const statusOptions = {
331
+ home,
332
+ env: doctorEnv,
333
+ processEnv: doctorEnv
334
+ };
335
+ const preRepairProviderStatus = await codexLbStatusImpl({
336
+ ...statusOptions,
337
+ probeToolOutputRecovery: false
338
+ }).catch(() => null);
339
+ const codexLbSecretProbeImpl = deps.codexLbSecretProbeImpl || inspectDoctorCodexLbSecretResolution;
340
+ const codexLbSecretProbe = await codexLbSecretProbeImpl({
341
+ home,
342
+ processEnv: doctorEnv,
343
+ fix: true,
344
+ forceRetry: flag(args, CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG),
345
+ baseUrl: preRepairProviderStatus?.base_url || preRepairProviderStatus?.provider_base_url || null,
346
+ migrationOptions: {
347
+ ...(deps.codexLbMigrationOptions || {}),
348
+ ...(!deps.codexLbMigrationOptions && deps.codexLbStatusImpl ? { platform: 'linux' } : {})
349
+ }
350
+ });
125
351
  const providerStatus = await codexLbStatusImpl({
352
+ ...statusOptions,
126
353
  probeToolOutputRecovery: true,
127
354
  allowUnverifiedToolOutputRecovery: codexLbToolOutputRecoveryOverrideAcknowledged({ args })
128
355
  }).catch((err) => ({
@@ -188,6 +415,25 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
188
415
  blockers: [err?.message || String(err)],
189
416
  warnings: []
190
417
  }));
418
+ const codexLbRoutingTruth = await codexLbRoutingTruthForStatus(providerStatus, { home, remeasure: true });
419
+ const codexLbRoutingReady = !doctorCodexLbRouteExpected(providerStatus)
420
+ || codexLbRoutingTruthIsActive(codexLbRoutingTruth);
421
+ const codexLbDivergence = await inspectDoctorCodexLbDivergence({
422
+ home,
423
+ processEnv: doctorEnv,
424
+ providerStatus,
425
+ fix: true,
426
+ ...(deps.codexLbLaunchctlBin ? { launchctlBin: deps.codexLbLaunchctlBin } : {}),
427
+ ...(deps.codexLbLaunchPlatform ? { platform: deps.codexLbLaunchPlatform } : {}),
428
+ ...(deps.codexLbLaunchRunProcessImpl ? { runProcessImpl: deps.codexLbLaunchRunProcessImpl } : {})
429
+ }, deps.codexLbDivergenceDeps || {});
430
+ const telegramRemote = await inspectTelegramRemote({
431
+ live: true,
432
+ fix: true,
433
+ root,
434
+ home,
435
+ env: doctorEnv
436
+ }, deps);
191
437
  const recoveryReady = codexLbRecoveryStatusReady(providerStatus, true);
192
438
  const globalSkillsReady = !globalSkills?.error
193
439
  && globalSkills?.ok !== false
@@ -202,7 +448,12 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
202
448
  ...(!globalFastModeReady ? [`global_fast_mode_repair_failed:${globalFastMode?.error || globalFastMode?.status || 'unknown'}`] : []),
203
449
  ...(!openRouterProviderReady ? (openRouterProvider?.blockers || ['openrouter_provider_repair_failed']) : []),
204
450
  ...(!menuBarReady ? (menuBar?.blockers || ['sks_menubar_repair_failed']) : []),
205
- ...(!recoveryReady ? (providerStatus?.tool_output_recovery?.blockers || ['codex_lb_tool_output_recovery_unverified']) : [])
451
+ ...(codexLbSecretProbe.ok === false
452
+ ? (codexLbSecretProbe.legacy_keychain_migration?.blockers || ['codex_lb_secret_resolution_failed'])
453
+ : []),
454
+ ...(!recoveryReady ? (providerStatus?.tool_output_recovery?.blockers || ['codex_lb_tool_output_recovery_unverified']) : []),
455
+ ...(!codexLbRoutingReady ? (codexLbRoutingTruth?.blockers || ['codex_lb_routing_truth_unverified']) : []),
456
+ ...(codexLbDivergence.blockers || [])
206
457
  ].map(String).filter(Boolean))];
207
458
  const ok = blockers.length === 0;
208
459
  return {
@@ -231,14 +482,23 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
231
482
  codex_app_fast_mode: globalFastMode,
232
483
  openrouter_provider: openRouterProvider,
233
484
  sks_menubar: menuBar,
485
+ telegram_remote: telegramRemote,
234
486
  codex_lb: {
235
487
  provider_status: providerStatus,
236
488
  tool_output_recovery: providerStatus?.tool_output_recovery || null,
237
- recovery_ok: recoveryReady
489
+ recovery_ok: recoveryReady,
490
+ routing_truth: codexLbRoutingTruth,
491
+ routing_ok: codexLbRoutingReady,
492
+ secret_resolution: codexLbSecretProbe.secret_resolution,
493
+ legacy_keychain_migration: codexLbSecretProbe.legacy_keychain_migration,
494
+ divergence: codexLbDivergence
238
495
  },
239
496
  blockers,
240
497
  next_actions: [
241
498
  ...(recoveryReady ? [] : (providerStatus?.tool_output_recovery?.operator_actions || [])),
499
+ ...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
500
+ ...(codexLbDivergence.operator_actions || []),
501
+ ...telegramDoctorOperatorActions(telegramRemote),
242
502
  ...(!openRouterProviderReady ? ['Run `sks codex-app set-openrouter-key` to repair the stored-key provider configuration without switching models.'] : []),
243
503
  'Run `sks doctor --fix --json` from a specific project directory when project-scoped repair is required.'
244
504
  ]
@@ -261,6 +521,15 @@ async function runDoctorGlobalOnlyFix(args = [], root, deps = {}) {
261
521
  console.log(`SKS Doctor global repair: ${result.ok ? 'ok' : 'blocked'}`);
262
522
  console.log(`Global skills: ${result.skills.global?.error ? 'blocked' : 'reconciled'}`);
263
523
  console.log(`SKS menu bar: ${result.sks_menubar?.status || (result.sks_menubar?.ok ? 'ok' : 'blocked')}`);
524
+ const telegramOutcome = result.telegram_remote?.self_heal_outcome;
525
+ const telegramAction = telegramOutcome?.attempted
526
+ ? telegramOutcome.action
527
+ : result.telegram_remote?.self_heal_action || 'none';
528
+ console.log(`Telegram Remote: ${result.telegram_remote?.status || 'unknown'} (self-heal ${telegramAction}${telegramOutcome?.attempted ? `, ${telegramOutcome.recovered ? 'recovered' : 'still degraded'}` : ''})`);
529
+ const telegramCheckedLine = telegramDoctorCheckedLine(result.telegram_remote);
530
+ if (telegramCheckedLine)
531
+ console.log(telegramCheckedLine);
532
+ console.log(`codex-lb key: ${result.codex_lb.secret_resolution.source} (prompt risk ${result.codex_lb.secret_resolution.prompt_risk})`);
264
533
  for (const blocker of result.blockers)
265
534
  console.log(`- blocker: ${blocker}`);
266
535
  for (const action of result.next_actions)
@@ -277,8 +546,203 @@ function codexLbRecoveryStatusReady(status, probeRequired = false) {
277
546
  return false;
278
547
  if (status.selected === false)
279
548
  return true;
549
+ if (!probeRequired)
550
+ return true;
280
551
  return status.selected === true && status.tool_output_recovery?.ok === true;
281
552
  }
553
+ export async function codexLbRoutingTruthForStatus(status, deps = {}) {
554
+ if (!status)
555
+ return null;
556
+ const statusEnvPath = typeof status.env_path === 'string' && status.env_path
557
+ ? status.env_path
558
+ : null;
559
+ const resolvedHome = deps.home
560
+ ? path.resolve(deps.home)
561
+ : statusEnvPath
562
+ ? path.dirname(path.dirname(statusEnvPath))
563
+ : undefined;
564
+ const envOptions = {
565
+ ...(resolvedHome ? { home: resolvedHome } : {}),
566
+ ...(statusEnvPath ? { envPath: statusEnvPath } : {})
567
+ };
568
+ const routeSelected = doctorCodexLbRouteExpected(status);
569
+ const mode = status.desktop_mode === 'desktop-native-bridge' ? 'bridge' : 'cli-provider';
570
+ const receiptOptions = {
571
+ ...(resolvedHome ? { home: resolvedHome } : {}),
572
+ ...(deps.receiptPath ? { receiptPath: deps.receiptPath } : {}),
573
+ expectedMode: mode,
574
+ expectedSelected: routeSelected
575
+ };
576
+ const receipt = await readCodexLbRoutingTruthReceipt(receiptOptions).catch(() => null);
577
+ if (deps.remeasure !== true && receipt?.fresh === true)
578
+ return receipt;
579
+ try {
580
+ const loaded = await loadCodexLbEnv({ ...envOptions, processEnv: {} });
581
+ const baseUrl = loaded.base_url || status.base_url || null;
582
+ const configuredHost = publicUrlHost(baseUrl);
583
+ const authTransport = mode === 'cli-provider'
584
+ ? 'authorization-bearer'
585
+ : doctorBridgeRoutingTransport(status, receipt);
586
+ const contextReceipt = await readCodexLbRoutingTruthReceipt({
587
+ ...receiptOptions,
588
+ expectedConfiguredHost: configuredHost,
589
+ expectedAuthTransport: authTransport
590
+ }).catch(() => null);
591
+ if (deps.remeasure !== true && contextReceipt?.fresh === true)
592
+ return contextReceipt;
593
+ return measureAndWriteCodexLbRoutingTruth({
594
+ mode,
595
+ selected: routeSelected,
596
+ baseUrl,
597
+ apiKey: loaded.secret_api_key,
598
+ authTransport,
599
+ measure: routeSelected,
600
+ ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {})
601
+ }, {
602
+ ...envOptions,
603
+ ...(deps.receiptPath ? { receiptPath: deps.receiptPath } : {})
604
+ });
605
+ }
606
+ catch {
607
+ return measureAndWriteCodexLbRoutingTruth({
608
+ mode,
609
+ selected: routeSelected,
610
+ baseUrl: status.base_url || null,
611
+ apiKey: null,
612
+ authTransport: mode === 'cli-provider'
613
+ ? 'authorization-bearer'
614
+ : doctorBridgeRoutingTransport(status, receipt),
615
+ measure: false
616
+ }, {
617
+ ...envOptions,
618
+ ...(deps.receiptPath ? { receiptPath: deps.receiptPath } : {})
619
+ });
620
+ }
621
+ }
622
+ function doctorCodexLbRouteExpected(status) {
623
+ return status?.selected === true || status?.desktop_mode === 'desktop-native-bridge';
624
+ }
625
+ function doctorBridgeRoutingTransport(status, receipt) {
626
+ if (receipt?.mode === 'bridge'
627
+ && (receipt.auth_transport === 'authorization-bearer'
628
+ || receipt.auth_transport === 'x-codex-lb-api-key')) {
629
+ return receipt.auth_transport;
630
+ }
631
+ return status?.gateway_auth_transport === 'authorization-bearer-compat'
632
+ ? 'authorization-bearer'
633
+ : 'x-codex-lb-api-key';
634
+ }
635
+ function publicUrlHost(value) {
636
+ try {
637
+ return value ? new URL(String(value)).host : null;
638
+ }
639
+ catch {
640
+ return null;
641
+ }
642
+ }
643
+ export async function inspectTelegramRemote(input = {}, deps = {}) {
644
+ const env = input.env || process.env;
645
+ const home = path.resolve(input.home || env.HOME || os.homedir());
646
+ const receiptPath = input.receiptPath || telegramLivenessPath(home);
647
+ const probeImpl = deps.probeTelegramImpl || probeTelegram;
648
+ let tokenProvider = deps.telegramTokenProvider;
649
+ let client = deps.telegramClient;
650
+ if (input.live && (!tokenProvider || !client)) {
651
+ const resolveImpl = deps.resolveTelegramBotTokenImpl || resolveTelegramBotToken;
652
+ let resolvedToken = null;
653
+ tokenProvider ||= {
654
+ loadToken: () => {
655
+ resolvedToken ||= Promise.resolve(resolveImpl({ env })).then((result) => result.token);
656
+ return resolvedToken;
657
+ }
658
+ };
659
+ client ||= new TelegramClient({ tokenProvider });
660
+ }
661
+ const runProbe = () => {
662
+ if (!input.live)
663
+ return probeImpl({ receiptPath });
664
+ if (!client || !tokenProvider)
665
+ throw new Error('telegram_live_probe_dependencies_missing');
666
+ return probeImpl({ client, tokenProvider, receiptPath });
667
+ };
668
+ const before = await runProbe();
669
+ const action = telegramSelfHealAction(before);
670
+ if (!input.fix || action !== 'restart_poll' || !before.token_configured || before.paired_chat_count < 1) {
671
+ return {
672
+ ...before,
673
+ self_heal_action: action,
674
+ self_heal_outcome: {
675
+ requested: input.fix === true,
676
+ attempted: false,
677
+ action,
678
+ reason: input.fix !== true
679
+ ? 'report_only'
680
+ : action !== 'restart_poll'
681
+ ? 'action_not_restart_poll'
682
+ : 'telegram_not_configured_or_paired'
683
+ }
684
+ };
685
+ }
686
+ const restartImpl = deps.restartLaunchAgentImpl || restartLaunchAgent;
687
+ const restart = await restartImpl(sksMenuBarPaths(home, input.root), env).catch((error) => ({
688
+ ok: false,
689
+ error: error instanceof Error ? error.message : String(error)
690
+ }));
691
+ const maximumReprobes = Math.max(1, Math.min(deps.telegramReprobeAttempts ?? 3, 5));
692
+ const reprobeDelayMs = Math.max(0, Math.min(deps.telegramReprobeDelayMs ?? 500, 5_000));
693
+ const sleepImpl = deps.telegramSleepImpl || ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
694
+ let reprobeAttempts = 1;
695
+ let after = await runProbe();
696
+ let afterAction = telegramSelfHealAction(after);
697
+ while (restart?.ok === true && afterAction === 'restart_poll' && reprobeAttempts < maximumReprobes) {
698
+ await sleepImpl(reprobeDelayMs);
699
+ after = await runProbe();
700
+ afterAction = telegramSelfHealAction(after);
701
+ reprobeAttempts += 1;
702
+ }
703
+ return {
704
+ ...after,
705
+ self_heal_action: afterAction,
706
+ self_heal_attempted_action: action,
707
+ self_heal_before: before,
708
+ self_heal_outcome: {
709
+ requested: true,
710
+ attempted: true,
711
+ action,
712
+ after_action: afterAction,
713
+ restart_ok: restart?.ok === true,
714
+ reprobe_ok: after.ok,
715
+ reprobe_attempts: reprobeAttempts,
716
+ recovered: restart?.ok === true && after.ok && afterAction === 'none',
717
+ error: restart?.error || null,
718
+ restart
719
+ }
720
+ };
721
+ }
722
+ export function telegramDoctorCheckedLine(probe, indent = '') {
723
+ if (!probe?.getme_checked_at)
724
+ return null;
725
+ return `${indent}checked: ${probe.getme_checked_at} (${probe.getme_latency_ms ?? 'unknown'} ms, ${probe.getme_check_kind || 'receipt'})`;
726
+ }
727
+ function telegramDoctorOperatorActions(probe) {
728
+ if (!probe || probe.status === 'not_configured')
729
+ return [];
730
+ if (probe.self_heal_action === 'restart_poll') {
731
+ if (probe.self_heal_outcome?.recovered === true)
732
+ return [];
733
+ return ['Run `sks menubar restart` to restart the resident Telegram long poller.'];
734
+ }
735
+ if (probe.self_heal_action === 'revalidate_token') {
736
+ return ['Run `sks telegram setup --token-stdin`, then `sks menubar restart`, to revalidate the bot identity.'];
737
+ }
738
+ if (probe.self_heal_action === 'operator_remove_webhook') {
739
+ return ['Remove the Telegram bot webhook through the Bot API, then run `sks menubar restart`; getUpdates and webhooks cannot be active together.'];
740
+ }
741
+ if (probe.self_heal_action === 'operator_repair_audit') {
742
+ return ['Restore owner-only write access to `~/.codex/sks-menubar/logs/telegram-audit.jsonl`, then run `sks menubar restart`.'];
743
+ }
744
+ return [];
745
+ }
282
746
  async function runDoctorJsonFastPath(args = [], root) {
283
747
  const startedAtMs = Date.now();
284
748
  const reportFile = readOption(args, '--report-file', null);
@@ -289,7 +753,7 @@ async function runDoctorJsonFastPath(args = [], root) {
289
753
  requireActualCodex: false,
290
754
  codexBin: codexBin || undefined
291
755
  };
292
- const [codex, rust, codexConfig, sneakoscopeExists, oauthCallbackPortDiagnostic] = await Promise.all([
756
+ const [codex, rust, codexConfig, sneakoscopeExists, oauthCallbackPortDiagnostic, codexLbSecretProbe] = await Promise.all([
293
757
  codexBin
294
758
  ? Promise.resolve({ bin: codexBin, version: 'fixture-or-explicit', available: true })
295
759
  : getCodexInfo().catch(() => ({ bin: null, version: null, available: false })),
@@ -301,9 +765,11 @@ async function runDoctorJsonFastPath(args = [], root) {
301
765
  blockers: [err?.message || String(err)]
302
766
  })),
303
767
  exists(`${root}/.sneakoscope`),
304
- inspectOAuthCallbackPortConflict()
768
+ inspectOAuthCallbackPortConflict(),
769
+ inspectDoctorCodexLbSecretResolution({ processEnv: process.env, fix: false })
305
770
  ]);
306
771
  const oauthCallbackOperatorActions = oauthCallbackDoctorGuidance(oauthCallbackPortDiagnostic);
772
+ const telegramRemote = await inspectTelegramRemote();
307
773
  const ready = {
308
774
  schema: 'sks.doctor-readiness-matrix.v2',
309
775
  generated_at: nowIso(),
@@ -340,13 +806,20 @@ async function runDoctorJsonFastPath(args = [], root) {
340
806
  deep_diagnostics_skipped: true,
341
807
  deep_ok: null,
342
808
  not_counted_as_full_doctor: true,
343
- next_actions: ['Run sks doctor --full --json for deep diagnostics.'],
809
+ next_actions: [
810
+ 'Run sks doctor --full --json for deep diagnostics.',
811
+ ...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
812
+ ...telegramDoctorOperatorActions(telegramRemote)
813
+ ],
344
814
  root,
345
815
  fast_path: true,
346
816
  no_fix_write_policy: reportFile ? 'report_file_only' : 'no_writes_performed',
347
817
  arg_warnings: doctorArgWarnings(args),
348
818
  warnings: [...oauthCallbackPortDiagnostic.warnings],
349
- operator_actions: oauthCallbackOperatorActions,
819
+ operator_actions: [
820
+ ...oauthCallbackOperatorActions,
821
+ ...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || [])
822
+ ],
350
823
  node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
351
824
  codex,
352
825
  oauth_callback_port_diagnostic: oauthCallbackPortDiagnostic,
@@ -371,6 +844,7 @@ async function runDoctorJsonFastPath(args = [], root) {
371
844
  blockers: [],
372
845
  warnings: ['menubar_install_deferred_to_fix_or_full_doctor']
373
846
  },
847
+ telegram_remote: telegramRemote,
374
848
  provider_context: {
375
849
  schema: 'sks.provider-context.v1',
376
850
  generated_at: nowIso(),
@@ -384,7 +858,13 @@ async function runDoctorJsonFastPath(args = [], root) {
384
858
  warnings: ['provider_context_optional_diagnostic_skipped'],
385
859
  signals: {}
386
860
  },
387
- codex_lb: codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({}))),
861
+ codex_lb: {
862
+ ...codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({}))),
863
+ routing_truth: null,
864
+ routing_measurement_deferred: true,
865
+ secret_resolution: codexLbSecretProbe.secret_resolution,
866
+ legacy_keychain_migration: codexLbSecretProbe.legacy_keychain_migration
867
+ },
388
868
  codex_doctor: null,
389
869
  pre_repair_codex_doctor: null,
390
870
  post_repair_codex_doctor: null,
@@ -452,7 +932,7 @@ async function runDoctorJsonFastPath(args = [], root) {
452
932
  process.exitCode = 1;
453
933
  return result;
454
934
  }
455
- async function runDoctor(args = [], root, doctorFix) {
935
+ async function runDoctor(args = [], root, doctorFix, deps = {}) {
456
936
  const startedAtMs = Date.now();
457
937
  const oauthCallbackPortDiagnostic = await inspectOAuthCallbackPortConflict();
458
938
  const oauthCallbackOperatorActions = oauthCallbackDoctorGuidance(oauthCallbackPortDiagnostic);
@@ -630,29 +1110,54 @@ async function runDoctor(args = [], root, doctorFix) {
630
1110
  ? await codexAppIntegrationStatus({ codex }).catch((err) => ({ ok: false, error: err.message }))
631
1111
  : { ok: false, skipped: true, warnings: ['codex_app_optional_diagnostic_skipped'] };
632
1112
  const codexLbCircuit = codexLbMetrics(await readCodexLbCircuit(root).catch(() => ({})));
633
- const codexLbProviderStatus = deepDiagnostics || doctorFix
634
- ? await codexLbStatus({
635
- probeToolOutputRecovery: true,
636
- allowUnverifiedToolOutputRecovery: codexLbToolOutputRecoveryOverrideAcknowledged({ args })
637
- }).catch((err) => ({
638
- selected: null,
639
- provider_ready: false,
640
- recovery_probe_failed: true,
641
- tool_output_recovery: {
642
- ok: false,
643
- status: 'probe_failed',
644
- blockers: ['codex_lb_tool_output_recovery_status_probe_failed'],
645
- operator_actions: []
646
- },
647
- error: err?.message || String(err)
648
- }))
1113
+ const codexLbPreRepairStatus = doctorFix
1114
+ ? await codexLbStatus({ probeToolOutputRecovery: false }).catch(() => null)
649
1115
  : null;
1116
+ const codexLbSecretProbe = await inspectDoctorCodexLbSecretResolution({
1117
+ processEnv: process.env,
1118
+ fix: doctorFix,
1119
+ forceRetry: flag(args, CODEX_LB_KEYCHAIN_MIGRATION_RETRY_FLAG),
1120
+ baseUrl: codexLbPreRepairStatus?.base_url || codexLbPreRepairStatus?.provider_base_url || null
1121
+ });
1122
+ const codexLbProviderStatus = await codexLbStatus({
1123
+ probeToolOutputRecovery: deepDiagnostics || doctorFix,
1124
+ allowUnverifiedToolOutputRecovery: codexLbToolOutputRecoveryOverrideAcknowledged({ args })
1125
+ }).catch((err) => ({
1126
+ selected: null,
1127
+ provider_ready: false,
1128
+ recovery_probe_failed: deepDiagnostics || doctorFix,
1129
+ tool_output_recovery: {
1130
+ ok: false,
1131
+ status: deepDiagnostics || doctorFix ? 'probe_failed' : 'not_checked',
1132
+ blockers: deepDiagnostics || doctorFix ? ['codex_lb_tool_output_recovery_status_probe_failed'] : [],
1133
+ operator_actions: []
1134
+ },
1135
+ error: err?.message || String(err)
1136
+ }));
650
1137
  const codexLbRecoveryReady = codexLbRecoveryStatusReady(codexLbProviderStatus, deepDiagnostics || doctorFix);
1138
+ const codexLbRoutingTruth = await codexLbRoutingTruthForStatus(codexLbProviderStatus, {
1139
+ remeasure: deepDiagnostics || doctorFix
1140
+ });
1141
+ const codexLbRoutingReady = !doctorCodexLbRouteExpected(codexLbProviderStatus)
1142
+ || codexLbRoutingTruthIsActive(codexLbRoutingTruth);
1143
+ const codexLbDivergence = await inspectDoctorCodexLbDivergence({
1144
+ processEnv: process.env,
1145
+ providerStatus: codexLbProviderStatus,
1146
+ fix: doctorFix,
1147
+ ...(deps.codexLbLaunchctlBin ? { launchctlBin: deps.codexLbLaunchctlBin } : {}),
1148
+ ...(deps.codexLbLaunchPlatform ? { platform: deps.codexLbLaunchPlatform } : {}),
1149
+ ...(deps.codexLbLaunchRunProcessImpl ? { runProcessImpl: deps.codexLbLaunchRunProcessImpl } : {})
1150
+ }, deps.codexLbDivergenceDeps || {});
651
1151
  const codexLb = {
652
1152
  ...codexLbCircuit,
653
1153
  provider_status: codexLbProviderStatus,
654
1154
  tool_output_recovery: codexLbProviderStatus?.tool_output_recovery || null,
655
- recovery_ok: codexLbRecoveryReady
1155
+ recovery_ok: codexLbRecoveryReady,
1156
+ routing_truth: codexLbRoutingTruth,
1157
+ routing_ok: codexLbRoutingReady,
1158
+ secret_resolution: codexLbSecretProbe.secret_resolution,
1159
+ legacy_keychain_migration: codexLbSecretProbe.legacy_keychain_migration,
1160
+ divergence: codexLbDivergence
656
1161
  };
657
1162
  const providerContext = deepDiagnostics
658
1163
  ? await resolveProviderContext({ root, route: '$Doctor', serviceTier: process.env.SKS_SERVICE_TIER || 'fast' }).catch((err) => ({
@@ -756,6 +1261,12 @@ async function runDoctor(args = [], root, doctorFix) {
756
1261
  blockers: [err?.message || String(err)],
757
1262
  warnings: []
758
1263
  }));
1264
+ const telegramRemote = await inspectTelegramRemote({
1265
+ live: deepDiagnostics || doctorFix,
1266
+ fix: doctorFix,
1267
+ root,
1268
+ env: process.env
1269
+ }, deps);
759
1270
  const zellijRepair = shouldRunZellijRepair
760
1271
  ? await runDoctorZellijRepair({ root, args, doctorFix }).catch((err) => ({
761
1272
  schema: 'sks.zellij-self-heal.v1',
@@ -968,14 +1479,34 @@ async function runDoctor(args = [], root, doctorFix) {
968
1479
  rollback_evidence: sksMenuBar?.launch_agent_path || sksMenuBar?.report_path || 'sks_menubar_optional_no_core_mutation'
969
1480
  }),
970
1481
  postcheck: async () => {
971
- const status = await inspectSksMenuBarStatus({ root }).catch((err) => ({
1482
+ const inspectMenuBarStatusImpl = deps.inspectSksMenuBarStatusImpl || inspectSksMenuBarStatus;
1483
+ const status = await inspectMenuBarStatusImpl({ root }).catch((err) => ({
972
1484
  ok: false,
973
1485
  launchd: { ok: false, state: null, pid: null, error: err?.message || String(err) },
974
1486
  action_target: { ok: false, smoke_code: null, smoke_output: null },
975
1487
  blockers: [err?.message || String(err)],
976
1488
  warnings: []
977
1489
  }));
978
- return buildSksMenuBarDoctorPostcheck(status);
1490
+ const launchdRepair = await rebootstrapSksMenuBarLaunchdForDoctorFix({
1491
+ fix: doctorFix,
1492
+ root,
1493
+ env: process.env,
1494
+ status
1495
+ }, {
1496
+ inspectSksMenuBarStatusImpl: inspectMenuBarStatusImpl,
1497
+ restartLaunchAgentImpl: deps.restartLaunchAgentImpl
1498
+ });
1499
+ const postcheck = buildSksMenuBarDoctorPostcheck(launchdRepair.status);
1500
+ return launchdRepair.attempted
1501
+ ? {
1502
+ ...postcheck,
1503
+ repaired: launchdRepair.ok,
1504
+ warnings: [
1505
+ ...(postcheck.warnings || []),
1506
+ launchdRepair.ok ? 'launchd_rebootstrap_recovered' : 'launchd_rebootstrap_failed'
1507
+ ]
1508
+ }
1509
+ : postcheck;
979
1510
  }
980
1511
  },
981
1512
  {
@@ -1284,6 +1815,7 @@ async function runDoctor(args = [], root, doctorFix) {
1284
1815
  repair: configRepair,
1285
1816
  codex_app_ui: codexAppUi,
1286
1817
  sks_menubar: sksMenuBar,
1818
+ telegram_remote: telegramRemote,
1287
1819
  codex_0138_doctor: codex0138Doctor,
1288
1820
  codex_plugin_inventory: pluginInventory?.report || null,
1289
1821
  codex_plugin_app_template_policy: pluginPolicy,
@@ -1295,7 +1827,9 @@ async function runDoctor(args = [], root, doctorFix) {
1295
1827
  ...(zellijRepair && !zellijRepair.ok && zellijRepair.command ? [`Run: ${zellijRepair.command}`] : []),
1296
1828
  ...(codexStartupRepair.manual_actions || []),
1297
1829
  ...(codexConfigSyntaxRepair?.manual_actions || []),
1298
- ...(pluginPolicy?.doctor_warnings || [])
1830
+ ...(pluginPolicy?.doctor_warnings || []),
1831
+ ...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
1832
+ ...telegramDoctorOperatorActions(telegramRemote)
1299
1833
  ]
1300
1834
  };
1301
1835
  let ready = await writeDoctorReadinessMatrix(root, doctorReadinessInput);
@@ -1316,6 +1850,7 @@ async function runDoctor(args = [], root, doctorFix) {
1316
1850
  && readinessBlockers.every(isMigrationUserOwnedProjectConfigBlocker);
1317
1851
  const receiptBlockers = preservedUserOwnedConfig ? [] : readinessBlockers;
1318
1852
  const migrationWarnings = [
1853
+ ...(commandAliasCleanup?.warnings || []),
1319
1854
  ...(doctorNativeCapabilityRepair?.optional_warnings || []),
1320
1855
  ...(doctorFixPostcheck?.optional_warnings || []),
1321
1856
  ...(preservedUserOwnedConfig
@@ -1371,7 +1906,10 @@ async function runDoctor(args = [], root, doctorFix) {
1371
1906
  && agentRoleConfigRepair.ok !== false
1372
1907
  && openRouterProviderRepair.ok !== false
1373
1908
  && (officialSubagentConfig.blockers || []).length === 0
1374
- && codexLbRecoveryReady;
1909
+ && codexLbSecretProbe.ok !== false
1910
+ && codexLbDivergence.ok !== false
1911
+ && codexLbRecoveryReady
1912
+ && codexLbRoutingReady;
1375
1913
  const result = {
1376
1914
  schema: 'sks.doctor-status.v3',
1377
1915
  elapsed_ms: Date.now() - startedAtMs,
@@ -1386,8 +1924,12 @@ async function runDoctor(args = [], root, doctorFix) {
1386
1924
  not_counted_as_full_doctor: !deepDiagnostics,
1387
1925
  root,
1388
1926
  arg_warnings: argWarnings,
1389
- warnings: [...oauthCallbackPortDiagnostic.warnings],
1390
- operator_actions: oauthCallbackOperatorActions,
1927
+ warnings: [...oauthCallbackPortDiagnostic.warnings, ...(codexLbDivergence.warnings || [])],
1928
+ operator_actions: [
1929
+ ...oauthCallbackOperatorActions,
1930
+ ...(codexLbSecretProbe.legacy_keychain_migration?.operator_actions || []),
1931
+ ...(codexLbDivergence.operator_actions || [])
1932
+ ],
1391
1933
  node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
1392
1934
  codex,
1393
1935
  oauth_callback_port_diagnostic: oauthCallbackPortDiagnostic,
@@ -1397,6 +1939,7 @@ async function runDoctor(args = [], root, doctorFix) {
1397
1939
  codex_app_ui: codexAppUi,
1398
1940
  openrouter_provider: openRouterProviderRepair,
1399
1941
  sks_menubar: sksMenuBar,
1942
+ telegram_remote: telegramRemote,
1400
1943
  provider_context: providerContext,
1401
1944
  codex_lb: codexLb,
1402
1945
  codex_doctor: authoritativeCodexDoctor,
@@ -1607,6 +2150,8 @@ async function runDoctor(args = [], root, doctorFix) {
1607
2150
  console.log(` next action: ${codexAppUi.next_action}`);
1608
2151
  console.log('SKS Menu Bar:');
1609
2152
  console.log(` status: ${sksMenuBar.status || (sksMenuBar.ok ? 'ok' : 'blocked')}`);
2153
+ for (const line of sksMenuBarRunningVersionConsoleLines(sksMenuBar))
2154
+ console.log(line);
1610
2155
  const menubarPhase = doctorFixTransaction?.phases?.find((phase) => phase?.id === 'sks_menubar');
1611
2156
  if (menubarPhase) {
1612
2157
  const menubarSummary = menubarPhase.ok
@@ -1622,6 +2167,21 @@ async function runDoctor(args = [], root, doctorFix) {
1622
2167
  console.log(` blockers: ${sksMenuBar.blockers.join(', ')}`);
1623
2168
  if (Array.isArray(sksMenuBar.warnings) && sksMenuBar.warnings.length)
1624
2169
  console.log(` warnings: ${sksMenuBar.warnings.join(', ')}`);
2170
+ console.log('Telegram Remote:');
2171
+ console.log(` status: ${telegramRemote.status || 'unknown'}`);
2172
+ console.log(` getMe: ${telegramRemote.bot_identity_valid ? 'verified' : telegramRemote.token_configured ? 'invalid' : 'not configured'}`);
2173
+ console.log(` audit: ${telegramRemote.audit_healthy ? 'healthy' : 'unavailable'}`);
2174
+ const telegramCheckedLine = telegramDoctorCheckedLine(telegramRemote, ' ');
2175
+ if (telegramCheckedLine)
2176
+ console.log(telegramCheckedLine);
2177
+ console.log(` long poll: ${telegramRemote.poller?.running ? 'running' : 'stopped'}`);
2178
+ const telegramSelfHealOutcome = telegramRemote.self_heal_outcome;
2179
+ const telegramDisplayedAction = telegramSelfHealOutcome?.attempted
2180
+ ? telegramSelfHealOutcome.action
2181
+ : telegramRemote.self_heal_action;
2182
+ if (telegramDisplayedAction && telegramDisplayedAction !== 'none') {
2183
+ console.log(` self-heal: ${telegramDisplayedAction}${telegramSelfHealOutcome?.attempted ? ` (${telegramSelfHealOutcome.recovered ? 'recovered' : 'attempted, still degraded'})` : ''}`);
2184
+ }
1625
2185
  console.log(`Provider: ${providerContext.provider || 'unknown'} ${providerContext.service_tier || ''} (${providerContext.source || 'unknown'}, ${providerContext.confidence || 'low'})`);
1626
2186
  const imagegenReady = imagegen.auth_readiness;
1627
2187
  if (imagegenReady) {
@@ -1674,6 +2234,17 @@ async function runDoctor(args = [], root, doctorFix) {
1674
2234
  if (codex0138Doctor?.fixed?.length)
1675
2235
  console.log(` doctor --fix repaired: ${codex0138Doctor.fixed.join(', ')}`);
1676
2236
  console.log(`codex-lb: ${codexLb.ok ? 'ok' : `warning ${codexLb.circuit?.state || 'unknown'}`}`);
2237
+ console.log(` key source: ${codexLb.secret_resolution.source}${codexLb.secret_resolution.path ? ` (${codexLb.secret_resolution.path})` : ''}; prompt risk: ${codexLb.secret_resolution.prompt_risk}`);
2238
+ for (const action of codexLb.legacy_keychain_migration?.operator_actions || [])
2239
+ console.log(` action: ${action}`);
2240
+ for (const warning of codexLb.divergence?.warnings || [])
2241
+ console.log(` warning: ${warning}`);
2242
+ for (const action of codexLb.divergence?.operator_actions || [])
2243
+ console.log(` action: ${action}`);
2244
+ if (codexLb.routing_truth) {
2245
+ const routingTruth = codexLb.routing_truth;
2246
+ console.log(` routing truth: ${codexLb.routing_ok ? 'verified' : 'blocked'} (${routingTruth.actual_host || routingTruth.configured_host || 'host unavailable'}, ${routingTruth.auth_outcome}, ${routingTruth.latency_ms ?? 'unmeasured'} ms at ${routingTruth.checked_at})`);
2247
+ }
1677
2248
  if (codexLb.tool_output_recovery) {
1678
2249
  const recovery = codexLb.tool_output_recovery;
1679
2250
  console.log(` interrupted tool-output recovery: ${recovery.ok ? 'ready' : 'blocked'} (${recovery.observed_version || recovery.status}; minimum ${recovery.minimum_version})`);
@@ -2011,6 +2582,95 @@ export function formatCodexDoctorConsoleStatus(report) {
2011
2582
  return 'unavailable';
2012
2583
  return report.disposition || (report.exit_code === 0 ? 'pass' : 'warn');
2013
2584
  }
2585
+ export function sksMenuBarRunningVersionConsoleLines(status) {
2586
+ const runningVersion = status?.running_process?.package_version
2587
+ || status?.menubar_version_probe?.running_version
2588
+ || null;
2589
+ const runningPid = status?.running_process?.pid ?? status?.menubar_version_probe?.pid ?? null;
2590
+ if (typeof runningVersion !== 'string' || !runningVersion || !Number.isInteger(runningPid) || runningPid <= 0) {
2591
+ return [];
2592
+ }
2593
+ const expectedVersion = status?.menubar_version_probe?.expected_version
2594
+ || status?.action_target?.expected_version
2595
+ || status?.package_version
2596
+ || null;
2597
+ const installedVersion = status?.installed_version || status?.build_stamp?.package_version || null;
2598
+ return [
2599
+ ` RUNNING: version ${runningVersion} (PID ${runningPid})`,
2600
+ ...(typeof expectedVersion === 'string' && expectedVersion && expectedVersion !== runningVersion
2601
+ ? [` expected: version ${expectedVersion}`]
2602
+ : []),
2603
+ ...(typeof installedVersion === 'string' && installedVersion && installedVersion !== runningVersion
2604
+ ? [` installed: version ${installedVersion}`]
2605
+ : [])
2606
+ ];
2607
+ }
2608
+ export async function rebootstrapSksMenuBarLaunchdForDoctorFix(input, deps = {}) {
2609
+ const env = input.env || process.env;
2610
+ const status = input.status;
2611
+ const expectedVersion = status?.menubar_version_probe?.expected_version
2612
+ || status?.action_target?.expected_version
2613
+ || status?.installed_version
2614
+ || null;
2615
+ const warningOnlyVerifiedProcess = input.fix
2616
+ && !sksMenuBarRestartDeferred(env)
2617
+ && status?.launchd?.checked === true
2618
+ && status?.launchd?.ok === false
2619
+ && Array.isArray(status?.warnings)
2620
+ && status.warnings.includes('launchd_not_running_process_active')
2621
+ && !(Array.isArray(status?.blockers) && status.blockers.includes('launchd_not_running'))
2622
+ && status?.running_process?.ok === true
2623
+ && typeof expectedVersion === 'string'
2624
+ && status.running_process.package_version === expectedVersion;
2625
+ if (!warningOnlyVerifiedProcess) {
2626
+ return { attempted: false, ok: true, status, restart: null };
2627
+ }
2628
+ const paths = sksMenuBarPaths(input.home || env.HOME, input.root);
2629
+ const restartImpl = deps.restartLaunchAgentImpl || restartLaunchAgent;
2630
+ const inspectImpl = deps.inspectSksMenuBarStatusImpl || inspectSksMenuBarStatus;
2631
+ const restart = await restartImpl(paths, env).catch((error) => ({
2632
+ ok: false,
2633
+ error: error instanceof Error ? error.message : String(error)
2634
+ }));
2635
+ if (restart?.ok !== true) {
2636
+ const detail = restart?.error ? `:${restart.error}` : '';
2637
+ return {
2638
+ attempted: true,
2639
+ ok: false,
2640
+ restart,
2641
+ status: {
2642
+ ...status,
2643
+ ok: false,
2644
+ blockers: [...new Set([...(status?.blockers || []), `launchd_rebootstrap_failed${detail}`])]
2645
+ }
2646
+ };
2647
+ }
2648
+ const refreshed = await inspectImpl({
2649
+ root: input.root,
2650
+ ...(input.home ? { home: input.home } : {}),
2651
+ ...(input.env ? { env: input.env } : {})
2652
+ }).catch((error) => ({
2653
+ ...status,
2654
+ ok: false,
2655
+ blockers: [...new Set([
2656
+ ...(status?.blockers || []),
2657
+ `launchd_rebootstrap_status_failed:${error instanceof Error ? error.message : String(error)}`
2658
+ ])]
2659
+ }));
2660
+ if (refreshed?.launchd?.checked !== true || refreshed?.launchd?.ok !== true) {
2661
+ return {
2662
+ attempted: true,
2663
+ ok: false,
2664
+ restart,
2665
+ status: {
2666
+ ...refreshed,
2667
+ ok: false,
2668
+ blockers: [...new Set([...(refreshed?.blockers || []), 'launchd_rebootstrap_not_confirmed'])]
2669
+ }
2670
+ };
2671
+ }
2672
+ return { attempted: true, ok: true, status: refreshed, restart };
2673
+ }
2014
2674
  function mergeObservedCodexStartupWarnings(startupRepair, codexDoctor) {
2015
2675
  const text = `${codexDoctor?.stdout_tail || ''}\n${codexDoctor?.stderr_tail || ''}`;
2016
2676
  const manual = new Set(Array.isArray(startupRepair?.manual_actions) ? startupRepair.manual_actions : []);