@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,7 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE Slack transport per bot a process holds: the mounted channel's, shared with the scaffolded send tool, found
|
|
3
|
+
* by the bot token both hold.
|
|
4
|
+
*/
|
|
5
|
+
import { roomThreads } from "../kit/room-threads.ts";
|
|
1
6
|
import { type SlackApi } from "./slack-api.ts";
|
|
2
7
|
/** What a proactive sender needs: Markdown delivery and file upload. */
|
|
3
8
|
export type SlackTransport = Pick<SlackApi, "sendMarkdown" | "uploadFile">;
|
|
4
|
-
/** A re-mount replaces the entry: the channel's transport is the authoritative one for its
|
|
5
|
-
export declare function registerSlackApi(
|
|
6
|
-
/** The transport of the
|
|
7
|
-
export declare function slackTransport(
|
|
9
|
+
/** A re-mount replaces the entry: the channel's transport is the authoritative one for its bot. */
|
|
10
|
+
export declare function registerSlackApi(botToken: string, api: SlackApi): void;
|
|
11
|
+
/** The transport of the bot `botToken` signs in as: its mounted channel's, or the token's own when none is mounted. */
|
|
12
|
+
export declare function slackTransport(botToken: string): SlackTransport;
|
|
13
|
+
/**
|
|
14
|
+
* The threads of the channel the message running in `sessionId` was asked in, and only those (`kit/room-threads.ts`):
|
|
15
|
+
* `list()` its recent threads, `read(threadTs)` one of them. Throws outside a Slack channel turn served by this
|
|
16
|
+
* process.
|
|
17
|
+
*/
|
|
18
|
+
export declare function slackThreads(sessionId: string | undefined): ReturnType<typeof roomThreads>;
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The ONE Slack transport per
|
|
2
|
+
* The ONE Slack transport per bot a process holds: the mounted channel's, shared with the scaffolded send tool, found
|
|
3
|
+
* by the bot token both hold.
|
|
3
4
|
*/
|
|
4
|
-
import {
|
|
5
|
+
import { roomThreads } from "../kit/room-threads.js";
|
|
5
6
|
import { createSlackApi } from "./slack-api.js";
|
|
6
|
-
const
|
|
7
|
-
/** A re-mount replaces the entry: the channel's transport is the authoritative one for its
|
|
8
|
-
export function registerSlackApi(
|
|
9
|
-
|
|
7
|
+
const byToken = new Map();
|
|
8
|
+
/** A re-mount replaces the entry: the channel's transport is the authoritative one for its bot. */
|
|
9
|
+
export function registerSlackApi(botToken, api) {
|
|
10
|
+
byToken.set(botToken, api);
|
|
10
11
|
}
|
|
11
|
-
/** The transport of the
|
|
12
|
-
export function slackTransport(
|
|
13
|
-
|
|
14
|
-
let api = byStateRoot.get(stateRoot);
|
|
12
|
+
/** The transport of the bot `botToken` signs in as: its mounted channel's, or the token's own when none is mounted. */
|
|
13
|
+
export function slackTransport(botToken) {
|
|
14
|
+
let api = byToken.get(botToken);
|
|
15
15
|
if (!api) {
|
|
16
|
-
const botToken = process.env.SLACK_BOT_TOKEN;
|
|
17
|
-
if (!botToken)
|
|
18
|
-
throw new Error("SLACK_BOT_TOKEN is not set and no Slack channel is mounted");
|
|
19
16
|
api = createSlackApi({ botToken });
|
|
20
|
-
|
|
17
|
+
byToken.set(botToken, api);
|
|
21
18
|
}
|
|
22
19
|
return api;
|
|
23
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* The threads of the channel the message running in `sessionId` was asked in, and only those (`kit/room-threads.ts`):
|
|
23
|
+
* `list()` its recent threads, `read(threadTs)` one of them. Throws outside a Slack channel turn served by this
|
|
24
|
+
* process.
|
|
25
|
+
*/
|
|
26
|
+
export function slackThreads(sessionId) {
|
|
27
|
+
return roomThreads("slack", sessionId);
|
|
28
|
+
}
|
|
@@ -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
|
import type { SlackFile } from "./model.ts";
|
|
4
4
|
export interface SlackTarget {
|
|
@@ -38,7 +38,65 @@ export interface SlackApiOptions {
|
|
|
38
38
|
botToken: string;
|
|
39
39
|
baseUrl?: string;
|
|
40
40
|
}
|
|
41
|
+
/** A message as `conversations.history` / `conversations.replies` list it: the fields a place's history reads. */
|
|
42
|
+
export interface SlackListedMessage {
|
|
43
|
+
type?: string;
|
|
44
|
+
subtype?: string;
|
|
45
|
+
hidden?: boolean;
|
|
46
|
+
ts?: string;
|
|
47
|
+
thread_ts?: string;
|
|
48
|
+
/** On a thread's root, in a channel listing. */
|
|
49
|
+
reply_count?: number;
|
|
50
|
+
latest_reply?: string;
|
|
51
|
+
user?: string;
|
|
52
|
+
bot_id?: string;
|
|
53
|
+
username?: string;
|
|
54
|
+
bot_profile?: {
|
|
55
|
+
name?: string;
|
|
56
|
+
};
|
|
57
|
+
text?: string;
|
|
58
|
+
blocks?: unknown[];
|
|
59
|
+
/** Legacy attachments: where many integrations (CI, alerts) put all they say, with an empty `text`. */
|
|
60
|
+
attachments?: {
|
|
61
|
+
fallback?: string;
|
|
62
|
+
pretext?: string;
|
|
63
|
+
title?: string;
|
|
64
|
+
text?: string;
|
|
65
|
+
}[];
|
|
66
|
+
files?: SlackFile[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The newest `limit` messages before `latest` (exclusive). There is deliberately no `oldest`: with it, both reads
|
|
70
|
+
* return the OLDEST `limit` after it instead, and `conversations.replies` then pages on to nothing (measured), so a
|
|
71
|
+
* reader cuts at its own cursor.
|
|
72
|
+
*/
|
|
73
|
+
interface SlackRange {
|
|
74
|
+
latest?: string;
|
|
75
|
+
limit: number;
|
|
76
|
+
}
|
|
77
|
+
/** Where a posted message landed: a channel's top level, or a thread in it. */
|
|
78
|
+
type OnSent = (where: {
|
|
79
|
+
channelId: string;
|
|
80
|
+
threadTs?: string;
|
|
81
|
+
}, ts: string) => void;
|
|
41
82
|
export interface SlackApi {
|
|
83
|
+
/**
|
|
84
|
+
* The same transport, reporting every message it posts (`chat.postMessage`, every chunk of a long body, and
|
|
85
|
+
* `chat.startStream`) with where it landed. Edits, deletions and file uploads are not reported.
|
|
86
|
+
*/
|
|
87
|
+
recordingSends(onSent: OnSent): SlackApi;
|
|
88
|
+
/** A channel's top-level messages in `range`, NEWEST first (`conversations.history`). */
|
|
89
|
+
channelHistory(channelId: string, range: SlackRange, opts?: CallOptions): Promise<{
|
|
90
|
+
messages: SlackListedMessage[];
|
|
91
|
+
hasMore: boolean;
|
|
92
|
+
}>;
|
|
93
|
+
/** A thread's replies in `range`, OLDEST first, after its root, which Slack lists whatever the range. */
|
|
94
|
+
threadReplies(channelId: string, threadTs: string, range: SlackRange, opts?: CallOptions): Promise<{
|
|
95
|
+
messages: SlackListedMessage[];
|
|
96
|
+
hasMore: boolean;
|
|
97
|
+
}>;
|
|
98
|
+
/** A person's name as the workspace shows it (`users.info`, needs `users:read`); undefined when it has none. */
|
|
99
|
+
userName(userId: string, opts?: CallOptions): Promise<string | undefined>;
|
|
42
100
|
authTest(): Promise<{
|
|
43
101
|
teamId?: string;
|
|
44
102
|
userId?: string;
|
|
@@ -73,4 +131,5 @@ export interface SlackApi {
|
|
|
73
131
|
export declare function chunkSlackText(text: string, maxPoints?: number): string[];
|
|
74
132
|
/** Split standard Markdown while balancing fenced code blocks across separately posted messages. */
|
|
75
133
|
export declare function chunkSlackMarkdown(markdown: string, maxPoints?: number): string[];
|
|
76
|
-
export declare function createSlackApi(
|
|
134
|
+
export declare function createSlackApi(options: SlackApiOptions): SlackApi;
|
|
135
|
+
export {};
|
|
@@ -143,8 +143,12 @@ function assertLongLivedBotToken(botToken) {
|
|
|
143
143
|
'create a new app (docs/slack.md → "If token rotation is on")');
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
export function createSlackApi(
|
|
147
|
-
assertLongLivedBotToken(botToken);
|
|
146
|
+
export function createSlackApi(options) {
|
|
147
|
+
assertLongLivedBotToken(options.botToken);
|
|
148
|
+
return slackClient(options, undefined);
|
|
149
|
+
}
|
|
150
|
+
/** A transport holds no state between calls, so a recording one is simply another instance. */
|
|
151
|
+
function slackClient({ botToken, baseUrl = "https://slack.com/api" }, onSent) {
|
|
148
152
|
const apiBase = baseUrl.replace(/\/$/, "");
|
|
149
153
|
const call = async (method, body, httpMethod = "POST", opts = {}) => {
|
|
150
154
|
const retries = opts.retries ?? RETRIES;
|
|
@@ -220,6 +224,10 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
|
|
|
220
224
|
}
|
|
221
225
|
return { bytes: await readBytesCapped(response), contentType: response.headers.get("content-type") ?? undefined };
|
|
222
226
|
};
|
|
227
|
+
const sent = (channelId, threadTs, ts) => {
|
|
228
|
+
onSent?.({ channelId, ...(threadTs ? { threadTs } : {}) }, ts);
|
|
229
|
+
return ts;
|
|
230
|
+
};
|
|
223
231
|
const postMarkdown = async (target, markdown) => {
|
|
224
232
|
const data = await call("chat.postMessage", {
|
|
225
233
|
channel: target.channelId,
|
|
@@ -231,9 +239,24 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
|
|
|
231
239
|
if (!data.ts)
|
|
232
240
|
throw new SlackApiError("chat.postMessage", 200, "response carried no ts");
|
|
233
241
|
// The response's `channel` is what resolves a user-id target to its DM; a channel target echoes itself.
|
|
234
|
-
|
|
242
|
+
const channelId = data.channel ?? target.channelId;
|
|
243
|
+
return { ts: sent(channelId, target.threadTs, data.ts), channelId };
|
|
235
244
|
};
|
|
236
245
|
const api = {
|
|
246
|
+
recordingSends: (record) => slackClient({ botToken, baseUrl }, record),
|
|
247
|
+
async channelHistory(channelId, range, opts) {
|
|
248
|
+
const data = await call("conversations.history", { channel: channelId, ...range }, "GET", opts);
|
|
249
|
+
return { messages: data.messages ?? [], hasMore: data.has_more === true };
|
|
250
|
+
},
|
|
251
|
+
async threadReplies(channelId, threadTs, range, opts) {
|
|
252
|
+
const data = await call("conversations.replies", { channel: channelId, ts: threadTs, ...range }, "GET", opts);
|
|
253
|
+
return { messages: data.messages ?? [], hasMore: data.has_more === true };
|
|
254
|
+
},
|
|
255
|
+
async userName(userId, opts) {
|
|
256
|
+
const data = await call("users.info", { user: userId }, "GET", opts);
|
|
257
|
+
const user = data.user;
|
|
258
|
+
return user?.profile?.display_name || user?.profile?.real_name || user?.real_name || user?.name || undefined;
|
|
259
|
+
},
|
|
237
260
|
async authTest() {
|
|
238
261
|
const data = await call("auth.test", {});
|
|
239
262
|
return { teamId: data.team_id, userId: data.user_id, botId: data.bot_id };
|
|
@@ -248,7 +271,7 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
|
|
|
248
271
|
});
|
|
249
272
|
if (!data.ts)
|
|
250
273
|
throw new SlackApiError("chat.postMessage", 200, "response carried no ts");
|
|
251
|
-
return data.ts;
|
|
274
|
+
return sent(data.channel ?? target.channelId, target.threadTs, data.ts);
|
|
252
275
|
},
|
|
253
276
|
async postMarkdown(target, markdown) {
|
|
254
277
|
return (await postMarkdown(target, markdown)).ts;
|
|
@@ -305,7 +328,7 @@ export function createSlackApi({ botToken, baseUrl = "https://slack.com/api" })
|
|
|
305
328
|
});
|
|
306
329
|
if (!data.ts)
|
|
307
330
|
throw new SlackApiError("chat.startStream", 200, "response carried no ts");
|
|
308
|
-
return data.ts;
|
|
331
|
+
return sent(target.channelId, target.threadTs, data.ts);
|
|
309
332
|
},
|
|
310
333
|
async appendStream(channelId, ts, markdown) {
|
|
311
334
|
await call("chat.appendStream", { channel: channelId, ts, markdown_text: markdown });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** First-party Slack HTTP Events API channel: signed ingress, durable turns/context, files, and edited previews. */
|
|
2
2
|
import { createHmac } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { join } from "node:path";
|
|
4
4
|
import { log } from "../../log.js";
|
|
5
5
|
import { readBodyCapped } from "../body.js";
|
|
6
6
|
import { text } from "../respond.js";
|
|
@@ -13,15 +13,15 @@ import * as Exit from "effect/Exit";
|
|
|
13
13
|
import { portJoin } from "../../effect-port.js";
|
|
14
14
|
import { createTaskTracker } from "../kit/tasks.js";
|
|
15
15
|
import { attachmentsDir } from "../kit/attachment-path.js";
|
|
16
|
-
import { ensureStateHome } from "../kit/state.js";
|
|
17
16
|
import { dispatchStop, isStopText } from "../kit/stop-command.js";
|
|
18
17
|
import { codePointPrefix } from "../kit/text.js";
|
|
18
|
+
import { createRoomThreads, registerRoomThreads } from "../kit/room-threads.js";
|
|
19
19
|
import { createTurnRunner } from "../kit/turn-runner.js";
|
|
20
20
|
import { createTurnStore } from "../kit/turn-store.js";
|
|
21
21
|
import { discussionBlock } from "../kit/context-buffer.js";
|
|
22
|
-
import {
|
|
22
|
+
import { collectFoldedFiles, createSlackPlaceHistory, slackHistoryKey, slackPlaceOf, } from "./history.js";
|
|
23
23
|
import { slackTurnStream } from "./invoke-turn.js";
|
|
24
|
-
import { defaultSlackRoute, isSlackDirectMessage, isSlackGroupMessage, hasSlackMention,
|
|
24
|
+
import { defaultSlackRoute, isSlackDirectMessage, isSlackGroupMessage, hasSlackMention, isSlackHumanMessage, mentionsSlackUser, stripSlackMentions, slackEnvelope, slackFileIds, slackMessageText, slackTeamId, } from "./parse.js";
|
|
25
25
|
import { defaultErrorMessage, deliverSlackAnswer, settleSlackPreview, slackReply, } from "./preview.js";
|
|
26
26
|
import { completeSlackReaction, resolveReactionEmojis, startSlackReaction } from "./reaction.js";
|
|
27
27
|
import { registerSlackApi } from "./shared-api.js";
|
|
@@ -40,9 +40,10 @@ function isStoredSlackTurn(value) {
|
|
|
40
40
|
const turn = value;
|
|
41
41
|
return (typeof turn?.id === "string" &&
|
|
42
42
|
typeof turn.seq === "number" &&
|
|
43
|
-
typeof turn.
|
|
43
|
+
typeof turn.conversation === "string" &&
|
|
44
44
|
typeof turn.baseText === "string" &&
|
|
45
|
-
typeof turn.
|
|
45
|
+
(turn.historyKey === undefined || typeof turn.historyKey === "string") &&
|
|
46
|
+
typeof turn.group === "boolean" &&
|
|
46
47
|
typeof turn.teamId === "string" &&
|
|
47
48
|
typeof turn.channelId === "string" &&
|
|
48
49
|
(turn.threadTs === undefined || typeof turn.threadTs === "string") &&
|
|
@@ -74,10 +75,12 @@ const AUTH_ACK_BUDGET_MS = 2_000;
|
|
|
74
75
|
function createSlackAuthentication(api, label) {
|
|
75
76
|
let teamId;
|
|
76
77
|
let botUserId;
|
|
78
|
+
let botId;
|
|
77
79
|
let state = "pending";
|
|
78
80
|
const settled = api.authTest().then((identity) => {
|
|
79
81
|
teamId = identity.teamId;
|
|
80
82
|
botUserId = identity.userId;
|
|
83
|
+
botId = identity.botId;
|
|
81
84
|
state = "ready";
|
|
82
85
|
log.info(`${label} authenticated${identity.teamId ? ` for workspace ${identity.teamId}` : ""}`);
|
|
83
86
|
}, (error) => {
|
|
@@ -91,6 +94,7 @@ function createSlackAuthentication(api, label) {
|
|
|
91
94
|
settled,
|
|
92
95
|
teamId: () => teamId,
|
|
93
96
|
botUserId: () => botUserId,
|
|
97
|
+
botId: () => botId,
|
|
94
98
|
ready: () => state !== "pending"
|
|
95
99
|
? settled
|
|
96
100
|
: new Promise((resolve, reject) => {
|
|
@@ -114,37 +118,74 @@ export function slackChannel(options) {
|
|
|
114
118
|
throw new Error("slackChannel welcome must be a string or false");
|
|
115
119
|
}
|
|
116
120
|
const reactionEmojis = resolveReactionEmojis(reactionAck);
|
|
117
|
-
return ({ agent,
|
|
121
|
+
return ({ agent, sessionFor, recordedSession, stateDir: stateHome }) => {
|
|
118
122
|
if (!botToken)
|
|
119
123
|
throw new Error("slackChannel requires a non-empty botToken (Bot User OAuth Token)");
|
|
120
124
|
if (!signingSecret)
|
|
121
125
|
throw new Error("slackChannel requires a non-empty signingSecret (Basic Information → App Credentials)");
|
|
122
|
-
if (!isAbsolute(stateRoot))
|
|
123
|
-
throw new Error(`slackChannel requires an absolute ctx.stateRoot, got "${stateRoot}"`);
|
|
124
126
|
const label = "[slack]";
|
|
125
127
|
const formatError = onError ?? defaultErrorMessage;
|
|
126
|
-
const stateHome = join(stateRoot, "channels", "slack");
|
|
127
|
-
ensureStateHome(stateHome);
|
|
128
128
|
const api = createSlackApi({ botToken, baseUrl: apiBaseUrl });
|
|
129
|
-
registerSlackApi(
|
|
129
|
+
registerSlackApi(botToken, api); // the send tool delivers through this one (shared-api.ts)
|
|
130
130
|
const auth = createSlackAuthentication(api, label);
|
|
131
131
|
// Side tasks (stop feedback, DM welcomes) run off the ACK path but drain in turnsIdle.
|
|
132
132
|
const sideTasks = createTaskTracker(label);
|
|
133
133
|
const seen = createSeenRing(join(stateHome, "seen.json"), label);
|
|
134
134
|
const threadParticipants = createThreadParticipants(join(stateHome, "thread-participants.json"), label);
|
|
135
135
|
/**
|
|
136
|
-
* A thread's participation is keyed by the
|
|
137
|
-
* memory, so the two must not be re-keyable independently.
|
|
136
|
+
* A thread's participation is keyed by the CONVERSATION it describes — "the agent answered here" is a claim about
|
|
137
|
+
* a memory, so the two must not be re-keyable independently.
|
|
138
138
|
*/
|
|
139
|
-
const threadKey = (teamId, channelId, threadTs) =>
|
|
139
|
+
const threadKey = (teamId, channelId, threadTs) => `${teamId}:${channelId}:${threadTs}`;
|
|
140
140
|
const welcomed = createWelcomedUsers(join(stateHome, "welcomed.json"), label);
|
|
141
|
-
|
|
141
|
+
// Every message this channel took as input (a turn, a /stop) is in `seen`. A history read also leaves these out,
|
|
142
|
+
// to shape the prompt only — what keeps a queued ask out of an earlier turn is the read's bound, not this ring.
|
|
143
|
+
const history = createSlackPlaceHistory({
|
|
144
|
+
api,
|
|
145
|
+
self: () => ({ userId: auth.botUserId(), botId: auth.botId() }),
|
|
146
|
+
label,
|
|
147
|
+
path: join(stateHome, "history.json"),
|
|
148
|
+
isTurnInput: (key, ts) => {
|
|
149
|
+
const place = slackPlaceOf(key);
|
|
150
|
+
return seen.has(`${place.teamId}:${place.channelId}:${ts}`);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
// A group turn's room, for the thread-reading tool (`slackThreads`): the channel it was asked in, and its threads.
|
|
154
|
+
const rooms = createRoomThreads({
|
|
155
|
+
list: (room) => history.threads(room),
|
|
156
|
+
read: async (room, threadTs) => {
|
|
157
|
+
if (!/^\d+\.\d+$/.test(threadTs)) {
|
|
158
|
+
throw new Error(`"${threadTs}" is not a Slack thread id: pass a thread's id from the list (like 1712345678.123456)`);
|
|
159
|
+
}
|
|
160
|
+
const { teamId, channelId } = slackPlaceOf(room);
|
|
161
|
+
const key = slackHistoryKey(teamId, { channelId, threadTs });
|
|
162
|
+
return (await history.snapshot(key)).text || `(thread ${threadTs} shows no messages)`;
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
registerRoomThreads("slack", stateHome, rooms);
|
|
166
|
+
/**
|
|
167
|
+
* The client a turn posts through: what it posts (answer, queue notice, stop feedback) is in the session already,
|
|
168
|
+
* so the place it LANDS in records it, and that place's next read leaves it out — an answer to a channel's
|
|
169
|
+
* top-level ask lands in the thread it opens. The send tool uses the plain client, so what the agent posts itself
|
|
170
|
+
* stays discussion. A routed turn has no place and reads no history.
|
|
171
|
+
*/
|
|
172
|
+
const placeApi = (turn) => turn.historyKey === undefined
|
|
173
|
+
? api
|
|
174
|
+
: api.recordingSends((where, ts) => history.recordOutput(slackHistoryKey(turn.teamId, where), ts));
|
|
142
175
|
const store = createTurnStore(join(stateHome, "turns.json"), {
|
|
143
176
|
label,
|
|
144
177
|
isRecord: isStoredSlackTurn,
|
|
145
178
|
order: (a, b) => a.seq - b.seq,
|
|
146
179
|
});
|
|
147
180
|
const decide = route ?? defaultSlackRoute;
|
|
181
|
+
/** A channel turn under the default routing reads its channel's threads; a DM has none to read. */
|
|
182
|
+
const enterRoom = (turn) => {
|
|
183
|
+
if (!turn.group || turn.historyKey === undefined)
|
|
184
|
+
return {};
|
|
185
|
+
const { teamId, channelId } = slackPlaceOf(turn.historyKey);
|
|
186
|
+
const room = slackHistoryKey(teamId, { channelId });
|
|
187
|
+
return { enter: (sessionId) => rooms.enter(sessionId, room) };
|
|
188
|
+
};
|
|
148
189
|
const targetOf = (turn) => ({
|
|
149
190
|
channelId: turn.channelId,
|
|
150
191
|
threadTs: turn.threadTs,
|
|
@@ -155,16 +196,20 @@ export function slackChannel(options) {
|
|
|
155
196
|
const target = targetOf(turn);
|
|
156
197
|
if (turn.nativeQueueStatus)
|
|
157
198
|
void api.setThreadStatus(target, "").catch(() => { });
|
|
158
|
-
void settleSlackPreview(
|
|
199
|
+
void settleSlackPreview(placeApi(turn), target, turn.previewTs, "⚠️ I couldn’t complete an earlier request — please ask again.").catch((error) => log.warn(`${label} could not notify a dropped turn: ${String(error)}`));
|
|
159
200
|
};
|
|
160
201
|
const runner = createTurnRunner({
|
|
161
202
|
label,
|
|
162
203
|
store,
|
|
163
|
-
|
|
204
|
+
discussion: history,
|
|
164
205
|
seen,
|
|
165
206
|
toStored: ({ previewTs: _preview, nativeQueueStatus: _status, ...intent }) => ({ ...intent, attempts: 0 }),
|
|
166
207
|
fromStored: ({ attempts: _attempts, ...intent }) => ({ ...intent }),
|
|
167
|
-
|
|
208
|
+
// The read ends at the ask (`messageRefOf`: the ts of the message that asked).
|
|
209
|
+
discussionKey: (turn) => {
|
|
210
|
+
const ask = messageRefOf(turn.id);
|
|
211
|
+
return turn.historyKey === undefined || ask === undefined ? undefined : { key: turn.historyKey, until: ask.ts };
|
|
212
|
+
},
|
|
168
213
|
where: (turn) => `channel=${turn.channelId}`,
|
|
169
214
|
onQueuedBehind(turn) {
|
|
170
215
|
const nativeDmStatus = rendering === "native" && turn.threadTs && turn.channelId.startsWith("D");
|
|
@@ -174,7 +219,9 @@ export function slackChannel(options) {
|
|
|
174
219
|
done: auth.settled
|
|
175
220
|
.then(() => nativeDmStatus
|
|
176
221
|
? api.setThreadStatus(targetOf(turn), "is queued behind an earlier request…")
|
|
177
|
-
:
|
|
222
|
+
: placeApi(turn)
|
|
223
|
+
.postMessage(targetOf(turn), QUEUED_PLACEHOLDER)
|
|
224
|
+
.then((ts) => {
|
|
178
225
|
turn.previewTs = ts;
|
|
179
226
|
}))
|
|
180
227
|
.catch((error) => log.warn(`${label} queue preview failed (the turn stays durable): ${String(error)}`)),
|
|
@@ -198,7 +245,7 @@ export function slackChannel(options) {
|
|
|
198
245
|
.catch((error) => log.warn(`${label} could not update a deferred Agent status: ${String(error)}`));
|
|
199
246
|
}
|
|
200
247
|
else if (turn.previewTs) {
|
|
201
|
-
void settleSlackPreview(
|
|
248
|
+
void settleSlackPreview(placeApi(turn), targetOf(turn), turn.previewTs, DEFERRED_PLACEHOLDER).catch((error) => log.warn(`${label} could not update a deferred queue preview: ${String(error)}`));
|
|
202
249
|
}
|
|
203
250
|
},
|
|
204
251
|
notifyDropped,
|
|
@@ -210,7 +257,7 @@ export function slackChannel(options) {
|
|
|
210
257
|
.setThreadStatus(targetOf(turn), "")
|
|
211
258
|
.catch((error) => log.warn(`${label} could not clear a queued Agent status: ${String(error)}`));
|
|
212
259
|
}
|
|
213
|
-
await deliverSlackAnswer(
|
|
260
|
+
await deliverSlackAnswer(placeApi(turn), targetOf(turn), answer, turn.previewTs);
|
|
214
261
|
// The 👀 on the asker's message was added by the run that produced this answer, and `execute`'s
|
|
215
262
|
// acquire/release — the only thing that turns it into ✅ — is not on this path.
|
|
216
263
|
const messageRef = messageRefOf(turn.id);
|
|
@@ -234,10 +281,15 @@ export function slackChannel(options) {
|
|
|
234
281
|
emojis: reactionEmojis,
|
|
235
282
|
label,
|
|
236
283
|
})
|
|
237
|
-
: undefined), () => Effect.scoped(slackReply(slackTurnStream(agent,
|
|
284
|
+
: undefined), () => Effect.scoped(slackReply(slackTurnStream(agent, {
|
|
285
|
+
session: () => sessionFor(turn.conversation),
|
|
286
|
+
...enterRoom(turn),
|
|
287
|
+
key: turn.id,
|
|
288
|
+
text: `${discussionBlock(discussion.text)}${turn.baseText}`,
|
|
289
|
+
}, { api, channelId: turn.channelId, filesDir: attachmentsDir(stateHome), label }, {
|
|
238
290
|
primaryFileIds: turn.fileIds,
|
|
239
|
-
buffered:
|
|
240
|
-
}),
|
|
291
|
+
buffered: collectFoldedFiles(discussion.consumed.flatMap((read) => read.folded), new Set(turn.fileIds)),
|
|
292
|
+
}), placeApi(turn), targetOf(turn), formatError, {
|
|
241
293
|
rendering,
|
|
242
294
|
initialPreviewTs: turn.previewTs,
|
|
243
295
|
threadTitle: turn.threadTitle,
|
|
@@ -249,7 +301,10 @@ export function slackChannel(options) {
|
|
|
249
301
|
await reaction?.complete();
|
|
250
302
|
else
|
|
251
303
|
await reaction?.remove();
|
|
252
|
-
}).pipe(
|
|
304
|
+
}).pipe(
|
|
305
|
+
// A release does not fail: its defect would make the turn's typed failure one too. Unreachable while
|
|
306
|
+
// the reaction's `complete` and `remove` catch their own failures (reaction.ts).
|
|
307
|
+
Effect.catchTag("PortFailure", (error) => Effect.sync(() => log.warn(`${label} could not settle the Slack reaction: ${String(error.cause)}`)))));
|
|
253
308
|
},
|
|
254
309
|
});
|
|
255
310
|
let seq = runner.recover().reduce((maximum, turn) => Math.max(maximum, turn.seq), 0);
|
|
@@ -275,16 +330,13 @@ export function slackChannel(options) {
|
|
|
275
330
|
const group = isSlackGroupMessage(event);
|
|
276
331
|
const direct = isSlackDirectMessage(event);
|
|
277
332
|
const rootTs = event.thread_ts ?? event.ts;
|
|
278
|
-
const bufferKey = slackPlaceKey(teamId, event);
|
|
279
333
|
// Listening is not speaking: every message the channel can see refines who takes part in its thread, whether or
|
|
280
334
|
// not it is answered.
|
|
281
335
|
if (group && event.thread_ts !== undefined) {
|
|
282
336
|
threadParticipants.merge(threadKey(teamId, event.channel, event.thread_ts), { humans: [event.user] });
|
|
283
337
|
}
|
|
284
338
|
let routed = decide(envelope);
|
|
285
|
-
// Two different questions (see parse.ts).
|
|
286
339
|
const addressesSomeone = hasSlackMention(event.text ?? "");
|
|
287
|
-
const mightBeTheBot = hasSlackUserMention(event.text ?? "");
|
|
288
340
|
const structurallyMentionsBot = botUserId !== undefined && mentionsSlackUser(event.text ?? "", botUserId);
|
|
289
341
|
// app_mention and message.* subscriptions can overlap.
|
|
290
342
|
if (!routed && route === undefined && group && event.type === "message" && structurallyMentionsBot)
|
|
@@ -303,23 +355,8 @@ export function slackChannel(options) {
|
|
|
303
355
|
routed = {};
|
|
304
356
|
}
|
|
305
357
|
if (!routed) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (route === undefined && group) {
|
|
309
|
-
const body = slackBufferText(slackMessageText(event));
|
|
310
|
-
if (body) {
|
|
311
|
-
const fileIds = slackFileIds(event);
|
|
312
|
-
buffer.push(bufferKey, {
|
|
313
|
-
sender: slackSenderLabel(event),
|
|
314
|
-
body,
|
|
315
|
-
messageId: event.ts,
|
|
316
|
-
replyTo: event.thread_ts,
|
|
317
|
-
fileIds: fileIds.length ? fileIds : undefined,
|
|
318
|
-
});
|
|
319
|
-
seen.add(logicalId);
|
|
320
|
-
log.debug(`${label} buffered unsummoned group message ${logicalId} (place ${bufferKey})`);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
358
|
+
// Not lost: the turn that is summoned here reads it from the place's history.
|
|
359
|
+
log.debug(`${label} not summoned — message ${logicalId}`);
|
|
323
360
|
return;
|
|
324
361
|
}
|
|
325
362
|
const targetChannel = routed.channelId ?? event.channel;
|
|
@@ -327,13 +364,20 @@ export function slackChannel(options) {
|
|
|
327
364
|
// Answer where asked (participant model §4).
|
|
328
365
|
const defaultThread = event.thread_ts ?? event.ts;
|
|
329
366
|
const threadTs = routed.threadTs === null ? undefined : (routed.threadTs ?? (sameChannel ? defaultThread : undefined));
|
|
330
|
-
const
|
|
367
|
+
const defaultConversation = threadKey(teamId, event.channel, rootTs);
|
|
368
|
+
// Every place's discussion under the default routing, a DM's too (what a send tool posted there is in no session);
|
|
369
|
+
// a routed session's place is the router's to decide.
|
|
370
|
+
const historyKey = route === undefined && (group || direct)
|
|
371
|
+
? slackHistoryKey(teamId, { channelId: event.channel, threadTs: event.thread_ts })
|
|
372
|
+
: undefined;
|
|
331
373
|
// Explicit user stop: a control action, never a turn — it must not queue behind the run it stops.
|
|
332
374
|
if (isStopText(stripSlackMentions(event.text ?? ""))) {
|
|
333
375
|
seen.add(logicalId);
|
|
334
376
|
const target = { channelId: event.channel, threadTs: event.thread_ts };
|
|
335
|
-
sideTasks.track(dispatchStop(
|
|
336
|
-
.then((feedback) =>
|
|
377
|
+
sideTasks.track(dispatchStop(agent, recordedSession(routed.conversation ?? defaultConversation), label)
|
|
378
|
+
.then((feedback) => placeApi({ historyKey, teamId })
|
|
379
|
+
.postMessage(target, feedback)
|
|
380
|
+
.then(() => undefined))
|
|
337
381
|
.catch((error) => log.warn(`${label} stop feedback failed: ${String(error)}`)));
|
|
338
382
|
return;
|
|
339
383
|
}
|
|
@@ -347,9 +391,10 @@ export function slackChannel(options) {
|
|
|
347
391
|
runner.submit({
|
|
348
392
|
id: logicalId,
|
|
349
393
|
seq: ++seq,
|
|
350
|
-
|
|
394
|
+
conversation: routed.conversation ?? defaultConversation,
|
|
351
395
|
baseText,
|
|
352
|
-
|
|
396
|
+
...(historyKey !== undefined ? { historyKey } : {}),
|
|
397
|
+
group,
|
|
353
398
|
teamId,
|
|
354
399
|
channelId: targetChannel,
|
|
355
400
|
threadTs,
|
|
@@ -362,7 +407,7 @@ export function slackChannel(options) {
|
|
|
362
407
|
if (group &&
|
|
363
408
|
threadTs !== undefined &&
|
|
364
409
|
sameChannel &&
|
|
365
|
-
routed.
|
|
410
|
+
routed.conversation === undefined &&
|
|
366
411
|
routed.threadTs === undefined) {
|
|
367
412
|
// The ASKER counts as heard in this thread too, and both halves are written together so the record can never
|
|
368
413
|
// say "the agent takes part and nobody has spoken".
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Durable set of users who have already received the first-run DM welcome, so it is sent at most once. */
|
|
2
2
|
import { log } from "../../log.js";
|
|
3
|
-
import { loadStateFile, saveStateFile } from "
|
|
3
|
+
import { loadStateFile, saveStateFile } from "../../state-file.js";
|
|
4
4
|
function key(teamId, userId) {
|
|
5
5
|
return `${teamId}:${userId}`;
|
|
6
6
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Telegram's half of the shared context buffer (mechanics + consume protocol: ../kit/context-buffer.ts). */
|
|
2
1
|
import { type ContextBuffer as GenericContextBuffer } from "../kit/context-buffer.ts";
|
|
3
2
|
/** One buffered un-summoned message (object identity is the commit key). */
|
|
4
3
|
export interface BufferEntry {
|
|
@@ -31,5 +30,11 @@ export declare function collectAttachments(consumed: BufferEntry[], primary: {
|
|
|
31
30
|
images: BufferedRef[];
|
|
32
31
|
skipped: number;
|
|
33
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* A message the agent sent itself (through `telegramTransport`), as the place's discussion: the agent never receives
|
|
35
|
+
* its own messages as updates (and the Bot API has no history read), so this is the only way the chat's next turn
|
|
36
|
+
* knows it was said. The label says only what the transport knows — the agent sent it — not which tool did.
|
|
37
|
+
*/
|
|
38
|
+
export declare function ownPostEntry(body: string, messageId: number | undefined): BufferEntry;
|
|
34
39
|
export type ContextBuffer = GenericContextBuffer<BufferEntry>;
|
|
35
40
|
export declare function createContextBuffer(path: string): ContextBuffer;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Telegram's half of the shared context buffer (mechanics + consume protocol: ../kit/context-buffer.ts). */
|
|
2
|
+
import { truncateCodePointPrefix } from "../kit/text.js";
|
|
2
3
|
import { capBufferedRefs, createContextBuffer as createGenericContextBuffer, } from "../kit/context-buffer.js";
|
|
3
4
|
function bufferLine(e) {
|
|
4
5
|
const meta = [
|
|
@@ -28,6 +29,24 @@ export function collectAttachments(consumed, primary) {
|
|
|
28
29
|
const images = capBufferedRefs(refs((e) => e.imageIds, primary.images));
|
|
29
30
|
return { files: files.kept, images: images.kept, skipped: files.skipped + images.skipped };
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* How much of a message the agent sent itself a place keeps. A digest runs to thousands of characters, and the
|
|
34
|
+
* per-line bound a human message gets would keep its title only; this stays inside the buffer's budget, so later
|
|
35
|
+
* discussion still evicts it oldest-first.
|
|
36
|
+
*/
|
|
37
|
+
const OWN_POST_MAX_CHARS = 2000;
|
|
38
|
+
/**
|
|
39
|
+
* A message the agent sent itself (through `telegramTransport`), as the place's discussion: the agent never receives
|
|
40
|
+
* its own messages as updates (and the Bot API has no history read), so this is the only way the chat's next turn
|
|
41
|
+
* knows it was said. The label says only what the transport knows — the agent sent it — not which tool did.
|
|
42
|
+
*/
|
|
43
|
+
export function ownPostEntry(body, messageId) {
|
|
44
|
+
return {
|
|
45
|
+
sender: "you (sent by the agent)",
|
|
46
|
+
body: truncateCodePointPrefix(body.replace(/\s+/g, " ").trim(), OWN_POST_MAX_CHARS, " … (truncated)"),
|
|
47
|
+
messageId,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
31
50
|
function isBufferEntry(e) {
|
|
32
51
|
const t = e;
|
|
33
52
|
const strings = (v) => v === undefined || (Array.isArray(v) && v.every((x) => typeof x === "string"));
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Split from parse.ts because this half touches the Bot API + disk; split from telegram.ts so the
|
|
5
5
|
* factory keeps only wiring and the per-turn lifecycle.
|
|
6
6
|
*/
|
|
7
|
-
import type { Agent
|
|
7
|
+
import type { Agent } from "../../agent-api.ts";
|
|
8
8
|
import type * as Stream from "effect/Stream";
|
|
9
9
|
import type { PortFailure } from "../../effect-port.ts";
|
|
10
|
-
import { type
|
|
10
|
+
import { type TurnEvent } from "../kit/invoke-turn-kit.ts";
|
|
11
11
|
import type { BufferedRef } from "./context-buffer.ts";
|
|
12
12
|
/** Everything the transport needs to fetch a turn's attachments. */
|
|
13
13
|
export interface TurnTransport {
|
|
@@ -31,6 +31,10 @@ export interface TurnAttachments {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Run one turn: resolve its attachments, then ask the agent (invoke-turn-kit — a primary-attachment
|
|
34
|
-
* failure surfaces as a `
|
|
34
|
+
* failure surfaces as a `rejected` event, never a silent drop). `key` is the turn's: the update it answers.
|
|
35
35
|
*/
|
|
36
|
-
export declare function telegramTurnStream(agent: Agent,
|
|
36
|
+
export declare function telegramTurnStream(agent: Agent, turn: {
|
|
37
|
+
session: () => Promise<string>;
|
|
38
|
+
key: string;
|
|
39
|
+
text: string;
|
|
40
|
+
}, transport: TurnTransport, attachments: TurnAttachments): Stream.Stream<TurnEvent, PortFailure>;
|