@gajae-code/coding-agent 0.10.2 → 0.11.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/CHANGELOG.md +50 -2
- package/dist/types/append-only-mode.d.ts +1 -1
- package/dist/types/cli/args.d.ts +4 -5
- package/dist/types/cli/daemon-cli.d.ts +12 -1
- package/dist/types/cli/notify-cli.d.ts +21 -1
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/notify.d.ts +28 -1
- package/dist/types/commands/sdk.d.ts +33 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
- package/dist/types/config/model-profile-activation.d.ts +35 -15
- package/dist/types/config/model-profiles.d.ts +11 -5
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/model-resolver.d.ts +39 -6
- package/dist/types/config/model-selector-value.d.ts +17 -0
- package/dist/types/config/models-config-schema.d.ts +10 -6
- package/dist/types/config/settings-schema.d.ts +133 -6
- package/dist/types/config/settings.d.ts +63 -35
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
- package/dist/types/coordinator-mcp/server.d.ts +6 -49
- package/dist/types/daemon/builtin.d.ts +6 -5
- package/dist/types/daemon/control-types.d.ts +1 -1
- package/dist/types/discovery/mcp-json.d.ts +12 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
- package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +10 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -3
- package/dist/types/extensibility/extensions/types.d.ts +141 -16
- package/dist/types/extensibility/hooks/types.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
- package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
- package/dist/types/harness-control-plane/operate.d.ts +5 -5
- package/dist/types/harness-control-plane/owner.d.ts +18 -4
- package/dist/types/harness-control-plane/receipts.d.ts +0 -1
- package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
- package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
- package/dist/types/harness-control-plane/types.d.ts +3 -4
- package/dist/types/lsp/index.d.ts +1 -1
- package/dist/types/main.d.ts +32 -38
- package/dist/types/modes/acp/acp-agent.d.ts +224 -53
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -15
- package/dist/types/modes/acp/permission-mode.d.ts +4 -0
- package/dist/types/modes/acp/startup-options.d.ts +6 -0
- package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
- package/dist/types/modes/components/model-selector.d.ts +1 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -4
- package/dist/types/modes/components/welcome.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
- package/dist/types/modes/index.d.ts +0 -4
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/runtime-init.d.ts +2 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
- package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
- package/dist/types/modes/types.d.ts +5 -0
- package/dist/types/runtime-mcp/client.d.ts +2 -0
- package/dist/types/runtime-mcp/config.d.ts +5 -1
- package/dist/types/runtime-mcp/manager.d.ts +8 -1
- package/dist/types/runtime-mcp/types.d.ts +4 -0
- package/dist/types/sdk/acp/adapter.d.ts +67 -0
- package/dist/types/sdk/acp/index.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +53 -0
- package/dist/types/sdk/broker/discovery.d.ts +30 -0
- package/dist/types/sdk/broker/ensure.d.ts +33 -0
- package/dist/types/sdk/broker/identity.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +8 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
- package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
- package/dist/types/sdk/broker/runtime.d.ts +33 -0
- package/dist/types/sdk/broker/session-index.d.ts +57 -0
- package/dist/types/sdk/broker/state-version.d.ts +8 -0
- package/dist/types/sdk/broker/transport.d.ts +9 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
- package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
- package/dist/types/sdk/bus/config.d.ts +274 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
- package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
- package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
- package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
- package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
- package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
- package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
- package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
- package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
- package/dist/types/sdk/bus/session-control.d.ts +83 -0
- package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
- package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
- package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
- package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
- package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/session-cli.d.ts +19 -0
- package/dist/types/sdk/client/client.d.ts +1 -0
- package/dist/types/sdk/client/discovery.d.ts +27 -0
- package/dist/types/sdk/client/index.d.ts +2 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
- package/dist/types/sdk/host/control/index.d.ts +2 -0
- package/dist/types/sdk/host/control/operations.d.ts +65 -0
- package/dist/types/sdk/host/events.d.ts +35 -0
- package/dist/types/sdk/host/host.d.ts +35 -0
- package/dist/types/sdk/host/index.d.ts +4 -0
- package/dist/types/sdk/host/query/cursor.d.ts +62 -0
- package/dist/types/sdk/host/query/handlers.d.ts +70 -0
- package/dist/types/sdk/host/query/index.d.ts +3 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
- package/dist/types/sdk/host/types.d.ts +32 -0
- package/dist/types/sdk/index.d.ts +6 -0
- package/dist/types/sdk/lifecycle-session.d.ts +14 -0
- package/dist/types/sdk/mcp/index.d.ts +1 -0
- package/dist/types/sdk/mcp/server.d.ts +37 -0
- package/dist/types/sdk/models.d.ts +44 -0
- package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
- package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
- package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
- package/dist/types/sdk/startup-capability.d.ts +50 -0
- package/dist/types/session/agent-session.d.ts +114 -21
- package/dist/types/session/default-model-selection.d.ts +25 -0
- package/dist/types/session/fallback-chain-controller.d.ts +52 -0
- package/dist/types/session/session-manager.d.ts +58 -1
- package/dist/types/setup/credential-auto-import.d.ts +36 -6
- package/dist/types/tools/ask-answer-registry.d.ts +1 -1
- package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
- package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
- package/dist/types/tools/browser/launch.d.ts +22 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
- package/dist/types/tools/browser/profile-posture.d.ts +37 -0
- package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
- package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
- package/dist/types/tools/browser/registry.d.ts +19 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/index.d.ts +8 -8
- package/package.json +34 -13
- package/scripts/build-sdk-package-smoke.ts +94 -0
- package/scripts/capture-notifications-settings-showcase.ts +435 -0
- package/scripts/compile-args.ts +4 -2
- package/scripts/g005-daemon-smoke.ts +4 -5
- package/scripts/g011-daemon-path-smoke.ts +5 -5
- package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
- package/scripts/generate-sdk-operation-inventory.ts +867 -0
- package/scripts/generate-telegram-baseline-manifest.ts +128 -0
- package/scripts/manifest-schema.ts +124 -0
- package/scripts/run-test-manifest.ts +153 -0
- package/scripts/run-threat-model-check.ts +157 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
- package/scripts/verify-gjc-sdk-rename.ts +90 -0
- package/src/append-only-mode.ts +1 -1
- package/src/cli/args.ts +43 -12
- package/src/cli/auth-gateway-cli.ts +2 -2
- package/src/cli/config-cli.ts +2 -1
- package/src/cli/daemon-cli.ts +63 -14
- package/src/cli/list-models.ts +1 -1
- package/src/cli/notify-cli.ts +288 -286
- package/src/cli.ts +21 -2
- package/src/commands/daemon.ts +50 -6
- package/src/commands/harness.ts +93 -36
- package/src/commands/launch.ts +5 -2
- package/src/commands/mcp-serve.ts +19 -12
- package/src/commands/notify.ts +36 -3
- package/src/commands/sdk.ts +426 -0
- package/src/commit/agentic/agent.ts +1 -0
- package/src/config/atomic-yaml-patch.ts +423 -0
- package/src/config/model-profile-activation.ts +240 -89
- package/src/config/model-profiles.ts +18 -15
- package/src/config/model-registry.ts +51 -26
- package/src/config/model-resolver.ts +138 -51
- package/src/config/model-selector-value.ts +50 -0
- package/src/config/models-config-schema.ts +14 -21
- package/src/config/settings-schema.ts +128 -10
- package/src/config/settings.ts +1120 -158
- package/src/coordinator-mcp/model-preset.ts +74 -20
- package/src/coordinator-mcp/server.ts +1597 -3052
- package/src/daemon/builtin.ts +8 -5
- package/src/daemon/control-types.ts +1 -1
- package/src/defaults/gjc/skills/team/SKILL.md +4 -4
- package/src/discovery/mcp-json.ts +327 -20
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/compact-handler.ts +3 -3
- package/src/extensibility/extensions/get-commands-handler.ts +2 -2
- package/src/extensibility/extensions/loader.ts +69 -5
- package/src/extensibility/extensions/runner.ts +112 -4
- package/src/extensibility/extensions/types.ts +176 -17
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/gjc-runtime/launch-tmux.ts +170 -86
- package/src/gjc-runtime/psmux-detect.ts +82 -4
- package/src/gjc-runtime/session-state-sidecar.ts +41 -31
- package/src/gjc-runtime/team-runtime.ts +54 -35
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
- package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
- package/src/gjc-runtime/tmux-sessions.ts +19 -13
- package/src/gjc-runtime/ultragoal-guard.ts +53 -6
- package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
- package/src/gjc-runtime/windows-powershell-command.ts +58 -0
- package/src/harness-control-plane/operate.ts +9 -9
- package/src/harness-control-plane/owner.ts +283 -39
- package/src/harness-control-plane/receipts.ts +0 -1
- package/src/harness-control-plane/sdk-transport.ts +395 -0
- package/src/harness-control-plane/session-transport.ts +94 -0
- package/src/harness-control-plane/types.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +34 -35
- package/src/lsp/client.ts +42 -6
- package/src/lsp/index.ts +2 -2
- package/src/main.ts +299 -204
- package/src/modes/DESIGN.md +258 -0
- package/src/modes/acp/acp-agent.ts +1228 -2612
- package/src/modes/acp/acp-client-bridge.ts +2 -20
- package/src/modes/acp/acp-event-mapper.ts +18 -2
- package/src/modes/acp/acp-mode.ts +8 -36
- package/src/modes/acp/permission-mode.ts +25 -0
- package/src/modes/acp/startup-options.ts +6 -0
- package/src/modes/components/agent-dashboard.ts +37 -17
- package/src/modes/components/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1379 -0
- package/src/modes/components/settings-selector.ts +60 -11
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/event-controller.ts +11 -16
- package/src/modes/controllers/extension-ui-controller.ts +348 -4
- package/src/modes/controllers/selector-controller.ts +674 -36
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +59 -20
- package/src/modes/runtime-init.ts +298 -6
- package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
- package/src/modes/shared/agent-wire/event-contract.ts +7 -8
- package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
- package/src/modes/shared/agent-wire/event-observation.ts +16 -7
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
- package/src/modes/types.ts +5 -0
- package/src/runtime-mcp/client.ts +94 -19
- package/src/runtime-mcp/config.ts +27 -15
- package/src/runtime-mcp/manager.ts +343 -105
- package/src/runtime-mcp/oauth-flow.ts +42 -12
- package/src/runtime-mcp/transports/http.ts +49 -19
- package/src/runtime-mcp/transports/stdio.ts +47 -28
- package/src/runtime-mcp/types.ts +10 -0
- package/src/sdk/acp/adapter.ts +418 -0
- package/src/sdk/acp/index.ts +2 -0
- package/src/sdk/broker/broker.ts +642 -0
- package/src/sdk/broker/discovery.ts +94 -0
- package/src/sdk/broker/ensure.ts +249 -0
- package/src/sdk/broker/identity.ts +42 -0
- package/src/sdk/broker/index.ts +8 -0
- package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
- package/src/sdk/broker/internal-source.bunfig.toml +1 -0
- package/src/sdk/broker/lifecycle-ledger.ts +286 -0
- package/src/sdk/broker/lifecycle.ts +2157 -0
- package/src/sdk/broker/process-incarnation.ts +159 -0
- package/src/sdk/broker/runtime.ts +164 -0
- package/src/sdk/broker/session-index.ts +318 -0
- package/src/sdk/broker/state-version.ts +23 -0
- package/src/sdk/broker/transport.ts +125 -0
- package/src/sdk/bus/chat-command-policy.ts +107 -0
- package/src/sdk/bus/chat-daemon-cli.ts +214 -0
- package/src/sdk/bus/chat-daemon-control.ts +713 -0
- package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
- package/src/sdk/bus/chat-effect-journal.ts +353 -0
- package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
- package/src/sdk/bus/config.ts +433 -0
- package/src/sdk/bus/conversation-store.ts +391 -0
- package/src/sdk/bus/discord-conversation.ts +85 -0
- package/src/sdk/bus/discord-daemon.ts +2275 -0
- package/src/sdk/bus/discord-live-provider.ts +521 -0
- package/src/sdk/bus/discord-provider.ts +56 -0
- package/src/{notifications → sdk/bus}/html-format.ts +2 -2
- package/src/sdk/bus/index.ts +3943 -0
- package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
- package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
- package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
- package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
- package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
- package/src/sdk/bus/natives-augment.d.ts +38 -0
- package/src/sdk/bus/notification-orchestration.ts +535 -0
- package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
- package/src/sdk/bus/session-control.ts +333 -0
- package/src/sdk/bus/slack-conversation.ts +74 -0
- package/src/sdk/bus/slack-daemon.ts +2144 -0
- package/src/sdk/bus/slack-live-provider.ts +380 -0
- package/src/sdk/bus/slack-provider.ts +76 -0
- package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
- package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
- package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
- package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
- package/src/sdk/bus/telegram-setup.ts +663 -0
- package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/session-cli.ts +275 -0
- package/src/sdk/client/client.ts +1 -0
- package/src/sdk/client/discovery.ts +108 -0
- package/src/sdk/client/index.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +367 -0
- package/src/sdk/host/control/index.ts +10 -0
- package/src/sdk/host/control/operations.ts +63 -0
- package/src/sdk/host/events.ts +91 -0
- package/src/sdk/host/host.ts +296 -0
- package/src/sdk/host/index.ts +4 -0
- package/src/sdk/host/query/cursor.ts +189 -0
- package/src/sdk/host/query/handlers.ts +533 -0
- package/src/sdk/host/query/index.ts +3 -0
- package/src/sdk/host/query/revision-store.ts +1126 -0
- package/src/sdk/host/reverse-leases.ts +325 -0
- package/src/sdk/host/types.ts +29 -0
- package/src/sdk/index.ts +13 -0
- package/src/sdk/lifecycle-session.ts +39 -0
- package/src/sdk/mcp/index.ts +1 -0
- package/src/sdk/mcp/server.ts +332 -0
- package/src/sdk/models.ts +168 -0
- package/src/sdk/protocol/adapter-validation.ts +48 -0
- package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
- package/src/sdk/protocol/operation-registry.ts +312 -0
- package/src/{sdk.ts → sdk/session.ts} +339 -212
- package/src/sdk/startup-capability.ts +189 -0
- package/src/session/agent-session.ts +2453 -894
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +136 -0
- package/src/session/session-manager.ts +438 -11
- package/src/session/session-storage.ts +8 -1
- package/src/setup/credential-auto-import.ts +290 -35
- package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/task/executor.ts +62 -13
- package/src/tools/ask-answer-registry.ts +1 -1
- package/src/tools/ask.ts +13 -8
- package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
- package/src/tools/browser/benchmark/detector-report.ts +157 -0
- package/src/tools/browser/benchmark/run-suite.ts +39 -0
- package/src/tools/browser/launch.ts +124 -19
- package/src/tools/browser/profile-discovery.ts +63 -0
- package/src/tools/browser/profile-posture.ts +59 -0
- package/src/tools/browser/profile-reuse.ts +102 -0
- package/src/tools/browser/profile-warmup.ts +109 -0
- package/src/tools/browser/registry.ts +87 -11
- package/src/tools/browser/tab-protocol.ts +1 -0
- package/src/tools/browser/tab-supervisor.ts +1 -0
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +6 -0
- package/src/tools/index.ts +8 -8
- package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
- package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
- package/src/tools/telegram-send.ts +2 -2
- package/vendor/insane-search/MANIFEST.json +2 -1
- package/vendor/insane-search/engine/bias_check.py +4 -1
- package/vendor/insane-search/engine/phase0.py +26 -1
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
- package/dist/types/modes/bridge/auth.d.ts +0 -12
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
- package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
- package/dist/types/modes/bridge/event-stream.d.ts +0 -8
- package/dist/types/modes/rpc/host-tools.d.ts +0 -1
- package/dist/types/modes/rpc/host-uris.d.ts +0 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
- package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
- package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
- package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
- package/dist/types/notifications/config.d.ts +0 -132
- package/src/harness-control-plane/rpc-adapter.ts +0 -290
- package/src/modes/bridge/auth.ts +0 -41
- package/src/modes/bridge/bridge-client-bridge.ts +0 -47
- package/src/modes/bridge/bridge-mode.ts +0 -675
- package/src/modes/bridge/bridge-ui-context.ts +0 -200
- package/src/modes/bridge/event-stream.ts +0 -70
- package/src/modes/rpc/host-tools.ts +0 -1
- package/src/modes/rpc/host-uris.ts +0 -1
- package/src/modes/rpc/rpc-client.ts +0 -1166
- package/src/modes/rpc/rpc-mode.ts +0 -1029
- package/src/modes/rpc/rpc-socket-security.ts +0 -115
- package/src/modes/rpc/rpc-types.ts +0 -637
- package/src/modes/shared/agent-wire/command-contract.ts +0 -18
- package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
- package/src/modes/shared/agent-wire/command-validation.ts +0 -181
- package/src/modes/shared/agent-wire/handshake.ts +0 -157
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
- package/src/modes/shared/agent-wire/protocol.ts +0 -46
- package/src/modes/shared/agent-wire/responses.ts +0 -17
- package/src/modes/shared/agent-wire/scopes.ts +0 -94
- package/src/modes/shared/agent-wire/session-registry.ts +0 -109
- package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
- package/src/modes/shared/agent-wire/ui-result.ts +0 -48
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
- package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
- package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
- package/src/notifications/config.ts +0 -219
- package/src/notifications/index.ts +0 -1835
- package/src/notifications/telegram-daemon-cli.ts +0 -243
- package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
- /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
- /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
- /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
- /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
- /package/src/{notifications → sdk/bus}/engine.ts +0 -0
- /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
- /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
- /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
- /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
- /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram **reference** client for the
|
|
2
|
+
* Telegram **reference** client for the Gajae-Code SDK.
|
|
3
3
|
*
|
|
4
4
|
* This is an example/template, NOT an upstream-owned integration: it implements
|
|
5
|
-
* the documented WS protocol (see `docs/
|
|
5
|
+
* the documented WS protocol (see `docs/sdk.md`) so you can copy it
|
|
6
6
|
* to build Discord/Slack/etc. clients with zero upstream changes. The Bot API
|
|
7
7
|
* transport shape is salvaged from the removed `telegram-remote` package.
|
|
8
8
|
*
|
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, side-effect-free Telegram pairing helpers.
|
|
3
|
+
*
|
|
4
|
+
* This module owns Bot API validation and the single-poller setup policy. It
|
|
5
|
+
* never writes settings or renders output: callers provide UI callbacks and
|
|
6
|
+
* persist a successful result only after the returned result is complete.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "node:fs/promises";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { withFileLock } from "../../config/file-lock";
|
|
12
|
+
|
|
13
|
+
import { tokenFingerprint } from "./config";
|
|
14
|
+
import { sanitizeDiagnostic } from "./notification-service";
|
|
15
|
+
|
|
16
|
+
export const DEFAULT_TELEGRAM_SETUP_API_BASE = "https://api.telegram.org";
|
|
17
|
+
export const DEFAULT_TELEGRAM_SETUP_POLL_TIMEOUT_MS = 60_000;
|
|
18
|
+
export const DEFAULT_TELEGRAM_SETUP_POLL_INTERVAL_MS = 1_000;
|
|
19
|
+
|
|
20
|
+
export type TelegramThreadedModeState = "enabled" | "disabled" | "unknown";
|
|
21
|
+
export type TelegramThreadedModeLabel = "verified" | "unverified" | "unknown";
|
|
22
|
+
export type TelegramSetupPairingSource = "discovered" | "provided" | "reused";
|
|
23
|
+
export type TelegramSetupFailureStatus =
|
|
24
|
+
| "error"
|
|
25
|
+
| "cancelled"
|
|
26
|
+
| "requires_explicit_chat"
|
|
27
|
+
| "ownership_lost"
|
|
28
|
+
| "aborted";
|
|
29
|
+
|
|
30
|
+
export interface TelegramSetupTimers {
|
|
31
|
+
now(): number;
|
|
32
|
+
sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TelegramSetupDeps {
|
|
36
|
+
fetchImpl: typeof fetch;
|
|
37
|
+
apiBase?: string;
|
|
38
|
+
timers?: TelegramSetupTimers;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Current daemon ownership facts collected by the caller before setup starts. */
|
|
42
|
+
export interface TelegramSetupPreflight {
|
|
43
|
+
storedChatId?: string;
|
|
44
|
+
daemon?: {
|
|
45
|
+
live: boolean;
|
|
46
|
+
tokenFingerprint?: string;
|
|
47
|
+
/** Required with a live daemon so reuse verifies the complete token/chat identity. */
|
|
48
|
+
chatId?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type TelegramSetupPollingPolicy =
|
|
53
|
+
| { kind: "discover" }
|
|
54
|
+
| { kind: "reuse_stored_chat"; chatId: string }
|
|
55
|
+
| { kind: "validate_explicit_chat"; chatId: string }
|
|
56
|
+
| { kind: "requires_explicit_chat" }
|
|
57
|
+
| { kind: "cancel_foreign_or_unknown_owner"; identity: "foreign" | "unknown" };
|
|
58
|
+
|
|
59
|
+
export type TelegramSetupEvent =
|
|
60
|
+
| { kind: "token_validated"; message: string }
|
|
61
|
+
| { kind: "threaded_mode_guidance"; message: string }
|
|
62
|
+
| { kind: "rejected_chat"; message: string };
|
|
63
|
+
|
|
64
|
+
export interface TelegramSetupSuccess {
|
|
65
|
+
ok: true;
|
|
66
|
+
chatId: string;
|
|
67
|
+
tokenFingerprint: string;
|
|
68
|
+
threadedMode: TelegramThreadedModeState;
|
|
69
|
+
threadedLabel: TelegramThreadedModeLabel;
|
|
70
|
+
pairingSource: TelegramSetupPairingSource;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface TelegramSetupFailure {
|
|
74
|
+
ok: false;
|
|
75
|
+
status: TelegramSetupFailureStatus;
|
|
76
|
+
detail: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type TelegramSetupResult = TelegramSetupSuccess | TelegramSetupFailure;
|
|
80
|
+
|
|
81
|
+
export interface TelegramBot {
|
|
82
|
+
id: number;
|
|
83
|
+
isBot?: boolean;
|
|
84
|
+
firstName?: string;
|
|
85
|
+
username?: string;
|
|
86
|
+
hasTopicsEnabled?: boolean;
|
|
87
|
+
allowsUsersToCreateTopics?: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type TelegramTokenValidationResult =
|
|
91
|
+
| {
|
|
92
|
+
ok: true;
|
|
93
|
+
bot: TelegramBot;
|
|
94
|
+
threadedMode: TelegramThreadedModeState;
|
|
95
|
+
}
|
|
96
|
+
| TelegramSetupFailure;
|
|
97
|
+
|
|
98
|
+
export type TelegramPrivateChatValidationResult =
|
|
99
|
+
| {
|
|
100
|
+
ok: true;
|
|
101
|
+
chatId: string;
|
|
102
|
+
}
|
|
103
|
+
| TelegramSetupFailure;
|
|
104
|
+
|
|
105
|
+
export interface RunTelegramSetupInput {
|
|
106
|
+
token: string;
|
|
107
|
+
preflight: TelegramSetupPreflight;
|
|
108
|
+
/**
|
|
109
|
+
* Re-reads daemon ownership immediately before any `getUpdates` call. It is
|
|
110
|
+
* mandatory for discovery because an earlier preflight cannot own Telegram's
|
|
111
|
+
* single-poller slot.
|
|
112
|
+
*/
|
|
113
|
+
revalidatePreflight?: (signal?: AbortSignal) => Promise<TelegramSetupPreflight>;
|
|
114
|
+
chatId?: string;
|
|
115
|
+
interactive: boolean;
|
|
116
|
+
threadedModePrompt?: (message: string) => Promise<string>;
|
|
117
|
+
pollTimeoutMs?: number;
|
|
118
|
+
pollIntervalMs?: number;
|
|
119
|
+
signal?: AbortSignal;
|
|
120
|
+
onEvent?: (event: TelegramSetupEvent) => void;
|
|
121
|
+
deps: TelegramSetupDeps;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface TelegramApiResponse<T> {
|
|
125
|
+
ok: boolean;
|
|
126
|
+
result?: T;
|
|
127
|
+
description?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface TelegramApiUser {
|
|
131
|
+
id?: unknown;
|
|
132
|
+
is_bot?: unknown;
|
|
133
|
+
first_name?: unknown;
|
|
134
|
+
username?: unknown;
|
|
135
|
+
has_topics_enabled?: unknown;
|
|
136
|
+
allows_users_to_create_topics?: unknown;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface TelegramApiChat {
|
|
140
|
+
id?: unknown;
|
|
141
|
+
type?: unknown;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface TelegramUpdate {
|
|
145
|
+
update_id?: unknown;
|
|
146
|
+
message?: {
|
|
147
|
+
chat?: {
|
|
148
|
+
id?: unknown;
|
|
149
|
+
type?: unknown;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const THREADED_ENABLED_SUCCESS =
|
|
155
|
+
"Telegram Threaded Mode capability verified for this bot. GJC will request a private-chat topic per session; if Telegram ever refuses topic creation, notifications fall back to this flat chat with inline ask buttons only and a one-time Threaded Mode nudge.\n";
|
|
156
|
+
const THREADED_MISSING_WARNING =
|
|
157
|
+
"Warning: Telegram getMe did not include has_topics_enabled, so GJC cannot verify private-chat Threaded Mode capability for this bot. Setup will continue; flat private-chat fallback supports outbound notifications and inline ask buttons only. Free-text replies and session commands require Threaded Mode/topic routing.\n";
|
|
158
|
+
const THREADED_NONINTERACTIVE_WARNING =
|
|
159
|
+
"Warning: Telegram Threaded Mode capability is OFF for this bot. Setup will be saved because this run is non-interactive. Flat private-chat fallback supports outbound notifications and inline ask buttons only; free-text replies and session commands require enabling Threaded Mode in @BotFather > Bot Settings > Threads Settings.\n";
|
|
160
|
+
const THREADED_DISABLED_GUIDANCE =
|
|
161
|
+
"Telegram Threaded Mode is OFF for this bot. GJC needs Telegram private-chat topics so each session can use its own thread.\n" +
|
|
162
|
+
"GJC cannot enable this through the Bot API. Open @BotFather > Bot Settings > Threads Settings for this bot, enable Threaded Mode / forum topics for private chats, then return here.\n" +
|
|
163
|
+
"Without Threaded Mode, flat private-chat fallback supports outbound notifications and inline ask buttons only; free-text replies and session commands require topic routing.\n";
|
|
164
|
+
const THREADED_DISABLED_PROMPT =
|
|
165
|
+
"Press Enter after enabling Threaded Mode, or type skip to finish setup with a warning: ";
|
|
166
|
+
const THREADED_STILL_OFF = "Telegram still reports Threaded Mode OFF for this bot.\n";
|
|
167
|
+
const THREADED_RETRY_PROMPT = "Press Enter to check again, or type skip to finish setup with a warning: ";
|
|
168
|
+
const THREADED_SKIP_WARNING =
|
|
169
|
+
"Warning: continuing without verified Telegram Threaded Mode capability. Setup will be saved. Flat private-chat fallback supports outbound notifications and inline ask buttons only; free-text replies and session commands require enabling Threaded Mode in BotFather.\n";
|
|
170
|
+
const THREADED_INVALID_INPUT = "Type Enter to retry or skip to continue with a warning.\n";
|
|
171
|
+
const THREADED_RETRY_INPUTS = new Set(["", "y", "yes", "r", "retry"]);
|
|
172
|
+
const THREADED_SKIP_INPUTS = new Set(["s", "skip", "n", "no"]);
|
|
173
|
+
|
|
174
|
+
function defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
175
|
+
if (signal?.aborted) return Promise.reject(abortError());
|
|
176
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
177
|
+
const timer = setTimeout(() => {
|
|
178
|
+
cleanup();
|
|
179
|
+
resolve();
|
|
180
|
+
}, ms);
|
|
181
|
+
const onAbort = () => {
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
cleanup();
|
|
184
|
+
reject(abortError());
|
|
185
|
+
};
|
|
186
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
187
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
188
|
+
if (signal?.aborted) onAbort();
|
|
189
|
+
return promise;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const defaultTimers: TelegramSetupTimers = {
|
|
193
|
+
now: () => Date.now(),
|
|
194
|
+
sleep: defaultSleep,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
function abortError(): Error {
|
|
198
|
+
const error = new Error("Telegram setup cancelled.");
|
|
199
|
+
error.name = "AbortError";
|
|
200
|
+
return error;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isAbortError(error: unknown): boolean {
|
|
204
|
+
return error instanceof Error && (error.name === "AbortError" || /\baborted\b|\bcancelled\b/i.test(error.message));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function assertNotAborted(signal: AbortSignal | undefined): void {
|
|
208
|
+
if (signal?.aborted) throw abortError();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function failure(token: string, status: TelegramSetupFailureStatus, detail: string): TelegramSetupFailure {
|
|
212
|
+
return { ok: false, status, detail: sanitizeDiagnostic(detail, token) };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function apiBase(deps: TelegramSetupDeps): string {
|
|
216
|
+
return (deps.apiBase ?? DEFAULT_TELEGRAM_SETUP_API_BASE).replace(/\/$/, "");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function timers(deps: TelegramSetupDeps): TelegramSetupTimers {
|
|
220
|
+
return deps.timers ?? defaultTimers;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function toBot(value: unknown): TelegramBot | undefined {
|
|
224
|
+
if (!value || typeof value !== "object") return undefined;
|
|
225
|
+
const user = value as TelegramApiUser;
|
|
226
|
+
if (typeof user.id !== "number" || !Number.isSafeInteger(user.id)) return undefined;
|
|
227
|
+
return {
|
|
228
|
+
id: user.id,
|
|
229
|
+
isBot: typeof user.is_bot === "boolean" ? user.is_bot : undefined,
|
|
230
|
+
firstName: typeof user.first_name === "string" ? user.first_name : undefined,
|
|
231
|
+
username: typeof user.username === "string" ? user.username : undefined,
|
|
232
|
+
hasTopicsEnabled: typeof user.has_topics_enabled === "boolean" ? user.has_topics_enabled : undefined,
|
|
233
|
+
allowsUsersToCreateTopics:
|
|
234
|
+
typeof user.allows_users_to_create_topics === "boolean" ? user.allows_users_to_create_topics : undefined,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function withTelegramSetupLease<T>(token: string, operation: () => Promise<T>): Promise<T> {
|
|
239
|
+
const dir = path.join(os.tmpdir(), "gjc-telegram-setup");
|
|
240
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
241
|
+
return await withFileLock(path.join(dir, tokenFingerprint(token.trim())), operation);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function threadedMode(bot: TelegramBot): TelegramThreadedModeState {
|
|
245
|
+
if (bot.hasTopicsEnabled === true) return "enabled";
|
|
246
|
+
if (bot.hasTopicsEnabled === false) return "disabled";
|
|
247
|
+
return "unknown";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function threadedLabel(state: TelegramThreadedModeState): TelegramThreadedModeLabel {
|
|
251
|
+
if (state === "enabled") return "verified";
|
|
252
|
+
if (state === "disabled") return "unverified";
|
|
253
|
+
return "unknown";
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function callTelegram<T>(input: {
|
|
257
|
+
deps: TelegramSetupDeps;
|
|
258
|
+
token: string;
|
|
259
|
+
method: string;
|
|
260
|
+
body: Record<string, unknown>;
|
|
261
|
+
signal?: AbortSignal;
|
|
262
|
+
}): Promise<T> {
|
|
263
|
+
assertNotAborted(input.signal);
|
|
264
|
+
const response = await input.deps.fetchImpl(`${apiBase(input.deps)}/bot${input.token}/${input.method}`, {
|
|
265
|
+
method: "POST",
|
|
266
|
+
headers: { "content-type": "application/json" },
|
|
267
|
+
body: JSON.stringify(input.body),
|
|
268
|
+
signal: input.signal,
|
|
269
|
+
});
|
|
270
|
+
let payload: TelegramApiResponse<T>;
|
|
271
|
+
try {
|
|
272
|
+
payload = (await response.json()) as TelegramApiResponse<T>;
|
|
273
|
+
} catch {
|
|
274
|
+
throw new Error(`Telegram ${input.method} returned invalid JSON.`);
|
|
275
|
+
}
|
|
276
|
+
if (!payload || typeof payload !== "object") {
|
|
277
|
+
throw new Error(`Telegram ${input.method} returned invalid JSON.`);
|
|
278
|
+
}
|
|
279
|
+
if (!response.ok || payload.ok !== true) {
|
|
280
|
+
throw new Error(`Telegram ${input.method} failed: ${payload.description ?? response.statusText}`);
|
|
281
|
+
}
|
|
282
|
+
return payload.result as T;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Validate a token with getMe without exposing it in diagnostics or results. */
|
|
286
|
+
export async function validateTelegramBotToken(input: {
|
|
287
|
+
token: string;
|
|
288
|
+
deps: TelegramSetupDeps;
|
|
289
|
+
signal?: AbortSignal;
|
|
290
|
+
}): Promise<TelegramTokenValidationResult> {
|
|
291
|
+
const token = input.token.trim();
|
|
292
|
+
if (!token) return failure(input.token, "error", "Telegram bot token is required.");
|
|
293
|
+
try {
|
|
294
|
+
const bot = toBot(await callTelegram<unknown>({ ...input, token, method: "getMe", body: {} }));
|
|
295
|
+
if (!bot) {
|
|
296
|
+
return failure(
|
|
297
|
+
token,
|
|
298
|
+
"error",
|
|
299
|
+
"Telegram getMe returned invalid Telegram response: missing valid User result.",
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return { ok: true, bot, threadedMode: threadedMode(bot) };
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (isAbortError(error)) return failure(token, "aborted", "Telegram setup cancelled.");
|
|
305
|
+
return failure(token, "error", error instanceof Error ? error.message : "Telegram getMe failed.");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Validate that a supplied or stored chat is an actual Telegram private chat. */
|
|
310
|
+
export async function validateTelegramPrivateChat(input: {
|
|
311
|
+
token: string;
|
|
312
|
+
chatId: string;
|
|
313
|
+
deps: TelegramSetupDeps;
|
|
314
|
+
signal?: AbortSignal;
|
|
315
|
+
}): Promise<TelegramPrivateChatValidationResult> {
|
|
316
|
+
const token = input.token.trim();
|
|
317
|
+
const requestedChatId = input.chatId.trim();
|
|
318
|
+
if (!requestedChatId) return failure(token, "error", "Telegram private chat id is required.");
|
|
319
|
+
try {
|
|
320
|
+
const chat = (await callTelegram<unknown>({
|
|
321
|
+
deps: input.deps,
|
|
322
|
+
token,
|
|
323
|
+
method: "getChat",
|
|
324
|
+
body: { chat_id: requestedChatId },
|
|
325
|
+
signal: input.signal,
|
|
326
|
+
})) as TelegramApiChat;
|
|
327
|
+
if (!chat || typeof chat !== "object" || (typeof chat.id !== "number" && typeof chat.id !== "string")) {
|
|
328
|
+
return failure(
|
|
329
|
+
token,
|
|
330
|
+
"error",
|
|
331
|
+
"Telegram getChat returned invalid Telegram response: missing valid Chat result.",
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
if (chat.type !== "private") {
|
|
335
|
+
const type = typeof chat.type === "string" && chat.type ? chat.type : "unknown";
|
|
336
|
+
return failure(
|
|
337
|
+
token,
|
|
338
|
+
"error",
|
|
339
|
+
`Provided chat id ${requestedChatId} is a ${type} chat; pairing requires a private Telegram chat.`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
return { ok: true, chatId: String(chat.id) };
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (isAbortError(error)) return failure(token, "aborted", "Telegram setup cancelled.");
|
|
345
|
+
return failure(token, "error", error instanceof Error ? error.message : "Telegram getChat failed.");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Decide whether setup may call getUpdates. Any live daemon is a hard polling
|
|
351
|
+
* boundary: only a verified complete token/chat identity may reuse the stored
|
|
352
|
+
* chat; setup never polls while any live owner exists.
|
|
353
|
+
*/
|
|
354
|
+
export function resolveTelegramSetupPollingPolicy(input: {
|
|
355
|
+
token: string;
|
|
356
|
+
preflight: TelegramSetupPreflight;
|
|
357
|
+
chatId?: string;
|
|
358
|
+
}): TelegramSetupPollingPolicy {
|
|
359
|
+
const explicitChatId = input.chatId?.trim();
|
|
360
|
+
const storedChatId = input.preflight.storedChatId?.trim();
|
|
361
|
+
const daemon = input.preflight.daemon;
|
|
362
|
+
if (!daemon?.live) {
|
|
363
|
+
return explicitChatId ? { kind: "validate_explicit_chat", chatId: explicitChatId } : { kind: "discover" };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const fingerprint = daemon.tokenFingerprint?.trim();
|
|
367
|
+
const daemonChatId = daemon.chatId?.trim();
|
|
368
|
+
if (!fingerprint || !daemonChatId) return { kind: "cancel_foreign_or_unknown_owner", identity: "unknown" };
|
|
369
|
+
if (fingerprint !== tokenFingerprint(input.token.trim())) {
|
|
370
|
+
return { kind: "cancel_foreign_or_unknown_owner", identity: "foreign" };
|
|
371
|
+
}
|
|
372
|
+
if (storedChatId && daemonChatId !== storedChatId) {
|
|
373
|
+
return { kind: "cancel_foreign_or_unknown_owner", identity: "foreign" };
|
|
374
|
+
}
|
|
375
|
+
if (storedChatId && (!explicitChatId || explicitChatId === storedChatId)) {
|
|
376
|
+
return { kind: "reuse_stored_chat", chatId: storedChatId };
|
|
377
|
+
}
|
|
378
|
+
if (explicitChatId) return { kind: "validate_explicit_chat", chatId: explicitChatId };
|
|
379
|
+
return { kind: "requires_explicit_chat" };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function nextOffset(updates: TelegramUpdate[], fallback?: number): number | undefined {
|
|
383
|
+
let max = fallback === undefined ? undefined : fallback - 1;
|
|
384
|
+
for (const update of updates) {
|
|
385
|
+
if (typeof update.update_id === "number" && Number.isSafeInteger(update.update_id)) {
|
|
386
|
+
if (max === undefined || update.update_id > max) max = update.update_id;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return max === undefined ? fallback : max + 1;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function getUpdates(input: {
|
|
393
|
+
token: string;
|
|
394
|
+
deps: TelegramSetupDeps;
|
|
395
|
+
offset?: number;
|
|
396
|
+
signal?: AbortSignal;
|
|
397
|
+
}): Promise<TelegramUpdate[]> {
|
|
398
|
+
const result = await callTelegram<unknown>({
|
|
399
|
+
deps: input.deps,
|
|
400
|
+
token: input.token,
|
|
401
|
+
method: "getUpdates",
|
|
402
|
+
body: { offset: input.offset, timeout: 0, allowed_updates: ["message"] },
|
|
403
|
+
signal: input.signal,
|
|
404
|
+
});
|
|
405
|
+
if (!Array.isArray(result)) throw new Error("Telegram getUpdates returned invalid Telegram response.");
|
|
406
|
+
return result as TelegramUpdate[];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function discoverPrivateTelegramChat(input: {
|
|
410
|
+
token: string;
|
|
411
|
+
deps: TelegramSetupDeps;
|
|
412
|
+
pollTimeoutMs: number;
|
|
413
|
+
pollIntervalMs: number;
|
|
414
|
+
signal?: AbortSignal;
|
|
415
|
+
onRejectedChat?: (type: string) => void;
|
|
416
|
+
}): Promise<TelegramPrivateChatValidationResult> {
|
|
417
|
+
const token = input.token.trim();
|
|
418
|
+
try {
|
|
419
|
+
assertNotAborted(input.signal);
|
|
420
|
+
const stale = await getUpdates({ token, deps: input.deps, signal: input.signal });
|
|
421
|
+
let offset = nextOffset(stale);
|
|
422
|
+
const timeoutMs = Math.max(0, input.pollTimeoutMs);
|
|
423
|
+
const intervalMs = Math.max(0, input.pollIntervalMs);
|
|
424
|
+
const clock = timers(input.deps);
|
|
425
|
+
const deadline = clock.now() + timeoutMs;
|
|
426
|
+
const maxAttempts = Math.max(1, Math.ceil(timeoutMs / Math.max(intervalMs, 1)) + 1);
|
|
427
|
+
let sawRejectedChatType: string | undefined;
|
|
428
|
+
|
|
429
|
+
for (let attempt = 0; attempt < maxAttempts && clock.now() <= deadline; attempt++) {
|
|
430
|
+
assertNotAborted(input.signal);
|
|
431
|
+
const updates = await getUpdates({ token, deps: input.deps, offset, signal: input.signal });
|
|
432
|
+
offset = nextOffset(updates, offset);
|
|
433
|
+
for (const update of updates) {
|
|
434
|
+
const chat = update.message?.chat;
|
|
435
|
+
if (!chat) continue;
|
|
436
|
+
if (chat.type === "private" && (typeof chat.id === "number" || typeof chat.id === "string")) {
|
|
437
|
+
return { ok: true, chatId: String(chat.id) };
|
|
438
|
+
}
|
|
439
|
+
if (chat.type === "group" || chat.type === "supergroup" || chat.type === "channel") {
|
|
440
|
+
sawRejectedChatType = chat.type;
|
|
441
|
+
input.onRejectedChat?.(chat.type);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (attempt + 1 >= maxAttempts) break;
|
|
445
|
+
const remaining = deadline - clock.now();
|
|
446
|
+
if (remaining <= 0) break;
|
|
447
|
+
await clock.sleep(Math.min(intervalMs, remaining), input.signal);
|
|
448
|
+
}
|
|
449
|
+
if (sawRejectedChatType) {
|
|
450
|
+
return failure(
|
|
451
|
+
token,
|
|
452
|
+
"error",
|
|
453
|
+
`Pairing rejected ${sawRejectedChatType} chat; message the bot from a private chat.`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
return failure(token, "error", "Timed out waiting for a private Telegram message to pair notifications.");
|
|
457
|
+
} catch (error) {
|
|
458
|
+
if (isAbortError(error)) return failure(token, "aborted", "Telegram setup cancelled.");
|
|
459
|
+
if (error instanceof Error && /\b409\b|\bconflict\b/i.test(error.message)) {
|
|
460
|
+
return failure(
|
|
461
|
+
token,
|
|
462
|
+
"ownership_lost",
|
|
463
|
+
"Telegram setup stopped because another poller owns this bot. No configuration was saved.",
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return failure(token, "error", error instanceof Error ? error.message : "Telegram getUpdates failed.");
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function verifyThreadedMode(input: {
|
|
471
|
+
token: string;
|
|
472
|
+
bot: TelegramBot;
|
|
473
|
+
interactive: boolean;
|
|
474
|
+
prompt?: (message: string) => Promise<string>;
|
|
475
|
+
deps: TelegramSetupDeps;
|
|
476
|
+
signal?: AbortSignal;
|
|
477
|
+
onEvent?: (event: TelegramSetupEvent) => void;
|
|
478
|
+
}): Promise<TelegramThreadedModeState | TelegramSetupFailure> {
|
|
479
|
+
const emit = (message: string) => input.onEvent?.({ kind: "threaded_mode_guidance", message });
|
|
480
|
+
let state = threadedMode(input.bot);
|
|
481
|
+
if (state === "enabled") {
|
|
482
|
+
emit(THREADED_ENABLED_SUCCESS);
|
|
483
|
+
return state;
|
|
484
|
+
}
|
|
485
|
+
if (state === "unknown") {
|
|
486
|
+
emit(THREADED_MISSING_WARNING);
|
|
487
|
+
return state;
|
|
488
|
+
}
|
|
489
|
+
if (!input.interactive || !input.prompt) {
|
|
490
|
+
emit(THREADED_NONINTERACTIVE_WARNING);
|
|
491
|
+
return state;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
emit(THREADED_DISABLED_GUIDANCE);
|
|
495
|
+
let firstPrompt = true;
|
|
496
|
+
try {
|
|
497
|
+
for (;;) {
|
|
498
|
+
assertNotAborted(input.signal);
|
|
499
|
+
const answer = (await input.prompt(firstPrompt ? THREADED_DISABLED_PROMPT : THREADED_RETRY_PROMPT))
|
|
500
|
+
.trim()
|
|
501
|
+
.toLowerCase();
|
|
502
|
+
firstPrompt = false;
|
|
503
|
+
if (THREADED_SKIP_INPUTS.has(answer)) {
|
|
504
|
+
emit(THREADED_SKIP_WARNING);
|
|
505
|
+
return "disabled";
|
|
506
|
+
}
|
|
507
|
+
if (!THREADED_RETRY_INPUTS.has(answer)) {
|
|
508
|
+
emit(THREADED_INVALID_INPUT);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
const validation = await validateTelegramBotToken({
|
|
512
|
+
token: input.token,
|
|
513
|
+
deps: input.deps,
|
|
514
|
+
signal: input.signal,
|
|
515
|
+
});
|
|
516
|
+
if (!validation.ok) return validation;
|
|
517
|
+
state = validation.threadedMode;
|
|
518
|
+
if (state === "enabled") {
|
|
519
|
+
emit(THREADED_ENABLED_SUCCESS);
|
|
520
|
+
return state;
|
|
521
|
+
}
|
|
522
|
+
if (state === "unknown") {
|
|
523
|
+
emit(THREADED_MISSING_WARNING);
|
|
524
|
+
return state;
|
|
525
|
+
}
|
|
526
|
+
emit(THREADED_STILL_OFF);
|
|
527
|
+
}
|
|
528
|
+
} catch (error) {
|
|
529
|
+
if (isAbortError(error)) return failure(input.token, "aborted", "Telegram setup cancelled.");
|
|
530
|
+
return failure(
|
|
531
|
+
input.token,
|
|
532
|
+
"error",
|
|
533
|
+
error instanceof Error ? error.message : "Telegram Threaded Mode verification failed.",
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Validate a token, assess Threaded Mode, and obtain a private chat according
|
|
540
|
+
* to the daemon poller-contention policy. No settings are written here.
|
|
541
|
+
*/
|
|
542
|
+
export async function runTelegramSetup(input: RunTelegramSetupInput): Promise<TelegramSetupResult> {
|
|
543
|
+
const token = input.token.trim();
|
|
544
|
+
const tokenValidation = await validateTelegramBotToken({ token, deps: input.deps, signal: input.signal });
|
|
545
|
+
if (!tokenValidation.ok) return tokenValidation;
|
|
546
|
+
input.onEvent?.({
|
|
547
|
+
kind: "token_validated",
|
|
548
|
+
message: "Token validated. Message your bot now from the private Telegram chat to pair notifications.\n",
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
const threadedMode = await verifyThreadedMode({
|
|
552
|
+
token,
|
|
553
|
+
bot: tokenValidation.bot,
|
|
554
|
+
interactive: input.interactive,
|
|
555
|
+
prompt: input.threadedModePrompt,
|
|
556
|
+
deps: input.deps,
|
|
557
|
+
signal: input.signal,
|
|
558
|
+
onEvent: input.onEvent,
|
|
559
|
+
});
|
|
560
|
+
if (typeof threadedMode !== "string") return threadedMode;
|
|
561
|
+
|
|
562
|
+
let policy = resolveTelegramSetupPollingPolicy({ token, preflight: input.preflight, chatId: input.chatId });
|
|
563
|
+
if (policy.kind === "discover") {
|
|
564
|
+
if (!input.revalidatePreflight) {
|
|
565
|
+
return failure(
|
|
566
|
+
token,
|
|
567
|
+
"ownership_lost",
|
|
568
|
+
"Telegram setup cannot safely start pairing because daemon ownership was not revalidated. No configuration was saved.",
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
assertNotAborted(input.signal);
|
|
573
|
+
const preflight = await input.revalidatePreflight(input.signal);
|
|
574
|
+
policy = resolveTelegramSetupPollingPolicy({ token, preflight, chatId: input.chatId });
|
|
575
|
+
} catch (error) {
|
|
576
|
+
if (isAbortError(error)) return failure(token, "aborted", "Telegram setup cancelled.");
|
|
577
|
+
return failure(
|
|
578
|
+
token,
|
|
579
|
+
"ownership_lost",
|
|
580
|
+
"Telegram setup could not revalidate daemon ownership before pairing. No configuration was saved.",
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (policy.kind === "cancel_foreign_or_unknown_owner") {
|
|
585
|
+
return failure(
|
|
586
|
+
token,
|
|
587
|
+
"cancelled",
|
|
588
|
+
`Telegram setup cancelled: a live daemon has a ${policy.identity} identity. Stop or reconfigure that daemon before pairing this bot.`,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (policy.kind === "requires_explicit_chat") {
|
|
592
|
+
return failure(
|
|
593
|
+
token,
|
|
594
|
+
"requires_explicit_chat",
|
|
595
|
+
"A live daemon already owns this bot token. Supply an explicit private --chat-id to reconfigure without polling.",
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (policy.kind === "discover") {
|
|
600
|
+
const revalidate = input.revalidatePreflight;
|
|
601
|
+
if (!revalidate)
|
|
602
|
+
return failure(
|
|
603
|
+
token,
|
|
604
|
+
"ownership_lost",
|
|
605
|
+
"Telegram setup cannot safely start pairing because daemon ownership was not revalidated. No configuration was saved.",
|
|
606
|
+
);
|
|
607
|
+
const chat = await withTelegramSetupLease(token, async () => {
|
|
608
|
+
try {
|
|
609
|
+
const preflight = await revalidate(input.signal);
|
|
610
|
+
if (resolveTelegramSetupPollingPolicy({ token, preflight, chatId: input.chatId }).kind !== "discover")
|
|
611
|
+
return failure(
|
|
612
|
+
token,
|
|
613
|
+
"ownership_lost",
|
|
614
|
+
"Telegram setup stopped because daemon ownership changed before pairing. No configuration was saved.",
|
|
615
|
+
);
|
|
616
|
+
return await discoverPrivateTelegramChat({
|
|
617
|
+
token,
|
|
618
|
+
deps: input.deps,
|
|
619
|
+
pollTimeoutMs: input.pollTimeoutMs ?? DEFAULT_TELEGRAM_SETUP_POLL_TIMEOUT_MS,
|
|
620
|
+
pollIntervalMs: input.pollIntervalMs ?? DEFAULT_TELEGRAM_SETUP_POLL_INTERVAL_MS,
|
|
621
|
+
signal: input.signal,
|
|
622
|
+
onRejectedChat: type =>
|
|
623
|
+
input.onEvent?.({
|
|
624
|
+
kind: "rejected_chat",
|
|
625
|
+
message: `Rejected ${type} chat. Pairing requires a private Telegram chat with the bot.\n`,
|
|
626
|
+
}),
|
|
627
|
+
});
|
|
628
|
+
} catch (error) {
|
|
629
|
+
return isAbortError(error)
|
|
630
|
+
? failure(token, "aborted", "Telegram setup cancelled.")
|
|
631
|
+
: failure(
|
|
632
|
+
token,
|
|
633
|
+
"ownership_lost",
|
|
634
|
+
"Telegram setup could not revalidate daemon ownership before pairing. No configuration was saved.",
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
if (!chat.ok) return chat;
|
|
639
|
+
return {
|
|
640
|
+
ok: true,
|
|
641
|
+
chatId: chat.chatId,
|
|
642
|
+
tokenFingerprint: tokenFingerprint(token),
|
|
643
|
+
threadedMode,
|
|
644
|
+
threadedLabel: threadedLabel(threadedMode),
|
|
645
|
+
pairingSource: "discovered",
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const chat = await validateTelegramPrivateChat({
|
|
649
|
+
token,
|
|
650
|
+
chatId: policy.chatId,
|
|
651
|
+
deps: input.deps,
|
|
652
|
+
signal: input.signal,
|
|
653
|
+
});
|
|
654
|
+
if (!chat.ok) return chat;
|
|
655
|
+
return {
|
|
656
|
+
ok: true,
|
|
657
|
+
chatId: chat.chatId,
|
|
658
|
+
tokenFingerprint: tokenFingerprint(token),
|
|
659
|
+
threadedMode,
|
|
660
|
+
threadedLabel: threadedLabel(threadedMode),
|
|
661
|
+
pairingSource: policy.kind === "reuse_stored_chat" ? "reused" : "provided",
|
|
662
|
+
};
|
|
663
|
+
}
|