@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
package/dist/channels/serve.js
CHANGED
|
@@ -1,256 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
import
|
|
1
|
+
/** The server a Fetch handler is served on, behind the totality boundary every serving path shares. */
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Exit from "effect/Exit";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Scope from "effect/Scope";
|
|
8
|
+
import { HttpEffect, HttpServer, HttpServerError } from "effect/http";
|
|
9
|
+
import { LIMITS } from "../agent-api.js";
|
|
6
10
|
import { log } from "../log.js";
|
|
7
|
-
import { refuseNonJsonBody } from "./body.js";
|
|
8
11
|
import { text } from "./respond.js";
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
/** Parse a route key: `"METHOD /path"` → `{ method, path }`, or `"/path"` → `{ path }` (any method). */
|
|
12
|
-
export function parseRouteKey(key) {
|
|
13
|
-
const sp = key.indexOf(" ");
|
|
14
|
-
return sp === -1 ? { path: key } : { method: key.slice(0, sp).toUpperCase(), path: key.slice(sp + 1) };
|
|
15
|
-
}
|
|
16
|
-
/** A route key is `"METHOD /path"` or `"/path"`, with a literal path. */
|
|
17
|
-
export function assertRouteKey(key, describe) {
|
|
18
|
-
const { method, path } = parseRouteKey(key);
|
|
19
|
-
if (method === "")
|
|
20
|
-
throw new Error(describe('a leading space is not a method — write "/path" for any method'));
|
|
21
|
-
if (!path.startsWith("/"))
|
|
22
|
-
throw new Error(describe('must start with "/"'));
|
|
23
|
-
// Asked of `URL` rather than by listing what it rewrites (`?`/`#`, `.`/`..`, `\`, `%2e`).
|
|
24
|
-
const arrives = new URL(path, "http://x").pathname;
|
|
25
|
-
if (arrives !== path) {
|
|
26
|
-
throw new Error(describe(`is not the path a request would carry — that request arrives as "${arrives}"`));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/** Do these two keys fight over the same request? */
|
|
30
|
-
export function routeKeysConflict(a, b) {
|
|
31
|
-
const ka = parseRouteKey(a);
|
|
32
|
-
const kb = parseRouteKey(b);
|
|
33
|
-
if (ka.path !== kb.path)
|
|
34
|
-
return false;
|
|
35
|
-
return ka.method === undefined || kb.method === undefined || ka.method === kb.method;
|
|
36
|
-
}
|
|
37
|
-
/** Same status and headers, no content (RFC 9110's HEAD). */
|
|
38
|
-
export function withoutBody(res) {
|
|
39
|
-
void res.body?.cancel().catch(() => { });
|
|
40
|
-
return new Response(null, { status: res.status, statusText: res.statusText, headers: res.headers });
|
|
41
|
-
}
|
|
42
|
-
export function pathUnderPrefix(path, prefix) {
|
|
43
|
-
return path === prefix || path.startsWith(`${prefix}/`);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* THE cross-origin policy, in one place, for the routes that authenticate nobody.
|
|
47
|
-
*
|
|
48
|
-
* THIS IS AN API, so the default is an API's: `*`, unconditionally. Origin is not access control — what makes any
|
|
49
|
-
* API safe to call from a page is a credential the page does not have, which makes the caller's origin irrelevant.
|
|
50
|
-
* We have no credential, and the boundary that replaces it — who may reach this port — belongs to the deployment,
|
|
51
|
-
* the same rule that removed the bearer token. Deciding for the operator from a signal this process can observe
|
|
52
|
-
* (which address it bound) is not that rule: a wildcard bind is what a container needs, not something the operator
|
|
53
|
-
* said, and a loopback one is `dev`'s default, not a claim about who may call.
|
|
54
|
-
*
|
|
55
|
-
* WHAT `*` GRANTS, stated because it is the default and nobody opts into it: any page the user's browser visits can
|
|
56
|
-
* call this port cross-origin and read the reply — a turn with the agent's full tool authority, and with
|
|
57
|
-
* `sessionControl: true`, reading and deleting sessions. On a published port that is nothing an attacker could not
|
|
58
|
-
* already curl. On a loopback `dev` serve it is the whole attack path, and this is the posture the decision costs:
|
|
59
|
-
* `announceControl` says so at every boot, and `http.cors` is the one way to take it back.
|
|
60
|
-
*
|
|
61
|
-
* `allow` is `http.cors`, and when it is set it REPLACES the default rather than adding to it: `["*"]` is the
|
|
62
|
-
* default said out loud, a list of origins is the narrowing.
|
|
63
|
-
*
|
|
64
|
-
* An origin that is not allowed is simply not ANSWERED — no headers, no refusal. That is safe only because a route
|
|
65
|
-
* that authenticates nobody also refuses a body that is not `application/json` (channels/body.ts): without that, a
|
|
66
|
-
* cross-origin `text/plain` POST is a CORS simple request, sent with no preflight, and withholding the headers
|
|
67
|
-
* would only stop the page from reading a turn that had already run.
|
|
68
|
-
*/
|
|
69
|
-
function allowedOrigin(origin, allow) {
|
|
70
|
-
if (allow.length === 0)
|
|
71
|
-
return "*";
|
|
72
|
-
return allow.includes("*") ? "*" : allow.includes(origin) ? origin : undefined;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Refuse a cross-origin allow-list that cannot match anything.
|
|
76
|
-
*
|
|
77
|
-
* ONE check for BOTH ways in: `http.cors` in a config file, and `corsOrigins` handed to `mountAgentService` by an
|
|
78
|
-
* embedder. `allowedOrigin` compares exact strings, so `"https://app.example.com/"` — one trailing slash — never
|
|
79
|
-
* matches and the front end is refused with nothing pointing at the rule. A rule that silently matches nothing is
|
|
80
|
-
* worse than one that refuses to load.
|
|
81
|
-
*/
|
|
82
|
-
export function assertCorsOrigins(origins, where) {
|
|
83
|
-
if (!Array.isArray(origins) || origins.some((o) => typeof o !== "string" || o === "")) {
|
|
84
|
-
throw new Error(`${where} must be an array of origin strings (e.g. ["https://app.example.com"])`);
|
|
85
|
-
}
|
|
86
|
-
// An EMPTY list is the one spelling that means the OPPOSITE of what it looks like. `allowedOrigin` reads "nothing
|
|
87
|
-
// configured" as the `*` default and cannot tell that apart from a list someone wrote as empty, so `http.cors: []`
|
|
88
|
-
// — the obvious way to write "no page may call this" — would grant the widest policy there is, and silence the
|
|
89
|
-
// boot warning that names it (`announceControl` treats a set list as the operator having chosen the origins).
|
|
90
|
-
if (origins.length === 0) {
|
|
91
|
-
throw new Error(`${where} is empty — list at least one origin, or write ["*"] to say the default out loud`);
|
|
92
|
-
}
|
|
93
|
-
for (const origin of origins) {
|
|
94
|
-
if (origin === "*")
|
|
95
|
-
continue;
|
|
96
|
-
let parsed;
|
|
97
|
-
try {
|
|
98
|
-
parsed = new URL(origin);
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
// `new URL` throwing IS the answer here ("not a URL at all"); rethrown as the rule that cannot load.
|
|
102
|
-
throw new Error(`${where} entry ${JSON.stringify(origin)} is not an origin ("https://host[:port]")`);
|
|
103
|
-
}
|
|
104
|
-
// An ORIGIN, not a URL with a path: `new URL(...).origin` is what a browser sends.
|
|
105
|
-
if (parsed.origin !== origin) {
|
|
106
|
-
throw new Error(`${where} entry ${JSON.stringify(origin)} is not the origin a browser sends — use ${JSON.stringify(parsed.origin)}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Compose the routes that AUTHENTICATE NOBODY, the self-verifying channel routes, and the {@link PrefixMount}s
|
|
112
|
-
* into one handler.
|
|
113
|
-
*
|
|
114
|
-
* That is the axis, not authorship. Two things follow from it and nothing else does: whether a browser may drive
|
|
115
|
-
* the route, and whether its body must declare JSON. A channel route is exempt from both because it checks its
|
|
116
|
-
* platform's signature inside itself — a Telegram webhook is public on purpose. Ours check nothing, so the router
|
|
117
|
-
* checks for them.
|
|
118
|
-
*
|
|
119
|
-
* TWO TABLES, not one table plus a list of which keys are which, so the three readers (browser reachability, the JSON
|
|
120
|
-
* body gate, the startup report) cannot answer differently.
|
|
121
|
-
*
|
|
122
|
-
* CAVEAT, stated because we cannot enforce it: a CUSTOM channel that verifies nothing lands in `selfVerifying`
|
|
123
|
-
* anyway and gets neither guard. That is decision B (docs/design/session-control.md §14) and it belongs to the
|
|
124
|
-
* channel's author, who owns the credential the platform issued — but it is an assumption here, not a property.
|
|
125
|
-
*/
|
|
126
|
-
export function router(surface) {
|
|
127
|
-
const { unverified = {}, selfVerifying = {}, mounts = [], corsOrigins = [] } = surface;
|
|
128
|
-
for (const [i, mount] of mounts.entries()) {
|
|
129
|
-
assertRouteKey(mount.prefix, (problem) => `mount prefix "${mount.prefix}" is invalid — ${problem}`);
|
|
130
|
-
if (mount.prefix === "/") {
|
|
131
|
-
throw new Error(`mount prefix "/" is invalid — a handler owning every path IS that handler; serve it directly`);
|
|
132
|
-
}
|
|
133
|
-
if (mount.prefix.endsWith("/")) {
|
|
134
|
-
throw new Error(`mount prefix "${mount.prefix}" is invalid — no trailing slash (write "/control")`);
|
|
135
|
-
}
|
|
136
|
-
const clash = mounts
|
|
137
|
-
.slice(0, i)
|
|
138
|
-
.find((other) => pathUnderPrefix(mount.prefix, other.prefix) || pathUnderPrefix(other.prefix, mount.prefix));
|
|
139
|
-
if (clash) {
|
|
140
|
-
throw new Error(`mount "${mount.prefix}" overlaps "${clash.prefix}" — one of them would never receive a request`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const byKey = new Map();
|
|
144
|
-
const paths = new Set();
|
|
145
|
-
const unguarded = new Set();
|
|
146
|
-
for (const [key, handler] of [...Object.entries(unverified), ...Object.entries(selfVerifying)]) {
|
|
147
|
-
assertRouteKey(key, (problem) => `route "${key}" is not a valid route key — ${problem}`);
|
|
148
|
-
const { path } = parseRouteKey(key);
|
|
149
|
-
for (const mount of mounts) {
|
|
150
|
-
if (pathUnderPrefix(path, mount.prefix)) {
|
|
151
|
-
throw new Error(`route "${key}" is inside the mount "${mount.prefix}" — it would never receive a request`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const shadowed = [...byKey.keys()].find((other) => routeKeysConflict(other, key));
|
|
155
|
-
if (shadowed) {
|
|
156
|
-
throw new Error(`route "${key}" conflicts with "${shadowed}" — one of them would never receive a request`);
|
|
157
|
-
}
|
|
158
|
-
// Stored normalised: `parseRouteKey` upper-cases the method, so `"get /x"` validates under `GET` and would
|
|
159
|
-
// otherwise be looked up under a name nothing stores.
|
|
160
|
-
const { method } = parseRouteKey(key);
|
|
161
|
-
const normalised = method ? `${method} ${path}` : path;
|
|
162
|
-
byKey.set(normalised, handler);
|
|
163
|
-
paths.add(path);
|
|
164
|
-
if (key in unverified)
|
|
165
|
-
unguarded.add(normalised);
|
|
166
|
-
}
|
|
167
|
-
/** Does this route authenticate nobody? Every mount does (the control plane), plus what we registered ourselves. */
|
|
168
|
-
const authenticatesNobody = (method, path) => mounts.some((mount) => pathUnderPrefix(path, mount.prefix)) ||
|
|
169
|
-
unguarded.has(`${method} ${path}`) ||
|
|
170
|
-
unguarded.has(path) ||
|
|
171
|
-
// The router answers HEAD from a GET route, so the question follows it.
|
|
172
|
-
(method === "HEAD" && unguarded.has(`GET ${path}`));
|
|
173
|
-
return (req) => {
|
|
174
|
-
// `URL` normalises the path (`/a/../x` → `/x`) and drops query/fragment.
|
|
175
|
-
const path = new URL(req.url).pathname;
|
|
176
|
-
const origin = req.headers.get("origin");
|
|
177
|
-
// A preflight asks ABOUT a method; ownership is that method's, not `OPTIONS`'s (nothing registers OPTIONS).
|
|
178
|
-
const asking = req.method === "OPTIONS"
|
|
179
|
-
? (req.headers.get("access-control-request-method")?.toUpperCase() ?? "OPTIONS")
|
|
180
|
-
: req.method;
|
|
181
|
-
// A BROWSER request against a route that authenticates nobody. A non-browser client sends no `Origin` and is
|
|
182
|
-
// untouched by any of this.
|
|
183
|
-
const owned = authenticatesNobody(asking, path);
|
|
184
|
-
const fromPage = origin !== null && owned;
|
|
185
|
-
// Decided BEFORE dispatch, so a 404, a 405 and a handler's own reply all leave with the same verdict — a browser
|
|
186
|
-
// that cannot read the 405 gets an opaque network error instead of the reason.
|
|
187
|
-
const cors = fromPage ? allowedOrigin(origin, corsOrigins) : undefined;
|
|
188
|
-
const answer = () => {
|
|
189
|
-
// The gate that makes "an origin we do not allow is simply not answered" safe: a POST carrying `text/plain`
|
|
190
|
-
// is a CORS simple request, sent with no preflight at all, so a route that authenticates nobody must refuse
|
|
191
|
-
// the body itself. HERE, over the whole unguarded surface, rather than inside each handler — the next route
|
|
192
|
-
// we add is covered by existing. A self-verifying channel is exempt: Slack posts urlencoded, and a page
|
|
193
|
-
// cannot forge its signature anyway.
|
|
194
|
-
if (owned && BODY_METHODS.has(req.method)) {
|
|
195
|
-
const wrongType = refuseNonJsonBody(req);
|
|
196
|
-
if (wrongType)
|
|
197
|
-
return wrongType;
|
|
198
|
-
}
|
|
199
|
-
// The preflight is the ROUTER's to answer: it names a method the route table does not register, so leaving it
|
|
200
|
-
// to the routes below is a 405 with no CORS headers, which is a browser client that cannot call a route that
|
|
201
|
-
// works.
|
|
202
|
-
if (req.method === "OPTIONS" && cors !== undefined)
|
|
203
|
-
return new Response(null, { status: 204 });
|
|
204
|
-
for (const mount of mounts)
|
|
205
|
-
if (pathUnderPrefix(path, mount.prefix))
|
|
206
|
-
return mount.handler(req);
|
|
207
|
-
const exact = byKey.get(`${req.method} ${path}`) ?? byKey.get(path);
|
|
208
|
-
if (exact)
|
|
209
|
-
return exact(req);
|
|
210
|
-
if (req.method === "HEAD") {
|
|
211
|
-
const get = byKey.get(`GET ${path}`);
|
|
212
|
-
if (get)
|
|
213
|
-
return get(req);
|
|
214
|
-
}
|
|
215
|
-
return paths.has(path) ? text("method not allowed\n", 405) : text("not found\n", 404);
|
|
216
|
-
};
|
|
217
|
-
/**
|
|
218
|
-
* ONE exit, so the HEAD rule and the CORS verdict hold for every reply — a mount's, a route's, the GET
|
|
219
|
-
* fallback's, and the 404/405 this router writes itself.
|
|
220
|
-
*/
|
|
221
|
-
const finish = (res) => {
|
|
222
|
-
const out = req.method === "HEAD" ? withoutBody(res) : res;
|
|
223
|
-
// `vary` whether or not the origin was allowed: the answer DEPENDS on the request's origin either way, and a
|
|
224
|
-
// cache that does not know it would serve one caller's verdict to another.
|
|
225
|
-
if (owned)
|
|
226
|
-
out.headers.append("vary", "origin");
|
|
227
|
-
if (cors === undefined)
|
|
228
|
-
return out;
|
|
229
|
-
out.headers.set("access-control-allow-origin", cors);
|
|
230
|
-
// Echoed for the same reason the methods are: what a caller needs is not ours to enumerate. A gateway in front
|
|
231
|
-
// of this port demands its own header (`connectSessionControl({ fetchFn })` is how a client sends one), and a
|
|
232
|
-
// fixed list turns that preflight into a 204 the browser then refuses to act on. The default names the two a
|
|
233
|
-
// client of ours always needs, for a preflight that asked about nothing.
|
|
234
|
-
out.headers.set("access-control-allow-headers", req.headers.get("access-control-request-headers") ?? "authorization, content-type");
|
|
235
|
-
// The method the preflight ASKED about, not a table lookup: a mount owns a prefix and does not publish which
|
|
236
|
-
// methods each path under it serves. Naming one the path does not serve costs nothing now that the 405 it
|
|
237
|
-
// produces carries these same headers.
|
|
238
|
-
const asked = req.headers.get("access-control-request-method")?.toUpperCase();
|
|
239
|
-
out.headers.set("access-control-allow-methods", [...new Set([asked, "OPTIONS"].filter(Boolean))].join(", "));
|
|
240
|
-
return out;
|
|
241
|
-
};
|
|
242
|
-
const res = answer();
|
|
243
|
-
return res instanceof Promise ? res.then(finish) : finish(res);
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
/** The totality boundary every serving path shares. */
|
|
247
|
-
function totalFetch(handler) {
|
|
12
|
+
/** The totality boundary every serving path shares; `prepare` shapes the handler's response inside it. */
|
|
13
|
+
export function totalFetch(handler, prepare = (response) => response) {
|
|
248
14
|
return async (req) => {
|
|
249
15
|
try {
|
|
250
16
|
const response = await handler(req);
|
|
251
17
|
if (!(response instanceof Response))
|
|
252
18
|
throw new TypeError("handler did not return a Response");
|
|
253
|
-
return response;
|
|
19
|
+
return prepare(response);
|
|
254
20
|
}
|
|
255
21
|
catch (error) {
|
|
256
22
|
log.error(`[serve] request failed: ${String(error)}`);
|
|
@@ -258,50 +24,102 @@ function totalFetch(handler) {
|
|
|
258
24
|
}
|
|
259
25
|
};
|
|
260
26
|
}
|
|
261
|
-
/** Has upstream middleware already drained the request body? */
|
|
262
|
-
function bodyAlreadyRead(req) {
|
|
263
|
-
const length = Number(req.headers["content-length"]);
|
|
264
|
-
return Number.isFinite(length) && length > 0 && req.readableEnded;
|
|
265
|
-
}
|
|
266
27
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
28
|
+
* A response whose body fails mid-flight, said in the log. Once the headers are out the failure can only cut the body
|
|
29
|
+
* short, and the server this binds to reports nothing of it; the client sees a truncated body and nothing more. A
|
|
30
|
+
* client that leaves is not a failure: the server cancels the body, which ends the read in progress and calls `left`.
|
|
269
31
|
*/
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
32
|
+
function watchedBody(response, left) {
|
|
33
|
+
if (response.body === null)
|
|
34
|
+
return response;
|
|
35
|
+
const reader = response.body.getReader();
|
|
36
|
+
const body = new ReadableStream({
|
|
37
|
+
async pull(controller) {
|
|
38
|
+
let next;
|
|
39
|
+
try {
|
|
40
|
+
next = await reader.read();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
log.error(`[serve] a response body failed mid-flight: ${String(error)}`);
|
|
44
|
+
controller.error(error);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Outside the try: when the client left, the read in progress ends `done` on a body already cancelled, and
|
|
48
|
+
// closing it throws; that is not the body failing.
|
|
49
|
+
if (next.done)
|
|
50
|
+
controller.close();
|
|
51
|
+
else
|
|
52
|
+
controller.enqueue(next.value);
|
|
53
|
+
},
|
|
54
|
+
cancel(reason) {
|
|
55
|
+
left();
|
|
56
|
+
return reader.cancel(reason);
|
|
57
|
+
},
|
|
290
58
|
});
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
59
|
+
return new Response(body, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
60
|
+
}
|
|
61
|
+
const isBun = typeof process.versions.bun === "string";
|
|
62
|
+
/**
|
|
63
|
+
* How long `close()` gives the requests in flight before it ends them, on either runtime: a webhook is answered in
|
|
64
|
+
* milliseconds, and a close must fit the CLI's shutdown deadline (`FORCED_EXIT_MS` in `src/cli/serve.ts`).
|
|
65
|
+
*/
|
|
66
|
+
const GRACEFUL_SHUTDOWN = "500 millis";
|
|
67
|
+
/**
|
|
68
|
+
* How long Bun lets a response say nothing before it ends it (10 s by default): longer than an Agent API request waits
|
|
69
|
+
* for a message, and than an event stream's keep-alive interval. Node sets no such limit.
|
|
70
|
+
*/
|
|
71
|
+
const BUN_IDLE_TIMEOUT_SECONDS = LIMITS.httpWaitSeconds + 15;
|
|
72
|
+
/** The server this runtime serves on: Node's `http.Server`, or `Bun.serve`. */
|
|
73
|
+
async function serverLayer(options) {
|
|
74
|
+
if (isBun) {
|
|
75
|
+
const { layer } = await import("@effect/platform-bun/BunHttpServer");
|
|
76
|
+
return layer({
|
|
77
|
+
port: options.port,
|
|
78
|
+
...(options.host === undefined ? {} : { hostname: options.host }),
|
|
79
|
+
gracefulShutdownTimeout: GRACEFUL_SHUTDOWN,
|
|
80
|
+
idleTimeout: BUN_IDLE_TIMEOUT_SECONDS,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const { layer } = await import("@effect/platform-node/NodeHttpServer");
|
|
84
|
+
return layer(() => createServer(), {
|
|
295
85
|
port: options.port,
|
|
296
|
-
...(options.host
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
86
|
+
...(options.host === undefined ? {} : { host: options.host }),
|
|
87
|
+
gracefulShutdownTimeout: GRACEFUL_SHUTDOWN,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** A bind that failed, as the error its runtime raised (`EADDRINUSE` and the like), not Effect's wrapper of it. */
|
|
91
|
+
function bindFailure(error) {
|
|
92
|
+
return error instanceof HttpServerError.ServeError ? error.cause : error;
|
|
93
|
+
}
|
|
94
|
+
/** Serve `handler` on this runtime's own server: Node's `http.Server`, or `Bun.serve`. */
|
|
95
|
+
export function serveHandler(handler, options) {
|
|
96
|
+
const scope = Scope.makeUnsafe();
|
|
97
|
+
let closed = false;
|
|
98
|
+
// A server runs each request uninterruptibly unless told otherwise; a request that has not answered when the server
|
|
99
|
+
// closes must be interruptible, or closing waits on it forever.
|
|
100
|
+
const app = Effect.interruptible(HttpEffect.fromWebHandler((request) => {
|
|
101
|
+
// The handler's `signal` follows its client to the end of the response, as a Node server's request does: this
|
|
102
|
+
// server aborts it only while the handler runs, so a body cancelled because the client left aborts it too.
|
|
103
|
+
const left = new AbortController();
|
|
104
|
+
const followed = new Request(request, { signal: AbortSignal.any([request.signal, left.signal]) });
|
|
105
|
+
return totalFetch(handler, (response) => watchedBody(response, () => left.abort()))(followed);
|
|
106
|
+
}));
|
|
107
|
+
const listening = serverLayer(options)
|
|
108
|
+
.then((server) => {
|
|
109
|
+
if (closed)
|
|
110
|
+
throw new Error("the server was closed before it listened");
|
|
111
|
+
return Effect.runPromise(Layer.buildWithScope(HttpServer.serve(app).pipe(Layer.provideMerge(server)), scope));
|
|
112
|
+
})
|
|
113
|
+
.then((context) => Context.get(context, HttpServer.HttpServer).address.port, (error) => {
|
|
114
|
+
throw bindFailure(error);
|
|
302
115
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
116
|
+
return {
|
|
117
|
+
listening,
|
|
118
|
+
async close() {
|
|
119
|
+
closed = true;
|
|
120
|
+
// A bind in progress finishes first, so what it bound is released here; how it ended is `listening`'s to say.
|
|
121
|
+
await listening.then(() => undefined, () => undefined);
|
|
122
|
+
await Effect.runPromise(Scope.close(scope, Exit.void));
|
|
123
|
+
},
|
|
124
|
+
};
|
|
307
125
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type PlaceDiscussion, type PlaceHistory, type PlaceMessage, type PlaceRead } from "../kit/place-history.ts";
|
|
2
|
+
import { type SlackApi } from "./slack-api.ts";
|
|
3
|
+
export type SlackPlaceRead = PlaceRead<string>;
|
|
4
|
+
export type SlackDiscussion = PlaceDiscussion<string>;
|
|
5
|
+
export interface SlackPlaceHistory extends PlaceHistory<string> {
|
|
6
|
+
/** The channel's threads among its newest messages, for a tool (`kit/room-threads.ts`); `room` is its place key. */
|
|
7
|
+
threads(room: string): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
/** A background file carried into a turn, with attribution for its prompt manifest. */
|
|
10
|
+
export interface SlackBufferedFileRef {
|
|
11
|
+
id: string;
|
|
12
|
+
from: string;
|
|
13
|
+
messageId: string;
|
|
14
|
+
}
|
|
15
|
+
/** A place: a channel's top level, or a thread in it. The turn record carries it as this key. */
|
|
16
|
+
export declare function slackHistoryKey(teamId: string, place: {
|
|
17
|
+
channelId: string;
|
|
18
|
+
threadTs?: string;
|
|
19
|
+
}): string;
|
|
20
|
+
/** The inverse of {@link slackHistoryKey}. Slack ids and timestamps never contain a colon. */
|
|
21
|
+
export declare function slackPlaceOf(key: string): {
|
|
22
|
+
teamId: string;
|
|
23
|
+
channelId: string;
|
|
24
|
+
threadTs?: string;
|
|
25
|
+
};
|
|
26
|
+
/** Select the most recent background files of the folded messages, excluding the turn's own. */
|
|
27
|
+
export declare function collectFoldedFiles(folded: readonly PlaceMessage[], primaryIds: ReadonlySet<string>): {
|
|
28
|
+
files: SlackBufferedFileRef[];
|
|
29
|
+
skipped: number;
|
|
30
|
+
};
|
|
31
|
+
export declare function createSlackPlaceHistory(deps: {
|
|
32
|
+
api: Pick<SlackApi, "channelHistory" | "threadReplies" | "userName">;
|
|
33
|
+
/** THIS app's bot user and bot ids, once `auth.test` answered: a message from either is the agent's own. */
|
|
34
|
+
self(): {
|
|
35
|
+
userId?: string;
|
|
36
|
+
botId?: string;
|
|
37
|
+
};
|
|
38
|
+
label: string;
|
|
39
|
+
/** Where each place's cursor and outputs persist. */
|
|
40
|
+
path: string;
|
|
41
|
+
isTurnInput(key: string, ts: string): boolean;
|
|
42
|
+
}): SlackPlaceHistory;
|