sneakoscope 7.3.0 → 7.6.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.
- package/README.md +129 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -1
- package/dist/cli/command-registry.js +2 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
- package/dist/cli/install-helpers-codex-lb-config.js +114 -9
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +788 -600
- package/dist/cli/router.js +30 -2
- package/dist/commands/codex-app.js +37 -1
- package/dist/commands/codex-lb.js +491 -300
- package/dist/config/skills-manifest.json +57 -71
- package/dist/core/align/align-route.js +127 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
- package/dist/core/codex-app/codex-app-restart.js +83 -0
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
- package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
- package/dist/core/codex-app/menubar/constants.js +5 -0
- package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
- package/dist/core/codex-app/menubar/installer.js +18 -0
- package/dist/core/codex-app/multi-provider-router.js +22 -10
- package/dist/core/codex-app/openrouter-activate.js +26 -1
- package/dist/core/codex-app.js +118 -26
- package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/capability-runner.js +204 -0
- package/dist/core/codex-lb/capability-types.js +1 -0
- package/dist/core/codex-lb/codex-lb-env.js +9 -13
- package/dist/core/codex-lb/codex-lb-setup.js +45 -8
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
- package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
- package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
- package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
- package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
- package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
- package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
- package/dist/core/codex-lb/desktop-controller.js +1410 -0
- package/dist/core/codex-lb/desktop-mode.js +32 -0
- package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
- package/dist/core/codex-lb/desktop-service.js +613 -0
- package/dist/core/codex-lb/legacy-migration.js +575 -0
- package/dist/core/codex-lb/migration-receipt.js +246 -0
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
- package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
- package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
- package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
- package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
- package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
- package/dist/core/codex-native/core-skill-manifest.js +22 -6
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
- package/dist/core/commands/align-command.js +220 -0
- package/dist/core/commands/image-ux-review-command.js +280 -68
- package/dist/core/commands/naruto-command.js +257 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +402 -34
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/doctor/skill-legacy-surface.js +1 -3
- package/dist/core/errors/message.js +3 -0
- package/dist/core/evidence/evidence-freshness.js +14 -0
- package/dist/core/feature-fixtures.js +9 -1
- package/dist/core/feature-registry.js +5 -3
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/hook-context.js +15 -2
- package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +33 -89
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +230 -12
- package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
- package/dist/core/imagegen/imagegen-capability.js +10 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +25 -13
- package/dist/core/init.js +1 -1
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/perf/release-latency-slo.js +12 -6
- package/dist/core/pipeline-internals/runtime-core.js +78 -0
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/preflight/parallel-preflight-engine.js +1 -1
- package/dist/core/proof/auto-finalize.js +98 -6
- package/dist/core/proof/route-proof-gate.js +12 -1
- package/dist/core/proof/route-proof-policy.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/release-proof-io.js +1 -3
- package/dist/core/release/release-real-contract.js +5 -4
- package/dist/core/release/release-stamp-proof.js +1 -3
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/remote/audit-idempotency.js +50 -18
- package/dist/core/remote/codex-public-activity.js +283 -0
- package/dist/core/remote/index.js +1 -0
- package/dist/core/remote/ssh-worker-client.js +4 -1
- package/dist/core/remote/worker.js +206 -27
- package/dist/core/research/research-adversarial-review.js +1 -3
- package/dist/core/research/research-claim-synthesizer.js +1 -3
- package/dist/core/research/research-falsification-runner.js +1 -3
- package/dist/core/research/research-source-evidence.js +1 -3
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +2 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +30 -10
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/secret-redaction.js +6 -0
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +85 -10
- package/dist/core/telegram/center-snapshot.js +718 -0
- package/dist/core/telegram/config.js +158 -31
- package/dist/core/telegram/hub.js +288 -33
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/ingress-spool.js +419 -0
- package/dist/core/telegram/ledgers.js +188 -29
- package/dist/core/telegram/messages.js +26 -6
- package/dist/core/telegram/owner-lock.js +10 -7
- package/dist/core/telegram/runtime-projection.js +38 -3
- package/dist/core/telegram/runtime.js +298 -63
- package/dist/core/telegram/service.js +137 -8
- package/dist/core/telegram/setup.js +291 -73
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
- package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
- package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
- package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
- package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
- package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
- package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
- package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
- package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
- package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
- package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
- package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +1 -1
- package/dist/scripts/helper-dedup-check.js +0 -2
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +21 -4
- package/dist/scripts/release-real-check.js +7 -0
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +8 -3
- package/release-gates.v2.json +176 -1
- package/runtime-required-scripts.json +30 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
- package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import Cocoa
|
|
2
|
+
|
|
3
|
+
extension RemoteTelegramViewController {
|
|
4
|
+
@objc func acknowledgeDeadLetters() {
|
|
5
|
+
guard let window = view.window, deadLetterIngressCount > 0 else { return }
|
|
6
|
+
AlertFactory.confirmSheet(
|
|
7
|
+
window: window,
|
|
8
|
+
title: "Acknowledge failed Telegram ingress?",
|
|
9
|
+
message: "This permanently retires \(deadLetterIngressCount) failed ingress record\(deadLetterIngressCount == 1 ? "" : "s"). It does not claim the Codex effect ran and it does not replay the original text. Keep the Hub stopped, acknowledge, restart it, then resend each failed request from the paired Telegram chat.",
|
|
10
|
+
destructive: true,
|
|
11
|
+
actionTitle: "Acknowledge"
|
|
12
|
+
) { [weak self] confirmed in
|
|
13
|
+
guard let self = self, confirmed else { return }
|
|
14
|
+
self.runMutation(
|
|
15
|
+
args: [
|
|
16
|
+
"telegram", "dead-letters", "acknowledge", "--yes",
|
|
17
|
+
"--project-root", AppRuntime.projectRoot,
|
|
18
|
+
"--json"
|
|
19
|
+
],
|
|
20
|
+
kind: "telegram-dead-letters-acknowledge",
|
|
21
|
+
summary: "Acknowledge failed Telegram ingress"
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func checkStatus(refreshConversation: Bool) {
|
|
27
|
+
guard !busy, !statusInFlight else { return }
|
|
28
|
+
statusInFlight = true
|
|
29
|
+
generation += 1
|
|
30
|
+
let requestGeneration = generation
|
|
31
|
+
checkButton?.isEnabled = false
|
|
32
|
+
readinessStatus.stringValue = "Checking Codex App, CLI, Git, Telegram, and Hub state…"
|
|
33
|
+
if !setupReady {
|
|
34
|
+
ControlKit.setBadge(setupBadge, text: "Checking Telegram setup…", tone: .busy)
|
|
35
|
+
setupStatus.stringValue = "Checking Keychain, bot identity, pairing, and dedicated sessions…"
|
|
36
|
+
}
|
|
37
|
+
var remote: [String: Any]?
|
|
38
|
+
var telegram: [String: Any]?
|
|
39
|
+
var telegramFailure: String?
|
|
40
|
+
let group = DispatchGroup()
|
|
41
|
+
group.enter()
|
|
42
|
+
processClient.run(
|
|
43
|
+
["remote", "readiness", "--project-root", AppRuntime.projectRoot, "--json"],
|
|
44
|
+
timeout: NativeView.statusTimeout
|
|
45
|
+
) { [weak self] result in
|
|
46
|
+
if result.code == 0, !result.truncated {
|
|
47
|
+
remote = self?.json(result.output)
|
|
48
|
+
}
|
|
49
|
+
group.leave()
|
|
50
|
+
}
|
|
51
|
+
group.enter()
|
|
52
|
+
processClient.run(
|
|
53
|
+
["telegram", "status", "--project-root", AppRuntime.projectRoot, "--json"],
|
|
54
|
+
timeout: ProcessClient.telegramStatusTimeout
|
|
55
|
+
) { [weak self] result in
|
|
56
|
+
if !result.timedOut,
|
|
57
|
+
!result.emptyOutput,
|
|
58
|
+
!result.truncated,
|
|
59
|
+
let payload = self?.json(result.output),
|
|
60
|
+
payload["schema"] as? String == "sks.telegram-status.v1" {
|
|
61
|
+
telegram = payload
|
|
62
|
+
} else {
|
|
63
|
+
telegramFailure = Self.telegramStatusFailureMessage(result)
|
|
64
|
+
}
|
|
65
|
+
group.leave()
|
|
66
|
+
}
|
|
67
|
+
group.notify(queue: .main) { [weak self] in
|
|
68
|
+
guard let self = self, self.generation == requestGeneration else { return }
|
|
69
|
+
self.statusInFlight = false
|
|
70
|
+
self.render(remote: remote, telegram: telegram)
|
|
71
|
+
if let telegramFailure = telegramFailure {
|
|
72
|
+
ControlKit.setBadge(
|
|
73
|
+
self.setupReady ? self.connectionBadge : self.setupBadge,
|
|
74
|
+
text: "Telegram live status unavailable",
|
|
75
|
+
tone: .warning
|
|
76
|
+
)
|
|
77
|
+
if self.setupReady {
|
|
78
|
+
self.connectionStatus.stringValue = "\(telegramFailure) The linked session and locally loaded conversation remain available."
|
|
79
|
+
} else {
|
|
80
|
+
self.setupStatus.stringValue = telegramFailure
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if !self.hubRunning,
|
|
84
|
+
let startupFailure = TelegramHubSupervisor.latestStartupFailureMessage {
|
|
85
|
+
ControlKit.setBadge(self.hubBadge, text: "Telegram Hub startup needs attention", tone: .error)
|
|
86
|
+
self.hubStatus.stringValue = startupFailure
|
|
87
|
+
}
|
|
88
|
+
self.updateButtons()
|
|
89
|
+
if self.setupReady {
|
|
90
|
+
if refreshConversation || self.loadedSessionId != self.selectedSessionId {
|
|
91
|
+
self.refreshSnapshot(force: true)
|
|
92
|
+
} else {
|
|
93
|
+
self.refreshLive(force: true)
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
self.clearSnapshot()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static func telegramStatusFailureMessage(_ result: ProcessResult) -> String {
|
|
102
|
+
if result.timedOut || result.output.contains("native_process_timeout") {
|
|
103
|
+
return "Telegram status exceeded its bounded native deadline. Retry after the current probe finishes or check network access to api.telegram.org."
|
|
104
|
+
}
|
|
105
|
+
if result.emptyOutput || result.output.contains("native_process_empty_output") {
|
|
106
|
+
return "Telegram status returned no JSON. Retry, then open Diagnostics if the CLI remains unavailable."
|
|
107
|
+
}
|
|
108
|
+
if result.truncated {
|
|
109
|
+
return "Telegram status exceeded the native output budget. Open Diagnostics and retry."
|
|
110
|
+
}
|
|
111
|
+
return "Telegram status returned an invalid response. Retry, then open Diagnostics if it persists."
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func refreshSnapshot(force: Bool = false) {
|
|
115
|
+
guard setupReady, !busy, !snapshotInFlight else { return }
|
|
116
|
+
if !force {
|
|
117
|
+
guard view.window?.isVisible == true else { return }
|
|
118
|
+
}
|
|
119
|
+
liveGeneration += 1
|
|
120
|
+
liveInFlight = false
|
|
121
|
+
snapshotInFlight = true
|
|
122
|
+
snapshotGeneration += 1
|
|
123
|
+
let requestGeneration = snapshotGeneration
|
|
124
|
+
let requestSessionId = selectedSessionId
|
|
125
|
+
let requestCursor = conversationCursor
|
|
126
|
+
let requestSortDirection = conversationSortDirection
|
|
127
|
+
updateButtons()
|
|
128
|
+
ControlKit.setBadge(liveSourceBadge, text: "Refreshing real Codex thread…", tone: .busy)
|
|
129
|
+
snapshotStatus.stringValue = "Reading the selected App Server thread and bounded live activity…"
|
|
130
|
+
var args = ["telegram", "center-snapshot", "--project-root", AppRuntime.projectRoot, "--json"]
|
|
131
|
+
if let selectedSessionId = selectedSessionId {
|
|
132
|
+
args += ["--session-id", selectedSessionId]
|
|
133
|
+
}
|
|
134
|
+
if let conversationCursor = conversationCursor {
|
|
135
|
+
args += ["--cursor", conversationCursor]
|
|
136
|
+
}
|
|
137
|
+
args += ["--sort-direction", conversationSortDirection]
|
|
138
|
+
processClient.run(
|
|
139
|
+
args,
|
|
140
|
+
timeout: 30,
|
|
141
|
+
maxOutputBytes: 896 * 1024,
|
|
142
|
+
logOutput: false
|
|
143
|
+
) { [weak self] result in
|
|
144
|
+
guard let self = self,
|
|
145
|
+
self.snapshotGeneration == requestGeneration,
|
|
146
|
+
self.selectedSessionId == requestSessionId,
|
|
147
|
+
self.conversationCursor == requestCursor,
|
|
148
|
+
self.conversationSortDirection == requestSortDirection else { return }
|
|
149
|
+
self.snapshotInFlight = false
|
|
150
|
+
self.updateButtons()
|
|
151
|
+
guard result.code == 0,
|
|
152
|
+
!result.truncated,
|
|
153
|
+
let snapshot = self.json(result.output),
|
|
154
|
+
self.validFullSnapshot(snapshot, sessionId: requestSessionId) else {
|
|
155
|
+
ControlKit.setBadge(self.liveSourceBadge, text: "Conversation refresh unavailable", tone: .warning)
|
|
156
|
+
self.snapshotStatus.stringValue = result.truncated
|
|
157
|
+
? "The private snapshot exceeded the native output budget. Open Diagnostics and retry after the thread is compacted."
|
|
158
|
+
: self.loadedSessionId == nil
|
|
159
|
+
? "The linked conversation could not be loaded. Retry the conversation refresh or open Diagnostics."
|
|
160
|
+
: "The last loaded conversation and activity remain visible. Retry the conversation refresh or open Diagnostics."
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
self.renderSnapshot(snapshot)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
func refreshLive(force: Bool = false) {
|
|
168
|
+
guard setupReady,
|
|
169
|
+
!busy,
|
|
170
|
+
!statusInFlight,
|
|
171
|
+
!snapshotInFlight,
|
|
172
|
+
!liveInFlight,
|
|
173
|
+
isViewingLatestConversationPage else { return }
|
|
174
|
+
if !force {
|
|
175
|
+
guard view.window?.isVisible == true else { return }
|
|
176
|
+
}
|
|
177
|
+
let requestSourceSignature = centerLiveSourceSignature()
|
|
178
|
+
let requestWasCatchUp = liveCatchUpInProgress
|
|
179
|
+
liveSourceFileSignature = requestSourceSignature
|
|
180
|
+
liveInFlight = true
|
|
181
|
+
liveGeneration += 1
|
|
182
|
+
let requestGeneration = liveGeneration
|
|
183
|
+
let requestSessionId = selectedSessionId
|
|
184
|
+
var args = ["telegram", "center-live", "--project-root", AppRuntime.projectRoot, "--json"]
|
|
185
|
+
if let selectedSessionId = selectedSessionId {
|
|
186
|
+
args += ["--session-id", selectedSessionId]
|
|
187
|
+
}
|
|
188
|
+
processClient.run(
|
|
189
|
+
args,
|
|
190
|
+
timeout: 10,
|
|
191
|
+
maxOutputBytes: 320 * 1024,
|
|
192
|
+
logOutput: false
|
|
193
|
+
) { [weak self] result in
|
|
194
|
+
guard let self = self,
|
|
195
|
+
self.liveGeneration == requestGeneration,
|
|
196
|
+
self.selectedSessionId == requestSessionId else { return }
|
|
197
|
+
self.liveInFlight = false
|
|
198
|
+
guard result.code == 0,
|
|
199
|
+
!result.truncated,
|
|
200
|
+
let live = self.json(result.output),
|
|
201
|
+
self.validLiveSnapshot(live, sessionId: requestSessionId) else {
|
|
202
|
+
self.liveCatchUpInProgress = false
|
|
203
|
+
self.renderLiveFailure()
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
self.renderLiveSnapshot(live)
|
|
207
|
+
let completionSourceSignature = self.centerLiveSourceSignature()
|
|
208
|
+
if Self.shouldRequestImmediateLiveCatchUp(
|
|
209
|
+
requestSignature: requestSourceSignature,
|
|
210
|
+
completionSignature: completionSourceSignature,
|
|
211
|
+
wasCatchUp: requestWasCatchUp
|
|
212
|
+
) {
|
|
213
|
+
self.liveCatchUpInProgress = true
|
|
214
|
+
self.liveSourceFileSignature = nil
|
|
215
|
+
self.refreshLive(force: true)
|
|
216
|
+
} else if completionSourceSignature != requestSourceSignature {
|
|
217
|
+
self.liveCatchUpInProgress = false
|
|
218
|
+
self.liveSourceFileSignature = nil
|
|
219
|
+
} else {
|
|
220
|
+
self.liveCatchUpInProgress = false
|
|
221
|
+
self.liveSourceFileSignature = requestSourceSignature
|
|
222
|
+
}
|
|
223
|
+
guard let revision = live["conversation_revision"] as? String,
|
|
224
|
+
!revision.isEmpty,
|
|
225
|
+
revision != self.loadedConversationRevision,
|
|
226
|
+
revision != self.liveTriggeredConversationRevision,
|
|
227
|
+
self.isCompletedTurn(live["binding"] as? [String: Any]) else { return }
|
|
228
|
+
self.liveTriggeredConversationRevision = revision
|
|
229
|
+
self.refreshSnapshot(force: true)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
func startLivePolling() {
|
|
234
|
+
liveTimer?.invalidate()
|
|
235
|
+
liveSourceFileSignature = nil
|
|
236
|
+
liveCatchUpInProgress = false
|
|
237
|
+
liveTimer = Timer.scheduledTimer(withTimeInterval: 2.5, repeats: true) { [weak self] _ in
|
|
238
|
+
guard let self = self else { return }
|
|
239
|
+
guard self.view.window?.isVisible == true else {
|
|
240
|
+
self.liveTimer?.invalidate()
|
|
241
|
+
self.liveTimer = nil
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
guard self.isViewingLatestConversationPage else { return }
|
|
245
|
+
let sourceSignature = self.centerLiveSourceSignature()
|
|
246
|
+
guard sourceSignature != self.liveSourceFileSignature else { return }
|
|
247
|
+
self.refreshLive()
|
|
248
|
+
}
|
|
249
|
+
if let liveTimer = liveTimer {
|
|
250
|
+
RunLoop.main.add(liveTimer, forMode: .common)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
func resetConversationPagination() {
|
|
255
|
+
conversationCursor = nil
|
|
256
|
+
conversationSortDirection = "desc"
|
|
257
|
+
isViewingLatestConversationPage = true
|
|
258
|
+
olderConversationCursor = nil
|
|
259
|
+
olderConversationSortDirection = "desc"
|
|
260
|
+
newerConversationCursor = nil
|
|
261
|
+
newerConversationSortDirection = "asc"
|
|
262
|
+
updateButtons()
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
func clearSnapshot() {
|
|
266
|
+
invalidateCenterRequests()
|
|
267
|
+
clearLoadedCenterState()
|
|
268
|
+
resetConversationPagination()
|
|
269
|
+
ControlKit.setBadge(liveSourceBadge, text: "Codex thread not loaded", tone: .neutral)
|
|
270
|
+
snapshotStatus.stringValue = "Complete Telegram setup to load a linked conversation."
|
|
271
|
+
renderConversationPlaceholder("Connect Telegram to load a Codex conversation.")
|
|
272
|
+
renderActivityPlaceholder("Public work activity will appear after a linked Codex turn starts.")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
func clearLoadedCenterState() {
|
|
276
|
+
loadedSessionId = nil
|
|
277
|
+
loadedConversationRevision = nil
|
|
278
|
+
loadedLiveActivityDigest = nil
|
|
279
|
+
liveTriggeredConversationRevision = nil
|
|
280
|
+
loadedTranscriptRows = []
|
|
281
|
+
loadedHistoryActivityRows = []
|
|
282
|
+
loadedLiveActivityRows = []
|
|
283
|
+
loadedThreadId = nil
|
|
284
|
+
loadedThreadStatus = "unknown"
|
|
285
|
+
loadedConversationPageTurnCount = 0
|
|
286
|
+
loadedSnapshotTruncated = false
|
|
287
|
+
loadedLiveActivityState = "pending"
|
|
288
|
+
loadedLiveActivityError = nil
|
|
289
|
+
renderedTranscriptSignature = nil
|
|
290
|
+
renderedActivitySignature = nil
|
|
291
|
+
liveSourceFileSignature = nil
|
|
292
|
+
liveCatchUpInProgress = false
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
func applyConversationPage(_ page: [String: Any]?) {
|
|
296
|
+
guard let page = page else {
|
|
297
|
+
resetConversationPagination()
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
isViewingLatestConversationPage = page["is_latest"] as? Bool == true
|
|
301
|
+
olderConversationCursor = page["older_cursor"] as? String
|
|
302
|
+
olderConversationSortDirection = validConversationSortDirection(
|
|
303
|
+
page["older_sort_direction"] as? String,
|
|
304
|
+
fallback: "desc"
|
|
305
|
+
)
|
|
306
|
+
newerConversationCursor = page["newer_cursor"] as? String
|
|
307
|
+
newerConversationSortDirection = validConversationSortDirection(
|
|
308
|
+
page["newer_sort_direction"] as? String,
|
|
309
|
+
fallback: "asc"
|
|
310
|
+
)
|
|
311
|
+
updateButtons()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private func validConversationSortDirection(_ value: String?, fallback: String) -> String {
|
|
315
|
+
guard let value = value, value == "asc" || value == "desc" else { return fallback }
|
|
316
|
+
return value
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private func isCompletedTurn(_ binding: [String: Any]?) -> Bool {
|
|
320
|
+
guard let status = binding?["last_turn_status"] as? String else { return false }
|
|
321
|
+
return status == "completed" || status == "failed" || status == "interrupted"
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private func validFullSnapshot(_ payload: [String: Any], sessionId: String?) -> Bool {
|
|
325
|
+
guard payload["schema"] as? String == "sks.telegram-center-snapshot.v1",
|
|
326
|
+
payload["ok"] as? Bool == true,
|
|
327
|
+
(payload["selected_session_id"] as? String) == sessionId,
|
|
328
|
+
let state = payload["state"] as? String,
|
|
329
|
+
["ready", "awaiting_first_message"].contains(state),
|
|
330
|
+
payload["conversation_revision"] is String,
|
|
331
|
+
payload["live_activity_digest"] is String,
|
|
332
|
+
payload["transcript"] is [[String: Any]],
|
|
333
|
+
payload["activity"] is [[String: Any]] else { return false }
|
|
334
|
+
if state == "ready" {
|
|
335
|
+
return payload["thread"] is [String: Any]
|
|
336
|
+
&& payload["conversation_page"] is [String: Any]
|
|
337
|
+
}
|
|
338
|
+
return true
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private func validLiveSnapshot(_ payload: [String: Any], sessionId: String?) -> Bool {
|
|
342
|
+
guard payload["schema"] as? String == "sks.telegram-center-live.v1",
|
|
343
|
+
payload["ok"] as? Bool == true,
|
|
344
|
+
(payload["selected_session_id"] as? String) == sessionId,
|
|
345
|
+
let state = payload["state"] as? String,
|
|
346
|
+
["ready", "awaiting_first_message"].contains(state),
|
|
347
|
+
payload["binding"] is [String: Any],
|
|
348
|
+
payload["conversation_revision"] is String,
|
|
349
|
+
payload["live_activity_digest"] is String,
|
|
350
|
+
payload["activity"] is [[String: Any]] else { return false }
|
|
351
|
+
return true
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
func invalidateCenterRequests() {
|
|
355
|
+
snapshotGeneration += 1
|
|
356
|
+
liveGeneration += 1
|
|
357
|
+
snapshotInFlight = false
|
|
358
|
+
liveInFlight = false
|
|
359
|
+
liveCatchUpInProgress = false
|
|
360
|
+
updateButtons()
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
static func shouldRequestImmediateLiveCatchUp(
|
|
364
|
+
requestSignature: String,
|
|
365
|
+
completionSignature: String,
|
|
366
|
+
wasCatchUp: Bool
|
|
367
|
+
) -> Bool {
|
|
368
|
+
completionSignature != requestSignature && !wasCatchUp
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
func centerLiveSourceSignature() -> String {
|
|
372
|
+
let remoteRoot = URL(fileURLWithPath: AppRuntime.projectRoot, isDirectory: true)
|
|
373
|
+
.appendingPathComponent(".sneakoscope", isDirectory: true)
|
|
374
|
+
.appendingPathComponent("remote", isDirectory: true)
|
|
375
|
+
let names = [
|
|
376
|
+
"events.json",
|
|
377
|
+
"events-health.json",
|
|
378
|
+
"codex-session-bindings.json"
|
|
379
|
+
]
|
|
380
|
+
return names.map { name in
|
|
381
|
+
let url = remoteRoot.appendingPathComponent(name)
|
|
382
|
+
guard let attributes = try? FileManager.default.attributesOfItem(atPath: url.path) else {
|
|
383
|
+
return "\(name):missing"
|
|
384
|
+
}
|
|
385
|
+
let modified = (attributes[.modificationDate] as? Date)?.timeIntervalSince1970 ?? 0
|
|
386
|
+
let size = (attributes[.size] as? NSNumber)?.uint64Value ?? 0
|
|
387
|
+
return "\(name):\(modified):\(size)"
|
|
388
|
+
}.joined(separator: "|")
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
func setupGuidance(
|
|
392
|
+
token: Bool,
|
|
393
|
+
botVerified: Bool,
|
|
394
|
+
longPollReady: Bool,
|
|
395
|
+
pairing: Bool,
|
|
396
|
+
sessions: Int,
|
|
397
|
+
fallback: String?
|
|
398
|
+
) -> String {
|
|
399
|
+
if !token || !botVerified {
|
|
400
|
+
return "Next: Create or regenerate the bot in @BotFather, send /start in its private chat, then connect the token here."
|
|
401
|
+
}
|
|
402
|
+
if !pairing {
|
|
403
|
+
return "Next: Send /start to the bot from the private Telegram account you want to pair, then reconnect."
|
|
404
|
+
}
|
|
405
|
+
if sessions == 0 {
|
|
406
|
+
return "Next: Reconnect the bot to register a dedicated coding session for this project."
|
|
407
|
+
}
|
|
408
|
+
if !longPollReady {
|
|
409
|
+
return "Next: Remove the bot’s existing webhook, send /start again, then reconnect."
|
|
410
|
+
}
|
|
411
|
+
return fallback.map { "Next: \($0)" } ?? "Next: Complete the highlighted setup step, then refresh status."
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
func userFacingThreadStatus(_ status: String) -> String {
|
|
415
|
+
switch status {
|
|
416
|
+
case "idle": return "ready"
|
|
417
|
+
case "active", "inProgress": return "working"
|
|
418
|
+
case "notLoaded": return "stored history available"
|
|
419
|
+
case "systemError": return "needs attention"
|
|
420
|
+
case "awaiting_first_message": return "awaiting first message"
|
|
421
|
+
case "unknown": return "status unavailable"
|
|
422
|
+
default: return "thread available"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
func setupFailureMessage(_ output: String) -> String {
|
|
427
|
+
if output.contains("telegram_pairing_start_not_found") {
|
|
428
|
+
return "No private /start message was found. Open the new bot, send /start from the Telegram account you want to pair, then retry."
|
|
429
|
+
}
|
|
430
|
+
if output.contains("telegram_pairing_ambiguous") {
|
|
431
|
+
return "More than one private /start was pending, so SKS refused to guess the operator. Use only the intended account, send /start again, then retry."
|
|
432
|
+
}
|
|
433
|
+
if output.contains("telegram_webhook_conflict") {
|
|
434
|
+
return "This bot still has a webhook from another service. Remove that webhook there, send /start again, then retry."
|
|
435
|
+
}
|
|
436
|
+
if output.contains("telegram_hub_must_be_stopped_before_setup") {
|
|
437
|
+
return "Stop the Telegram Hub before reconnecting the bot or rotating its token."
|
|
438
|
+
}
|
|
439
|
+
if output.contains("telegram_keychain_readback_failed") || output.contains("telegram_keychain_snapshot_failed") {
|
|
440
|
+
return "The token could not be read back from macOS Keychain for the Hub. Approve the Keychain prompt, then retry."
|
|
441
|
+
}
|
|
442
|
+
if output.contains("telegram_bot_auth_failed") || output.contains("\"error_code\":401") || output.contains("Unauthorized") {
|
|
443
|
+
return "Telegram rejected the token. Copy the current token from @BotFather and try again."
|
|
444
|
+
}
|
|
445
|
+
return "Setup failed. Confirm the BotFather token and private /start message, then retry. The token was not written to logs."
|
|
446
|
+
}
|
|
447
|
+
}
|