sneakoscope 8.2.0 → 8.3.0

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 (101) hide show
  1. package/README.md +46 -110
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/command-manifest-lite.js +0 -2
  5. package/dist/cli/command-registry.js +0 -2
  6. package/dist/commands/codex.js +3 -3
  7. package/dist/commands/doctor.js +5 -157
  8. package/dist/config/skills-manifest.json +1 -1
  9. package/dist/core/codex/codex-cli-syntax-builder.js +0 -8
  10. package/dist/core/codex-app/codex-models-cache.js +2 -1
  11. package/dist/core/codex-app/menubar/constants.js +0 -7
  12. package/dist/core/codex-compat/codex-capability-matrix.js +7 -7
  13. package/dist/core/codex-compat/codex-compat-report.js +8 -12
  14. package/dist/core/codex-compat/codex-runtime-contract.js +62 -0
  15. package/dist/core/codex-compat/codex-version-policy.js +7 -7
  16. package/dist/core/codex-control/codex-current-app-capability.js +3 -3
  17. package/dist/core/codex-control/codex-current-capability.js +67 -94
  18. package/dist/core/codex-control/codex-current-core-capability.js +3 -3
  19. package/dist/core/codex-control/codex-current-core-probe-runner.js +4 -4
  20. package/dist/core/codex-control/codex-current-core-real-probes.js +2 -2
  21. package/dist/core/codex-control/codex-current-feature-capability.js +4 -4
  22. package/dist/core/codex-control/codex-current-image-path-real-probe.js +1 -1
  23. package/dist/core/codex-control/codex-current-web-search-probe.js +1 -1
  24. package/dist/core/codex-control/codex-sdk-capability.js +2 -2
  25. package/dist/core/commands/remote-command.js +2 -2
  26. package/dist/core/context7-client.js +5 -2
  27. package/dist/core/doctor/codex-current-core-probe-doctor.js +2 -2
  28. package/dist/core/doctor/doctor-dirty-planner.js +1 -1
  29. package/dist/core/doctor/doctor-readiness-matrix.js +0 -7
  30. package/dist/core/feature-fixtures.js +0 -4
  31. package/dist/core/feature-registry.js +0 -1
  32. package/dist/core/image-ux-review.js +3 -3
  33. package/dist/core/install/installed-package-smoke.js +2 -3
  34. package/dist/core/mad-sks/sql-plane/mcp-executor.js +4 -3
  35. package/dist/core/mcp/modern-protocol.js +36 -0
  36. package/dist/core/mcp-config/health-check.js +45 -30
  37. package/dist/core/perf/release-latency-slo.js +1 -2
  38. package/dist/core/proof/runtime-truth-matrix.js +1 -1
  39. package/dist/core/release/canonical-test-proof.js +1 -2
  40. package/dist/core/release/gate-manifest.js +5 -2
  41. package/dist/core/release/physical-release-gates.js +0 -26
  42. package/dist/core/release/release-gate-affected-selector.js +2 -1
  43. package/dist/core/release/release-gate-contract.js +3 -1
  44. package/dist/core/release/release-pack-receipt.js +0 -1
  45. package/dist/core/release/stage-publish.js +1 -1
  46. package/dist/core/version-manager.js +4 -0
  47. package/dist/core/version.js +1 -1
  48. package/dist/native/sks-menubar/Sources/AppDelegate.swift +1 -12
  49. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +2 -6
  50. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +28 -471
  51. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +1 -43
  52. package/dist/scripts/codex-current-app-server-v2-check.js +9 -8
  53. package/dist/scripts/codex-current-binary-identity-check.js +2 -2
  54. package/dist/scripts/codex-current-capability-check.js +9 -7
  55. package/dist/scripts/codex-current-dependency-check.js +21 -0
  56. package/dist/scripts/docs-truthfulness-check.js +4 -19
  57. package/dist/scripts/fast-codex-service-tier-proof-check.js +0 -1
  58. package/dist/scripts/install-surface-ssot-check.js +2 -2
  59. package/dist/scripts/latest-version-guidance-check.js +1 -1
  60. package/dist/scripts/packlist-performance-check.js +0 -1
  61. package/dist/scripts/release-metadata-check.js +4 -1
  62. package/dist/scripts/release-version-truth-check.js +149 -0
  63. package/dist/vendor/openai-codex/latest/hooks/permission-request.command.input.schema.json +6 -0
  64. package/dist/vendor/openai-codex/latest/hooks/permission-request.command.output.schema.json +2 -16
  65. package/dist/vendor/openai-codex/latest/hooks/post-compact.command.input.schema.json +6 -0
  66. package/dist/vendor/openai-codex/latest/hooks/post-tool-use.command.input.schema.json +6 -0
  67. package/dist/vendor/openai-codex/latest/hooks/post-tool-use.command.output.schema.json +2 -16
  68. package/dist/vendor/openai-codex/latest/hooks/pre-compact.command.input.schema.json +6 -0
  69. package/dist/vendor/openai-codex/latest/hooks/pre-tool-use.command.input.schema.json +6 -0
  70. package/dist/vendor/openai-codex/latest/hooks/pre-tool-use.command.output.schema.json +2 -16
  71. package/dist/vendor/openai-codex/latest/hooks/session-start.command.output.schema.json +2 -16
  72. package/dist/vendor/openai-codex/latest/hooks/snapshot-metadata.json +7 -5
  73. package/dist/vendor/openai-codex/latest/hooks/subagent-start.command.output.schema.json +4 -18
  74. package/dist/vendor/openai-codex/latest/hooks/user-prompt-submit.command.input.schema.json +6 -0
  75. package/dist/vendor/openai-codex/latest/hooks/user-prompt-submit.command.output.schema.json +2 -16
  76. package/package.json +4 -7
  77. package/release-gates.v2.json +49 -19
  78. package/config/codex-releases/rust-v0.146.0.json +0 -45
  79. package/dist/core/agents/agent-runner-codex-exec.js +0 -177
  80. package/dist/core/codex-compat/codex-release-manifest.js +0 -72
  81. package/dist/core/commands/telegram-command.js +0 -447
  82. package/dist/core/telegram/access.js +0 -58
  83. package/dist/core/telegram/client.js +0 -157
  84. package/dist/core/telegram/confirmation.js +0 -37
  85. package/dist/core/telegram/doctor.js +0 -154
  86. package/dist/core/telegram/index.js +0 -9
  87. package/dist/core/telegram/keychain.js +0 -317
  88. package/dist/core/telegram/liveness.js +0 -124
  89. package/dist/core/telegram/poller.js +0 -90
  90. package/dist/core/telegram/redaction.js +0 -15
  91. package/dist/core/telegram/state-lock.js +0 -416
  92. package/dist/core/telegram/types.js +0 -1
  93. package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +0 -498
  94. package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +0 -236
  95. package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +0 -454
  96. package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +0 -56
  97. package/dist/native/sks-menubar/Sources/TelegramStateLock.swift +0 -448
  98. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +0 -243
  99. package/dist/native/sks-menubar/Sources/TelegramTransport.swift +0 -499
  100. package/dist/scripts/codex-current-manifest-check.js +0 -29
  101. package/schemas/codex/app-server-0.146/codex_app_server_protocol.v2.schemas.json +0 -19858
