@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
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** A single live task browser per private chat. All reads are session-bound and
|
|
2
|
+
* serialized with navigation, replacement, and explicit cancellation. */
|
|
3
|
+
export interface TaskBrowserMessage {
|
|
4
|
+
chatId: string; userId: string; messageId: number; sessionId: string;
|
|
5
|
+
view: { page?: number; action?: 'detail'; task_id?: string };
|
|
6
|
+
}
|
|
7
|
+
export interface TaskBrowserResult {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
task?: { taskId: string; state: string };
|
|
10
|
+
page?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TaskBrowserMenu { text: string; reply_markup: unknown }
|
|
13
|
+
interface Entry extends TaskBrowserMessage { fingerprint?: string; retryAt?: number; finished?: boolean }
|
|
14
|
+
interface Dependencies {
|
|
15
|
+
read(chat: string, user: string, payload: Record<string, unknown>): Promise<TaskBrowserResult>;
|
|
16
|
+
render(result: TaskBrowserResult): TaskBrowserMenu;
|
|
17
|
+
send(chat: string, menu: TaskBrowserMenu): Promise<number>;
|
|
18
|
+
edit(chat: string, id: number, menu: TaskBrowserMenu): Promise<unknown>;
|
|
19
|
+
remove(chat: string, id: number): Promise<unknown>;
|
|
20
|
+
close(chat: string, id: number): Promise<unknown>;
|
|
21
|
+
allowed(chat: string, user: string): boolean;
|
|
22
|
+
persist(entries: TaskBrowserMessage[]): void;
|
|
23
|
+
now?: () => number;
|
|
24
|
+
}
|
|
25
|
+
const description = (e: any) => String(e?.description ?? e?.message ?? '');
|
|
26
|
+
const absent = (e: any) => /message to (?:edit|delete) not found|message_id_invalid/i.test(description(e));
|
|
27
|
+
const unchanged = (e: any) => /message is not modified/i.test(description(e));
|
|
28
|
+
export class LiveTaskBrowser {
|
|
29
|
+
private entries = new Map<string, Entry>();
|
|
30
|
+
private locks = new Map<string, Promise<unknown>>();
|
|
31
|
+
private nextEditAt = 0;
|
|
32
|
+
constructor(private io: Dependencies, restored: TaskBrowserMessage[] = []) {
|
|
33
|
+
for (const e of restored) {
|
|
34
|
+
if (e && typeof e === 'object' && /^\d+$/.test(e.chatId) && e.userId === e.chatId && Number.isSafeInteger(e.messageId) && e.messageId > 0 && typeof e.sessionId === 'string' && e.view &&
|
|
35
|
+
(e.view.action === 'detail' ? typeof e.view.task_id === 'string' : Number.isSafeInteger(e.view.page ?? 0))) this.entries.set(e.chatId, { ...e });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
private now() { return this.io.now?.() ?? Date.now(); }
|
|
39
|
+
private persist() {
|
|
40
|
+
this.io.persist([...this.entries.values()].map(({chatId,userId,messageId,sessionId,view}) => ({chatId,userId,messageId,sessionId,view})));
|
|
41
|
+
}
|
|
42
|
+
private serial<T>(chat: string, work: () => Promise<T>): Promise<T> {
|
|
43
|
+
const previous = this.locks.get(chat) ?? Promise.resolve();
|
|
44
|
+
const job = previous.catch(() => {}).then(work);
|
|
45
|
+
this.locks.set(chat, job);
|
|
46
|
+
void job.finally(() => { if (this.locks.get(chat) === job) this.locks.delete(chat); }).catch(() => {});
|
|
47
|
+
return job;
|
|
48
|
+
}
|
|
49
|
+
private async retire(e: Entry) {
|
|
50
|
+
try { await this.io.remove(e.chatId, e.messageId); }
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (!absent(error)) {
|
|
53
|
+
try { await this.io.close(e.chatId, e.messageId); }
|
|
54
|
+
catch (closed) { if (!absent(closed) && !unchanged(closed)) throw closed; }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
this.entries.delete(e.chatId); this.persist();
|
|
58
|
+
}
|
|
59
|
+
open(chatId: string, userId: string) {
|
|
60
|
+
return this.serial(chatId, async () => {
|
|
61
|
+
if (!this.io.allowed(chatId, userId)) throw Error('TASK_BROWSER_UNAUTHORIZED');
|
|
62
|
+
const result = await this.io.read(chatId, userId, { page: 0 });
|
|
63
|
+
const menu = this.io.render(result);
|
|
64
|
+
const previous = this.entries.get(chatId);
|
|
65
|
+
if (previous) await this.retire(previous);
|
|
66
|
+
const messageId = await this.io.send(chatId, menu);
|
|
67
|
+
this.entries.set(chatId, {chatId,userId,messageId,sessionId:result.sessionId,view:{page:result.page ?? 0},fingerprint:JSON.stringify(menu)});
|
|
68
|
+
this.persist();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
navigate(chatId: string, userId: string, messageId: number, payload: Record<string, unknown>) {
|
|
72
|
+
return this.serial(chatId, async () => {
|
|
73
|
+
const e = this.entries.get(chatId);
|
|
74
|
+
if (!e || e.messageId !== messageId || e.userId !== userId || !this.io.allowed(chatId,userId)) throw Error('TASK_BROWSER_EXPIRED');
|
|
75
|
+
if (payload.action === 'dismiss') { await this.retire(e); return; }
|
|
76
|
+
try {
|
|
77
|
+
const result = await this.io.read(chatId,userId,{...payload,session_id:e.sessionId});
|
|
78
|
+
// Never repeat a mutation during auto-refresh, even if Telegram edit fails.
|
|
79
|
+
e.view = result.task ? {action:'detail',task_id:result.task.taskId} : {page:result.page ?? 0};
|
|
80
|
+
this.persist();
|
|
81
|
+
await this.update(e,result);
|
|
82
|
+
} catch (error) { await this.failure(e,error); throw error; }
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
private async update(e: Entry, result: TaskBrowserResult) {
|
|
86
|
+
if (result.sessionId !== e.sessionId) throw Error('TASK_SESSION_CHANGED');
|
|
87
|
+
const view = result.task ? { action: 'detail' as const, task_id: result.task.taskId } : { page: result.page ?? 0 };
|
|
88
|
+
if (JSON.stringify(view) !== JSON.stringify(e.view)) { e.view = view; this.persist(); }
|
|
89
|
+
const finished = !!result.task && ['completed','failed','cancelled'].includes(result.task.state);
|
|
90
|
+
const menu = this.io.render(result), fingerprint = JSON.stringify(menu);
|
|
91
|
+
if (fingerprint === e.fingerprint) { e.finished = finished; return; }
|
|
92
|
+
try { await this.io.edit(e.chatId,e.messageId,menu); }
|
|
93
|
+
catch (error) { if (!unchanged(error)) throw error; }
|
|
94
|
+
e.fingerprint = fingerprint; e.retryAt = undefined; e.finished = finished;
|
|
95
|
+
}
|
|
96
|
+
private async failure(e: Entry, error: any) {
|
|
97
|
+
if (description(error).includes('TASK_SESSION_CHANGED')) { await this.retire(e); return; }
|
|
98
|
+
if (absent(error) || error?.error_code === 403) { this.entries.delete(e.chatId); this.persist(); return; }
|
|
99
|
+
const retry = Number(error?.parameters?.retry_after);
|
|
100
|
+
e.retryAt = this.now() + (Number.isFinite(retry) && retry > 0 ? retry * 1000 : 10000);
|
|
101
|
+
if (retry > 0) this.nextEditAt = Math.max(this.nextEditAt,e.retryAt);
|
|
102
|
+
}
|
|
103
|
+
async tick() {
|
|
104
|
+
// One tick cannot overlap a previous read/edit or user action for this chat.
|
|
105
|
+
for (const e of [...this.entries.values()]) {
|
|
106
|
+
if (e.finished || this.locks.has(e.chatId) || this.now() < Math.max(e.retryAt ?? 0,this.nextEditAt)) continue;
|
|
107
|
+
await this.serial(e.chatId, async () => {
|
|
108
|
+
if (this.entries.get(e.chatId) !== e) return;
|
|
109
|
+
if (!this.io.allowed(e.chatId,e.userId)) {
|
|
110
|
+
this.entries.delete(e.chatId); this.persist(); return;
|
|
111
|
+
}
|
|
112
|
+
try { await this.update(e,await this.io.read(e.chatId,e.userId,{...e.view,session_id:e.sessionId})); }
|
|
113
|
+
catch (error) { await this.failure(e,error).catch(() => {}); }
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { formatTiming } from '../../../dist/orchestration/tasks/timing';
|
|
2
|
+
import { formatTaskElapsed } from './task-elapsed';
|
|
3
|
+
import type { ProcessSample } from '../../../src/orchestration/process-activity';
|
|
4
|
+
import type { ToolOutcome } from '../../../src/orchestration/execution-observation';
|
|
5
|
+
|
|
6
|
+
export interface TaskDetail {
|
|
7
|
+
title: string; state: string; startedAt?: number; finishedAt?: number;
|
|
8
|
+
timing?: Partial<Record<'working' | 'tool' | 'input' | 'queued' | 'other' | 'finished', number>>;
|
|
9
|
+
timingMeasuredFrom?: number;
|
|
10
|
+
progress?: string; progressText?: string; question?: string;
|
|
11
|
+
activityDetails?: { counters: string; status: string; age: number; activeTools: string[];
|
|
12
|
+
observationStatus?: string; process?: ProcessSample; lastTool?: ToolOutcome; quiet?: boolean };
|
|
13
|
+
}
|
|
14
|
+
function bytes(value: number): string {
|
|
15
|
+
const unit = value >= 1000000 ? 'MB' : value >= 1000 ? 'KB' : 'B';
|
|
16
|
+
return `${Number((value / (unit === 'MB' ? 1000000 : unit === 'KB' ? 1000 : 1)).toFixed(2))} ${unit}`;
|
|
17
|
+
}
|
|
18
|
+
export function formatTaskDetail(task: TaskDetail, label: string): string {
|
|
19
|
+
const activity = task.activityDetails;
|
|
20
|
+
const progress = task.progressText ?? task.progress;
|
|
21
|
+
const progressLines = [progress, activity?.activeTools.length ? `Active tools: ${activity.activeTools.join(', ')}` : '', activity ? `Last activity: ${activity.age}s ago.` : '', task.question ? `Waiting for your input:\n${task.question}` : ''].filter(Boolean);
|
|
22
|
+
const process = activity?.process;
|
|
23
|
+
const counters = process ? process.available ? [
|
|
24
|
+
`Processes: ${process.processCount}`,
|
|
25
|
+
process.cpuTicksDelta !== undefined ? `CPU: +${process.cpuTicksDelta} ticks · Children: ${process.childCpuTicksDelta === undefined ? 'unavailable' : `+${process.childCpuTicksDelta} ticks`}` : 'CPU: unavailable',
|
|
26
|
+
process.ioAvailable ? `I/O: Read ${process.readBytesDelta === undefined ? 'unavailable' : `+${bytes(process.readBytesDelta)}`} · Write ${process.writeBytesDelta === undefined ? 'unavailable' : `+${bytes(process.writeBytesDelta)}`}` : 'I/O: partially unavailable',
|
|
27
|
+
].join('\n') : '' : activity?.counters;
|
|
28
|
+
const lastTool = activity?.lastTool;
|
|
29
|
+
const diagnostics = [counters, lastTool ? `Last tool: ${lastTool.name} · ${lastTool.status === 'error' ? 'Reported error' : 'Result received'}${lastTool.exitCode === undefined ? '' : ` · Exit code: ${lastTool.exitCode}`}\nReported at: ${new Date(lastTool.observedAt).toISOString().replace('T',' ').replace(/\.\d{3}Z$/, ' UTC')}` : '', activity?.observationStatus ?? activity?.status, activity?.quiet ? 'No recent tool/model progress; task retained for inspection or cancellation.' : ''].filter(Boolean);
|
|
30
|
+
return [
|
|
31
|
+
`${task.title}\n${label} · Elapsed: ${formatTaskElapsed(task.startedAt,task.finishedAt)}`,
|
|
32
|
+
formatTiming(task.timing) ? 'Time breakdown\n' + formatTiming(task.timing) + (task.timingMeasuredFrom && task.startedAt && task.timingMeasuredFrom > task.startedAt + 1000 ? '\nMeasured since ' + new Date(task.timingMeasuredFrom).toISOString() : '') : '',
|
|
33
|
+
progressLines.length ? `Progress\n${progressLines.join('\n')}` : '',
|
|
34
|
+
diagnostics.length ? `Diagnostics\n${diagnostics.join('\n')}` : '',
|
|
35
|
+
].filter(Boolean).join('\n\n');
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { formatTaskElapsed } from '../../../dist/shared/task-elapsed';
|
|
@@ -24,127 +24,15 @@ import {
|
|
|
24
24
|
type TurnIncidentEvidence,
|
|
25
25
|
} from '../../../dist/agent/turn-trace.js'
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Telegram rejects a message whose HTML entities are unbalanced, so a chunk cut
|
|
31
|
-
* that falls inside a <pre><code>…</code></pre> block must close the open tags
|
|
32
|
-
* at the end of the chunk and reopen them at the start of the next one.
|
|
33
|
-
* Reserve room for that worst-case suffix (</a></code></pre></b></i>) plus the
|
|
34
|
-
* mirrored reopening prefix so balancing never pushes a chunk past the limit.
|
|
35
|
-
*/
|
|
36
|
-
const HTML_BALANCE_HEADROOM = 64
|
|
37
|
-
|
|
38
|
-
/** Tags toTelegramHtml() emits — the only ones balancing needs to understand. */
|
|
39
|
-
const BALANCED_TAGS = ['b', 'i', 'code', 'pre', 'a'] as const
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Scan an HTML fragment (as produced by toTelegramHtml — no attributes except
|
|
43
|
-
* <a href>, no self-closing forms) and return the stack of tags still open at
|
|
44
|
-
* the end, as full opening-tag strings in opening order.
|
|
45
|
-
*/
|
|
46
|
-
export function openTagStack(html: string): string[] {
|
|
47
|
-
const stack: string[] = []
|
|
48
|
-
// Attribute part tolerates '>' inside quoted values (<a href="a>b">).
|
|
49
|
-
const re = /<(\/?)([a-z]+)((?:\s(?:"[^"]*"|[^>])*)?)>/g
|
|
50
|
-
let m: RegExpExecArray | null
|
|
51
|
-
while ((m = re.exec(html)) !== null) {
|
|
52
|
-
const closing = m[1] === '/'
|
|
53
|
-
const name = m[2]
|
|
54
|
-
if (!(BALANCED_TAGS as readonly string[]).includes(name)) continue
|
|
55
|
-
if (closing) {
|
|
56
|
-
// toTelegramHtml emits well-nested pairs, so the match is always the top.
|
|
57
|
-
const top = stack.length - 1
|
|
58
|
-
if (top >= 0 && /^<([a-z]+)/.exec(stack[top])?.[1] === name) stack.pop()
|
|
59
|
-
} else {
|
|
60
|
-
stack.push(`<${name}${m[3] ?? ''}>`)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return stack
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Strip Telegram-HTML tags and unescape entities → plain-text equivalent. */
|
|
67
|
-
export function htmlToPlain(html: string): string {
|
|
68
|
-
return html
|
|
69
|
-
.replace(/<br\s*\/?>/gi, '\n')
|
|
70
|
-
.replace(/<[^>]+>/g, '')
|
|
71
|
-
.replace(/</g, '<')
|
|
72
|
-
.replace(/>/g, '>')
|
|
73
|
-
.replace(/"/g, '"')
|
|
74
|
-
.replace(/&/g, '&')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Split text into chunks that fit within Telegram's message size limit.
|
|
79
|
-
* Prefers paragraph → line → space boundaries over hard cuts.
|
|
80
|
-
* When htmlSafe=true, avoids cutting inside an HTML tag (e.g. <code>, <b>) AND
|
|
81
|
-
* keeps every chunk entity-balanced: tags left open at a cut are closed at the
|
|
82
|
-
* chunk's end and reopened at the next chunk's start, so no chunk is ever
|
|
83
|
-
* rejected by Telegram's HTML parser for an unclosed <pre>/<code>/<b>.
|
|
84
|
-
*/
|
|
85
|
-
export function chunkText(text: string, limit = TELEGRAM_MAX_CHARS, htmlSafe = false): string[] {
|
|
86
|
-
if (text.length <= limit) return [text]
|
|
87
|
-
const out: string[] = []
|
|
88
|
-
let rest = text
|
|
89
|
-
const effLimit = htmlSafe ? limit - HTML_BALANCE_HEADROOM : limit
|
|
90
|
-
// If cut lands inside an open tag (<...>), move cut to before the '<'
|
|
91
|
-
const avoidMidTag = (cut: number): number => {
|
|
92
|
-
const tagStart = rest.lastIndexOf('<', cut)
|
|
93
|
-
const tagEnd = rest.lastIndexOf('>', cut)
|
|
94
|
-
return tagStart > tagEnd ? tagStart : cut
|
|
95
|
-
}
|
|
96
|
-
const closersFor = (open: string[]): string =>
|
|
97
|
-
open.map((t) => `</${/^<([a-z]+)/.exec(t)![1]}>`).reverse().join('')
|
|
98
|
-
// Cut at `cut`, balancing tags across the boundary when htmlSafe.
|
|
99
|
-
const splitAt = (cut: number): { head: string; tail: string } => {
|
|
100
|
-
let head = rest.slice(0, cut)
|
|
101
|
-
let tail = rest.slice(cut).replace(/^\n+/, '')
|
|
102
|
-
if (htmlSafe) {
|
|
103
|
-
const open = openTagStack(head)
|
|
104
|
-
if (open.length) {
|
|
105
|
-
head += closersFor(open)
|
|
106
|
-
tail = open.join('') + tail
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return { head, tail }
|
|
110
|
-
}
|
|
111
|
-
while (rest.length > effLimit) {
|
|
112
|
-
const para = rest.lastIndexOf('\n\n', effLimit)
|
|
113
|
-
const line = rest.lastIndexOf('\n', effLimit)
|
|
114
|
-
const space = rest.lastIndexOf(' ', effLimit)
|
|
115
|
-
let cut = para > effLimit / 2 ? para : line > effLimit / 2 ? line : space > 0 ? space : effLimit
|
|
116
|
-
if (htmlSafe) cut = avoidMidTag(cut)
|
|
117
|
-
let { head, tail } = splitAt(cut)
|
|
118
|
-
// Forward-progress guard: when the chosen boundary sits right after an
|
|
119
|
-
// opening tag (e.g. "<b> " + one unbroken >limit token), the reopened tag
|
|
120
|
-
// prefix can re-add as much as the cut removed and `rest` never shrinks —
|
|
121
|
-
// an infinite loop that would hang the whole receiver. Retry with a hard
|
|
122
|
-
// cut at effLimit; if even that cannot shrink (degenerate tag-heavy input,
|
|
123
|
-
// e.g. a single huge <a href>), emit the remainder as one oversized chunk
|
|
124
|
-
// and stop — Telegram rejects it and the plain-text retry rescues the
|
|
125
|
-
// content, which beats hanging the process.
|
|
126
|
-
if (tail.length >= rest.length) {
|
|
127
|
-
cut = htmlSafe ? avoidMidTag(effLimit) : effLimit
|
|
128
|
-
;({ head, tail } = splitAt(cut))
|
|
129
|
-
if (tail.length >= rest.length) {
|
|
130
|
-
out.push(rest)
|
|
131
|
-
rest = ''
|
|
132
|
-
break
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
out.push(head)
|
|
136
|
-
rest = tail
|
|
137
|
-
}
|
|
138
|
-
if (rest) out.push(rest)
|
|
139
|
-
return out
|
|
140
|
-
}
|
|
27
|
+
import { TELEGRAM_MAX_CHARS, htmlToPlain, chunkText } from '../../../dist/telegram/chunks.js'
|
|
28
|
+
export { TELEGRAM_MAX_CHARS, openTagStack, htmlToPlain, chunkText } from '../../../dist/telegram/chunks.js'
|
|
141
29
|
|
|
142
30
|
export const STATUS_MESSAGES = [
|
|
143
31
|
'⏳ Thinking...',
|
|
144
32
|
'🔍 Analyzing your request...',
|
|
145
33
|
'⚙️ Working on it...',
|
|
146
34
|
'📝 Preparing a response...',
|
|
147
|
-
'🧠
|
|
35
|
+
'🧠 Thinking...',
|
|
148
36
|
]
|
|
149
37
|
|
|
150
38
|
export const STALLED_TIMEOUT_MS = 300_000 // 5 minutes without heartbeat → warn + stop
|
|
@@ -429,6 +317,7 @@ export function createWorkingStateManager(
|
|
|
429
317
|
botApi: BotApi,
|
|
430
318
|
fsApi: FsApi,
|
|
431
319
|
onIncident?: TurnIncidentSink,
|
|
320
|
+
mode: 'legacy' | 'orchestration' = 'legacy',
|
|
432
321
|
) {
|
|
433
322
|
const states = new Map<string, WorkingState>()
|
|
434
323
|
|
|
@@ -645,7 +534,8 @@ export function createWorkingStateManager(
|
|
|
645
534
|
}
|
|
646
535
|
|
|
647
536
|
function start(chatId: string): void {
|
|
648
|
-
|
|
537
|
+
// Managed turns use durable orchestration activity, not legacy file heartbeats.
|
|
538
|
+
if (mode === 'orchestration' || states.has(chatId)) return
|
|
649
539
|
|
|
650
540
|
fsApi.mkdirSync(typingDir, { recursive: true })
|
|
651
541
|
fsApi.writeFileSync(typingFilePath(chatId), String(Date.now()))
|
|
@@ -553,7 +553,7 @@ export class VideoModule implements ToolModule {
|
|
|
553
553
|
byok: job.byok ?? false,
|
|
554
554
|
cost: job.cost ?? 0,
|
|
555
555
|
files: [filePath],
|
|
556
|
-
note: 'Video saved. Deliver it to the user with your channel delivery tool — api_reply/reply (files: [...]). Do NOT open/Read the file to inspect it first; attach it and answer briefly.'
|
|
556
|
+
note: (process.env.GATEWAY_ORCHESTRATION_ROLE === 'worker' ? 'Video saved. Call task_stage_file with this file path, then report completion. Orchestration handles delivery.' : 'Video saved. Deliver it to the user with your channel delivery tool — api_reply/reply (files: [...]). Do NOT open/Read the file to inspect it first; attach it and answer briefly.')
|
|
557
557
|
+ (model ? ` Mention which model made it (${model}).` : ''),
|
|
558
558
|
}),
|
|
559
559
|
}],
|
package/mcp/types.ts
CHANGED
|
@@ -14,8 +14,13 @@ export type InboundMessage = {
|
|
|
14
14
|
text: string;
|
|
15
15
|
messageId: string;
|
|
16
16
|
replyToMessageId?: string;
|
|
17
|
+
repliedText?: string;
|
|
18
|
+
repliedSender?: string;
|
|
19
|
+
attachments?: Array<{url:string;name?:string;kind?:string;quoted?:boolean}>;
|
|
17
20
|
threadId?: string;
|
|
18
21
|
attachmentFileId?: string;
|
|
22
|
+
attachmentKind?: 'voice' | 'file';
|
|
23
|
+
controlMessageId?: string;
|
|
19
24
|
ts: number;
|
|
20
25
|
};
|
|
21
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xmaxma/claude-gateway",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Multi-agent gateway for Claude",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"!mcp/node_modules",
|
|
22
22
|
"!mcp/node_modules/**",
|
|
23
23
|
"README.md",
|
|
24
|
+
"scripts/orchestration/",
|
|
24
25
|
"config.template.json"
|
|
25
26
|
],
|
|
26
27
|
"publishConfig": {
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Local durability latency, not a provider/model/network performance claim.
|
|
3
|
+
const { mkdtempSync, rmSync } = require('node:fs');
|
|
4
|
+
const { tmpdir } = require('node:os');
|
|
5
|
+
const { join } = require('node:path');
|
|
6
|
+
const { performance } = require('node:perf_hooks');
|
|
7
|
+
const { OrchestrationStore } = require('../../dist/orchestration/store');
|
|
8
|
+
const { DecisionService } = require('../../dist/orchestration/decisions');
|
|
9
|
+
const { TaskService } = require('../../dist/orchestration/tasks/service');
|
|
10
|
+
const root = mkdtempSync(join(tmpdir(), 'orchestration-bench-'));
|
|
11
|
+
const store = new OrchestrationStore(join(root, 'orchestration.db'), 'fixture');
|
|
12
|
+
const decisions = new DecisionService(store), tasks = new TaskService(store, { tasks: { maxQueuedPerAgent: 1000, maxQueuedPerConversation: 1000 } });
|
|
13
|
+
const inputTimes = [], commandTimes = [];
|
|
14
|
+
const scope = { agentId: 'fixture', agentSessionId: 'agentSession', source: 'api', accountId: 'key', chatId: 'chat', threadKey: '', principalId: 'owner' };
|
|
15
|
+
try {
|
|
16
|
+
for (let i = 0; i < 200; i++) {
|
|
17
|
+
let start = performance.now();
|
|
18
|
+
const receipt = store.acceptInput({ scope, text: `Task ${i}`, ingressKey: String(i) });
|
|
19
|
+
inputTimes.push(performance.now() - start);
|
|
20
|
+
const decision = decisions.begin(receipt.conversationId, 'owner', [receipt.inputId]);
|
|
21
|
+
start = performance.now();
|
|
22
|
+
tasks.spawn({ ...receipt, ...decision, principalId: 'owner', actionId: `action-${i}`, execute: true, writeMemory: false }, { title: `Task ${i}`, targetProfile: 'default-worker', instructions: 'Do the fixture task' });
|
|
23
|
+
commandTimes.push(performance.now() - start);
|
|
24
|
+
decisions.finish(decision, 'Queued.');
|
|
25
|
+
}
|
|
26
|
+
const summarize = values => { values.sort((a,b) => a-b); return { n: values.length, p50Ms: values[Math.floor(values.length * .50)], p95Ms: values[Math.floor(values.length * .95)], maxMs: values.at(-1) }; };
|
|
27
|
+
const input = summarize(inputTimes), command = summarize(commandTimes);
|
|
28
|
+
console.log(JSON.stringify({ node: process.version, platform: process.platform, sqlite: 'WAL, synchronous=FULL', input, command, pass: input.p95Ms <= 250 && command.p95Ms <= 250 }, null, 2));
|
|
29
|
+
process.exitCode = input.p95Ms <= 250 && command.p95Ms <= 250 ? 0 : 1;
|
|
30
|
+
} finally { store.close(); rmSync(root, { recursive: true, force: true }); }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Read-only operator inspection. Never print tickets, prompts or credentials.
|
|
3
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
4
|
+
if (process.argv.length !== 3) { console.error('Usage: node scripts/orchestration/inspect.cjs /path/to/agent/orchestration.db'); process.exit(2); }
|
|
5
|
+
const db = new DatabaseSync(process.argv[2], { readOnly: true });
|
|
6
|
+
try {
|
|
7
|
+
const all = sql => db.prepare(sql).all();
|
|
8
|
+
console.log(JSON.stringify({
|
|
9
|
+
conversations: all('SELECT status,COUNT(*) count FROM conversations GROUP BY status'),
|
|
10
|
+
inputs: all('SELECT status,COUNT(*) count FROM conversation_inputs GROUP BY status'),
|
|
11
|
+
tasks: all('SELECT state,COUNT(*) count FROM tasks GROUP BY state'),
|
|
12
|
+
outbox: all('SELECT kind,state,COUNT(*) count FROM outbox GROUP BY kind,state'),
|
|
13
|
+
resources: all('SELECT mode,lifecycle_state,COUNT(*) count FROM task_resources GROUP BY mode,lifecycle_state'),
|
|
14
|
+
reconcile: all("SELECT t.id,t.state,t.active_attempt_id,r.worktree_path FROM tasks t LEFT JOIN task_resources r ON r.task_id=t.id WHERE t.state='needs_reconciliation'"),
|
|
15
|
+
}, null, 2));
|
|
16
|
+
} finally { db.close(); }
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Opt-in live P0 probe. Only disposable workspace data enters the prompt.
|
|
3
|
+
const { mkdtempSync, writeFileSync, mkdirSync } = require('node:fs');
|
|
4
|
+
const { tmpdir } = require('node:os');
|
|
5
|
+
const { join, resolve } = require('node:path');
|
|
6
|
+
const { spawn, execFileSync } = require('node:child_process');
|
|
7
|
+
const { randomUUID } = require('node:crypto');
|
|
8
|
+
const { loadWorkspace } = require('../../dist/agent/workspace-loader');
|
|
9
|
+
const { SessionStore } = require('../../dist/session/store');
|
|
10
|
+
const { runtimeProfileArgs, AGENT_OVERLAY } = require('../../dist/session/runtime-profile');
|
|
11
|
+
(async () => {
|
|
12
|
+
const root = mkdtempSync(join(tmpdir(), 'gateway-orchestration-probe-'));
|
|
13
|
+
const workspace = join(root, 'probe', 'workspace');
|
|
14
|
+
mkdirSync(workspace, { recursive: true });
|
|
15
|
+
for (const [file, content] of Object.entries({
|
|
16
|
+
'AGENTS.md': 'You are a disposable conversation profile test fixture.',
|
|
17
|
+
'IDENTITY.md': 'Your fixture identity is CitrineHeron.',
|
|
18
|
+
'SOUL.md': 'Be concise and truthful.',
|
|
19
|
+
'USER.md': 'The fixture user prefers Thai.',
|
|
20
|
+
'MEMORY.md': 'The fixture project codeword is cobalt-orchid.',
|
|
21
|
+
})) writeFileSync(join(workspace, file), content);
|
|
22
|
+
const loaded = await loadWorkspace(workspace);
|
|
23
|
+
mkdirSync(join(workspace, 'memory'));
|
|
24
|
+
writeFileSync(join(workspace, 'memory', 'fixture.md'), 'Archived marker: silver-pine.');
|
|
25
|
+
writeFileSync(join(workspace, 'CLAUDE.md'), loaded.systemPrompt);
|
|
26
|
+
const sessionId = randomUUID();
|
|
27
|
+
const store = new SessionStore(root);
|
|
28
|
+
await store.appendMessage('probe', sessionId, { role: 'user', content: 'Our previous fixture choice was amber-bridge.', ts: Date.now() });
|
|
29
|
+
const history = await store.loadSession('probe', sessionId);
|
|
30
|
+
const mcpConfigPath = join(root, 'mcp.json');
|
|
31
|
+
writeFileSync(mcpConfigPath, JSON.stringify({ mcpServers: { gateway: { command: 'bun',
|
|
32
|
+
args: [resolve(__dirname, '../../mcp/server.ts')],
|
|
33
|
+
env: { GATEWAY_ORCHESTRATION_ROLE: 'agent', GATEWAY_WORKSPACE_DIR: workspace, GATEWAY_ORCHESTRATION_TICKET_FILE: join(root, 'unused-ticket.json') },
|
|
34
|
+
} } }));
|
|
35
|
+
const args = ['--print', '--verbose', '--input-format', 'stream-json', '--output-format', 'stream-json',
|
|
36
|
+
'--include-partial-messages', '--session-id', sessionId, '--dangerously-skip-permissions',
|
|
37
|
+
...runtimeProfileArgs({ role: 'agent', mcpConfigPath, overlay: AGENT_OVERLAY, context: loaded.systemPrompt }, [])];
|
|
38
|
+
const child = spawn('claude', args, { cwd: workspace, env: { ...process.env }, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
39
|
+
let buffer = '', stderr = '', inventory, result;
|
|
40
|
+
const timer = setTimeout(() => child.kill('SIGTERM'), 45000);
|
|
41
|
+
child.stdout.on('data', chunk => {
|
|
42
|
+
buffer += chunk;
|
|
43
|
+
let end;
|
|
44
|
+
while ((end = buffer.indexOf('\n')) >= 0) {
|
|
45
|
+
const line = buffer.slice(0, end); buffer = buffer.slice(end + 1);
|
|
46
|
+
try {
|
|
47
|
+
const event = JSON.parse(line);
|
|
48
|
+
if (event.type === 'system' && event.subtype === 'init') inventory = { tools: event.tools, mcp_servers: event.mcp_servers, session_id: event.session_id };
|
|
49
|
+
if (event.type === 'result') { result = { subtype: event.subtype, result: event.result, is_error: event.is_error }; child.stdin.end(); }
|
|
50
|
+
} catch {}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
child.stderr.on('data', chunk => { stderr += chunk; });
|
|
54
|
+
child.stdin.write(JSON.stringify({ type: 'user', message: { role: 'user', content: `Fixture history: ${JSON.stringify(history)}\nWhat are your fixture identity, project codeword and previous choice? Use memory_get to retrieve memory/fixture.md and report its archived marker. State whether Read or Bash tools are available; do not create any tasks.` } }) + '\n');
|
|
55
|
+
child.stdin.end();
|
|
56
|
+
child.on('close', (code, signal) => {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
const report = { cli: execFileSync('claude', ['--version'], { encoding: 'utf8' }).trim(), backend: 'headless', workspace, sessionId,
|
|
59
|
+
inventory, result, exitCode: code, signal, stderrPresent: Boolean(stderr),
|
|
60
|
+
contextPassed: Boolean(result?.result && ['CitrineHeron', 'cobalt-orchid', 'amber-bridge', 'silver-pine'].every(s => result.result.includes(s))),
|
|
61
|
+
inventoryPassed: Array.isArray(inventory?.tools) && inventory.tools.length === 7 && inventory.tools.every(name => /^mcp__gateway__(task_(spawn|status|cancel|update|answer)|memory_(search|get))$/.test(name)) };
|
|
62
|
+
const target = process.argv[2];
|
|
63
|
+
if (target) writeFileSync(resolve(target), JSON.stringify(report, null, 2) + '\n');
|
|
64
|
+
console.log(JSON.stringify(report, null, 2));
|
|
65
|
+
process.exitCode = report.contextPassed && report.inventoryPassed && code === 0 ? 0 : 1;
|
|
66
|
+
});
|
|
67
|
+
})().catch(error => { console.error(error.message); process.exitCode = 1; });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Explicit offline operator resolution; no automatic liveness/side-effect guess.
|
|
3
|
+
const { dirname, join } = require('node:path');
|
|
4
|
+
const { readFileSync } = require('node:fs');
|
|
5
|
+
const { OrchestrationStore } = require('../../dist/orchestration/store');
|
|
6
|
+
const { TaskService } = require('../../dist/orchestration/tasks/service');
|
|
7
|
+
const { acquireInstanceLock } = require('../../dist/orchestration/instance-lock');
|
|
8
|
+
const [database, agentId, taskId, state, evidenceFile] = process.argv.slice(2);
|
|
9
|
+
if (!evidenceFile || !['queued', 'failed', 'cancelled'].includes(state)) {
|
|
10
|
+
console.error('Usage: node scripts/orchestration/reconcile.cjs ORCHESTRATION_DB AGENT_ID TASK_ID queued|failed|cancelled EVIDENCE_FILE');
|
|
11
|
+
console.error('Stop the gateway and verify process liveness and side effects first. queued explicitly authorizes another attempt.');
|
|
12
|
+
process.exit(2);
|
|
13
|
+
}
|
|
14
|
+
let release, store;
|
|
15
|
+
try {
|
|
16
|
+
release = acquireInstanceLock(join(dirname(database), 'orchestration-instance.lock'));
|
|
17
|
+
store = new OrchestrationStore(database, agentId);
|
|
18
|
+
const result = new TaskService(store).reconcile(taskId, state, readFileSync(evidenceFile, 'utf8'));
|
|
19
|
+
console.log(JSON.stringify({ taskId: result.taskId, state: result.state, stateVersion: result.stateVersion }));
|
|
20
|
+
} catch (error) { console.error(error.code || error.message); process.exitCode = 1; }
|
|
21
|
+
finally { store?.close(); release?.(); }
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Paid, bounded provider smoke: two short TTS requests, one file STT, one voice turn.
|
|
3
|
+
// npm run build && node scripts/orchestration/smoke-elevenlabs.cjs /path/to/elevenlabs.env [voice-id]
|
|
4
|
+
// Uses a synthetic summary fixture; does not run agent inference or test hardware.
|
|
5
|
+
const assert = require('node:assert/strict');
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const { join } = require('node:path');
|
|
8
|
+
const { tmpdir } = require('node:os');
|
|
9
|
+
const { ElevenLabsTts } = require('../../dist/voice/providers/elevenlabs-tts');
|
|
10
|
+
const { ElevenLabsStt } = require('../../dist/voice/providers/elevenlabs-stt');
|
|
11
|
+
const { transcribeVoiceNote } = require('../../dist/voice/notes');
|
|
12
|
+
const { VoiceSession } = require('../../dist/voice/session');
|
|
13
|
+
const { decodeVoiceFrame } = require('../../dist/voice/protocol');
|
|
14
|
+
const { splitSpeechResponse } = require('../../dist/orchestration/speech');
|
|
15
|
+
const { PCM16 } = require('../../dist/voice/types');
|
|
16
|
+
const voiceSurfaces = {
|
|
17
|
+
display: 'ตัวอย่างผลทดสอบ: แก้ฟังก์ชันจากลบเป็นบวกสำเร็จ และทดสอบผ่านแล้ว รายละเอียดนี้เป็นข้อมูลจำลองสำหรับตรวจการสรุปเสียงเท่านั้น '.repeat(4),
|
|
18
|
+
spoken: 'ทดสอบสำเร็จแล้ว แก้ฟังก์ชันจากลบเป็นบวก และรันเทสต์ผ่านครับ',
|
|
19
|
+
};
|
|
20
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
21
|
+
function wav(pcm) {
|
|
22
|
+
const h = Buffer.alloc(44); h.write('RIFF'); h.writeUInt32LE(36 + pcm.length, 4); h.write('WAVEfmt ', 8);
|
|
23
|
+
h.writeUInt32LE(16, 16); h.writeUInt16LE(1, 20); h.writeUInt16LE(1, 22); h.writeUInt32LE(16000, 24);
|
|
24
|
+
h.writeUInt32LE(32000, 28); h.writeUInt16LE(2, 32); h.writeUInt16LE(16, 34); h.write('data', 36); h.writeUInt32LE(pcm.length, 40);
|
|
25
|
+
return Buffer.concat([h, pcm]);
|
|
26
|
+
}
|
|
27
|
+
(async () => {
|
|
28
|
+
if (process.argv[2]) process.loadEnvFile(process.argv[2]);
|
|
29
|
+
const key = process.env.ELEVENLABS_API_KEY; assert(key, 'ELEVENLABS_API_KEY required');
|
|
30
|
+
const voiceId = process.argv[3] || 'JBFqnCBsd6RMkjVDRZzb';
|
|
31
|
+
const root = fs.mkdtempSync(join(tmpdir(), 'orchestration-elevenlabs-'));
|
|
32
|
+
const report = { date: new Date().toISOString(), voiceId, root, pass: false,
|
|
33
|
+
limits: ['Synthetic microphone; no hardware, echo or platform-account delivery tested.', 'Synthetic summary fixture, not a fresh agent inference.', 'No quota exhaustion or outage injected.'] };
|
|
34
|
+
let session;
|
|
35
|
+
const watchdog = setTimeout(() => { console.error('SMOKE_DEADLINE'); process.exit(1); }, 120000);
|
|
36
|
+
try {
|
|
37
|
+
const surfaces = splitSpeechResponse(JSON.stringify({ display_text: voiceSurfaces.display, spoken_text: voiceSurfaces.spoken }));
|
|
38
|
+
assert(surfaces.spoken && surfaces.spoken.length <= 600 && surfaces.display.length > surfaces.spoken.length);
|
|
39
|
+
const tts = new ElevenLabsTts(key), stt = new ElevenLabsStt(key);
|
|
40
|
+
const started = Date.now(), chunks = [];
|
|
41
|
+
report.tts = { model: 'eleven_v3_conversational', characters: surfaces.spoken.length };
|
|
42
|
+
for await (const chunk of tts.synthesize({ text: (async function* () { yield surfaces.spoken; })(), voiceId, language: 'th', outputFormat: PCM16, signal: AbortSignal.timeout(40000) })) {
|
|
43
|
+
if (!chunks.length) report.tts.firstAudioMs = Date.now() - started;
|
|
44
|
+
chunks.push(chunk.bytes); assert(chunks.reduce((n, c) => n + c.length, 0) <= 960000, 'Audio exceeds 30-second bound');
|
|
45
|
+
}
|
|
46
|
+
const pcm = Buffer.concat(chunks); assert(pcm.length > 3200 && pcm.some(b => b));
|
|
47
|
+
Object.assign(report.tts, { durationMs: Date.now() - started, audioSeconds: pcm.length / 32000, chunks: chunks.length });
|
|
48
|
+
const path = join(root, 'summary.wav'); fs.writeFileSync(path, wav(pcm), { mode: 0o600 });
|
|
49
|
+
console.log(JSON.stringify({ stage: 'tts', ...report.tts }));
|
|
50
|
+
const noteStart = Date.now();
|
|
51
|
+
const transcript = await transcribeVoiceNote(path, { provider: 'elevenlabs', model: 'scribe_v2', language: 'th' });
|
|
52
|
+
assert(/สำเร็จ/.test(transcript) && /บวก/.test(transcript), 'Thai file transcript lost key summary facts');
|
|
53
|
+
report.note = { model: 'scribe_v2', transcript, durationMs: Date.now() - noteStart };
|
|
54
|
+
console.log(JSON.stringify({ stage: 'file-stt', ...report.note }));
|
|
55
|
+
const controls = [], spokenInputs = [], output = []; let listening, played = 0, submittedText;
|
|
56
|
+
const tracedTts = { id: tts.id, capabilities: tts.capabilities, synthesize: options => tts.synthesize({ ...options, text: (async function* () {
|
|
57
|
+
for await (const text of options.text) { spokenInputs.push(text); assert.equal(text, surfaces.spoken); yield text; }
|
|
58
|
+
})() }) };
|
|
59
|
+
session = new VoiceSession(stt, tracedTts, voiceId, {
|
|
60
|
+
control: c => { controls.push(c); if (c.type === 'voice.state' && c.state === 'listening') listening = c; },
|
|
61
|
+
audio: bytes => { const frame = decodeVoiceFrame(bytes); output.push(frame.audio); played += frame.audio.length / 2; session.progress(frame.epoch, played); },
|
|
62
|
+
bufferedBytes: () => 0,
|
|
63
|
+
}, async text => {
|
|
64
|
+
submittedText = text;
|
|
65
|
+
assert(/สำเร็จ/.test(text) && /บวก/.test(text), 'Realtime transcript lost key summary facts');
|
|
66
|
+
return { inputId: 'live-summary-proof', response: Promise.resolve(surfaces.display), responseId: () => 'summary-proof',
|
|
67
|
+
stream: (async function* () { yield { responseId: 'summary-proof', text: surfaces.spoken }; })() };
|
|
68
|
+
}, () => {}, { language: 'th', silenceCommitMs: 650, finalizationTimeoutMs: 15000, maxUtteranceMs: 60000, maxBufferedAudioMs: 1500 });
|
|
69
|
+
await session.start(); const turn = listening; let sequence = 0;
|
|
70
|
+
const mic = Buffer.concat([pcm, Buffer.alloc(16000)]);
|
|
71
|
+
for (let offset = 0; offset < mic.length; offset += 3200) {
|
|
72
|
+
await session.audio({ generation: turn.generation, epoch: turn.epoch, sequence: ++sequence, segmentId: turn.utterance_id, audio: mic.subarray(offset, offset + 3200) });
|
|
73
|
+
await sleep(100);
|
|
74
|
+
}
|
|
75
|
+
const commitStart = Date.now(); await session.commit(sequence);
|
|
76
|
+
const deadline = Date.now() + 40000;
|
|
77
|
+
while (!controls.some(c => c.type === 'playback.end' || c.type === 'voice.error') && Date.now() < deadline) await sleep(20);
|
|
78
|
+
assert.deepEqual(controls.filter(c => c.type === 'voice.error'), []);
|
|
79
|
+
assert(controls.some(c => c.type === 'playback.end'), 'Missing final audio');
|
|
80
|
+
assert(controls.some(c => c.type === 'response.text' && c.text === surfaces.display && c.final));
|
|
81
|
+
assert.deepEqual(spokenInputs, [surfaces.spoken]); assert(output.length > 0);
|
|
82
|
+
report.voiceSession = { transcript: submittedText, partials: controls.filter(c => c.type === 'stt.partial').length,
|
|
83
|
+
displayCharacters: surfaces.display.length, ttsCharacters: spokenInputs.join('').length, audioSeconds: played / 16000,
|
|
84
|
+
commitToPlaybackEndMs: Date.now() - commitStart, nextTurnListening: listening.utterance_id !== turn.utterance_id, fullReportExcludedFromTts: true };
|
|
85
|
+
fs.writeFileSync(join(root, 'playback.wav'), wav(Buffer.concat(output)), { mode: 0o600 });
|
|
86
|
+
assert(report.voiceSession.nextTurnListening); report.pass = true;
|
|
87
|
+
} catch (error) { report.error = { code: error.code, message: String(error.message).replaceAll(key, '[redacted]') }; }
|
|
88
|
+
finally { await session?.close(); clearTimeout(watchdog); fs.writeFileSync(join(root, 'report.json'), JSON.stringify(report, null, 2), { mode: 0o600 }); console.log(JSON.stringify(report, null, 2)); }
|
|
89
|
+
process.exitCode = report.pass ? 0 : 1;
|
|
90
|
+
})().catch(error => { console.error(error.code || error.name); process.exitCode = 1; });
|