sneakoscope 8.2.1 → 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 (99) 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 -8
  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 -1
  40. package/dist/core/release/gate-manifest.js +1 -2
  41. package/dist/core/release/physical-release-gates.js +0 -26
  42. package/dist/core/release/release-gate-affected-selector.js +0 -1
  43. package/dist/core/release/release-gate-contract.js +1 -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.js +1 -1
  47. package/dist/native/sks-menubar/Sources/AppDelegate.swift +1 -12
  48. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +2 -6
  49. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +28 -470
  50. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +1 -43
  51. package/dist/scripts/codex-current-app-server-v2-check.js +9 -8
  52. package/dist/scripts/codex-current-binary-identity-check.js +2 -2
  53. package/dist/scripts/codex-current-capability-check.js +9 -7
  54. package/dist/scripts/codex-current-dependency-check.js +21 -0
  55. package/dist/scripts/docs-truthfulness-check.js +4 -19
  56. package/dist/scripts/fast-codex-service-tier-proof-check.js +0 -1
  57. package/dist/scripts/latest-version-guidance-check.js +1 -1
  58. package/dist/scripts/packlist-performance-check.js +0 -1
  59. package/dist/scripts/release-metadata-check.js +1 -1
  60. package/dist/vendor/openai-codex/latest/hooks/permission-request.command.input.schema.json +6 -0
  61. package/dist/vendor/openai-codex/latest/hooks/permission-request.command.output.schema.json +2 -16
  62. package/dist/vendor/openai-codex/latest/hooks/post-compact.command.input.schema.json +6 -0
  63. package/dist/vendor/openai-codex/latest/hooks/post-tool-use.command.input.schema.json +6 -0
  64. package/dist/vendor/openai-codex/latest/hooks/post-tool-use.command.output.schema.json +2 -16
  65. package/dist/vendor/openai-codex/latest/hooks/pre-compact.command.input.schema.json +6 -0
  66. package/dist/vendor/openai-codex/latest/hooks/pre-tool-use.command.input.schema.json +6 -0
  67. package/dist/vendor/openai-codex/latest/hooks/pre-tool-use.command.output.schema.json +2 -16
  68. package/dist/vendor/openai-codex/latest/hooks/session-start.command.output.schema.json +2 -16
  69. package/dist/vendor/openai-codex/latest/hooks/snapshot-metadata.json +7 -5
  70. package/dist/vendor/openai-codex/latest/hooks/subagent-start.command.output.schema.json +4 -18
  71. package/dist/vendor/openai-codex/latest/hooks/user-prompt-submit.command.input.schema.json +6 -0
  72. package/dist/vendor/openai-codex/latest/hooks/user-prompt-submit.command.output.schema.json +2 -16
  73. package/package.json +3 -7
  74. package/release-gates.v2.json +10 -17
  75. package/config/codex-releases/rust-v0.146.0.json +0 -45
  76. package/dist/core/agents/agent-runner-codex-exec.js +0 -177
  77. package/dist/core/codex-compat/codex-release-manifest.js +0 -72
  78. package/dist/core/commands/telegram-command.js +0 -447
  79. package/dist/core/telegram/access.js +0 -58
  80. package/dist/core/telegram/client.js +0 -157
  81. package/dist/core/telegram/confirmation.js +0 -37
  82. package/dist/core/telegram/doctor.js +0 -159
  83. package/dist/core/telegram/index.js +0 -9
  84. package/dist/core/telegram/keychain.js +0 -317
  85. package/dist/core/telegram/liveness.js +0 -124
  86. package/dist/core/telegram/poller.js +0 -90
  87. package/dist/core/telegram/redaction.js +0 -15
  88. package/dist/core/telegram/state-lock.js +0 -416
  89. package/dist/core/telegram/types.js +0 -1
  90. package/dist/native/sks-menubar/Sources/RemoteCodingSettingsControls.swift +0 -160
  91. package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +0 -498
  92. package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +0 -236
  93. package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +0 -454
  94. package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +0 -56
  95. package/dist/native/sks-menubar/Sources/TelegramStateLock.swift +0 -448
  96. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +0 -244
  97. package/dist/native/sks-menubar/Sources/TelegramTransport.swift +0 -499
  98. package/dist/scripts/codex-current-manifest-check.js +0 -29
  99. package/schemas/codex/app-server-0.146/codex_app_server_protocol.v2.schemas.json +0 -19858
