@0xmaxma/claude-gateway 1.8.14 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1613
- package/config.template.json +7 -1
- package/dist/agent/runner.d.ts +377 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +680 -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 +135 -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 +256 -0
- package/dist/orchestration/config.d.ts.map +1 -0
- package/dist/orchestration/config.js +110 -0
- package/dist/orchestration/config.js.map +1 -0
- package/dist/orchestration/container.d.ts +21 -0
- package/dist/orchestration/container.d.ts.map +1 -0
- package/dist/orchestration/container.js +195 -0
- package/dist/orchestration/container.js.map +1 -0
- package/dist/orchestration/control-delivery.d.ts +5 -0
- package/dist/orchestration/control-delivery.d.ts.map +1 -0
- package/dist/orchestration/control-delivery.js +50 -0
- package/dist/orchestration/control-delivery.js.map +1 -0
- package/dist/orchestration/conversation-intake.d.ts +32 -0
- package/dist/orchestration/conversation-intake.d.ts.map +1 -0
- package/dist/orchestration/conversation-intake.js +92 -0
- package/dist/orchestration/conversation-intake.js.map +1 -0
- package/dist/orchestration/decisions.d.ts +27 -0
- package/dist/orchestration/decisions.d.ts.map +1 -0
- package/dist/orchestration/decisions.js +148 -0
- package/dist/orchestration/decisions.js.map +1 -0
- package/dist/orchestration/delivery.d.ts +30 -0
- package/dist/orchestration/delivery.d.ts.map +1 -0
- package/dist/orchestration/delivery.js +219 -0
- package/dist/orchestration/delivery.js.map +1 -0
- package/dist/orchestration/display-stream.d.ts +4 -0
- package/dist/orchestration/display-stream.d.ts.map +1 -0
- package/dist/orchestration/display-stream.js +77 -0
- package/dist/orchestration/display-stream.js.map +1 -0
- package/dist/orchestration/events.d.ts +30 -0
- package/dist/orchestration/events.d.ts.map +1 -0
- package/dist/orchestration/events.js +65 -0
- package/dist/orchestration/events.js.map +1 -0
- package/dist/orchestration/execution-observation.d.ts +38 -0
- package/dist/orchestration/execution-observation.d.ts.map +1 -0
- package/dist/orchestration/execution-observation.js +25 -0
- package/dist/orchestration/execution-observation.js.map +1 -0
- package/dist/orchestration/file-delivery.d.ts +16 -0
- package/dist/orchestration/file-delivery.d.ts.map +1 -0
- package/dist/orchestration/file-delivery.js +139 -0
- package/dist/orchestration/file-delivery.js.map +1 -0
- package/dist/orchestration/gateway-config.d.ts +17 -0
- package/dist/orchestration/gateway-config.d.ts.map +1 -0
- package/dist/orchestration/gateway-config.js +128 -0
- package/dist/orchestration/gateway-config.js.map +1 -0
- package/dist/orchestration/history.d.ts +13 -0
- package/dist/orchestration/history.d.ts.map +1 -0
- package/dist/orchestration/history.js +48 -0
- package/dist/orchestration/history.js.map +1 -0
- package/dist/orchestration/identity.d.ts +6 -0
- package/dist/orchestration/identity.d.ts.map +1 -0
- package/dist/orchestration/identity.js +13 -0
- package/dist/orchestration/identity.js.map +1 -0
- package/dist/orchestration/inference-errors.d.ts +3 -0
- package/dist/orchestration/inference-errors.d.ts.map +1 -0
- package/dist/orchestration/inference-errors.js +16 -0
- package/dist/orchestration/inference-errors.js.map +1 -0
- package/dist/orchestration/input-images.d.ts +12 -0
- package/dist/orchestration/input-images.d.ts.map +1 -0
- package/dist/orchestration/input-images.js +66 -0
- package/dist/orchestration/input-images.js.map +1 -0
- package/dist/orchestration/instance-lock.d.ts +5 -0
- package/dist/orchestration/instance-lock.d.ts.map +1 -0
- package/dist/orchestration/instance-lock.js +33 -0
- package/dist/orchestration/instance-lock.js.map +1 -0
- package/dist/orchestration/line-loading.d.ts +21 -0
- package/dist/orchestration/line-loading.d.ts.map +1 -0
- package/dist/orchestration/line-loading.js +89 -0
- package/dist/orchestration/line-loading.js.map +1 -0
- package/dist/orchestration/media.d.ts +3 -0
- package/dist/orchestration/media.d.ts.map +1 -0
- package/dist/orchestration/media.js +46 -0
- package/dist/orchestration/media.js.map +1 -0
- package/dist/orchestration/migrations/schema.d.ts +3 -0
- package/dist/orchestration/migrations/schema.d.ts.map +1 -0
- package/dist/orchestration/migrations/schema.js +80 -0
- package/dist/orchestration/migrations/schema.js.map +1 -0
- package/dist/orchestration/notification-mailbox.d.ts +5 -0
- package/dist/orchestration/notification-mailbox.d.ts.map +1 -0
- package/dist/orchestration/notification-mailbox.js +29 -0
- package/dist/orchestration/notification-mailbox.js.map +1 -0
- package/dist/orchestration/process-activity.d.ts +23 -0
- package/dist/orchestration/process-activity.d.ts.map +1 -0
- package/dist/orchestration/process-activity.js +95 -0
- package/dist/orchestration/process-activity.js.map +1 -0
- package/dist/orchestration/process-supervisor.d.ts +15 -0
- package/dist/orchestration/process-supervisor.d.ts.map +1 -0
- package/dist/orchestration/process-supervisor.js +92 -0
- package/dist/orchestration/process-supervisor.js.map +1 -0
- package/dist/orchestration/process-turn.d.ts +35 -0
- package/dist/orchestration/process-turn.d.ts.map +1 -0
- package/dist/orchestration/process-turn.js +246 -0
- package/dist/orchestration/process-turn.js.map +1 -0
- package/dist/orchestration/progress-review.d.ts +28 -0
- package/dist/orchestration/progress-review.d.ts.map +1 -0
- package/dist/orchestration/progress-review.js +48 -0
- package/dist/orchestration/progress-review.js.map +1 -0
- package/dist/orchestration/recovery.d.ts +8 -0
- package/dist/orchestration/recovery.d.ts.map +1 -0
- package/dist/orchestration/recovery.js +35 -0
- package/dist/orchestration/recovery.js.map +1 -0
- package/dist/orchestration/reply-context.d.ts +7 -0
- package/dist/orchestration/reply-context.d.ts.map +1 -0
- package/dist/orchestration/reply-context.js +54 -0
- package/dist/orchestration/reply-context.js.map +1 -0
- package/dist/orchestration/runtime.d.ts +232 -0
- package/dist/orchestration/runtime.d.ts.map +1 -0
- package/dist/orchestration/runtime.js +962 -0
- package/dist/orchestration/runtime.js.map +1 -0
- package/dist/orchestration/skills.d.ts +17 -0
- package/dist/orchestration/skills.d.ts.map +1 -0
- package/dist/orchestration/skills.js +32 -0
- package/dist/orchestration/skills.js.map +1 -0
- package/dist/orchestration/source-policy.d.ts +6 -0
- package/dist/orchestration/source-policy.d.ts.map +1 -0
- package/dist/orchestration/source-policy.js +16 -0
- package/dist/orchestration/source-policy.js.map +1 -0
- package/dist/orchestration/speech.d.ts +24 -0
- package/dist/orchestration/speech.d.ts.map +1 -0
- package/dist/orchestration/speech.js +61 -0
- package/dist/orchestration/speech.js.map +1 -0
- package/dist/orchestration/stop-controls.d.ts +30 -0
- package/dist/orchestration/stop-controls.d.ts.map +1 -0
- package/dist/orchestration/stop-controls.js +79 -0
- package/dist/orchestration/stop-controls.js.map +1 -0
- package/dist/orchestration/store.d.ts +86 -0
- package/dist/orchestration/store.d.ts.map +1 -0
- package/dist/orchestration/store.js +275 -0
- package/dist/orchestration/store.js.map +1 -0
- package/dist/orchestration/task-controls.d.ts +130 -0
- package/dist/orchestration/task-controls.d.ts.map +1 -0
- package/dist/orchestration/task-controls.js +55 -0
- package/dist/orchestration/task-controls.js.map +1 -0
- package/dist/orchestration/task-files.d.ts +29 -0
- package/dist/orchestration/task-files.d.ts.map +1 -0
- package/dist/orchestration/task-files.js +200 -0
- package/dist/orchestration/task-files.js.map +1 -0
- package/dist/orchestration/task-report.d.ts +7 -0
- package/dist/orchestration/task-report.d.ts.map +1 -0
- package/dist/orchestration/task-report.js +33 -0
- package/dist/orchestration/task-report.js.map +1 -0
- package/dist/orchestration/tasks/checkpoint-hook.d.ts +19 -0
- package/dist/orchestration/tasks/checkpoint-hook.d.ts.map +1 -0
- package/dist/orchestration/tasks/checkpoint-hook.js +49 -0
- package/dist/orchestration/tasks/checkpoint-hook.js.map +1 -0
- package/dist/orchestration/tasks/cleanup.d.ts +18 -0
- package/dist/orchestration/tasks/cleanup.d.ts.map +1 -0
- package/dist/orchestration/tasks/cleanup.js +113 -0
- package/dist/orchestration/tasks/cleanup.js.map +1 -0
- package/dist/orchestration/tasks/driver.d.ts +23 -0
- package/dist/orchestration/tasks/driver.d.ts.map +1 -0
- package/dist/orchestration/tasks/driver.js +209 -0
- package/dist/orchestration/tasks/driver.js.map +1 -0
- package/dist/orchestration/tasks/failure.d.ts +5 -0
- package/dist/orchestration/tasks/failure.d.ts.map +1 -0
- package/dist/orchestration/tasks/failure.js +17 -0
- package/dist/orchestration/tasks/failure.js.map +1 -0
- package/dist/orchestration/tasks/pool.d.ts +18 -0
- package/dist/orchestration/tasks/pool.d.ts.map +1 -0
- package/dist/orchestration/tasks/pool.js +73 -0
- package/dist/orchestration/tasks/pool.js.map +1 -0
- package/dist/orchestration/tasks/scheduler.d.ts +38 -0
- package/dist/orchestration/tasks/scheduler.d.ts.map +1 -0
- package/dist/orchestration/tasks/scheduler.js +160 -0
- package/dist/orchestration/tasks/scheduler.js.map +1 -0
- package/dist/orchestration/tasks/service.d.ts +109 -0
- package/dist/orchestration/tasks/service.d.ts.map +1 -0
- package/dist/orchestration/tasks/service.js +697 -0
- package/dist/orchestration/tasks/service.js.map +1 -0
- package/dist/orchestration/tasks/shared-workspace.d.ts +25 -0
- package/dist/orchestration/tasks/shared-workspace.d.ts.map +1 -0
- package/dist/orchestration/tasks/shared-workspace.js +162 -0
- package/dist/orchestration/tasks/shared-workspace.js.map +1 -0
- package/dist/orchestration/tasks/task-directive.d.ts +6 -0
- package/dist/orchestration/tasks/task-directive.d.ts.map +1 -0
- package/dist/orchestration/tasks/task-directive.js +41 -0
- package/dist/orchestration/tasks/task-directive.js.map +1 -0
- package/dist/orchestration/tasks/timing.d.ts +15 -0
- package/dist/orchestration/tasks/timing.d.ts.map +1 -0
- package/dist/orchestration/tasks/timing.js +48 -0
- package/dist/orchestration/tasks/timing.js.map +1 -0
- package/dist/orchestration/tasks/tool-repetition.d.ts +3 -0
- package/dist/orchestration/tasks/tool-repetition.d.ts.map +1 -0
- package/dist/orchestration/tasks/tool-repetition.js +30 -0
- package/dist/orchestration/tasks/tool-repetition.js.map +1 -0
- package/dist/orchestration/tasks/workspace.d.ts +24 -0
- package/dist/orchestration/tasks/workspace.d.ts.map +1 -0
- package/dist/orchestration/tasks/workspace.js +147 -0
- package/dist/orchestration/tasks/workspace.js.map +1 -0
- package/dist/orchestration/telegram-speech.d.ts +17 -0
- package/dist/orchestration/telegram-speech.d.ts.map +1 -0
- package/dist/orchestration/telegram-speech.js +55 -0
- package/dist/orchestration/telegram-speech.js.map +1 -0
- package/dist/orchestration/telegram-tool-status.d.ts +23 -0
- package/dist/orchestration/telegram-tool-status.d.ts.map +1 -0
- package/dist/orchestration/telegram-tool-status.js +207 -0
- package/dist/orchestration/telegram-tool-status.js.map +1 -0
- package/dist/orchestration/telegram-voices.d.ts +42 -0
- package/dist/orchestration/telegram-voices.d.ts.map +1 -0
- package/dist/orchestration/telegram-voices.js +70 -0
- package/dist/orchestration/telegram-voices.js.map +1 -0
- package/dist/orchestration/tool-activity.d.ts +10 -0
- package/dist/orchestration/tool-activity.d.ts.map +1 -0
- package/dist/orchestration/tool-activity.js +38 -0
- package/dist/orchestration/tool-activity.js.map +1 -0
- package/dist/orchestration/types.d.ts +177 -0
- package/dist/orchestration/types.d.ts.map +1 -0
- package/dist/orchestration/types.js +13 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/orchestration/voice-reply-policy.d.ts +8 -0
- package/dist/orchestration/voice-reply-policy.d.ts.map +1 -0
- package/dist/orchestration/voice-reply-policy.js +46 -0
- package/dist/orchestration/voice-reply-policy.js.map +1 -0
- package/dist/orchestration/worker-shares.d.ts +8 -0
- package/dist/orchestration/worker-shares.d.ts.map +1 -0
- package/dist/orchestration/worker-shares.js +59 -0
- package/dist/orchestration/worker-shares.js.map +1 -0
- package/dist/orchestration/workflow.d.ts +121 -0
- package/dist/orchestration/workflow.d.ts.map +1 -0
- package/dist/orchestration/workflow.js +69 -0
- package/dist/orchestration/workflow.js.map +1 -0
- package/dist/session/input-image.d.ts +10 -0
- package/dist/session/input-image.d.ts.map +1 -0
- package/dist/session/input-image.js +3 -0
- package/dist/session/input-image.js.map +1 -0
- package/dist/session/process.d.ts +15 -3
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +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 +232 -11
- package/mcp/tools/telegram/selection-confirmation.ts +20 -0
- package/mcp/tools/telegram/task-browser.ts +117 -0
- package/mcp/tools/telegram/task-detail.ts +36 -0
- package/mcp/tools/telegram/task-elapsed.ts +1 -0
- package/mcp/tools/telegram/typing.ts +6 -116
- package/mcp/tools/video/module.ts +1 -1
- package/mcp/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,229 @@ 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 (!['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}` : '' }, 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
|
+
stopVoiceResponse(sessionId) { this.orchestration?.stopResponse(sessionId); }
|
|
400
|
+
async sendOrchestratedApi(sessionId, chatId, message, opts, onText, onTool) {
|
|
401
|
+
const semantic = this.agentConfig.orchestration?.conversation?.semanticIntake === true;
|
|
402
|
+
if (!semantic && this.pendingApiSessions.has(sessionId))
|
|
403
|
+
throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
|
|
404
|
+
if (!opts.principalId)
|
|
405
|
+
throw new Error('Authenticated principal required for conversation orchestration');
|
|
406
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
407
|
+
if (!semantic)
|
|
408
|
+
this.pendingApiSessions.add(sessionId); // reserve before the first await
|
|
409
|
+
try {
|
|
410
|
+
const orchestration = await this.getOrchestration();
|
|
411
|
+
await this.sessionStore.ensureApiSession(this.agentConfig.id, chatId, sessionId);
|
|
412
|
+
this.apiChatIds.set(sessionId, chatId);
|
|
413
|
+
const media = opts.mediaFiles?.length ? await promoteUiUploads(this.agentsBaseDir, this.agentConfig.id, sessionId, opts.mediaFiles, this.logger) : [];
|
|
414
|
+
const index = await this.sessionStore.loadIndex(this.agentConfig.id, chatId, 'api');
|
|
415
|
+
const saved = index?.sessions.find(session => session.id === sessionId)?.imageConfig;
|
|
416
|
+
const selected = { ...(saved ? AgentRunner.durableImageConfig(saved) : {}), ...opts.imageParams };
|
|
417
|
+
const imageParams = AgentRunner.remapImageParamsRefs(Object.keys(selected).length ? selected : undefined, opts.mediaFiles, media);
|
|
418
|
+
const videoParams = AgentRunner.remapVideoParamsRefs(opts.videoParams, opts.mediaFiles, media);
|
|
419
|
+
if (videoParams)
|
|
420
|
+
await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { videoConfig: videoParams }, 'api');
|
|
421
|
+
const imageConfig = opts.imageParams && imageParams ? AgentRunner.durableImageConfig(imageParams) : undefined;
|
|
422
|
+
if (imageConfig)
|
|
423
|
+
await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { imageConfig }, 'api');
|
|
424
|
+
const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
|
|
425
|
+
const scopedInput = { scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: 'api', accountId: opts.principalId,
|
|
426
|
+
chatId, threadKey: '', principalId: opts.principalId }, text: message || '[Attachment inspection requested]', attachmentIds: media,
|
|
427
|
+
requestId, storeUserMessage: !opts.skipUserMessage,
|
|
428
|
+
metadata: imageParams || videoParams ? { promptContext: (imageParams ? AgentRunner.buildImageParamsNote(imageParams) : '') + (videoParams ? AgentRunner.buildVideoParamsNote(videoParams) : ''), imageRefs: imageParams?.image_refs } : undefined };
|
|
429
|
+
let text;
|
|
430
|
+
if (semantic) {
|
|
431
|
+
const accepted = orchestration.submitInput({ ...scopedInput, model: opts.model }, { execute: opts.allowTools ?? false, writeMemory: false }, onTool);
|
|
432
|
+
const lengths = new Map();
|
|
433
|
+
const unsubscribe = orchestration.subscribeText(sessionId, opts.principalId, event => {
|
|
434
|
+
if (!orchestration.responseBelongsToInput(accepted.inputId, event.responseId) || !event.text)
|
|
435
|
+
return;
|
|
436
|
+
const before = lengths.get(event.responseId) ?? 0;
|
|
437
|
+
if (event.text.length > before)
|
|
438
|
+
onText?.(event.text.slice(before));
|
|
439
|
+
lengths.set(event.responseId, event.text.length);
|
|
440
|
+
});
|
|
441
|
+
try {
|
|
442
|
+
text = await accepted.response;
|
|
443
|
+
}
|
|
444
|
+
finally {
|
|
445
|
+
unsubscribe();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else
|
|
449
|
+
text = await orchestration.send(scopedInput, { execute: opts.allowTools ?? false, writeMemory: false }, { timeoutMs: opts.timeoutMs, model: opts.model, onText, onTool });
|
|
450
|
+
if (opts.waitForTasks)
|
|
451
|
+
text = await orchestration.waitForTaskReport(sessionId, opts.principalId, requestId, text, deadline);
|
|
452
|
+
this.addApiAttachments(sessionId, orchestration.responseFiles(sessionId, requestId).map(ref => media_store_1.MediaStore.resolvePath(this.agentsBaseDir, this.agentConfig.id, ref)));
|
|
453
|
+
return { text, attachments: this.popApiAttachments(sessionId) };
|
|
454
|
+
}
|
|
455
|
+
finally {
|
|
456
|
+
if (!semantic)
|
|
457
|
+
this.pendingApiSessions.delete(sessionId);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
229
460
|
constructor(agentConfig, gatewayConfig, logger) {
|
|
230
461
|
super();
|
|
231
462
|
this.stopping = false;
|
|
@@ -246,6 +477,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
246
477
|
this.slackThreadTs = new Map();
|
|
247
478
|
this.receiver = null;
|
|
248
479
|
this.discordReceiver = null;
|
|
480
|
+
this.discordCommandConfig = '';
|
|
249
481
|
// LINE slow-LLM postback button manager (null when LINE disabled or threshold=0).
|
|
250
482
|
this.lineReply = null;
|
|
251
483
|
// Slack has no reply-token TTL to work around (see the plan's "no reply-manager
|
|
@@ -357,6 +589,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
357
589
|
this.apiChatIds = new Map();
|
|
358
590
|
// Skill registry for detecting /skill-name commands in user messages
|
|
359
591
|
this.skillRegistry = { skills: new Map() };
|
|
592
|
+
this.skillNotificationOrigins = new Map();
|
|
360
593
|
// Cancel function for the daily history cleanup timer
|
|
361
594
|
this.cancelCleanup = null;
|
|
362
595
|
/**
|
|
@@ -399,7 +632,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
399
632
|
}
|
|
400
633
|
});
|
|
401
634
|
};
|
|
402
|
-
this.
|
|
635
|
+
this.telegramCommandConfig = '';
|
|
636
|
+
this.agentConfig = (0, gateway_config_1.applyGatewayOrchestration)(agentConfig, gatewayConfig);
|
|
403
637
|
this.gatewayConfig = gatewayConfig;
|
|
404
638
|
this.logger = logger ?? (0, logger_1.createLogger)(agentConfig.id, gatewayConfig.gateway.logDir);
|
|
405
639
|
// Resolve agentsBaseDir: workspace is at <agentsBaseDir>/<agentId>/workspace
|
|
@@ -428,6 +662,25 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
428
662
|
return this.skillRegistry;
|
|
429
663
|
}
|
|
430
664
|
/** Wire the skill-learning manager (planning-62). Optional; unset = feature off. */
|
|
665
|
+
async notifySkillLearning(sessionId, text) {
|
|
666
|
+
if (this.orchestration?.ownsSession(sessionId)) {
|
|
667
|
+
const conversation = this.orchestration.store.get('SELECT source,chat_id FROM conversations WHERE agent_session_id=?', sessionId);
|
|
668
|
+
const source = conversation?.source;
|
|
669
|
+
const index = source && source !== 'api' ? await this.sessionStore.loadIndex(this.agentConfig.id, String(conversation.chat_id), source) : undefined;
|
|
670
|
+
await this.orchestration.notifySession(sessionId, text, source === 'api' || index?.activeSessionId === sessionId);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
const origin = this.skillNotificationOrigins.get(sessionId);
|
|
674
|
+
if (!origin)
|
|
675
|
+
return; // Diary only when the original destination is unavailable.
|
|
676
|
+
this.historyDb.insertMessage({ chatId: `${origin.source}-${origin.chatId}`, sessionId, source: origin.source, role: 'assistant', content: text, ts: Date.now() });
|
|
677
|
+
const index = await this.sessionStore.loadIndex(this.agentConfig.id, origin.chatId, origin.source);
|
|
678
|
+
if (index?.activeSessionId !== sessionId)
|
|
679
|
+
return;
|
|
680
|
+
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)());
|
|
681
|
+
if (result.state !== 'delivered')
|
|
682
|
+
this.logger.warn('Skill notification delivery failed', { sessionId, code: result.code });
|
|
683
|
+
}
|
|
431
684
|
setSkillLearning(manager) {
|
|
432
685
|
this.skillLearning = manager;
|
|
433
686
|
}
|
|
@@ -453,7 +706,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
453
706
|
req.on('data', (chunk) => {
|
|
454
707
|
raw += chunk;
|
|
455
708
|
});
|
|
456
|
-
req.on('end', () => {
|
|
709
|
+
req.on('end', async () => {
|
|
457
710
|
if (url.pathname === '/command') {
|
|
458
711
|
this.handleCommandRequest(raw, res);
|
|
459
712
|
return;
|
|
@@ -471,8 +724,6 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
471
724
|
// receiver (Bun) can try to reuse the stale socket → "socket connection
|
|
472
725
|
// was closed unexpectedly". Closing per-request avoids the race entirely.
|
|
473
726
|
res.setHeader('Connection', 'close');
|
|
474
|
-
res.writeHead(200);
|
|
475
|
-
res.end('ok');
|
|
476
727
|
try {
|
|
477
728
|
const params = JSON.parse(raw);
|
|
478
729
|
const meta = params.meta ?? {};
|
|
@@ -486,6 +737,66 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
486
737
|
// forget adding to a chain of `=== 'x' ? 'x' : ...` checks.
|
|
487
738
|
const channelSource = ((0, types_1.isChatChannel)(meta['source']) ? meta['source'] : 'telegram');
|
|
488
739
|
this.channelSourceMap.set(chatId, channelSource);
|
|
740
|
+
const channelOrchestration = this.orchestration?.ownsChannel(channelSource, chatId) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
|
|
741
|
+
if (channelSource !== 'telegram' && this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && /^\/sessions?(?:\s|$)/.test(content.trim())) {
|
|
742
|
+
const index = await this.sessionStore.listSessions(this.agentConfig.id, chatId, channelSource);
|
|
743
|
+
const current = index.sessions.find(session => session.id === index.activeSessionId);
|
|
744
|
+
const text = content.trim() === '/sessions'
|
|
745
|
+
? `Sessions\n${index.sessions.slice(0, 15).map(session => `${session.id === index.activeSessionId ? '✅ ' : ''}${session.name}\n${session.id}`).join('\n\n')}`
|
|
746
|
+
: `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`;
|
|
747
|
+
await this.sendOrchestrationControl(channelSource, chatId, { text, buttons: [] }, meta);
|
|
748
|
+
res.writeHead(200);
|
|
749
|
+
res.end('ok');
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
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'))) {
|
|
753
|
+
const enabled = !!(this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
|
|
754
|
+
if (enabled) {
|
|
755
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
|
|
756
|
+
const runtime = await this.getOrchestration();
|
|
757
|
+
const scope = { channel: channelSource, chatId, thread: meta.thread_ts ?? meta.message_thread_id ?? '', sessionId, principalId: `${channelSource}:${meta.user_id ?? meta.user ?? chatId}` };
|
|
758
|
+
let menu;
|
|
759
|
+
try {
|
|
760
|
+
menu = await runtime.channelControls.handle(scope, content.trim());
|
|
761
|
+
}
|
|
762
|
+
catch {
|
|
763
|
+
menu = { text: 'Control unavailable or expired. Please open the command again.', buttons: [] };
|
|
764
|
+
}
|
|
765
|
+
await this.sendOrchestrationControl(channelSource, chatId, menu, meta);
|
|
766
|
+
res.writeHead(200);
|
|
767
|
+
res.end('ok');
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
// Keep the legacy /stop path, but never turn disabled controls into model prompts.
|
|
771
|
+
if (!/^\/stop(?:\s|$)/.test(content.trim())) {
|
|
772
|
+
await this.sendOrchestrationControl(channelSource, chatId, { text: 'These controls require orchestration mode.', buttons: [] }, meta);
|
|
773
|
+
res.writeHead(200);
|
|
774
|
+
res.end('ok');
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (channelOrchestration && !(0, builtin_commands_1.isBuiltinCommand)(content.trim(), channelSource)) {
|
|
779
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
|
|
780
|
+
const orchestration = await this.getOrchestration();
|
|
781
|
+
const { media, quoted, details } = await (0, channel_input_media_1.channelInputMedia)(this.agentConfig, this.agentsBaseDir, channelSource, chatId, meta, AgentRunner.discardEphemeralStaging);
|
|
782
|
+
const senderId = meta.user_id ?? meta.user ?? chatId;
|
|
783
|
+
const accepted = orchestration.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: channelSource,
|
|
784
|
+
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}` },
|
|
785
|
+
text: content || '[Attachment inspection requested]', attachmentIds: media, trustedChannelMember: true,
|
|
786
|
+
modality: ['voice', 'audio'].includes(meta.media_type ?? meta.attachment_kind) ? 'voice_note' : 'text',
|
|
787
|
+
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,
|
|
788
|
+
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 });
|
|
789
|
+
// The durable receipt precedes receiver acknowledgment and inference.
|
|
790
|
+
res.writeHead(200);
|
|
791
|
+
res.end('ok');
|
|
792
|
+
void accepted.response.catch(error => {
|
|
793
|
+
// The durable decision/outbox owns failure delivery and activity, including autonomous turns.
|
|
794
|
+
this.logger.error('Conversation channel turn failed', { chatId, code: error.code ?? 'ORCHESTRATION_ERROR', ...(error.timeout ? { timeout: error.timeout } : {}) });
|
|
795
|
+
});
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
res.writeHead(200);
|
|
799
|
+
res.end('ok');
|
|
489
800
|
// Slack: remember the current message's thread context so the
|
|
490
801
|
// auto-forward fallback (writeAutoForward) can stay in-thread. A
|
|
491
802
|
// top-level message clears it so a later fallback isn't misrouted
|
|
@@ -554,6 +865,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
554
865
|
return;
|
|
555
866
|
}
|
|
556
867
|
catch (err) {
|
|
868
|
+
if (!res.headersSent) {
|
|
869
|
+
res.writeHead(503);
|
|
870
|
+
res.end('Channel input was not accepted');
|
|
871
|
+
}
|
|
557
872
|
this.logger.warn('Failed to parse channel callback body', {
|
|
558
873
|
error: err.message,
|
|
559
874
|
});
|
|
@@ -597,6 +912,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
597
912
|
return;
|
|
598
913
|
}
|
|
599
914
|
const session = this.sessions.get(chatId);
|
|
915
|
+
if (session?.runtimeProfile || this.requiresDurableChannelIngress('telegram')) {
|
|
916
|
+
respond({ ok: false, error: 'Recovery is owned by orchestration; legacy turn replay is disabled.' });
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
600
919
|
const autoRecover = this.gatewayConfig.gateway.selfHealing?.autoRecover === true;
|
|
601
920
|
const req = {
|
|
602
921
|
incidentId: body.incidentId ?? '',
|
|
@@ -764,6 +1083,119 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
764
1083
|
return;
|
|
765
1084
|
}
|
|
766
1085
|
const command = body.command;
|
|
1086
|
+
if (command === 'telegram_voices') {
|
|
1087
|
+
const chatId = body.chat_id ?? '', payload = body.payload ?? {};
|
|
1088
|
+
if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
|
|
1089
|
+
respond({ success: false }, 400);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
|
|
1093
|
+
respond({ success: false, error: 'Voice controls unavailable.' });
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
try {
|
|
1097
|
+
const voices = (await this.getOrchestration()).telegramVoices;
|
|
1098
|
+
if (payload.action === 'dismiss' && typeof payload.menu_id === 'string') {
|
|
1099
|
+
voices.dismiss(chatId, payload.menu_id);
|
|
1100
|
+
respond({ success: true });
|
|
1101
|
+
}
|
|
1102
|
+
else if (payload.action === 'choose' && typeof payload.menu_id === 'string' && typeof payload.index === 'number')
|
|
1103
|
+
respond({ success: true, ...await voices.choose(chatId, payload.menu_id, payload.index) });
|
|
1104
|
+
else
|
|
1105
|
+
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) });
|
|
1106
|
+
}
|
|
1107
|
+
catch {
|
|
1108
|
+
respond({ success: false, error: 'Voice list unavailable or provider changed. Use /voices to refresh.' });
|
|
1109
|
+
}
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (command === 'telegram_tasks') {
|
|
1113
|
+
const chatId = body.chat_id ?? '', payload = body.payload ?? {}, principalId = `telegram:${payload.user_id ?? ''}`;
|
|
1114
|
+
if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
|
|
1115
|
+
respond({ success: false }, 400);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
|
|
1119
|
+
respond({ success: false, error: 'Task controls require orchestration mode.' });
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
try {
|
|
1123
|
+
const runtime = await this.getOrchestration();
|
|
1124
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
|
|
1125
|
+
if (payload.session_id !== undefined && payload.session_id !== sessionId) {
|
|
1126
|
+
respond({ success: false, error: 'TASK_SESSION_CHANGED' });
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
if (payload.action === 'detail' || payload.action === 'cancel') {
|
|
1130
|
+
if (typeof payload.task_id !== 'string')
|
|
1131
|
+
throw Error('INVALID_TASK');
|
|
1132
|
+
const task = payload.action === 'cancel' ? runtime.taskControls.cancel(sessionId, principalId, payload.task_id) : runtime.taskControls.detail(sessionId, principalId, payload.task_id);
|
|
1133
|
+
respond({ success: true, sessionId, task });
|
|
1134
|
+
}
|
|
1135
|
+
else
|
|
1136
|
+
respond({ success: true, sessionId, ...runtime.taskControls.list(sessionId, principalId, typeof payload.page === 'number' ? payload.page : 0) });
|
|
1137
|
+
}
|
|
1138
|
+
catch {
|
|
1139
|
+
respond({ success: false, error: 'Task unavailable. Use /tasks to refresh.' });
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
if (command === 'task_stop') {
|
|
1144
|
+
const chatId = body.chat_id ?? '', principalId = `telegram:${body.payload?.user_id ?? ''}`;
|
|
1145
|
+
if (!/^\d+$/.test(chatId) || body.payload?.user_id !== chatId) {
|
|
1146
|
+
respond({ success: false, error: 'invalid_request' }, 400);
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
if (!(this.agentConfig.orchestration?.enabled && this.agentConfig.orchestration.channels?.includes('telegram'))) {
|
|
1151
|
+
if (body.payload?.menu_id) {
|
|
1152
|
+
respond({ success: false, error: 'Orchestration is disabled.' });
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
await this.handleCommandStop(chatId);
|
|
1156
|
+
respond({ success: true, legacy: true });
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
const runtime = await this.getOrchestration();
|
|
1160
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
|
|
1161
|
+
if (typeof body.payload?.menu_id === 'string' && typeof body.payload?.index === 'number') {
|
|
1162
|
+
respond({ success: true, ...runtime.stopControls.choose(sessionId, principalId, body.payload.menu_id, body.payload.index) });
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
const menu = runtime.stopControls.open(sessionId, principalId);
|
|
1166
|
+
respond({ success: true, ...menu, text: (0, stop_controls_1.stopMenuText)(menu, true) });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
catch {
|
|
1170
|
+
respond({ success: false, error: 'Selection unavailable. Use /stop to refresh the task list.' });
|
|
1171
|
+
}
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if (command === 'telegram_voice') {
|
|
1175
|
+
const chatId = body.chat_id ?? '';
|
|
1176
|
+
const value = body.payload?.enabled;
|
|
1177
|
+
const mode = body.payload?.mode;
|
|
1178
|
+
if (!/^\d+$/.test(chatId) || (value !== undefined && typeof value !== 'boolean') || (mode !== undefined && !voice_reply_policy_1.VOICE_REPLY_MODES.includes(mode))) {
|
|
1179
|
+
respond({ success: false, error: 'invalid_request' }, 400);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram') || !this.agentConfig.voice?.enabled || this.agentConfig.voice?.notes?.replyWithVoice === false) {
|
|
1183
|
+
respond({ success: false, error: 'voice_not_configured' });
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
try {
|
|
1187
|
+
const runtime = await this.getOrchestration();
|
|
1188
|
+
if (mode !== undefined)
|
|
1189
|
+
runtime.store.setTelegramVoiceMode(chatId, mode);
|
|
1190
|
+
else if (typeof value === 'boolean')
|
|
1191
|
+
runtime.store.setTelegramVoice(chatId, value);
|
|
1192
|
+
respond({ success: true, enabled: runtime.store.telegramVoice(chatId), mode: runtime.store.telegramVoiceMode(chatId) });
|
|
1193
|
+
}
|
|
1194
|
+
catch {
|
|
1195
|
+
respond({ success: false, error: 'voice_settings_unavailable' }, 500);
|
|
1196
|
+
}
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
767
1199
|
if (command === 'get_model') {
|
|
768
1200
|
respond({ model: this.agentConfig.claude.model });
|
|
769
1201
|
return;
|
|
@@ -796,6 +1228,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
796
1228
|
// its initData. The browser-facing routes live on the gateway; here we only
|
|
797
1229
|
// mint the pairing and hand back the phone-openable link.
|
|
798
1230
|
if (command === 'cli_pair') {
|
|
1231
|
+
if (this.gatewayConfig.gateway.headless !== false || this.agentConfig.type === 'app-agent' || this.agentConfig.orchestration?.enabled) {
|
|
1232
|
+
respond({ success: false, error: 'interactive_mode_required' });
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
799
1235
|
const payload = body.payload ?? {};
|
|
800
1236
|
const channel = payload['channel'];
|
|
801
1237
|
const userId = typeof payload['user_id'] === 'string' ? payload['user_id'] : '';
|
|
@@ -1240,6 +1676,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1240
1676
|
session.setProcessing(true);
|
|
1241
1677
|
const turnText = blocks.join('\n');
|
|
1242
1678
|
// Skill-learning: mark the start of a user turn (cheap, best-effort).
|
|
1679
|
+
const originMeta = entries[0]?.meta ?? {};
|
|
1680
|
+
this.skillNotificationOrigins.set(sessionId, { source: channelSource, chatId, thread: originMeta.thread_ts ?? originMeta.message_thread_id ?? '' });
|
|
1243
1681
|
this.skillLearning?.onTurnStart(chatId, sessionId, entries[0]?.content ?? '', invokedSkills);
|
|
1244
1682
|
session.sendMessage(turnText);
|
|
1245
1683
|
// Remember this turn for the C1 guarded resend (Phase 3b): reset the
|
|
@@ -2115,7 +2553,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2115
2553
|
// event. Start the typing-done timer here so the indicator stays alive
|
|
2116
2554
|
// through multi-turn tool-call sequences and only stops when all work is done.
|
|
2117
2555
|
if (obj['type'] === 'session_idle' && proc.backend === 'pty-shell') {
|
|
2118
|
-
//
|
|
2556
|
+
// An agent can become idle immediately after dispatching background
|
|
2119
2557
|
// Agent/Workflow work. Keep the Telegram typing signal and processing
|
|
2120
2558
|
// sentinel alive until the task notification starts its follow-up turn.
|
|
2121
2559
|
const waitingForBackgroundWork = proc.retainBackgroundWorkingState();
|
|
@@ -2171,7 +2609,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2171
2609
|
}, this.channelFor(mapKey)).catch(() => { });
|
|
2172
2610
|
}
|
|
2173
2611
|
proc.on('backgroundWorkExpired', () => {
|
|
2174
|
-
// The child task never notified its
|
|
2612
|
+
// The child task never notified its agent within the bounded grace
|
|
2175
2613
|
// window; release the Telegram typing signal instead of leaving it live.
|
|
2176
2614
|
this.writeTypingDone(mapKey);
|
|
2177
2615
|
});
|
|
@@ -2236,6 +2674,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2236
2674
|
});
|
|
2237
2675
|
return proc;
|
|
2238
2676
|
}
|
|
2677
|
+
apiTaskStopReply(sessionId, principalId, text) {
|
|
2678
|
+
return this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') ? this.orchestration?.stopControls.replyCommand(sessionId, principalId, text) : undefined;
|
|
2679
|
+
}
|
|
2239
2680
|
static isApiBuiltinCommand(content) {
|
|
2240
2681
|
return (0, builtin_commands_1.isBuiltinCommand)(content, 'api');
|
|
2241
2682
|
}
|
|
@@ -2435,6 +2876,13 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2435
2876
|
* "No turn in progress." that fired whenever interrupt() no-op'd mid-spawn.
|
|
2436
2877
|
*/
|
|
2437
2878
|
async handleCommandStop(chatId) {
|
|
2879
|
+
if (this.orchestration && this.agentConfig.orchestration?.enabled) {
|
|
2880
|
+
const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, this.channelFor(chatId));
|
|
2881
|
+
if (this.orchestration.stopResponse(sessionId)) {
|
|
2882
|
+
this.writeAutoForward(chatId, 'Response stopped. Tasks remain active.');
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2438
2886
|
// Cancel queued + buffered work first so nothing flushes in behind the interrupt.
|
|
2439
2887
|
const buffered = this.channelCoalesce.get(chatId);
|
|
2440
2888
|
if (buffered?.timer)
|
|
@@ -2816,7 +3264,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2816
3264
|
const { immediate, deferred } = await this.restartOrDefer({
|
|
2817
3265
|
skipBusy: false,
|
|
2818
3266
|
deferIdle: true,
|
|
2819
|
-
|
|
3267
|
+
// Managed turns use a fresh subprocess/config on their next decision or task.
|
|
3268
|
+
// Never send them through legacy restart/replay while effects may be in flight.
|
|
3269
|
+
filter: (proc) => !proc.runtimeProfile && proc.connectorConfigChanged(connectorId, target),
|
|
2820
3270
|
});
|
|
2821
3271
|
return { restarted: immediate + deferred > 0 };
|
|
2822
3272
|
}
|
|
@@ -3043,7 +3493,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3043
3493
|
startIdleCleaner() {
|
|
3044
3494
|
this.idleCleanerTimer = setInterval(async () => {
|
|
3045
3495
|
for (const [id, proc] of this.sessions) {
|
|
3046
|
-
//
|
|
3496
|
+
// An agent that just dispatched Agent/Workflow/Monitor work is CLI-idle
|
|
3047
3497
|
// but that dispatch may still need this session to receive its
|
|
3048
3498
|
// eventual task notification.
|
|
3049
3499
|
if (proc.hasLikelyOutstandingBackgroundWork())
|
|
@@ -3058,16 +3508,20 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3058
3508
|
}, 5 * 60 * 1000);
|
|
3059
3509
|
}
|
|
3060
3510
|
async start() {
|
|
3511
|
+
if (this.agentConfig.orchestration?.enabled || fs.existsSync(path.join(this.agentDir, 'orchestration.db')))
|
|
3512
|
+
await this.getOrchestration();
|
|
3061
3513
|
this.stopping = false;
|
|
3062
3514
|
await this.sweepStaleSessionDirs();
|
|
3063
3515
|
await this.startCallbackServer();
|
|
3064
3516
|
if (this.agentConfig.telegram?.botToken) {
|
|
3065
|
-
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
3517
|
+
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3066
3518
|
this.receiver.start();
|
|
3519
|
+
this.telegramCommandConfig = this.telegramCommandsKey();
|
|
3067
3520
|
}
|
|
3068
3521
|
if (this.agentConfig.discord?.botToken) {
|
|
3069
|
-
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
3522
|
+
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3070
3523
|
this.discordReceiver.start();
|
|
3524
|
+
this.discordCommandConfig = this.telegramCommandsKey();
|
|
3071
3525
|
}
|
|
3072
3526
|
// LINE slow-LLM postback button: gateway-side token lifecycle + cache.
|
|
3073
3527
|
// Enabled for any LINE agent unless its threshold is 0 (then the MCP
|
|
@@ -3128,8 +3582,34 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3128
3582
|
}
|
|
3129
3583
|
}
|
|
3130
3584
|
}
|
|
3585
|
+
telegramCommandsKey() {
|
|
3586
|
+
return JSON.stringify([this.gatewayConfig.gateway.headless !== false, this.agentConfig.type,
|
|
3587
|
+
Boolean(this.agentConfig.orchestration?.enabled), this.agentConfig.orchestration?.channels ?? ['api']]);
|
|
3588
|
+
}
|
|
3589
|
+
/** Reload only the receiver when command visibility changes; Agent/Worker work continues. */
|
|
3590
|
+
refreshTelegramCommands() {
|
|
3591
|
+
const discord = this.discordReceiver, discordKey = this.telegramCommandsKey();
|
|
3592
|
+
if (discord && discordKey !== this.discordCommandConfig) {
|
|
3593
|
+
this.discordCommandConfig = discordKey;
|
|
3594
|
+
void discord.stop().then(() => { if (this.discordReceiver !== discord || this.stopping)
|
|
3595
|
+
return; this.discordReceiver = null; this.startDiscordReceiver(); });
|
|
3596
|
+
}
|
|
3597
|
+
const previous = this.receiver, key = this.telegramCommandsKey();
|
|
3598
|
+
if (!previous || key === this.telegramCommandConfig)
|
|
3599
|
+
return;
|
|
3600
|
+
this.telegramCommandConfig = key;
|
|
3601
|
+
void previous.stop().then(() => {
|
|
3602
|
+
if (this.receiver !== previous || this.stopping)
|
|
3603
|
+
return;
|
|
3604
|
+
this.receiver = null;
|
|
3605
|
+
this.startTelegramReceiver();
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3131
3608
|
updateAgentConfig(newConfig) {
|
|
3609
|
+
newConfig = (0, gateway_config_1.applyGatewayOrchestration)(newConfig, this.gatewayConfig);
|
|
3610
|
+
this.orchestration?.updateAgentConfig(newConfig);
|
|
3132
3611
|
this.agentConfig = newConfig;
|
|
3612
|
+
this.refreshTelegramCommands();
|
|
3133
3613
|
// Restart LineReplyManager if the LINE config changed so the live instance
|
|
3134
3614
|
// picks up a new access token, threshold, or labels without a full restart.
|
|
3135
3615
|
this.stopLineReply();
|
|
@@ -3381,6 +3861,25 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3381
3861
|
});
|
|
3382
3862
|
}
|
|
3383
3863
|
}
|
|
3864
|
+
if (update.reply) {
|
|
3865
|
+
if (update.reply.messageId)
|
|
3866
|
+
meta.replied_message_id = update.reply.messageId;
|
|
3867
|
+
if (update.reply.text)
|
|
3868
|
+
meta.replied_text = update.reply.text;
|
|
3869
|
+
const ref = update.reply.image ?? update.reply.file;
|
|
3870
|
+
if (ref)
|
|
3871
|
+
try {
|
|
3872
|
+
const bytes = await (0, ilink_client_1.downloadWeixinImage)(ref, { maxBytes: media_store_1.MediaStore.maxUploadBytes });
|
|
3873
|
+
const name = update.reply.file?.fileName.replace(/[^a-zA-Z0-9_.-]/g, '_') ?? 'image.jpg';
|
|
3874
|
+
const dest = path.join(os.tmpdir(), `wechat-reply-${safeId}-${Date.now()}-${name}`);
|
|
3875
|
+
await fsPromises.writeFile(dest, bytes, { mode: 0o600 });
|
|
3876
|
+
meta.replied_image_path = dest;
|
|
3877
|
+
meta.media_ephemeral = '1';
|
|
3878
|
+
}
|
|
3879
|
+
catch {
|
|
3880
|
+
meta.attachment_error = 'The quoted attachment could not be downloaded. Do not claim to have read it.';
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3384
3883
|
try {
|
|
3385
3884
|
await fetch(`http://127.0.0.1:${this.callbackPort}/channel`, {
|
|
3386
3885
|
method: 'POST',
|
|
@@ -3458,8 +3957,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3458
3957
|
return;
|
|
3459
3958
|
if (!this.agentConfig.telegram?.botToken)
|
|
3460
3959
|
return;
|
|
3461
|
-
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
3960
|
+
this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3462
3961
|
this.receiver.start();
|
|
3962
|
+
this.telegramCommandConfig = this.telegramCommandsKey();
|
|
3463
3963
|
this.logger.info('TelegramReceiver hot-started', { agentId: this.agentConfig.id });
|
|
3464
3964
|
}
|
|
3465
3965
|
stopTelegramReceiver() {
|
|
@@ -3477,8 +3977,9 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3477
3977
|
return;
|
|
3478
3978
|
if (!this.agentConfig.discord?.botToken)
|
|
3479
3979
|
return;
|
|
3480
|
-
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
|
|
3980
|
+
this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
|
|
3481
3981
|
this.discordReceiver.start();
|
|
3982
|
+
this.discordCommandConfig = this.telegramCommandsKey();
|
|
3482
3983
|
this.logger.info('DiscordReceiver hot-started', { agentId: this.agentConfig.id });
|
|
3483
3984
|
}
|
|
3484
3985
|
stopDiscordReceiver() {
|
|
@@ -3493,6 +3994,12 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3493
3994
|
}
|
|
3494
3995
|
async stop() {
|
|
3495
3996
|
this.stopping = true;
|
|
3997
|
+
if (this.orchestrationStarting) {
|
|
3998
|
+
const orchestration = await this.orchestrationStarting.catch(() => undefined);
|
|
3999
|
+
await orchestration?.close();
|
|
4000
|
+
this.orchestration = undefined;
|
|
4001
|
+
this.orchestrationStarting = undefined;
|
|
4002
|
+
}
|
|
3496
4003
|
if (this.idleCleanerTimer !== null) {
|
|
3497
4004
|
clearInterval(this.idleCleanerTimer);
|
|
3498
4005
|
this.idleCleanerTimer = null;
|
|
@@ -3575,6 +4082,17 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3575
4082
|
isRunning() {
|
|
3576
4083
|
return this.receiver?.isRunning() ?? false;
|
|
3577
4084
|
}
|
|
4085
|
+
getOrchestrationSummary() {
|
|
4086
|
+
if (!this.agentConfig.orchestration?.enabled && !this.orchestration)
|
|
4087
|
+
return undefined;
|
|
4088
|
+
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: [] };
|
|
4089
|
+
const processes = [...this.sessions.values()].filter(p => p.runtimeProfile?.role === 'agent' && p.isRunning());
|
|
4090
|
+
return { ...summary, sessions: summary.sessions.map(s => {
|
|
4091
|
+
const p = processes.find(p => p.sessionId === s.sessionId);
|
|
4092
|
+
return { ...s, model: p?.model ?? s.model, tokens: p?.totalTokens ?? 0, spawnedAt: p?.spawnedAt ?? 0,
|
|
4093
|
+
uptimeSec: p ? Math.floor((Date.now() - p.spawnedAt) / 1000) : 0 };
|
|
4094
|
+
}), agentProcesses: processes.map(p => ({ sessionId: p.sessionId, hostProcessId: p.processId, container: this.agentConfig.container })) };
|
|
4095
|
+
}
|
|
3578
4096
|
getSessionsSummary() {
|
|
3579
4097
|
const now = Date.now();
|
|
3580
4098
|
// A single logical session can appear multiple times in the ring buffer: a
|
|
@@ -3612,6 +4130,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3612
4130
|
* - Appends user message and assistant reply to SessionStore for history persistence.
|
|
3613
4131
|
*/
|
|
3614
4132
|
async sendApiMessage(sessionId, chatId, message, opts) {
|
|
4133
|
+
if (this.orchestrationForApi(sessionId))
|
|
4134
|
+
return this.sendOrchestratedApi(sessionId, chatId, message, opts);
|
|
3615
4135
|
if (this.pendingApiSessions.has(sessionId)) {
|
|
3616
4136
|
const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
|
|
3617
4137
|
throw err;
|
|
@@ -3894,6 +4414,18 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
3894
4414
|
* expires (#421).
|
|
3895
4415
|
*/
|
|
3896
4416
|
async sendApiMessageStream(sessionId, chatId, message, callbacks, opts) {
|
|
4417
|
+
if (this.orchestrationForApi(sessionId)) {
|
|
4418
|
+
if (this.pendingApiSessions.has(sessionId))
|
|
4419
|
+
throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
|
|
4420
|
+
const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)('api', sessionId), opts.requestId ?? (0, crypto_1.randomUUID)());
|
|
4421
|
+
const sink = (0, turn_stream_1.callbackSink)(callbacks);
|
|
4422
|
+
turn.attach(sink, 0);
|
|
4423
|
+
void this.sendOrchestratedApi(sessionId, chatId, message, { ...opts, requestId: turn.requestId }, text => turn.emit({ type: 'text_delta', text }), event => { if (event.type === 'tool_use')
|
|
4424
|
+
turn.emit({ ...event, type: 'tool_use' }); })
|
|
4425
|
+
.then(result => this.turnStreams.complete(turn, (0, turn_stream_1.resultEvent)(result.text, result.attachments)))
|
|
4426
|
+
.catch(error => this.turnStreams.complete(turn, (0, turn_stream_1.errorEvent)(error), error));
|
|
4427
|
+
return () => turn.detach(sink);
|
|
4428
|
+
}
|
|
3897
4429
|
if (this.pendingApiSessions.has(sessionId)) {
|
|
3898
4430
|
const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
|
|
3899
4431
|
throw err;
|
|
@@ -4365,6 +4897,8 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4365
4897
|
if (!AgentRunner.isApiBuiltinCommand(cmd)) {
|
|
4366
4898
|
throw new Error(`Unknown command: ${cmd}`);
|
|
4367
4899
|
}
|
|
4900
|
+
if (cmd === '/stop' && this.agentConfig.orchestration?.enabled && opts?.principalId)
|
|
4901
|
+
this.orchestration?.authorizeSession(sessionId, opts.principalId);
|
|
4368
4902
|
// Register session in the api-{chatId} index on first use (same as sendApiMessageStream)
|
|
4369
4903
|
await this.sessionStore.ensureApiSession(agentId, storeChatId, sessionId).catch((err) => {
|
|
4370
4904
|
this.logger.warn('Failed to register API session in index for command', { agentId, chatId, sessionId, error: err.message });
|
|
@@ -4387,7 +4921,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4387
4921
|
// Persist the full user command before executing so it appears in history.
|
|
4388
4922
|
// Skip for /clear — clearSession() below wipes the table anyway; only the response survives.
|
|
4389
4923
|
if (cmd !== '/clear') {
|
|
4390
|
-
persist('user', command);
|
|
4924
|
+
persist('user', opts?.displayCommand ?? command);
|
|
4391
4925
|
}
|
|
4392
4926
|
let result;
|
|
4393
4927
|
let responseText;
|
|
@@ -4402,17 +4936,41 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4402
4936
|
: `Current model: ${model}`;
|
|
4403
4937
|
}
|
|
4404
4938
|
else if (cmd === '/stop') {
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4939
|
+
if (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') && this.orchestration && opts?.principalId) {
|
|
4940
|
+
const selection = /^\/stop\s+([a-f0-9-]{36})\s+(\d+)$/.exec(command.trim());
|
|
4941
|
+
if (selection) {
|
|
4942
|
+
try {
|
|
4943
|
+
const selected = this.orchestration.stopControls.choose(sessionId, opts.principalId, selection[1], Number(selection[2]));
|
|
4944
|
+
result = { ...selected };
|
|
4945
|
+
responseText = selected.text;
|
|
4946
|
+
}
|
|
4947
|
+
catch {
|
|
4948
|
+
result = { error: 'STOP_SELECTION_UNAVAILABLE' };
|
|
4949
|
+
responseText = 'Selection unavailable. Use /stop to refresh the task list.';
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
else {
|
|
4953
|
+
const menu = this.orchestration.stopControls.open(sessionId, opts.principalId);
|
|
4954
|
+
responseText = (0, stop_controls_1.stopMenuText)(menu);
|
|
4955
|
+
result = { ...menu, responseText };
|
|
4956
|
+
}
|
|
4957
|
+
forcePersist = true;
|
|
4958
|
+
}
|
|
4959
|
+
else {
|
|
4960
|
+
const session = this.sessions.get(sessionId);
|
|
4961
|
+
const stopped = this.agentConfig.orchestration?.enabled && this.orchestration?.isBusy(sessionId)
|
|
4962
|
+
? this.orchestration.stopResponse(sessionId)
|
|
4963
|
+
: session ? session.interrupt() : false;
|
|
4964
|
+
result = { stopped };
|
|
4965
|
+
responseText = stopped
|
|
4966
|
+
? 'Session was interrupted before I could respond.'
|
|
4967
|
+
: 'No active session to stop.';
|
|
4968
|
+
// A turn was actually cut off — always leave a visible note in history so the
|
|
4969
|
+
// web Stop button (skipPersist:true, to suppress the "/stop" command echo) and
|
|
4970
|
+
// a typed /stop command show the same outcome instead of the button leaving a
|
|
4971
|
+
// silently-dangling user turn.
|
|
4972
|
+
forcePersist = stopped;
|
|
4973
|
+
}
|
|
4416
4974
|
}
|
|
4417
4975
|
else if (cmd === '/restart') {
|
|
4418
4976
|
this.restartProcess(sessionId).catch(() => { });
|
|
@@ -4692,6 +5250,100 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4692
5250
|
const mediaPaths = this.historyDb.clearSession(`api-${chatId}`, sessionId);
|
|
4693
5251
|
media_store_1.MediaStore.deleteMediaFiles(this.agentsBaseDir, this.agentConfig.id, mediaPaths);
|
|
4694
5252
|
}
|
|
5253
|
+
/** The route has already authorized this API principal for the Agent. Preserve
|
|
5254
|
+
* the original channel binding, but never impersonate its human sender. */
|
|
5255
|
+
async sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
|
|
5256
|
+
if (!opts.principalId)
|
|
5257
|
+
throw new Error('Authenticated principal required for conversation orchestration');
|
|
5258
|
+
const runtime = await this.getOrchestration();
|
|
5259
|
+
const rows = runtime.store.all('SELECT * FROM conversations WHERE agent_session_id=?', sessionId);
|
|
5260
|
+
if (rows.length > 1 || rows.some(row => row.source !== channel || row.chat_id !== rawChatId))
|
|
5261
|
+
throw new Error('Ambiguous or mismatched channel session');
|
|
5262
|
+
if (!rows.length) {
|
|
5263
|
+
const index = await this.sessionStore.loadIndex(this.agentConfig.id, rawChatId, channel);
|
|
5264
|
+
if (!index?.sessions.some(session => session.id === sessionId))
|
|
5265
|
+
throw new Error('Session not found');
|
|
5266
|
+
}
|
|
5267
|
+
const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
|
|
5268
|
+
const accepted = runtime.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId,
|
|
5269
|
+
source: channel, accountId: String(rows[0]?.account_id ?? this.agentConfig.id), chatId: rawChatId,
|
|
5270
|
+
threadKey: String(rows[0]?.thread_key ?? ''), principalId: opts.principalId },
|
|
5271
|
+
text: message, requestId, trustedChannelMember: true, metadata: { senderName, senderId: opts.principalId } }, { execute: opts.allowTools ?? false, writeMemory: false });
|
|
5272
|
+
const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)(channel, sessionId), requestId);
|
|
5273
|
+
const sink = (0, turn_stream_1.callbackSink)(callbacks);
|
|
5274
|
+
turn.attach(sink, 0);
|
|
5275
|
+
const displayed = new Map();
|
|
5276
|
+
const unsubscribe = runtime.subscribeText(sessionId, opts.principalId, event => {
|
|
5277
|
+
if (!runtime.responseBelongsToInput(accepted.inputId, event.responseId))
|
|
5278
|
+
return;
|
|
5279
|
+
const previous = displayed.get(event.responseId) ?? '';
|
|
5280
|
+
if (event.text.startsWith(previous)) {
|
|
5281
|
+
const delta = event.text.slice(previous.length);
|
|
5282
|
+
displayed.set(event.responseId, event.text);
|
|
5283
|
+
if (delta)
|
|
5284
|
+
turn.emit({ type: 'text_delta', text: delta });
|
|
5285
|
+
}
|
|
5286
|
+
});
|
|
5287
|
+
const input = runtime.store.get('SELECT conversation_id FROM conversation_inputs WHERE id=?', accepted.inputId);
|
|
5288
|
+
const conversation = runtime.store.assertMember(String(input.conversation_id), opts.principalId);
|
|
5289
|
+
let cursor = { streamId: String(conversation.stream_id), seq: Number(conversation.last_event_seq) };
|
|
5290
|
+
const events = runtime.events.subscribe(String(conversation.id), opts.principalId, cursor);
|
|
5291
|
+
let closed = false;
|
|
5292
|
+
let timer;
|
|
5293
|
+
const close = () => { closed = true; clearTimeout(timer); unsubscribe(); events.close(); };
|
|
5294
|
+
const forward = (page) => {
|
|
5295
|
+
if (page.snapshotRequired)
|
|
5296
|
+
throw new Error('Tool activity history expired; reload session activity');
|
|
5297
|
+
for (const event of page.events) {
|
|
5298
|
+
if (event.seq <= cursor.seq)
|
|
5299
|
+
continue;
|
|
5300
|
+
const payload = event.payload;
|
|
5301
|
+
if (event.type === 'tool.activity' && payload.type === 'tool_use')
|
|
5302
|
+
turn.emit({ ...payload, type: 'tool_use' });
|
|
5303
|
+
cursor = { streamId: page.cursor.streamId, seq: event.seq };
|
|
5304
|
+
}
|
|
5305
|
+
};
|
|
5306
|
+
const drain = () => {
|
|
5307
|
+
while (true) {
|
|
5308
|
+
const page = runtime.events.read(String(conversation.id), opts.principalId, cursor);
|
|
5309
|
+
forward(page);
|
|
5310
|
+
if (!page.snapshotRequired && !page.events.length)
|
|
5311
|
+
break;
|
|
5312
|
+
}
|
|
5313
|
+
};
|
|
5314
|
+
const fail = (error) => {
|
|
5315
|
+
if (closed)
|
|
5316
|
+
return;
|
|
5317
|
+
try {
|
|
5318
|
+
drain();
|
|
5319
|
+
}
|
|
5320
|
+
catch { /* Preserve the original stream/provider failure. */ }
|
|
5321
|
+
this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.errorEvent)(error), error);
|
|
5322
|
+
close();
|
|
5323
|
+
};
|
|
5324
|
+
void (async () => {
|
|
5325
|
+
for await (const page of events.pages) {
|
|
5326
|
+
if (closed)
|
|
5327
|
+
break;
|
|
5328
|
+
forward(page);
|
|
5329
|
+
}
|
|
5330
|
+
})().catch(fail);
|
|
5331
|
+
timer = setTimeout(() => fail(Object.assign(new Error('Agent response timeout'), { code: 'TIMEOUT_SOFT' })), opts.timeoutMs);
|
|
5332
|
+
void accepted.response.then(text => {
|
|
5333
|
+
if (closed)
|
|
5334
|
+
return;
|
|
5335
|
+
try {
|
|
5336
|
+
// Drain before terminal success, even when completion precedes the next poll.
|
|
5337
|
+
drain();
|
|
5338
|
+
this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.resultEvent)(text, []));
|
|
5339
|
+
close();
|
|
5340
|
+
}
|
|
5341
|
+
catch (error) {
|
|
5342
|
+
fail(error instanceof Error ? error : new Error('Tool activity stream failed'));
|
|
5343
|
+
}
|
|
5344
|
+
}, fail);
|
|
5345
|
+
return () => { turn.detach(sink); }; // Durable work and original channel delivery survive disconnect.
|
|
5346
|
+
}
|
|
4695
5347
|
/**
|
|
4696
5348
|
* Send a message into an existing channel session (cross-channel continuation from UI).
|
|
4697
5349
|
* The session process receives full history context from the session JSON (Layer 1).
|
|
@@ -4701,6 +5353,10 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
4701
5353
|
* parser — and writing here as well produced a second row per turn. See done().
|
|
4702
5354
|
*/
|
|
4703
5355
|
async sendMessageToSession(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
|
|
5356
|
+
const managed = (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channel)) ||
|
|
5357
|
+
(this.orchestration?.ownsSession(sessionId) && !this.orchestration.canReturnToLegacy());
|
|
5358
|
+
if (managed)
|
|
5359
|
+
return this.sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts);
|
|
4704
5360
|
// Ensure the session process uses the correct channel source
|
|
4705
5361
|
this.channelSourceMap.set(rawChatId, channel);
|
|
4706
5362
|
// Channel sessions use agent-level model (not per-session)
|