@fastagent-sh/fastagent 0.24.4 → 0.26.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 +54 -82
- package/dist/agent-api.d.ts +460 -0
- package/dist/agent-api.js +209 -0
- package/dist/answer-text.d.ts +8 -0
- package/dist/answer-text.js +18 -0
- package/dist/atomic-write.d.ts +2 -2
- package/dist/atomic-write.js +2 -2
- package/dist/authoring.d.ts +81 -0
- package/dist/authoring.js +245 -0
- package/dist/channel.d.ts +29 -15
- package/dist/channel.js +4 -1
- package/dist/channels/agentcore-protocol.d.ts +36 -50
- package/dist/channels/agentcore-protocol.js +3 -3
- package/dist/channels/agentcore-service.d.ts +8 -18
- package/dist/channels/agentcore-service.js +108 -127
- package/dist/channels/agentcore.d.ts +8 -24
- package/dist/channels/agentcore.js +32 -65
- package/dist/channels/body.d.ts +0 -16
- package/dist/channels/body.js +0 -25
- package/dist/channels/busy.js +6 -1
- package/dist/channels/context.d.ts +4 -0
- package/dist/channels/context.js +35 -0
- package/dist/channels/discover.d.ts +18 -20
- package/dist/channels/discover.js +41 -99
- package/dist/channels/feishu/feishu-api.d.ts +54 -4
- package/dist/channels/feishu/feishu-api.js +192 -149
- package/dist/channels/feishu/feishu.d.ts +3 -0
- package/dist/channels/feishu/feishu.js +117 -106
- package/dist/channels/feishu/history.d.ts +55 -0
- package/dist/channels/feishu/history.js +199 -0
- package/dist/channels/feishu/invoke-turn.d.ts +13 -11
- package/dist/channels/feishu/invoke-turn.js +9 -10
- package/dist/channels/feishu/model.d.ts +2 -1
- package/dist/channels/feishu/normalize.js +100 -0
- package/dist/channels/feishu/preview.d.ts +2 -2
- package/dist/channels/feishu/register-webhook.d.ts +1 -0
- package/dist/channels/feishu/register-webhook.js +3 -2
- package/dist/channels/feishu/scaffold/channel.ts +65 -46
- package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
- package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
- package/dist/channels/feishu/setup-mode.d.ts +45 -25
- package/dist/channels/feishu/setup-mode.js +74 -23
- package/dist/channels/feishu/shared-api.d.ts +24 -6
- package/dist/channels/feishu/shared-api.js +30 -24
- package/dist/channels/kit/context-buffer.d.ts +11 -2
- package/dist/channels/kit/context-buffer.js +7 -4
- package/dist/channels/kit/delivery.d.ts +19 -3
- package/dist/channels/kit/delivery.js +74 -15
- package/dist/channels/kit/event-stream.js +2 -1
- package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
- package/dist/channels/kit/invoke-turn-kit.js +62 -71
- package/dist/channels/kit/place-history.d.ts +107 -0
- package/dist/channels/kit/place-history.js +182 -0
- package/dist/channels/kit/preview-kit.d.ts +9 -5
- package/dist/channels/kit/preview-kit.js +36 -15
- package/dist/channels/kit/room-threads.d.ts +46 -0
- package/dist/channels/kit/room-threads.js +81 -0
- package/dist/channels/kit/seen.js +1 -1
- package/dist/channels/kit/stop-command.d.ts +6 -3
- package/dist/channels/kit/stop-command.js +20 -18
- package/dist/channels/kit/thread-fork.d.ts +17 -0
- package/dist/channels/kit/thread-fork.js +43 -0
- package/dist/channels/kit/thread-participants.js +1 -1
- package/dist/channels/kit/transport.d.ts +5 -0
- package/dist/channels/kit/transport.js +5 -0
- package/dist/channels/kit/turn-runner.d.ts +10 -9
- package/dist/channels/kit/turn-runner.js +17 -19
- package/dist/channels/kit/turn-store.d.ts +5 -4
- package/dist/channels/kit/turn-store.js +5 -5
- package/dist/channels/lark/lark.d.ts +3 -0
- package/dist/channels/lark/lark.js +5 -0
- package/dist/channels/lark/onboard.d.ts +5 -3
- package/dist/channels/lark/onboard.js +14 -14
- package/dist/channels/lark/scaffold/channel.ts +67 -44
- package/dist/channels/lark/scaffold/lark-send.ts +38 -33
- package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
- package/dist/channels/respond.d.ts +0 -4
- package/dist/channels/respond.js +1 -1
- package/dist/channels/router.d.ts +18 -0
- package/dist/channels/router.js +68 -0
- package/dist/channels/serve.d.ts +5 -70
- package/dist/channels/serve.js +105 -287
- package/dist/channels/slack/history.d.ts +42 -0
- package/dist/channels/slack/history.js +282 -0
- package/dist/channels/slack/invoke-turn.d.ts +12 -5
- package/dist/channels/slack/invoke-turn.js +7 -5
- package/dist/channels/slack/manifest.d.ts +1 -4
- package/dist/channels/slack/manifest.js +22 -10
- package/dist/channels/slack/model.d.ts +2 -1
- package/dist/channels/slack/onboard.d.ts +0 -2
- package/dist/channels/slack/onboard.js +2 -4
- package/dist/channels/slack/onboarding-state.d.ts +0 -2
- package/dist/channels/slack/onboarding-state.js +0 -1
- package/dist/channels/slack/parse.d.ts +0 -5
- package/dist/channels/slack/parse.js +1 -16
- package/dist/channels/slack/preview.d.ts +2 -3
- package/dist/channels/slack/preview.js +78 -52
- package/dist/channels/slack/register-webhook.d.ts +1 -0
- package/dist/channels/slack/register-webhook.js +6 -5
- package/dist/channels/slack/scaffold/channel.ts +23 -24
- package/dist/channels/slack/scaffold/slack-send.ts +50 -43
- package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
- package/dist/channels/slack/setup-server.d.ts +19 -0
- package/dist/channels/slack/setup-server.js +113 -11
- package/dist/channels/slack/shared-api.d.ts +15 -4
- package/dist/channels/slack/shared-api.js +19 -14
- package/dist/channels/slack/slack-api.d.ts +61 -2
- package/dist/channels/slack/slack-api.js +28 -5
- package/dist/channels/slack/slack.js +97 -52
- package/dist/channels/slack/welcomed.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +6 -1
- package/dist/channels/telegram/context-buffer.js +19 -0
- package/dist/channels/telegram/invoke-turn.d.ts +8 -4
- package/dist/channels/telegram/invoke-turn.js +7 -7
- package/dist/channels/telegram/parse.d.ts +7 -2
- package/dist/channels/telegram/parse.js +7 -0
- package/dist/channels/telegram/preview.d.ts +2 -2
- package/dist/channels/telegram/register-webhook.d.ts +1 -0
- package/dist/channels/telegram/register-webhook.js +3 -2
- package/dist/channels/telegram/scaffold/channel.ts +27 -28
- package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
- package/dist/channels/telegram/shared-api.d.ts +34 -0
- package/dist/channels/telegram/shared-api.js +79 -0
- package/dist/channels/telegram/telegram-api.d.ts +15 -5
- package/dist/channels/telegram/telegram-api.js +10 -6
- package/dist/channels/telegram/telegram.js +27 -21
- package/dist/channels/telegram/turn-store.js +1 -1
- package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
- package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
- package/dist/cli/add-feishu.d.ts +42 -12
- package/dist/cli/add-feishu.js +177 -124
- package/dist/cli/add-slack.d.ts +8 -3
- package/dist/cli/add-slack.js +116 -64
- package/dist/cli/auth-view.d.ts +10 -3
- package/dist/cli/auth-view.js +8 -6
- package/dist/cli/box-login.d.ts +18 -7
- package/dist/cli/box-login.js +46 -12
- package/dist/cli/commands/add.d.ts +15 -4
- package/dist/cli/commands/add.js +253 -143
- package/dist/cli/commands/auth.d.ts +6 -0
- package/dist/cli/commands/auth.js +50 -0
- package/dist/cli/commands/content.d.ts +6 -0
- package/dist/cli/commands/content.js +45 -0
- package/dist/cli/commands/deploy/agentcore.js +50 -77
- package/dist/cli/commands/deploy/docker.js +38 -39
- package/dist/cli/commands/deploy/fly.js +21 -18
- package/dist/cli/commands/deploy/host-login.d.ts +26 -0
- package/dist/cli/commands/deploy/host-login.js +90 -0
- package/dist/cli/commands/deploy/railway.js +21 -26
- package/dist/cli/commands/deploy/shared.d.ts +45 -17
- package/dist/cli/commands/deploy/shared.js +34 -15
- package/dist/cli/commands/deploy.d.ts +0 -4
- package/dist/cli/commands/deploy.js +237 -50
- package/dist/cli/commands/destroy.js +8 -6
- package/dist/cli/commands/dev.d.ts +0 -2
- package/dist/cli/commands/dev.js +20 -19
- package/dist/cli/commands/env.d.ts +12 -0
- package/dist/cli/commands/env.js +191 -0
- package/dist/cli/commands/info.js +95 -156
- package/dist/cli/commands/init.d.ts +2 -2
- package/dist/cli/commands/init.js +35 -33
- package/dist/cli/commands/invoke.d.ts +6 -0
- package/dist/cli/commands/invoke.js +132 -16
- package/dist/cli/commands/login.d.ts +31 -2
- package/dist/cli/commands/login.js +416 -65
- package/dist/cli/commands/logout.d.ts +4 -0
- package/dist/cli/commands/logout.js +53 -0
- package/dist/cli/commands/logs.js +8 -6
- package/dist/cli/commands/models.d.ts +2 -1
- package/dist/cli/commands/models.js +39 -37
- package/dist/cli/commands/schedules.d.ts +21 -0
- package/dist/cli/commands/schedules.js +143 -0
- package/dist/cli/commands/start.d.ts +11 -10
- package/dist/cli/commands/start.js +35 -62
- package/dist/cli/content-view.d.ts +9 -0
- package/dist/cli/content-view.js +26 -0
- package/dist/cli/fail.d.ts +9 -12
- package/dist/cli/fail.js +17 -14
- package/dist/cli/invoke-stream.d.ts +12 -3
- package/dist/cli/invoke-stream.js +85 -23
- package/dist/cli/kernel.d.ts +9 -2
- package/dist/cli/kernel.js +15 -6
- package/dist/cli/login-relay.d.ts +6 -4
- package/dist/cli/login-relay.js +19 -7
- package/dist/cli/models-view.d.ts +5 -1
- package/dist/cli/models-view.js +1 -1
- package/dist/cli/person.d.ts +10 -0
- package/dist/cli/person.js +23 -0
- package/dist/cli/program.js +421 -203
- package/dist/cli/serve.d.ts +13 -63
- package/dist/cli/serve.js +31 -121
- package/dist/cli/shared.d.ts +21 -48
- package/dist/cli/shared.js +70 -89
- package/dist/cli/ticket.d.ts +136 -0
- package/dist/cli/ticket.js +476 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +2 -0
- package/dist/content/declare.d.ts +70 -0
- package/dist/content/declare.js +163 -0
- package/dist/content/file.d.ts +32 -0
- package/dist/content/file.js +119 -0
- package/dist/content/git.d.ts +41 -0
- package/dist/content/git.js +220 -0
- package/dist/content/mount.d.ts +2 -0
- package/dist/content/mount.js +15 -0
- package/dist/content/resolve.d.ts +52 -0
- package/dist/content/resolve.js +151 -0
- package/dist/content/source.d.ts +27 -0
- package/dist/content/source.js +53 -0
- package/dist/conversation-sessions.d.ts +26 -0
- package/dist/conversation-sessions.js +99 -0
- package/dist/declared-secrets.d.ts +19 -37
- package/dist/declared-secrets.js +16 -58
- package/dist/deploy/agentcore/destroy.d.ts +3 -3
- package/dist/deploy/agentcore/destroy.js +46 -28
- package/dist/deploy/agentcore/forwarder.js +105 -22
- package/dist/deploy/agentcore/logs.js +1 -1
- package/dist/deploy/agentcore/plan.d.ts +10 -24
- package/dist/deploy/agentcore/plan.js +31 -135
- package/dist/deploy/agentcore/run.d.ts +1 -1
- package/dist/deploy/agentcore/run.js +8 -5
- package/dist/deploy/agentcore/shell.d.ts +1 -1
- package/dist/deploy/agentcore/shell.js +5 -5
- package/dist/deploy/api.d.ts +34 -0
- package/dist/deploy/api.js +50 -0
- package/dist/deploy/box-shell.d.ts +6 -0
- package/dist/deploy/box-shell.js +7 -0
- package/dist/deploy/build-context.d.ts +7 -5
- package/dist/deploy/build-context.js +41 -35
- package/dist/deploy/channel-ingress.d.ts +6 -1
- package/dist/deploy/channel-ingress.js +18 -5
- package/dist/deploy/container.d.ts +22 -9
- package/dist/deploy/container.js +96 -55
- package/dist/deploy/docker/plan.d.ts +3 -2
- package/dist/deploy/docker/plan.js +13 -14
- package/dist/deploy/docker/run.d.ts +2 -2
- package/dist/deploy/docker/run.js +8 -4
- package/dist/deploy/fly/plan.d.ts +1 -1
- package/dist/deploy/fly/plan.js +12 -21
- package/dist/deploy/fly/run.d.ts +10 -6
- package/dist/deploy/fly/run.js +11 -6
- package/dist/deploy/hosts.d.ts +2 -1
- package/dist/deploy/hosts.js +2 -1
- package/dist/deploy/preflight.d.ts +21 -23
- package/dist/deploy/preflight.js +223 -202
- package/dist/deploy/railway/plan.d.ts +8 -6
- package/dist/deploy/railway/plan.js +15 -58
- package/dist/deploy/railway/run.d.ts +8 -3
- package/dist/deploy/railway/run.js +12 -8
- package/dist/deploy/residency.d.ts +5 -11
- package/dist/deploy/residency.js +0 -6
- package/dist/deploy/runner.d.ts +1 -1
- package/dist/deploy/runner.js +1 -1
- package/dist/deploy/secrets.d.ts +16 -2
- package/dist/deploy/secrets.js +33 -6
- package/dist/deploy/workspace.d.ts +11 -6
- package/dist/deploy/workspace.js +55 -38
- package/dist/dev-supervisor.d.ts +11 -2
- package/dist/dev-supervisor.js +52 -27
- package/dist/effect-port.d.ts +4 -4
- package/dist/effect-port.js +4 -4
- package/dist/env.d.ts +0 -13
- package/dist/env.js +1 -44
- package/dist/environment/apt.d.ts +4 -0
- package/dist/environment/apt.js +32 -0
- package/dist/environment/declare.d.ts +59 -0
- package/dist/environment/declare.js +210 -0
- package/dist/environment/mise.d.ts +51 -0
- package/dist/environment/mise.js +272 -0
- package/dist/feishu.d.ts +2 -2
- package/dist/feishu.js +2 -2
- package/dist/git.d.ts +13 -0
- package/dist/git.js +55 -0
- package/dist/harnesses/models/catalog.d.ts +20 -0
- package/dist/harnesses/models/catalog.js +254 -0
- package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
- package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
- package/dist/harnesses/models/environment.d.ts +48 -0
- package/dist/harnesses/models/environment.js +88 -0
- package/dist/harnesses/models/host.d.ts +30 -0
- package/dist/harnesses/models/host.js +97 -0
- package/dist/{engines/pi → harnesses/models}/login.d.ts +66 -70
- package/dist/harnesses/models/login.js +439 -0
- package/dist/harnesses/models/models.d.ts +84 -0
- package/dist/harnesses/models/models.js +197 -0
- package/dist/harnesses/pi-durable/agent.d.ts +42 -0
- package/dist/harnesses/pi-durable/agent.js +581 -0
- package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
- package/dist/harnesses/pi-durable/defaults.js +26 -0
- package/dist/harnesses/pi-durable/define.d.ts +77 -0
- package/dist/harnesses/pi-durable/define.js +28 -0
- package/dist/harnesses/pi-durable/entries.d.ts +32 -0
- package/dist/harnesses/pi-durable/entries.js +126 -0
- package/dist/harnesses/pi-durable/events.d.ts +25 -0
- package/dist/harnesses/pi-durable/events.js +238 -0
- package/dist/harnesses/pi-durable/ids.d.ts +16 -0
- package/dist/harnesses/pi-durable/ids.js +55 -0
- package/dist/harnesses/pi-durable/messages.d.ts +41 -0
- package/dist/harnesses/pi-durable/messages.js +116 -0
- package/dist/harnesses/pi-durable/open.d.ts +95 -0
- package/dist/harnesses/pi-durable/open.js +330 -0
- package/dist/harnesses/pi-durable/reads.d.ts +30 -0
- package/dist/harnesses/pi-durable/reads.js +94 -0
- package/dist/harnesses/pi-durable/registry.d.ts +18 -0
- package/dist/harnesses/pi-durable/registry.js +56 -0
- package/dist/harnesses/pi-durable/release.d.ts +13 -0
- package/dist/harnesses/pi-durable/release.js +40 -0
- package/dist/harnesses/pi-durable/serve.d.ts +90 -0
- package/dist/harnesses/pi-durable/serve.js +186 -0
- package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
- package/dist/harnesses/pi-durable/sessions.js +47 -0
- package/dist/harnesses/pi-durable/storage.d.ts +4 -0
- package/dist/harnesses/pi-durable/storage.js +49 -0
- package/dist/harnesses/pi-durable/stream.d.ts +20 -0
- package/dist/harnesses/pi-durable/stream.js +221 -0
- package/dist/harnesses/pi-durable/text.d.ts +30 -0
- package/dist/harnesses/pi-durable/text.js +292 -0
- package/dist/harnesses/pi-durable/wake.d.ts +11 -0
- package/dist/harnesses/pi-durable/wake.js +90 -0
- package/dist/http/client.d.ts +6 -0
- package/dist/http/client.js +256 -0
- package/dist/http/contract.d.ts +541 -0
- package/dist/http/contract.js +179 -0
- package/dist/http/server.d.ts +34 -0
- package/dist/http/server.js +194 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +13 -5
- package/dist/lark.d.ts +2 -2
- package/dist/lark.js +2 -2
- package/dist/locked-file.d.ts +17 -0
- package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
- package/dist/once.d.ts +6 -0
- package/dist/once.js +23 -0
- package/dist/paths.d.ts +36 -52
- package/dist/paths.js +66 -182
- package/dist/runtime.d.ts +25 -2
- package/dist/runtime.js +27 -0
- package/dist/scaffold/add-channel.d.ts +14 -27
- package/dist/scaffold/add-channel.js +82 -180
- package/dist/scaffold/init.d.ts +17 -18
- package/dist/scaffold/init.js +87 -99
- package/dist/scaffold/templates/SYSTEM.md +12 -0
- package/dist/scaffold/templates/env.example +4 -10
- package/dist/scaffold/templates/fastagent.config.ts +21 -33
- package/dist/scaffold/templates/gitignore +8 -1
- package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
- package/dist/scaffold/templates.d.ts +9 -4
- package/dist/scaffold/templates.js +7 -5
- package/dist/scaffold/vendor-skill.d.ts +1 -4
- package/dist/scaffold/vendor-skill.js +5 -6
- package/dist/schedule/cron.d.ts +13 -0
- package/dist/schedule/cron.js +52 -0
- package/dist/schedule/eventbridge-cron.d.ts +17 -0
- package/dist/schedule/eventbridge-cron.js +88 -0
- package/dist/schedule/schedule.d.ts +23 -0
- package/dist/schedule/schedule.js +34 -0
- package/dist/schedule/scheduler.d.ts +34 -33
- package/dist/schedule/scheduler.js +251 -137
- package/dist/schedule/state.d.ts +4 -14
- package/dist/schedule/state.js +7 -7
- package/dist/schedule/wake-alarm.d.ts +22 -2
- package/dist/schedule/wake-alarm.js +66 -20
- package/dist/schedule/wakeups.d.ts +16 -15
- package/dist/schedule/wakeups.js +47 -45
- package/dist/service.d.ts +56 -169
- package/dist/service.js +91 -187
- package/dist/serving-command.d.ts +10 -0
- package/dist/serving-command.js +16 -0
- package/dist/sign-in-api.d.ts +147 -0
- package/dist/sign-in-api.js +9 -0
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/dist/{channels/kit/state.js → state-file.js} +6 -6
- package/dist/telegram.d.ts +1 -0
- package/dist/telegram.js +1 -0
- package/dist/tunnel.d.ts +3 -0
- package/dist/tunnel.js +14 -5
- package/dist/value-file.d.ts +40 -0
- package/dist/value-file.js +164 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +14 -1
- package/package.json +18 -28
- package/dist/agent.d.ts +0 -106
- package/dist/agent.js +0 -21
- package/dist/channels/control.d.ts +0 -31
- package/dist/channels/control.js +0 -329
- package/dist/channels/define-channel.d.ts +0 -39
- package/dist/channels/define-channel.js +0 -9
- package/dist/channels/feishu/context-buffer.d.ts +0 -35
- package/dist/channels/feishu/context-buffer.js +0 -65
- package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/http.d.ts +0 -23
- package/dist/channels/http.js +0 -57
- package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/slack/context-buffer.d.ts +0 -23
- package/dist/channels/slack/context-buffer.js +0 -37
- package/dist/channels/sse.d.ts +0 -4
- package/dist/channels/sse.js +0 -66
- package/dist/cli/commands/chat.d.ts +0 -3
- package/dist/cli/commands/chat.js +0 -14
- package/dist/cli/commands/routine-run.d.ts +0 -6
- package/dist/cli/commands/routine-run.js +0 -46
- package/dist/cli/commands/routine.d.ts +0 -25
- package/dist/cli/commands/routine.js +0 -122
- package/dist/cli/commands/tool.d.ts +0 -1
- package/dist/cli/commands/tool.js +0 -57
- package/dist/collect.d.ts +0 -63
- package/dist/collect.js +0 -90
- package/dist/core.d.ts +0 -13
- package/dist/core.js +0 -15
- package/dist/engines/pi/agent-models.d.ts +0 -83
- package/dist/engines/pi/agent-models.js +0 -120
- package/dist/engines/pi/agent-session-factory.d.ts +0 -121
- package/dist/engines/pi/agent-session-factory.js +0 -535
- package/dist/engines/pi/chat.d.ts +0 -3
- package/dist/engines/pi/chat.js +0 -8
- package/dist/engines/pi/config.d.ts +0 -71
- package/dist/engines/pi/config.js +0 -199
- package/dist/engines/pi/create.d.ts +0 -170
- package/dist/engines/pi/create.js +0 -343
- package/dist/engines/pi/definition.d.ts +0 -74
- package/dist/engines/pi/definition.js +0 -236
- package/dist/engines/pi/entry-images.d.ts +0 -14
- package/dist/engines/pi/entry-images.js +0 -23
- package/dist/engines/pi/invoke-session.d.ts +0 -18
- package/dist/engines/pi/invoke-session.js +0 -403
- package/dist/engines/pi/locked-file.d.ts +0 -9
- package/dist/engines/pi/login.js +0 -331
- package/dist/engines/pi/machine.d.ts +0 -53
- package/dist/engines/pi/machine.js +0 -137
- package/dist/engines/pi/models.d.ts +0 -200
- package/dist/engines/pi/models.js +0 -533
- package/dist/engines/pi/open.d.ts +0 -180
- package/dist/engines/pi/open.js +0 -270
- package/dist/engines/pi/openai-account-models.d.ts +0 -35
- package/dist/engines/pi/openai-account-models.js +0 -116
- package/dist/engines/pi/report.d.ts +0 -18
- package/dist/engines/pi/report.js +0 -59
- package/dist/engines/pi/retry-event.d.ts +0 -6
- package/dist/engines/pi/retry-event.js +0 -15
- package/dist/engines/pi/service.d.ts +0 -9
- package/dist/engines/pi/service.js +0 -14
- package/dist/engines/pi/session-builder.d.ts +0 -13
- package/dist/engines/pi/session-builder.js +0 -117
- package/dist/engines/pi/session-control.d.ts +0 -77
- package/dist/engines/pi/session-control.js +0 -908
- package/dist/engines/pi/session-effects.d.ts +0 -14
- package/dist/engines/pi/session-effects.js +0 -30
- package/dist/engines/pi/session-inheritance.d.ts +0 -21
- package/dist/engines/pi/session-inheritance.js +0 -204
- package/dist/engines/pi/session-markers.d.ts +0 -49
- package/dist/engines/pi/session-markers.js +0 -60
- package/dist/engines/pi/session-settings.d.ts +0 -67
- package/dist/engines/pi/session-settings.js +0 -119
- package/dist/engines/pi/session-store.d.ts +0 -69
- package/dist/engines/pi/session-store.js +0 -430
- package/dist/engines/pi/tool-context.d.ts +0 -45
- package/dist/engines/pi/tool-context.js +0 -31
- package/dist/engines/pi/tool.d.ts +0 -99
- package/dist/engines/pi/tool.js +0 -118
- package/dist/engines/pi/turn-kit.d.ts +0 -92
- package/dist/engines/pi/turn-kit.js +0 -288
- package/dist/engines/pi/wake-tool.d.ts +0 -9
- package/dist/engines/pi/wake-tool.js +0 -92
- package/dist/loader.d.ts +0 -41
- package/dist/loader.js +0 -116
- package/dist/node.d.ts +0 -3
- package/dist/node.js +0 -4
- package/dist/observe.d.ts +0 -6
- package/dist/observe.js +0 -47
- package/dist/pi.d.ts +0 -25
- package/dist/pi.js +0 -15
- package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
- package/dist/schedule/discover.d.ts +0 -17
- package/dist/schedule/discover.js +0 -61
- package/dist/schedule/routine.d.ts +0 -43
- package/dist/schedule/routine.js +0 -32
- package/dist/schedule/run.d.ts +0 -55
- package/dist/schedule/run.js +0 -146
- package/dist/secrets-gate.d.ts +0 -32
- package/dist/secrets-gate.js +0 -33
- package/dist/session-remote.d.ts +0 -47
- package/dist/session-remote.js +0 -460
- package/dist/session.d.ts +0 -466
- package/dist/session.js +0 -57
- /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { attachedFilesManifest, attributedFileName, loadBackground, missingAttachmentsNote, turnStream, promptContent, } from "../kit/invoke-turn-kit.js";
|
|
2
2
|
import { resolveFiles, resolveImages } from "./telegram-api.js";
|
|
3
3
|
/** Appended to the prompt (not the system prompt): the channel owns Telegram-HTML formatting. */
|
|
4
4
|
const HTML_INSTRUCTION = "\n\n(Format your reply in Telegram-supported HTML — <b> <i> <u> <s> <code> <pre> <a href> — not Markdown.)";
|
|
5
5
|
/**
|
|
6
6
|
* Resolve a turn's attachments: images (vision) inline, files downloaded to disk with their absolute
|
|
7
7
|
* paths listed in a manifest the agent reads with its tools. Two tiers, different failure policies.
|
|
8
|
-
* PRIMARY (this turn's own message) THROWS on any load failure — the caller turns it into a `
|
|
8
|
+
* PRIMARY (this turn's own message) THROWS on any load failure — the caller turns it into a `rejected`
|
|
9
9
|
* event, so the agent never runs on inputs the user sent but we failed to load. BACKGROUND (`buffered`,
|
|
10
10
|
* from the un-summoned discussion) degrades PER ATTACHMENT — a warn + a prompt note — rather than
|
|
11
11
|
* failing the ask it merely accompanies: one expired earlier file must neither block the answer nor drag
|
|
@@ -43,17 +43,17 @@ async function resolveTurnAttachments(t, attachments) {
|
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* Run one turn: resolve its attachments, then ask the agent (invoke-turn-kit — a primary-attachment
|
|
46
|
-
* failure surfaces as a `
|
|
46
|
+
* failure surfaces as a `rejected` event, never a silent drop). `key` is the turn's: the update it answers.
|
|
47
47
|
*/
|
|
48
|
-
export function telegramTurnStream(agent,
|
|
48
|
+
export function telegramTurnStream(agent, turn, transport, attachments) {
|
|
49
49
|
return turnStream({
|
|
50
50
|
agent,
|
|
51
51
|
label: "[telegram]",
|
|
52
|
-
|
|
52
|
+
session: turn.session,
|
|
53
53
|
resolve: () => resolveTurnAttachments(transport, attachments),
|
|
54
54
|
turn: (resolved) => ({
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
idempotencyKey: turn.key,
|
|
56
|
+
content: promptContent(`${turn.text}${resolved.promptSuffix}${HTML_INSTRUCTION}`, resolved.images),
|
|
57
57
|
}),
|
|
58
58
|
// A Bot API download that failed may simply succeed on the redelivery.
|
|
59
59
|
retryableLoadFailure: () => true,
|
|
@@ -98,8 +98,8 @@ export interface TelegramUpdate {
|
|
|
98
98
|
* ignore.
|
|
99
99
|
*/
|
|
100
100
|
export interface TelegramRoute {
|
|
101
|
-
/**
|
|
102
|
-
|
|
101
|
+
/** Which conversation it belongs to, the one its session is kept by (default: `chat` or `chat:thread`). */
|
|
102
|
+
conversation?: string;
|
|
103
103
|
/** Reply target chat (default: the message's chat). */
|
|
104
104
|
chatId?: number | string;
|
|
105
105
|
/** Reply thread (default: the message's thread). */
|
|
@@ -117,6 +117,11 @@ export declare function ownImages(m: TelegramMessage): string[];
|
|
|
117
117
|
export declare function extractFiles(m: TelegramMessage): string[];
|
|
118
118
|
/** The message's OWN files, without the replied-to message's. */
|
|
119
119
|
export declare function ownFiles(m: TelegramMessage): string[];
|
|
120
|
+
/**
|
|
121
|
+
* The place a message lives in: a chat, or a forum topic within it. The context buffer's bucket and the default
|
|
122
|
+
* conversation, for an update and for a message the agent sends itself alike.
|
|
123
|
+
*/
|
|
124
|
+
export declare function telegramPlaceKey(chatId: number | string, threadId?: number): string;
|
|
120
125
|
/** A stable sender label for attribution. */
|
|
121
126
|
export declare function fromLabel(from: TelegramMessage["from"]): string | undefined;
|
|
122
127
|
/**
|
|
@@ -21,6 +21,13 @@ export function extractFiles(m) {
|
|
|
21
21
|
export function ownFiles(m) {
|
|
22
22
|
return [m.document?.file_id, m.voice?.file_id, m.video?.file_id, m.audio?.file_id].filter((id) => Boolean(id));
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The place a message lives in: a chat, or a forum topic within it. The context buffer's bucket and the default
|
|
26
|
+
* conversation, for an update and for a message the agent sends itself alike.
|
|
27
|
+
*/
|
|
28
|
+
export function telegramPlaceKey(chatId, threadId) {
|
|
29
|
+
return threadId ? `${chatId}:${threadId}` : `${chatId}`;
|
|
30
|
+
}
|
|
24
31
|
/** A stable sender label for attribution. */
|
|
25
32
|
export function fromLabel(from) {
|
|
26
33
|
if (!from)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* carry unbalanced HTML); on completion the same message is edited into the final answer as HTML. One
|
|
6
6
|
* message, works in groups and private (unlike sendMessageDraft, which is private/forum-topic only).
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { TurnEvent } from "../kit/invoke-turn-kit.ts";
|
|
9
9
|
import * as Effect from "effect/Effect";
|
|
10
10
|
import type * as Stream from "effect/Stream";
|
|
11
11
|
import { type PortFailure } from "../../effect-port.ts";
|
|
@@ -25,4 +25,4 @@ export declare function deliverTelegramAnswer(api: string, botToken: string, tar
|
|
|
25
25
|
* model). Preview edits are best-effort (logged once if they fail); the final write is authoritative
|
|
26
26
|
* and surfaces a real failure (bad token, etc.).
|
|
27
27
|
*/
|
|
28
|
-
export declare function telegramReply(events: Stream.Stream<
|
|
28
|
+
export declare function telegramReply(events: Stream.Stream<TurnEvent, PortFailure>, api: string, botToken: string, target: Target, formatError: (failed: TelegramFailure) => string | undefined, previewId?: number, onAnswered?: (answer: string) => void): Effect.Effect<void, PortFailure, import("effect/Scope").Scope>;
|
|
@@ -8,6 +8,7 @@ import { type RegistrationOutcome } from "../registration.ts";
|
|
|
8
8
|
* Reports its outcome as a {@link RegistrationOutcome} fact; gating policy belongs to the caller.
|
|
9
9
|
*/
|
|
10
10
|
export declare function registerTelegramWebhook(baseUrl: string, opts?: {
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
11
12
|
attempts?: number;
|
|
12
13
|
retryMs?: number;
|
|
13
14
|
}): Promise<RegistrationOutcome>;
|
|
@@ -18,8 +18,9 @@ function isTransientRegistrationError(error) {
|
|
|
18
18
|
* Reports its outcome as a {@link RegistrationOutcome} fact; gating policy belongs to the caller.
|
|
19
19
|
*/
|
|
20
20
|
export async function registerTelegramWebhook(baseUrl, opts = {}) {
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
const env = opts.env ?? process.env;
|
|
22
|
+
const botToken = env.TELEGRAM_BOT_TOKEN;
|
|
23
|
+
const secret = env.TELEGRAM_SECRET_TOKEN;
|
|
23
24
|
const webhookUrl = `${baseUrl}/telegram`;
|
|
24
25
|
if (!botToken || !secret) {
|
|
25
26
|
log.info(`[fastagent] telegram: set TELEGRAM_BOT_TOKEN + TELEGRAM_SECRET_TOKEN in .env, then re-run to auto-register. Webhook URL: ${webhookUrl}`);
|
|
@@ -1,36 +1,35 @@
|
|
|
1
|
+
import { needs } from "@fastagent-sh/fastagent";
|
|
1
2
|
import { telegramChannel } from "@fastagent-sh/fastagent/telegram";
|
|
2
|
-
import { defineChannel } from "@fastagent-sh/fastagent";
|
|
3
3
|
|
|
4
4
|
// A channel = a third-party ADAPTER (telegramChannel: verify + run + reply) configured with YOUR policy.
|
|
5
|
-
// fastagent
|
|
6
|
-
//
|
|
5
|
+
// fastagent registers this file from src/channels/, mounts POST /telegram, and hands the adapter the
|
|
6
|
+
// agent, its sessions and a directory of its own; this file holds only policy. Setup:
|
|
7
7
|
// 1. @BotFather → /newbot → put the bot token in TELEGRAM_BOT_TOKEN
|
|
8
8
|
// 2. pick a random TELEGRAM_SECRET_TOKEN (verifies that inbound updates really come from Telegram)
|
|
9
9
|
// 3. register the webhook once, pointing Telegram at POST /telegram with that secret:
|
|
10
10
|
// curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
|
|
11
11
|
// -d url=https://your.host/telegram -d secret_token=$TELEGRAM_SECRET_TOKEN
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
12
|
+
//
|
|
13
|
+
// `needs` declares the env vars this channel reads: fastagent carries them to a deployed box and
|
|
14
|
+
// refuses to serve while one is unset.
|
|
15
|
+
export default needs(["TELEGRAM_BOT_TOKEN", "TELEGRAM_SECRET_TOKEN"], (secrets) =>
|
|
16
|
+
telegramChannel({
|
|
17
|
+
secretToken: secrets.TELEGRAM_SECRET_TOKEN, // verifies inbound updates really come from Telegram
|
|
18
|
+
botToken: secrets.TELEGRAM_BOT_TOKEN, // used to send the agent's reply back to the chat
|
|
19
|
+
// Dev/personal bot: surface raw errors to the chat so you (and your AI agent) can act on them. The
|
|
20
|
+
// chat is customer-facing by default — for a public bot, drop this or return a neutral string;
|
|
21
|
+
// full details always go to the server log regardless.
|
|
22
|
+
onError: (failed) => `⚠️ ${failed.details}`,
|
|
23
|
+
// Your bot's @username. Optional — resolved via getMe when omitted — but set it if you pass a `route`
|
|
24
|
+
// below: the channel needs its own copy to recognise a `/stop` addressed to it.
|
|
25
|
+
// botUsername: "your_bot",
|
|
26
|
+
// The channel owns transport + format (HTML) + attachments (photo→vision, file→disk) + streaming.
|
|
27
|
+
// `route` (POLICY) is OPTIONAL — omitted, it uses defaultTelegramRoute: private chats always answer,
|
|
28
|
+
// groups only on a reply to THIS bot, an @mention of it, or a `/stop` addressed to it. Override to
|
|
29
|
+
// customise, reusing the export — but pass your bot's identity BOTH here and as `botUsername` above
|
|
30
|
+
// (the omitted default gets it from the channel; a bare `defaultTelegramRoute(u)` answers only
|
|
31
|
+
// private chats):
|
|
32
|
+
// route: (u) => defaultTelegramRoute(u, { botUsername: "my_bot" }) && { conversation: `user:${u.message?.from?.id}` },
|
|
33
|
+
// route: (u) => defaultTelegramRoute(u, { botUsername: "my_bot" }) && { text: `${telegramEnvelope(u.message!)}\n[extra]` },
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
@@ -1,112 +1,65 @@
|
|
|
1
|
-
import { defineTool, z } from "@fastagent-sh/fastagent";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
1
|
import { basename } from "node:path";
|
|
2
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
3
|
+
import { defineTool } from "@earendil-works/pi-durable";
|
|
4
|
+
import { needs } from "@fastagent-sh/fastagent";
|
|
5
|
+
import { telegramTransport } from "@fastagent-sh/fastagent/telegram";
|
|
4
6
|
|
|
5
|
-
// Send a message or a local file
|
|
6
|
-
// reply itself
|
|
7
|
-
// (
|
|
8
|
-
// The chatId comes from the [telegram: chat …] context line in a chat turn; a scheduled
|
|
9
|
-
// such line, so the schedule's prompt must name the target chat id.
|
|
10
|
-
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
signal: AbortSignal.timeout(120_000),
|
|
41
|
-
});
|
|
42
|
-
raw = await res.text();
|
|
43
|
-
} catch (e) {
|
|
44
|
-
throw new Error(`telegram ${method}: ${String(e)}`, { cause: e });
|
|
45
|
-
}
|
|
46
|
-
let data: { ok?: boolean; description?: string };
|
|
47
|
-
try {
|
|
48
|
-
data = JSON.parse(raw) as { ok?: boolean; description?: string };
|
|
49
|
-
} catch {
|
|
50
|
-
data = {};
|
|
51
|
-
}
|
|
52
|
-
if (!res.ok || !data.ok) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
`telegram ${method} failed: ${res.status} ${data.description ?? "Bot API response was not the expected JSON"}`,
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export default defineTool({
|
|
60
|
-
description:
|
|
61
|
-
"Send to a Telegram chat: a text message (`text` — long text is split into multiple messages " +
|
|
62
|
-
"automatically), or a local file (`path` — a document, or a photo if it is an image). Exactly one " +
|
|
63
|
-
"of text/path. Use it for a turn NO channel is carrying — a scheduled or self-scheduled (wake) " +
|
|
64
|
-
"turn — or to reach a chat OTHER than the one you are answering. In a normal chat turn the channel " +
|
|
65
|
-
"already delivers your reply, so do NOT call this to answer (it would send it twice). chatId comes " +
|
|
66
|
-
"from the [telegram: chat …] context line in a chat turn; a scheduled/woken turn has no context " +
|
|
67
|
-
"line, so name the destination in your instruction.",
|
|
68
|
-
input: z.object({
|
|
69
|
-
chatId: z.union([z.string(), z.number()]).describe("target chat id"),
|
|
70
|
-
text: z.string().optional().describe("message text to send"),
|
|
71
|
-
path: z.string().optional().describe("absolute path of the local file to send"),
|
|
72
|
-
caption: z.string().optional().describe("file caption (file mode only)"),
|
|
73
|
-
asPhoto: z.boolean().optional().describe("send the file as a photo (inline) instead of a document"),
|
|
74
|
-
messageThreadId: z.number().optional().describe("thread to reply into (from the context line), if any"),
|
|
75
|
-
}),
|
|
76
|
-
// The bot token, declared: fastagent carries it to a deployed box and refuses to start while it is
|
|
77
|
-
// unset, so `execute` gets a value rather than checking for one.
|
|
78
|
-
secrets: ["TELEGRAM_BOT_TOKEN"],
|
|
79
|
-
async execute({ chatId, text, path, caption, asPhoto, messageThreadId }, ctx) {
|
|
80
|
-
const token = ctx.secrets.TELEGRAM_BOT_TOKEN;
|
|
81
|
-
if ((text === undefined) === (path === undefined)) {
|
|
82
|
-
throw new Error("pass exactly one of `text` (a message) or `path` (a file)");
|
|
83
|
-
}
|
|
84
|
-
const base = (): FormData => {
|
|
85
|
-
const form = new FormData();
|
|
86
|
-
form.set("chat_id", String(chatId));
|
|
87
|
-
if (messageThreadId !== undefined) form.set("message_thread_id", String(messageThreadId));
|
|
88
|
-
return form;
|
|
89
|
-
};
|
|
90
|
-
if (text !== undefined) {
|
|
91
|
-
// A file-only param alongside text would be silently dropped — same parameter-responsibility
|
|
92
|
-
// class as the XOR above, so it gets the same corrective error, not a silent ignore.
|
|
93
|
-
if (caption !== undefined || asPhoto !== undefined) {
|
|
94
|
-
throw new Error("`caption`/`asPhoto` are file-mode only — with `text`, put everything in the text");
|
|
7
|
+
// Send a message or a local file to a Telegram chat. In a CHAT turn the channel delivers the
|
|
8
|
+
// reply itself: this tool is for files, and for messages NO channel is carrying: a schedule's
|
|
9
|
+
// (src/schedules/) or a self-scheduled wake-up's, whose plain reply is not delivered anywhere.
|
|
10
|
+
// The chatId comes from the [telegram: chat …] context line in a chat turn; a scheduled message has
|
|
11
|
+
// no such line, so the schedule's prompt must name the target chat id.
|
|
12
|
+
//
|
|
13
|
+
// Delivery rides the channel's own transport (telegramTransport): its token, Telegram's length cap,
|
|
14
|
+
// its rate-limit handling. What it sends becomes part of that chat's discussion, so the chat's next
|
|
15
|
+
// turn knows the agent said it (Telegram never echoes a bot its own messages). With no channel
|
|
16
|
+
// mounted, it sends with TELEGRAM_BOT_TOKEN and says the message was not recorded.
|
|
17
|
+
export default needs(["TELEGRAM_BOT_TOKEN"], ({ TELEGRAM_BOT_TOKEN }) =>
|
|
18
|
+
defineTool({
|
|
19
|
+
name: "telegram_send",
|
|
20
|
+
description:
|
|
21
|
+
"Send to a Telegram chat: a text message (`text`: long text is split into multiple messages " +
|
|
22
|
+
"automatically), or a local file (`path`: a document, or a photo if it is an image). Exactly one " +
|
|
23
|
+
"of text/path. Use it for a message NO channel is carrying (a scheduled or self-scheduled one) " +
|
|
24
|
+
"or to reach a chat OTHER than the one you are answering. In a normal chat turn the channel " +
|
|
25
|
+
"already delivers your reply, so do NOT call this to answer (it would send it twice). chatId comes " +
|
|
26
|
+
"from the [telegram: chat …] context line in a chat turn; a scheduled or woken message has no context " +
|
|
27
|
+
"line, so name the destination in your instruction. What you send is shown to you in that chat's " +
|
|
28
|
+
"next turn.",
|
|
29
|
+
parameters: Type.Object({
|
|
30
|
+
chatId: Type.Union([Type.String(), Type.Number()], { description: "target chat id" }),
|
|
31
|
+
text: Type.Optional(Type.String({ description: "message text to send" })),
|
|
32
|
+
path: Type.Optional(Type.String({ description: "absolute path of the local file to send" })),
|
|
33
|
+
caption: Type.Optional(Type.String({ description: "file caption (file mode only)" })),
|
|
34
|
+
asPhoto: Type.Optional(Type.Boolean({ description: "send the file as a photo (inline) instead of a document" })),
|
|
35
|
+
messageThreadId: Type.Optional(
|
|
36
|
+
Type.Number({ description: "thread to reply into (from the context line), if any" }),
|
|
37
|
+
),
|
|
38
|
+
}),
|
|
39
|
+
execute: async ({ chatId, text, path, caption, asPhoto, messageThreadId }) => {
|
|
40
|
+
if ((text === undefined) === (path === undefined)) {
|
|
41
|
+
throw new Error("pass exactly one of `text` (a message) or `path` (a file)");
|
|
95
42
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
43
|
+
const telegram = telegramTransport(TELEGRAM_BOT_TOKEN);
|
|
44
|
+
const target = { chatId, threadId: messageThreadId };
|
|
45
|
+
let said: string;
|
|
46
|
+
if (text !== undefined) {
|
|
47
|
+
// A file-only param alongside text would be silently dropped: the same corrective error as above.
|
|
48
|
+
if (caption !== undefined || asPhoto !== undefined) {
|
|
49
|
+
throw new Error("`caption`/`asPhoto` are file-mode only: with `text`, put everything in the text");
|
|
50
|
+
}
|
|
51
|
+
const sent = await telegram.sendText(target, text);
|
|
52
|
+
const what =
|
|
53
|
+
sent.messageIds.length === 1
|
|
54
|
+
? `sent message to chat ${chatId}`
|
|
55
|
+
: `sent ${sent.messageIds.length} messages to chat ${chatId} (split at Telegram's length cap)`;
|
|
56
|
+
said = sent.notRecorded ? `${what}; not recorded: ${sent.notRecorded}` : what;
|
|
57
|
+
} else {
|
|
58
|
+
const sent = await telegram.sendFile(target, { path: path as string, caption, asPhoto });
|
|
59
|
+
const what = `sent ${basename(path as string)} to chat ${chatId}`;
|
|
60
|
+
said = sent.notRecorded ? `${what}; not recorded: ${sent.notRecorded}` : what;
|
|
101
61
|
}
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const form = base();
|
|
107
|
-
if (caption) form.set("caption", caption);
|
|
108
|
-
form.set(asPhoto ? "photo" : "document", new Blob([await readFile(path as string)]), basename(path as string));
|
|
109
|
-
await callBotApi(token, asPhoto ? "sendPhoto" : "sendDocument", form);
|
|
110
|
-
return `sent ${basename(path as string)} to chat ${chatId}`;
|
|
111
|
-
},
|
|
112
|
-
});
|
|
62
|
+
return { output: [{ type: "text", text: said }] };
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type BufferEntry } from "./context-buffer.ts";
|
|
2
|
+
/** Where a proactive message goes: a chat, optionally a forum topic. */
|
|
3
|
+
export interface TelegramSendTarget {
|
|
4
|
+
chatId: number | string;
|
|
5
|
+
threadId?: number;
|
|
6
|
+
}
|
|
7
|
+
/** What a send did. */
|
|
8
|
+
export interface TelegramSent {
|
|
9
|
+
/** One per message, in order (long text is split at Telegram's length cap). */
|
|
10
|
+
messageIds: number[];
|
|
11
|
+
/** Why the chat's next turn will NOT see this message, when it will not; undefined when it was recorded. */
|
|
12
|
+
notRecorded?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TelegramTransport {
|
|
15
|
+
/** Send plain text, split at Telegram's length cap. */
|
|
16
|
+
sendText(target: TelegramSendTarget, text: string): Promise<TelegramSent>;
|
|
17
|
+
/** Send a local file as a document, or inline as a photo. */
|
|
18
|
+
sendFile(target: TelegramSendTarget, file: {
|
|
19
|
+
path: string;
|
|
20
|
+
caption?: string;
|
|
21
|
+
asPhoto?: boolean;
|
|
22
|
+
}): Promise<TelegramSent>;
|
|
23
|
+
}
|
|
24
|
+
interface Mounted {
|
|
25
|
+
apiBaseUrl: string;
|
|
26
|
+
botToken: string;
|
|
27
|
+
/** The mounted channel's buffer write; absent when no channel is mounted in this process. */
|
|
28
|
+
record?: (placeKey: string, entry: BufferEntry) => void;
|
|
29
|
+
}
|
|
30
|
+
/** The channel's transport is the authoritative one for its bot; a re-mount replaces it. */
|
|
31
|
+
export declare function registerTelegramTransport(mounted: Required<Mounted>): void;
|
|
32
|
+
/** The transport of the bot `botToken` signs in as: its mounted channel's, or the token's own when none is mounted. */
|
|
33
|
+
export declare function telegramTransport(botToken: string): TelegramTransport;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE Telegram transport per bot a process holds: the mounted channel's, shared with the scaffolded send tool,
|
|
3
|
+
* found by the bot token both hold, and the reason it is shared. A message the agent sends itself (a schedule's digest, a post into another chat)
|
|
4
|
+
* never comes back as an update, so it is recorded into the target place's discussion here, where the channel's
|
|
5
|
+
* context buffer lives, and the chat's next turn knows it was said.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { basename } from "node:path";
|
|
9
|
+
import { log } from "../../log.js";
|
|
10
|
+
import { ownPostEntry } from "./context-buffer.js";
|
|
11
|
+
import { telegramPlaceKey } from "./parse.js";
|
|
12
|
+
import { callApi, chunkText } from "./telegram-api.js";
|
|
13
|
+
const byToken = new Map();
|
|
14
|
+
/** The channel's transport is the authoritative one for its bot; a re-mount replaces it. */
|
|
15
|
+
export function registerTelegramTransport(mounted) {
|
|
16
|
+
byToken.set(mounted.botToken, mounted);
|
|
17
|
+
}
|
|
18
|
+
const NOT_MOUNTED = "no telegram channel is mounted in this process, so the chat's next turn will not see it";
|
|
19
|
+
/**
|
|
20
|
+
* The place a sent message landed in, keyed by the same fields an update is keyed by (`chat.id`,
|
|
21
|
+
* `message_thread_id`): the platform's echo names the chat by number where the target named it `@channel`, and
|
|
22
|
+
* omits the thread id exactly where an update in that place would. The target stands in only for a reply that names
|
|
23
|
+
* no chat.
|
|
24
|
+
*/
|
|
25
|
+
function landedPlace(sent, target) {
|
|
26
|
+
return sent.chat?.id !== undefined
|
|
27
|
+
? telegramPlaceKey(sent.chat.id, sent.message_thread_id)
|
|
28
|
+
: telegramPlaceKey(target.chatId, target.threadId);
|
|
29
|
+
}
|
|
30
|
+
/** The transport of the bot `botToken` signs in as: its mounted channel's, or the token's own when none is mounted. */
|
|
31
|
+
export function telegramTransport(botToken) {
|
|
32
|
+
const { apiBaseUrl, record } = byToken.get(botToken) ?? { apiBaseUrl: "https://api.telegram.org" };
|
|
33
|
+
/** Record what was delivered. It is already in the chat, so a failure here is reported, never thrown: a thrown send
|
|
34
|
+
* would read as "not sent" and invite a duplicate. */
|
|
35
|
+
const remember = (placeKey, entry) => {
|
|
36
|
+
if (!record)
|
|
37
|
+
return NOT_MOUNTED;
|
|
38
|
+
try {
|
|
39
|
+
record(placeKey, entry);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
log.warn(`[telegram] a sent message was not recorded into place ${placeKey}: ${String(error)}`);
|
|
44
|
+
return `recording it failed: ${String(error)}`;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const params = (target) => ({
|
|
48
|
+
chat_id: target.chatId,
|
|
49
|
+
...(target.threadId !== undefined ? { message_thread_id: target.threadId } : {}),
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
async sendText(target, text) {
|
|
53
|
+
const messageIds = [];
|
|
54
|
+
let place = telegramPlaceKey(target.chatId, target.threadId);
|
|
55
|
+
for (const chunk of chunkText(text, { html: false })) {
|
|
56
|
+
const sent = await callApi(apiBaseUrl, botToken, "sendMessage", { ...params(target), text: chunk });
|
|
57
|
+
if (sent.message_id !== undefined)
|
|
58
|
+
messageIds.push(sent.message_id);
|
|
59
|
+
place = landedPlace(sent, target);
|
|
60
|
+
}
|
|
61
|
+
return { messageIds, notRecorded: remember(place, ownPostEntry(text, messageIds[0])) };
|
|
62
|
+
},
|
|
63
|
+
async sendFile(target, file) {
|
|
64
|
+
const form = new FormData();
|
|
65
|
+
for (const [key, value] of Object.entries(params(target)))
|
|
66
|
+
form.set(key, String(value));
|
|
67
|
+
if (file.caption)
|
|
68
|
+
form.set("caption", file.caption);
|
|
69
|
+
const name = basename(file.path);
|
|
70
|
+
form.set(file.asPhoto ? "photo" : "document", new Blob([await readFile(file.path)]), name);
|
|
71
|
+
const sent = await callApi(apiBaseUrl, botToken, file.asPhoto ? "sendPhoto" : "sendDocument", form);
|
|
72
|
+
const label = file.asPhoto ? "[photo]" : `[document: ${name}]`;
|
|
73
|
+
return {
|
|
74
|
+
messageIds: sent.message_id !== undefined ? [sent.message_id] : [],
|
|
75
|
+
notRecorded: remember(landedPlace(sent, target), ownPostEntry(file.caption ? `${label} ${file.caption}` : label, sent.message_id)),
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageRef } from "
|
|
1
|
+
import type { ImageAttachment as ImageRef } from "../kit/invoke-turn-kit.ts";
|
|
2
2
|
import type { CallOptions } from "../kit/transport.ts";
|
|
3
3
|
/** Telegram's hard text limit per message. */
|
|
4
4
|
export declare const TELEGRAM_MAX_TEXT = 4096;
|
|
@@ -15,13 +15,21 @@ export interface DownloadedFile {
|
|
|
15
15
|
name: string;
|
|
16
16
|
size: number;
|
|
17
17
|
}
|
|
18
|
+
/** The part of a sent Message this channel reads: where it landed. */
|
|
19
|
+
interface SentMessage {
|
|
20
|
+
message_id?: number;
|
|
21
|
+
chat?: {
|
|
22
|
+
id?: number;
|
|
23
|
+
};
|
|
24
|
+
message_thread_id?: number;
|
|
25
|
+
}
|
|
18
26
|
/** The methods this channel speaks and their result shapes — a hand-written slice of the Bot API
|
|
19
27
|
* schema. Adding a method = adding a row here, not writing a function. (If this table ever needs to
|
|
20
28
|
* grow past roughly ten rows, or entity-based formatting, adopt gramIO instead of growing it.) */
|
|
21
29
|
interface Api {
|
|
22
|
-
sendMessage:
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
sendMessage: SentMessage;
|
|
31
|
+
sendDocument: SentMessage;
|
|
32
|
+
sendPhoto: SentMessage;
|
|
25
33
|
editMessageText: unknown;
|
|
26
34
|
deleteMessage: unknown;
|
|
27
35
|
getMe: {
|
|
@@ -38,7 +46,9 @@ interface Api {
|
|
|
38
46
|
* The pipeline: one Bot API call, carrying every transport invariant (see the module header). Returns
|
|
39
47
|
* the method's `result` payload; throws {@link TelegramApiError} on any failure.
|
|
40
48
|
*/
|
|
41
|
-
export declare function callApi<M extends keyof Api>(api: string, botToken: string, method: M,
|
|
49
|
+
export declare function callApi<M extends keyof Api>(api: string, botToken: string, method: M,
|
|
50
|
+
/** JSON params, or a multipart form for an upload (a file's bytes; its own, longer timeout). */
|
|
51
|
+
params: Record<string, unknown> | FormData, opts?: CallOptions): Promise<Api[M]>;
|
|
42
52
|
/**
|
|
43
53
|
* Split text into ≤4096-char chunks (Telegram's limit), preferring a newline boundary. When `html`, it is
|
|
44
54
|
* tag-aware: a tag that would SPAN a boundary is CLOSED at the chunk's end and REOPENED (attributes and
|
|
@@ -24,7 +24,7 @@ export const TELEGRAM_MAX_TEXT = 4096;
|
|
|
24
24
|
const PARSE_ERROR = /can't parse|entit|unsupported|unclosed|tag/i;
|
|
25
25
|
/** Per-attempt timeout for a JSON Bot API call — small JSON round-trips, so 30s is generous. */
|
|
26
26
|
const API_TIMEOUT_MS = 30_000;
|
|
27
|
-
/** Timeout for
|
|
27
|
+
/** Timeout for moving file bytes, down or up (up to the 20 MB cap) — sized for a slow link, not a JSON call. */
|
|
28
28
|
const DOWNLOAD_TIMEOUT_MS = 120_000;
|
|
29
29
|
/** Longest flood wait (seconds) honoured PER ATTEMPT before failing visibly instead. Telegram's
|
|
30
30
|
* `retry_after` can reach minutes–hours on a flood ban; sleeping that long would silently park the
|
|
@@ -33,7 +33,7 @@ const FLOOD_WAIT_MAX_S = 30;
|
|
|
33
33
|
/** How many 429s one call absorbs before giving up. */
|
|
34
34
|
const RETRIES = 3;
|
|
35
35
|
/** Download sanity cap (Telegram's own getFile limit); a larger file/image is rejected visibly. The
|
|
36
|
-
*
|
|
36
|
+
* harness resizes images to the model's needs, so this is a transport guard, not the model size limit. */
|
|
37
37
|
const MAX_DOWNLOAD_BYTES = 20 * 1024 * 1024;
|
|
38
38
|
/** A named Bot API failure. `status` 0 = the transport itself failed (network error / timeout) before
|
|
39
39
|
* any HTTP status existed; otherwise the HTTP status with Telegram's own description (or a note that
|
|
@@ -63,17 +63,21 @@ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
63
63
|
* The pipeline: one Bot API call, carrying every transport invariant (see the module header). Returns
|
|
64
64
|
* the method's `result` payload; throws {@link TelegramApiError} on any failure.
|
|
65
65
|
*/
|
|
66
|
-
export async function callApi(api, botToken, method,
|
|
66
|
+
export async function callApi(api, botToken, method,
|
|
67
|
+
/** JSON params, or a multipart form for an upload (a file's bytes; its own, longer timeout). */
|
|
68
|
+
params, opts = {}) {
|
|
67
69
|
const retries = opts.retries ?? RETRIES;
|
|
70
|
+
const upload = params instanceof FormData;
|
|
68
71
|
for (let attempt = 0;; attempt++) {
|
|
69
72
|
let res;
|
|
70
73
|
let raw;
|
|
71
74
|
try {
|
|
72
75
|
res = await fetch(`${api}/bot${botToken}/${method}`, {
|
|
73
76
|
method: "POST",
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
...(upload
|
|
78
|
+
? { body: params }
|
|
79
|
+
: { headers: { "content-type": "application/json" }, body: JSON.stringify(params) }),
|
|
80
|
+
signal: AbortSignal.timeout(upload ? DOWNLOAD_TIMEOUT_MS : API_TIMEOUT_MS),
|
|
77
81
|
});
|
|
78
82
|
raw = await res.text(); // the body read shares the timeout — a mid-body stall is a transport failure too
|
|
79
83
|
}
|