@0xmaxma/claude-gateway 1.8.13 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1605
- package/config.template.json +7 -1
- package/dist/agent/runner.d.ts +394 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +867 -38
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/skill-learning/index.d.ts +6 -4
- package/dist/agent/skill-learning/index.d.ts.map +1 -1
- package/dist/agent/skill-learning/index.js +3 -3
- package/dist/agent/skill-learning/index.js.map +1 -1
- package/dist/agent/skill-learning/notifier.d.ts +3 -4
- package/dist/agent/skill-learning/notifier.d.ts.map +1 -1
- package/dist/agent/skill-learning/notifier.js +15 -13
- package/dist/agent/skill-learning/notifier.js.map +1 -1
- package/dist/agent/skill-learning/reviewer.d.ts.map +1 -1
- package/dist/agent/skill-learning/reviewer.js +3 -1
- package/dist/agent/skill-learning/reviewer.js.map +1 -1
- package/dist/api/connectors-router.d.ts.map +1 -1
- package/dist/api/connectors-router.js +110 -4
- package/dist/api/connectors-router.js.map +1 -1
- package/dist/api/gateway-router.d.ts +1 -0
- package/dist/api/gateway-router.d.ts.map +1 -1
- package/dist/api/gateway-router.js +13 -1
- package/dist/api/gateway-router.js.map +1 -1
- package/dist/api/line-webhook-router.d.ts +2 -2
- package/dist/api/line-webhook-router.d.ts.map +1 -1
- package/dist/api/line-webhook-router.js +264 -230
- package/dist/api/line-webhook-router.js.map +1 -1
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +372 -11
- package/dist/api/router.js.map +1 -1
- package/dist/api/share-router.d.ts.map +1 -1
- package/dist/api/share-router.js +29 -10
- package/dist/api/share-router.js.map +1 -1
- package/dist/api/slack-client.d.ts +2 -0
- package/dist/api/slack-client.d.ts.map +1 -1
- package/dist/api/slack-client.js +6 -2
- package/dist/api/slack-client.js.map +1 -1
- package/dist/api/slack-webhook-router.d.ts +3 -0
- package/dist/api/slack-webhook-router.d.ts.map +1 -1
- package/dist/api/slack-webhook-router.js +226 -131
- package/dist/api/slack-webhook-router.js.map +1 -1
- package/dist/api/voice-router.d.ts +21 -0
- package/dist/api/voice-router.d.ts.map +1 -0
- package/dist/api/voice-router.js +372 -0
- package/dist/api/voice-router.js.map +1 -0
- package/dist/api/voice-settings-router.d.ts +5 -0
- package/dist/api/voice-settings-router.d.ts.map +1 -0
- package/dist/api/voice-settings-router.js +318 -0
- package/dist/api/voice-settings-router.js.map +1 -0
- package/dist/api/wechat-access.d.ts +45 -0
- package/dist/api/wechat-access.d.ts.map +1 -0
- package/dist/api/wechat-access.js +48 -0
- package/dist/api/wechat-access.js.map +1 -0
- package/dist/apps/agent-container-migration.d.ts +11 -0
- package/dist/apps/agent-container-migration.d.ts.map +1 -0
- package/dist/apps/agent-container-migration.js +85 -0
- package/dist/apps/agent-container-migration.js.map +1 -0
- package/dist/apps/installer.d.ts +1 -1
- package/dist/apps/installer.d.ts.map +1 -1
- package/dist/apps/installer.js +2 -1
- package/dist/apps/installer.js.map +1 -1
- package/dist/cli/commands/tasks.d.ts +5 -0
- package/dist/cli/commands/tasks.d.ts.map +1 -0
- package/dist/cli/commands/tasks.js +92 -0
- package/dist/cli/commands/tasks.js.map +1 -0
- package/dist/cli/http-client.d.ts +2 -0
- package/dist/cli/http-client.d.ts.map +1 -1
- package/dist/cli/http-client.js +8 -3
- package/dist/cli/http-client.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/config-write-lock.d.ts +2 -0
- package/dist/config/config-write-lock.d.ts.map +1 -1
- package/dist/config/config-write-lock.js +5 -0
- package/dist/config/config-write-lock.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +87 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/config/migrator.d.ts.map +1 -1
- package/dist/config/migrator.js +12 -1
- package/dist/config/migrator.js.map +1 -1
- package/dist/config/watcher.d.ts.map +1 -1
- package/dist/config/watcher.js +6 -0
- package/dist/config/watcher.js.map +1 -1
- package/dist/connectors/resolve.d.ts.map +1 -1
- package/dist/connectors/resolve.js +3 -1
- package/dist/connectors/resolve.js.map +1 -1
- package/dist/connectors/types.d.ts +8 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/cron/manager.d.ts.map +1 -1
- package/dist/cron/manager.js +21 -2
- package/dist/cron/manager.js.map +1 -1
- package/dist/discord/receiver.d.ts +2 -1
- package/dist/discord/receiver.d.ts.map +1 -1
- package/dist/discord/receiver.js +5 -1
- package/dist/discord/receiver.js.map +1 -1
- package/dist/history/db.d.ts +3 -0
- package/dist/history/db.d.ts.map +1 -1
- package/dist/history/db.js +21 -1
- package/dist/history/db.js.map +1 -1
- package/dist/history/types.d.ts +12 -1
- package/dist/history/types.d.ts.map +1 -1
- package/dist/history/types.js +10 -1
- package/dist/history/types.js.map +1 -1
- package/dist/index.js +48 -9
- package/dist/index.js.map +1 -1
- package/dist/orchestration/bounded-queue.d.ts +15 -0
- package/dist/orchestration/bounded-queue.d.ts.map +1 -0
- package/dist/orchestration/bounded-queue.js +58 -0
- package/dist/orchestration/bounded-queue.js.map +1 -0
- package/dist/orchestration/bridge.d.ts +55 -0
- package/dist/orchestration/bridge.d.ts.map +1 -0
- package/dist/orchestration/bridge.js +234 -0
- package/dist/orchestration/bridge.js.map +1 -0
- package/dist/orchestration/browser-routing.d.ts +7 -0
- package/dist/orchestration/browser-routing.d.ts.map +1 -0
- package/dist/orchestration/browser-routing.js +22 -0
- package/dist/orchestration/browser-routing.js.map +1 -0
- package/dist/orchestration/capabilities.d.ts +49 -0
- package/dist/orchestration/capabilities.d.ts.map +1 -0
- package/dist/orchestration/capabilities.js +417 -0
- package/dist/orchestration/capabilities.js.map +1 -0
- package/dist/orchestration/capacity.d.ts +14 -0
- package/dist/orchestration/capacity.d.ts.map +1 -0
- package/dist/orchestration/capacity.js +52 -0
- package/dist/orchestration/capacity.js.map +1 -0
- package/dist/orchestration/channel-activity.d.ts +21 -0
- package/dist/orchestration/channel-activity.d.ts.map +1 -0
- package/dist/orchestration/channel-activity.js +72 -0
- package/dist/orchestration/channel-activity.js.map +1 -0
- package/dist/orchestration/channel-controls.d.ts +32 -0
- package/dist/orchestration/channel-controls.d.ts.map +1 -0
- package/dist/orchestration/channel-controls.js +105 -0
- package/dist/orchestration/channel-controls.js.map +1 -0
- package/dist/orchestration/channel-input-media.d.ts +11 -0
- package/dist/orchestration/channel-input-media.d.ts.map +1 -0
- package/dist/orchestration/channel-input-media.js +54 -0
- package/dist/orchestration/channel-input-media.js.map +1 -0
- package/dist/orchestration/channel-media.d.ts +4 -0
- package/dist/orchestration/channel-media.d.ts.map +1 -0
- package/dist/orchestration/channel-media.js +70 -0
- package/dist/orchestration/channel-media.js.map +1 -0
- package/dist/orchestration/channel-speech.d.ts +7 -0
- package/dist/orchestration/channel-speech.d.ts.map +1 -0
- package/dist/orchestration/channel-speech.js +65 -0
- package/dist/orchestration/channel-speech.js.map +1 -0
- package/dist/orchestration/cli-skills.d.ts +12 -0
- package/dist/orchestration/cli-skills.d.ts.map +1 -0
- package/dist/orchestration/cli-skills.js +132 -0
- package/dist/orchestration/cli-skills.js.map +1 -0
- package/dist/orchestration/config.d.ts +256 -0
- package/dist/orchestration/config.d.ts.map +1 -0
- package/dist/orchestration/config.js +110 -0
- package/dist/orchestration/config.js.map +1 -0
- package/dist/orchestration/container.d.ts +21 -0
- package/dist/orchestration/container.d.ts.map +1 -0
- package/dist/orchestration/container.js +195 -0
- package/dist/orchestration/container.js.map +1 -0
- package/dist/orchestration/control-delivery.d.ts +5 -0
- package/dist/orchestration/control-delivery.d.ts.map +1 -0
- package/dist/orchestration/control-delivery.js +50 -0
- package/dist/orchestration/control-delivery.js.map +1 -0
- package/dist/orchestration/conversation-intake.d.ts +32 -0
- package/dist/orchestration/conversation-intake.d.ts.map +1 -0
- package/dist/orchestration/conversation-intake.js +92 -0
- package/dist/orchestration/conversation-intake.js.map +1 -0
- package/dist/orchestration/decisions.d.ts +27 -0
- package/dist/orchestration/decisions.d.ts.map +1 -0
- package/dist/orchestration/decisions.js +148 -0
- package/dist/orchestration/decisions.js.map +1 -0
- package/dist/orchestration/delivery.d.ts +30 -0
- package/dist/orchestration/delivery.d.ts.map +1 -0
- package/dist/orchestration/delivery.js +219 -0
- package/dist/orchestration/delivery.js.map +1 -0
- package/dist/orchestration/display-stream.d.ts +4 -0
- package/dist/orchestration/display-stream.d.ts.map +1 -0
- package/dist/orchestration/display-stream.js +77 -0
- package/dist/orchestration/display-stream.js.map +1 -0
- package/dist/orchestration/events.d.ts +30 -0
- package/dist/orchestration/events.d.ts.map +1 -0
- package/dist/orchestration/events.js +65 -0
- package/dist/orchestration/events.js.map +1 -0
- package/dist/orchestration/execution-observation.d.ts +38 -0
- package/dist/orchestration/execution-observation.d.ts.map +1 -0
- package/dist/orchestration/execution-observation.js +25 -0
- package/dist/orchestration/execution-observation.js.map +1 -0
- package/dist/orchestration/file-delivery.d.ts +16 -0
- package/dist/orchestration/file-delivery.d.ts.map +1 -0
- package/dist/orchestration/file-delivery.js +139 -0
- package/dist/orchestration/file-delivery.js.map +1 -0
- package/dist/orchestration/gateway-config.d.ts +17 -0
- package/dist/orchestration/gateway-config.d.ts.map +1 -0
- package/dist/orchestration/gateway-config.js +128 -0
- package/dist/orchestration/gateway-config.js.map +1 -0
- package/dist/orchestration/history.d.ts +13 -0
- package/dist/orchestration/history.d.ts.map +1 -0
- package/dist/orchestration/history.js +48 -0
- package/dist/orchestration/history.js.map +1 -0
- package/dist/orchestration/identity.d.ts +6 -0
- package/dist/orchestration/identity.d.ts.map +1 -0
- package/dist/orchestration/identity.js +13 -0
- package/dist/orchestration/identity.js.map +1 -0
- package/dist/orchestration/inference-errors.d.ts +3 -0
- package/dist/orchestration/inference-errors.d.ts.map +1 -0
- package/dist/orchestration/inference-errors.js +16 -0
- package/dist/orchestration/inference-errors.js.map +1 -0
- package/dist/orchestration/input-images.d.ts +12 -0
- package/dist/orchestration/input-images.d.ts.map +1 -0
- package/dist/orchestration/input-images.js +66 -0
- package/dist/orchestration/input-images.js.map +1 -0
- package/dist/orchestration/instance-lock.d.ts +5 -0
- package/dist/orchestration/instance-lock.d.ts.map +1 -0
- package/dist/orchestration/instance-lock.js +33 -0
- package/dist/orchestration/instance-lock.js.map +1 -0
- package/dist/orchestration/line-loading.d.ts +21 -0
- package/dist/orchestration/line-loading.d.ts.map +1 -0
- package/dist/orchestration/line-loading.js +89 -0
- package/dist/orchestration/line-loading.js.map +1 -0
- package/dist/orchestration/media.d.ts +3 -0
- package/dist/orchestration/media.d.ts.map +1 -0
- package/dist/orchestration/media.js +46 -0
- package/dist/orchestration/media.js.map +1 -0
- package/dist/orchestration/migrations/schema.d.ts +3 -0
- package/dist/orchestration/migrations/schema.d.ts.map +1 -0
- package/dist/orchestration/migrations/schema.js +80 -0
- package/dist/orchestration/migrations/schema.js.map +1 -0
- package/dist/orchestration/notification-mailbox.d.ts +5 -0
- package/dist/orchestration/notification-mailbox.d.ts.map +1 -0
- package/dist/orchestration/notification-mailbox.js +29 -0
- package/dist/orchestration/notification-mailbox.js.map +1 -0
- package/dist/orchestration/process-activity.d.ts +23 -0
- package/dist/orchestration/process-activity.d.ts.map +1 -0
- package/dist/orchestration/process-activity.js +95 -0
- package/dist/orchestration/process-activity.js.map +1 -0
- package/dist/orchestration/process-supervisor.d.ts +15 -0
- package/dist/orchestration/process-supervisor.d.ts.map +1 -0
- package/dist/orchestration/process-supervisor.js +92 -0
- package/dist/orchestration/process-supervisor.js.map +1 -0
- package/dist/orchestration/process-turn.d.ts +35 -0
- package/dist/orchestration/process-turn.d.ts.map +1 -0
- package/dist/orchestration/process-turn.js +246 -0
- package/dist/orchestration/process-turn.js.map +1 -0
- package/dist/orchestration/progress-review.d.ts +28 -0
- package/dist/orchestration/progress-review.d.ts.map +1 -0
- package/dist/orchestration/progress-review.js +48 -0
- package/dist/orchestration/progress-review.js.map +1 -0
- package/dist/orchestration/recovery.d.ts +8 -0
- package/dist/orchestration/recovery.d.ts.map +1 -0
- package/dist/orchestration/recovery.js +35 -0
- package/dist/orchestration/recovery.js.map +1 -0
- package/dist/orchestration/reply-context.d.ts +7 -0
- package/dist/orchestration/reply-context.d.ts.map +1 -0
- package/dist/orchestration/reply-context.js +54 -0
- package/dist/orchestration/reply-context.js.map +1 -0
- package/dist/orchestration/runtime.d.ts +232 -0
- package/dist/orchestration/runtime.d.ts.map +1 -0
- package/dist/orchestration/runtime.js +962 -0
- package/dist/orchestration/runtime.js.map +1 -0
- package/dist/orchestration/skills.d.ts +17 -0
- package/dist/orchestration/skills.d.ts.map +1 -0
- package/dist/orchestration/skills.js +32 -0
- package/dist/orchestration/skills.js.map +1 -0
- package/dist/orchestration/source-policy.d.ts +6 -0
- package/dist/orchestration/source-policy.d.ts.map +1 -0
- package/dist/orchestration/source-policy.js +16 -0
- package/dist/orchestration/source-policy.js.map +1 -0
- package/dist/orchestration/speech.d.ts +24 -0
- package/dist/orchestration/speech.d.ts.map +1 -0
- package/dist/orchestration/speech.js +61 -0
- package/dist/orchestration/speech.js.map +1 -0
- package/dist/orchestration/stop-controls.d.ts +30 -0
- package/dist/orchestration/stop-controls.d.ts.map +1 -0
- package/dist/orchestration/stop-controls.js +79 -0
- package/dist/orchestration/stop-controls.js.map +1 -0
- package/dist/orchestration/store.d.ts +86 -0
- package/dist/orchestration/store.d.ts.map +1 -0
- package/dist/orchestration/store.js +275 -0
- package/dist/orchestration/store.js.map +1 -0
- package/dist/orchestration/task-controls.d.ts +130 -0
- package/dist/orchestration/task-controls.d.ts.map +1 -0
- package/dist/orchestration/task-controls.js +55 -0
- package/dist/orchestration/task-controls.js.map +1 -0
- package/dist/orchestration/task-files.d.ts +29 -0
- package/dist/orchestration/task-files.d.ts.map +1 -0
- package/dist/orchestration/task-files.js +200 -0
- package/dist/orchestration/task-files.js.map +1 -0
- package/dist/orchestration/task-report.d.ts +7 -0
- package/dist/orchestration/task-report.d.ts.map +1 -0
- package/dist/orchestration/task-report.js +33 -0
- package/dist/orchestration/task-report.js.map +1 -0
- package/dist/orchestration/tasks/checkpoint-hook.d.ts +19 -0
- package/dist/orchestration/tasks/checkpoint-hook.d.ts.map +1 -0
- package/dist/orchestration/tasks/checkpoint-hook.js +49 -0
- package/dist/orchestration/tasks/checkpoint-hook.js.map +1 -0
- package/dist/orchestration/tasks/cleanup.d.ts +18 -0
- package/dist/orchestration/tasks/cleanup.d.ts.map +1 -0
- package/dist/orchestration/tasks/cleanup.js +113 -0
- package/dist/orchestration/tasks/cleanup.js.map +1 -0
- package/dist/orchestration/tasks/driver.d.ts +23 -0
- package/dist/orchestration/tasks/driver.d.ts.map +1 -0
- package/dist/orchestration/tasks/driver.js +209 -0
- package/dist/orchestration/tasks/driver.js.map +1 -0
- package/dist/orchestration/tasks/failure.d.ts +5 -0
- package/dist/orchestration/tasks/failure.d.ts.map +1 -0
- package/dist/orchestration/tasks/failure.js +17 -0
- package/dist/orchestration/tasks/failure.js.map +1 -0
- package/dist/orchestration/tasks/pool.d.ts +18 -0
- package/dist/orchestration/tasks/pool.d.ts.map +1 -0
- package/dist/orchestration/tasks/pool.js +73 -0
- package/dist/orchestration/tasks/pool.js.map +1 -0
- package/dist/orchestration/tasks/scheduler.d.ts +38 -0
- package/dist/orchestration/tasks/scheduler.d.ts.map +1 -0
- package/dist/orchestration/tasks/scheduler.js +160 -0
- package/dist/orchestration/tasks/scheduler.js.map +1 -0
- package/dist/orchestration/tasks/service.d.ts +109 -0
- package/dist/orchestration/tasks/service.d.ts.map +1 -0
- package/dist/orchestration/tasks/service.js +697 -0
- package/dist/orchestration/tasks/service.js.map +1 -0
- package/dist/orchestration/tasks/shared-workspace.d.ts +25 -0
- package/dist/orchestration/tasks/shared-workspace.d.ts.map +1 -0
- package/dist/orchestration/tasks/shared-workspace.js +162 -0
- package/dist/orchestration/tasks/shared-workspace.js.map +1 -0
- package/dist/orchestration/tasks/task-directive.d.ts +6 -0
- package/dist/orchestration/tasks/task-directive.d.ts.map +1 -0
- package/dist/orchestration/tasks/task-directive.js +41 -0
- package/dist/orchestration/tasks/task-directive.js.map +1 -0
- package/dist/orchestration/tasks/timing.d.ts +15 -0
- package/dist/orchestration/tasks/timing.d.ts.map +1 -0
- package/dist/orchestration/tasks/timing.js +48 -0
- package/dist/orchestration/tasks/timing.js.map +1 -0
- package/dist/orchestration/tasks/tool-repetition.d.ts +3 -0
- package/dist/orchestration/tasks/tool-repetition.d.ts.map +1 -0
- package/dist/orchestration/tasks/tool-repetition.js +30 -0
- package/dist/orchestration/tasks/tool-repetition.js.map +1 -0
- package/dist/orchestration/tasks/workspace.d.ts +24 -0
- package/dist/orchestration/tasks/workspace.d.ts.map +1 -0
- package/dist/orchestration/tasks/workspace.js +147 -0
- package/dist/orchestration/tasks/workspace.js.map +1 -0
- package/dist/orchestration/telegram-speech.d.ts +17 -0
- package/dist/orchestration/telegram-speech.d.ts.map +1 -0
- package/dist/orchestration/telegram-speech.js +55 -0
- package/dist/orchestration/telegram-speech.js.map +1 -0
- package/dist/orchestration/telegram-tool-status.d.ts +23 -0
- package/dist/orchestration/telegram-tool-status.d.ts.map +1 -0
- package/dist/orchestration/telegram-tool-status.js +207 -0
- package/dist/orchestration/telegram-tool-status.js.map +1 -0
- package/dist/orchestration/telegram-voices.d.ts +42 -0
- package/dist/orchestration/telegram-voices.d.ts.map +1 -0
- package/dist/orchestration/telegram-voices.js +70 -0
- package/dist/orchestration/telegram-voices.js.map +1 -0
- package/dist/orchestration/tool-activity.d.ts +10 -0
- package/dist/orchestration/tool-activity.d.ts.map +1 -0
- package/dist/orchestration/tool-activity.js +38 -0
- package/dist/orchestration/tool-activity.js.map +1 -0
- package/dist/orchestration/types.d.ts +177 -0
- package/dist/orchestration/types.d.ts.map +1 -0
- package/dist/orchestration/types.js +13 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/orchestration/voice-reply-policy.d.ts +8 -0
- package/dist/orchestration/voice-reply-policy.d.ts.map +1 -0
- package/dist/orchestration/voice-reply-policy.js +46 -0
- package/dist/orchestration/voice-reply-policy.js.map +1 -0
- package/dist/orchestration/worker-shares.d.ts +8 -0
- package/dist/orchestration/worker-shares.d.ts.map +1 -0
- package/dist/orchestration/worker-shares.js +59 -0
- package/dist/orchestration/worker-shares.js.map +1 -0
- package/dist/orchestration/workflow.d.ts +121 -0
- package/dist/orchestration/workflow.d.ts.map +1 -0
- package/dist/orchestration/workflow.js +69 -0
- package/dist/orchestration/workflow.js.map +1 -0
- package/dist/session/input-image.d.ts +10 -0
- package/dist/session/input-image.d.ts.map +1 -0
- package/dist/session/input-image.js +3 -0
- package/dist/session/input-image.js.map +1 -0
- package/dist/session/process.d.ts +15 -3
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +144 -35
- package/dist/session/process.js.map +1 -1
- package/dist/session/runtime-profile.d.ts +36 -0
- package/dist/session/runtime-profile.d.ts.map +1 -0
- package/dist/session/runtime-profile.js +63 -0
- package/dist/session/runtime-profile.js.map +1 -0
- package/dist/session/store.d.ts.map +1 -1
- package/dist/session/store.js +23 -1
- package/dist/session/store.js.map +1 -1
- package/dist/share/share-store.d.ts +3 -1
- package/dist/share/share-store.d.ts.map +1 -1
- package/dist/share/share-store.js +13 -2
- package/dist/share/share-store.js.map +1 -1
- package/dist/shared/line-request-order.d.ts +2 -0
- package/dist/shared/line-request-order.d.ts.map +1 -0
- package/dist/shared/line-request-order.js +16 -0
- package/dist/shared/line-request-order.js.map +1 -0
- package/dist/shared/task-elapsed.d.ts +3 -0
- package/dist/shared/task-elapsed.d.ts.map +1 -0
- package/dist/shared/task-elapsed.js +12 -0
- package/dist/shared/task-elapsed.js.map +1 -0
- package/dist/shared/task-updated-time.d.ts +3 -0
- package/dist/shared/task-updated-time.d.ts.map +1 -0
- package/dist/shared/task-updated-time.js +15 -0
- package/dist/shared/task-updated-time.js.map +1 -0
- package/dist/skills/loader.d.ts +2 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js.map +1 -1
- package/dist/telegram/chunks.d.ts +19 -0
- package/dist/telegram/chunks.d.ts.map +1 -0
- package/dist/telegram/chunks.js +129 -0
- package/dist/telegram/chunks.js.map +1 -0
- package/dist/telegram/markdown.d.ts.map +1 -1
- package/dist/telegram/markdown.js +18 -3
- package/dist/telegram/markdown.js.map +1 -1
- package/dist/telegram/receiver.d.ts +2 -1
- package/dist/telegram/receiver.d.ts.map +1 -1
- package/dist/telegram/receiver.js +6 -1
- package/dist/telegram/receiver.js.map +1 -1
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/web-ui.d.ts.map +1 -1
- package/dist/ui/web-ui.js +31 -5
- package/dist/ui/web-ui.js.map +1 -1
- package/dist/voice/diagnostics.d.ts +29 -0
- package/dist/voice/diagnostics.d.ts.map +1 -0
- package/dist/voice/diagnostics.js +37 -0
- package/dist/voice/diagnostics.js.map +1 -0
- package/dist/voice/errors.d.ts +19 -0
- package/dist/voice/errors.d.ts.map +1 -0
- package/dist/voice/errors.js +133 -0
- package/dist/voice/errors.js.map +1 -0
- package/dist/voice/line-audio.d.ts +4 -0
- package/dist/voice/line-audio.d.ts.map +1 -0
- package/dist/voice/line-audio.js +27 -0
- package/dist/voice/line-audio.js.map +1 -0
- package/dist/voice/managed-quota.d.ts +5 -0
- package/dist/voice/managed-quota.d.ts.map +1 -0
- package/dist/voice/managed-quota.js +35 -0
- package/dist/voice/managed-quota.js.map +1 -0
- package/dist/voice/mp3.d.ts +3 -0
- package/dist/voice/mp3.d.ts.map +1 -0
- package/dist/voice/mp3.js +30 -0
- package/dist/voice/mp3.js.map +1 -0
- package/dist/voice/notes.d.ts +9 -0
- package/dist/voice/notes.d.ts.map +1 -0
- package/dist/voice/notes.js +68 -0
- package/dist/voice/notes.js.map +1 -0
- package/dist/voice/playback.d.ts +22 -0
- package/dist/voice/playback.d.ts.map +1 -0
- package/dist/voice/playback.js +52 -0
- package/dist/voice/playback.js.map +1 -0
- package/dist/voice/preview.d.ts +6 -0
- package/dist/voice/preview.d.ts.map +1 -0
- package/dist/voice/preview.js +44 -0
- package/dist/voice/preview.js.map +1 -0
- package/dist/voice/protocol.d.ts +10 -0
- package/dist/voice/protocol.d.ts.map +1 -0
- package/dist/voice/protocol.js +30 -0
- package/dist/voice/protocol.js.map +1 -0
- package/dist/voice/providers/cartesia-tts.d.ts +26 -0
- package/dist/voice/providers/cartesia-tts.d.ts.map +1 -0
- package/dist/voice/providers/cartesia-tts.js +95 -0
- package/dist/voice/providers/cartesia-tts.js.map +1 -0
- package/dist/voice/providers/deepgram-stt.d.ts +20 -0
- package/dist/voice/providers/deepgram-stt.d.ts.map +1 -0
- package/dist/voice/providers/deepgram-stt.js +66 -0
- package/dist/voice/providers/deepgram-stt.js.map +1 -0
- package/dist/voice/providers/elevenlabs-stt.d.ts +20 -0
- package/dist/voice/providers/elevenlabs-stt.d.ts.map +1 -0
- package/dist/voice/providers/elevenlabs-stt.js +92 -0
- package/dist/voice/providers/elevenlabs-stt.js.map +1 -0
- package/dist/voice/providers/elevenlabs-tts.d.ts +26 -0
- package/dist/voice/providers/elevenlabs-tts.d.ts.map +1 -0
- package/dist/voice/providers/elevenlabs-tts.js +82 -0
- package/dist/voice/providers/elevenlabs-tts.js.map +1 -0
- package/dist/voice/providers/encoded-audio-tts.d.ts +21 -0
- package/dist/voice/providers/encoded-audio-tts.d.ts.map +1 -0
- package/dist/voice/providers/encoded-audio-tts.js +175 -0
- package/dist/voice/providers/encoded-audio-tts.js.map +1 -0
- package/dist/voice/providers/fake.d.ts +39 -0
- package/dist/voice/providers/fake.d.ts.map +1 -0
- package/dist/voice/providers/fake.js +41 -0
- package/dist/voice/providers/fake.js.map +1 -0
- package/dist/voice/providers/gemini-live-stt.d.ts +21 -0
- package/dist/voice/providers/gemini-live-stt.d.ts.map +1 -0
- package/dist/voice/providers/gemini-live-stt.js +158 -0
- package/dist/voice/providers/gemini-live-stt.js.map +1 -0
- package/dist/voice/providers/gemini.d.ts +60 -0
- package/dist/voice/providers/gemini.d.ts.map +1 -0
- package/dist/voice/providers/gemini.js +124 -0
- package/dist/voice/providers/gemini.js.map +1 -0
- package/dist/voice/providers/model-ref.d.ts +9 -0
- package/dist/voice/providers/model-ref.d.ts.map +1 -0
- package/dist/voice/providers/model-ref.js +31 -0
- package/dist/voice/providers/model-ref.js.map +1 -0
- package/dist/voice/providers/openrouter.d.ts +39 -0
- package/dist/voice/providers/openrouter.d.ts.map +1 -0
- package/dist/voice/providers/openrouter.js +154 -0
- package/dist/voice/providers/openrouter.js.map +1 -0
- package/dist/voice/providers/paxalabs-stt.d.ts +12 -0
- package/dist/voice/providers/paxalabs-stt.d.ts.map +1 -0
- package/dist/voice/providers/paxalabs-stt.js +41 -0
- package/dist/voice/providers/paxalabs-stt.js.map +1 -0
- package/dist/voice/providers/paxalabs-tts.d.ts +23 -0
- package/dist/voice/providers/paxalabs-tts.d.ts.map +1 -0
- package/dist/voice/providers/paxalabs-tts.js +80 -0
- package/dist/voice/providers/paxalabs-tts.js.map +1 -0
- package/dist/voice/providers/recorded-stt.d.ts +34 -0
- package/dist/voice/providers/recorded-stt.d.ts.map +1 -0
- package/dist/voice/providers/recorded-stt.js +59 -0
- package/dist/voice/providers/recorded-stt.js.map +1 -0
- package/dist/voice/providers/registry.d.ts +10 -0
- package/dist/voice/providers/registry.d.ts.map +1 -0
- package/dist/voice/providers/registry.js +77 -0
- package/dist/voice/providers/registry.js.map +1 -0
- package/dist/voice/providers/socket.d.ts +10 -0
- package/dist/voice/providers/socket.d.ts.map +1 -0
- package/dist/voice/providers/socket.js +91 -0
- package/dist/voice/providers/socket.js.map +1 -0
- package/dist/voice/providers/upstream.d.ts +9 -0
- package/dist/voice/providers/upstream.d.ts.map +1 -0
- package/dist/voice/providers/upstream.js +51 -0
- package/dist/voice/providers/upstream.js.map +1 -0
- package/dist/voice/providers/voice-catalog.d.ts +17 -0
- package/dist/voice/providers/voice-catalog.d.ts.map +1 -0
- package/dist/voice/providers/voice-catalog.js +167 -0
- package/dist/voice/providers/voice-catalog.js.map +1 -0
- package/dist/voice/queue.d.ts +2 -0
- package/dist/voice/queue.d.ts.map +1 -0
- package/dist/voice/queue.js +6 -0
- package/dist/voice/queue.js.map +1 -0
- package/dist/voice/session.d.ts +86 -0
- package/dist/voice/session.d.ts.map +1 -0
- package/dist/voice/session.js +464 -0
- package/dist/voice/session.js.map +1 -0
- package/dist/voice/turn-manager.d.ts +27 -0
- package/dist/voice/turn-manager.d.ts.map +1 -0
- package/dist/voice/turn-manager.js +75 -0
- package/dist/voice/turn-manager.js.map +1 -0
- package/dist/voice/types.d.ts +78 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +18 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/wav.d.ts +2 -0
- package/dist/voice/wav.d.ts.map +1 -0
- package/dist/voice/wav.js +21 -0
- package/dist/voice/wav.js.map +1 -0
- package/dist/wechat/ilink-client.d.ts +207 -0
- package/dist/wechat/ilink-client.d.ts.map +1 -0
- package/dist/wechat/ilink-client.js +660 -0
- package/dist/wechat/ilink-client.js.map +1 -0
- package/dist/wechat/manager.d.ts +93 -0
- package/dist/wechat/manager.d.ts.map +1 -0
- package/dist/wechat/manager.js +409 -0
- package/dist/wechat/manager.js.map +1 -0
- package/dist/whatsapp/manager.d.ts.map +1 -1
- package/dist/whatsapp/manager.js +51 -1
- package/dist/whatsapp/manager.js.map +1 -1
- package/mcp/capability-catalog.ts +206 -0
- package/mcp/modules.ts +56 -0
- package/mcp/server.ts +18 -35
- package/mcp/tools/browser/module.ts +1 -1
- package/mcp/tools/browser/skills/open-browser/SKILL.md +5 -3
- package/mcp/tools/discord/commands.ts +17 -1
- package/mcp/tools/discord/inbound.ts +11 -0
- package/mcp/tools/discord/module.ts +42 -2
- package/mcp/tools/discord/receiver-server.ts +10 -0
- package/mcp/tools/discord/types.ts +4 -1
- package/mcp/tools/image/module.ts +3 -1
- package/mcp/tools/receiver-spool.test.ts +28 -0
- package/mcp/tools/receiver-spool.ts +56 -0
- package/mcp/tools/share-file/module.ts +9 -1
- package/mcp/tools/shared/share-client.ts +19 -0
- package/mcp/tools/tasks/module.ts +35 -0
- package/mcp/tools/telegram/media-group.ts +21 -0
- package/mcp/tools/telegram/receiver-server.ts +232 -11
- package/mcp/tools/telegram/selection-confirmation.ts +20 -0
- package/mcp/tools/telegram/task-browser.ts +117 -0
- package/mcp/tools/telegram/task-detail.ts +36 -0
- package/mcp/tools/telegram/task-elapsed.ts +1 -0
- package/mcp/tools/telegram/typing.ts +6 -116
- package/mcp/tools/video/module.ts +1 -1
- package/mcp/tools/wechat/client.ts +49 -0
- package/mcp/tools/wechat/module.ts +88 -0
- package/mcp/types.ts +5 -0
- package/package.json +2 -1
- package/resource/claude_gateway.jpeg +0 -0
- package/scripts/orchestration/bench-store.cjs +30 -0
- package/scripts/orchestration/inspect.cjs +16 -0
- package/scripts/orchestration/probe-profile.cjs +67 -0
- package/scripts/orchestration/reconcile.cjs +21 -0
- package/scripts/orchestration/smoke-elevenlabs.cjs +90 -0
- package/scripts/orchestration/smoke-media.cjs +70 -0
- package/scripts/orchestration/smoke-text.cjs +101 -0
- package/scripts/orchestration/smoke-vision.cjs +49 -0
|
@@ -25,6 +25,13 @@ import { z } from 'zod'
|
|
|
25
25
|
import { Bot, GrammyError, InlineKeyboard, InputFile, type Context } from 'grammy'
|
|
26
26
|
import type { ReactionTypeEmoji } from 'grammy/types'
|
|
27
27
|
import { randomBytes } from 'crypto'
|
|
28
|
+
import { confirmSelection, voiceModeConfirmation } from './selection-confirmation'
|
|
29
|
+
import { LiveTaskBrowser, type TaskBrowserMessage } from './task-browser'
|
|
30
|
+
import { ReceiverSpool } from '../receiver-spool'
|
|
31
|
+
const orchestrationSpool = process.env.GATEWAY_ORCHESTRATION_INGRESS_DIR && process.env.CLAUDE_CHANNEL_CALLBACK
|
|
32
|
+
? new ReceiverSpool(process.env.GATEWAY_ORCHESTRATION_INGRESS_DIR, process.env.CLAUDE_CHANNEL_CALLBACK, fetch, Math.max(250,Math.min(10000,Number(process.env.GATEWAY_MEDIA_GROUP_WAIT_MS)||2000))) : undefined
|
|
33
|
+
const ORCHESTRATION_ENABLED = process.env.GATEWAY_ORCHESTRATION_ENABLED === 'true' || Boolean(orchestrationSpool)
|
|
34
|
+
const INTERACTIVE_CLI_ENABLED = process.env.GATEWAY_INTERACTIVE_CLI_ENABLED === 'true' && !ORCHESTRATION_ENABLED
|
|
28
35
|
import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync, renameSync, realpathSync, chmodSync, existsSync } from 'fs'
|
|
29
36
|
import { homedir } from 'os'
|
|
30
37
|
import { join, extname, sep } from 'path'
|
|
@@ -35,6 +42,7 @@ import { extractRepliedAttachment, safeName } from './reply-attachment'
|
|
|
35
42
|
// so a src/ import crashes this bun-run receiver on installed packages (the bug
|
|
36
43
|
// that silenced every bot on systemd installs). Enforced by
|
|
37
44
|
// tests/unit/mcp-no-src-imports.test.ts.
|
|
45
|
+
import { formatTaskDetail, type TaskDetail } from './task-detail'
|
|
38
46
|
import { formatTurnIncident, type TurnIncident } from '../../../dist/agent/turn-trace.js'
|
|
39
47
|
import { createIncidentStore } from '../../../dist/agent/incident-store.js'
|
|
40
48
|
import type { RecoveryOutcome } from '../../../dist/agent/incident.js'
|
|
@@ -214,6 +222,7 @@ const typingManager = createWorkingStateManager(
|
|
|
214
222
|
)
|
|
215
223
|
}
|
|
216
224
|
},
|
|
225
|
+
ORCHESTRATION_ENABLED ? 'orchestration' : 'legacy',
|
|
217
226
|
)
|
|
218
227
|
|
|
219
228
|
// Stages whose recovery must run in the runner process (live session control):
|
|
@@ -863,17 +872,18 @@ const RECEIVER_MODE = process.env.TELEGRAM_RECEIVER_MODE === 'true'
|
|
|
863
872
|
import { parseMenuFileContent } from './menu-parser'
|
|
864
873
|
|
|
865
874
|
const BOT_COMMANDS = [
|
|
875
|
+
...(ORCHESTRATION_ENABLED ? [{ command: 'tasks', description: 'View pending tasks in this chat' }, { command: 'voice', description: 'Turn automatic voice replies on or off' }, { command: 'voices', description: 'Choose the agent voice' }] : []),
|
|
866
876
|
{ command: 'session', description: 'Show current session info' },
|
|
867
877
|
{ command: 'sessions', description: 'Manage conversation sessions' },
|
|
868
878
|
{ command: 'new', description: 'Create a new session' },
|
|
869
879
|
{ command: 'rename', description: 'Rename current session' },
|
|
870
880
|
{ command: 'clear', description: 'Clear current session history' },
|
|
871
881
|
{ command: 'compact', description: 'Summarize and compress session history' },
|
|
872
|
-
{ command: 'stop', description: 'Interrupt the agent and stop current work' },
|
|
882
|
+
{ command: 'stop', description: ORCHESTRATION_ENABLED ? 'Stop the agent reply and choose a task to cancel' : 'Interrupt the agent and stop current work' },
|
|
873
883
|
{ command: 'restart', description: 'Graceful restart session' },
|
|
874
884
|
{ command: 'model', description: 'Show current AI model' },
|
|
875
885
|
{ command: 'models', description: 'Switch AI model' },
|
|
876
|
-
{ command: 'cli', description: 'Open the live terminal viewer' },
|
|
886
|
+
...(INTERACTIVE_CLI_ENABLED ? [{ command: 'cli', description: 'Open the live terminal viewer' }] : []),
|
|
877
887
|
{ command: 'start', description: 'Welcome and setup guide' },
|
|
878
888
|
{ command: 'status', description: 'Check your pairing status' },
|
|
879
889
|
{ command: 'help', description: 'What this bot can do' },
|
|
@@ -1023,7 +1033,7 @@ async function handleInbound(
|
|
|
1023
1033
|
// Download photo from replied-to message if present
|
|
1024
1034
|
const replyMsg = ctx.message?.reply_to_message
|
|
1025
1035
|
let repliedImagePath: string | undefined
|
|
1026
|
-
if (replyMsg?.photo) {
|
|
1036
|
+
if (replyMsg?.photo && !orchestrationSpool) {
|
|
1027
1037
|
try {
|
|
1028
1038
|
const largest = replyMsg.photo[replyMsg.photo.length - 1]!
|
|
1029
1039
|
const file = await bot.api.getFile(largest.file_id)
|
|
@@ -1054,7 +1064,9 @@ async function handleInbound(
|
|
|
1054
1064
|
content: text,
|
|
1055
1065
|
meta: {
|
|
1056
1066
|
chat_id,
|
|
1067
|
+
...(ctx.message?.media_group_id ? {media_group_id:ctx.message.media_group_id,media_caption:ctx.message.caption??''} : {}),
|
|
1057
1068
|
...(msgId != null ? { message_id: String(msgId) } : {}),
|
|
1069
|
+
...(ctx.message?.message_thread_id != null ? { message_thread_id: String(ctx.message.message_thread_id) } : {}),
|
|
1058
1070
|
user: from.username ?? String(from.id),
|
|
1059
1071
|
user_id: String(from.id),
|
|
1060
1072
|
// Human-readable name for the chat picker / history (first_name is always
|
|
@@ -1072,8 +1084,9 @@ async function handleInbound(
|
|
|
1072
1084
|
...(replyMsg ? {
|
|
1073
1085
|
replied_message_id: String(replyMsg.message_id),
|
|
1074
1086
|
replied_user: replyMsg.from?.username ?? String(replyMsg.from?.id ?? ''),
|
|
1075
|
-
...(replyMsg.text ? { replied_text: replyMsg.text } : {}),
|
|
1087
|
+
...((replyMsg.text || replyMsg.caption) ? { replied_text: replyMsg.text || replyMsg.caption } : {}),
|
|
1076
1088
|
...(repliedImagePath ? { replied_image_path: repliedImagePath } : {}),
|
|
1089
|
+
...(orchestrationSpool && replyMsg.photo ? {replied_attachment_file_id:replyMsg.photo[replyMsg.photo.length-1]!.file_id} : {}),
|
|
1077
1090
|
...(repliedAttachment ? {
|
|
1078
1091
|
replied_attachment_kind: repliedAttachment.kind,
|
|
1079
1092
|
replied_attachment_file_id: repliedAttachment.file_id,
|
|
@@ -1103,15 +1116,16 @@ async function handleInbound(
|
|
|
1103
1116
|
// turns in --print --channels mode).
|
|
1104
1117
|
const callbackUrl = process.env.CLAUDE_CHANNEL_CALLBACK
|
|
1105
1118
|
if (callbackUrl) {
|
|
1106
|
-
|
|
1119
|
+
if (orchestrationSpool) orchestrationSpool.enqueue(channelParams)
|
|
1120
|
+
else fetch(callbackUrl, {
|
|
1107
1121
|
method: 'POST',
|
|
1108
1122
|
headers: { 'Content-Type': 'application/json' },
|
|
1109
1123
|
body: JSON.stringify(channelParams),
|
|
1110
1124
|
}).catch(err => {
|
|
1111
1125
|
process.stderr.write(`telegram channel: callback POST failed: ${err}\n`)
|
|
1112
1126
|
})
|
|
1113
|
-
//
|
|
1114
|
-
if (RECEIVER_MODE) {
|
|
1127
|
+
// Legacy turn watchdog only. Orchestration owns typing through durable task/decision state.
|
|
1128
|
+
if (RECEIVER_MODE && !ORCHESTRATION_ENABLED) {
|
|
1115
1129
|
typingManager.start(chat_id)
|
|
1116
1130
|
}
|
|
1117
1131
|
}
|
|
@@ -1157,12 +1171,13 @@ bot.command('help', async ctx => {
|
|
|
1157
1171
|
`/rename <name> — rename current session\n` +
|
|
1158
1172
|
`/clear — clear current session history\n` +
|
|
1159
1173
|
`/compact — summarise and compress session history\n` +
|
|
1160
|
-
`/stop — interrupt the running turn\n` +
|
|
1174
|
+
(ORCHESTRATION_ENABLED ? `/stop — stop the reply and choose a task to cancel\n` : `/stop — interrupt the running turn\n`) +
|
|
1161
1175
|
`/restart — graceful restart session\n\n` +
|
|
1162
1176
|
`*Agent*\n` +
|
|
1163
1177
|
`/model — show current AI model\n` +
|
|
1164
1178
|
`/models — switch AI model\n` +
|
|
1165
|
-
`/
|
|
1179
|
+
(ORCHESTRATION_ENABLED ? `/tasks — view pending tasks in this chat\n/voice — turn automatic voice replies on/off (default off)\n/voices — choose the agent voice\n` : '') +
|
|
1180
|
+
(INTERACTIVE_CLI_ENABLED ? `/cli — open the live terminal viewer\n\n` : '\n') +
|
|
1166
1181
|
`*Account*\n` +
|
|
1167
1182
|
`/start — pairing instructions\n` +
|
|
1168
1183
|
`/status — check your pairing state`,
|
|
@@ -1295,6 +1310,7 @@ bot.command('models', async ctx => {
|
|
|
1295
1310
|
// this bot's token), so nothing secret rides in the URL. Private chat + allowlist
|
|
1296
1311
|
// only, matching every other command.
|
|
1297
1312
|
bot.command('cli', async ctx => {
|
|
1313
|
+
if (!INTERACTIVE_CLI_ENABLED) { await ctx.reply('Terminal viewer requires interactive mode (headless: false).'); return }
|
|
1298
1314
|
if (ctx.chat?.type !== 'private') return
|
|
1299
1315
|
const access = loadAccess()
|
|
1300
1316
|
if (!access.allowFrom.includes(String(ctx.from!.id))) return
|
|
@@ -1360,6 +1376,144 @@ bot.command('restart', async ctx => {
|
|
|
1360
1376
|
)
|
|
1361
1377
|
})
|
|
1362
1378
|
|
|
1379
|
+
type VoicePicker = {menuId:string;provider:string;page:number;pages:number;gender?:string;groups:string[];selected:string;voices:Array<{name:string;gender?:string;index:number;selected:boolean}>}
|
|
1380
|
+
async function voicePickerRequest(chatId:string,userId:string,payload:Record<string,unknown>={}) {
|
|
1381
|
+
if(!CALLBACK_URL_BASE)throw new Error('not_configured')
|
|
1382
|
+
const response=await fetch(CALLBACK_URL_BASE+'/command',{method:'POST',headers:{'Content-Type':'application/json'},signal:AbortSignal.timeout(20000),body:JSON.stringify({command:'telegram_voices',chat_id:chatId,payload:{...payload,user_id:userId}})})
|
|
1383
|
+
const result=await response.json() as VoicePicker & {success?:boolean;name?:string}
|
|
1384
|
+
if(!response.ok||!result.success)throw new Error('unavailable')
|
|
1385
|
+
return result
|
|
1386
|
+
}
|
|
1387
|
+
function voicePickerMenu(menu:VoicePicker) {
|
|
1388
|
+
const keyboard=new InlineKeyboard()
|
|
1389
|
+
const labels:Record<string,string>={male:'👨 Male',female:'👩 Female',neutral:'Neutral',unspecified:'Unspecified'}
|
|
1390
|
+
if(!menu.gender){
|
|
1391
|
+
for(const group of menu.groups)keyboard.text(labels[group],`voicegroup:${menu.menuId}:${group}`).row()
|
|
1392
|
+
}else{
|
|
1393
|
+
for(const v of menu.voices)keyboard.text(`${v.selected?'✅ ':''}${v.name.slice(0,60)}`,`voicepick:${menu.menuId}:${v.index}`).row()
|
|
1394
|
+
if(menu.page>0)keyboard.text('Previous',`voicepage:${menu.menuId}:${menu.gender}:${menu.page-1}`)
|
|
1395
|
+
if(menu.page+1<menu.pages)keyboard.text('Next',`voicepage:${menu.menuId}:${menu.gender}:${menu.page+1}`)
|
|
1396
|
+
keyboard.row().text('Back',`voicegroup:${menu.menuId}:back`).row()
|
|
1397
|
+
}
|
|
1398
|
+
keyboard.text('Dismiss',`voicedismiss:${menu.menuId}`)
|
|
1399
|
+
return {text:`Agent voice · ${menu.provider}\nSelected: ${menu.selected}\n${menu.gender?`${labels[menu.gender]} — choose a voice (${menu.page+1}/${menu.pages}):`:'Choose a voice category:'}`,reply_markup:keyboard}
|
|
1400
|
+
}
|
|
1401
|
+
bot.command('voices',async ctx=>{
|
|
1402
|
+
if(ctx.chat?.type!=='private'||!isCallbackAuthorized(ctx))return
|
|
1403
|
+
if(!ORCHESTRATION_ENABLED){await ctx.reply('Voice controls require orchestration mode.');return}
|
|
1404
|
+
try{const menu=voicePickerMenu(await voicePickerRequest(String(ctx.chat.id),String(ctx.from!.id)));await ctx.reply(menu.text,{reply_markup:menu.reply_markup})}
|
|
1405
|
+
catch{await ctx.reply('Voice list unavailable. Check the TTS provider configuration and try /voices again.')}
|
|
1406
|
+
})
|
|
1407
|
+
|
|
1408
|
+
type TaskView = TaskDetail & {taskId:string;title:string;state:string;progress?:string;question?:string;updatedAt:number;startedAt?:number;finishedAt?:number;canStop:boolean}
|
|
1409
|
+
type TaskBrowser = {sessionId:string;task?:TaskView;tasks?:TaskView[];page?:number;pages?:number;total?:number}
|
|
1410
|
+
async function taskBrowserRequest(chatId:string,userId:string,payload:Record<string,unknown>={}) {
|
|
1411
|
+
if(!CALLBACK_URL_BASE)throw Error('not_configured')
|
|
1412
|
+
const response=await fetch(CALLBACK_URL_BASE+'/command',{method:'POST',headers:{'Content-Type':'application/json'},signal:AbortSignal.timeout(10000),body:JSON.stringify({command:'telegram_tasks',chat_id:chatId,payload:{...payload,user_id:userId}})})
|
|
1413
|
+
const result=await response.json() as TaskBrowser & {success?:boolean}
|
|
1414
|
+
if(!response.ok||!result.success)throw Error((result as {error?:string}).error==='TASK_SESSION_CHANGED'?'TASK_SESSION_CHANGED':'unavailable')
|
|
1415
|
+
if(typeof result.sessionId!=='string'||!result.sessionId)throw Error('unavailable')
|
|
1416
|
+
return result
|
|
1417
|
+
}
|
|
1418
|
+
function taskBrowserMenu(result:TaskBrowser) {
|
|
1419
|
+
const keyboard=new InlineKeyboard()
|
|
1420
|
+
const labels:Record<string,string>={queued:'⏳ Queued',starting:'⏳ Starting',running:'🔥 Running',waiting_input:'💬 Waiting for your input',interrupting:'⏸ Interrupting',cancel_requested:'⏹ Stopping',recovering:'🔄 Recovering',needs_reconciliation:'⚠️ Needs reconciliation',completed:'✅ Completed',failed:'❌ Failed',cancelled:'⏹ Cancelled'}
|
|
1421
|
+
let text:string
|
|
1422
|
+
if(result.task){
|
|
1423
|
+
const task=result.task
|
|
1424
|
+
text=formatTaskDetail(task,labels[task.state]??task.state)
|
|
1425
|
+
if(task.canStop)keyboard.text(task.state==='needs_reconciliation'?'🔄 Retry cleanup':'🔴 Stop task',`taskcancel:${task.taskId}`).row()
|
|
1426
|
+
keyboard.text('Back','tasklist:0').row()
|
|
1427
|
+
}else{
|
|
1428
|
+
text=result.total?`Tasks (${result.total}) · Page ${(result.page??0)+1}/${result.pages}\nChoose a task for details.`:'No pending tasks in this chat.'
|
|
1429
|
+
for(const [i,task] of (result.tasks??[]).entries())keyboard.text(`${(result.page??0)*10+i+1}. ${task.title.replace(/\s+/g,' ').slice(0,45)} · ${labels[task.state]??task.state}`,`taskview:${task.taskId}`).row()
|
|
1430
|
+
if((result.page??0)>0)keyboard.text('Previous',`tasklist:${result.page!-1}`)
|
|
1431
|
+
if((result.page??0)+1<(result.pages??1))keyboard.text('Next',`tasklist:${result.page!+1}`)
|
|
1432
|
+
keyboard.row()
|
|
1433
|
+
}
|
|
1434
|
+
keyboard.text('Dismiss','taskdismiss')
|
|
1435
|
+
return {text,reply_markup:keyboard}
|
|
1436
|
+
}
|
|
1437
|
+
const TASK_BROWSER_FILE = join(STATE_DIR, 'task-browser.json')
|
|
1438
|
+
let restoredTaskBrowsers: TaskBrowserMessage[] = []
|
|
1439
|
+
try { const saved = JSON.parse(readFileSync(TASK_BROWSER_FILE, 'utf8')); if (Array.isArray(saved)) restoredTaskBrowsers = saved } catch {}
|
|
1440
|
+
const liveTaskBrowser = new LiveTaskBrowser({
|
|
1441
|
+
read: taskBrowserRequest,
|
|
1442
|
+
render: result => taskBrowserMenu(result as TaskBrowser),
|
|
1443
|
+
send: async (chat, menu) => (await bot.api.sendMessage(chat, menu.text, {reply_markup: menu.reply_markup as InlineKeyboard})).message_id,
|
|
1444
|
+
edit: (chat, id, menu) => bot.api.editMessageText(chat, id, menu.text, {reply_markup: menu.reply_markup as InlineKeyboard}),
|
|
1445
|
+
remove: (chat, id) => bot.api.deleteMessage(chat, id),
|
|
1446
|
+
close: (chat, id) => bot.api.editMessageText(chat, id, 'Task view closed.', {reply_markup: {inline_keyboard: []}}),
|
|
1447
|
+
allowed: (chat, user) => ORCHESTRATION_ENABLED && chat === user && loadAccess().allowFrom.includes(user),
|
|
1448
|
+
persist: entries => {
|
|
1449
|
+
mkdirSync(STATE_DIR, {recursive:true,mode:0o700})
|
|
1450
|
+
const temp = TASK_BROWSER_FILE + '.tmp'
|
|
1451
|
+
writeFileSync(temp,JSON.stringify(entries),{mode:0o600});renameSync(temp,TASK_BROWSER_FILE)
|
|
1452
|
+
},
|
|
1453
|
+
}, restoredTaskBrowsers)
|
|
1454
|
+
if (ORCHESTRATION_ENABLED) setInterval(() => { void liveTaskBrowser.tick().catch(() => {}) }, 3000).unref()
|
|
1455
|
+
bot.command('tasks',async ctx=>{
|
|
1456
|
+
if(ctx.chat?.type!=='private'||!isCallbackAuthorized(ctx))return
|
|
1457
|
+
if(!ORCHESTRATION_ENABLED){await ctx.reply('Task controls require orchestration mode.');return}
|
|
1458
|
+
try{await liveTaskBrowser.open(String(ctx.chat.id),String(ctx.from!.id))}
|
|
1459
|
+
catch{await ctx.reply('Could not load tasks. Please try /tasks again.')}
|
|
1460
|
+
})
|
|
1461
|
+
|
|
1462
|
+
async function taskStopRequest(chatId: string, userId: string, menuId?: string, index?: number) {
|
|
1463
|
+
if (!CALLBACK_URL_BASE) throw new Error('not_configured')
|
|
1464
|
+
const response=await fetch(CALLBACK_URL_BASE+'/command', {
|
|
1465
|
+
method:'POST',headers:{'Content-Type':'application/json'},signal:AbortSignal.timeout(10000),
|
|
1466
|
+
body:JSON.stringify({command:'task_stop',chat_id:chatId,payload:{user_id:userId,menu_id:menuId,index}}),
|
|
1467
|
+
})
|
|
1468
|
+
const result=await response.json() as {success?:boolean;legacy?:boolean;text?:string;menuId?:string;tasks?:Array<{title:string;state:string}>}
|
|
1469
|
+
if (!response.ok || !result.success) throw new Error('unavailable')
|
|
1470
|
+
return result
|
|
1471
|
+
}
|
|
1472
|
+
bot.command('stop',async (ctx,next)=>{
|
|
1473
|
+
if (!ORCHESTRATION_ENABLED || ctx.chat?.type!=='private') return next()
|
|
1474
|
+
if (!isCallbackAuthorized(ctx)) return
|
|
1475
|
+
try {
|
|
1476
|
+
const result=await taskStopRequest(String(ctx.chat.id),String(ctx.from!.id))
|
|
1477
|
+
if (result.legacy) return
|
|
1478
|
+
const keyboard=new InlineKeyboard()
|
|
1479
|
+
for (const [i,task] of (result.tasks??[]).entries()) keyboard.text(`${i+1}. ${task.title.slice(0,65)} (${task.state})`,`taskstop:${result.menuId}:${i+1}`).row()
|
|
1480
|
+
if (result.tasks?.length) keyboard.text('Dismiss',`taskstop:${result.menuId}:0`)
|
|
1481
|
+
await ctx.reply(result.text??'Nothing to stop.',result.tasks?.length?{reply_markup:keyboard}:{})
|
|
1482
|
+
} catch { await ctx.reply('Could not load tasks. Please try /stop again.') }
|
|
1483
|
+
})
|
|
1484
|
+
|
|
1485
|
+
// Voice settings are handled directly; no model call or TTS for this menu.
|
|
1486
|
+
type VoiceReplyMode = 'on' | 'auto' | 'off'
|
|
1487
|
+
async function telegramVoiceSetting(chatId: string, mode?: VoiceReplyMode): Promise<VoiceReplyMode> {
|
|
1488
|
+
if (!CALLBACK_URL_BASE) throw new Error('not_configured')
|
|
1489
|
+
const response = await fetch(CALLBACK_URL_BASE + '/command', {
|
|
1490
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
1491
|
+
body: JSON.stringify({ command: 'telegram_voice', chat_id: chatId, payload: { mode } }),
|
|
1492
|
+
signal: AbortSignal.timeout(10000),
|
|
1493
|
+
})
|
|
1494
|
+
const result = await response.json() as { success?: boolean; enabled?: boolean; mode?: VoiceReplyMode }
|
|
1495
|
+
if (!response.ok || !result.success || typeof result.enabled !== 'boolean') throw new Error('unavailable')
|
|
1496
|
+
return result.mode ?? (result.enabled ? 'on' : 'off')
|
|
1497
|
+
}
|
|
1498
|
+
function voiceMenu(mode: VoiceReplyMode) {
|
|
1499
|
+
const labels = {on:'🔊 Always', auto:'🎙️ Only reply voice message', off:'🔇 Off'}
|
|
1500
|
+
const keyboard = new InlineKeyboard()
|
|
1501
|
+
for (const value of ['on','auto','off'] as const) keyboard.text(`${mode === value ? '✅ ' : ''}${labels[value]}`, `voice:${value}`).row()
|
|
1502
|
+
return {text: `Voice replies: ${labels[mode]}\nAuto replies with audio to voice messages and their task results.`, reply_markup: keyboard.text('Dismiss','voice:dismiss')}
|
|
1503
|
+
}
|
|
1504
|
+
bot.command('voice', async ctx => {
|
|
1505
|
+
if (!ORCHESTRATION_ENABLED) { await ctx.reply('Voice controls require orchestration mode.'); return }
|
|
1506
|
+
if (ctx.chat?.type !== 'private' || !isCallbackAuthorized(ctx)) return
|
|
1507
|
+
const choice = ctx.match.trim().toLowerCase()
|
|
1508
|
+
if (choice && choice !== 'on' && choice !== 'off' && choice !== 'auto') {
|
|
1509
|
+
await ctx.reply('Use /voice to open the menu, or /voice on, /voice auto and /voice off.'); return
|
|
1510
|
+
}
|
|
1511
|
+
try {
|
|
1512
|
+
const menu = voiceMenu(await telegramVoiceSetting(String(ctx.chat.id), choice ? choice as VoiceReplyMode : undefined))
|
|
1513
|
+
await ctx.reply(menu.text, choice ? {} : { reply_markup: menu.reply_markup })
|
|
1514
|
+
} catch { await ctx.reply('Voice settings are unavailable. Check the agent TTS configuration.') }
|
|
1515
|
+
})
|
|
1516
|
+
|
|
1363
1517
|
// /session — show current session info (direct command, no typing manager)
|
|
1364
1518
|
bot.command('session', async ctx => {
|
|
1365
1519
|
if (ctx.chat?.type !== 'private') return
|
|
@@ -1481,6 +1635,69 @@ function validModelRows(value: unknown): Array<{ id: string; label: string }> {
|
|
|
1481
1635
|
bot.on('callback_query:data', async ctx => {
|
|
1482
1636
|
const data = ctx.callbackQuery.data
|
|
1483
1637
|
|
|
1638
|
+
const voicePicker=/^(voicepick|voicepage|voicegroup|voicedismiss):([a-f0-9-]{36})(?::(male|female|neutral|unspecified|back))?(?::(\d+))?$/.exec(data)
|
|
1639
|
+
if(voicePicker){
|
|
1640
|
+
if(!ORCHESTRATION_ENABLED||ctx.callbackQuery.message?.chat.type!=='private'||!isCallbackAuthorized(ctx)){await ctx.answerCallbackQuery({text:'Not available or not authorized.'}).catch(()=>{});return}
|
|
1641
|
+
try{
|
|
1642
|
+
const action=voicePicker[1],id=voicePicker[2],n=Number(voicePicker[4]),chatId=String(ctx.callbackQuery.message.chat.id),userId=String(ctx.from.id)
|
|
1643
|
+
if(action==='voicepage'||action==='voicegroup'){
|
|
1644
|
+
const menu=voicePickerMenu(await voicePickerRequest(chatId,userId,{menu_id:id,page:action==='voicepage'?n:0,gender:voicePicker[3]==='back'?undefined:voicePicker[3]}))
|
|
1645
|
+
await ctx.editMessageText(menu.text,{reply_markup:menu.reply_markup}).catch(()=>{});await ctx.answerCallbackQuery().catch(()=>{})
|
|
1646
|
+
}else{
|
|
1647
|
+
const result=await voicePickerRequest(chatId,userId,{action:action==='voicepick'?'choose':'dismiss',menu_id:id,index:n})
|
|
1648
|
+
await ctx.answerCallbackQuery({text:action==='voicepick'?`Selected: ${result.name}`.slice(0,180):'Dismissed'}).catch(()=>{})
|
|
1649
|
+
if(action==='voicepick') await confirmSelection(ctx,`✅ Agent voice: ${result.name} · ${result.provider}`)
|
|
1650
|
+
else await ctx.deleteMessage().catch(()=>{})
|
|
1651
|
+
}
|
|
1652
|
+
}catch{await ctx.answerCallbackQuery({text:'Voice list changed or unavailable. Use /voices again.'}).catch(()=>{})}
|
|
1653
|
+
return
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
const taskBrowser=/^(taskview|taskcancel):([a-f0-9-]{36})$|^(tasklist):(\d+)$|^(taskdismiss)$/.exec(data)
|
|
1657
|
+
if(taskBrowser){
|
|
1658
|
+
if(!ORCHESTRATION_ENABLED||ctx.callbackQuery.message?.chat.type!=='private'||!isCallbackAuthorized(ctx)){await ctx.answerCallbackQuery({text:'Not available or not authorized.'}).catch(()=>{});return}
|
|
1659
|
+
try{
|
|
1660
|
+
await ctx.answerCallbackQuery().catch(()=>{})
|
|
1661
|
+
const payload=data==='taskdismiss'?{action:'dismiss'}:taskBrowser[1]?{action:taskBrowser[1]==='taskcancel'?'cancel':'detail',task_id:taskBrowser[2]}:{page:Number(taskBrowser[4])}
|
|
1662
|
+
await liveTaskBrowser.navigate(String(ctx.callbackQuery.message.chat.id),String(ctx.from.id),ctx.callbackQuery.message.message_id,payload)
|
|
1663
|
+
}catch{await ctx.answerCallbackQuery({text:'Task unavailable. Use /tasks to refresh.'}).catch(()=>{})}
|
|
1664
|
+
return
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
const taskStop=/^taskstop:([a-f0-9-]{36}):(\d+)$/.exec(data)
|
|
1668
|
+
if (taskStop) {
|
|
1669
|
+
if (!ORCHESTRATION_ENABLED) { await ctx.answerCallbackQuery({text:'Task controls require orchestration mode.'}).catch(()=>{}); return }
|
|
1670
|
+
if (ctx.callbackQuery.message?.chat.type!=='private' || !isCallbackAuthorized(ctx)) {
|
|
1671
|
+
await ctx.answerCallbackQuery({text:'Not authorized.'}).catch(()=>{}); return
|
|
1672
|
+
}
|
|
1673
|
+
try {
|
|
1674
|
+
const result=await taskStopRequest(String(ctx.callbackQuery.message.chat.id),String(ctx.from.id),taskStop[1],Number(taskStop[2]))
|
|
1675
|
+
await ctx.answerCallbackQuery({text:Number(taskStop[2])===0?'Dismissed':'Stop requested.'}).catch(()=>{})
|
|
1676
|
+
await ctx.deleteMessage().catch(()=>{})
|
|
1677
|
+
if (Number(taskStop[2])!==0) await ctx.reply(result.text??'Stop requested.')
|
|
1678
|
+
} catch { await ctx.answerCallbackQuery({text:'Selection unavailable. Use /stop to refresh.'}).catch(()=>{}) }
|
|
1679
|
+
return
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
if (data === 'voice:on' || data === 'voice:auto' || data === 'voice:off' || data === 'voice:dismiss') {
|
|
1683
|
+
if (!ORCHESTRATION_ENABLED) { await ctx.answerCallbackQuery({text:'Voice controls require orchestration mode.'}).catch(()=>{}); return }
|
|
1684
|
+
if (ctx.callbackQuery.message?.chat.type !== 'private' || !isCallbackAuthorized(ctx)) {
|
|
1685
|
+
await ctx.answerCallbackQuery({ text: 'Not authorized.' }).catch(() => {}); return
|
|
1686
|
+
}
|
|
1687
|
+
if (data === 'voice:dismiss') {
|
|
1688
|
+
await ctx.answerCallbackQuery({ text: 'Dismissed' }).catch(() => {})
|
|
1689
|
+
await ctx.deleteMessage().catch(() => {})
|
|
1690
|
+
return
|
|
1691
|
+
}
|
|
1692
|
+
try {
|
|
1693
|
+
await telegramVoiceSetting(String(ctx.callbackQuery.message.chat.id), data.slice(6) as VoiceReplyMode)
|
|
1694
|
+
const confirmation=voiceModeConfirmation(data.slice(6))
|
|
1695
|
+
await ctx.answerCallbackQuery({text:confirmation}).catch(()=>{})
|
|
1696
|
+
await confirmSelection(ctx,confirmation)
|
|
1697
|
+
} catch { await ctx.answerCallbackQuery({ text: 'Could not update voice settings. Please try again.' }).catch(() => {}) }
|
|
1698
|
+
return
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1484
1701
|
// Handle interactive-menu choice: choice:<N>. A tap is routed back exactly like
|
|
1485
1702
|
// the user typing "N" — same content + meta as the text path — so the session's
|
|
1486
1703
|
// pending-menu handler injects the selection into the PTY. Security mirrors the
|
|
@@ -1514,7 +1731,7 @@ bot.on('callback_query:data', async ctx => {
|
|
|
1514
1731
|
}).catch(err => {
|
|
1515
1732
|
process.stderr.write(`telegram channel: choice callback POST failed: ${err}\n`)
|
|
1516
1733
|
})
|
|
1517
|
-
if (RECEIVER_MODE) typingManager.start(chat_id)
|
|
1734
|
+
if (RECEIVER_MODE && !ORCHESTRATION_ENABLED) typingManager.start(chat_id)
|
|
1518
1735
|
}
|
|
1519
1736
|
return
|
|
1520
1737
|
}
|
|
@@ -1732,7 +1949,7 @@ bot.on('callback_query:data', async ctx => {
|
|
|
1732
1949
|
await ctx.answerCallbackQuery({ text: 'Compacting...' }).catch(() => {})
|
|
1733
1950
|
await ctx.deleteMessage().catch(() => {})
|
|
1734
1951
|
await ctx.reply('🧠 Session compacting, please wait...\nThis may take a moment.').catch(() => {})
|
|
1735
|
-
if (RECEIVER_MODE) {
|
|
1952
|
+
if (RECEIVER_MODE && !ORCHESTRATION_ENABLED) {
|
|
1736
1953
|
typingManager.start(chatId)
|
|
1737
1954
|
}
|
|
1738
1955
|
await fetch(CALLBACK_URL_BASE + '/command', {
|
|
@@ -1978,6 +2195,10 @@ bot.on('message:text', async ctx => {
|
|
|
1978
2195
|
|
|
1979
2196
|
bot.on('message:photo', async ctx => {
|
|
1980
2197
|
const caption = ctx.message.caption ?? '(photo)'
|
|
2198
|
+
if(orchestrationSpool){
|
|
2199
|
+
const photo=ctx.message.photo[ctx.message.photo.length-1]!;
|
|
2200
|
+
await handleInbound(ctx,caption,undefined,{kind:'photo',file_id:photo.file_id,size:photo.file_size,mime:'image/jpeg'});return;
|
|
2201
|
+
}
|
|
1981
2202
|
// Defer download until after the gate approves — any user can send photos,
|
|
1982
2203
|
// and we don't want to burn API quota or fill the inbox for dropped messages.
|
|
1983
2204
|
await handleInbound(ctx, caption, async () => {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Keep a durable confirmation in chat, matching the /models menu behavior. */
|
|
2
|
+
export async function confirmSelection(ctx: {
|
|
3
|
+
editMessageText(text: string, options: {reply_markup: {inline_keyboard: never[]}}): Promise<unknown>;
|
|
4
|
+
reply(text: string): Promise<unknown>;
|
|
5
|
+
deleteMessage(): Promise<unknown>;
|
|
6
|
+
}, text: string): Promise<void> {
|
|
7
|
+
try { await ctx.editMessageText(text, {reply_markup: {inline_keyboard: []}}); }
|
|
8
|
+
catch (error) {
|
|
9
|
+
if (/message is not modified/i.test(String((error as any)?.description ?? (error as any)?.message))) return;
|
|
10
|
+
// If Telegram cannot edit an old menu, retain the confirmation as a new
|
|
11
|
+
// message before removing the old controls. Never lose both.
|
|
12
|
+
await ctx.reply(text);
|
|
13
|
+
await ctx.deleteMessage().catch(() => {});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function voiceModeConfirmation(mode: string): string {
|
|
17
|
+
const labels: Record<string,string> = {on:'Always',auto:'Only reply voice message',off:'Off'};
|
|
18
|
+
if (!(mode in labels)) throw Error('INVALID_VOICE_MODE');
|
|
19
|
+
return `✅ Voice replies: ${labels[mode]}`;
|
|
20
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** A single live task browser per private chat. All reads are session-bound and
|
|
2
|
+
* serialized with navigation, replacement, and explicit cancellation. */
|
|
3
|
+
export interface TaskBrowserMessage {
|
|
4
|
+
chatId: string; userId: string; messageId: number; sessionId: string;
|
|
5
|
+
view: { page?: number; action?: 'detail'; task_id?: string };
|
|
6
|
+
}
|
|
7
|
+
export interface TaskBrowserResult {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
task?: { taskId: string; state: string };
|
|
10
|
+
page?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TaskBrowserMenu { text: string; reply_markup: unknown }
|
|
13
|
+
interface Entry extends TaskBrowserMessage { fingerprint?: string; retryAt?: number; finished?: boolean }
|
|
14
|
+
interface Dependencies {
|
|
15
|
+
read(chat: string, user: string, payload: Record<string, unknown>): Promise<TaskBrowserResult>;
|
|
16
|
+
render(result: TaskBrowserResult): TaskBrowserMenu;
|
|
17
|
+
send(chat: string, menu: TaskBrowserMenu): Promise<number>;
|
|
18
|
+
edit(chat: string, id: number, menu: TaskBrowserMenu): Promise<unknown>;
|
|
19
|
+
remove(chat: string, id: number): Promise<unknown>;
|
|
20
|
+
close(chat: string, id: number): Promise<unknown>;
|
|
21
|
+
allowed(chat: string, user: string): boolean;
|
|
22
|
+
persist(entries: TaskBrowserMessage[]): void;
|
|
23
|
+
now?: () => number;
|
|
24
|
+
}
|
|
25
|
+
const description = (e: any) => String(e?.description ?? e?.message ?? '');
|
|
26
|
+
const absent = (e: any) => /message to (?:edit|delete) not found|message_id_invalid/i.test(description(e));
|
|
27
|
+
const unchanged = (e: any) => /message is not modified/i.test(description(e));
|
|
28
|
+
export class LiveTaskBrowser {
|
|
29
|
+
private entries = new Map<string, Entry>();
|
|
30
|
+
private locks = new Map<string, Promise<unknown>>();
|
|
31
|
+
private nextEditAt = 0;
|
|
32
|
+
constructor(private io: Dependencies, restored: TaskBrowserMessage[] = []) {
|
|
33
|
+
for (const e of restored) {
|
|
34
|
+
if (e && typeof e === 'object' && /^\d+$/.test(e.chatId) && e.userId === e.chatId && Number.isSafeInteger(e.messageId) && e.messageId > 0 && typeof e.sessionId === 'string' && e.view &&
|
|
35
|
+
(e.view.action === 'detail' ? typeof e.view.task_id === 'string' : Number.isSafeInteger(e.view.page ?? 0))) this.entries.set(e.chatId, { ...e });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
private now() { return this.io.now?.() ?? Date.now(); }
|
|
39
|
+
private persist() {
|
|
40
|
+
this.io.persist([...this.entries.values()].map(({chatId,userId,messageId,sessionId,view}) => ({chatId,userId,messageId,sessionId,view})));
|
|
41
|
+
}
|
|
42
|
+
private serial<T>(chat: string, work: () => Promise<T>): Promise<T> {
|
|
43
|
+
const previous = this.locks.get(chat) ?? Promise.resolve();
|
|
44
|
+
const job = previous.catch(() => {}).then(work);
|
|
45
|
+
this.locks.set(chat, job);
|
|
46
|
+
void job.finally(() => { if (this.locks.get(chat) === job) this.locks.delete(chat); }).catch(() => {});
|
|
47
|
+
return job;
|
|
48
|
+
}
|
|
49
|
+
private async retire(e: Entry) {
|
|
50
|
+
try { await this.io.remove(e.chatId, e.messageId); }
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (!absent(error)) {
|
|
53
|
+
try { await this.io.close(e.chatId, e.messageId); }
|
|
54
|
+
catch (closed) { if (!absent(closed) && !unchanged(closed)) throw closed; }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
this.entries.delete(e.chatId); this.persist();
|
|
58
|
+
}
|
|
59
|
+
open(chatId: string, userId: string) {
|
|
60
|
+
return this.serial(chatId, async () => {
|
|
61
|
+
if (!this.io.allowed(chatId, userId)) throw Error('TASK_BROWSER_UNAUTHORIZED');
|
|
62
|
+
const result = await this.io.read(chatId, userId, { page: 0 });
|
|
63
|
+
const menu = this.io.render(result);
|
|
64
|
+
const previous = this.entries.get(chatId);
|
|
65
|
+
if (previous) await this.retire(previous);
|
|
66
|
+
const messageId = await this.io.send(chatId, menu);
|
|
67
|
+
this.entries.set(chatId, {chatId,userId,messageId,sessionId:result.sessionId,view:{page:result.page ?? 0},fingerprint:JSON.stringify(menu)});
|
|
68
|
+
this.persist();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
navigate(chatId: string, userId: string, messageId: number, payload: Record<string, unknown>) {
|
|
72
|
+
return this.serial(chatId, async () => {
|
|
73
|
+
const e = this.entries.get(chatId);
|
|
74
|
+
if (!e || e.messageId !== messageId || e.userId !== userId || !this.io.allowed(chatId,userId)) throw Error('TASK_BROWSER_EXPIRED');
|
|
75
|
+
if (payload.action === 'dismiss') { await this.retire(e); return; }
|
|
76
|
+
try {
|
|
77
|
+
const result = await this.io.read(chatId,userId,{...payload,session_id:e.sessionId});
|
|
78
|
+
// Never repeat a mutation during auto-refresh, even if Telegram edit fails.
|
|
79
|
+
e.view = result.task ? {action:'detail',task_id:result.task.taskId} : {page:result.page ?? 0};
|
|
80
|
+
this.persist();
|
|
81
|
+
await this.update(e,result);
|
|
82
|
+
} catch (error) { await this.failure(e,error); throw error; }
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
private async update(e: Entry, result: TaskBrowserResult) {
|
|
86
|
+
if (result.sessionId !== e.sessionId) throw Error('TASK_SESSION_CHANGED');
|
|
87
|
+
const view = result.task ? { action: 'detail' as const, task_id: result.task.taskId } : { page: result.page ?? 0 };
|
|
88
|
+
if (JSON.stringify(view) !== JSON.stringify(e.view)) { e.view = view; this.persist(); }
|
|
89
|
+
const finished = !!result.task && ['completed','failed','cancelled'].includes(result.task.state);
|
|
90
|
+
const menu = this.io.render(result), fingerprint = JSON.stringify(menu);
|
|
91
|
+
if (fingerprint === e.fingerprint) { e.finished = finished; return; }
|
|
92
|
+
try { await this.io.edit(e.chatId,e.messageId,menu); }
|
|
93
|
+
catch (error) { if (!unchanged(error)) throw error; }
|
|
94
|
+
e.fingerprint = fingerprint; e.retryAt = undefined; e.finished = finished;
|
|
95
|
+
}
|
|
96
|
+
private async failure(e: Entry, error: any) {
|
|
97
|
+
if (description(error).includes('TASK_SESSION_CHANGED')) { await this.retire(e); return; }
|
|
98
|
+
if (absent(error) || error?.error_code === 403) { this.entries.delete(e.chatId); this.persist(); return; }
|
|
99
|
+
const retry = Number(error?.parameters?.retry_after);
|
|
100
|
+
e.retryAt = this.now() + (Number.isFinite(retry) && retry > 0 ? retry * 1000 : 10000);
|
|
101
|
+
if (retry > 0) this.nextEditAt = Math.max(this.nextEditAt,e.retryAt);
|
|
102
|
+
}
|
|
103
|
+
async tick() {
|
|
104
|
+
// One tick cannot overlap a previous read/edit or user action for this chat.
|
|
105
|
+
for (const e of [...this.entries.values()]) {
|
|
106
|
+
if (e.finished || this.locks.has(e.chatId) || this.now() < Math.max(e.retryAt ?? 0,this.nextEditAt)) continue;
|
|
107
|
+
await this.serial(e.chatId, async () => {
|
|
108
|
+
if (this.entries.get(e.chatId) !== e) return;
|
|
109
|
+
if (!this.io.allowed(e.chatId,e.userId)) {
|
|
110
|
+
this.entries.delete(e.chatId); this.persist(); return;
|
|
111
|
+
}
|
|
112
|
+
try { await this.update(e,await this.io.read(e.chatId,e.userId,{...e.view,session_id:e.sessionId})); }
|
|
113
|
+
catch (error) { await this.failure(e,error).catch(() => {}); }
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { formatTiming } from '../../../dist/orchestration/tasks/timing';
|
|
2
|
+
import { formatTaskElapsed } from './task-elapsed';
|
|
3
|
+
import type { ProcessSample } from '../../../src/orchestration/process-activity';
|
|
4
|
+
import type { ToolOutcome } from '../../../src/orchestration/execution-observation';
|
|
5
|
+
|
|
6
|
+
export interface TaskDetail {
|
|
7
|
+
title: string; state: string; startedAt?: number; finishedAt?: number;
|
|
8
|
+
timing?: Partial<Record<'working' | 'tool' | 'input' | 'queued' | 'other' | 'finished', number>>;
|
|
9
|
+
timingMeasuredFrom?: number;
|
|
10
|
+
progress?: string; progressText?: string; question?: string;
|
|
11
|
+
activityDetails?: { counters: string; status: string; age: number; activeTools: string[];
|
|
12
|
+
observationStatus?: string; process?: ProcessSample; lastTool?: ToolOutcome; quiet?: boolean };
|
|
13
|
+
}
|
|
14
|
+
function bytes(value: number): string {
|
|
15
|
+
const unit = value >= 1000000 ? 'MB' : value >= 1000 ? 'KB' : 'B';
|
|
16
|
+
return `${Number((value / (unit === 'MB' ? 1000000 : unit === 'KB' ? 1000 : 1)).toFixed(2))} ${unit}`;
|
|
17
|
+
}
|
|
18
|
+
export function formatTaskDetail(task: TaskDetail, label: string): string {
|
|
19
|
+
const activity = task.activityDetails;
|
|
20
|
+
const progress = task.progressText ?? task.progress;
|
|
21
|
+
const progressLines = [progress, activity?.activeTools.length ? `Active tools: ${activity.activeTools.join(', ')}` : '', activity ? `Last activity: ${activity.age}s ago.` : '', task.question ? `Waiting for your input:\n${task.question}` : ''].filter(Boolean);
|
|
22
|
+
const process = activity?.process;
|
|
23
|
+
const counters = process ? process.available ? [
|
|
24
|
+
`Processes: ${process.processCount}`,
|
|
25
|
+
process.cpuTicksDelta !== undefined ? `CPU: +${process.cpuTicksDelta} ticks · Children: ${process.childCpuTicksDelta === undefined ? 'unavailable' : `+${process.childCpuTicksDelta} ticks`}` : 'CPU: unavailable',
|
|
26
|
+
process.ioAvailable ? `I/O: Read ${process.readBytesDelta === undefined ? 'unavailable' : `+${bytes(process.readBytesDelta)}`} · Write ${process.writeBytesDelta === undefined ? 'unavailable' : `+${bytes(process.writeBytesDelta)}`}` : 'I/O: partially unavailable',
|
|
27
|
+
].join('\n') : '' : activity?.counters;
|
|
28
|
+
const lastTool = activity?.lastTool;
|
|
29
|
+
const diagnostics = [counters, lastTool ? `Last tool: ${lastTool.name} · ${lastTool.status === 'error' ? 'Reported error' : 'Result received'}${lastTool.exitCode === undefined ? '' : ` · Exit code: ${lastTool.exitCode}`}\nReported at: ${new Date(lastTool.observedAt).toISOString().replace('T',' ').replace(/\.\d{3}Z$/, ' UTC')}` : '', activity?.observationStatus ?? activity?.status, activity?.quiet ? 'No recent tool/model progress; task retained for inspection or cancellation.' : ''].filter(Boolean);
|
|
30
|
+
return [
|
|
31
|
+
`${task.title}\n${label} · Elapsed: ${formatTaskElapsed(task.startedAt,task.finishedAt)}`,
|
|
32
|
+
formatTiming(task.timing) ? 'Time breakdown\n' + formatTiming(task.timing) + (task.timingMeasuredFrom && task.startedAt && task.timingMeasuredFrom > task.startedAt + 1000 ? '\nMeasured since ' + new Date(task.timingMeasuredFrom).toISOString() : '') : '',
|
|
33
|
+
progressLines.length ? `Progress\n${progressLines.join('\n')}` : '',
|
|
34
|
+
diagnostics.length ? `Diagnostics\n${diagnostics.join('\n')}` : '',
|
|
35
|
+
].filter(Boolean).join('\n\n');
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { formatTaskElapsed } from '../../../dist/shared/task-elapsed';
|