@fastagent-sh/fastagent 0.24.4 → 0.25.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 +53 -82
- package/dist/agent-api.d.ts +452 -0
- package/dist/agent-api.js +201 -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 +77 -0
- package/dist/authoring.js +238 -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 +115 -63
- package/dist/cli/auth-view.d.ts +10 -3
- package/dist/cli/auth-view.js +8 -6
- package/dist/cli/box-login.d.ts +17 -7
- package/dist/cli/box-login.js +46 -12
- package/dist/cli/commands/add.d.ts +15 -4
- package/dist/cli/commands/add.js +252 -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 +190 -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 +131 -16
- package/dist/cli/commands/login.d.ts +31 -2
- package/dist/cli/commands/login.js +413 -64
- 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 +3 -2
- package/dist/cli/login-relay.js +18 -6
- 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 +68 -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 +217 -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 +222 -201
- 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 +51 -26
- package/dist/effect-port.d.ts +4 -4
- package/dist/effect-port.js +4 -4
- 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 +50 -0
- package/dist/environment/mise.js +268 -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 +43 -0
- package/dist/harnesses/models/environment.js +82 -0
- package/dist/{engines/pi → harnesses/models}/login.d.ts +57 -26
- package/dist/{engines/pi → harnesses/models}/login.js +187 -79
- package/dist/harnesses/models/models.d.ts +83 -0
- package/dist/harnesses/models/models.js +158 -0
- package/dist/harnesses/pi-durable/agent.d.ts +42 -0
- package/dist/harnesses/pi-durable/agent.js +579 -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 +88 -0
- package/dist/harnesses/pi-durable/open.js +329 -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 +80 -0
- package/dist/harnesses/pi-durable/serve.js +156 -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 +4 -0
- package/dist/harnesses/pi-durable/wake.js +81 -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 +17 -4
- package/dist/index.js +10 -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/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 +12 -4
- package/dist/value-file.d.ts +21 -0
- package/dist/value-file.js +108 -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/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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Slack place's history, read when a turn runs (docs/design/place-history.md): a channel's top level from
|
|
3
|
+
* `conversations.history`, a thread from `conversations.replies`. What a place remembers between turns, and what a
|
|
4
|
+
* read leaves out, is the kit's (`createPlaceHistory`). A message's `ts` is its id and its position at once: the
|
|
5
|
+
* turn's ask is the read's exclusive `latest`, and the next read's cursor. Each read lists the newest page before the
|
|
6
|
+
* ask and stops at the cursor itself, as Feishu's does: Slack has no reliable "after this point" (`SlackRange`).
|
|
7
|
+
*/
|
|
8
|
+
import { capBufferedRefs } from "../kit/context-buffer.js";
|
|
9
|
+
import { createPlaceHistory, } from "../kit/place-history.js";
|
|
10
|
+
import { threadList } from "../kit/room-threads.js";
|
|
11
|
+
import { CONTEXT_READ } from "../kit/transport.js";
|
|
12
|
+
import { slackMessageText } from "./parse.js";
|
|
13
|
+
import { log } from "../../log.js";
|
|
14
|
+
import { SlackApiError } from "./slack-api.js";
|
|
15
|
+
/** How many of a place's newest messages one read keeps; a place busier than this since its last answer says so. */
|
|
16
|
+
const PAGE_SIZE = 50;
|
|
17
|
+
/** What a place's first answered turn reads back, with no cursor yet. */
|
|
18
|
+
const FIRST_READ = 20;
|
|
19
|
+
/** Message subtypes that are someone saying something; every other subtype is the platform's (a join, a topic). */
|
|
20
|
+
const SPOKEN_SUBTYPES = new Set(["file_share", "thread_broadcast", "bot_message", "me_message"]);
|
|
21
|
+
/** How long a person's name is reused before it is looked up again. */
|
|
22
|
+
const NAMES_TTL_MS = 10 * 60_000;
|
|
23
|
+
/** How many of a channel's newest messages a thread list looks through. */
|
|
24
|
+
const THREAD_SCAN = 100;
|
|
25
|
+
/** A place: a channel's top level, or a thread in it. The turn record carries it as this key. */
|
|
26
|
+
export function slackHistoryKey(teamId, place) {
|
|
27
|
+
const base = `${teamId}:${place.channelId}`;
|
|
28
|
+
return place.threadTs ? `${base}:root:${place.threadTs}` : base;
|
|
29
|
+
}
|
|
30
|
+
/** The inverse of {@link slackHistoryKey}. Slack ids and timestamps never contain a colon. */
|
|
31
|
+
export function slackPlaceOf(key) {
|
|
32
|
+
const [teamId = "", channelId = "", , threadTs] = key.split(":");
|
|
33
|
+
return { teamId, channelId, ...(threadTs ? { threadTs } : {}) };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Order two Slack timestamps (`seconds.micros`). A double tells microseconds apart until 2^33 seconds (the year 2242),
|
|
37
|
+
* so plain numbers compare them exactly.
|
|
38
|
+
*/
|
|
39
|
+
const compareSlackTs = (a, b) => Number(a) - Number(b);
|
|
40
|
+
/** Select the most recent background files of the folded messages, excluding the turn's own. */
|
|
41
|
+
export function collectFoldedFiles(folded, primaryIds) {
|
|
42
|
+
const seen = new Set(primaryIds);
|
|
43
|
+
const files = [];
|
|
44
|
+
for (const message of folded) {
|
|
45
|
+
for (const file of message.files) {
|
|
46
|
+
if (seen.has(file.key))
|
|
47
|
+
continue;
|
|
48
|
+
seen.add(file.key);
|
|
49
|
+
files.push({ id: file.key, from: message.from.label, messageId: message.id });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const capped = capBufferedRefs(files);
|
|
53
|
+
return { files: capped.kept, skipped: capped.skipped };
|
|
54
|
+
}
|
|
55
|
+
const str = (value) => (typeof value === "string" ? value : undefined);
|
|
56
|
+
const list = (value) => Array.isArray(value)
|
|
57
|
+
? value.filter((item) => typeof item === "object" && item !== null)
|
|
58
|
+
: [];
|
|
59
|
+
const textOf = (value) => str(value?.text);
|
|
60
|
+
/** One run of `rich_text`, as the person wrote it. */
|
|
61
|
+
function inline(element) {
|
|
62
|
+
switch (element.type) {
|
|
63
|
+
case "text":
|
|
64
|
+
return str(element.text);
|
|
65
|
+
case "link":
|
|
66
|
+
return str(element.text) ?? str(element.url);
|
|
67
|
+
case "user":
|
|
68
|
+
return `<@${str(element.user_id)}>`;
|
|
69
|
+
case "usergroup":
|
|
70
|
+
return `<!subteam^${str(element.usergroup_id)}>`;
|
|
71
|
+
case "channel":
|
|
72
|
+
return `<#${str(element.channel_id)}>`;
|
|
73
|
+
case "emoji":
|
|
74
|
+
return `:${str(element.name)}:`;
|
|
75
|
+
case "broadcast":
|
|
76
|
+
return `@${str(element.range)}`;
|
|
77
|
+
case "date":
|
|
78
|
+
return str(element.fallback) ?? "";
|
|
79
|
+
default:
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function joined(items, read, separator) {
|
|
84
|
+
const parts = items.map(read);
|
|
85
|
+
return parts.every((part) => part !== undefined) ? parts.join(separator) : undefined;
|
|
86
|
+
}
|
|
87
|
+
/** A `rich_text` block: sections, lists, quotes and code, each a run of inline elements. */
|
|
88
|
+
function richText(block) {
|
|
89
|
+
return joined(list(block.elements), (element) => {
|
|
90
|
+
const runs = (item) => joined(list(item.elements), inline, "");
|
|
91
|
+
switch (element.type) {
|
|
92
|
+
case "rich_text_section":
|
|
93
|
+
case "rich_text_preformatted":
|
|
94
|
+
return runs(element);
|
|
95
|
+
case "rich_text_quote": {
|
|
96
|
+
const run = runs(element);
|
|
97
|
+
return run === undefined ? undefined : `> ${run}`;
|
|
98
|
+
}
|
|
99
|
+
case "rich_text_list": {
|
|
100
|
+
// A list split by other blocks continues its numbering from `offset`.
|
|
101
|
+
const first = typeof element.offset === "number" ? element.offset + 1 : 1;
|
|
102
|
+
return joined(list(element.elements).map((item, index) => ({ item, index })), ({ item, index }) => {
|
|
103
|
+
const run = runs(item);
|
|
104
|
+
return run === undefined
|
|
105
|
+
? undefined
|
|
106
|
+
: `${element.style === "ordered" ? `${first + index}.` : "-"} ${run}`;
|
|
107
|
+
}, "\n");
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
// Each element is a block (a paragraph, a list, a quote, code): Slack does not always end one with a newline.
|
|
114
|
+
"\n");
|
|
115
|
+
}
|
|
116
|
+
const BLOCKS = {
|
|
117
|
+
header: (block) => textOf(block.text),
|
|
118
|
+
section: (block) => [textOf(block.text), ...list(block.fields).map(textOf)].filter(Boolean).join("\n"),
|
|
119
|
+
context: (block) => list(block.elements).map(textOf).filter(Boolean).join(" "),
|
|
120
|
+
markdown: (block) => str(block.text),
|
|
121
|
+
divider: () => "---",
|
|
122
|
+
image: (block) => `[image${str(block.alt_text) ? `: ${str(block.alt_text)}` : ""}]`,
|
|
123
|
+
rich_text: richText,
|
|
124
|
+
// A cell is `rich_text` or `raw_text`.
|
|
125
|
+
table: (block) => joined(Array.isArray(block.rows) ? block.rows : [], (row) => joined(list(row), (cell) => (cell.type === "raw_text" ? str(cell.text) : richText(cell)), " | "), "\n"),
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* What a message says. A Markdown post (every answer and `slack-send` post) is stored as blocks, and its `text` is a
|
|
129
|
+
* rendering that drops a table to ", with interactive elements" (measured), so the blocks are read when this knows them
|
|
130
|
+
* all, and `text` otherwise. Legacy attachments follow: an integration often says everything there.
|
|
131
|
+
*/
|
|
132
|
+
function spokenText(message) {
|
|
133
|
+
const blocks = list(message.blocks);
|
|
134
|
+
const read = joined(blocks, (block) => BLOCKS[String(block.type)]?.(block), "\n");
|
|
135
|
+
const body = blocks.length > 0 && read !== undefined ? read : (message.text ?? "");
|
|
136
|
+
const attached = (message.attachments ?? []).map((attachment) => {
|
|
137
|
+
const parts = [attachment.pretext, attachment.title, attachment.text].filter(Boolean);
|
|
138
|
+
return parts.length > 0 ? parts.join("\n") : (attachment.fallback ?? "");
|
|
139
|
+
});
|
|
140
|
+
return [body, ...attached].filter((part) => part.trim() !== "").join("\n");
|
|
141
|
+
}
|
|
142
|
+
export function createSlackPlaceHistory(deps) {
|
|
143
|
+
const { api } = deps;
|
|
144
|
+
/**
|
|
145
|
+
* The people already looked up, named or known to have no name to find (a failed lookup), and whether the app
|
|
146
|
+
* lacks `users:read`. All of it expires together, so a failure, a renamed person or a scope granted by a reinstall
|
|
147
|
+
* is picked up after the TTL, not on every turn and not only at the next start.
|
|
148
|
+
*/
|
|
149
|
+
const fresh = () => ({
|
|
150
|
+
named: new Map(),
|
|
151
|
+
unnamed: new Set(),
|
|
152
|
+
scopeMissing: false,
|
|
153
|
+
at: Date.now(),
|
|
154
|
+
});
|
|
155
|
+
let names = fresh();
|
|
156
|
+
/** The missing scope is said once per process: after that, each retry would only repeat it. */
|
|
157
|
+
let scopeWarned = false;
|
|
158
|
+
/** Names for the people a fold shows; one not found is shown by user id. Never rejects: a name is a label. */
|
|
159
|
+
const peopleNames = async (messages) => {
|
|
160
|
+
if (Date.now() - names.at >= NAMES_TTL_MS)
|
|
161
|
+
names = fresh();
|
|
162
|
+
const { named, unnamed } = names;
|
|
163
|
+
const self = deps.self();
|
|
164
|
+
const wanted = new Set(messages.flatMap((message) => message.user !== undefined && message.bot_id === undefined && message.user !== self.userId
|
|
165
|
+
? [message.user]
|
|
166
|
+
: []));
|
|
167
|
+
const unknown = [...wanted].filter((id) => !named.has(id) && !unnamed.has(id));
|
|
168
|
+
if (names.scopeMissing || unknown.length === 0)
|
|
169
|
+
return named;
|
|
170
|
+
const failures = [];
|
|
171
|
+
await Promise.all(unknown.map(async (id) => {
|
|
172
|
+
try {
|
|
173
|
+
const name = await api.userName(id, CONTEXT_READ);
|
|
174
|
+
if (name)
|
|
175
|
+
named.set(id, name);
|
|
176
|
+
else
|
|
177
|
+
unnamed.add(id);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
unnamed.add(id);
|
|
181
|
+
failures.push(error);
|
|
182
|
+
}
|
|
183
|
+
}));
|
|
184
|
+
const missingScope = failures.find((error) => error instanceof SlackApiError && error.slackError === "missing_scope");
|
|
185
|
+
if (missingScope) {
|
|
186
|
+
names.scopeMissing = true;
|
|
187
|
+
if (!scopeWarned) {
|
|
188
|
+
scopeWarned = true;
|
|
189
|
+
log.warn(`${deps.label} users:read is not granted, so people are shown by user id — add it under the Slack app's OAuth & Permissions → Bot Token Scopes (an app \`fastagent add slack\` created gets it with the next \`dev --tunnel\` or \`deploy --run\`), then Reinstall to Workspace; names appear within 10 minutes, or after a restart if the reinstall issued a new Bot Token`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (failures.length > 0) {
|
|
193
|
+
log.warn(`${deps.label} could not look up ${failures.length} name(s) (shown by user id for 10 minutes): ${String(failures[0])}`);
|
|
194
|
+
}
|
|
195
|
+
return named;
|
|
196
|
+
};
|
|
197
|
+
/** Who said it: this app, another bot, or a person (by name where `users:read` gives one). */
|
|
198
|
+
const speaker = (message, people) => {
|
|
199
|
+
const self = deps.self();
|
|
200
|
+
const own = (message.user !== undefined && message.user === self.userId) ||
|
|
201
|
+
(message.bot_id !== undefined && message.bot_id === self.botId);
|
|
202
|
+
if (own)
|
|
203
|
+
return { kind: "self", label: "you" };
|
|
204
|
+
if (message.bot_id !== undefined) {
|
|
205
|
+
return { kind: "bot", label: `bot ${message.bot_profile?.name ?? message.username ?? message.bot_id}` };
|
|
206
|
+
}
|
|
207
|
+
const name = message.user === undefined ? undefined : people.get(message.user);
|
|
208
|
+
return { kind: "human", label: name ?? `user ${message.user ?? "unknown"}` };
|
|
209
|
+
};
|
|
210
|
+
const read = async (key, { cursor, drop }, until) => {
|
|
211
|
+
const place = slackPlaceOf(key);
|
|
212
|
+
const range = { ...(until ? { latest: until } : {}), limit: cursor ? PAGE_SIZE : FIRST_READ };
|
|
213
|
+
const page = place.threadTs === undefined
|
|
214
|
+
? await api.channelHistory(place.channelId, range, CONTEXT_READ).then(({ messages, hasMore }) => ({
|
|
215
|
+
messages: messages.reverse(),
|
|
216
|
+
hasMore,
|
|
217
|
+
}))
|
|
218
|
+
: await api
|
|
219
|
+
.threadReplies(place.channelId, place.threadTs, range, CONTEXT_READ)
|
|
220
|
+
.then(({ messages, hasMore }) => ({
|
|
221
|
+
// The root comes first whatever the range: it is this place's discussion only on a first read.
|
|
222
|
+
messages: messages.filter((message) => message.ts !== place.threadTs || cursor === undefined),
|
|
223
|
+
hasMore,
|
|
224
|
+
}));
|
|
225
|
+
const after = (ts) => cursor === undefined || compareSlackTs(ts, cursor) > 0;
|
|
226
|
+
const listed = page.messages.filter((message) => message.ts !== undefined);
|
|
227
|
+
const fresh = listed.filter((message) => after(message.ts));
|
|
228
|
+
const earlier = page.hasMore && fresh.length === listed.length;
|
|
229
|
+
const spoken = fresh.filter((message) => message.hidden !== true &&
|
|
230
|
+
(message.subtype === undefined || SPOKEN_SUBTYPES.has(message.subtype)) &&
|
|
231
|
+
!drop(message.ts));
|
|
232
|
+
const people = await peopleNames(spoken);
|
|
233
|
+
const messages = spoken
|
|
234
|
+
.map((message) => {
|
|
235
|
+
const from = speaker(message, people);
|
|
236
|
+
const text = slackMessageText({ text: spokenText(message), files: message.files });
|
|
237
|
+
// No text, attachment or file to show: an empty line would only spend the fold's budget.
|
|
238
|
+
if (text.trim() === "")
|
|
239
|
+
return undefined;
|
|
240
|
+
return {
|
|
241
|
+
id: message.ts,
|
|
242
|
+
at: Number(message.ts) * 1000,
|
|
243
|
+
from,
|
|
244
|
+
text,
|
|
245
|
+
images: [],
|
|
246
|
+
// Whether a file is an image is `files.info`'s answer, read when the turn loads it.
|
|
247
|
+
files: (message.files ?? []).flatMap((file) => file.id ? [{ key: file.id, ...(file.name ? { name: file.name } : {}) }] : []),
|
|
248
|
+
};
|
|
249
|
+
})
|
|
250
|
+
.filter((message) => message !== undefined);
|
|
251
|
+
const newest = fresh.at(-1)?.ts;
|
|
252
|
+
return {
|
|
253
|
+
covered: fresh.map((message) => message.ts),
|
|
254
|
+
messages,
|
|
255
|
+
...(newest ? { newest } : {}),
|
|
256
|
+
earlier,
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
/** Threads are roots with replies; `conversations.history` gives their count and last reply. */
|
|
260
|
+
const threads = async (room) => {
|
|
261
|
+
const { channelId } = slackPlaceOf(room);
|
|
262
|
+
const { messages } = await api.channelHistory(channelId, { limit: THREAD_SCAN }, CONTEXT_READ);
|
|
263
|
+
const roots = messages.filter((message) => message.ts !== undefined && (message.reply_count ?? 0) > 0);
|
|
264
|
+
const people = await peopleNames(roots);
|
|
265
|
+
const summaries = roots.map((message) => ({
|
|
266
|
+
id: message.ts,
|
|
267
|
+
time: { at: Number(message.latest_reply ?? message.ts) * 1000, is: "last active" },
|
|
268
|
+
replies: message.reply_count,
|
|
269
|
+
first: { label: speaker(message, people).label, text: spokenText(message) },
|
|
270
|
+
}));
|
|
271
|
+
return threadList(summaries, `this channel's newest ${THREAD_SCAN} messages`);
|
|
272
|
+
};
|
|
273
|
+
const history = createPlaceHistory({
|
|
274
|
+
label: deps.label,
|
|
275
|
+
path: deps.path,
|
|
276
|
+
isCursor: (value) => typeof value === "string",
|
|
277
|
+
compare: compareSlackTs,
|
|
278
|
+
isTurnInput: deps.isTurnInput,
|
|
279
|
+
read,
|
|
280
|
+
});
|
|
281
|
+
return { ...history, threads };
|
|
282
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/** Resolve Slack file IDs at dequeue, then stream one
|
|
2
|
-
import type { Agent
|
|
1
|
+
/** Resolve Slack file IDs at dequeue, then stream one harness-neutral Agent turn. */
|
|
2
|
+
import type { Agent } from "../../agent-api.ts";
|
|
3
3
|
import type * as Stream from "effect/Stream";
|
|
4
4
|
import type { PortFailure } from "../../effect-port.ts";
|
|
5
|
-
import { type
|
|
6
|
-
import type { SlackBufferedFileRef } from "./
|
|
5
|
+
import { type TurnEvent } from "../kit/invoke-turn-kit.ts";
|
|
6
|
+
import type { SlackBufferedFileRef } from "./history.ts";
|
|
7
7
|
import { type SlackApi } from "./slack-api.ts";
|
|
8
8
|
export interface SlackTurnTransport {
|
|
9
9
|
api: SlackApi;
|
|
@@ -18,4 +18,11 @@ export interface SlackTurnAttachments {
|
|
|
18
18
|
skipped: number;
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/** One turn: resolve its files, then ask the agent. `key` is the turn's: the message it answers. */
|
|
22
|
+
export declare function slackTurnStream(agent: Agent, turn: {
|
|
23
|
+
session: () => Promise<string>;
|
|
24
|
+
/** While it runs, the room its thread-reading tool reads (a channel turn's channel). */
|
|
25
|
+
enter?: (sessionId: string) => () => void;
|
|
26
|
+
key: string;
|
|
27
|
+
text: string;
|
|
28
|
+
}, transport: SlackTurnTransport, attachments: SlackTurnAttachments): Stream.Stream<TurnEvent, PortFailure>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { attachedFilesManifest, attributedFileName, backgroundImagesManifest, loadBackground, missingAttachmentsNote, turnStream, promptContent, } from "../kit/invoke-turn-kit.js";
|
|
2
2
|
import { SlackApiError } from "./slack-api.js";
|
|
3
3
|
const MARKDOWN_INSTRUCTION = "\n\n(Format your reply as standard Markdown. Slack renders it natively. Do not use HTML or Slack control-mention syntax such as <!here>, <!channel>, or <!everyone>.)";
|
|
4
4
|
async function resolveFile(transport, fileId) {
|
|
@@ -44,15 +44,17 @@ async function resolveInputs(transport, attachments) {
|
|
|
44
44
|
promptSuffix: `${missingNote}${imageManifest}${attachedFilesManifest(allFiles)}`,
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
/** One turn: resolve its files, then ask the agent. `key` is the turn's: the message it answers. */
|
|
48
|
+
export function slackTurnStream(agent, turn, transport, attachments) {
|
|
48
49
|
return turnStream({
|
|
49
50
|
agent,
|
|
50
51
|
label: transport.label,
|
|
51
|
-
|
|
52
|
+
session: turn.session,
|
|
53
|
+
...(turn.enter === undefined ? {} : { enter: turn.enter }),
|
|
52
54
|
resolve: () => resolveInputs(transport, attachments),
|
|
53
55
|
turn: (resolved) => ({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
idempotencyKey: turn.key,
|
|
57
|
+
content: promptContent(`${turn.text}${resolved.promptSuffix}${MARKDOWN_INSTRUCTION}`, resolved.images),
|
|
56
58
|
}),
|
|
57
59
|
// Slack says which failures are transient; anything else (a deleted file, a missing scope) will
|
|
58
60
|
// fail the same way on redelivery.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export type SlackGroupBehavior = "context" | "mentions";
|
|
2
1
|
export interface SlackAppManifest {
|
|
3
2
|
display_information: {
|
|
4
3
|
name: string;
|
|
@@ -39,11 +38,9 @@ export interface SlackAppManifest {
|
|
|
39
38
|
token_rotation_enabled: boolean;
|
|
40
39
|
};
|
|
41
40
|
}
|
|
42
|
-
export declare function slackBotScopes(
|
|
43
|
-
export declare function slackBotEvents(groupBehavior: SlackGroupBehavior): string[];
|
|
41
|
+
export declare function slackBotScopes(): string[];
|
|
44
42
|
export declare function buildSlackManifest(input: {
|
|
45
43
|
name: string;
|
|
46
|
-
groupBehavior: SlackGroupBehavior;
|
|
47
44
|
requestUrl?: string;
|
|
48
45
|
redirectUrl?: string;
|
|
49
46
|
}): SlackAppManifest;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// One posture, as on Feishu: the agent hears the channels it is in (participant-model.md §2), so the history scopes
|
|
2
|
+
// and their message events are part of every app. `users:read` names the people in the history a turn reads.
|
|
3
|
+
const SLACK_BOT_SCOPES = [
|
|
2
4
|
"app_mentions:read",
|
|
3
5
|
"assistant:write",
|
|
4
6
|
"chat:write",
|
|
@@ -6,15 +8,25 @@ const SLACK_BASE_BOT_SCOPES = [
|
|
|
6
8
|
"files:write",
|
|
7
9
|
"im:history",
|
|
8
10
|
"reactions:write",
|
|
11
|
+
"channels:history",
|
|
12
|
+
"groups:history",
|
|
13
|
+
"mpim:history",
|
|
14
|
+
"users:read",
|
|
9
15
|
];
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
const SLACK_BOT_EVENTS = [
|
|
17
|
+
"app_context_changed",
|
|
18
|
+
"app_home_opened",
|
|
19
|
+
"app_mention",
|
|
20
|
+
"message.im",
|
|
21
|
+
"message.channels",
|
|
22
|
+
"message.groups",
|
|
23
|
+
"message.mpim",
|
|
24
|
+
];
|
|
25
|
+
export function slackBotScopes() {
|
|
26
|
+
return [...SLACK_BOT_SCOPES].sort();
|
|
15
27
|
}
|
|
16
|
-
|
|
17
|
-
return [...
|
|
28
|
+
function slackBotEvents() {
|
|
29
|
+
return [...SLACK_BOT_EVENTS].sort();
|
|
18
30
|
}
|
|
19
31
|
/** Slack's bot display name is more restrictive than the app name. */
|
|
20
32
|
function slackBotDisplayName(name) {
|
|
@@ -49,7 +61,7 @@ export function buildSlackManifest(input) {
|
|
|
49
61
|
bot_user: { display_name: slackBotDisplayName(name), always_online: false },
|
|
50
62
|
},
|
|
51
63
|
oauth_config: {
|
|
52
|
-
scopes: { bot: slackBotScopes(
|
|
64
|
+
scopes: { bot: slackBotScopes() },
|
|
53
65
|
...(input.redirectUrl ? { redirect_urls: [input.redirectUrl] } : {}),
|
|
54
66
|
},
|
|
55
67
|
settings: {
|
|
@@ -57,7 +69,7 @@ export function buildSlackManifest(input) {
|
|
|
57
69
|
? {
|
|
58
70
|
event_subscriptions: {
|
|
59
71
|
request_url: input.requestUrl,
|
|
60
|
-
bot_events: slackBotEvents(
|
|
72
|
+
bot_events: slackBotEvents(),
|
|
61
73
|
},
|
|
62
74
|
}
|
|
63
75
|
: {}),
|
|
@@ -58,7 +58,8 @@ export interface SlackEventEnvelope {
|
|
|
58
58
|
}>;
|
|
59
59
|
}
|
|
60
60
|
export interface SlackRoute {
|
|
61
|
-
session
|
|
61
|
+
/** Which conversation it belongs to, the one its session is kept by (default: the thread). */
|
|
62
|
+
conversation?: string;
|
|
62
63
|
channelId?: string;
|
|
63
64
|
threadTs?: string | null;
|
|
64
65
|
text?: string;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createSlackApp, exchangeSlackOAuthCode, updateSlackAppManifest } from "./config-api.ts";
|
|
2
|
-
import { type SlackGroupBehavior } from "./manifest.ts";
|
|
3
2
|
import { type SlackOnboardingState } from "./onboarding-state.ts";
|
|
4
3
|
export interface SlackOnboardIO {
|
|
5
4
|
note(message: string): void;
|
|
@@ -33,7 +32,6 @@ export declare function onboardSlackApp(input: SlackOnboardInput, io: SlackOnboa
|
|
|
33
32
|
}): Promise<SlackOnboardingState>;
|
|
34
33
|
export declare function newSlackOnboardingState(input: {
|
|
35
34
|
appName: string;
|
|
36
|
-
groupBehavior: SlackGroupBehavior;
|
|
37
35
|
configToken: string;
|
|
38
36
|
configRefreshToken: string;
|
|
39
37
|
/** When the pair was captured; pass it through rather than re-deriving the same expiry twice. */
|
|
@@ -22,7 +22,6 @@ export async function onboardSlackApp(input, io, deps = {}) {
|
|
|
22
22
|
});
|
|
23
23
|
const manifest = buildSlackManifest({
|
|
24
24
|
name: state.appName,
|
|
25
|
-
groupBehavior: state.groupBehavior,
|
|
26
25
|
requestUrl: input.requestUrl,
|
|
27
26
|
redirectUrl: input.redirectUrl,
|
|
28
27
|
});
|
|
@@ -91,7 +90,7 @@ export async function onboardSlackApp(input, io, deps = {}) {
|
|
|
91
90
|
const oauthState = randomBytes(24).toString("hex");
|
|
92
91
|
const authorize = new URL("https://slack.com/oauth/v2/authorize");
|
|
93
92
|
authorize.searchParams.set("client_id", state.clientId);
|
|
94
|
-
authorize.searchParams.set("scope", slackBotScopes(
|
|
93
|
+
authorize.searchParams.set("scope", slackBotScopes().join(","));
|
|
95
94
|
authorize.searchParams.set("redirect_uri", input.redirectUrl);
|
|
96
95
|
authorize.searchParams.set("state", oauthState);
|
|
97
96
|
// The redirect lands on the tunnel hostname, which THIS machine may not resolve yet (#421).
|
|
@@ -112,7 +111,7 @@ export async function onboardSlackApp(input, io, deps = {}) {
|
|
|
112
111
|
});
|
|
113
112
|
if (oauth.appId !== state.appId)
|
|
114
113
|
throw new Error("Slack OAuth installed a different app than the manifest app");
|
|
115
|
-
if (slackBotScopes(
|
|
114
|
+
if (slackBotScopes().some((scope) => !oauth.scopes.includes(scope))) {
|
|
116
115
|
throw new Error("Slack OAuth completed without all required bot scopes; re-run fastagent add slack to reinstall");
|
|
117
116
|
}
|
|
118
117
|
await io.writeRuntimeSecrets({ botToken: oauth.botToken });
|
|
@@ -130,7 +129,6 @@ export function newSlackOnboardingState(input) {
|
|
|
130
129
|
return {
|
|
131
130
|
version: 1,
|
|
132
131
|
appName: input.appName,
|
|
133
|
-
groupBehavior: input.groupBehavior,
|
|
134
132
|
configToken: input.configToken,
|
|
135
133
|
configRefreshToken: input.configRefreshToken,
|
|
136
134
|
configTokenExpiresAt: input.configTokenExpiresAt ?? configTokenExpiry(input.now),
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import type { SlackGroupBehavior } from "./manifest.ts";
|
|
2
1
|
/** When a token pair captured `now` stops being usable — the ONE place that answers it. */
|
|
3
2
|
export declare function configTokenExpiry(now?: number): number;
|
|
4
3
|
export interface SlackOnboardingState {
|
|
5
4
|
version: 1;
|
|
6
5
|
appName: string;
|
|
7
|
-
groupBehavior: SlackGroupBehavior;
|
|
8
6
|
appId?: string;
|
|
9
7
|
/** Set before apps.manifest.create; without an appId it blocks blind duplicate-creation retries. */
|
|
10
8
|
createAttemptedAt?: string;
|
|
@@ -18,7 +18,6 @@ function validState(value) {
|
|
|
18
18
|
state !== null &&
|
|
19
19
|
state.version === 1 &&
|
|
20
20
|
typeof state.appName === "string" &&
|
|
21
|
-
(state.groupBehavior === "context" || state.groupBehavior === "mentions") &&
|
|
22
21
|
typeof state.configToken === "string" &&
|
|
23
22
|
typeof state.configRefreshToken === "string" &&
|
|
24
23
|
typeof state.configTokenExpiresAt === "number");
|
|
@@ -2,7 +2,6 @@ import type { SlackEventEnvelope, SlackMessageEvent, SlackRoute } from "./model.
|
|
|
2
2
|
export type { SlackEventEnvelope, SlackFile, SlackMessageEvent, SlackRoute } from "./model.ts";
|
|
3
3
|
/** Does this text address ANYONE — a user, a broadcast, or a user group? */
|
|
4
4
|
export declare function hasSlackMention(text: string): boolean;
|
|
5
|
-
export declare function hasSlackUserMention(text: string): boolean;
|
|
6
5
|
/** Does this text mention this specific user (either form)? */
|
|
7
6
|
export declare function mentionsSlackUser(text: string, userId: string): boolean;
|
|
8
7
|
/** Strip every mention, e.g. before matching a bare command word or building a title. */
|
|
@@ -20,10 +19,6 @@ export declare function slackTeamId(envelope: SlackEventEnvelope): string | unde
|
|
|
20
19
|
export declare function slackFileIds(event: SlackMessageEvent): string[];
|
|
21
20
|
/** Text plus a structural marker for file-only or captioned file shares. */
|
|
22
21
|
export declare function slackMessageText(event: SlackMessageEvent): string;
|
|
23
|
-
export declare function slackSenderLabel(event: SlackMessageEvent): string;
|
|
24
|
-
/** Main-channel discussion and each concrete thread are independent context buckets. */
|
|
25
|
-
export declare function slackPlaceKey(teamId: string, event: Pick<SlackMessageEvent, "channel" | "thread_ts">): string;
|
|
26
|
-
export declare function slackBufferText(text: string): string;
|
|
27
22
|
export declare function slackEnvelope(envelope: SlackEventEnvelope): string;
|
|
28
23
|
/** Default explicit-summon policy: DMs and app_mention events only. */
|
|
29
24
|
export declare function defaultSlackRoute(envelope: SlackEventEnvelope): SlackRoute | null;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { codePointPrefix, truncateCodePointPrefix } from "../kit/text.js";
|
|
1
|
+
import { codePointPrefix } from "../kit/text.js";
|
|
3
2
|
const HUMAN_MESSAGE_SUBTYPES = new Set(["file_share", "thread_broadcast"]);
|
|
4
3
|
/** Both forms Slack writes a mention in: `<@U123>` and the labelled `<@U123|name>`. */
|
|
5
4
|
const mentionSource = (idPattern) => String.raw `<@${idPattern}(?:\|[^>]*)?>`;
|
|
@@ -10,9 +9,6 @@ const ANY_MENTION = String.raw `(?:${USER_MENTION}|<!(?:here|channel|everyone)(?
|
|
|
10
9
|
export function hasSlackMention(text) {
|
|
11
10
|
return new RegExp(ANY_MENTION, "i").test(text);
|
|
12
11
|
}
|
|
13
|
-
export function hasSlackUserMention(text) {
|
|
14
|
-
return new RegExp(USER_MENTION, "i").test(text);
|
|
15
|
-
}
|
|
16
12
|
/** Escape a platform-supplied id before it becomes part of a pattern. */
|
|
17
13
|
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18
14
|
/** Does this text mention this specific user (either form)? */
|
|
@@ -58,17 +54,6 @@ export function slackMessageText(event) {
|
|
|
58
54
|
const marker = `[${files} attached file${files === 1 ? "" : "s"}]`;
|
|
59
55
|
return text ? `${text}\n${marker}` : marker;
|
|
60
56
|
}
|
|
61
|
-
export function slackSenderLabel(event) {
|
|
62
|
-
return `user ${event.user ?? "unknown"}`;
|
|
63
|
-
}
|
|
64
|
-
/** Main-channel discussion and each concrete thread are independent context buckets. */
|
|
65
|
-
export function slackPlaceKey(teamId, event) {
|
|
66
|
-
const base = `${teamId}:${event.channel ?? "unknown-channel"}`;
|
|
67
|
-
return event.thread_ts ? `${base}:root:${event.thread_ts}` : base;
|
|
68
|
-
}
|
|
69
|
-
export function slackBufferText(text) {
|
|
70
|
-
return truncateCodePointPrefix(text.replace(/\s+/g, " ").trim(), BUFFER_LINE_MAX_CHARS);
|
|
71
|
-
}
|
|
72
57
|
export function slackEnvelope(envelope) {
|
|
73
58
|
const event = envelope.event;
|
|
74
59
|
if (!event?.channel)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import type { AgentEvent } from "../../agent.ts";
|
|
1
|
+
import type { TurnEvent } from "../kit/invoke-turn-kit.ts";
|
|
3
2
|
import * as Effect from "effect/Effect";
|
|
4
3
|
import * as Stream from "effect/Stream";
|
|
5
4
|
import { PortFailure } from "../../effect-port.ts";
|
|
@@ -23,7 +22,7 @@ export declare function sanitizeSlackMarkdown(markdown: string): string;
|
|
|
23
22
|
export declare function deliverSlackAnswer(api: SlackApi, target: SlackTarget, markdown: string, previewTs?: string): Promise<void>;
|
|
24
23
|
/** Settle a queue/drop/defer notice. These are authored plain strings, so the basic text API is enough. */
|
|
25
24
|
export declare function settleSlackPreview(api: SlackApi, target: SlackTarget, previewTs: string | undefined, text: string): Promise<void>;
|
|
26
|
-
export declare function slackReply(events: Stream.Stream<
|
|
25
|
+
export declare function slackReply(events: Stream.Stream<TurnEvent, PortFailure>, api: SlackApi, target: SlackTarget, formatError: (failure: SlackFailure) => string | undefined, options?: {
|
|
27
26
|
rendering?: SlackRendering;
|
|
28
27
|
initialPreviewTs?: string;
|
|
29
28
|
threadTitle?: string;
|