@0xmaxma/claude-gateway 1.8.14 → 2.0.1
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 -1613
- package/config.template.json +7 -1
- package/dist/agent/runner.d.ts +419 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +736 -24
- 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 +162 -10
- 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/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 +2 -0
- package/dist/history/types.d.ts.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 +260 -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 +151 -0
- package/dist/orchestration/decisions.js.map +1 -0
- package/dist/orchestration/delivery.d.ts +34 -0
- package/dist/orchestration/delivery.d.ts.map +1 -0
- package/dist/orchestration/delivery.js +235 -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 +237 -0
- package/dist/orchestration/runtime.d.ts.map +1 -0
- package/dist/orchestration/runtime.js +1031 -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 +92 -0
- package/dist/orchestration/store.d.ts.map +1 -0
- package/dist/orchestration/store.js +326 -0
- package/dist/orchestration/store.js.map +1 -0
- package/dist/orchestration/task-controls.d.ts +172 -0
- package/dist/orchestration/task-controls.d.ts.map +1 -0
- package/dist/orchestration/task-controls.js +59 -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-questions.d.ts +26 -0
- package/dist/orchestration/task-questions.d.ts.map +1 -0
- package/dist/orchestration/task-questions.js +160 -0
- package/dist/orchestration/task-questions.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 +114 -0
- package/dist/orchestration/tasks/service.d.ts.map +1 -0
- package/dist/orchestration/tasks/service.js +760 -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 +143 -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 +14 -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 +13 -0
- package/dist/wechat/ilink-client.d.ts.map +1 -1
- package/dist/wechat/ilink-client.js +19 -0
- package/dist/wechat/ilink-client.js.map +1 -1
- 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 -37
- 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 +246 -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/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
package/dist/agent/runner.js
CHANGED
|
@@ -35,6 +35,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.AgentRunner = exports.CHANNEL_COALESCE_WINDOW_MS = exports.WHATSAPP_CLOUD_WINDOW_NOTE = exports.DEFAULT_MODELS = exports.MAX_IMAGE_SIZE_BYTES = void 0;
|
|
37
37
|
exports.toRelMediaFiles = toRelMediaFiles;
|
|
38
|
+
const channel_input_media_1 = require("../orchestration/channel-input-media");
|
|
39
|
+
const chunks_1 = require("../telegram/chunks");
|
|
40
|
+
const file_delivery_1 = require("../orchestration/file-delivery");
|
|
41
|
+
const voice_reply_policy_1 = require("../orchestration/voice-reply-policy");
|
|
42
|
+
const gateway_config_1 = require("../orchestration/gateway-config");
|
|
43
|
+
const delivery_1 = require("../orchestration/delivery");
|
|
44
|
+
const control_delivery_1 = require("../orchestration/control-delivery");
|
|
45
|
+
const stop_controls_1 = require("../orchestration/stop-controls");
|
|
38
46
|
const crypto_1 = require("crypto");
|
|
39
47
|
const events_1 = require("events");
|
|
40
48
|
const fs = __importStar(require("fs"));
|
|
@@ -226,6 +234,237 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
226
234
|
channelFor(chatId) {
|
|
227
235
|
return this.channelSourceMap.get(chatId) ?? 'telegram';
|
|
228
236
|
}
|
|
237
|
+
requiresDurableChannelIngress(source) {
|
|
238
|
+
return Boolean((this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(source)) ||
|
|
239
|
+
this.orchestration?.ownsChannelIngress(source));
|
|
240
|
+
}
|
|
241
|
+
orchestrationForApi(sessionId) {
|
|
242
|
+
return Boolean((this.orchestration?.ownsSession(sessionId) && !this.orchestration.canReturnToLegacy()) ||
|
|
243
|
+
(this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api')));
|
|
244
|
+
}
|
|
245
|
+
async sendLinkedOrchestrationChannel(...[binding, text, _id, file]) {
|
|
246
|
+
const channel = String(binding.channel), chat = String(binding.chat_id);
|
|
247
|
+
if (channel === 'wechat' && file)
|
|
248
|
+
return { state: 'failed', code: 'CHANNEL_ATTACHMENTS_UNSUPPORTED' };
|
|
249
|
+
let path;
|
|
250
|
+
if (file) {
|
|
251
|
+
try {
|
|
252
|
+
path = (0, file_delivery_1.resolveChannelFile)(this.agentConfig, file).path;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return { state: 'failed', code: 'ATTACHMENT_UNAVAILABLE' };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
if (channel === 'whatsapp') {
|
|
260
|
+
// Persist the receiving number in the binding; an in-memory last-chat map
|
|
261
|
+
// can change while a worker is running or disappear after a restart.
|
|
262
|
+
const thread = String(binding.thread_key ?? '');
|
|
263
|
+
const account = thread.startsWith('whatsapp-account:') ? thread.slice('whatsapp-account:'.length) : undefined;
|
|
264
|
+
await this.sendWhatsAppMessage(chat, file?.caption || text, path, account, { asDocument: !!file && file.kind !== 'image' });
|
|
265
|
+
}
|
|
266
|
+
else if (channel === 'wechat')
|
|
267
|
+
await this.sendWeChatMessage(chat, text);
|
|
268
|
+
else
|
|
269
|
+
return { state: 'failed', code: 'DELIVERY_NOT_CONFIGURED' };
|
|
270
|
+
return { state: 'delivered' };
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return { state: 'unknown', code: 'PROVIDER_RECEIPT_UNKNOWN' };
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async sendOrchestrationControl(channel, chatId, menu, meta) {
|
|
277
|
+
if (!['telegram', 'whatsapp', 'whatsapp_cloud', 'wechat'].includes(channel))
|
|
278
|
+
return (0, control_delivery_1.sendControlMenu)(this.agentConfig, channel, chatId, menu, meta);
|
|
279
|
+
const text = [menu.text, ...menu.buttons.map(button => `${button.label}: /orch ${button.data.replace(/^orch:/, '')}`)].join('\n');
|
|
280
|
+
const send = (0, delivery_1.channelSender)(() => this.agentConfig, fetch, () => false, (...args) => this.sendLinkedOrchestrationChannel(...args));
|
|
281
|
+
for (const part of (0, chunks_1.chunkText)(text, 1900)) {
|
|
282
|
+
const result = await send({ channel, chat_id: chatId, thread_key: channel === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : meta.message_thread_id ?? '' }, part, (0, crypto_1.randomUUID)());
|
|
283
|
+
if (result.state !== 'delivered')
|
|
284
|
+
throw new Error(result.code);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async getOrchestration() {
|
|
288
|
+
if (this.orchestration)
|
|
289
|
+
return this.orchestration;
|
|
290
|
+
if (!this.orchestrationStarting) {
|
|
291
|
+
this.orchestrationStarting = Promise.resolve().then(() => __importStar(require('../orchestration/runtime'))).then(({ AgentOrchestrationRuntime }) => AgentOrchestrationRuntime.open(this.agentConfig, this.gatewayConfig, this.agentDir, this.sessionStore, this.historyDb, {
|
|
292
|
+
sendLinkedChannel: (...args) => this.sendLinkedOrchestrationChannel(...args),
|
|
293
|
+
skills: () => this.skillRegistry,
|
|
294
|
+
refreshSkills: async () => {
|
|
295
|
+
try {
|
|
296
|
+
const { discoverCliSkills } = await Promise.resolve().then(() => __importStar(require('../orchestration/cli-skills')));
|
|
297
|
+
this.skillRegistry.cliSkills = await discoverCliSkills(this.agentConfig, undefined, this.gatewayConfig);
|
|
298
|
+
delete this.skillRegistry.cliDiscoveryError;
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
this.skillRegistry.cliSkills = [];
|
|
302
|
+
this.skillRegistry.cliDiscoveryError = 'CLI_SKILL_DISCOVERY_UNAVAILABLE';
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
onManagedTurn: (sessionId, text, metrics, skills) => {
|
|
306
|
+
const key = `orchestration:${sessionId}`;
|
|
307
|
+
this.skillLearning?.onTurnStart(key, sessionId, text, skills, metrics.startedAt);
|
|
308
|
+
for (const id of metrics.toolIds)
|
|
309
|
+
this.skillLearning?.onToolUse(key, id);
|
|
310
|
+
this.skillLearning?.onTokenUsage(key, metrics.inputTokens, metrics.totalTokens);
|
|
311
|
+
this.skillLearning?.onTurnEnd(key, sessionId);
|
|
312
|
+
},
|
|
313
|
+
createAgentSession: async (sessionId, profile, model, scope) => {
|
|
314
|
+
const key = scope && scope.source !== 'api' ? scope.chatId : sessionId;
|
|
315
|
+
const old = this.sessions.get(key);
|
|
316
|
+
if (old) {
|
|
317
|
+
if (!old.isIdle(0))
|
|
318
|
+
throw Object.assign(new Error('Agent session is busy'), { code: 'CONFLICT' });
|
|
319
|
+
await old.stop();
|
|
320
|
+
this.sessions.delete(key);
|
|
321
|
+
}
|
|
322
|
+
if (this.sessions.size >= this.maxConcurrent)
|
|
323
|
+
throw new Error('Session pool full');
|
|
324
|
+
if (this.agentConfig.type !== 'app-agent')
|
|
325
|
+
(0, knowledge_1.spawnArchiveReindex)(this.agentConfig.workspace, this.agentConfig.knowledge, this.gatewayConfig.gateway.knowledge);
|
|
326
|
+
const agentSession = new process_1.SessionProcess(sessionId, scope?.source ?? 'api', this.agentConfig, this.gatewayConfig, this.sessionStore, scope?.chatId, profile);
|
|
327
|
+
agentSession.historyLimit = (0, process_1.resolveMaxHistoryMessages)(this.agentConfig.history?.maxHistoryMessages, this.gatewayConfig.gateway.history?.maxHistoryMessages);
|
|
328
|
+
if (model)
|
|
329
|
+
agentSession.modelOverride = model;
|
|
330
|
+
this.sessions.set(key, agentSession);
|
|
331
|
+
return agentSession;
|
|
332
|
+
},
|
|
333
|
+
releaseAgentSession: async (sessionId, agentSession) => {
|
|
334
|
+
await agentSession.stop();
|
|
335
|
+
for (const [key, value] of this.sessions)
|
|
336
|
+
if (value === agentSession)
|
|
337
|
+
this.sessions.delete(key);
|
|
338
|
+
},
|
|
339
|
+
})).then(orchestration => { this.orchestration = orchestration; return orchestration; }).catch(error => { this.orchestrationStarting = undefined; throw error; });
|
|
340
|
+
}
|
|
341
|
+
return this.orchestrationStarting;
|
|
342
|
+
}
|
|
343
|
+
async orchestrationActivity(sessionId, principalId, after) {
|
|
344
|
+
return (await this.getOrchestration()).activity(sessionId, principalId, after);
|
|
345
|
+
}
|
|
346
|
+
async subscribeResponseText(sessionId, principalId, receive) {
|
|
347
|
+
return (await this.getOrchestration()).subscribeText(sessionId, principalId, receive);
|
|
348
|
+
}
|
|
349
|
+
async voiceReplay(sessionId, principalId, responseId) {
|
|
350
|
+
return (await this.getOrchestration()).voiceAudio(sessionId, principalId, responseId);
|
|
351
|
+
}
|
|
352
|
+
saveVoiceReplay(sessionId, principalId, responseId, audio) {
|
|
353
|
+
this.orchestration?.saveVoiceAudio(sessionId, principalId, responseId, audio);
|
|
354
|
+
}
|
|
355
|
+
async authorizeVoiceSession(sessionId, principalId) {
|
|
356
|
+
(await this.getOrchestration()).authorizeSession(sessionId, principalId);
|
|
357
|
+
}
|
|
358
|
+
async subscribeVoiceResults(sessionId, principalId, receive, gender) {
|
|
359
|
+
return (await this.getOrchestration()).subscribeVoiceResults(sessionId, principalId, receive, gender);
|
|
360
|
+
}
|
|
361
|
+
recordVoicePlayback(responseId, principalId, progress, state) {
|
|
362
|
+
this.orchestration?.recordPlayback(responseId, principalId, progress, state);
|
|
363
|
+
}
|
|
364
|
+
async submitVoiceUtterance(sessionId, chatId, principalId, text, utteranceId, allowTools, model) {
|
|
365
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.voice?.enabled)
|
|
366
|
+
throw new Error('Voice is disabled');
|
|
367
|
+
if (!(await this.apiSessionExists(chatId, sessionId)))
|
|
368
|
+
throw new Error('Session not found');
|
|
369
|
+
const orchestration = await this.getOrchestration();
|
|
370
|
+
const accepted = orchestration.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: 'api', accountId: principalId,
|
|
371
|
+
chatId, threadKey: '', principalId }, text, model, modality: 'live_voice', ingressKey: `utterance:${utteranceId}` }, { execute: allowTools, writeMemory: false });
|
|
372
|
+
// The browser refreshes history on utterance.accepted, before inference finishes.
|
|
373
|
+
await orchestration.flushHistory();
|
|
374
|
+
return { ...accepted, responseId: () => orchestration.responseIdForInput(accepted.inputId) };
|
|
375
|
+
}
|
|
376
|
+
async listApiTasks(sessionId, principalId, page = 0, pageSize = 10, includeFinished = false) {
|
|
377
|
+
if (!this.agentConfig.orchestration?.enabled)
|
|
378
|
+
throw new Error('ORCHESTRATION_DISABLED');
|
|
379
|
+
const runtime = await this.getOrchestration();
|
|
380
|
+
runtime.authorizeSession(sessionId, principalId);
|
|
381
|
+
return runtime.taskControls.list(sessionId, principalId, page, pageSize, includeFinished);
|
|
382
|
+
}
|
|
383
|
+
async showApiTask(sessionId, principalId, taskId) {
|
|
384
|
+
if (!this.agentConfig.orchestration?.enabled)
|
|
385
|
+
throw new Error('ORCHESTRATION_DISABLED');
|
|
386
|
+
const runtime = await this.getOrchestration();
|
|
387
|
+
const detail = runtime.taskControls.detail(sessionId, principalId, taskId);
|
|
388
|
+
const task = runtime.store.task(taskId);
|
|
389
|
+
// Full retained result, with worker-only skill context removed by status().
|
|
390
|
+
return { ...runtime.tasks.status(task.conversationId, principalId, taskId)[0], ...detail };
|
|
391
|
+
}
|
|
392
|
+
async cancelApiTask(sessionId, principalId, taskId) {
|
|
393
|
+
if (!this.agentConfig.orchestration?.enabled)
|
|
394
|
+
throw new Error('ORCHESTRATION_DISABLED');
|
|
395
|
+
const runtime = await this.getOrchestration();
|
|
396
|
+
const task = runtime.taskControls.cancel(sessionId, principalId, taskId);
|
|
397
|
+
return task;
|
|
398
|
+
}
|
|
399
|
+
async answerApiTask(sessionId, principalId, taskId, questionId, answer) {
|
|
400
|
+
if (!this.agentConfig.orchestration?.enabled)
|
|
401
|
+
throw new Error('ORCHESTRATION_DISABLED');
|
|
402
|
+
const runtime = await this.getOrchestration();
|
|
403
|
+
const task = runtime.taskControls.answer(sessionId, principalId, taskId, questionId, answer);
|
|
404
|
+
await runtime.flushHistory();
|
|
405
|
+
return task;
|
|
406
|
+
}
|
|
407
|
+
stopVoiceResponse(sessionId) { this.orchestration?.stopResponse(sessionId); }
|
|
408
|
+
async sendOrchestratedApi(sessionId, chatId, message, opts, onText, onTool) {
|
|
409
|
+
const semantic = this.agentConfig.orchestration?.conversation?.semanticIntake === true;
|
|
410
|
+
if (!semantic && this.pendingApiSessions.has(sessionId))
|
|
411
|
+
throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
|
|
412
|
+
if (!opts.principalId)
|
|
413
|
+
throw new Error('Authenticated principal required for conversation orchestration');
|
|
414
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
415
|
+
if (!semantic)
|
|
416
|
+
this.pendingApiSessions.add(sessionId); // reserve before the first await
|
|
417
|
+
try {
|
|
418
|
+
const orchestration = await this.getOrchestration();
|
|
419
|
+
await this.sessionStore.ensureApiSession(this.agentConfig.id, chatId, sessionId);
|
|
420
|
+
this.apiChatIds.set(sessionId, chatId);
|
|
421
|
+
const media = opts.mediaFiles?.length ? await promoteUiUploads(this.agentsBaseDir, this.agentConfig.id, sessionId, opts.mediaFiles, this.logger) : [];
|
|
422
|
+
const index = await this.sessionStore.loadIndex(this.agentConfig.id, chatId, 'api');
|
|
423
|
+
const saved = index?.sessions.find(session => session.id === sessionId)?.imageConfig;
|
|
424
|
+
const selected = { ...(saved ? AgentRunner.durableImageConfig(saved) : {}), ...opts.imageParams };
|
|
425
|
+
const imageParams = AgentRunner.remapImageParamsRefs(Object.keys(selected).length ? selected : undefined, opts.mediaFiles, media);
|
|
426
|
+
const videoParams = AgentRunner.remapVideoParamsRefs(opts.videoParams, opts.mediaFiles, media);
|
|
427
|
+
if (videoParams)
|
|
428
|
+
await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { videoConfig: videoParams }, 'api');
|
|
429
|
+
const imageConfig = opts.imageParams && imageParams ? AgentRunner.durableImageConfig(imageParams) : undefined;
|
|
430
|
+
if (imageConfig)
|
|
431
|
+
await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { imageConfig }, 'api');
|
|
432
|
+
const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
|
|
433
|
+
const scopedInput = { scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: 'api', accountId: opts.principalId,
|
|
434
|
+
chatId, threadKey: '', principalId: opts.principalId }, text: message || '[Attachment inspection requested]', attachmentIds: media,
|
|
435
|
+
requestId, storeUserMessage: !opts.skipUserMessage,
|
|
436
|
+
metadata: imageParams || videoParams ? { promptContext: (imageParams ? AgentRunner.buildImageParamsNote(imageParams) : '') + (videoParams ? AgentRunner.buildVideoParamsNote(videoParams) : ''), imageRefs: imageParams?.image_refs } : undefined };
|
|
437
|
+
let text;
|
|
438
|
+
if (semantic) {
|
|
439
|
+
const accepted = orchestration.submitInput({ ...scopedInput, model: opts.model }, { execute: opts.allowTools ?? false, writeMemory: false }, onTool);
|
|
440
|
+
const lengths = new Map();
|
|
441
|
+
const unsubscribe = orchestration.subscribeText(sessionId, opts.principalId, event => {
|
|
442
|
+
if (!orchestration.responseBelongsToInput(accepted.inputId, event.responseId) || !event.text)
|
|
443
|
+
return;
|
|
444
|
+
const before = lengths.get(event.responseId) ?? 0;
|
|
445
|
+
if (event.text.length > before)
|
|
446
|
+
onText?.(event.text.slice(before));
|
|
447
|
+
lengths.set(event.responseId, event.text.length);
|
|
448
|
+
});
|
|
449
|
+
try {
|
|
450
|
+
text = await accepted.response;
|
|
451
|
+
}
|
|
452
|
+
finally {
|
|
453
|
+
unsubscribe();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else
|
|
457
|
+
text = await orchestration.send(scopedInput, { execute: opts.allowTools ?? false, writeMemory: false }, { timeoutMs: opts.timeoutMs, model: opts.model, onText, onTool });
|
|
458
|
+
if (opts.waitForTasks)
|
|
459
|
+
text = await orchestration.waitForTaskReport(sessionId, opts.principalId, requestId, text, deadline);
|
|
460
|
+
this.addApiAttachments(sessionId, orchestration.responseFiles(sessionId, requestId).map(ref => media_store_1.MediaStore.resolvePath(this.agentsBaseDir, this.agentConfig.id, ref)));
|
|
461
|
+
return { text, attachments: this.popApiAttachments(sessionId) };
|
|
462
|
+
}
|
|
463
|
+
finally {
|
|
464
|
+
if (!semantic)
|
|
465
|
+
this.pendingApiSessions.delete(sessionId);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
229
468
|
constructor(agentConfig, gatewayConfig, logger) {
|
|
230
469
|
super();
|
|
231
470
|
this.stopping = false;
|
|
@@ -246,6 +485,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
246
485
|
this.slackThreadTs = new Map();
|
|
247
486
|
this.receiver = null;
|
|
248
487
|
this.discordReceiver = null;
|
|
488
|
+
this.discordCommandConfig = '';
|
|
249
489
|
// LINE slow-LLM postback button manager (null when LINE disabled or threshold=0).
|
|
250
490
|
this.lineReply = null;
|
|
251
491
|
// Slack has no reply-token TTL to work around (see the plan's "no reply-manager
|
|
@@ -357,6 +597,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
357
597
|
this.apiChatIds = new Map();
|
|
358
598
|
// Skill registry for detecting /skill-name commands in user messages
|
|
359
599
|
this.skillRegistry = { skills: new Map() };
|
|
600
|
+
this.skillNotificationOrigins = new Map();
|
|
360
601
|
// Cancel function for the daily history cleanup timer
|
|
361
602
|
this.cancelCleanup = null;
|
|
362
603
|
/**
|
|
@@ -399,7 +640,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
399
640
|
}
|
|
400
641
|
});
|
|
401
642
|
};
|
|
402
|
-
this.
|
|
643
|
+
this.telegramCommandConfig = '';
|
|
644
|
+
this.agentConfig = (0, gateway_config_1.applyGatewayOrchestration)(agentConfig, gatewayConfig);
|
|
403
645
|
this.gatewayConfig = gatewayConfig;
|
|
404
646
|
this.logger = logger ?? (0, logger_1.createLogger)(agentConfig.id, gatewayConfig.gateway.logDir);
|
|
405
647
|
// Resolve agentsBaseDir: workspace is at <agentsBaseDir>/<agentId>/workspace
|
|
@@ -428,6 +670,25 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
428
670
|
return this.skillRegistry;
|
|
429
671
|
}
|
|
430
672
|
/** Wire the skill-learning manager (planning-62). Optional; unset = feature off. */
|
|
673
|
+
async notifySkillLearning(sessionId, text) {
|
|
674
|
+
if (this.orchestration?.ownsSession(sessionId)) {
|
|
675
|
+
const conversation = this.orchestration.store.get('SELECT source,chat_id FROM conversations WHERE agent_session_id=?', sessionId);
|
|
676
|
+
const source = conversation?.source;
|
|
677
|
+
const index = source && source !== 'api' ? await this.sessionStore.loadIndex(this.agentConfig.id, String(conversation.chat_id), source) : undefined;
|
|
678
|
+
await this.orchestration.notifySession(sessionId, text, source === 'api' || index?.activeSessionId === sessionId);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const origin = this.skillNotificationOrigins.get(sessionId);
|
|
682
|
+
if (!origin)
|
|
683
|
+
return; // Diary only when the original destination is unavailable.
|
|
684
|
+
this.historyDb.insertMessage({ chatId: `${origin.source}-${origin.chatId}`, sessionId, source: origin.source, role: 'assistant', content: text, ts: Date.now() });
|
|
685
|
+
const index = await this.sessionStore.loadIndex(this.agentConfig.id, origin.chatId, origin.source);
|
|
686
|
+
if (index?.activeSessionId !== sessionId)
|
|
687
|
+
return;
|
|
688
|
+
const result = await (0, delivery_1.channelSender)(() => this.agentConfig)({ channel: origin.source, chat_id: origin.chatId, thread_key: origin.thread }, text, (0, crypto_1.randomUUID)());
|
|
689
|
+
if (result.state !== 'delivered')
|
|
690
|
+
this.logger.warn('Skill notification delivery failed', { sessionId, code: result.code });
|
|
691
|
+
}
|
|
431
692
|
setSkillLearning(manager) {
|
|
432
693
|
this.skillLearning = manager;
|
|
433
694
|
}
|
|
@@ -453,7 +714,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
453
714
|
req.on('data', (chunk) => {
|
|
454
715
|
raw += chunk;
|
|
455
716
|
});
|
|
456
|
-
req.on('end', () => {
|
|
717
|
+
req.on('end', async () => {
|
|
457
718
|
if (url.pathname === '/command') {
|
|
458
719
|
this.handleCommandRequest(raw, res);
|
|
459
720
|
return;
|
|
@@ -471,8 +732,6 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
471
732
|
// receiver (Bun) can try to reuse the stale socket → "socket connection
|
|
472
733
|
// was closed unexpectedly". Closing per-request avoids the race entirely.
|
|
473
734
|
res.setHeader('Connection', 'close');
|
|
474
|
-
res.writeHead(200);
|
|
475
|
-
res.end('ok');
|
|
476
735
|
try {
|
|
477
736
|
const params = JSON.parse(raw);
|
|
478
737
|
const meta = params.meta ?? {};
|
|
@@ -486,6 +745,87 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
486
745
|
// forget adding to a chain of `=== 'x' ? 'x' : ...` checks.
|
|
487
746
|
const channelSource = ((0, types_1.isChatChannel)(meta['source']) ? meta['source'] : 'telegram');
|
|
488
747
|
this.channelSourceMap.set(chatId, channelSource);
|
|
748
|
+
const channelOrchestration = this.orchestration?.ownsChannel(channelSource, chatId) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
|
|
749
|
+
if (channelSource !== 'telegram' && this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && /^\/sessions?(?:\s|$)/.test(content.trim())) {
|
|
750
|
+
const index = await this.sessionStore.listSessions(this.agentConfig.id, chatId, channelSource);
|
|
751
|
+
const current = index.sessions.find(session => session.id === index.activeSessionId);
|
|
752
|
+
const text = content.trim() === '/sessions'
|
|
753
|
+
? `Sessions\n${index.sessions.slice(0, 15).map(session => `${session.id === index.activeSessionId ? '✅ ' : ''}${session.name}\n${session.id}`).join('\n\n')}`
|
|
754
|
+
: `Current session: ${current?.name ?? '(unnamed)'}\n${index.activeSessionId}\nMode: Orchestration\nModel: ${this.agentConfig.claude.model}\nMessages: ${current?.messageCount ?? 0}\n\nCommands: /session /sessions /voice /voices /tasks /stop`;
|
|
755
|
+
await this.sendOrchestrationControl(channelSource, chatId, { text, buttons: [] }, meta);
|
|
756
|
+
res.writeHead(200);
|
|
757
|
+
res.end('ok');
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (/^\/task_question(?:\s|$)/.test(content.trim()) || (channelSource !== 'telegram' && /^\/orch\s+q:/.test(content.trim()))) {
|
|
761
|
+
let menu = { text: 'Task controls require orchestration mode.', buttons: [] };
|
|
762
|
+
if (channelOrchestration) {
|
|
763
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
|
|
764
|
+
const runtime = await this.getOrchestration();
|
|
765
|
+
const scope = { channel: channelSource, chatId, thread: channelSource === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : meta.thread_ts ?? meta.message_thread_id ?? '', sessionId, principalId: `${channelSource}:${meta.user_id ?? meta.user ?? chatId}` };
|
|
766
|
+
try {
|
|
767
|
+
menu = runtime.questionControls.handle(scope, content.trim()) ?? { text: 'Invalid task question command.', buttons: [] };
|
|
768
|
+
}
|
|
769
|
+
catch {
|
|
770
|
+
menu = { text: 'Question unavailable or already answered. Use /tasks to refresh.', buttons: [] };
|
|
771
|
+
}
|
|
772
|
+
await runtime.flushHistory();
|
|
773
|
+
}
|
|
774
|
+
const responseMeta = { ...meta };
|
|
775
|
+
delete responseMeta.control_message_id;
|
|
776
|
+
await this.sendOrchestrationControl(channelSource, chatId, menu, responseMeta);
|
|
777
|
+
res.writeHead(200);
|
|
778
|
+
res.end('ok');
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (channelSource !== 'telegram' && (/^\/(voice|voices|tasks|stop|orch)(?:\s|$)/.test(content.trim()) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && content.trim() === '/help'))) {
|
|
782
|
+
const enabled = !!(this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
|
|
783
|
+
if (enabled) {
|
|
784
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
|
|
785
|
+
const runtime = await this.getOrchestration();
|
|
786
|
+
const scope = { channel: channelSource, chatId, thread: meta.thread_ts ?? meta.message_thread_id ?? '', sessionId, principalId: `${channelSource}:${meta.user_id ?? meta.user ?? chatId}` };
|
|
787
|
+
let menu;
|
|
788
|
+
try {
|
|
789
|
+
menu = await runtime.channelControls.handle(scope, content.trim());
|
|
790
|
+
}
|
|
791
|
+
catch {
|
|
792
|
+
menu = { text: 'Control unavailable or expired. Please open the command again.', buttons: [] };
|
|
793
|
+
}
|
|
794
|
+
await this.sendOrchestrationControl(channelSource, chatId, menu, meta);
|
|
795
|
+
res.writeHead(200);
|
|
796
|
+
res.end('ok');
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
// Keep the legacy /stop path, but never turn disabled controls into model prompts.
|
|
800
|
+
if (!/^\/stop(?:\s|$)/.test(content.trim())) {
|
|
801
|
+
await this.sendOrchestrationControl(channelSource, chatId, { text: 'These controls require orchestration mode.', buttons: [] }, meta);
|
|
802
|
+
res.writeHead(200);
|
|
803
|
+
res.end('ok');
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (channelOrchestration && !(0, builtin_commands_1.isBuiltinCommand)(content.trim(), channelSource)) {
|
|
808
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
|
|
809
|
+
const orchestration = await this.getOrchestration();
|
|
810
|
+
const { media, quoted, details } = await (0, channel_input_media_1.channelInputMedia)(this.agentConfig, this.agentsBaseDir, channelSource, chatId, meta, AgentRunner.discardEphemeralStaging);
|
|
811
|
+
const senderId = meta.user_id ?? meta.user ?? chatId;
|
|
812
|
+
const accepted = orchestration.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: channelSource,
|
|
813
|
+
accountId: this.agentConfig.id, chatId, threadKey: channelSource === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : meta.thread_ts ?? meta.message_thread_id ?? '', principalId: `${channelSource}:${senderId}` },
|
|
814
|
+
text: content || '[Attachment inspection requested]', attachmentIds: media, trustedChannelMember: true,
|
|
815
|
+
modality: ['voice', 'audio'].includes(meta.media_type ?? meta.attachment_kind) ? 'voice_note' : 'text',
|
|
816
|
+
ingressKey: meta.message_id, metadata: { senderId, senderName: meta.sender_name, platformMessageId: meta.message_id, platformMessageIds: meta.message_ids_json ? JSON.parse(meta.message_ids_json) : undefined, mediaGroupId: meta.media_group_id,
|
|
817
|
+
attachmentName: meta.attachment_name, mediaType: meta.media_type ?? meta.attachment_kind, repliedText: meta.replied_text, repliedMessageId: meta.replied_message_id, repliedSender: meta.replied_user, repliedAttachmentIds: quoted, attachmentDetails: details, attachmentError: meta.attachment_error } }, { execute: this.agentConfig.allow_tools !== false, writeMemory: this.agentConfig.allow_tools !== false });
|
|
818
|
+
// The durable receipt precedes receiver acknowledgment and inference.
|
|
819
|
+
res.writeHead(200);
|
|
820
|
+
res.end('ok');
|
|
821
|
+
void accepted.response.catch(error => {
|
|
822
|
+
// The durable decision/outbox owns failure delivery and activity, including autonomous turns.
|
|
823
|
+
this.logger.error('Conversation channel turn failed', { chatId, code: error.code ?? 'ORCHESTRATION_ERROR', ...(error.timeout ? { timeout: error.timeout } : {}) });
|
|
824
|
+
});
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
res.writeHead(200);
|
|
828
|
+
res.end('ok');
|
|
489
829
|
// Slack: remember the current message's thread context so the
|
|
490
830
|
// auto-forward fallback (writeAutoForward) can stay in-thread. A
|
|
491
831
|
// top-level message clears it so a later fallback isn't misrouted
|
|
@@ -554,6 +894,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
554
894
|
return;
|
|
555
895
|
}
|
|
556
896
|
catch (err) {
|
|
897
|
+
if (!res.headersSent) {
|
|
898
|
+
res.writeHead(503);
|
|
899
|
+
res.end('Channel input was not accepted');
|
|
900
|
+
}
|
|
557
901
|
this.logger.warn('Failed to parse channel callback body', {
|
|
558
902
|
error: err.message,
|
|
559
903
|
});
|
|
@@ -597,6 +941,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
597
941
|
return;
|
|
598
942
|
}
|
|
599
943
|
const session = this.sessions.get(chatId);
|
|
944
|
+
if (session?.runtimeProfile || this.requiresDurableChannelIngress('telegram')) {
|
|
945
|
+
respond({ ok: false, error: 'Recovery is owned by orchestration; legacy turn replay is disabled.' });
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
600
948
|
const autoRecover = this.gatewayConfig.gateway.selfHealing?.autoRecover === true;
|
|
601
949
|
const req = {
|
|
602
950
|
incidentId: body.incidentId ?? '',
|
|
@@ -764,6 +1112,146 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
764
1112
|
return;
|
|
765
1113
|
}
|
|
766
1114
|
const command = body.command;
|
|
1115
|
+
if (command === 'telegram_voices') {
|
|
1116
|
+
const chatId = body.chat_id ?? '', payload = body.payload ?? {};
|
|
1117
|
+
if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
|
|
1118
|
+
respond({ success: false }, 400);
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
|
|
1122
|
+
respond({ success: false, error: 'Voice controls unavailable.' });
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
try {
|
|
1126
|
+
const voices = (await this.getOrchestration()).telegramVoices;
|
|
1127
|
+
if (payload.action === 'dismiss' && typeof payload.menu_id === 'string') {
|
|
1128
|
+
voices.dismiss(chatId, payload.menu_id);
|
|
1129
|
+
respond({ success: true });
|
|
1130
|
+
}
|
|
1131
|
+
else if (payload.action === 'choose' && typeof payload.menu_id === 'string' && typeof payload.index === 'number')
|
|
1132
|
+
respond({ success: true, ...await voices.choose(chatId, payload.menu_id, payload.index) });
|
|
1133
|
+
else
|
|
1134
|
+
respond({ success: true, ...await voices.menu(chatId, typeof payload.page === 'number' ? payload.page : 0, typeof payload.menu_id === 'string' ? payload.menu_id : undefined, typeof payload.gender === 'string' ? payload.gender : undefined) });
|
|
1135
|
+
}
|
|
1136
|
+
catch {
|
|
1137
|
+
respond({ success: false, error: 'Voice list unavailable or provider changed. Use /voices to refresh.' });
|
|
1138
|
+
}
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (command === 'telegram_question') {
|
|
1142
|
+
const chatId = body.chat_id ?? '', payload = body.payload ?? {};
|
|
1143
|
+
if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
|
|
1144
|
+
respond({ success: false }, 400);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
|
|
1148
|
+
respond({ success: false, error: 'Task controls require orchestration mode.' });
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (typeof payload.question_id !== 'string' || !['snooze', 'mute'].includes(String(payload.action))) {
|
|
1152
|
+
respond({ success: false, error: 'Invalid question command.' }, 400);
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
try {
|
|
1156
|
+
const runtime = await this.getOrchestration();
|
|
1157
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
|
|
1158
|
+
const menu = runtime.questionControls.handle({ channel: 'telegram', chatId, thread: typeof payload.thread === 'string' ? payload.thread : '', sessionId, principalId: `telegram:${payload.user_id}` }, `/task_question ${payload.question_id} ${payload.action}`);
|
|
1159
|
+
if (!menu)
|
|
1160
|
+
throw Error('INVALID_QUESTION');
|
|
1161
|
+
respond({ success: true, text: menu.text });
|
|
1162
|
+
}
|
|
1163
|
+
catch {
|
|
1164
|
+
respond({ success: false, error: 'Question unavailable or already answered. Use /tasks to refresh.' });
|
|
1165
|
+
}
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
if (command === 'telegram_tasks') {
|
|
1169
|
+
const chatId = body.chat_id ?? '', payload = body.payload ?? {}, principalId = `telegram:${payload.user_id ?? ''}`;
|
|
1170
|
+
if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
|
|
1171
|
+
respond({ success: false }, 400);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
|
|
1175
|
+
respond({ success: false, error: 'Task controls require orchestration mode.' });
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
try {
|
|
1179
|
+
const runtime = await this.getOrchestration();
|
|
1180
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
|
|
1181
|
+
if (payload.session_id !== undefined && payload.session_id !== sessionId) {
|
|
1182
|
+
respond({ success: false, error: 'TASK_SESSION_CHANGED' });
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (payload.action === 'detail' || payload.action === 'cancel') {
|
|
1186
|
+
if (typeof payload.task_id !== 'string')
|
|
1187
|
+
throw Error('INVALID_TASK');
|
|
1188
|
+
const task = payload.action === 'cancel' ? runtime.taskControls.cancel(sessionId, principalId, payload.task_id) : runtime.taskControls.detail(sessionId, principalId, payload.task_id);
|
|
1189
|
+
respond({ success: true, sessionId, task });
|
|
1190
|
+
}
|
|
1191
|
+
else
|
|
1192
|
+
respond({ success: true, sessionId, ...runtime.taskControls.list(sessionId, principalId, typeof payload.page === 'number' ? payload.page : 0) });
|
|
1193
|
+
}
|
|
1194
|
+
catch {
|
|
1195
|
+
respond({ success: false, error: 'Task unavailable. Use /tasks to refresh.' });
|
|
1196
|
+
}
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (command === 'task_stop') {
|
|
1200
|
+
const chatId = body.chat_id ?? '', principalId = `telegram:${body.payload?.user_id ?? ''}`;
|
|
1201
|
+
if (!/^\d+$/.test(chatId) || body.payload?.user_id !== chatId) {
|
|
1202
|
+
respond({ success: false, error: 'invalid_request' }, 400);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
try {
|
|
1206
|
+
if (!(this.agentConfig.orchestration?.enabled && this.agentConfig.orchestration.channels?.includes('telegram'))) {
|
|
1207
|
+
if (body.payload?.menu_id) {
|
|
1208
|
+
respond({ success: false, error: 'Orchestration is disabled.' });
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
await this.handleCommandStop(chatId);
|
|
1212
|
+
respond({ success: true, legacy: true });
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
const runtime = await this.getOrchestration();
|
|
1216
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
|
|
1217
|
+
if (typeof body.payload?.menu_id === 'string' && typeof body.payload?.index === 'number') {
|
|
1218
|
+
respond({ success: true, ...runtime.stopControls.choose(sessionId, principalId, body.payload.menu_id, body.payload.index) });
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
const menu = runtime.stopControls.open(sessionId, principalId);
|
|
1222
|
+
respond({ success: true, ...menu, text: (0, stop_controls_1.stopMenuText)(menu, true) });
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
catch {
|
|
1226
|
+
respond({ success: false, error: 'Selection unavailable. Use /stop to refresh the task list.' });
|
|
1227
|
+
}
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (command === 'telegram_voice') {
|
|
1231
|
+
const chatId = body.chat_id ?? '';
|
|
1232
|
+
const value = body.payload?.enabled;
|
|
1233
|
+
const mode = body.payload?.mode;
|
|
1234
|
+
if (!/^\d+$/.test(chatId) || (value !== undefined && typeof value !== 'boolean') || (mode !== undefined && !voice_reply_policy_1.VOICE_REPLY_MODES.includes(mode))) {
|
|
1235
|
+
respond({ success: false, error: 'invalid_request' }, 400);
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram') || !this.agentConfig.voice?.enabled || this.agentConfig.voice?.notes?.replyWithVoice === false) {
|
|
1239
|
+
respond({ success: false, error: 'voice_not_configured' });
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
try {
|
|
1243
|
+
const runtime = await this.getOrchestration();
|
|
1244
|
+
if (mode !== undefined)
|
|
1245
|
+
runtime.store.setTelegramVoiceMode(chatId, mode);
|
|
1246
|
+
else if (typeof value === 'boolean')
|
|
1247
|
+
runtime.store.setTelegramVoice(chatId, value);
|
|
1248
|
+
respond({ success: true, enabled: runtime.store.telegramVoice(chatId), mode: runtime.store.telegramVoiceMode(chatId) });
|
|
1249
|
+
}
|
|
1250
|
+
catch {
|
|
1251
|
+
respond({ success: false, error: 'voice_settings_unavailable' }, 500);
|
|
1252
|
+
}
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
767
1255
|
if (command === 'get_model') {
|
|
768
1256
|
respond({ model: this.agentConfig.claude.model });
|
|
769
1257
|
return;
|
|
@@ -796,6 +1284,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
796
1284
|
// its initData. The browser-facing routes live on the gateway; here we only
|
|
797
1285
|
// mint the pairing and hand back the phone-openable link.
|
|
798
1286
|
if (command === 'cli_pair') {
|
|
1287
|
+
if (this.gatewayConfig.gateway.headless !== false || this.agentConfig.type === 'app-agent' || this.agentConfig.orchestration?.enabled) {
|
|
1288
|
+
respond({ success: false, error: 'interactive_mode_required' });
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
799
1291
|
const payload = body.payload ?? {};
|
|
800
1292
|
const channel = payload['channel'];
|
|
801
1293
|
const userId = typeof payload['user_id'] === 'string' ? payload['user_id'] : '';
|
|
@@ -1240,6 +1732,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1240
1732
|
session.setProcessing(true);
|
|
1241
1733
|
const turnText = blocks.join('\n');
|
|
1242
1734
|
// Skill-learning: mark the start of a user turn (cheap, best-effort).
|
|
1735
|
+
const originMeta = entries[0]?.meta ?? {};
|
|
1736
|
+
this.skillNotificationOrigins.set(sessionId, { source: channelSource, chatId, thread: originMeta.thread_ts ?? originMeta.message_thread_id ?? '' });
|
|
1243
1737
|
this.skillLearning?.onTurnStart(chatId, sessionId, entries[0]?.content ?? '', invokedSkills);
|
|
1244
1738
|
session.sendMessage(turnText);
|
|
1245
1739
|
// Remember this turn for the C1 guarded resend (Phase 3b): reset the
|
|
@@ -2115,7 +2609,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2115
2609
|
// event. Start the typing-done timer here so the indicator stays alive
|
|
2116
2610
|
// through multi-turn tool-call sequences and only stops when all work is done.
|
|
2117
2611
|
if (obj['type'] === 'session_idle' && proc.backend === 'pty-shell') {
|
|
2118
|
-
//
|
|
2612
|
+
// An agent can become idle immediately after dispatching background
|
|
2119
2613
|
// Agent/Workflow work. Keep the Telegram typing signal and processing
|
|
2120
2614
|
// sentinel alive until the task notification starts its follow-up turn.
|
|
2121
2615
|
const waitingForBackgroundWork = proc.retainBackgroundWorkingState();
|
|
@@ -2171,7 +2665,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2171
2665
|
}, this.channelFor(mapKey)).catch(() => { });
|
|
2172
2666
|
}
|
|
2173
2667
|
proc.on('backgroundWorkExpired', () => {
|
|
2174
|
-
// The child task never notified its
|
|
2668
|
+
// The child task never notified its agent within the bounded grace
|
|
2175
2669
|
// window; release the Telegram typing signal instead of leaving it live.
|
|
2176
2670
|
this.writeTypingDone(mapKey);
|
|
2177
2671
|
});
|
|
@@ -2236,6 +2730,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2236
2730
|
});
|
|
2237
2731
|
return proc;
|
|
2238
2732
|
}
|
|
2733
|
+
apiTaskStopReply(sessionId, principalId, text) {
|
|
2734
|
+
return this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') ? this.orchestration?.stopControls.replyCommand(sessionId, principalId, text) : undefined;
|
|
2735
|
+
}
|
|
2239
2736
|
static isApiBuiltinCommand(content) {
|
|
2240
2737
|
return (0, builtin_commands_1.isBuiltinCommand)(content, 'api');
|
|
2241
2738
|
}
|
|
@@ -2435,6 +2932,13 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2435
2932
|
* "No turn in progress." that fired whenever interrupt() no-op'd mid-spawn.
|
|
2436
2933
|
*/
|
|
2437
2934
|
async handleCommandStop(chatId) {
|
|
2935
|
+
if (this.orchestration && this.agentConfig.orchestration?.enabled) {
|
|
2936
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, this.channelFor(chatId));
|
|
2937
|
+
if (this.orchestration.stopResponse(sessionId)) {
|
|
2938
|
+
this.writeAutoForward(chatId, 'Response stopped. Tasks remain active.');
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2438
2942
|
// Cancel queued + buffered work first so nothing flushes in behind the interrupt.
|
|
2439
2943
|
const buffered = this.channelCoalesce.get(chatId);
|
|
2440
2944
|
if (buffered?.timer)
|
|
@@ -2816,7 +3320,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2816
3320
|
const { immediate, deferred } = await this.restartOrDefer({
|
|
2817
3321
|
skipBusy: false,
|
|
2818
3322
|
deferIdle: true,
|
|
2819
|
-
|
|
3323
|
+
// Managed turns use a fresh subprocess/config on their next decision or task.
|
|
3324
|
+
// Never send them through legacy restart/replay while effects may be in flight.
|
|
3325
|
+
filter: (proc) => !proc.runtimeProfile && proc.connectorConfigChanged(connectorId, target),
|
|
2820
3326
|
});
|
|
2821
3327
|
return { restarted: immediate + deferred > 0 };
|
|
2822
3328
|
}
|
|
@@ -3043,7 +3549,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3043
3549
|
startIdleCleaner() {
|
|
3044
3550
|
this.idleCleanerTimer = setInterval(async () => {
|
|
3045
3551
|
for (const [id, proc] of this.sessions) {
|
|
3046
|
-
//
|
|
3552
|
+
// An agent that just dispatched Agent/Workflow/Monitor work is CLI-idle
|
|
3047
3553
|
// but that dispatch may still need this session to receive its
|
|
3048
3554
|
// eventual task notification.
|
|
3049
3555
|
if (proc.hasLikelyOutstandingBackgroundWork())
|
|
@@ -3058,16 +3564,20 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3058
3564
|
}, 5 * 60 * 1000);
|
|
3059
3565
|
}
|
|
3060
3566
|
async start() {
|
|
3567
|
+
if (this.agentConfig.orchestration?.enabled || fs.existsSync(path.join(this.agentDir, 'orchestration.db')))
|
|
3568
|
+
await this.getOrchestration();
|
|
3061
3569
|
this.stopping = false;
|
|
3062
3570
|
await this.sweepStaleSessionDirs();
|
|
3063
3571
|
await this.startCallbackServer();
|
|
3064
3572
|
if (this.agentConfig.telegram?.botToken) {
|
|
3065
|
-
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
3573
|
+
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3066
3574
|
this.receiver.start();
|
|
3575
|
+
this.telegramCommandConfig = this.telegramCommandsKey();
|
|
3067
3576
|
}
|
|
3068
3577
|
if (this.agentConfig.discord?.botToken) {
|
|
3069
|
-
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
3578
|
+
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3070
3579
|
this.discordReceiver.start();
|
|
3580
|
+
this.discordCommandConfig = this.telegramCommandsKey();
|
|
3071
3581
|
}
|
|
3072
3582
|
// LINE slow-LLM postback button: gateway-side token lifecycle + cache.
|
|
3073
3583
|
// Enabled for any LINE agent unless its threshold is 0 (then the MCP
|
|
@@ -3128,8 +3638,34 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3128
3638
|
}
|
|
3129
3639
|
}
|
|
3130
3640
|
}
|
|
3641
|
+
telegramCommandsKey() {
|
|
3642
|
+
return JSON.stringify([this.gatewayConfig.gateway.headless !== false, this.agentConfig.type,
|
|
3643
|
+
Boolean(this.agentConfig.orchestration?.enabled), this.agentConfig.orchestration?.channels ?? ['api']]);
|
|
3644
|
+
}
|
|
3645
|
+
/** Reload only the receiver when command visibility changes; Agent/Worker work continues. */
|
|
3646
|
+
refreshTelegramCommands() {
|
|
3647
|
+
const discord = this.discordReceiver, discordKey = this.telegramCommandsKey();
|
|
3648
|
+
if (discord && discordKey !== this.discordCommandConfig) {
|
|
3649
|
+
this.discordCommandConfig = discordKey;
|
|
3650
|
+
void discord.stop().then(() => { if (this.discordReceiver !== discord || this.stopping)
|
|
3651
|
+
return; this.discordReceiver = null; this.startDiscordReceiver(); });
|
|
3652
|
+
}
|
|
3653
|
+
const previous = this.receiver, key = this.telegramCommandsKey();
|
|
3654
|
+
if (!previous || key === this.telegramCommandConfig)
|
|
3655
|
+
return;
|
|
3656
|
+
this.telegramCommandConfig = key;
|
|
3657
|
+
void previous.stop().then(() => {
|
|
3658
|
+
if (this.receiver !== previous || this.stopping)
|
|
3659
|
+
return;
|
|
3660
|
+
this.receiver = null;
|
|
3661
|
+
this.startTelegramReceiver();
|
|
3662
|
+
});
|
|
3663
|
+
}
|
|
3131
3664
|
updateAgentConfig(newConfig) {
|
|
3665
|
+
newConfig = (0, gateway_config_1.applyGatewayOrchestration)(newConfig, this.gatewayConfig);
|
|
3666
|
+
this.orchestration?.updateAgentConfig(newConfig);
|
|
3132
3667
|
this.agentConfig = newConfig;
|
|
3668
|
+
this.refreshTelegramCommands();
|
|
3133
3669
|
// Restart LineReplyManager if the LINE config changed so the live instance
|
|
3134
3670
|
// picks up a new access token, threshold, or labels without a full restart.
|
|
3135
3671
|
this.stopLineReply();
|
|
@@ -3381,6 +3917,25 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3381
3917
|
});
|
|
3382
3918
|
}
|
|
3383
3919
|
}
|
|
3920
|
+
if (update.reply) {
|
|
3921
|
+
if (update.reply.messageId)
|
|
3922
|
+
meta.replied_message_id = update.reply.messageId;
|
|
3923
|
+
if (update.reply.text)
|
|
3924
|
+
meta.replied_text = update.reply.text;
|
|
3925
|
+
const ref = update.reply.image ?? update.reply.file;
|
|
3926
|
+
if (ref)
|
|
3927
|
+
try {
|
|
3928
|
+
const bytes = await (0, ilink_client_1.downloadWeixinImage)(ref, { maxBytes: media_store_1.MediaStore.maxUploadBytes });
|
|
3929
|
+
const name = update.reply.file?.fileName.replace(/[^a-zA-Z0-9_.-]/g, '_') ?? 'image.jpg';
|
|
3930
|
+
const dest = path.join(os.tmpdir(), `wechat-reply-${safeId}-${Date.now()}-${name}`);
|
|
3931
|
+
await fsPromises.writeFile(dest, bytes, { mode: 0o600 });
|
|
3932
|
+
meta.replied_image_path = dest;
|
|
3933
|
+
meta.media_ephemeral = '1';
|
|
3934
|
+
}
|
|
3935
|
+
catch {
|
|
3936
|
+
meta.attachment_error = 'The quoted attachment could not be downloaded. Do not claim to have read it.';
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3384
3939
|
try {
|
|
3385
3940
|
await fetch(`http://127.0.0.1:${this.callbackPort}/channel`, {
|
|
3386
3941
|
method: 'POST',
|
|
@@ -3458,8 +4013,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3458
4013
|
return;
|
|
3459
4014
|
if (!this.agentConfig.telegram?.botToken)
|
|
3460
4015
|
return;
|
|
3461
|
-
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
4016
|
+
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3462
4017
|
this.receiver.start();
|
|
4018
|
+
this.telegramCommandConfig = this.telegramCommandsKey();
|
|
3463
4019
|
this.logger.info('TelegramReceiver hot-started', { agentId: this.agentConfig.id });
|
|
3464
4020
|
}
|
|
3465
4021
|
stopTelegramReceiver() {
|
|
@@ -3477,8 +4033,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3477
4033
|
return;
|
|
3478
4034
|
if (!this.agentConfig.discord?.botToken)
|
|
3479
4035
|
return;
|
|
3480
|
-
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
4036
|
+
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3481
4037
|
this.discordReceiver.start();
|
|
4038
|
+
this.discordCommandConfig = this.telegramCommandsKey();
|
|
3482
4039
|
this.logger.info('DiscordReceiver hot-started', { agentId: this.agentConfig.id });
|
|
3483
4040
|
}
|
|
3484
4041
|
stopDiscordReceiver() {
|
|
@@ -3493,6 +4050,12 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3493
4050
|
}
|
|
3494
4051
|
async stop() {
|
|
3495
4052
|
this.stopping = true;
|
|
4053
|
+
if (this.orchestrationStarting) {
|
|
4054
|
+
const orchestration = await this.orchestrationStarting.catch(() => undefined);
|
|
4055
|
+
await orchestration?.close();
|
|
4056
|
+
this.orchestration = undefined;
|
|
4057
|
+
this.orchestrationStarting = undefined;
|
|
4058
|
+
}
|
|
3496
4059
|
if (this.idleCleanerTimer !== null) {
|
|
3497
4060
|
clearInterval(this.idleCleanerTimer);
|
|
3498
4061
|
this.idleCleanerTimer = null;
|
|
@@ -3575,6 +4138,17 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3575
4138
|
isRunning() {
|
|
3576
4139
|
return this.receiver?.isRunning() ?? false;
|
|
3577
4140
|
}
|
|
4141
|
+
getOrchestrationSummary() {
|
|
4142
|
+
if (!this.agentConfig.orchestration?.enabled && !this.orchestration)
|
|
4143
|
+
return undefined;
|
|
4144
|
+
const summary = this.orchestration?.dashboardSummary() ?? { enabled: true, backend: 'headless', workspaceMode: this.agentConfig.type === 'app-agent' ? 'container' : this.agentConfig.orchestration?.tasks?.workspaceMode ?? 'host', activeAgentSessions: [], tasks: [], sessions: [] };
|
|
4145
|
+
const processes = [...this.sessions.values()].filter(p => p.runtimeProfile?.role === 'agent' && p.isRunning());
|
|
4146
|
+
return { ...summary, sessions: summary.sessions.map(s => {
|
|
4147
|
+
const p = processes.find(p => p.sessionId === s.sessionId);
|
|
4148
|
+
return { ...s, model: p?.model ?? s.model, tokens: p?.totalTokens ?? 0, spawnedAt: p?.spawnedAt ?? 0,
|
|
4149
|
+
uptimeSec: p ? Math.floor((Date.now() - p.spawnedAt) / 1000) : 0 };
|
|
4150
|
+
}), agentProcesses: processes.map(p => ({ sessionId: p.sessionId, hostProcessId: p.processId, container: this.agentConfig.container })) };
|
|
4151
|
+
}
|
|
3578
4152
|
getSessionsSummary() {
|
|
3579
4153
|
const now = Date.now();
|
|
3580
4154
|
// A single logical session can appear multiple times in the ring buffer: a
|
|
@@ -3612,6 +4186,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3612
4186
|
* - Appends user message and assistant reply to SessionStore for history persistence.
|
|
3613
4187
|
*/
|
|
3614
4188
|
async sendApiMessage(sessionId, chatId, message, opts) {
|
|
4189
|
+
if (this.orchestrationForApi(sessionId))
|
|
4190
|
+
return this.sendOrchestratedApi(sessionId, chatId, message, opts);
|
|
3615
4191
|
if (this.pendingApiSessions.has(sessionId)) {
|
|
3616
4192
|
const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
|
|
3617
4193
|
throw err;
|
|
@@ -3894,6 +4470,18 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3894
4470
|
* expires (#421).
|
|
3895
4471
|
*/
|
|
3896
4472
|
async sendApiMessageStream(sessionId, chatId, message, callbacks, opts) {
|
|
4473
|
+
if (this.orchestrationForApi(sessionId)) {
|
|
4474
|
+
if (this.pendingApiSessions.has(sessionId))
|
|
4475
|
+
throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
|
|
4476
|
+
const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)('api', sessionId), opts.requestId ?? (0, crypto_1.randomUUID)());
|
|
4477
|
+
const sink = (0, turn_stream_1.callbackSink)(callbacks);
|
|
4478
|
+
turn.attach(sink, 0);
|
|
4479
|
+
void this.sendOrchestratedApi(sessionId, chatId, message, { ...opts, requestId: turn.requestId }, text => turn.emit({ type: 'text_delta', text }), event => { if (event.type === 'tool_use')
|
|
4480
|
+
turn.emit({ ...event, type: 'tool_use' }); })
|
|
4481
|
+
.then(result => this.turnStreams.complete(turn, (0, turn_stream_1.resultEvent)(result.text, result.attachments)))
|
|
4482
|
+
.catch(error => this.turnStreams.complete(turn, (0, turn_stream_1.errorEvent)(error), error));
|
|
4483
|
+
return () => turn.detach(sink);
|
|
4484
|
+
}
|
|
3897
4485
|
if (this.pendingApiSessions.has(sessionId)) {
|
|
3898
4486
|
const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
|
|
3899
4487
|
throw err;
|
|
@@ -4365,6 +4953,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4365
4953
|
if (!AgentRunner.isApiBuiltinCommand(cmd)) {
|
|
4366
4954
|
throw new Error(`Unknown command: ${cmd}`);
|
|
4367
4955
|
}
|
|
4956
|
+
if (cmd === '/stop' && this.agentConfig.orchestration?.enabled && opts?.principalId)
|
|
4957
|
+
this.orchestration?.authorizeSession(sessionId, opts.principalId);
|
|
4368
4958
|
// Register session in the api-{chatId} index on first use (same as sendApiMessageStream)
|
|
4369
4959
|
await this.sessionStore.ensureApiSession(agentId, storeChatId, sessionId).catch((err) => {
|
|
4370
4960
|
this.logger.warn('Failed to register API session in index for command', { agentId, chatId, sessionId, error: err.message });
|
|
@@ -4387,7 +4977,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4387
4977
|
// Persist the full user command before executing so it appears in history.
|
|
4388
4978
|
// Skip for /clear — clearSession() below wipes the table anyway; only the response survives.
|
|
4389
4979
|
if (cmd !== '/clear') {
|
|
4390
|
-
persist('user', command);
|
|
4980
|
+
persist('user', opts?.displayCommand ?? command);
|
|
4391
4981
|
}
|
|
4392
4982
|
let result;
|
|
4393
4983
|
let responseText;
|
|
@@ -4402,17 +4992,41 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4402
4992
|
: `Current model: ${model}`;
|
|
4403
4993
|
}
|
|
4404
4994
|
else if (cmd === '/stop') {
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4995
|
+
if (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') && this.orchestration && opts?.principalId) {
|
|
4996
|
+
const selection = /^\/stop\s+([a-f0-9-]{36})\s+(\d+)$/.exec(command.trim());
|
|
4997
|
+
if (selection) {
|
|
4998
|
+
try {
|
|
4999
|
+
const selected = this.orchestration.stopControls.choose(sessionId, opts.principalId, selection[1], Number(selection[2]));
|
|
5000
|
+
result = { ...selected };
|
|
5001
|
+
responseText = selected.text;
|
|
5002
|
+
}
|
|
5003
|
+
catch {
|
|
5004
|
+
result = { error: 'STOP_SELECTION_UNAVAILABLE' };
|
|
5005
|
+
responseText = 'Selection unavailable. Use /stop to refresh the task list.';
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
else {
|
|
5009
|
+
const menu = this.orchestration.stopControls.open(sessionId, opts.principalId);
|
|
5010
|
+
responseText = (0, stop_controls_1.stopMenuText)(menu);
|
|
5011
|
+
result = { ...menu, responseText };
|
|
5012
|
+
}
|
|
5013
|
+
forcePersist = true;
|
|
5014
|
+
}
|
|
5015
|
+
else {
|
|
5016
|
+
const session = this.sessions.get(sessionId);
|
|
5017
|
+
const stopped = this.agentConfig.orchestration?.enabled && this.orchestration?.isBusy(sessionId)
|
|
5018
|
+
? this.orchestration.stopResponse(sessionId)
|
|
5019
|
+
: session ? session.interrupt() : false;
|
|
5020
|
+
result = { stopped };
|
|
5021
|
+
responseText = stopped
|
|
5022
|
+
? 'Session was interrupted before I could respond.'
|
|
5023
|
+
: 'No active session to stop.';
|
|
5024
|
+
// A turn was actually cut off — always leave a visible note in history so the
|
|
5025
|
+
// web Stop button (skipPersist:true, to suppress the "/stop" command echo) and
|
|
5026
|
+
// a typed /stop command show the same outcome instead of the button leaving a
|
|
5027
|
+
// silently-dangling user turn.
|
|
5028
|
+
forcePersist = stopped;
|
|
5029
|
+
}
|
|
4416
5030
|
}
|
|
4417
5031
|
else if (cmd === '/restart') {
|
|
4418
5032
|
this.restartProcess(sessionId).catch(() => { });
|
|
@@ -4692,6 +5306,100 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4692
5306
|
const mediaPaths = this.historyDb.clearSession(`api-${chatId}`, sessionId);
|
|
4693
5307
|
media_store_1.MediaStore.deleteMediaFiles(this.agentsBaseDir, this.agentConfig.id, mediaPaths);
|
|
4694
5308
|
}
|
|
5309
|
+
/** The route has already authorized this API principal for the Agent. Preserve
|
|
5310
|
+
* the original channel binding, but never impersonate its human sender. */
|
|
5311
|
+
async sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
|
|
5312
|
+
if (!opts.principalId)
|
|
5313
|
+
throw new Error('Authenticated principal required for conversation orchestration');
|
|
5314
|
+
const runtime = await this.getOrchestration();
|
|
5315
|
+
const rows = runtime.store.all('SELECT * FROM conversations WHERE agent_session_id=?', sessionId);
|
|
5316
|
+
if (rows.length > 1 || rows.some(row => row.source !== channel || row.chat_id !== rawChatId))
|
|
5317
|
+
throw new Error('Ambiguous or mismatched channel session');
|
|
5318
|
+
if (!rows.length) {
|
|
5319
|
+
const index = await this.sessionStore.loadIndex(this.agentConfig.id, rawChatId, channel);
|
|
5320
|
+
if (!index?.sessions.some(session => session.id === sessionId))
|
|
5321
|
+
throw new Error('Session not found');
|
|
5322
|
+
}
|
|
5323
|
+
const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
|
|
5324
|
+
const accepted = runtime.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId,
|
|
5325
|
+
source: channel, accountId: String(rows[0]?.account_id ?? this.agentConfig.id), chatId: rawChatId,
|
|
5326
|
+
threadKey: String(rows[0]?.thread_key ?? ''), principalId: opts.principalId },
|
|
5327
|
+
text: message, requestId, trustedChannelMember: true, metadata: { senderName, senderId: opts.principalId } }, { execute: opts.allowTools ?? false, writeMemory: false });
|
|
5328
|
+
const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)(channel, sessionId), requestId);
|
|
5329
|
+
const sink = (0, turn_stream_1.callbackSink)(callbacks);
|
|
5330
|
+
turn.attach(sink, 0);
|
|
5331
|
+
const displayed = new Map();
|
|
5332
|
+
const unsubscribe = runtime.subscribeText(sessionId, opts.principalId, event => {
|
|
5333
|
+
if (!runtime.responseBelongsToInput(accepted.inputId, event.responseId))
|
|
5334
|
+
return;
|
|
5335
|
+
const previous = displayed.get(event.responseId) ?? '';
|
|
5336
|
+
if (event.text.startsWith(previous)) {
|
|
5337
|
+
const delta = event.text.slice(previous.length);
|
|
5338
|
+
displayed.set(event.responseId, event.text);
|
|
5339
|
+
if (delta)
|
|
5340
|
+
turn.emit({ type: 'text_delta', text: delta });
|
|
5341
|
+
}
|
|
5342
|
+
});
|
|
5343
|
+
const input = runtime.store.get('SELECT conversation_id FROM conversation_inputs WHERE id=?', accepted.inputId);
|
|
5344
|
+
const conversation = runtime.store.assertMember(String(input.conversation_id), opts.principalId);
|
|
5345
|
+
let cursor = { streamId: String(conversation.stream_id), seq: Number(conversation.last_event_seq) };
|
|
5346
|
+
const events = runtime.events.subscribe(String(conversation.id), opts.principalId, cursor);
|
|
5347
|
+
let closed = false;
|
|
5348
|
+
let timer;
|
|
5349
|
+
const close = () => { closed = true; clearTimeout(timer); unsubscribe(); events.close(); };
|
|
5350
|
+
const forward = (page) => {
|
|
5351
|
+
if (page.snapshotRequired)
|
|
5352
|
+
throw new Error('Tool activity history expired; reload session activity');
|
|
5353
|
+
for (const event of page.events) {
|
|
5354
|
+
if (event.seq <= cursor.seq)
|
|
5355
|
+
continue;
|
|
5356
|
+
const payload = event.payload;
|
|
5357
|
+
if (event.type === 'tool.activity' && payload.type === 'tool_use')
|
|
5358
|
+
turn.emit({ ...payload, type: 'tool_use' });
|
|
5359
|
+
cursor = { streamId: page.cursor.streamId, seq: event.seq };
|
|
5360
|
+
}
|
|
5361
|
+
};
|
|
5362
|
+
const drain = () => {
|
|
5363
|
+
while (true) {
|
|
5364
|
+
const page = runtime.events.read(String(conversation.id), opts.principalId, cursor);
|
|
5365
|
+
forward(page);
|
|
5366
|
+
if (!page.snapshotRequired && !page.events.length)
|
|
5367
|
+
break;
|
|
5368
|
+
}
|
|
5369
|
+
};
|
|
5370
|
+
const fail = (error) => {
|
|
5371
|
+
if (closed)
|
|
5372
|
+
return;
|
|
5373
|
+
try {
|
|
5374
|
+
drain();
|
|
5375
|
+
}
|
|
5376
|
+
catch { /* Preserve the original stream/provider failure. */ }
|
|
5377
|
+
this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.errorEvent)(error), error);
|
|
5378
|
+
close();
|
|
5379
|
+
};
|
|
5380
|
+
void (async () => {
|
|
5381
|
+
for await (const page of events.pages) {
|
|
5382
|
+
if (closed)
|
|
5383
|
+
break;
|
|
5384
|
+
forward(page);
|
|
5385
|
+
}
|
|
5386
|
+
})().catch(fail);
|
|
5387
|
+
timer = setTimeout(() => fail(Object.assign(new Error('Agent response timeout'), { code: 'TIMEOUT_SOFT' })), opts.timeoutMs);
|
|
5388
|
+
void accepted.response.then(text => {
|
|
5389
|
+
if (closed)
|
|
5390
|
+
return;
|
|
5391
|
+
try {
|
|
5392
|
+
// Drain before terminal success, even when completion precedes the next poll.
|
|
5393
|
+
drain();
|
|
5394
|
+
this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.resultEvent)(text, []));
|
|
5395
|
+
close();
|
|
5396
|
+
}
|
|
5397
|
+
catch (error) {
|
|
5398
|
+
fail(error instanceof Error ? error : new Error('Tool activity stream failed'));
|
|
5399
|
+
}
|
|
5400
|
+
}, fail);
|
|
5401
|
+
return () => { turn.detach(sink); }; // Durable work and original channel delivery survive disconnect.
|
|
5402
|
+
}
|
|
4695
5403
|
/**
|
|
4696
5404
|
* Send a message into an existing channel session (cross-channel continuation from UI).
|
|
4697
5405
|
* The session process receives full history context from the session JSON (Layer 1).
|
|
@@ -4701,6 +5409,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4701
5409
|
* parser — and writing here as well produced a second row per turn. See done().
|
|
4702
5410
|
*/
|
|
4703
5411
|
async sendMessageToSession(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
|
|
5412
|
+
const managed = (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channel)) ||
|
|
5413
|
+
(this.orchestration?.ownsSession(sessionId) && !this.orchestration.canReturnToLegacy());
|
|
5414
|
+
if (managed)
|
|
5415
|
+
return this.sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts);
|
|
4704
5416
|
// Ensure the session process uses the correct channel source
|
|
4705
5417
|
this.channelSourceMap.set(rawChatId, channel);
|
|
4706
5418
|
// Channel sessions use agent-level model (not per-session)
|