@@ -1,494 +1,51 @@
1
1
  import Cocoa
2
2
 
3
- final class RemoteCodingViewController: NSViewController, ControlCenterPage {
4
- private static let botFatherURL = URL(string: "https://t.me/BotFather")!
5
- private static let tokenPattern = #"^\d{5,20}:[A-Za-z0-9_-]{20,128}$"#
6
-
7
- private let processClient: ProcessClient
8
- private let telegramService: TelegramMenuBarService
9
- private let serviceBadge = NativeView.badge("Checking Telegram service", color: .systemBlue)
10
- private let serviceDetail = NativeView.detail("Reading secret-free Telegram liveness status…")
11
- private let pairingDetail = NativeView.detail("No pairing code has been issued in this session.")
12
- private let serviceSpinner = NativeView.spinner(label: "Telegram operation in progress")
13
- private lazy var setupButton = NativeView.button("Enter Bot Token…", target: self, action: #selector(enterToken))
14
- private lazy var pairButton = NativeView.button("Generate Pairing Code", target: self, action: #selector(generatePairingCode))
15
- private lazy var startButton = NativeView.button("Start", target: self, action: #selector(startService))
16
- private lazy var stopButton = NativeView.button("Stop", target: self, action: #selector(stopService))
17
- private lazy var restartButton = NativeView.button("Restart", target: self, action: #selector(restartService))
18
- private lazy var refreshButton = NativeView.button("Refresh Status", target: self, action: #selector(refreshStatusAction))
19
- private var operationInFlight = false
20
- private var tokenConfigured = false
21
- private var pollerRunning = false
22
-
23
- init(processClient: ProcessClient, telegramService: TelegramMenuBarService) {
24
- self.processClient = processClient
25
- self.telegramService = telegramService
26
- super.init(nibName: nil, bundle: nil)
27
- }
28
-
29
- required init?(coder: NSCoder) { nil }
3
+ final class RemoteCodingViewController: NSViewController {
4
+ private static let websiteURL = URL(string: "https://paseo.sh/")!
5
+ private static let docsURL = URL(string: "https://paseo.sh/docs")!
30
6
 
31
7
  override func loadView() {
32
- let botFatherButton = NativeView.button("Open @BotFather", target: self, action: #selector(openBotFather))
33
- botFatherButton.setAccessibilityHelp("Open the official Telegram BotFather chat in your default handler.")
34
- setupButton.setAccessibilityHelp("Enter a BotFather token securely. The token is verified live and stored in the private SKS user secret file.")
35
- pairButton.setAccessibilityHelp("With Telegram polling running, issue a short-lived, single-use code for pairing one intended private chat.")
36
- startButton.setAccessibilityHelp("Start Telegram polling for the current SKS companion session after a bot token is configured.")
37
- stopButton.setAccessibilityHelp("Stop polling for the current companion session without deleting the token or paired chats. A companion relaunch starts it again.")
38
- restartButton.setAccessibilityHelp("Restart the single resident Telegram poller and revalidate the stored bot identity.")
39
- refreshButton.setAccessibilityHelp("Refresh secret-free bot, pairing, audit, and poller status.")
40
- pairingDetail.isSelectable = true
41
- pairingDetail.setAccessibilityLabel("Telegram pairing instructions")
42
- serviceDetail.setAccessibilityLabel("Telegram service status detail")
8
+ let websiteButton = NativeView.button(
9
+ "Visit Paseo",
10
+ target: self,
11
+ action: #selector(openWebsite)
12
+ )
13
+ websiteButton.setAccessibilityLabel("Visit Paseo website")
14
+ websiteButton.setAccessibilityHelp("Open the independent Paseo website in your default browser.")
43
15
 
44
- let setupCard = NativeView.card(
45
- title: "Connect with BotFather",
46
- subtitle: "Create a Telegram bot, then let SKS validate and store its token privately.",
47
- views: [
48
- NativeView.detail(
49
- "1. Create or select any bot you own in @BotFather. 2. Copy that bot's complete HTTP API token. " +
50
- "3. Choose Enter Bot Token below. SKS binds the identity returned by Telegram getMe; it never assumes a bot name or username."
51
- ),
52
- NativeView.detail(
53
- "The token is sent only through process stdin to the canonical SKS setup command, verified with Telegram getMe, and stored in the private user secret file. It is never shown again or written to the SKS action log. Revoke it in BotFather if you suspect exposure."
54
- ),
55
- NativeView.row([botFatherButton, setupButton])
56
- ]
16
+ let docsButton = NativeView.button(
17
+ "Read Paseo Docs",
18
+ target: self,
19
+ action: #selector(openDocs)
57
20
  )
21
+ docsButton.setAccessibilityLabel("Read Paseo documentation")
22
+ docsButton.setAccessibilityHelp("Open the official Paseo documentation in your default browser.")
58
23
 
59
- let pairingCard = NativeView.card(
60
- title: "Pair a Private Chat",
61
- subtitle: "Authorize only the intended Telegram account and private chat.",
24
+ let recommendation = NativeView.card(
25
+ title: "Paseo (recommended)",
26
+ subtitle: "Run Codex from your desktop, phone, web browser, or terminal.",
62
27
  views: [
63
28
  NativeView.detail(
64
- "With Telegram polling running, generate a short-lived code, open your new bot from the intended private chat, and send /start followed by that code. Codes are single-use; group chats are rejected. After pairing, try /sks status {}."
29
+ "Paseo is an independent open-source project. Sneakoscope does not install it, bundle its daemon, configure its authentication or relay, monitor it, or depend on it. Review Paseo's own documentation and security guidance before connecting devices."
65
30
  ),
66
- pairingDetail,
67
- pairButton
68
- ]
69
- )
70
-
71
- let statusCard = NativeView.card(
72
- title: "Service Status",
73
- subtitle: "Control the single SKS-owned poller for this companion session and inspect secret-free health evidence.",
74
- views: [
75
- NativeView.row([serviceBadge, serviceSpinner]),
76
- serviceDetail,
77
- NativeView.row([startButton, stopButton, restartButton, refreshButton])
31
+ NativeView.row([websiteButton, docsButton])
78
32
  ]
79
33
  )
80
34
 
81
35
  view = NativeView.page([
82
- NativeView.title("Telegram Remote Control"),
36
+ NativeView.title("Remote Coding"),
83
37
  NativeView.detail(
84
- "Connect SKS to a BotFather-created Telegram bot for paired, allowlisted remote commands with confirmation gates and a local audit trail."
38
+ "For remote and cross-device coding, Sneakoscope recommends evaluating Paseo as a separate companion for Codex."
85
39
  ),
86
- setupCard,
87
- pairingCard,
88
- statusCard
40
+ recommendation
89
41
  ])
90
- updateControls()
91
- }
92
-
93
- func refreshOnAppear() { refreshStatus() }
94
-
95
- @objc private func openBotFather() {
96
- NSWorkspace.shared.open(Self.botFatherURL)
97
- }
98
-
99
- @objc private func enterToken() {
100
- if TelegramPrivateFileStore.operatorEnvironmentOverrideActive() {
101
- showAttention(
102
- "Environment-managed token is active",
103
- detail: "SKS is using an operator-managed Telegram token environment override. Remove TELEGRAM_BOT_TOKEN or SKS_TELEGRAM_BOT_TOKEN and relaunch the companion before storing a BotFather token file."
104
- )
105
- return
106
- }
107
- promptForToken(removeWebhook: false)
108
- }
109
-
110
- private func promptForToken(removeWebhook: Bool) {
111
- guard !operationInFlight, let window = view.window else { return }
112
- AlertFactory.textSheet(
113
- window: window,
114
- title: "Enter Telegram Bot Token",
115
- message: removeWebhook
116
- ? "Paste this selected bot's BotFather token again. With your consent, SKS will remove that bot's existing webhook without dropping pending updates, verify its live identity, and store the token privately."
117
- : "Paste the complete HTTP API token for any bot you own in @BotFather. SKS will accept the identity returned by Telegram getMe without assuming a bot name, then store the token privately.",
118
- secure: true,
119
- placeholder: "123456789:BotFatherToken",
120
- actionTitle: removeWebhook ? "Remove Webhook & Save" : "Verify & Save"
121
- ) { [weak self] token in
122
- guard let self, let token else { return }
123
- let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
124
- guard self.validTokenShape(normalizedToken) else {
125
- self.showAttention(
126
- "Token not saved",
127
- detail: "That value does not match the BotFather token format. Copy the complete HTTP API token and try again."
128
- )
129
- return
130
- }
131
- self.saveToken(normalizedToken, removeWebhook: removeWebhook)
132
- }
133
- }
134
-
135
- @objc private func generatePairingCode() {
136
- guard !operationInFlight else { return }
137
- beginOperation("Issuing a short-lived pairing code…")
138
- processClient.run(
139
- ["telegram", "pair", "--json"],
140
- environment: telegramSecretFreeEnvironment,
141
- timeout: NativeView.statusTimeout,
142
- logOutput: false
143
- ) { [weak self] result in
144
- guard let self else { return }
145
- self.endOperation()
146
- guard result.code == 0,
147
- let response = self.decode(TelegramCenterPairResponse.self, result.output),
148
- response.ok,
149
- let code = response.code,
150
- code.range(of: #"^\d{6}-[A-F0-9]{4}$"#, options: .regularExpression) != nil else {
151
- self.showAttention(
152
- "Pairing code unavailable",
153
- detail: self.publicFailure(result.output, fallback: "SKS could not create a pairing code. Refresh status and try again.")
154
- )
155
- return
156
- }
157
- let expiration = response.expires_at.map { " It expires at \($0)." } ?? ""
158
- let instruction = response.instruction
159
- ?? "Send /start \(code) to this bot from the intended private chat."
160
- let postPair = response.post_pair_command ?? "/sks status {}"
161
- let confirmation = response.confirmation_grammar ?? "/confirm <nonce>"
162
- let statusGuidance = instruction.contains(postPair) ? "" : " Then try \(postPair)."
163
- self.pairingDetail.stringValue = "\(instruction)\(expiration)\(statusGuidance) Confirm prompted actions with \(confirmation)."
164
- self.pairingDetail.setAccessibilityValue(self.pairingDetail.stringValue)
165
- self.showProgress("Pairing code ready", detail: "Use the displayed code once in the intended private chat.")
166
- self.refreshStatus(after: 1)
167
- }
168
- }
169
-
170
- @objc private func startService() { runLifecycle(.start) }
171
- @objc private func stopService() { runLifecycle(.stop) }
172
- @objc private func restartService() { runLifecycle(.restart) }
173
- @objc private func refreshStatusAction() { refreshStatus() }
174
-
175
- private enum LifecycleAction { case start, stop, restart }
176
-
177
- private func runLifecycle(_ action: LifecycleAction) {
178
- guard !operationInFlight else { return }
179
- let actionName: String
180
- switch action {
181
- case .start: actionName = "Starting"
182
- case .stop: actionName = "Stopping"
183
- case .restart: actionName = "Restarting"
184
- }
185
- beginOperation("\(actionName) Telegram polling…")
186
-
187
- switch action {
188
- case .stop:
189
- let task = telegramService.stop()
190
- Task { @MainActor [weak self] in
191
- await task.value
192
- self?.endOperation()
193
- self?.showProgress(
194
- "Telegram polling stopped",
195
- detail: "The token and paired chats were preserved. Relaunching the SKS companion starts polling again."
196
- )
197
- self?.refreshStatus(after: 0.2)
198
- }
199
- case .start, .restart:
200
- let task = action == .start ? telegramService.start() : telegramService.restart()
201
- Task { @MainActor [weak self] in
202
- do {
203
- let receipt = try await task.value
204
- self?.endOperation()
205
- self?.render(receipt)
206
- } catch {
207
- self?.endOperation()
208
- self?.showAttention(
209
- "Telegram polling did not start",
210
- detail: "Refresh status for the verified blocker. Re-enter the BotFather token if identity validation failed."
211
- )
212
- }
213
- self?.refreshStatus(after: 0.3)
214
- }
215
- }
216
- }
217
-
218
- private func saveToken(_ token: String, removeWebhook: Bool) {
219
- let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
220
- guard validTokenShape(normalizedToken) else {
221
- showAttention(
222
- "Token not saved",
223
- detail: "That value does not match the BotFather token format. Copy the complete token and try again."
224
- )
225
- return
226
- }
227
- beginOperation("Verifying bot identity with Telegram…")
228
- var arguments = ["telegram", "setup", "--token-stdin", "--json"]
229
- if TelegramPrivateFileStore.operatorEnvironmentOverrideActive() {
230
- arguments.append("--operator-env-override-active")
231
- }
232
- if removeWebhook { arguments.append("--remove-webhook") }
233
- processClient.run(
234
- arguments,
235
- stdin: normalizedToken + "\n",
236
- environment: telegramSecretFreeEnvironment,
237
- timeout: NativeView.mutationTimeout,
238
- logOutput: false
239
- ) { [weak self] result in
240
- guard let self else { return }
241
- let response = self.decode(TelegramCenterSetupResponse.self, result.output)
242
- guard result.code == 0,
243
- let response,
244
- response.ok,
245
- response.getme_verified == true,
246
- response.token_stored == true else {
247
- self.endOperation()
248
- if !removeWebhook,
249
- response?.error == "telegram_webhook_configured_remove_consent_required" {
250
- self.confirmWebhookRemoval()
251
- return
252
- }
253
- if response?.error == "telegram_operator_env_override_active" {
254
- self.showAttention(
255
- "Environment-managed token is active",
256
- detail: response?.operator_action
257
- ?? "Remove the operator-managed Telegram token environment override and relaunch before storing a file token."
258
- )
259
- return
260
- }
261
- if response?.partial_success == true {
262
- self.showAttention(
263
- "Telegram setup partially completed",
264
- detail: self.partialSetupRecovery(response)
265
- )
266
- return
267
- }
268
- self.showAttention(
269
- "Token not saved",
270
- detail: self.publicFailure(
271
- result.output,
272
- fallback: "Telegram setup could not complete. Check the connection and SKS diagnostics, then try again."
273
- )
274
- )
275
- return
276
- }
277
- let restart = self.telegramService.restart()
278
- Task { @MainActor [weak self] in
279
- do {
280
- let receipt = try await restart.value
281
- self?.endOperation()
282
- self?.render(receipt)
283
- let selectedBot = response.bot_username.map { "@\($0)" } ?? "The selected bot"
284
- self?.showProgress(
285
- "Bot verified and connected",
286
- detail: response.bot_state_reset == true
287
- ? "\(selectedBot) is verified and stored privately. Bot-scoped pairing and poll state were reset; generate a new pairing code."
288
- : "\(selectedBot) is verified and stored privately. Generate a pairing code for the intended private chat."
289
- )
290
- } catch {
291
- self?.endOperation()
292
- self?.showAttention(
293
- "Token saved; poller needs attention",
294
- detail: "The verified token was stored, but the resident poller did not restart. Refresh status for the blocker."
295
- )
296
- }
297
- self?.refreshStatus(after: 0.3)
298
- }
299
- }
300
- }
301
-
302
- private func confirmWebhookRemoval() {
303
- guard let window = view.window else { return }
304
- AlertFactory.confirmSheet(
305
- window: window,
306
- title: "Remove the existing Telegram webhook?",
307
- message: "Telegram cannot run getUpdates while this bot has a webhook. Removing it changes delivery: the existing webhook endpoint will stop receiving updates and SKS long polling can start. SKS requests removal without dropping pending updates. The token was not retained; after Continue, paste it again to authorize this one change.",
308
- destructive: true,
309
- actionTitle: "Continue"
310
- ) { [weak self] confirmed in
311
- guard confirmed else { return }
312
- self?.promptForToken(removeWebhook: true)
313
- }
314
- }
315
-
316
- private var telegramSecretFreeEnvironment: [String: String] {
317
- ["TELEGRAM_BOT_TOKEN": "", "SKS_TELEGRAM_BOT_TOKEN": ""]
318
- }
319
-
320
- private func refreshStatus(after delay: TimeInterval = 0) {
321
- guard delay > 0 else {
322
- loadStatus()
323
- return
324
- }
325
- DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in self?.loadStatus() }
326
- }
327
-
328
- private func loadStatus() {
329
- guard !operationInFlight else { return }
330
- beginOperation("Refreshing secret-free Telegram status…")
331
- processClient.run(
332
- ["telegram", "doctor", "--json"],
333
- environment: telegramSecretFreeEnvironment,
334
- timeout: NativeView.statusTimeout,
335
- logOutput: false
336
- ) { [weak self] result in
337
- guard let self else { return }
338
- self.endOperation()
339
- guard let response = self.decode(TelegramCenterDoctorResponse.self, result.output) else {
340
- self.tokenConfigured = false
341
- self.pollerRunning = false
342
- self.showAttention("Status unavailable", detail: "SKS could not read the Telegram liveness receipt.")
343
- return
344
- }
345
- self.render(response)
346
- }
347
- }
348
-
349
- private func render(_ response: TelegramCenterDoctorResponse) {
350
- tokenConfigured = response.token_configured
351
- pollerRunning = response.poller.running
352
- let paired = response.paired_chat_count == 1 ? "1 paired chat" : "\(response.paired_chat_count) paired chats"
353
- let source = tokenSourceDescription(response.token_source)
354
- if response.ok {
355
- NativeView.setBadge(serviceBadge, text: "Ready", color: .systemGreen)
356
- serviceDetail.stringValue = "Bot identity verified · \(source) · \(paired) · poller running · audit healthy."
357
- } else if response.self_heal_action == "operator_remove_webhook"
358
- || response.poller.last_error?.range(of: "409|webhook", options: .regularExpression) != nil {
359
- NativeView.setBadge(serviceBadge, text: "Webhook blocks polling", color: .systemOrange)
360
- serviceDetail.stringValue = "This bot has an active Telegram webhook, so getUpdates polling cannot run. Choose Enter Bot Token again; SKS will explain the delivery change and request explicit consent before removing the webhook without dropping pending updates."
361
- } else if !response.token_configured {
362
- NativeView.setBadge(serviceBadge, text: "Bot token required", color: .systemOrange)
363
- serviceDetail.stringValue = "Create a bot with @BotFather, then enter and verify its HTTP API token."
364
- } else if !response.bot_identity_valid {
365
- NativeView.setBadge(serviceBadge, text: "Bot identity not verified", color: .systemRed)
366
- serviceDetail.stringValue = "Telegram could not validate the stored bot identity. Enter the current BotFather token again."
367
- } else if !response.audit_healthy {
368
- NativeView.setBadge(serviceBadge, text: "Audit unavailable", color: .systemRed)
369
- serviceDetail.stringValue = "Remote control is stopped because its local audit ledger is unavailable. Repair local SKS permissions before restarting."
370
- } else if response.paired_chat_count == 0 {
371
- NativeView.setBadge(serviceBadge, text: "Private chat not paired", color: .systemOrange)
372
- serviceDetail.stringValue = "Bot verified · \(source) · no paired chats · \(response.poller.running ? "poller running" : "poller stopped"). Generate a pairing code and use it once in the intended private chat."
373
- } else if !response.poller.running {
374
- NativeView.setBadge(serviceBadge, text: "Poller stopped", color: .systemOrange)
375
- serviceDetail.stringValue = "Bot identity verified · \(source) · \(paired) · poller stopped. Choose Start to resume."
376
- } else {
377
- NativeView.setBadge(serviceBadge, text: "Needs attention", color: .systemOrange)
378
- serviceDetail.stringValue = "Bot identity verified · \(paired) · poller degraded after \(response.poller.consecutive_failures) consecutive failures. Refresh status or Restart."
379
- }
380
- serviceDetail.setAccessibilityValue(serviceDetail.stringValue)
381
- updateControls()
382
- }
383
-
384
- private func render(_ receipt: TelegramLivenessReceipt) {
385
- tokenConfigured = receipt.token_configured
386
- pollerRunning = receipt.running && receipt.poller.running
387
- let paired = receipt.paired_chat_count == 1 ? "1 paired chat" : "\(receipt.paired_chat_count) paired chats"
388
- let source = tokenSourceDescription(receipt.token_source.rawValue)
389
- NativeView.setBadge(
390
- serviceBadge,
391
- text: pollerRunning ? "Running" : "Stopped",
392
- color: pollerRunning ? .systemGreen : .systemOrange
393
- )
394
- serviceDetail.stringValue = "Bot identity \(receipt.bot_identity_valid ? "verified" : "not verified") · \(source) · \(paired) · poller \(pollerRunning ? "running" : "stopped") · audit \(receipt.audit_healthy == true ? "healthy" : "unavailable")."
395
- serviceDetail.setAccessibilityValue(serviceDetail.stringValue)
396
- updateControls()
397
- }
398
-
399
- private func beginOperation(_ detail: String) {
400
- operationInFlight = true
401
- serviceSpinner.startAnimation(nil)
402
- serviceDetail.stringValue = detail
403
- updateControls()
404
- }
405
-
406
- private func endOperation() {
407
- operationInFlight = false
408
- serviceSpinner.stopAnimation(nil)
409
- updateControls()
410
- }
411
-
412
- private func showProgress(_ title: String, detail: String) {
413
- NativeView.setBadge(serviceBadge, text: title, color: .systemBlue)
414
- serviceDetail.stringValue = detail
415
- serviceDetail.setAccessibilityValue(detail)
416
- updateControls()
417
- }
418
-
419
- private func showAttention(_ title: String, detail: String) {
420
- NativeView.setBadge(serviceBadge, text: title, color: .systemOrange)
421
- serviceDetail.stringValue = detail
422
- serviceDetail.setAccessibilityValue(detail)
423
- updateControls()
424
- }
425
-
426
- private func updateControls() {
427
- setupButton.isEnabled = !operationInFlight
428
- pairButton.isEnabled = !operationInFlight && tokenConfigured && pollerRunning
429
- startButton.isEnabled = !operationInFlight && tokenConfigured && !pollerRunning
430
- stopButton.isEnabled = !operationInFlight && pollerRunning
431
- restartButton.isEnabled = !operationInFlight && tokenConfigured
432
- refreshButton.isEnabled = !operationInFlight
433
- }
434
-
435
- private func validTokenShape(_ value: String) -> Bool {
436
- value.utf8.count <= 1024
437
- && value.range(of: Self.tokenPattern, options: .regularExpression) != nil
438
- }
439
-
440
- private func decode<T: Decodable>(_ type: T.Type, _ output: String) -> T? {
441
- guard let data = output.data(using: .utf8) else { return nil }
442
- return try? JSONDecoder().decode(type, from: data)
443
- }
444
-
445
- private func publicFailure(_ output: String, fallback: String) -> String {
446
- guard let data = output.data(using: .utf8),
447
- let row = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
448
- let error = row["error"] as? String else { return fallback }
449
- switch error {
450
- case "telegram_token_invalid", "telegram_token_stdin_empty":
451
- return "That value is not a complete BotFather HTTP API token."
452
- case "telegram_token_rejected":
453
- return "Telegram rejected this token. In @BotFather, select the bot you want SKS to use and copy that bot's current complete token."
454
- case "telegram_identity_verification_timeout":
455
- return "Telegram identity verification timed out. Check the network connection and try the selected bot token again."
456
- case "telegram_identity_verification_network_failed":
457
- return "Telegram identity verification could not reach the Bot API. Check the network connection and try again."
458
- case "telegram_identity_verification_failed":
459
- return "Telegram returned an invalid bot identity response. Try the selected bot's current token again."
460
- case let value where value.contains("401") || value.contains("Unauthorized"):
461
- return "Telegram rejected this token. Copy the current token from @BotFather and try again."
462
- case let value where value.contains("timeout") || value.contains("network"):
463
- return "Telegram identity verification could not complete. Check the network connection and try again."
464
- default:
465
- return fallback
466
- }
467
42
  }
468
43
 
469
- private func partialSetupRecovery(_ response: TelegramCenterSetupResponse?) -> String {
470
- var parts: [String] = []
471
- if response?.webhook_removed == true {
472
- parts.append("The existing webhook was removed without dropping pending updates.")
473
- }
474
- if response?.bot_state_reset == true {
475
- parts.append("Bot-scoped pairing and poll state were reset safely.")
476
- }
477
- if response?.token_stored == false {
478
- parts.append("The token was not stored; rerun secure setup to finish.")
479
- }
480
- if let note = response?.recovery?.note, !note.isEmpty { parts.append(note) }
481
- return parts.isEmpty
482
- ? "Review the secret-free recovery status, then rerun secure setup."
483
- : parts.joined(separator: " ")
44
+ @objc private func openWebsite() {
45
+ NSWorkspace.shared.open(Self.websiteURL)
484
46
  }
485
47
 
486
- private func tokenSourceDescription(_ source: String) -> String {
487
- switch source {
488
- case "env": return "operator environment token"
489
- case "user_secret_file": return "private token file"
490
- case "none": return "no token source"
491
- default: return "token source unknown"
492
- }
48
+ @objc private func openDocs() {
49
+ NSWorkspace.shared.open(Self.docsURL)
493
50
  }
494
51
  }
@@ -51,43 +51,6 @@ enum SecureProcessEnvelope {
51
51
  }
52
52
  }
53
53
  }
54
- if sourceSchema == "sks.telegram-setup-command.v1" {
55
- for key in [
56
- "getme_verified", "token_stored", "partial_success",
57
- "storage_attempted", "webhook_removed", "pending_updates_dropped",
58
- "bot_rotated", "bot_state_reset", "restart_required"
59
- ] {
60
- if let value = object[key] as? Bool { envelope[key] = value }
61
- }
62
- if let source = object["token_source"] as? String,
63
- ["env", "user_secret_file", "none", "unchanged"].contains(source) {
64
- envelope["token_source"] = source
65
- }
66
- if let stage = boundedString(object["failure_stage"], limit: 40),
67
- stage == "getme" {
68
- envelope["failure_stage"] = stage
69
- }
70
- if let botID = object["bot_id"] as? NSNumber,
71
- botID.int64Value > 0 {
72
- envelope["bot_id"] = botID
73
- }
74
- if let username = boundedString(object["bot_username"], limit: 64),
75
- username.range(of: #"^[A-Za-z0-9_]{5,64}$"#, options: .regularExpression) != nil {
76
- envelope["bot_username"] = username
77
- }
78
- if let action = boundedString(object["operator_action"], limit: 480) {
79
- envelope["operator_action"] = action
80
- }
81
- if let recovery = object["recovery"] as? [String: Any] {
82
- var publicRecovery: [String: String] = [:]
83
- for key in ["action", "command", "note"] {
84
- if let value = boundedString(recovery[key], limit: 480) {
85
- publicRecovery[key] = value
86
- }
87
- }
88
- if !publicRecovery.isEmpty { envelope["recovery"] = publicRecovery }
89
- }
90
- }
91
54
  }
92
55
  }
93
56
  if !secureOk, envelope["error"] == nil {
@@ -118,12 +81,7 @@ enum SecureProcessEnvelope {
118
81
  arguments[2] == "configure" {
119
82
  return ["sks.desktop-bridge-command-result.v1"]
120
83
  }
121
- switch (arguments[0], arguments[1]) {
122
- case ("telegram", "setup"):
123
- return ["sks.telegram-setup-command.v1"]
124
- default:
125
- return []
126
- }
84
+ return []
127
85
  }
128
86
 
129
87
  private static func recoveryFields(from object: [String: Any]) -> [String: Any] {
@@ -1,19 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import { assertGate, emitGate, readText } from './gate-lib.js';
3
3
  import { createCodexAppServerV2Client, currentTimeResponse } from '../core/codex-control/codex-app-server-v2-client.js';
4
- import { CURRENT_CODEX_RELEASE_MANIFEST } from '../core/codex-compat/codex-release-manifest.js';
4
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from '../core/codex-compat/codex-runtime-contract.js';
5
+ import { detectCodexCurrentCapability } from '../core/codex-control/codex-current-capability.js';
5
6
  const requireReal = process.argv.includes('--require-real') || process.env.SKS_REQUIRE_CODEX_CURRENT_APP_SERVER === '1';
6
7
  const clientSource = readText('src/core/codex-control/codex-app-server-v2-client.ts');
7
- const schema = readText('schemas/codex/app-server-0.146/codex_app_server_protocol.v2.schemas.json');
8
+ const capability = await detectCodexCurrentCapability({ root: process.cwd(), requireReal: true });
8
9
  assertGate(clientSource.includes('resolveCodexRuntime'), 'app-server-v2 client must use the shared Codex runtime resolver');
9
10
  assertGate(clientSource.includes('currentTime/read'), 'app-server-v2 client must implement currentTime/read server request handling');
10
11
  assertGate(clientSource.includes("request('thread/list'"), 'app-server-v2 client must wrap native thread/list');
11
12
  assertGate(clientSource.includes("request('thread/read'"), 'app-server-v2 client must wrap native thread/read');
12
13
  assertGate(clientSource.includes('searchThreads'), 'app-server-v2 client must expose search over native thread list searchTerm');
13
- assertGate(schema.includes('"thread/list"'), 'generated 0.146.0 schema must contain thread/list');
14
- assertGate(schema.includes('"thread/read"'), 'generated 0.146.0 schema must contain thread/read');
15
- assertGate(schema.includes('"searchTerm"'), 'generated 0.146.0 schema must contain ThreadListParams.searchTerm');
16
- assertGate(schema.includes('"ThreadSearchResult"'), 'generated 0.146.0 schema must contain ThreadSearchResult');
14
+ assertGate(capability.ok, 'resolved Codex runtime must generate a compatible App Server schema', capability);
15
+ assertGate(capability.probe_mode === 'real-schema', 'App Server compatibility must use runtime-generated schema evidence', capability);
16
+ assertGate(capability.feature_states.native_thread_list_search_schema.supported, 'runtime schema must expose thread list/read/search', capability);
17
+ assertGate(capability.feature_states.plugin_catalog_refresh_schema.supported, 'runtime schema must expose portable plugin catalog operations', capability);
17
18
  const deterministic = currentTimeResponse(new Date('2026-06-23T00:00:00.000Z'));
18
19
  assertGate(deterministic.utcIso === '2026-06-23T00:00:00.000Z', 'currentTime/read UTC ISO must be deterministic');
19
20
  assertGate(deterministic.unixTimeSeconds === 1782172800, 'currentTime/read seconds must be Unix UTC seconds');
@@ -29,7 +30,7 @@ if (requireReal) {
29
30
  runtime_sha256: runtimeIdentity.sha256,
30
31
  list_returned_object: Boolean(list && typeof list === 'object')
31
32
  };
32
- assertGate(runtimeIdentity.version === CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion, `app-server-v2 require-real must resolve Codex ${CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion}`, realProbe);
33
+ assertGate(runtimeIdentity.version === CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion, `app-server-v2 require-real must resolve Codex ${CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion}`, realProbe);
33
34
  assertGate(realProbe.list_returned_object === true, 'app-server-v2 require-real thread/list must return an object', realProbe);
34
35
  }
35
36
  finally {
@@ -40,7 +41,7 @@ emitGate('codex:current:app-server-v2', {
40
41
  current_time_handler: true,
41
42
  thread_list: true,
42
43
  thread_read: true,
43
- thread_search_method: schema.includes('"thread/search"'),
44
+ generated_schema_sha256: capability.generated_schema_sha256,
44
45
  thread_search_via_list_search_term: true,
45
46
  real_probe: realProbe
46
47
  });
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { assertGate, emitGate } from './gate-lib.js';
3
- import { CURRENT_CODEX_RELEASE_MANIFEST } from '../core/codex-compat/codex-release-manifest.js';
3
+ import { CURRENT_CODEX_RUNTIME_CONTRACT } from '../core/codex-compat/codex-runtime-contract.js';
4
4
  import { compareSemverLike } from '../core/codex-compat/codex-version-policy.js';
5
5
  import { resolveOfficialCodexPackageRuntime } from '../core/codex-runtime/resolve-codex-runtime.js';
6
6
  const resolved = await resolveOfficialCodexPackageRuntime({ requestedBy: 'codex-current-binary-identity-check' });
7
7
  assertGate(resolved.ok && resolved.identity !== null, 'Official Codex runtime identity must resolve', resolved);
8
8
  const identity = resolved.identity;
9
- assertGate(compareSemverLike(identity.version, CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion) >= 0, `Codex runtime must satisfy ${CURRENT_CODEX_RELEASE_MANIFEST.requiredCliVersion}`, identity);
9
+ assertGate(compareSemverLike(identity.version, CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion) >= 0, `Codex runtime must satisfy ${CURRENT_CODEX_RUNTIME_CONTRACT.requiredCliVersion}`, identity);
10
10
  assertGate(identity.sha256.length === 64, 'Codex runtime identity must include SHA-256', identity);
11
11
  assertGate(identity.trusted === true, 'Codex runtime identity must be trusted before execution', identity);
12
12
  assertGate(identity.trust_basis === (process.platform === 'darwin'