@@ -1,493 +1,51 @@
1
1
  import Cocoa
2
2
 
3
- final class RemoteCodingViewController: NSViewController, ControlCenterPage, NSTextFieldDelegate {
4
- private static let botFatherURL = URL(string: "https://t.me/BotFather")!
5
-
6
- private let processClient: ProcessClient
7
- private let telegramService: TelegramMenuBarService
8
- private let serviceBadge = NativeView.badge("Checking Telegram service", color: .systemBlue)
9
- private let serviceDetail = NativeView.detail("Reading secret-free Telegram liveness status…")
10
- private let pairingDetail = NativeView.detail("No pairing code has been issued in this session.")
11
- private let serviceSpinner = NativeView.spinner(label: "Telegram operation in progress")
12
- private lazy var settingsControls = RemoteCodingSettingsControls(
13
- target: self,
14
- submitTokenAction: #selector(enterToken),
15
- copyPairingAction: #selector(copyPairingCommand)
16
- )
17
- private lazy var pairButton = NativeView.button("Generate Pairing Code", target: self, action: #selector(generatePairingCode))
18
- private lazy var startButton = NativeView.button("Start", target: self, action: #selector(startService))
19
- private lazy var stopButton = NativeView.button("Stop", target: self, action: #selector(stopService))
20
- private lazy var restartButton = NativeView.button("Restart", target: self, action: #selector(restartService))
21
- private lazy var refreshButton = NativeView.button("Refresh Status", target: self, action: #selector(refreshStatusAction))
22
- private var operationInFlight = false
23
- private var tokenConfigured = false
24
- private var pollerRunning = false
25
-
26
- init(processClient: ProcessClient, telegramService: TelegramMenuBarService) {
27
- self.processClient = processClient
28
- self.telegramService = telegramService
29
- super.init(nibName: nil, bundle: nil)
30
- }
31
-
32
- 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")!
33
6
 
34
7
  override func loadView() {
35
- let botFatherButton = NativeView.button("Open @BotFather", target: self, action: #selector(openBotFather))
36
- botFatherButton.setAccessibilityHelp("Open the official Telegram BotFather chat in your default handler.")
37
- settingsControls.configure(delegate: self)
38
- pairButton.setAccessibilityHelp("With Telegram polling running, issue a short-lived, single-use code for pairing one intended private chat.")
39
- startButton.setAccessibilityHelp("Start Telegram polling for the current SKS companion session after a bot token is configured.")
40
- stopButton.setAccessibilityHelp("Stop polling for the current companion session without deleting the token or paired chats. A companion relaunch starts it again.")
41
- restartButton.setAccessibilityHelp("Restart the single resident Telegram poller and revalidate the stored bot identity.")
42
- refreshButton.setAccessibilityHelp("Refresh secret-free bot, pairing, audit, and poller status.")
43
- pairingDetail.isSelectable = true
44
- pairingDetail.setAccessibilityLabel("Telegram pairing instructions")
45
- 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.")
46
15
 
47
- let setupCard = NativeView.card(
48
- title: "Connect with BotFather",
49
- subtitle: "Create a Telegram bot, then let SKS validate and store its token privately.",
50
- views: [
51
- NativeView.detail(
52
- "1. Create or select any bot you own in @BotFather. 2. Copy that bot's complete HTTP API token. " +
53
- "3. Paste it below and choose Verify, Save & Apply. The token is the only value you enter: SKS derives the bot ID from Telegram getMe, and learns chat and user IDs through private-chat pairing."
54
- ),
55
- NativeView.detail(
56
- "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."
57
- ),
58
- ControlKit.keyValueRow("Active Bot", settingsControls.selectedBotDetail),
59
- ControlKit.keyValueRow("Bot Token", settingsControls.botTokenField),
60
- NativeView.row([botFatherButton, settingsControls.setupButton])
61
- ]
16
+ let docsButton = NativeView.button(
17
+ "Read Paseo Docs",
18
+ target: self,
19
+ action: #selector(openDocs)
62
20
  )
21
+ docsButton.setAccessibilityLabel("Read Paseo documentation")
22
+ docsButton.setAccessibilityHelp("Open the official Paseo documentation in your default browser.")
63
23
 
64
- let pairingCard = NativeView.card(
65
- title: "Pair a Private Chat",
66
- 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.",
67
27
  views: [
68
28
  NativeView.detail(
69
- "With Telegram polling running, generate a short-lived command, open the selected bot from the intended private chat, and send that command. SKS derives the chat and user IDs from Telegram; they are never typed into this screen. Codes are single-use and 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."
70
30
  ),
71
- ControlKit.keyValueRow("Pair Command", settingsControls.pairingCommandField),
72
- pairingDetail,
73
- NativeView.row([pairButton, settingsControls.copyPairingButton])
74
- ]
75
- )
76
-
77
- let statusCard = NativeView.card(
78
- title: "Service Status",
79
- subtitle: "Control the single SKS-owned poller for this companion session and inspect secret-free health evidence.",
80
- views: [
81
- NativeView.row([serviceBadge, serviceSpinner]),
82
- serviceDetail,
83
- NativeView.row([startButton, stopButton, restartButton, refreshButton])
31
+ NativeView.row([websiteButton, docsButton])
84
32
  ]
85
33
  )
86
34
 
87
35
  view = NativeView.page([
88
- NativeView.title("Telegram Remote Control"),
36
+ NativeView.title("Remote Coding"),
89
37
  NativeView.detail(
90
- "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."
91
39
  ),
92
- setupCard,
93
- pairingCard,
94
- statusCard
40
+ recommendation
95
41
  ])
96
- updateControls()
97
- }
98
-
99
- func refreshOnAppear() { refreshStatus() }
100
-
101
- @objc private func openBotFather() {
102
- NSWorkspace.shared.open(Self.botFatherURL)
103
- }
104
-
105
- @objc private func enterToken() {
106
- guard !operationInFlight else { return }
107
- if TelegramPrivateFileStore.operatorEnvironmentOverrideActive() {
108
- showAttention(
109
- "Environment-managed token is active",
110
- 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."
111
- )
112
- return
113
- }
114
- let normalizedToken = settingsControls.consumeTokenInput()
115
- updateControls()
116
- guard RemoteCodingSettingsControls.validTokenShape(normalizedToken) else {
117
- showAttention(
118
- "Token not saved",
119
- detail: "That value does not match the BotFather token format. Copy the complete HTTP API token and try again."
120
- )
121
- return
122
- }
123
- saveToken(normalizedToken, removeWebhook: false)
124
- }
125
-
126
- private func promptForWebhookRemovalToken() {
127
- guard !operationInFlight, let window = view.window else { return }
128
- AlertFactory.textSheet(
129
- window: window,
130
- title: "Authorize Telegram Webhook Removal",
131
- message: "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.",
132
- secure: true,
133
- placeholder: "123456789:BotFatherToken",
134
- actionTitle: "Remove Webhook & Save"
135
- ) { [weak self] token in
136
- guard let self, let token else { return }
137
- let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
138
- guard RemoteCodingSettingsControls.validTokenShape(normalizedToken) else {
139
- self.showAttention(
140
- "Token not saved",
141
- detail: "That value does not match the BotFather token format. Copy the complete HTTP API token and try again."
142
- )
143
- return
144
- }
145
- self.saveToken(normalizedToken, removeWebhook: true)
146
- }
147
- }
148
-
149
- @objc private func generatePairingCode() {
150
- guard !operationInFlight else { return }
151
- beginOperation("Issuing a short-lived pairing code…")
152
- processClient.run(
153
- ["telegram", "pair", "--json"],
154
- environment: telegramSecretFreeEnvironment,
155
- timeout: NativeView.statusTimeout,
156
- logOutput: false
157
- ) { [weak self] result in
158
- guard let self else { return }
159
- self.endOperation()
160
- guard result.code == 0,
161
- let response = self.decode(TelegramCenterPairResponse.self, result.output),
162
- response.ok,
163
- let code = response.code,
164
- code.range(of: #"^\d{6}-[A-F0-9]{4}$"#, options: .regularExpression) != nil else {
165
- self.showAttention(
166
- "Pairing code unavailable",
167
- detail: RemoteCodingSettingsControls.publicFailure(
168
- result.output,
169
- fallback: "SKS could not create a pairing code. Refresh status and try again."
170
- )
171
- )
172
- return
173
- }
174
- let expiration = response.expires_at.map { " It expires at \($0)." } ?? ""
175
- let instruction = response.instruction
176
- ?? "Send /start \(code) to this bot from the intended private chat."
177
- let postPair = response.post_pair_command ?? "/sks status {}"
178
- let confirmation = response.confirmation_grammar ?? "/confirm <nonce>"
179
- let statusGuidance = instruction.contains(postPair) ? "" : " Then try \(postPair)."
180
- self.settingsControls.setPairingCode(code)
181
- self.pairingDetail.stringValue = "\(instruction)\(expiration)\(statusGuidance) Confirm prompted actions with \(confirmation)."
182
- self.pairingDetail.setAccessibilityValue(self.pairingDetail.stringValue)
183
- self.showProgress("Pairing code ready", detail: "Use the displayed code once in the intended private chat.")
184
- self.refreshStatus(after: 1)
185
- }
186
- }
187
-
188
- @objc private func copyPairingCommand() {
189
- guard settingsControls.copyPairingCommand() else { return }
190
- showProgress("Pairing command copied", detail: "Paste it once into the intended private chat with the selected bot.")
191
- }
192
-
193
- @objc private func startService() { runLifecycle(.start) }
194
- @objc private func stopService() { runLifecycle(.stop) }
195
- @objc private func restartService() { runLifecycle(.restart) }
196
- @objc private func refreshStatusAction() { refreshStatus() }
197
-
198
- private enum LifecycleAction { case start, stop, restart }
199
-
200
- private func runLifecycle(_ action: LifecycleAction) {
201
- guard !operationInFlight else { return }
202
- let actionName: String
203
- switch action {
204
- case .start: actionName = "Starting"
205
- case .stop: actionName = "Stopping"
206
- case .restart: actionName = "Restarting"
207
- }
208
- beginOperation("\(actionName) Telegram polling…")
209
-
210
- switch action {
211
- case .stop:
212
- let task = telegramService.stop()
213
- Task { @MainActor [weak self] in
214
- await task.value
215
- self?.endOperation()
216
- self?.showProgress(
217
- "Telegram polling stopped",
218
- detail: "The token and paired chats were preserved. Relaunching the SKS companion starts polling again."
219
- )
220
- self?.refreshStatus(after: 0.2)
221
- }
222
- case .start, .restart:
223
- let task = action == .start ? telegramService.start() : telegramService.restart()
224
- Task { @MainActor [weak self] in
225
- do {
226
- let receipt = try await task.value
227
- self?.endOperation()
228
- self?.render(receipt)
229
- } catch {
230
- self?.endOperation()
231
- self?.showAttention(
232
- "Telegram polling did not start",
233
- detail: "Refresh status for the verified blocker. Re-enter the BotFather token if identity validation failed."
234
- )
235
- }
236
- self?.refreshStatus(after: 0.3)
237
- }
238
- }
239
- }
240
-
241
- private func saveToken(_ token: String, removeWebhook: Bool) {
242
- let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
243
- guard RemoteCodingSettingsControls.validTokenShape(normalizedToken) else {
244
- showAttention(
245
- "Token not saved",
246
- detail: "That value does not match the BotFather token format. Copy the complete token and try again."
247
- )
248
- return
249
- }
250
- beginOperation("Verifying bot identity with Telegram…")
251
- var arguments = ["telegram", "setup", "--token-stdin", "--json"]
252
- if TelegramPrivateFileStore.operatorEnvironmentOverrideActive() {
253
- arguments.append("--operator-env-override-active")
254
- }
255
- if removeWebhook { arguments.append("--remove-webhook") }
256
- processClient.run(
257
- arguments,
258
- stdin: normalizedToken + "\n",
259
- environment: telegramSecretFreeEnvironment,
260
- timeout: NativeView.mutationTimeout,
261
- logOutput: false
262
- ) { [weak self] result in
263
- guard let self else { return }
264
- let response = self.decode(TelegramCenterSetupResponse.self, result.output)
265
- guard result.code == 0,
266
- let response,
267
- response.ok,
268
- response.getme_verified == true,
269
- response.token_stored == true else {
270
- self.endOperation()
271
- if !removeWebhook,
272
- response?.error == "telegram_webhook_configured_remove_consent_required" {
273
- self.confirmWebhookRemoval()
274
- return
275
- }
276
- if response?.error == "telegram_operator_env_override_active" {
277
- self.showAttention(
278
- "Environment-managed token is active",
279
- detail: response?.operator_action
280
- ?? "Remove the operator-managed Telegram token environment override and relaunch before storing a file token."
281
- )
282
- return
283
- }
284
- if response?.partial_success == true {
285
- self.showAttention(
286
- "Telegram setup partially completed",
287
- detail: RemoteCodingSettingsControls.partialSetupRecovery(response)
288
- )
289
- return
290
- }
291
- self.showAttention(
292
- "Token not saved",
293
- detail: RemoteCodingSettingsControls.publicFailure(
294
- result.output,
295
- fallback: "Telegram setup could not complete. Check the connection and SKS diagnostics, then try again."
296
- )
297
- )
298
- return
299
- }
300
- let restart = self.telegramService.restart()
301
- Task { @MainActor [weak self] in
302
- do {
303
- let receipt = try await restart.value
304
- self?.endOperation()
305
- self?.render(receipt)
306
- self?.settingsControls.renderSelectedBot(
307
- botID: response.bot_id ?? receipt.bot_id,
308
- username: response.bot_username,
309
- configured: true,
310
- identityValid: true
311
- )
312
- if response.bot_state_reset == true {
313
- self?.settingsControls.resetPairingCommand("Generate a new code because the selected bot changed.")
314
- }
315
- let selectedBot = response.bot_username.map { "@\($0)" } ?? "The selected bot"
316
- self?.showProgress(
317
- "Bot verified and connected",
318
- detail: response.bot_state_reset == true
319
- ? "\(selectedBot) is verified and stored privately. Bot-scoped pairing and poll state were reset; generate a new pairing code."
320
- : "\(selectedBot) is verified and stored privately. Generate a pairing code for the intended private chat."
321
- )
322
- } catch {
323
- self?.endOperation()
324
- self?.showAttention(
325
- "Token saved; poller needs attention",
326
- detail: "The verified token was stored, but the resident poller did not restart. Refresh status for the blocker."
327
- )
328
- }
329
- self?.refreshStatus(after: 0.3)
330
- }
331
- }
332
- }
333
-
334
- private func confirmWebhookRemoval() {
335
- guard let window = view.window else { return }
336
- AlertFactory.confirmSheet(
337
- window: window,
338
- title: "Remove the existing Telegram webhook?",
339
- 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.",
340
- destructive: true,
341
- actionTitle: "Continue"
342
- ) { [weak self] confirmed in
343
- guard confirmed else { return }
344
- self?.promptForWebhookRemovalToken()
345
- }
346
- }
347
-
348
- private var telegramSecretFreeEnvironment: [String: String] {
349
- ["TELEGRAM_BOT_TOKEN": "", "SKS_TELEGRAM_BOT_TOKEN": ""]
350
- }
351
-
352
- private func refreshStatus(after delay: TimeInterval = 0) {
353
- guard delay > 0 else {
354
- loadStatus()
355
- return
356
- }
357
- DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in self?.loadStatus() }
358
- }
359
-
360
- private func loadStatus() {
361
- guard !operationInFlight else { return }
362
- beginOperation("Refreshing secret-free Telegram status…")
363
- processClient.run(
364
- ["telegram", "doctor", "--json"],
365
- environment: telegramSecretFreeEnvironment,
366
- timeout: NativeView.statusTimeout,
367
- logOutput: false
368
- ) { [weak self] result in
369
- guard let self else { return }
370
- self.endOperation()
371
- guard let response = self.decode(TelegramCenterDoctorResponse.self, result.output) else {
372
- self.tokenConfigured = false
373
- self.pollerRunning = false
374
- self.showAttention("Status unavailable", detail: "SKS could not read the Telegram liveness receipt.")
375
- return
376
- }
377
- self.render(response)
378
- }
379
- }
380
-
381
- private func render(_ response: TelegramCenterDoctorResponse) {
382
- tokenConfigured = response.token_configured
383
- pollerRunning = response.poller.running
384
- settingsControls.renderSelectedBot(
385
- botID: response.bot_id,
386
- configured: response.token_configured,
387
- identityValid: response.bot_identity_valid
388
- )
389
- let paired = response.paired_chat_count == 1 ? "1 paired chat" : "\(response.paired_chat_count) paired chats"
390
- let source = RemoteCodingSettingsControls.tokenSourceDescription(response.token_source)
391
- if response.ok {
392
- NativeView.setBadge(serviceBadge, text: "Ready", color: .systemGreen)
393
- serviceDetail.stringValue = "Bot identity verified · \(source) · \(paired) · poller running · audit healthy."
394
- } else if response.self_heal_action == "operator_remove_webhook"
395
- || response.poller.last_error?.range(of: "409|webhook", options: .regularExpression) != nil {
396
- NativeView.setBadge(serviceBadge, text: "Webhook blocks polling", color: .systemOrange)
397
- 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."
398
- } else if !response.token_configured {
399
- NativeView.setBadge(serviceBadge, text: "Bot token required", color: .systemOrange)
400
- serviceDetail.stringValue = "Create a bot with @BotFather, then enter and verify its HTTP API token."
401
- } else if !response.bot_identity_valid {
402
- NativeView.setBadge(serviceBadge, text: "Bot identity not verified", color: .systemRed)
403
- serviceDetail.stringValue = "Telegram could not validate the stored bot identity. Enter the current BotFather token again."
404
- } else if !response.audit_healthy {
405
- NativeView.setBadge(serviceBadge, text: "Audit unavailable", color: .systemRed)
406
- serviceDetail.stringValue = "Remote control is stopped because its local audit ledger is unavailable. Repair local SKS permissions before restarting."
407
- } else if response.paired_chat_count == 0 {
408
- NativeView.setBadge(serviceBadge, text: "Private chat not paired", color: .systemOrange)
409
- 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."
410
- } else if !response.poller.running {
411
- NativeView.setBadge(serviceBadge, text: "Poller stopped", color: .systemOrange)
412
- serviceDetail.stringValue = "Bot identity verified · \(source) · \(paired) · poller stopped. Choose Start to resume."
413
- } else {
414
- NativeView.setBadge(serviceBadge, text: "Needs attention", color: .systemOrange)
415
- serviceDetail.stringValue = "Bot identity verified · \(paired) · poller degraded after \(response.poller.consecutive_failures) consecutive failures. Refresh status or Restart."
416
- }
417
- serviceDetail.setAccessibilityValue(serviceDetail.stringValue)
418
- updateControls()
419
- }
420
-
421
- private func render(_ receipt: TelegramLivenessReceipt) {
422
- tokenConfigured = receipt.token_configured
423
- pollerRunning = receipt.running && receipt.poller.running
424
- settingsControls.renderSelectedBot(
425
- botID: receipt.bot_id,
426
- configured: receipt.token_configured,
427
- identityValid: receipt.bot_identity_valid
428
- )
429
- let paired = receipt.paired_chat_count == 1 ? "1 paired chat" : "\(receipt.paired_chat_count) paired chats"
430
- let source = RemoteCodingSettingsControls.tokenSourceDescription(receipt.token_source.rawValue)
431
- NativeView.setBadge(
432
- serviceBadge,
433
- text: pollerRunning ? "Running" : "Stopped",
434
- color: pollerRunning ? .systemGreen : .systemOrange
435
- )
436
- serviceDetail.stringValue = "Bot identity \(receipt.bot_identity_valid ? "verified" : "not verified") · \(source) · \(paired) · poller \(pollerRunning ? "running" : "stopped") · audit \(receipt.audit_healthy == true ? "healthy" : "unavailable")."
437
- serviceDetail.setAccessibilityValue(serviceDetail.stringValue)
438
- updateControls()
439
- }
440
-
441
- private func beginOperation(_ detail: String) {
442
- operationInFlight = true
443
- serviceSpinner.startAnimation(nil)
444
- serviceDetail.stringValue = detail
445
- updateControls()
446
- }
447
-
448
- private func endOperation() {
449
- operationInFlight = false
450
- serviceSpinner.stopAnimation(nil)
451
- updateControls()
452
42
  }
453
43
 
454
- private func showProgress(_ title: String, detail: String) {
455
- NativeView.setBadge(serviceBadge, text: title, color: .systemBlue)
456
- serviceDetail.stringValue = detail
457
- serviceDetail.setAccessibilityValue(detail)
458
- updateControls()
44
+ @objc private func openWebsite() {
45
+ NSWorkspace.shared.open(Self.websiteURL)
459
46
  }
460
47
 
461
- private func showAttention(_ title: String, detail: String) {
462
- NativeView.setBadge(serviceBadge, text: title, color: .systemOrange)
463
- serviceDetail.stringValue = detail
464
- serviceDetail.setAccessibilityValue(detail)
465
- updateControls()
48
+ @objc private func openDocs() {
49
+ NSWorkspace.shared.open(Self.docsURL)
466
50
  }
467
-
468
- private func updateControls() {
469
- let environmentOverride = TelegramPrivateFileStore.operatorEnvironmentOverrideActive()
470
- settingsControls.updateActionControls(
471
- operationInFlight: operationInFlight,
472
- tokenConfigured: tokenConfigured,
473
- environmentOverride: environmentOverride
474
- )
475
- pairButton.isEnabled = !operationInFlight && tokenConfigured && pollerRunning
476
- startButton.isEnabled = !operationInFlight && tokenConfigured && !pollerRunning
477
- stopButton.isEnabled = !operationInFlight && pollerRunning
478
- restartButton.isEnabled = !operationInFlight && tokenConfigured
479
- refreshButton.isEnabled = !operationInFlight
480
- }
481
-
482
- func controlTextDidChange(_ notification: Notification) {
483
- guard let field = notification.object as? NSTextField,
484
- field === settingsControls.botTokenField else { return }
485
- updateControls()
486
- }
487
-
488
- private func decode<T: Decodable>(_ type: T.Type, _ output: String) -> T? {
489
- guard let data = output.data(using: .utf8) else { return nil }
490
- return try? JSONDecoder().decode(type, from: data)
491
- }
492
-
493
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'