@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
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { fromForwarder } from "./agentcore-protocol.js";
|
|
2
2
|
import { log } from "../log.js";
|
|
3
|
-
import {
|
|
3
|
+
import { routesFor, serviceOver, startSchedules, } from "../service.js";
|
|
4
4
|
import { agentcoreRoutes, agentcorePing } from "./agentcore.js";
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { fireScheduleOnce } from "../schedule/scheduler.js";
|
|
7
|
+
import { nextRun } from "../schedule/cron.js";
|
|
8
|
+
import { createWakeAlarmSink } from "../schedule/wake-alarm.js";
|
|
9
|
+
import { setWakeupsSink } from "../schedule/wakeups.js";
|
|
8
10
|
import { text } from "./respond.js";
|
|
9
11
|
import { activeWork, beginWork } from "./busy.js";
|
|
10
|
-
import { router } from "./
|
|
12
|
+
import { router } from "./router.js";
|
|
11
13
|
import { readBodyCapped } from "./body.js";
|
|
12
14
|
import { MAX_ENVELOPE_BYTES } from "./agentcore-limits.js";
|
|
13
15
|
/**
|
|
@@ -29,7 +31,7 @@ export function deferAgentcoreService(stages) {
|
|
|
29
31
|
throw error;
|
|
30
32
|
});
|
|
31
33
|
const taken = await prepared;
|
|
32
|
-
// Shutdown may have arrived while the
|
|
34
|
+
// Shutdown may have arrived while the storage was being taken.
|
|
33
35
|
if (closed)
|
|
34
36
|
throw new Error("service closed during initialization");
|
|
35
37
|
assembling ??= stages.assemble(taken);
|
|
@@ -40,128 +42,92 @@ export function deferAgentcoreService(stages) {
|
|
|
40
42
|
done();
|
|
41
43
|
}
|
|
42
44
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return Response.json({ ok: false, error: message });
|
|
73
|
-
}
|
|
45
|
+
const handler = async (request) => {
|
|
46
|
+
if (closed)
|
|
47
|
+
return new Response("service closed\n", { status: 503 });
|
|
48
|
+
if (service)
|
|
49
|
+
return service.fetch(request);
|
|
50
|
+
const path = new URL(request.url).pathname;
|
|
51
|
+
if (path === "/ping" && request.method === "GET")
|
|
52
|
+
return ping(request);
|
|
53
|
+
if (path !== "/invocations" || request.method !== "POST")
|
|
54
|
+
return new Response("not found\n", { status: 404 });
|
|
55
|
+
let ready;
|
|
56
|
+
// Only the INITIALIZATION is caught here.
|
|
57
|
+
try {
|
|
58
|
+
ready = await initialize();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = `initialization failed: ${String(error)}`;
|
|
62
|
+
log.error(`[agentcore] ${message}`);
|
|
63
|
+
const body = await readBodyCapped(request, MAX_ENVELOPE_BYTES);
|
|
64
|
+
if (!("tooLarge" in body)) {
|
|
65
|
+
let envelope;
|
|
66
|
+
try {
|
|
67
|
+
envelope = JSON.parse(body.text);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Invalid envelopes retain the initialization error.
|
|
71
|
+
}
|
|
72
|
+
if (envelope?.kind === "probe" && fromForwarder(envelope, process.env.FASTAGENT_INGRESS_SECRET)) {
|
|
73
|
+
return Response.json({ ok: false, error: message });
|
|
74
74
|
}
|
|
75
|
-
return new Response(`${message}\n`, { status: 503 });
|
|
76
75
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
closed = true;
|
|
83
|
-
closing ??= (async () => {
|
|
84
|
-
// A failed assembly already reached its caller.
|
|
85
|
-
await (await assembling?.catch(() => undefined))?.close();
|
|
86
|
-
})();
|
|
87
|
-
return closing;
|
|
88
|
-
},
|
|
76
|
+
return new Response(`${message}\n`, { status: 503 });
|
|
77
|
+
}
|
|
78
|
+
if (closed)
|
|
79
|
+
return new Response("service closed\n", { status: 503 });
|
|
80
|
+
return ready.fetch(request);
|
|
89
81
|
};
|
|
82
|
+
// Nothing to wait for before the first envelope: the definition opens on it.
|
|
83
|
+
return serviceOver(handler, Promise.resolve(), () => {
|
|
84
|
+
closed = true;
|
|
85
|
+
closing ??= (async () => {
|
|
86
|
+
// A failed assembly already reached its caller.
|
|
87
|
+
await (await assembling?.catch(() => undefined))?.close();
|
|
88
|
+
})();
|
|
89
|
+
return closing;
|
|
90
|
+
});
|
|
90
91
|
}
|
|
91
|
-
export async function mountAgentcoreService(opened
|
|
92
|
-
const {
|
|
93
|
-
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
// ingress secret ITSELF (deploy/agentcore/forwarder.js), so every anonymous caller arrives as trusted ingress. A
|
|
97
|
-
// channel route survives that because it checks its platform's signature; `/control/*` has no such check.
|
|
98
|
-
//
|
|
99
|
-
// The recipe if it is ever wanted: a `kind: "control"` envelope on the IAM-gated `InvokeAgentRuntime`, which the
|
|
100
|
-
// forwarder never emits (the way `kind: "invoke"` runs a turn without the ingress secret). The envelope is
|
|
101
|
-
// request/response with a buffered body, so the long-lived `GET /control/sessions/{id}/events` stream a client
|
|
102
|
-
// renders from could not ride it.
|
|
92
|
+
export async function mountAgentcoreService(opened) {
|
|
93
|
+
const { agent, agentDir, stateRoot, schedules } = opened;
|
|
94
|
+
// The only public way in is the forwarder's Function URL, which relays an arbitrary `rawPath` verbatim as a webhook
|
|
95
|
+
// envelope and attaches the ingress secret ITSELF (deploy/agentcore/forwarder.js), so every anonymous caller arrives
|
|
96
|
+
// as trusted ingress. A channel route survives that because it checks its platform's signature.
|
|
103
97
|
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
if (opened.http?.invoke !== undefined) {
|
|
111
|
-
log.warn("[fastagent] agentcore: http.invoke has no effect here — this host serves the Runtime's POST /invocations " +
|
|
112
|
-
"contract instead of our own /invoke, and reaching it already requires bedrock-agentcore:InvokeAgentRuntime.");
|
|
113
|
-
}
|
|
114
|
-
if (opened.http?.run !== undefined) {
|
|
115
|
-
log.warn("[fastagent] agentcore: http.run has no effect here — routines fire through the forwarder's " +
|
|
116
|
-
"routine-fire envelope, which is gated by the ingress secret rather than served as an anonymous route.");
|
|
117
|
-
}
|
|
118
|
-
if (opened.publishControl) {
|
|
119
|
-
log.warn("[fastagent] agentcore: sessionControl is ON but /control/* is NOT served here — this host's only public " +
|
|
120
|
-
"ingress relays anonymous traffic as trusted, so the plane is not assembled behind it " +
|
|
121
|
-
"(docs/design/session-control.md §14)");
|
|
122
|
-
}
|
|
123
|
-
// Started here, not deferred to an envelope.
|
|
124
|
-
const routines = await loadServingRoutines(agentDir);
|
|
125
|
-
const scheduled = startSchedules(agent, stateRoot, routines, { externalClock: true });
|
|
98
|
+
// Started here, not deferred to an envelope. No resident timers: each instant arrives as its own fire from the
|
|
99
|
+
// recurring schedule the container sets (`armAlarms`), ONE path per instant, so the reply to that delivery says
|
|
100
|
+
// whether it ran. The clock still pumps wake-ups; a redelivered fire claims its slot once.
|
|
101
|
+
const mirror = armAlarms(stateRoot, () => schedules);
|
|
102
|
+
const scheduled = startSchedules(agent, stateRoot, schedules, { localClock: false });
|
|
126
103
|
const lazyChannels = async () => {
|
|
127
|
-
const lazy =
|
|
104
|
+
const lazy = routesFor(agent, stateRoot, opened.channels);
|
|
128
105
|
if (lazy.longConnections.length > 0) {
|
|
129
106
|
throw new Error(`long-connection channel(s) ${lazy.longConnections.map((c) => c.name).join(", ")} cannot serve on ` +
|
|
130
107
|
`AgentCore (scale-to-zero severs resident connections) — use the channel's webhook form`);
|
|
131
108
|
}
|
|
132
|
-
|
|
133
|
-
// with the control plane, for the same reason: what arrives through that URL is anonymous.
|
|
134
|
-
return { routes: lazy.selfVerifying };
|
|
109
|
+
return { routes: lazy.routes };
|
|
135
110
|
};
|
|
136
111
|
const adapterRoutes = mountAgentcore({
|
|
137
112
|
agent,
|
|
138
113
|
stateRoot,
|
|
139
|
-
|
|
140
|
-
onStateReady:
|
|
114
|
+
schedules: () => schedules,
|
|
115
|
+
...(mirror ? { onStateReady: mirror } : {}),
|
|
141
116
|
channels: lazyChannels,
|
|
142
117
|
});
|
|
143
|
-
|
|
144
|
-
// IAM. Putting them in the other table applied our JSON body gate to `POST /invocations` (whose content type is
|
|
145
|
-
// AWS's to set) and reported two IAM-protected paths as authenticating nobody.
|
|
146
|
-
const handler = router({ selfVerifying: adapterRoutes });
|
|
118
|
+
const handler = router(adapterRoutes);
|
|
147
119
|
log.info(`[fastagent] agentcore: serving POST /invocations + GET /ping (FASTAGENT_AGENTCORE=1)`);
|
|
148
120
|
return {
|
|
149
|
-
|
|
150
|
-
|
|
121
|
+
// Nothing to open: no port of its own, no resident connections.
|
|
122
|
+
...serviceOver(handler, Promise.resolve(), async () => scheduled.stop()),
|
|
151
123
|
// The adapter IS the surface here; the channel routes arrive lazily BEHIND it.
|
|
152
124
|
routes: adapterRoutes,
|
|
153
125
|
agentDir,
|
|
154
|
-
workspace,
|
|
155
126
|
// Channels remain lazy until the adapter receives trusted ingress.
|
|
156
127
|
channels: { routes: [], longConnections: [] },
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
routines: scheduled.routines,
|
|
161
|
-
ready: Promise.resolve(), // nothing to open: no port of our own, no resident connections
|
|
162
|
-
async close() {
|
|
163
|
-
scheduled.stop();
|
|
164
|
-
},
|
|
128
|
+
schedules,
|
|
129
|
+
// Reached only through /invocations here (SPEC.md §9.5).
|
|
130
|
+
api: false,
|
|
165
131
|
};
|
|
166
132
|
}
|
|
167
133
|
/**
|
|
@@ -169,43 +135,58 @@ export async function mountAgentcoreService(opened, options = {}) {
|
|
|
169
135
|
* surface (channels/agentcore.ts).
|
|
170
136
|
*/
|
|
171
137
|
export function mountAgentcore(options) {
|
|
172
|
-
const { agent, stateRoot,
|
|
173
|
-
// OCCURRENCE SEMANTICS, because this host's clock is ours:
|
|
174
|
-
// relays
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
138
|
+
const { agent, stateRoot, schedules, onStateReady, channels } = options;
|
|
139
|
+
// OCCURRENCE SEMANTICS, because this host's clock is ours: the container set the recurring schedule that names this
|
|
140
|
+
// instant and the forwarder relays it behind the ingress secret, so a claim means something (dedup across
|
|
141
|
+
// EventBridge's redeliveries, a fire history, the overlap policy).
|
|
142
|
+
const fireSchedule = async (name, occurrence) => {
|
|
143
|
+
const schedule = schedules().find((s) => s.name === name);
|
|
144
|
+
// EventBridge can fire what a release has not reached yet (a mirror still in flight, or one that failed): a fire
|
|
145
|
+
// for a schedule since removed, or for an instant its cron no longer has, is answered as skipped, not run — and not
|
|
146
|
+
// as an error the forwarder would make EventBridge retry.
|
|
147
|
+
const skip = (why) => {
|
|
148
|
+
log.info(`[schedule] ${name}: alarm for ${occurrence.toISOString()} skipped — ${why}`);
|
|
149
|
+
return Response.json({ slot: occurrence.toISOString(), fired: false, skippedReason: why, ms: 0 });
|
|
150
|
+
};
|
|
151
|
+
if (!schedule)
|
|
152
|
+
return skip("no such schedule any more");
|
|
153
|
+
if (nextRun(schedule.cron, schedule.tz, new Date(occurrence.getTime() - 1))?.getTime() !== occurrence.getTime()) {
|
|
154
|
+
return skip(`not an instant of its cron "${schedule.cron}" any more`);
|
|
155
|
+
}
|
|
156
|
+
const outcome = await Effect.runPromise(fireScheduleOnce({ agent, stateRoot, schedule, slot: occurrence }).pipe(Effect.mapError((e) => e.cause))).catch((cause) => {
|
|
157
|
+
// `fireScheduleOnce`'s only failure is a claim-state fault, which happens BEFORE any claim exists — the
|
|
183
158
|
// occurrence is unburned, so the forwarder's throw makes EventBridge retry it, which is the right answer.
|
|
184
159
|
log.error(`[schedule] firing "${name}" for ${occurrence.toISOString()} failed: ${String(cause)}`);
|
|
185
160
|
return undefined;
|
|
186
161
|
});
|
|
187
162
|
if (outcome === undefined) {
|
|
188
|
-
return text(`
|
|
163
|
+
return text(`schedule "${name}": claim state unavailable, nothing was claimed — retry\n`, 500);
|
|
189
164
|
}
|
|
190
165
|
return Response.json({ slot: occurrence.toISOString(), ...outcome });
|
|
191
166
|
};
|
|
192
|
-
// BY NAME, on the IAM door. `POST /run`'s contract, through the only ingress this host has for a caller AWS has
|
|
193
|
-
// already identified — which makes it stricter here than the anonymous route other hosts publish. A routine
|
|
194
|
-
// without a cron is reachable ONLY this way on this host, and that is the point: nothing is unreachable.
|
|
195
|
-
const runRoutine = async (name) => {
|
|
196
|
-
const routine = routines.find((r) => r.name === name);
|
|
197
|
-
if (!routine)
|
|
198
|
-
return unknownRoutine(name, routines);
|
|
199
|
-
return Response.json(await runRoutineByName(agent, routine));
|
|
200
|
-
};
|
|
201
167
|
return agentcoreRoutes({
|
|
202
168
|
channels,
|
|
203
|
-
agent,
|
|
204
169
|
stateRoot,
|
|
205
170
|
isBusy: () => activeWork() > 0,
|
|
206
171
|
// What separates a forwarder envelope from any IAM principal's InvokeAgentRuntime call.
|
|
207
172
|
ingressSecret: process.env.FASTAGENT_INGRESS_SECRET,
|
|
208
173
|
onStateReady,
|
|
209
|
-
|
|
174
|
+
fireSchedule,
|
|
210
175
|
});
|
|
211
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* The alarm mirror: each schedule as a recurring EventBridge schedule and each pending wake-up as a one-shot one, set
|
|
179
|
+
* by the forwarder (schedule/wake-alarm.ts), so a container AgentCore reclaimed is woken for them. Installed BEFORE the
|
|
180
|
+
* clock starts: the first wake poll may advance a recurring entry, and that save must already re-arm its alarm.
|
|
181
|
+
* Without the wake secret there is no way to set one, which is said.
|
|
182
|
+
*/
|
|
183
|
+
function armAlarms(stateRoot, schedules) {
|
|
184
|
+
const secret = process.env.FASTAGENT_WAKE_SECRET;
|
|
185
|
+
if (!secret) {
|
|
186
|
+
log.warn("[fastagent] FASTAGENT_WAKE_SECRET is missing; wake-ups and schedules cannot wake a reclaimed container");
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
const sink = Effect.runSync(createWakeAlarmSink({ secret, schedules }));
|
|
190
|
+
setWakeupsSink(sink);
|
|
191
|
+
return () => sink(stateRoot);
|
|
192
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Agent } from "../agent.ts";
|
|
2
1
|
import type { ChannelHandler, Routes } from "../channel.ts";
|
|
3
2
|
/**
|
|
4
3
|
* What the lazy factory hands back: the CHANNELS' routes, and nothing else.
|
|
@@ -8,10 +7,8 @@ import type { ChannelHandler, Routes } from "../channel.ts";
|
|
|
8
7
|
* so behind THIS door an anonymous caller and an IAM one are the same thing. A channel route survives that because
|
|
9
8
|
* it verifies the platform's signature inside itself; nothing fastagent serves does.
|
|
10
9
|
*
|
|
11
|
-
* The other door is the Runtime's own, IAM-gated:
|
|
12
|
-
* and
|
|
13
|
-
* `kind: "invoke"` run a turn here with no ingress secret, and it is where anything else that needs a real caller
|
|
14
|
-
* identity belongs.
|
|
10
|
+
* The other door is the Runtime's own, IAM-gated: a direct `InvokeAgentRuntime` call, which carries no ingress
|
|
11
|
+
* secret, and which this container answers only with a `probe`.
|
|
15
12
|
*/
|
|
16
13
|
export interface RouteSurface {
|
|
17
14
|
routes: Routes;
|
|
@@ -19,32 +16,19 @@ export interface RouteSurface {
|
|
|
19
16
|
export interface AgentcoreAdapterOptions {
|
|
20
17
|
/** The channel surface. */
|
|
21
18
|
channels: () => Promise<RouteSurface> | RouteSurface;
|
|
22
|
-
agent: Agent;
|
|
23
19
|
/** Where the forwarder URL from envelopes is persisted for the wake-alarm sink (the state root). */
|
|
24
20
|
stateRoot: string;
|
|
25
21
|
/** Process-wide background-work signal (busy.ts `activeWork() > 0`) — injected for tests. */
|
|
26
22
|
isBusy: () => boolean;
|
|
27
23
|
/**
|
|
28
|
-
* Fire ONE declared schedule for the occurrence EventBridge named — the claim/run/settle the resident clock uses
|
|
29
|
-
* not the `POST /run` API.
|
|
24
|
+
* Fire ONE declared schedule for the occurrence EventBridge named — the claim/run/settle the resident clock uses.
|
|
30
25
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* history and the overlap policy) mean anything. `POST /run` has none of that — it is an unauthenticated API
|
|
36
|
-
* whose caller cannot be told which occurrence it means, so it does not pretend to (schedule/run.ts).
|
|
37
|
-
*
|
|
38
|
-
* Undefined when the definition declares no schedules.
|
|
39
|
-
*/
|
|
40
|
-
fireRoutine?: (name: string, occurrence: Date) => Promise<Response>;
|
|
41
|
-
/**
|
|
42
|
-
* Run a declared routine BY NAME — the `POST /run` contract, for the host that publishes no routes.
|
|
43
|
-
*
|
|
44
|
-
* Undefined when the definition declares none. Note what this does NOT do: no occurrence, no claim, no fire
|
|
45
|
-
* history. Those belong to {@link fireRoutine}, whose caller is a clock we wrote.
|
|
26
|
+
* The container set the recurring schedule that names this instant and the forwarder relays it behind the ingress
|
|
27
|
+
* secret, so this caller is one we produced and authenticated, which is what makes a claim (and therefore dedup
|
|
28
|
+
* across EventBridge's redeliveries, a fire history and the overlap policy) mean anything. Always present: a
|
|
29
|
+
* schedule can be added while the container runs, and a fire for one since removed is answered by it, as skipped.
|
|
46
30
|
*/
|
|
47
|
-
|
|
31
|
+
fireSchedule: (name: string, occurrence: Date) => Promise<Response>;
|
|
48
32
|
/** FASTAGENT_INGRESS_SECRET: what makes an envelope the FORWARDER's rather than any IAM principal's. */
|
|
49
33
|
ingressSecret?: string;
|
|
50
34
|
/** Runs once on activation, after accepting the current forwarder callback URL. */
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/** AWS AgentCore Runtime adapter: serve
|
|
1
|
+
/** AWS AgentCore Runtime adapter: serve the channels, the schedules' fires and the wake pokes through the Runtime's service contract. */
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import { PortFailure, portJoin } from "../effect-port.js";
|
|
5
|
-
import { ENVELOPE_KINDS, fromForwarder } from "./agentcore-protocol.js";
|
|
5
|
+
import { ENVELOPE_KINDS, fromForwarder, } from "./agentcore-protocol.js";
|
|
6
6
|
import { beginWork } from "./busy.js";
|
|
7
|
-
import { router } from "../channels/
|
|
7
|
+
import { router } from "../channels/router.js";
|
|
8
8
|
import { log } from "../log.js";
|
|
9
|
+
import { once } from "../once.js";
|
|
9
10
|
import { rememberWakeAlarmUrl } from "../schedule/wake-alarm.js";
|
|
10
11
|
import { readBodyCapped } from "./body.js";
|
|
11
|
-
import { createInvokeHandler } from "./http.js";
|
|
12
12
|
import { text } from "./respond.js";
|
|
13
13
|
import { MAX_ENVELOPE_BYTES, MAX_WEBHOOK_BODY_BYTES } from "./agentcore-limits.js";
|
|
14
14
|
const jsonHeaders = { "content-type": "application/json" };
|
|
@@ -18,18 +18,13 @@ const json = (body, status) => new Response(`${JSON.stringify(body)}\n`, { statu
|
|
|
18
18
|
*/
|
|
19
19
|
function createActivation(deps) {
|
|
20
20
|
const { stateRoot, onStateReady } = deps;
|
|
21
|
-
|
|
22
|
-
// interruption included. A once-per-process activation that remembered "interrupted" would answer
|
|
23
|
-
// every later envelope with an empty cause instead of the success or the diagnosable failure this
|
|
24
|
-
// block promises. Both are already unreachable (no caller passes a signal), which is exactly why
|
|
25
|
-
// the invariant has to be written down rather than relied on.
|
|
26
|
-
const stateReady = Effect.runSync(Effect.cached(portJoin(async () => {
|
|
21
|
+
const stateReady = once(portJoin(async () => {
|
|
27
22
|
onStateReady?.();
|
|
28
|
-
})
|
|
29
|
-
const channels =
|
|
23
|
+
}));
|
|
24
|
+
const channels = once(portJoin(async () => {
|
|
30
25
|
const surface = await deps.channels();
|
|
31
|
-
return router(
|
|
32
|
-
})
|
|
26
|
+
return router(surface.routes);
|
|
27
|
+
}));
|
|
33
28
|
return {
|
|
34
29
|
prepare: (envelope) => Effect.gen(function* () {
|
|
35
30
|
// Use the current callback URL rather than the one persisted by an earlier deployment.
|
|
@@ -46,9 +41,19 @@ function createActivation(deps) {
|
|
|
46
41
|
};
|
|
47
42
|
}
|
|
48
43
|
export function agentcoreRoutes(options) {
|
|
49
|
-
const { channels,
|
|
44
|
+
const { channels, stateRoot, isBusy, fireSchedule, ingressSecret, onStateReady } = options;
|
|
50
45
|
const activation = createActivation({ stateRoot, onStateReady, channels });
|
|
51
|
-
const
|
|
46
|
+
const carried = (request) => {
|
|
47
|
+
const { method, path, query, headers, bodyB64 } = request;
|
|
48
|
+
return new Request(`http://agentcore.local${path}${typeof query === "string" && query !== "" ? `?${query}` : ""}`, {
|
|
49
|
+
method,
|
|
50
|
+
headers: headers ?? {},
|
|
51
|
+
body: typeof bodyB64 === "string" && method !== "GET" && method !== "HEAD"
|
|
52
|
+
? Buffer.from(bodyB64, "base64")
|
|
53
|
+
: undefined,
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const malformed = (request) => typeof request.method !== "string" || typeof request.path !== "string" || !request.path.startsWith("/");
|
|
52
57
|
const handleInvocation = async (req) => {
|
|
53
58
|
const body = await readBodyCapped(req, MAX_ENVELOPE_BYTES);
|
|
54
59
|
if ("tooLarge" in body)
|
|
@@ -66,10 +71,10 @@ export function agentcoreRoutes(options) {
|
|
|
66
71
|
// The forwarder, or another IAM principal? See `fromForwarder` for what that actually decides.
|
|
67
72
|
const trusted = fromForwarder(envelope, ingressSecret);
|
|
68
73
|
if (!trusted) {
|
|
69
|
-
// The IAM door's
|
|
70
|
-
// here only opens the
|
|
71
|
-
// `login --deployment` gets a
|
|
72
|
-
if (envelope.kind !== "
|
|
74
|
+
// The IAM door's one kind: AWS authenticated this caller, so no ingress secret is expected or wanted. A `probe`
|
|
75
|
+
// here only opens the deployed definition (the deferred wrapper did that before this line) and says so: it is
|
|
76
|
+
// how `login --deployment` gets a definition to log in, after a reset, on a runtime nothing has invoked yet.
|
|
77
|
+
if (envelope.kind !== "probe") {
|
|
73
78
|
log.warn(`[agentcore] rejected an unauthenticated "${envelope.kind}" envelope`);
|
|
74
79
|
return text("forbidden\n", 403);
|
|
75
80
|
}
|
|
@@ -87,10 +92,9 @@ export function agentcoreRoutes(options) {
|
|
|
87
92
|
return json({ ok: false, error: `activation failed: ${String(e)}` }, 200);
|
|
88
93
|
return text(`activation failed: ${String(e)}\n`, 503);
|
|
89
94
|
}
|
|
90
|
-
// Public invokes do not activate channels.
|
|
91
95
|
let constructionError;
|
|
92
96
|
let dispatch;
|
|
93
|
-
if (trusted
|
|
97
|
+
if (trusted) {
|
|
94
98
|
try {
|
|
95
99
|
dispatch = await Effect.runPromise(activation.channels.pipe(Effect.mapError((error) => error.cause)));
|
|
96
100
|
}
|
|
@@ -101,10 +105,9 @@ export function agentcoreRoutes(options) {
|
|
|
101
105
|
}
|
|
102
106
|
switch (envelope.kind) {
|
|
103
107
|
case "webhook": {
|
|
104
|
-
const {
|
|
105
|
-
if (
|
|
108
|
+
const { bodyB64 } = envelope;
|
|
109
|
+
if (malformed(envelope))
|
|
106
110
|
return text('webhook envelope needs { "method": string, "path": "/..." }\n', 400);
|
|
107
|
-
}
|
|
108
111
|
if (typeof bodyB64 === "string" && Buffer.byteLength(bodyB64, "base64") > MAX_WEBHOOK_BODY_BYTES) {
|
|
109
112
|
return json({
|
|
110
113
|
status: 413,
|
|
@@ -112,13 +115,7 @@ export function agentcoreRoutes(options) {
|
|
|
112
115
|
bodyB64: Buffer.from("payload too large\n").toString("base64"),
|
|
113
116
|
}, 200);
|
|
114
117
|
}
|
|
115
|
-
const inner =
|
|
116
|
-
method,
|
|
117
|
-
headers: headers ?? {},
|
|
118
|
-
body: typeof bodyB64 === "string" && method !== "GET" && method !== "HEAD"
|
|
119
|
-
? Buffer.from(bodyB64, "base64")
|
|
120
|
-
: undefined,
|
|
121
|
-
});
|
|
118
|
+
const inner = carried(envelope);
|
|
122
119
|
// A construction failure is the request's failure (503 through the forwarder, so the platform retries and the
|
|
123
120
|
// operator sees the message), never a silently-empty channel.
|
|
124
121
|
if (!dispatch)
|
|
@@ -138,36 +135,16 @@ export function agentcoreRoutes(options) {
|
|
|
138
135
|
};
|
|
139
136
|
return json(reply, 200);
|
|
140
137
|
}
|
|
141
|
-
case "
|
|
138
|
+
case "schedule-fire": {
|
|
142
139
|
const { name, occurrence } = envelope;
|
|
143
140
|
if (typeof name !== "string" || typeof occurrence !== "string" || Number.isNaN(Date.parse(occurrence))) {
|
|
144
|
-
return text('
|
|
141
|
+
return text('schedule-fire envelope needs { "name": string, "occurrence": ISO-date }\n', 400);
|
|
145
142
|
}
|
|
146
|
-
// No schedules in this definition: nothing to fire, and no rule should have survived a deploy that removed
|
|
147
|
-
// them either.
|
|
148
|
-
if (!fireRoutine)
|
|
149
|
-
return text(`no routines in this deployment (routine-fire "${name}")\n`, 404);
|
|
150
143
|
// The whole agent turn runs inside this request — but the CALLER (the forwarder Lambda) may time out and drop
|
|
151
144
|
// the connection while the turn keeps running server-side.
|
|
152
145
|
const workDone = beginWork();
|
|
153
146
|
try {
|
|
154
|
-
return await
|
|
155
|
-
}
|
|
156
|
-
finally {
|
|
157
|
-
workDone();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
case "routine-run": {
|
|
161
|
-
const { name } = envelope;
|
|
162
|
-
if (typeof name !== "string" || name === "") {
|
|
163
|
-
return text('routine-run envelope needs { "name": string }\n', 400);
|
|
164
|
-
}
|
|
165
|
-
if (!runRoutine)
|
|
166
|
-
return text(`no routines in this deployment (routine-run "${name}")\n`, 404);
|
|
167
|
-
// The whole agent turn runs inside this request; the caller may time out while it keeps running.
|
|
168
|
-
const workDone = beginWork();
|
|
169
|
-
try {
|
|
170
|
-
return await runRoutine(name);
|
|
147
|
+
return await fireSchedule(name, new Date(occurrence));
|
|
171
148
|
}
|
|
172
149
|
finally {
|
|
173
150
|
workDone();
|
|
@@ -187,16 +164,6 @@ export function agentcoreRoutes(options) {
|
|
|
187
164
|
? { ok: true }
|
|
188
165
|
: { ok: false, error: `channel construction failed: ${constructionError}` }, 200);
|
|
189
166
|
}
|
|
190
|
-
case "invoke": {
|
|
191
|
-
// Reuse the HTTP channel's handler wholesale (SSE, cancellation, backpressure) by handing it the shape it
|
|
192
|
-
// already validates — one protocol, one implementation.
|
|
193
|
-
const inner = new Request("http://agentcore.local/invoke", {
|
|
194
|
-
method: "POST",
|
|
195
|
-
headers: jsonHeaders,
|
|
196
|
-
body: JSON.stringify({ session: envelope.session, text: envelope.text }),
|
|
197
|
-
});
|
|
198
|
-
return invokeHandler(inner);
|
|
199
|
-
}
|
|
200
167
|
default:
|
|
201
168
|
return text(`unknown envelope kind "${envelope.kind}"\n`, 400);
|
|
202
169
|
}
|
package/dist/channels/body.d.ts
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Refuse a request to one of OUR routes that does not declare a JSON body — `undefined` when it does.
|
|
3
|
-
*
|
|
4
|
-
* This is what makes "a foreign origin simply gets no CORS headers" safe. Withholding the headers stops a page from
|
|
5
|
-
* READING the reply; it does not stop the request. A cross-origin POST carrying `text/plain` (or a form's
|
|
6
|
-
* `multipart/form-data`) is a CORS *simple request*: no preflight, sent regardless, and the turn it asks for runs.
|
|
7
|
-
* Demanding `application/json` takes the request out of that class, so the browser must preflight it — and a
|
|
8
|
-
* preflight we do not answer is a request that is never sent.
|
|
9
|
-
*
|
|
10
|
-
* Asked of the ROUTE, never of "does this have a body": the control plane reads an empty body as `{}`, so a
|
|
11
|
-
* body-less POST would otherwise walk straight through the gate.
|
|
12
|
-
*
|
|
13
|
-
* NOT applied to channel routes. Their caller is a platform's server with its own content types (Slack posts
|
|
14
|
-
* `application/x-www-form-urlencoded` for some payloads) and its own signature check, and a page cannot forge that.
|
|
15
|
-
*/
|
|
16
|
-
export declare function refuseNonJsonBody(req: Request): Response | undefined;
|
|
17
1
|
/** Read a request body with a hard byte cap (real bytes). */
|
|
18
2
|
export declare function readBodyCapped(req: Request, max: number): Promise<{
|
|
19
3
|
text: string;
|
package/dist/channels/body.js
CHANGED
|
@@ -1,28 +1,3 @@
|
|
|
1
|
-
import { text } from "./respond.js";
|
|
2
|
-
/**
|
|
3
|
-
* Refuse a request to one of OUR routes that does not declare a JSON body — `undefined` when it does.
|
|
4
|
-
*
|
|
5
|
-
* This is what makes "a foreign origin simply gets no CORS headers" safe. Withholding the headers stops a page from
|
|
6
|
-
* READING the reply; it does not stop the request. A cross-origin POST carrying `text/plain` (or a form's
|
|
7
|
-
* `multipart/form-data`) is a CORS *simple request*: no preflight, sent regardless, and the turn it asks for runs.
|
|
8
|
-
* Demanding `application/json` takes the request out of that class, so the browser must preflight it — and a
|
|
9
|
-
* preflight we do not answer is a request that is never sent.
|
|
10
|
-
*
|
|
11
|
-
* Asked of the ROUTE, never of "does this have a body": the control plane reads an empty body as `{}`, so a
|
|
12
|
-
* body-less POST would otherwise walk straight through the gate.
|
|
13
|
-
*
|
|
14
|
-
* NOT applied to channel routes. Their caller is a platform's server with its own content types (Slack posts
|
|
15
|
-
* `application/x-www-form-urlencoded` for some payloads) and its own signature check, and a page cannot forge that.
|
|
16
|
-
*/
|
|
17
|
-
export function refuseNonJsonBody(req) {
|
|
18
|
-
// Parameters are part of the header (`application/json; charset=utf-8`), so compare the media type alone.
|
|
19
|
-
const mediaType = (req.headers.get("content-type") ?? "").split(";")[0]?.trim().toLowerCase();
|
|
20
|
-
if (mediaType === "application/json")
|
|
21
|
-
return undefined;
|
|
22
|
-
// Says what is required, and does not echo what arrived: reflecting a request header into a response body is a
|
|
23
|
-
// habit worth not having, and the sender already knows what they sent.
|
|
24
|
-
return text("content-type must be application/json\n", 415);
|
|
25
|
-
}
|
|
26
1
|
/** Read a request body with a hard byte cap (real bytes). */
|
|
27
2
|
export async function readBodyCapped(req, max) {
|
|
28
3
|
if (!req.body)
|
package/dist/channels/busy.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The process's work in flight: every message not yet settled (counted by the Agent, pi-durable/agent.ts), and the
|
|
3
|
+
* channel, schedule and AgentCore work that has not reached an accepted message yet. Webhooks ACK before their turns
|
|
4
|
+
* finish, so open requests cannot tell whether the process is idle; this can. Read by AgentCore's `/ping` and by
|
|
5
|
+
* `dev`'s restart.
|
|
6
|
+
*/
|
|
2
7
|
let inFlight = 0;
|
|
3
8
|
/** Mark background work as started. */
|
|
4
9
|
export function beginWork() {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Agent } from "../agent-api.ts";
|
|
2
|
+
import type { ChannelContext } from "../channel.ts";
|
|
3
|
+
/** The context of the channel `name` (its file's name under `src/channels/`), its state under `stateRoot`. */
|
|
4
|
+
export declare function channelContext(agent: Agent, stateRoot: string, name: string): ChannelContext;
|