@gajae-code/coding-agent 0.13.3 → 0.14.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 +184 -2
- package/README.md +2 -0
- package/bin/gajaessi.js +4 -0
- package/dist/types/async/job-manager.d.ts +55 -2
- package/dist/types/async/support.d.ts +14 -0
- package/dist/types/capability/mcp.d.ts +6 -0
- package/dist/types/capability/types.d.ts +6 -0
- package/dist/types/cli/accounts-cli.d.ts +19 -0
- package/dist/types/cli/args.d.ts +3 -0
- package/dist/types/cli/config-cli.d.ts +6 -0
- package/dist/types/cli/crash-cli.d.ts +10 -0
- package/dist/types/cli/customize-doctor.d.ts +145 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/mcp-cli.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/root-flags.d.ts +12 -0
- package/dist/types/cli/setup-cli.d.ts +3 -1
- package/dist/types/cli/skills-cli.d.ts +2 -1
- package/dist/types/cli/update-cli.d.ts +19 -1
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/accounts.d.ts +40 -0
- package/dist/types/commands/crash.d.ts +24 -0
- package/dist/types/commands/customize.d.ts +22 -0
- package/dist/types/commands/daemon.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/commands/setup.d.ts +6 -0
- package/dist/types/commands/skills.d.ts +6 -1
- package/dist/types/commands/update.d.ts +7 -0
- package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
- package/dist/types/config/config-schema-version.d.ts +10 -0
- package/dist/types/config/file-lock.d.ts +18 -0
- package/dist/types/config/model-profile-activation.d.ts +3 -1
- package/dist/types/config/model-registry.d.ts +9 -1
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +70 -6
- package/dist/types/config/settings.d.ts +63 -0
- package/dist/types/config/skill-settings-defaults.d.ts +26 -0
- package/dist/types/config/task-delegation.d.ts +39 -0
- package/dist/types/config/update-channel.d.ts +10 -0
- package/dist/types/coordinator-mcp/server.d.ts +5 -1
- package/dist/types/crash/index-store.d.ts +87 -0
- package/dist/types/crash/nudge.d.ts +50 -0
- package/dist/types/crash/record-loader.d.ts +17 -0
- package/dist/types/crash/report.d.ts +134 -0
- package/dist/types/crash/sanitize.d.ts +25 -0
- package/dist/types/cursor.d.ts +13 -2
- package/dist/types/customization/import.d.ts +37 -0
- package/dist/types/customization/index.d.ts +4 -0
- package/dist/types/customization/inventory.d.ts +25 -0
- package/dist/types/customization/mutations.d.ts +32 -0
- package/dist/types/customization/types.d.ts +115 -0
- package/dist/types/daemon/control-types.d.ts +8 -1
- package/dist/types/daemon/operator-contract.d.ts +4 -4
- package/dist/types/discovery/claude.d.ts +39 -1
- package/dist/types/discovery/codex.d.ts +39 -1
- package/dist/types/discovery/index.d.ts +2 -0
- package/dist/types/discovery/mcp-compat.d.ts +30 -0
- package/dist/types/edit/renderer.d.ts +23 -0
- package/dist/types/extensibility/extensions/runner.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -4
- package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
- package/dist/types/extensibility/hooks/loader.d.ts +19 -5
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
- package/dist/types/extensibility/skill-management.d.ts +107 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
- package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
- package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
- package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
- package/dist/types/hooks/events.d.ts +60 -0
- package/dist/types/hooks/normalize.d.ts +83 -0
- package/dist/types/hooks/skill-state.d.ts +9 -2
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/acp/abort-scope.d.ts +13 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -4
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
- package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
- package/dist/types/modes/components/customization/index.d.ts +1 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +8 -30
- package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
- package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
- package/dist/types/modes/components/oauth-selector.d.ts +10 -4
- package/dist/types/modes/components/pet-capability.d.ts +11 -25
- package/dist/types/modes/components/status-line/gh.d.ts +6 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
- package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
- package/dist/types/runtime-mcp/config.d.ts +9 -0
- package/dist/types/runtime-mcp/manager.d.ts +26 -1
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
- package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
- package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
- package/dist/types/runtime-mcp/protocol.d.ts +278 -0
- package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
- package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
- package/dist/types/runtime-mcp/types.d.ts +102 -10
- package/dist/types/sdk/acp/adapter.d.ts +26 -5
- package/dist/types/sdk/broker/broker.d.ts +3 -55
- package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
- package/dist/types/sdk/broker/session-index.d.ts +14 -5
- package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
- package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
- package/dist/types/sdk/bus/index.d.ts +85 -99
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
- package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
- package/dist/types/sdk/bus/notification-service.d.ts +52 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
- package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
- package/dist/types/sdk/bus/session-control.d.ts +25 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
- package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
- package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
- package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
- package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
- package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
- package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
- package/dist/types/sdk/cli/rows.d.ts +2 -11
- package/dist/types/sdk/cli/session-cli.d.ts +15 -25
- package/dist/types/sdk/client/client.d.ts +97 -1
- package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +18 -5
- package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
- package/dist/types/sdk/host/host.d.ts +15 -1
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/host/session-runtime.d.ts +127 -7
- package/dist/types/sdk/host/types.d.ts +1 -1
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +2 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
- package/dist/types/sdk/lifecycle/client.d.ts +59 -0
- package/dist/types/sdk/lifecycle/index.d.ts +3 -0
- package/dist/types/sdk/lifecycle/service.d.ts +232 -0
- package/dist/types/sdk/mcp/server.d.ts +9 -6
- package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
- package/dist/types/sdk/prompt-status.d.ts +10 -3
- package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
- package/dist/types/sdk/receipt-state.d.ts +17 -0
- package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
- package/dist/types/sdk/router/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +152 -0
- package/dist/types/sdk/session-list.d.ts +22 -0
- package/dist/types/sdk/session-reconnect.d.ts +43 -1
- package/dist/types/sdk/session.d.ts +34 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
- package/dist/types/session/account-inventory.d.ts +86 -0
- package/dist/types/session/agent-session.d.ts +145 -6
- package/dist/types/session/agent-storage.d.ts +10 -0
- package/dist/types/session/coordinator-tool-label.d.ts +28 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/secure-token-file.d.ts +3 -0
- package/dist/types/session/session-manager.d.ts +40 -1
- package/dist/types/session/startup-auth-config.d.ts +44 -0
- package/dist/types/session/terminal-abort.d.ts +378 -0
- package/dist/types/session/yield-queue.d.ts +7 -0
- package/dist/types/session-import/claude.d.ts +26 -0
- package/dist/types/session-import/codex.d.ts +88 -0
- package/dist/types/session-import/command.d.ts +35 -0
- package/dist/types/session-import/detect.d.ts +22 -0
- package/dist/types/session-import/index.d.ts +7 -0
- package/dist/types/session-import/provider-codex.d.ts +22 -0
- package/dist/types/session-import/provider-service.d.ts +38 -0
- package/dist/types/session-import/redact.d.ts +23 -0
- package/dist/types/session-import/service.d.ts +47 -0
- package/dist/types/session-import/types.d.ts +120 -0
- package/dist/types/setup/paseo/check.d.ts +16 -0
- package/dist/types/setup/paseo/install-saga.d.ts +115 -0
- package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
- package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
- package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
- package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
- package/dist/types/setup/paseo/provider-config.d.ts +35 -0
- package/dist/types/setup/paseo/remove.d.ts +14 -0
- package/dist/types/setup/paseo/result-types.d.ts +59 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
- package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
- package/dist/types/skill-state/active-state.d.ts +5 -1
- package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
- package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
- package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
- package/dist/types/slash-commands/types.d.ts +7 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/tools/ask-answer-registry.d.ts +6 -3
- package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
- package/dist/types/tools/bash.d.ts +2 -6
- package/dist/types/tools/browser/attach.d.ts +11 -0
- package/dist/types/tools/browser/launch.d.ts +20 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +9 -2
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/monitor.d.ts +1 -1
- package/dist/types/tools/skill-discovery.d.ts +8 -1
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
- package/dist/types/utils/herdr-pane.d.ts +76 -0
- package/package.json +19 -9
- package/scripts/build-binary.ts +2 -1
- package/scripts/build-sdk-package-smoke.ts +2 -13
- package/scripts/capture-sticky-viewport-showcase.ts +0 -1
- package/scripts/compile-args.ts +2 -0
- package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
- package/scripts/generate-sdk-operation-inventory.ts +28 -0
- package/scripts/generate-telegram-baseline-manifest.ts +1 -0
- package/scripts/generate-tool-catalog.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
- package/src/async/job-manager.ts +352 -17
- package/src/async/support.ts +25 -0
- package/src/autoresearch/dashboard.ts +28 -13
- package/src/capability/index.ts +3 -1
- package/src/capability/mcp.ts +6 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/accounts-cli.ts +395 -0
- package/src/cli/args.ts +23 -0
- package/src/cli/auth-broker-cli.ts +136 -66
- package/src/cli/auth-gateway-cli.ts +94 -70
- package/src/cli/config-cli.ts +27 -2
- package/src/cli/crash-cli.ts +78 -0
- package/src/cli/customize-doctor.ts +1605 -0
- package/src/cli/daemon-cli.ts +12 -3
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/mcp-cli.ts +128 -60
- package/src/cli/notify-cli.ts +8 -14
- package/src/cli/root-flags.ts +8 -0
- package/src/cli/setup-cli.ts +145 -0
- package/src/cli/skills-cli.ts +38 -2
- package/src/cli/update-cli.ts +167 -12
- package/src/cli.ts +4 -0
- package/src/commands/accounts.ts +71 -0
- package/src/commands/crash.ts +35 -0
- package/src/commands/customize.ts +84 -0
- package/src/commands/daemon.ts +2 -2
- package/src/commands/deep-interview.ts +2 -0
- package/src/commands/launch.ts +1 -0
- package/src/commands/mcp.ts +12 -7
- package/src/commands/ralplan.ts +2 -0
- package/src/commands/sdk.ts +65 -32
- package/src/commands/setup.ts +5 -0
- package/src/commands/skills.ts +14 -4
- package/src/commands/ultragoal.ts +10 -1
- package/src/commands/update.ts +26 -15
- package/src/config/atomic-yaml-patch-worker.ts +57 -0
- package/src/config/atomic-yaml-patch.ts +769 -29
- package/src/config/config-schema-version.ts +10 -0
- package/src/config/file-lock.ts +70 -1
- package/src/config/mcp-schema.json +52 -14
- package/src/config/model-profile-activation.ts +20 -1
- package/src/config/model-profiles.ts +29 -19
- package/src/config/model-registry.ts +132 -21
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +76 -6
- package/src/config/settings.ts +4038 -199
- package/src/config/skill-settings-defaults.ts +41 -3
- package/src/config/task-delegation.ts +87 -0
- package/src/config/update-channel.ts +23 -0
- package/src/coordinator-mcp/server.ts +377 -308
- package/src/crash/index-store.ts +723 -0
- package/src/crash/nudge.ts +89 -0
- package/src/crash/record-loader.ts +149 -0
- package/src/crash/report.ts +551 -0
- package/src/crash/sanitize.ts +129 -0
- package/src/cursor.ts +36 -10
- package/src/customization/import.ts +946 -0
- package/src/customization/index.ts +4 -0
- package/src/customization/inventory.ts +335 -0
- package/src/customization/mutations.ts +141 -0
- package/src/customization/types.ts +223 -0
- package/src/daemon/control-types.ts +8 -1
- package/src/daemon/operator-contract.ts +6 -6
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
- package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
- package/src/discovery/claude-hooks.ts +38 -0
- package/src/discovery/claude.ts +142 -209
- package/src/discovery/codex-hooks.ts +38 -0
- package/src/discovery/codex.ts +132 -198
- package/src/discovery/helpers.ts +8 -1
- package/src/discovery/index.ts +2 -0
- package/src/discovery/mcp-compat.ts +234 -0
- package/src/discovery/mcp-json.ts +6 -0
- package/src/edit/renderer.ts +40 -0
- package/src/edit/streaming.ts +4 -2
- package/src/exec/bash-executor.ts +24 -20
- package/src/export/html/index.ts +51 -2
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +17 -4
- package/src/extensibility/extensions/types.ts +31 -6
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
- package/src/extensibility/gjc-plugins/installer.ts +9 -5
- package/src/extensibility/gjc-plugins/observability.ts +8 -2
- package/src/extensibility/gjc-plugins/registry.ts +13 -2
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
- package/src/extensibility/gjc-plugins/schema.ts +222 -39
- package/src/extensibility/hooks/loader.ts +154 -10
- package/src/extensibility/runtime-skill-discovery.ts +257 -60
- package/src/extensibility/skill-management.ts +368 -0
- package/src/extensibility/skills.ts +52 -16
- package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
- package/src/gjc-runtime/launch-worktree.ts +56 -2
- package/src/gjc-runtime/linux-proc.ts +7 -1
- package/src/gjc-runtime/ralplan-runtime.ts +100 -164
- package/src/gjc-runtime/session-state-lock.ts +992 -0
- package/src/gjc-runtime/session-state-sidecar.ts +644 -142
- package/src/gjc-runtime/state-writer.ts +39 -14
- package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
- package/src/gjc-runtime/ultragoal-guard.ts +11 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
- package/src/gjc-runtime/workflow-settings.ts +596 -0
- package/src/goals/tools/goal-tool.ts +6 -1
- package/src/hooks/events.ts +291 -0
- package/src/hooks/native-skill-hook.ts +4 -0
- package/src/hooks/normalize.ts +369 -0
- package/src/hooks/skill-state.ts +165 -46
- package/src/internal-urls/docs-index.generated.ts +40 -32
- package/src/main.ts +32 -5
- package/src/migrate/mcp-mapper.ts +25 -2
- package/src/modes/acp/abort-scope.ts +29 -0
- package/src/modes/acp/acp-agent.ts +423 -98
- package/src/modes/components/assistant-message.ts +6 -1
- package/src/modes/components/customization/customization-dashboard.ts +443 -0
- package/src/modes/components/customization/import-wizard.ts +372 -0
- package/src/modes/components/customization/index.ts +1 -0
- package/src/modes/components/gajae-pet-widget.ts +539 -213
- package/src/modes/components/irc-sidebar.ts +11 -0
- package/src/modes/components/iterm-pet-transport.ts +585 -0
- package/src/modes/components/oauth-selector.ts +277 -86
- package/src/modes/components/pet-capability.ts +39 -40
- package/src/modes/components/settings-selector.ts +9 -0
- package/src/modes/components/status-line/gh.ts +45 -1
- package/src/modes/components/status-line/segments.ts +16 -1
- package/src/modes/components/tool-status-header.ts +13 -3
- package/src/modes/controllers/command-controller.ts +20 -8
- package/src/modes/controllers/extension-ui-controller.ts +56 -257
- package/src/modes/controllers/input-controller.ts +10 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
- package/src/modes/controllers/selector-controller.ts +177 -72
- package/src/modes/controllers/stt-controller.ts +3 -3
- package/src/modes/interactive-mode.ts +88 -16
- package/src/modes/print-mode.ts +35 -6
- package/src/modes/runtime-init.ts +21 -68
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/injected-user-submission.ts +25 -0
- package/src/modes/utils/terminal-bell.ts +15 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/critic.md +1 -1
- package/src/prompts/system/rlm-research.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -2
- package/src/prompts/tools/bash.md +7 -1
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/cron.md +6 -6
- package/src/prompts/tools/read.md +0 -2
- package/src/prompts/tools/skill-discovery.md +4 -2
- package/src/runtime-mcp/client.ts +427 -23
- package/src/runtime-mcp/config.ts +37 -5
- package/src/runtime-mcp/manager-pool.test.ts +56 -0
- package/src/runtime-mcp/manager.ts +52 -2
- package/src/runtime-mcp/oauth-discovery.ts +13 -0
- package/src/runtime-mcp/oauth-flow.ts +46 -1
- package/src/runtime-mcp/pool-key.ts +5 -1
- package/src/runtime-mcp/pool.test.ts +9 -0
- package/src/runtime-mcp/protocol.test.ts +353 -0
- package/src/runtime-mcp/protocol.ts +624 -0
- package/src/runtime-mcp/scope-config.ts +78 -0
- package/src/runtime-mcp/tool-bridge.ts +44 -10
- package/src/runtime-mcp/transports/http.ts +84 -13
- package/src/runtime-mcp/types.ts +123 -12
- package/src/sdk/acp/adapter.ts +328 -97
- package/src/sdk/broker/broker.ts +160 -741
- package/src/sdk/broker/ensure.ts +9 -1
- package/src/sdk/broker/lifecycle.ts +510 -116
- package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
- package/src/sdk/broker/process-incarnation.ts +19 -0
- package/src/sdk/broker/session-index.ts +516 -117
- package/src/sdk/broker/startup-failure.ts +28 -5
- package/src/sdk/broker/transport.ts +1 -13
- package/src/sdk/bus/chat-daemon-cli.ts +4 -2
- package/src/sdk/bus/chat-daemon-control.ts +67 -6
- package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
- package/src/sdk/bus/config.ts +13 -0
- package/src/sdk/bus/conversation-store.ts +59 -171
- package/src/sdk/bus/daemon-paths.ts +20 -0
- package/src/sdk/bus/discord-conversation.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +524 -126
- package/src/sdk/bus/discord-live-provider.ts +206 -79
- package/src/sdk/bus/existing-thread-readiness.ts +4 -97
- package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
- package/src/sdk/bus/index.ts +2480 -456
- package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
- package/src/sdk/bus/lifecycle-commands.ts +2 -2
- package/src/sdk/bus/managed-daemon.ts +2 -145
- package/src/sdk/bus/notification-service.ts +196 -0
- package/src/sdk/bus/operator-runtime.ts +1 -1
- package/src/sdk/bus/prompt-reconciliation.ts +24 -15
- package/src/sdk/bus/reconciliation-store.ts +553 -24
- package/src/sdk/bus/session-control.ts +49 -3
- package/src/sdk/bus/slack-conversation.ts +4 -0
- package/src/sdk/bus/slack-daemon.ts +733 -134
- package/src/sdk/bus/slack-live-provider.ts +65 -15
- package/src/sdk/bus/slack-provider.ts +4 -0
- package/src/sdk/bus/slack-thread-binding.ts +9 -104
- package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
- package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
- package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
- package/src/sdk/bus/telegram-daemon-control.ts +3 -13
- package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
- package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
- package/src/sdk/bus/telegram-daemon.ts +2552 -2280
- package/src/sdk/bus/telegram-reference.ts +1 -226
- package/src/sdk/bus/telegram-setup.ts +2 -2
- package/src/sdk/bus/threaded-render.ts +1 -0
- package/src/sdk/bus/topic-registry.ts +279 -146
- package/src/sdk/cli/rows.ts +2 -35
- package/src/sdk/cli/session-cli.ts +776 -700
- package/src/sdk/client/client.ts +892 -1
- package/src/sdk/host/control/dispatch.ts +93 -58
- package/src/sdk/host/control/index.ts +1 -1
- package/src/sdk/host/control/operations.ts +18 -10
- package/src/sdk/host/control/runtime-gate.ts +40 -0
- package/src/sdk/host/host.ts +63 -12
- package/src/sdk/host/query/handlers.ts +66 -7
- package/src/sdk/host/reverse-leases.ts +2 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +2787 -34
- package/src/sdk/host/session-runtime.ts +1489 -101
- package/src/sdk/host/surface-policy.ts +12 -8
- package/src/sdk/host/types.ts +1 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
- package/src/sdk/host/websocket-transport.ts +85 -50
- package/src/sdk/index.ts +2 -1
- package/src/sdk/lifecycle/broker-client.ts +53 -0
- package/src/sdk/lifecycle/client.ts +234 -0
- package/src/sdk/lifecycle/index.ts +3 -0
- package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
- package/src/sdk/lifecycle/service.ts +720 -0
- package/src/sdk/mcp/server.ts +226 -188
- package/src/sdk/prompt-deadline-lease.ts +55 -0
- package/src/sdk/prompt-deadline-manager.ts +148 -0
- package/src/sdk/prompt-status.ts +11 -3
- package/src/sdk/protocol/operation-inventory.generated.json +272 -129
- package/src/sdk/protocol/operation-registry.ts +71 -8
- package/src/sdk/receipt-state.ts +43 -0
- package/src/sdk/reconciliation-extensions.ts +14 -0
- package/src/sdk/router/index.ts +1 -0
- package/src/sdk/router/session-router.ts +1658 -0
- package/src/sdk/session-list.ts +93 -0
- package/src/sdk/session-reconnect.ts +45 -1
- package/src/sdk/session.ts +716 -120
- package/src/sdk/transport/serve-cli.ts +18 -34
- package/src/session/account-inventory.ts +603 -0
- package/src/session/agent-session.ts +2857 -582
- package/src/session/agent-storage.ts +35 -0
- package/src/session/blob-store.ts +32 -1
- package/src/session/coordinator-tool-label.ts +40 -0
- package/src/session/internal/managed-session-scope.ts +4 -0
- package/src/session/internal/managed-session-storage.ts +173 -12
- package/src/session/messages.ts +1 -1
- package/src/session/secure-token-file.ts +80 -0
- package/src/session/session-manager.ts +730 -58
- package/src/session/session-storage.ts +25 -16
- package/src/session/startup-auth-config.ts +297 -0
- package/src/session/terminal-abort.ts +1253 -0
- package/src/session/yield-queue.ts +57 -15
- package/src/session-import/claude.ts +334 -0
- package/src/session-import/codex.ts +1033 -0
- package/src/session-import/command.ts +77 -0
- package/src/session-import/detect.ts +133 -0
- package/src/session-import/index.ts +7 -0
- package/src/session-import/provider-codex.ts +327 -0
- package/src/session-import/provider-service.ts +457 -0
- package/src/session-import/redact.ts +128 -0
- package/src/session-import/service.ts +1264 -0
- package/src/session-import/types.ts +148 -0
- package/src/setup/paseo/check.ts +213 -0
- package/src/setup/paseo/install-saga.ts +293 -0
- package/src/setup/paseo/json-publisher.ts +263 -0
- package/src/setup/paseo/orchestration-preferences.ts +86 -0
- package/src/setup/paseo/paseo-ownership.ts +224 -0
- package/src/setup/paseo/paseo-setup.ts +253 -0
- package/src/setup/paseo/provider-config.ts +139 -0
- package/src/setup/paseo/remove.ts +183 -0
- package/src/setup/paseo/result-types.ts +79 -0
- package/src/setup/paseo/setup-deps.ts +175 -0
- package/src/setup/paseo/skills-bridge.ts +260 -0
- package/src/skill-state/active-state.ts +21 -11
- package/src/slash-commands/acp-builtins.ts +9 -1
- package/src/slash-commands/builtin-registry.ts +98 -16
- package/src/slash-commands/helpers/credential-switch.ts +143 -0
- package/src/slash-commands/helpers/mcp.ts +9 -3
- package/src/slash-commands/helpers/usage-report.ts +61 -56
- package/src/slash-commands/types.ts +7 -2
- package/src/task/executor.ts +30 -11
- package/src/task/index.ts +66 -7
- package/src/task/render.ts +23 -9
- package/src/tools/ask-answer-registry.ts +23 -7
- package/src/tools/ask.ts +8 -4
- package/src/tools/bash-sleep-advisory.ts +115 -0
- package/src/tools/bash.ts +98 -18
- package/src/tools/browser/attach.ts +71 -3
- package/src/tools/browser/launch.ts +86 -13
- package/src/tools/browser/profile-discovery.ts +57 -7
- package/src/tools/browser/render.ts +8 -1
- package/src/tools/browser.ts +130 -21
- package/src/tools/computer.enforcement.test.ts +1 -1
- package/src/tools/computer.ts +3 -2
- package/src/tools/cron.ts +12 -1
- package/src/tools/image-gen.ts +27 -16
- package/src/tools/index.ts +8 -0
- package/src/tools/job.ts +33 -6
- package/src/tools/monitor.ts +41 -2
- package/src/tools/skill-discovery.ts +21 -29
- package/src/tools/subagent.ts +142 -24
- package/src/tools/tool-catalog.generated.ts +6 -6
- package/src/tools/ultragoal-ask-guard.ts +10 -3
- package/src/utils/herdr-pane.ts +345 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/scrapers/index.ts +6 -4
- package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
- package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
- package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
- package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
- package/dist/types/runtime/lazy-service.test.d.ts +0 -1
- package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
- package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
- package/dist/types/runtime-mcp/config.test.d.ts +0 -1
- package/dist/types/runtime-mcp/http.test.d.ts +0 -1
- package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
- package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
- package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
- package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
- package/dist/types/sdk/elevation/audit.d.ts +0 -93
- package/dist/types/sdk/elevation/capability.d.ts +0 -3
- package/dist/types/sdk/elevation/digest.d.ts +0 -72
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
- package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
- package/dist/types/sdk/elevation/owner.d.ts +0 -48
- package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
- package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
- package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
- package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
- package/dist/types/session/auth-broker-config.d.ts +0 -13
- package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
- package/dist/types/skills/skills.test.d.ts +0 -1
- package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
- package/dist/types/task/token-log.test.d.ts +0 -1
- package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
- package/dist/types/tools/descriptors.test.d.ts +0 -1
- package/scripts/g005-daemon-smoke.ts +0 -180
- package/scripts/g011-daemon-path-smoke.ts +0 -153
- package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
- package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
- package/src/sdk/bus/telegram-cli.ts +0 -212
- package/src/sdk/elevation/allowlist.ts +0 -54
- package/src/sdk/elevation/audit.ts +0 -208
- package/src/sdk/elevation/capability.ts +0 -41
- package/src/sdk/elevation/digest.ts +0 -129
- package/src/sdk/elevation/dispatch-receipt.ts +0 -184
- package/src/sdk/elevation/grant-ledger.ts +0 -1142
- package/src/sdk/elevation/owner.ts +0 -109
- package/src/session/auth-broker-config.ts +0 -109
- /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
- /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
- /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Telegram session-lifecycle orchestrator (G005 core).
|
|
3
|
-
*
|
|
4
|
-
* Owns the daemon-side policy for remote session create / close / resume:
|
|
5
|
-
* strict paired-chat gating, a durable + atomic idempotency state machine,
|
|
6
|
-
* per-chat create rate limiting, audit logging with token/prompt redaction, and
|
|
7
|
-
* dispatch to injected effects (spawn / close / resume). It is deliberately
|
|
8
|
-
* effect-injected so the decision logic is unit-testable and the same code path
|
|
9
|
-
* is exercised end-to-end by a real-tmux integration smoke.
|
|
10
|
-
*
|
|
11
|
-
* The Rust control ingress (crates/gjc-sdk control server) has already
|
|
12
|
-
* authenticated frames before they reach here; this module never sees or logs
|
|
13
|
-
* the raw control token.
|
|
14
|
-
*/
|
|
15
|
-
import * as crypto from "node:crypto";
|
|
16
|
-
|
|
17
|
-
import type { LifecycleErrorReason, ResumeCandidate, SessionCreateFrame, SessionLifecycleRequest } from "./index";
|
|
18
|
-
|
|
19
|
-
/** Durable idempotency state for a single lifecycle request. */
|
|
20
|
-
export type LedgerState = "in_progress" | "success" | "failure" | "terminal_uncertain";
|
|
21
|
-
|
|
22
|
-
/** One persisted idempotency entry, keyed by `chatId:updateId`. */
|
|
23
|
-
export interface LedgerEntry {
|
|
24
|
-
requestHash: string;
|
|
25
|
-
state: LedgerState;
|
|
26
|
-
requestId: string;
|
|
27
|
-
verb: SessionLifecycleRequest["type"];
|
|
28
|
-
intendedSessionId?: string;
|
|
29
|
-
startupPromptRef?: string;
|
|
30
|
-
createdAt: number;
|
|
31
|
-
updatedAt: number;
|
|
32
|
-
targetSummary: Record<string, unknown>;
|
|
33
|
-
sessionId?: string;
|
|
34
|
-
tmuxSession?: string;
|
|
35
|
-
sessionStateFile?: string;
|
|
36
|
-
endpointUrl?: string;
|
|
37
|
-
/** Close effect outcome: whether the tmux process is confirmed gone. */
|
|
38
|
-
processGone?: boolean;
|
|
39
|
-
reason?: LifecycleErrorReason;
|
|
40
|
-
resumeMode?: ResumeEffectResult["mode"];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** The full on-disk ledger document. */
|
|
44
|
-
export interface LedgerDoc {
|
|
45
|
-
version: 1;
|
|
46
|
-
entries: Record<string, LedgerEntry>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Persistence boundary: atomic + fsynced read/write of the ledger document. */
|
|
50
|
-
export interface LedgerStore {
|
|
51
|
-
read(): Promise<LedgerDoc>;
|
|
52
|
-
/** Write atomically (temp + fsync + rename) under a per-ledger lock. */
|
|
53
|
-
write(doc: LedgerDoc): Promise<void>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Redacted audit-v2 event. */
|
|
57
|
-
export interface AuditEvent {
|
|
58
|
-
schemaVersion: 2;
|
|
59
|
-
ts: string;
|
|
60
|
-
event:
|
|
61
|
-
| "accepted"
|
|
62
|
-
| "rejected"
|
|
63
|
-
| "duplicate_reack"
|
|
64
|
-
| "rate_limited"
|
|
65
|
-
| "spawn_started"
|
|
66
|
-
| "recovered_in_progress"
|
|
67
|
-
| "success"
|
|
68
|
-
| "failure"
|
|
69
|
-
| "terminal_uncertain";
|
|
70
|
-
chatRef: string;
|
|
71
|
-
updateId: number;
|
|
72
|
-
requestRef: string;
|
|
73
|
-
verb: SessionLifecycleRequest["type"];
|
|
74
|
-
targetKind: CanonicalTargetKind;
|
|
75
|
-
targetRef: string;
|
|
76
|
-
reason?: LifecycleErrorReason;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface CreateEffectResult {
|
|
80
|
-
sessionId: string;
|
|
81
|
-
tmuxSession: string;
|
|
82
|
-
sessionStateFile?: string;
|
|
83
|
-
endpointUrl: string;
|
|
84
|
-
topicThreadId: string;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface ResumeEffectResult extends CreateEffectResult {
|
|
88
|
-
mode: "reattached" | "cold_restarted";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Injected effects + policy. Pure orchestration calls into these. */
|
|
92
|
-
export interface OrchestratorDeps {
|
|
93
|
-
/** The single paired chat id. Anything else is rejected before parsing. */
|
|
94
|
-
pairedChatId: string;
|
|
95
|
-
/** In-memory, derived 32-byte audit-v2 HMAC key. */
|
|
96
|
-
auditRedactionKey: Uint8Array;
|
|
97
|
-
now: () => number;
|
|
98
|
-
store: LedgerStore;
|
|
99
|
-
audit: (event: AuditEvent) => Promise<void> | void;
|
|
100
|
-
/** Resolves the returned tmux provider once per request for psmux preflight. */
|
|
101
|
-
isPsmuxProvider: () => boolean;
|
|
102
|
-
/** Per-chat create rate limiter: returns true when allowed. */
|
|
103
|
-
allowCreate: (chatId: string, nowMs: number) => boolean;
|
|
104
|
-
/** Persist the once-consumed 0600 startup-prompt file after durably recording its ref. */
|
|
105
|
-
writeStartupPrompt: (
|
|
106
|
-
requestId: string,
|
|
107
|
-
prompt: string | undefined,
|
|
108
|
-
persistRef: (ref: string) => Promise<void>,
|
|
109
|
-
) => Promise<string | undefined>;
|
|
110
|
-
/** Spawn a session for a create/cold-restart. */
|
|
111
|
-
spawnCreate: (
|
|
112
|
-
frame: SessionCreateFrame,
|
|
113
|
-
ids: { lifecycleRequestId: string; intendedSessionId: string; startupPromptRef?: string },
|
|
114
|
-
) => Promise<CreateEffectResult>;
|
|
115
|
-
closeSession: (target: {
|
|
116
|
-
sessionId: string;
|
|
117
|
-
tmuxSession?: string;
|
|
118
|
-
sessionStateFile?: string;
|
|
119
|
-
}) => Promise<{ processGone: boolean }>;
|
|
120
|
-
resumeSession: (target: {
|
|
121
|
-
sessionIdOrPrefix: string;
|
|
122
|
-
path?: string;
|
|
123
|
-
}) => Promise<ResumeEffectResult | { ambiguous: ResumeCandidate[] } | { notFound: true }>;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export type CanonicalTargetKind = "existing_path" | "worktree" | "plain_dir" | "session_close" | "session_resume";
|
|
127
|
-
|
|
128
|
-
function isUnicodeScalarString(value: string): boolean {
|
|
129
|
-
for (let index = 0; index < value.length; index++) {
|
|
130
|
-
const code = value.charCodeAt(index);
|
|
131
|
-
if (code >= 0xd800 && code <= 0xdbff) {
|
|
132
|
-
if (index + 1 >= value.length) return false;
|
|
133
|
-
const next = value.charCodeAt(++index);
|
|
134
|
-
if (next < 0xdc00 || next > 0xdfff) return false;
|
|
135
|
-
} else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function jsonString(value: string): string {
|
|
143
|
-
if (!isUnicodeScalarString(value)) throw new Error("invalid_target");
|
|
144
|
-
return JSON.stringify(value);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function canonicalTarget(frame: SessionLifecycleRequest): string {
|
|
148
|
-
switch (frame.type) {
|
|
149
|
-
case "session_create":
|
|
150
|
-
switch (frame.target.kind) {
|
|
151
|
-
case "existing_path":
|
|
152
|
-
return `{"kind":"existing_path","path":${jsonString(frame.target.path)}}`;
|
|
153
|
-
case "worktree":
|
|
154
|
-
return `{"kind":"worktree","repo":${jsonString(frame.target.repo)},"branch":${jsonString(frame.target.branch)}}`;
|
|
155
|
-
case "plain_dir":
|
|
156
|
-
return `{"kind":"plain_dir","path":${jsonString(frame.target.path)}}`;
|
|
157
|
-
}
|
|
158
|
-
throw new Error("invalid_target");
|
|
159
|
-
case "session_close":
|
|
160
|
-
return `{"kind":"session_close","sessionId":${jsonString(frame.target.sessionId)},"tmuxSession":${frame.target.tmuxSession === undefined ? "null" : jsonString(frame.target.tmuxSession)},"sessionStateFile":${frame.target.sessionStateFile === undefined ? "null" : jsonString(frame.target.sessionStateFile)}}`;
|
|
161
|
-
case "session_resume":
|
|
162
|
-
return `{"kind":"session_resume","sessionIdOrPrefix":${jsonString(frame.target.sessionIdOrPrefix)},"path":${frame.target.path === undefined ? "null" : jsonString(frame.target.path)}}`;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function canonicalRequest(frame: SessionLifecycleRequest): string {
|
|
167
|
-
const target = canonicalTarget(frame);
|
|
168
|
-
const startupPromptRef =
|
|
169
|
-
frame.type === "session_create" && frame.startupPromptRef !== undefined
|
|
170
|
-
? jsonString(frame.startupPromptRef)
|
|
171
|
-
: "null";
|
|
172
|
-
const modelPreset =
|
|
173
|
-
frame.type === "session_create" && frame.modelPreset !== undefined ? jsonString(frame.modelPreset) : "null";
|
|
174
|
-
const force = frame.type === "session_close" ? (frame.force === true ? "true" : "false") : "null";
|
|
175
|
-
return `{"type":${jsonString(frame.type)},"target":${target},"startupPromptRef":${startupPromptRef},"modelPreset":${modelPreset},"force":${force}}`;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function requestHashFromCanonical(canonical: string): string {
|
|
179
|
-
return crypto.createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function requestHash(frame: SessionLifecycleRequest): string {
|
|
183
|
-
return requestHashFromCanonical(canonicalRequest(frame));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function auditRedactionRef(key: Uint8Array, domain: string, value: string): string {
|
|
187
|
-
if (key.byteLength !== 32) throw new Error("invalid_audit_redaction_key");
|
|
188
|
-
return crypto.createHmac("sha256", key).update(domain, "utf8").update(value, "utf8").digest("hex");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function summarizeTarget(frame: SessionLifecycleRequest): Record<string, unknown> {
|
|
192
|
-
switch (frame.type) {
|
|
193
|
-
case "session_create":
|
|
194
|
-
return frame.target.kind === "worktree"
|
|
195
|
-
? { kind: "worktree", repo: frame.target.repo, branch: frame.target.branch }
|
|
196
|
-
: { kind: frame.target.kind, path: frame.target.path };
|
|
197
|
-
case "session_close":
|
|
198
|
-
return { sessionId: frame.target.sessionId };
|
|
199
|
-
case "session_resume":
|
|
200
|
-
return { sessionIdOrPrefix: frame.target.sessionIdOrPrefix };
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export function ledgerKey(chatId: string, updateId: number): string {
|
|
205
|
-
return `${chatId}:${updateId}`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** How a freshly-arrived request relates to the durable ledger. */
|
|
209
|
-
export type DuplicateClass =
|
|
210
|
-
| { kind: "new" }
|
|
211
|
-
| { kind: "reack_success"; entry: LedgerEntry }
|
|
212
|
-
| { kind: "reack_failure"; entry: LedgerEntry }
|
|
213
|
-
| { kind: "in_progress"; entry: LedgerEntry }
|
|
214
|
-
| { kind: "terminal_uncertain"; entry: LedgerEntry }
|
|
215
|
-
| { kind: "conflict"; entry: LedgerEntry };
|
|
216
|
-
|
|
217
|
-
/** Classify a request against an existing ledger entry (pure). */
|
|
218
|
-
export function classifyDuplicate(existing: LedgerEntry | undefined, hash: string): DuplicateClass {
|
|
219
|
-
if (!existing) return { kind: "new" };
|
|
220
|
-
if (existing.requestHash !== hash) return { kind: "conflict", entry: existing };
|
|
221
|
-
switch (existing.state) {
|
|
222
|
-
case "success":
|
|
223
|
-
return { kind: "reack_success", entry: existing };
|
|
224
|
-
case "failure":
|
|
225
|
-
return { kind: "reack_failure", entry: existing };
|
|
226
|
-
case "in_progress":
|
|
227
|
-
return { kind: "in_progress", entry: existing };
|
|
228
|
-
case "terminal_uncertain":
|
|
229
|
-
return { kind: "terminal_uncertain", entry: existing };
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** The structured outcome the daemon translates into a wire response frame. */
|
|
234
|
-
export type LifecycleOutcome =
|
|
235
|
-
| { status: "ok"; entry: LedgerEntry; mode?: "reattached" | "cold_restarted" }
|
|
236
|
-
| { status: "error"; reason: LifecycleErrorReason; message: string; candidates?: ResumeCandidate[] }
|
|
237
|
-
| { status: "pending"; entry: LedgerEntry };
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Handle one authenticated lifecycle request. Enforces paired-chat gating,
|
|
241
|
-
* idempotency, and rate limiting BEFORE any side effect, then dispatches.
|
|
242
|
-
*/
|
|
243
|
-
export async function handleLifecycleRequest(
|
|
244
|
-
frame: SessionLifecycleRequest,
|
|
245
|
-
deps: OrchestratorDeps,
|
|
246
|
-
): Promise<LifecycleOutcome> {
|
|
247
|
-
const nowMs = deps.now();
|
|
248
|
-
let hash: string;
|
|
249
|
-
let canonicalRequestBytes: string;
|
|
250
|
-
let canonicalTargetBytes: string;
|
|
251
|
-
try {
|
|
252
|
-
canonicalRequestBytes = canonicalRequest(frame);
|
|
253
|
-
canonicalTargetBytes = canonicalTarget(frame);
|
|
254
|
-
hash = requestHashFromCanonical(canonicalRequestBytes);
|
|
255
|
-
} catch {
|
|
256
|
-
return { status: "error", reason: "invalid_target", message: "invalid lifecycle target" };
|
|
257
|
-
}
|
|
258
|
-
if (deps.auditRedactionKey.byteLength !== 32) throw new Error("invalid_audit_redaction_key");
|
|
259
|
-
const key = ledgerKey(frame.chatId, frame.updateId);
|
|
260
|
-
const targetSummary = summarizeTarget(frame);
|
|
261
|
-
const targetKind: CanonicalTargetKind =
|
|
262
|
-
frame.type === "session_create"
|
|
263
|
-
? frame.target.kind
|
|
264
|
-
: frame.type === "session_close"
|
|
265
|
-
? "session_close"
|
|
266
|
-
: "session_resume";
|
|
267
|
-
const baseAudit = {
|
|
268
|
-
schemaVersion: 2 as const,
|
|
269
|
-
ts: new Date(nowMs).toISOString(),
|
|
270
|
-
chatRef: auditRedactionRef(deps.auditRedactionKey, "gjc.lifecycle.audit.v2.chat\0", frame.chatId),
|
|
271
|
-
updateId: frame.updateId,
|
|
272
|
-
requestRef: auditRedactionRef(deps.auditRedactionKey, "gjc.lifecycle.audit.v2.request\0", canonicalRequestBytes),
|
|
273
|
-
verb: frame.type,
|
|
274
|
-
targetKind,
|
|
275
|
-
targetRef: auditRedactionRef(deps.auditRedactionKey, "gjc.lifecycle.audit.v2.target\0", canonicalTargetBytes),
|
|
276
|
-
} as const;
|
|
277
|
-
|
|
278
|
-
// 1. Strict paired-chat gating — BEFORE touching paths/processes or the ledger.
|
|
279
|
-
if (frame.chatId !== deps.pairedChatId) {
|
|
280
|
-
await deps.audit({ ...baseAudit, event: "rejected", reason: "unauthorized" });
|
|
281
|
-
return { status: "error", reason: "unauthorized", message: "chat not paired" };
|
|
282
|
-
}
|
|
283
|
-
if (frame.type === "session_close" && frame.force !== true) {
|
|
284
|
-
await deps.audit({ ...baseAudit, event: "rejected", reason: "invalid_target" });
|
|
285
|
-
return {
|
|
286
|
-
status: "error",
|
|
287
|
-
reason: "invalid_target",
|
|
288
|
-
message: "session_close requires force=true; graceful close is not supported",
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const suppliedStartupPromptRef =
|
|
292
|
-
frame.type === "session_create" || frame.type === "session_resume" ? frame.startupPromptRef : undefined;
|
|
293
|
-
if (suppliedStartupPromptRef !== undefined) {
|
|
294
|
-
return {
|
|
295
|
-
status: "error",
|
|
296
|
-
reason: "invalid_target",
|
|
297
|
-
message: "startup prompt capability transport is unavailable; retry without a startup prompt",
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
if (deps.isPsmuxProvider()) {
|
|
301
|
-
await deps.audit({ ...baseAudit, event: "rejected", reason: "unsupported_platform" });
|
|
302
|
-
return {
|
|
303
|
-
status: "error",
|
|
304
|
-
reason: "unsupported_platform",
|
|
305
|
-
message:
|
|
306
|
-
"Remote session lifecycle is unavailable on this psmux host because GJC cannot prove immutable session identity. No lifecycle action was performed. Use a local GJC terminal with a supported tmux provider.",
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// 4. Durable idempotency.
|
|
311
|
-
const doc = await deps.store.read();
|
|
312
|
-
const existing = doc.entries[key];
|
|
313
|
-
if (
|
|
314
|
-
frame.type === "session_close" &&
|
|
315
|
-
existing?.verb === "session_close" &&
|
|
316
|
-
existing.requestHash === hash &&
|
|
317
|
-
existing.state === "success" &&
|
|
318
|
-
typeof existing.sessionId === "string" &&
|
|
319
|
-
existing.processGone === false
|
|
320
|
-
) {
|
|
321
|
-
existing.state = "terminal_uncertain";
|
|
322
|
-
existing.reason = "terminal_uncertain";
|
|
323
|
-
existing.updatedAt = deps.now();
|
|
324
|
-
delete existing.processGone;
|
|
325
|
-
await deps.store.write(doc);
|
|
326
|
-
}
|
|
327
|
-
const dup = classifyDuplicate(doc.entries[key], hash);
|
|
328
|
-
switch (dup.kind) {
|
|
329
|
-
case "conflict":
|
|
330
|
-
await deps.audit({ ...baseAudit, event: "rejected", reason: "duplicate_conflict" });
|
|
331
|
-
return { status: "error", reason: "duplicate_conflict", message: "update id reused with different body" };
|
|
332
|
-
case "reack_success":
|
|
333
|
-
await deps.audit({ ...baseAudit, event: "duplicate_reack" });
|
|
334
|
-
return { status: "ok", entry: dup.entry, ...(dup.entry.resumeMode ? { mode: dup.entry.resumeMode } : {}) };
|
|
335
|
-
case "reack_failure":
|
|
336
|
-
if (dup.entry.reason === undefined) throw new Error("invalid lifecycle failure ledger entry");
|
|
337
|
-
await deps.audit({ ...baseAudit, event: "duplicate_reack", reason: dup.entry.reason });
|
|
338
|
-
return {
|
|
339
|
-
status: "error",
|
|
340
|
-
reason: dup.entry.reason,
|
|
341
|
-
message: "previously failed; send a new update to retry",
|
|
342
|
-
};
|
|
343
|
-
case "in_progress":
|
|
344
|
-
// A retry arrived while the first attempt is still running: never
|
|
345
|
-
// respawn — report pending so the caller waits for the original.
|
|
346
|
-
await deps.audit({ ...baseAudit, event: "recovered_in_progress" });
|
|
347
|
-
return { status: "pending", entry: dup.entry };
|
|
348
|
-
case "terminal_uncertain":
|
|
349
|
-
await deps.audit({ ...baseAudit, event: "recovered_in_progress", reason: "terminal_uncertain" });
|
|
350
|
-
return {
|
|
351
|
-
status: "error",
|
|
352
|
-
reason: "terminal_uncertain",
|
|
353
|
-
message: "prior attempt outcome unknown; manual check",
|
|
354
|
-
};
|
|
355
|
-
case "new":
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// 5. Per-chat create rate limit (create only).
|
|
360
|
-
if (frame.type === "session_create" && !deps.allowCreate(frame.chatId, nowMs)) {
|
|
361
|
-
await deps.audit({ ...baseAudit, event: "rate_limited", reason: "rate_limited" });
|
|
362
|
-
return { status: "error", reason: "rate_limited", message: "create rate limit exceeded" };
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// 6. Persist immutable create ids + write in_progress (fsynced) BEFORE any spawn.
|
|
366
|
-
if (frame.type === "session_create" && (!frame.lifecycleRequestId || !frame.intendedSessionId))
|
|
367
|
-
return { status: "error", reason: "invalid_target", message: "create lifecycle ids must be non-empty" };
|
|
368
|
-
let startupPromptRef: string | undefined;
|
|
369
|
-
|
|
370
|
-
const entry: LedgerEntry = {
|
|
371
|
-
requestHash: hash,
|
|
372
|
-
state: "in_progress",
|
|
373
|
-
requestId: frame.requestId,
|
|
374
|
-
verb: frame.type,
|
|
375
|
-
intendedSessionId: frame.type === "session_create" ? frame.intendedSessionId : undefined,
|
|
376
|
-
createdAt: nowMs,
|
|
377
|
-
updatedAt: nowMs,
|
|
378
|
-
targetSummary,
|
|
379
|
-
};
|
|
380
|
-
doc.entries[key] = entry;
|
|
381
|
-
await deps.store.write(doc);
|
|
382
|
-
await deps.audit({ ...baseAudit, event: "accepted" });
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
if (frame.type === "session_create") {
|
|
386
|
-
startupPromptRef = await deps.writeStartupPrompt(frame.requestId, frame.startupPromptRef, async ref => {
|
|
387
|
-
entry.startupPromptRef = ref;
|
|
388
|
-
await deps.store.write(doc);
|
|
389
|
-
});
|
|
390
|
-
entry.startupPromptRef = startupPromptRef;
|
|
391
|
-
await deps.audit({ ...baseAudit, event: "spawn_started" });
|
|
392
|
-
const result = await deps.spawnCreate(frame, {
|
|
393
|
-
lifecycleRequestId: frame.lifecycleRequestId,
|
|
394
|
-
intendedSessionId: frame.intendedSessionId,
|
|
395
|
-
startupPromptRef,
|
|
396
|
-
});
|
|
397
|
-
Object.assign(entry, {
|
|
398
|
-
state: "success",
|
|
399
|
-
updatedAt: deps.now(),
|
|
400
|
-
sessionId: result.sessionId,
|
|
401
|
-
tmuxSession: result.tmuxSession,
|
|
402
|
-
sessionStateFile: result.sessionStateFile,
|
|
403
|
-
endpointUrl: result.endpointUrl,
|
|
404
|
-
});
|
|
405
|
-
await deps.store.write(doc);
|
|
406
|
-
await deps.audit({ ...baseAudit, event: "success" });
|
|
407
|
-
return { status: "ok", entry };
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (frame.type === "session_close") {
|
|
411
|
-
const closed = await deps.closeSession(frame.target);
|
|
412
|
-
if (!closed.processGone) {
|
|
413
|
-
Object.assign(entry, {
|
|
414
|
-
state: "terminal_uncertain",
|
|
415
|
-
updatedAt: deps.now(),
|
|
416
|
-
reason: "terminal_uncertain",
|
|
417
|
-
sessionId: frame.target.sessionId,
|
|
418
|
-
tmuxSession: frame.target.tmuxSession,
|
|
419
|
-
});
|
|
420
|
-
await deps.store.write(doc);
|
|
421
|
-
await deps.audit({ ...baseAudit, event: "terminal_uncertain", reason: "terminal_uncertain" });
|
|
422
|
-
return {
|
|
423
|
-
status: "error",
|
|
424
|
-
reason: "terminal_uncertain",
|
|
425
|
-
message: "session_close could not confirm process disappearance; manual check",
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
Object.assign(entry, {
|
|
429
|
-
state: "success",
|
|
430
|
-
updatedAt: deps.now(),
|
|
431
|
-
sessionId: frame.target.sessionId,
|
|
432
|
-
tmuxSession: frame.target.tmuxSession,
|
|
433
|
-
processGone: true,
|
|
434
|
-
});
|
|
435
|
-
await deps.store.write(doc);
|
|
436
|
-
await deps.audit({ ...baseAudit, event: "success" });
|
|
437
|
-
return { status: "ok", entry };
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// session_resume
|
|
441
|
-
const resumed = await deps.resumeSession(frame.target);
|
|
442
|
-
if ("ambiguous" in resumed) {
|
|
443
|
-
Object.assign(entry, { state: "failure", updatedAt: deps.now(), reason: "ambiguous_target" });
|
|
444
|
-
await deps.store.write(doc);
|
|
445
|
-
await deps.audit({ ...baseAudit, event: "failure", reason: "ambiguous_target" });
|
|
446
|
-
return {
|
|
447
|
-
status: "error",
|
|
448
|
-
reason: "ambiguous_target",
|
|
449
|
-
message: "multiple sessions match; pick one",
|
|
450
|
-
candidates: resumed.ambiguous,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
if ("notFound" in resumed) {
|
|
454
|
-
Object.assign(entry, { state: "failure", updatedAt: deps.now(), reason: "not_found" });
|
|
455
|
-
await deps.store.write(doc);
|
|
456
|
-
await deps.audit({ ...baseAudit, event: "failure", reason: "not_found" });
|
|
457
|
-
return { status: "error", reason: "not_found", message: "no matching session found" };
|
|
458
|
-
}
|
|
459
|
-
Object.assign(entry, {
|
|
460
|
-
state: "success",
|
|
461
|
-
updatedAt: deps.now(),
|
|
462
|
-
sessionId: resumed.sessionId,
|
|
463
|
-
tmuxSession: resumed.tmuxSession,
|
|
464
|
-
endpointUrl: resumed.endpointUrl,
|
|
465
|
-
resumeMode: resumed.mode,
|
|
466
|
-
});
|
|
467
|
-
await deps.store.write(doc);
|
|
468
|
-
await deps.audit({ ...baseAudit, event: "success" });
|
|
469
|
-
return { status: "ok", entry, mode: resumed.mode };
|
|
470
|
-
} catch (err) {
|
|
471
|
-
if (typeof err === "object" && err !== null && "startupPromptRef" in err) {
|
|
472
|
-
const failedPromptRef = Reflect.get(err, "startupPromptRef");
|
|
473
|
-
if (typeof failedPromptRef === "string") entry.startupPromptRef = failedPromptRef;
|
|
474
|
-
}
|
|
475
|
-
// A side effect may have occurred; do not repeat it automatically. Mark
|
|
476
|
-
// terminal-uncertain so a retry reconciles instead of duplicating work.
|
|
477
|
-
let reason: LifecycleErrorReason = "terminal_uncertain";
|
|
478
|
-
if (frame.type === "session_create") reason = "spawn_failed";
|
|
479
|
-
if (frame.type === "session_close") reason = "close_refused";
|
|
480
|
-
Object.assign(entry, {
|
|
481
|
-
state: "terminal_uncertain",
|
|
482
|
-
updatedAt: deps.now(),
|
|
483
|
-
reason,
|
|
484
|
-
});
|
|
485
|
-
await deps.store.write(doc);
|
|
486
|
-
await deps.audit({ ...baseAudit, event: "terminal_uncertain", reason });
|
|
487
|
-
return { status: "error", reason: "terminal_uncertain", message: `${frame.type} effect failed: ${String(err)}` };
|
|
488
|
-
}
|
|
489
|
-
}
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Reference CLI for the Gajae-Code SDK Telegram client.
|
|
4
|
-
*
|
|
5
|
-
* Bridges a running GJC session's notification endpoint to a Telegram bot so you
|
|
6
|
-
* can answer asks / see idle pings from your phone — no RPC mode required. This
|
|
7
|
-
* is an EXAMPLE/template (the SDK contract is in `docs/sdk.md`);
|
|
8
|
-
* Discord/Slack clients are written the same way.
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* bun run packages/coding-agent/src/sdk/bus/telegram-cli.ts \
|
|
12
|
-
* --bot-token <token> [--chat-id <id>] [--endpoint-file <path> | --session-id <id>] [--repo <dir>]
|
|
13
|
-
*
|
|
14
|
-
* Env fallbacks: GJC_TG_BOT_TOKEN, GJC_TG_CHAT_ID.
|
|
15
|
-
* If --chat-id is omitted it is auto-resolved from getUpdates (message the bot once).
|
|
16
|
-
* If neither --endpoint-file nor --session-id is given, the newest endpoint file
|
|
17
|
-
* under <repo>/.gjc/state/sdk/ is used.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import * as fs from "node:fs";
|
|
21
|
-
import * as path from "node:path";
|
|
22
|
-
import { $credentialEnv, getAgentDir } from "@gajae-code/utils";
|
|
23
|
-
import { tokenFingerprint } from "./config";
|
|
24
|
-
import { isFreshLiveOwner, readOwnerFreshnessSnapshot } from "./telegram-daemon";
|
|
25
|
-
import { runTelegramReferenceClient, type TelegramNotificationSound } from "./telegram-reference";
|
|
26
|
-
|
|
27
|
-
interface CliArgs {
|
|
28
|
-
botToken?: string;
|
|
29
|
-
chatId?: string;
|
|
30
|
-
endpointFile?: string;
|
|
31
|
-
sessionId?: string;
|
|
32
|
-
repo: string;
|
|
33
|
-
apiBase?: string;
|
|
34
|
-
sound: TelegramNotificationSound;
|
|
35
|
-
force: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
class CliUsageError extends Error {}
|
|
39
|
-
|
|
40
|
-
export function parseArgs(argv: string[]): CliArgs {
|
|
41
|
-
const args: CliArgs = { repo: process.cwd(), sound: "all", force: false };
|
|
42
|
-
for (let i = 0; i < argv.length; i++) {
|
|
43
|
-
const next = () => argv[++i];
|
|
44
|
-
switch (argv[i]) {
|
|
45
|
-
case "--bot-token":
|
|
46
|
-
args.botToken = next();
|
|
47
|
-
break;
|
|
48
|
-
case "--chat-id":
|
|
49
|
-
args.chatId = next();
|
|
50
|
-
break;
|
|
51
|
-
case "--endpoint-file":
|
|
52
|
-
args.endpointFile = next();
|
|
53
|
-
break;
|
|
54
|
-
case "--session-id":
|
|
55
|
-
args.sessionId = next();
|
|
56
|
-
break;
|
|
57
|
-
case "--repo":
|
|
58
|
-
args.repo = next() ?? process.cwd();
|
|
59
|
-
break;
|
|
60
|
-
case "--api-base":
|
|
61
|
-
args.apiBase = next();
|
|
62
|
-
break;
|
|
63
|
-
case "--sound": {
|
|
64
|
-
const sound = next();
|
|
65
|
-
if (sound !== "all" && sound !== "important" && sound !== "none") {
|
|
66
|
-
throw new CliUsageError("--sound must be all, important, or none");
|
|
67
|
-
}
|
|
68
|
-
args.sound = sound;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
case "--force":
|
|
72
|
-
args.force = true;
|
|
73
|
-
break;
|
|
74
|
-
case "-h":
|
|
75
|
-
case "--help":
|
|
76
|
-
printHelpAndExit();
|
|
77
|
-
break;
|
|
78
|
-
default:
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return args;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function printHelpAndExit(): never {
|
|
86
|
-
process.stdout.write(
|
|
87
|
-
[
|
|
88
|
-
"gjc notifications — Telegram reference client",
|
|
89
|
-
"",
|
|
90
|
-
" --bot-token <token> Telegram bot token (or env GJC_TG_BOT_TOKEN)",
|
|
91
|
-
" --chat-id <id> Target chat id (or env GJC_TG_CHAT_ID; auto-resolved if omitted)",
|
|
92
|
-
" --endpoint-file <path> Session endpoint discovery file",
|
|
93
|
-
" --session-id <id> Resolve <repo>/.gjc/state/sdk/<id>.json",
|
|
94
|
-
" --repo <dir> Repo root for endpoint discovery (default: cwd)",
|
|
95
|
-
" --api-base <url> Telegram API base (default: https://api.telegram.org)",
|
|
96
|
-
" --sound <all|important|none> Telegram notification sound (default: all)",
|
|
97
|
-
" --force Bypass active daemon guard (debug only; may cause Telegram 409 conflicts)",
|
|
98
|
-
"",
|
|
99
|
-
].join("\n"),
|
|
100
|
-
);
|
|
101
|
-
process.exit(0);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Find the most recently modified endpoint discovery file under the repo. */
|
|
105
|
-
function findLatestEndpoint(repo: string): string | undefined {
|
|
106
|
-
const dir = path.join(repo, ".gjc", "state", "sdk");
|
|
107
|
-
let entries: string[];
|
|
108
|
-
try {
|
|
109
|
-
entries = fs.readdirSync(dir).filter(f => f.endsWith(".json"));
|
|
110
|
-
} catch {
|
|
111
|
-
return undefined;
|
|
112
|
-
}
|
|
113
|
-
let best: { file: string; mtime: number } | undefined;
|
|
114
|
-
for (const f of entries) {
|
|
115
|
-
const full = path.join(dir, f);
|
|
116
|
-
const mtime = fs.statSync(full).mtimeMs;
|
|
117
|
-
if (!best || mtime > best.mtime) best = { file: full, mtime };
|
|
118
|
-
}
|
|
119
|
-
return best?.file;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function resolveChatId(botToken: string, apiBase: string): Promise<string> {
|
|
123
|
-
const api = `${apiBase}/bot${botToken}`;
|
|
124
|
-
for (let i = 0; i < 150; i++) {
|
|
125
|
-
const body = (await fetch(`${api}/getUpdates`)
|
|
126
|
-
.then(r => r.json())
|
|
127
|
-
.catch(() => ({}))) as {
|
|
128
|
-
result?: Array<Record<string, any>>;
|
|
129
|
-
};
|
|
130
|
-
for (const u of body.result ?? []) {
|
|
131
|
-
const id = u.message?.chat?.id ?? u.callback_query?.message?.chat?.id;
|
|
132
|
-
if (id !== undefined) return String(id);
|
|
133
|
-
}
|
|
134
|
-
if (i === 0) process.stderr.write("Waiting for a message to the bot to resolve the chat id...\n");
|
|
135
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
136
|
-
}
|
|
137
|
-
throw new Error("could not resolve a chat id; send the bot a message first");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function pidAlive(pid: number): boolean {
|
|
141
|
-
try {
|
|
142
|
-
process.kill(pid, 0);
|
|
143
|
-
return true;
|
|
144
|
-
} catch (err) {
|
|
145
|
-
return (err as NodeJS.ErrnoException).code === "EPERM";
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function activeDaemonOwnsToken(input: { botToken: string; chatId: string }): Promise<boolean> {
|
|
150
|
-
const snapshot = await readOwnerFreshnessSnapshot({ settings: { getAgentDir } as never });
|
|
151
|
-
const state = snapshot.state;
|
|
152
|
-
if (!state) return false;
|
|
153
|
-
return isFreshLiveOwner({
|
|
154
|
-
state,
|
|
155
|
-
now: Date.now(),
|
|
156
|
-
tokenFingerprint: tokenFingerprint(input.botToken),
|
|
157
|
-
chatId: input.chatId,
|
|
158
|
-
pidAlive,
|
|
159
|
-
effectiveHeartbeatAt: snapshot.effectiveHeartbeatAt,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function main(): Promise<void> {
|
|
164
|
-
let args: CliArgs;
|
|
165
|
-
try {
|
|
166
|
-
args = parseArgs(process.argv.slice(2));
|
|
167
|
-
} catch (error) {
|
|
168
|
-
if (error instanceof CliUsageError) {
|
|
169
|
-
process.stderr.write(`error: ${error.message}\n`);
|
|
170
|
-
process.exit(2);
|
|
171
|
-
}
|
|
172
|
-
throw error;
|
|
173
|
-
}
|
|
174
|
-
const apiBase = args.apiBase ?? "https://api.telegram.org";
|
|
175
|
-
// Trusted sources only: this token is the bot the client talks to, so whatever
|
|
176
|
-
// can set it receives the session's notifications and the operator's replies.
|
|
177
|
-
// `$env` merges the caller's `cwd/.env` into `process.env`, and this client is
|
|
178
|
-
// normally run from inside a repository.
|
|
179
|
-
const botToken = args.botToken ?? $credentialEnv("GJC_TG_BOT_TOKEN");
|
|
180
|
-
if (!botToken) {
|
|
181
|
-
process.stderr.write("error: --bot-token (or GJC_TG_BOT_TOKEN) is required\n");
|
|
182
|
-
process.exit(2);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const endpointFile =
|
|
186
|
-
args.endpointFile ??
|
|
187
|
-
(args.sessionId ? path.join(args.repo, ".gjc", "state", "sdk", `${args.sessionId}.json`) : undefined) ??
|
|
188
|
-
findLatestEndpoint(args.repo);
|
|
189
|
-
if (!endpointFile || !fs.existsSync(endpointFile)) {
|
|
190
|
-
process.stderr.write(
|
|
191
|
-
`error: no endpoint file found (looked under ${args.repo}/.gjc/state/sdk). Start a session with GJC_NOTIFICATIONS=1 first.\n`,
|
|
192
|
-
);
|
|
193
|
-
process.exit(2);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const chatId = args.chatId ?? $credentialEnv("GJC_TG_CHAT_ID") ?? (await resolveChatId(botToken, apiBase));
|
|
197
|
-
if (!args.force && (await activeDaemonOwnsToken({ botToken, chatId }))) {
|
|
198
|
-
process.stderr.write(
|
|
199
|
-
"an active gjc notifications daemon already owns this bot token; running a second poller will cause Telegram 409 conflicts. Re-run with --force to override.\n",
|
|
200
|
-
);
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
process.stderr.write(`notifications: bridging ${endpointFile} <-> Telegram chat ${chatId}\n`);
|
|
204
|
-
await runTelegramReferenceClient({ botToken, chatId, endpointFile, apiBase, sound: args.sound });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (import.meta.main) {
|
|
208
|
-
main().catch(e => {
|
|
209
|
-
process.stderr.write(`fatal: ${String(e)}\n`);
|
|
210
|
-
process.exit(1);
|
|
211
|
-
});
|
|
212
|
-
}
|