@fastagent-sh/fastagent 0.21.0 → 0.22.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 +9 -12
- package/dist/agent.d.ts +24 -37
- package/dist/agent.js +7 -15
- package/dist/atomic-write.d.ts +13 -17
- package/dist/atomic-write.js +18 -29
- package/dist/bind.d.ts +12 -22
- package/dist/bind.js +18 -27
- package/dist/channel.d.ts +11 -33
- package/dist/channels/agentcore-limits.d.ts +2 -13
- package/dist/channels/agentcore-limits.js +2 -13
- package/dist/channels/agentcore-protocol.d.ts +67 -58
- package/dist/channels/agentcore-protocol.js +23 -15
- package/dist/channels/agentcore-service.d.ts +19 -33
- package/dist/channels/agentcore-service.js +176 -59
- package/dist/channels/agentcore.d.ts +39 -33
- package/dist/channels/agentcore.js +90 -224
- package/dist/channels/body.d.ts +15 -2
- package/dist/channels/body.js +24 -2
- package/dist/channels/busy.d.ts +1 -21
- package/dist/channels/busy.js +2 -39
- package/dist/channels/control.d.ts +17 -62
- package/dist/channels/control.js +74 -182
- package/dist/channels/define-channel.d.ts +39 -0
- package/dist/channels/define-channel.js +9 -0
- package/dist/channels/discover.d.ts +10 -31
- package/dist/channels/discover.js +37 -31
- package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
- package/dist/channels/feishu/bootstrap-token.js +7 -23
- package/dist/channels/feishu/card.d.ts +14 -28
- package/dist/channels/feishu/card.js +16 -30
- package/dist/channels/feishu/cloud.d.ts +1 -5
- package/dist/channels/feishu/context-buffer.d.ts +4 -23
- package/dist/channels/feishu/context-buffer.js +8 -31
- package/dist/channels/feishu/crypto.d.ts +10 -5
- package/dist/channels/feishu/crypto.js +12 -19
- package/dist/channels/feishu/feishu-api.d.ts +2 -1
- package/dist/channels/feishu/feishu-api.js +5 -4
- package/dist/channels/feishu/feishu.d.ts +4 -10
- package/dist/channels/feishu/feishu.js +113 -227
- package/dist/channels/feishu/invoke-turn.d.ts +5 -4
- package/dist/channels/feishu/invoke-turn.js +36 -54
- package/dist/channels/feishu/model.d.ts +4 -21
- package/dist/channels/feishu/model.js +1 -8
- package/dist/channels/feishu/normalize.d.ts +2 -10
- package/dist/channels/feishu/normalize.js +7 -42
- package/dist/channels/feishu/parse.d.ts +9 -29
- package/dist/channels/feishu/parse.js +8 -26
- package/dist/channels/feishu/preview.d.ts +12 -14
- package/dist/channels/feishu/preview.js +108 -207
- package/dist/channels/feishu/register-app.d.ts +8 -23
- package/dist/channels/feishu/register-app.js +7 -20
- package/dist/channels/feishu/register-webhook.d.ts +2 -9
- package/dist/channels/feishu/register-webhook.js +9 -33
- package/dist/channels/feishu/scaffold/channel.ts +27 -19
- package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/feishu/setup-mode.d.ts +11 -36
- package/dist/channels/feishu/setup-mode.js +7 -26
- package/dist/channels/feishu/ws-ingress.d.ts +1 -2
- package/dist/channels/feishu/ws-ingress.js +6 -8
- package/dist/channels/http.d.ts +15 -15
- package/dist/channels/http.js +19 -9
- package/dist/channels/kit/attachment-path.d.ts +4 -5
- package/dist/channels/kit/attachment-path.js +10 -34
- package/dist/channels/kit/context-buffer.d.ts +14 -21
- package/dist/channels/kit/context-buffer.js +25 -38
- package/dist/channels/kit/delivery.d.ts +35 -0
- package/dist/channels/kit/delivery.js +129 -0
- package/dist/channels/kit/event-stream.d.ts +5 -0
- package/dist/channels/kit/event-stream.js +29 -0
- package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
- package/dist/channels/kit/invoke-turn-kit.js +89 -32
- package/dist/channels/kit/preview-kit.d.ts +18 -72
- package/dist/channels/kit/preview-kit.js +22 -88
- package/dist/channels/kit/signature.d.ts +3 -9
- package/dist/channels/kit/signature.js +3 -9
- package/dist/channels/kit/state.d.ts +1 -2
- package/dist/channels/kit/state.js +5 -14
- package/dist/channels/kit/stop-command.d.ts +1 -2
- package/dist/channels/kit/stop-command.js +6 -9
- package/dist/channels/kit/tasks.d.ts +1 -1
- package/dist/channels/kit/tasks.js +8 -21
- package/dist/channels/kit/text.d.ts +2 -16
- package/dist/channels/kit/text.js +23 -31
- package/dist/channels/kit/thread-participants.d.ts +4 -17
- package/dist/channels/kit/thread-participants.js +8 -55
- package/dist/channels/kit/transport.d.ts +26 -0
- package/dist/channels/kit/transport.js +20 -0
- package/dist/channels/kit/turn-queue.d.ts +7 -11
- package/dist/channels/kit/turn-queue.js +21 -39
- package/dist/channels/kit/turn-runner.d.ts +32 -18
- package/dist/channels/kit/turn-runner.js +98 -53
- package/dist/channels/kit/turn-store.d.ts +32 -32
- package/dist/channels/kit/turn-store.js +45 -88
- package/dist/channels/lark/lark.d.ts +1 -5
- package/dist/channels/lark/onboard.d.ts +3 -12
- package/dist/channels/lark/onboard.js +3 -13
- package/dist/channels/lark/scaffold/channel.ts +27 -19
- package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/registration.d.ts +9 -35
- package/dist/channels/registration.js +6 -37
- package/dist/channels/secret.js +2 -5
- package/dist/channels/serve.d.ts +47 -44
- package/dist/channels/serve.js +165 -101
- package/dist/channels/slack/config-api.d.ts +4 -27
- package/dist/channels/slack/config-api.js +5 -34
- package/dist/channels/slack/context-buffer.d.ts +2 -3
- package/dist/channels/slack/context-buffer.js +5 -5
- package/dist/channels/slack/invoke-turn.d.ts +3 -1
- package/dist/channels/slack/invoke-turn.js +25 -34
- package/dist/channels/slack/manifest.js +1 -3
- package/dist/channels/slack/model.d.ts +1 -2
- package/dist/channels/slack/model.js +1 -1
- package/dist/channels/slack/onboard.d.ts +4 -3
- package/dist/channels/slack/onboard.js +8 -11
- package/dist/channels/slack/onboarding-state.d.ts +3 -5
- package/dist/channels/slack/onboarding-state.js +9 -6
- package/dist/channels/slack/parse.d.ts +4 -13
- package/dist/channels/slack/parse.js +7 -28
- package/dist/channels/slack/preview.d.ts +12 -2
- package/dist/channels/slack/preview.js +292 -269
- package/dist/channels/slack/reaction.d.ts +11 -3
- package/dist/channels/slack/reaction.js +19 -8
- package/dist/channels/slack/register-webhook.js +4 -7
- package/dist/channels/slack/scaffold/channel.ts +20 -13
- package/dist/channels/slack/scaffold/slack-send.ts +2 -2
- package/dist/channels/slack/setup-server.d.ts +1 -5
- package/dist/channels/slack/setup-server.js +1 -5
- package/dist/channels/slack/shared-api.d.ts +1 -4
- package/dist/channels/slack/shared-api.js +2 -13
- package/dist/channels/slack/slack-api.d.ts +2 -1
- package/dist/channels/slack/slack-api.js +6 -4
- package/dist/channels/slack/slack.d.ts +11 -14
- package/dist/channels/slack/slack.js +121 -122
- package/dist/channels/sse.d.ts +1 -1
- package/dist/channels/sse.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +7 -22
- package/dist/channels/telegram/context-buffer.js +6 -23
- package/dist/channels/telegram/invoke-turn.d.ts +7 -7
- package/dist/channels/telegram/invoke-turn.js +24 -43
- package/dist/channels/telegram/parse.d.ts +24 -51
- package/dist/channels/telegram/parse.js +32 -84
- package/dist/channels/telegram/preview.d.ts +10 -1
- package/dist/channels/telegram/preview.js +79 -100
- package/dist/channels/telegram/register-webhook.js +5 -23
- package/dist/channels/telegram/scaffold/channel.ts +25 -18
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
- package/dist/channels/telegram/telegram-api.d.ts +4 -3
- package/dist/channels/telegram/telegram-api.js +9 -7
- package/dist/channels/telegram/telegram.d.ts +4 -18
- package/dist/channels/telegram/telegram.js +38 -94
- package/dist/channels/telegram/turn-store.d.ts +11 -14
- package/dist/channels/telegram/turn-store.js +8 -6
- package/dist/channels/wait-health.d.ts +4 -6
- package/dist/channels/wait-health.js +10 -14
- package/dist/cli/add-feishu.d.ts +2 -12
- package/dist/cli/add-feishu.js +24 -71
- package/dist/cli/add-slack.d.ts +5 -3
- package/dist/cli/add-slack.js +53 -76
- package/dist/cli/auth-view.d.ts +2 -16
- package/dist/cli/auth-view.js +2 -16
- package/dist/cli/commands/add.js +25 -43
- package/dist/cli/commands/chat.d.ts +0 -1
- package/dist/cli/commands/chat.js +5 -6
- package/dist/cli/commands/deploy/agentcore.js +60 -65
- package/dist/cli/commands/deploy/docker.js +50 -23
- package/dist/cli/commands/deploy/fly.js +23 -61
- package/dist/cli/commands/deploy/railway.js +23 -18
- package/dist/cli/commands/deploy/shared.d.ts +38 -57
- package/dist/cli/commands/deploy/shared.js +40 -59
- package/dist/cli/commands/deploy.d.ts +10 -27
- package/dist/cli/commands/deploy.js +38 -60
- package/dist/cli/commands/destroy.d.ts +4 -0
- package/dist/cli/commands/destroy.js +56 -0
- package/dist/cli/commands/dev.d.ts +2 -1
- package/dist/cli/commands/dev.js +20 -23
- package/dist/cli/commands/info.d.ts +0 -2
- package/dist/cli/commands/info.js +83 -49
- package/dist/cli/commands/init.d.ts +1 -2
- package/dist/cli/commands/init.js +16 -55
- package/dist/cli/commands/invoke.d.ts +0 -1
- package/dist/cli/commands/invoke.js +5 -7
- package/dist/cli/commands/login.d.ts +2 -1
- package/dist/cli/commands/login.js +33 -38
- package/dist/cli/commands/logs.js +3 -4
- package/dist/cli/commands/models.js +1 -5
- package/dist/cli/commands/routine-run.d.ts +6 -0
- package/dist/cli/commands/routine-run.js +47 -0
- package/dist/cli/commands/routine.d.ts +25 -0
- package/dist/cli/commands/routine.js +122 -0
- package/dist/cli/commands/start.d.ts +31 -3
- package/dist/cli/commands/start.js +131 -81
- package/dist/cli/commands/tool.js +26 -12
- package/dist/cli/fail.d.ts +13 -23
- package/dist/cli/fail.js +20 -23
- package/dist/cli/invoke-stream.d.ts +1 -8
- package/dist/cli/kernel.d.ts +6 -38
- package/dist/cli/kernel.js +14 -77
- package/dist/cli/models-view.d.ts +1 -9
- package/dist/cli/models-view.js +11 -22
- package/dist/cli/program.d.ts +5 -6
- package/dist/cli/program.js +120 -157
- package/dist/cli/serve.d.ts +57 -41
- package/dist/cli/serve.js +94 -121
- package/dist/cli/shared.d.ts +15 -61
- package/dist/cli/shared.js +55 -181
- package/dist/cli.js +1 -8
- package/dist/collect.d.ts +6 -0
- package/dist/collect.js +6 -0
- package/dist/core.d.ts +4 -1
- package/dist/core.js +9 -7
- package/dist/declared-secrets.d.ts +55 -0
- package/dist/declared-secrets.js +88 -0
- package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
- package/dist/deploy/agentcore/aws-cli.js +64 -0
- package/dist/deploy/agentcore/destroy.d.ts +51 -0
- package/dist/deploy/agentcore/destroy.js +273 -0
- package/dist/deploy/agentcore/forwarder.js +28 -79
- package/dist/deploy/agentcore/logs.d.ts +2 -18
- package/dist/deploy/agentcore/logs.js +27 -51
- package/dist/deploy/agentcore/plan.d.ts +85 -105
- package/dist/deploy/agentcore/plan.js +152 -314
- package/dist/deploy/agentcore/run.d.ts +28 -28
- package/dist/deploy/agentcore/run.js +227 -306
- package/dist/deploy/agentcore/zip.d.ts +2 -15
- package/dist/deploy/agentcore/zip.js +3 -17
- package/dist/deploy/build-context.d.ts +21 -0
- package/dist/deploy/build-context.js +137 -0
- package/dist/deploy/channel-ingress.d.ts +26 -39
- package/dist/deploy/channel-ingress.js +57 -28
- package/dist/deploy/container.d.ts +20 -34
- package/dist/deploy/container.js +49 -100
- package/dist/deploy/docker/plan.d.ts +12 -11
- package/dist/deploy/docker/plan.js +43 -24
- package/dist/deploy/docker/run.d.ts +22 -25
- package/dist/deploy/docker/run.js +39 -37
- package/dist/deploy/fly/plan.d.ts +15 -44
- package/dist/deploy/fly/plan.js +41 -73
- package/dist/deploy/fly/run.d.ts +16 -51
- package/dist/deploy/fly/run.js +42 -95
- package/dist/deploy/hosts.d.ts +24 -3
- package/dist/deploy/hosts.js +24 -3
- package/dist/deploy/preflight.d.ts +47 -41
- package/dist/deploy/preflight.js +282 -383
- package/dist/deploy/railway/plan.d.ts +16 -42
- package/dist/deploy/railway/plan.js +42 -60
- package/dist/deploy/railway/run.d.ts +30 -68
- package/dist/deploy/railway/run.js +86 -100
- package/dist/deploy/registration-gate.d.ts +6 -13
- package/dist/deploy/residency.d.ts +37 -0
- package/dist/deploy/residency.js +24 -0
- package/dist/deploy/runner.d.ts +10 -9
- package/dist/deploy/runner.js +7 -18
- package/dist/deploy/secrets.d.ts +40 -41
- package/dist/deploy/secrets.js +117 -103
- package/dist/deploy/workspace.d.ts +50 -0
- package/dist/deploy/workspace.js +197 -0
- package/dist/dev-supervisor.d.ts +1 -9
- package/dist/dev-supervisor.js +21 -64
- package/dist/effect-port.d.ts +50 -0
- package/dist/effect-port.js +77 -0
- package/dist/engines/pi/agent-session-factory.d.ts +50 -69
- package/dist/engines/pi/agent-session-factory.js +249 -146
- package/dist/engines/pi/auth.d.ts +15 -11
- package/dist/engines/pi/auth.js +96 -99
- package/dist/engines/pi/chat.d.ts +1 -5
- package/dist/engines/pi/chat.js +2 -14
- package/dist/engines/pi/config.d.ts +53 -51
- package/dist/engines/pi/config.js +83 -92
- package/dist/engines/pi/create.d.ts +60 -75
- package/dist/engines/pi/create.js +108 -141
- package/dist/engines/pi/definition.d.ts +14 -58
- package/dist/engines/pi/definition.js +21 -85
- package/dist/engines/pi/invoke-session.d.ts +5 -25
- package/dist/engines/pi/invoke-session.js +209 -244
- package/dist/engines/pi/login.d.ts +95 -35
- package/dist/engines/pi/login.js +259 -107
- package/dist/engines/pi/machine.d.ts +31 -0
- package/dist/engines/pi/machine.js +106 -0
- package/dist/engines/pi/models.d.ts +70 -89
- package/dist/engines/pi/models.js +188 -81
- package/dist/engines/pi/open.d.ts +65 -53
- package/dist/engines/pi/open.js +129 -69
- package/dist/engines/pi/report.d.ts +3 -12
- package/dist/engines/pi/report.js +2 -16
- package/dist/engines/pi/search-tools.d.ts +1 -9
- package/dist/engines/pi/search-tools.js +26 -57
- package/dist/engines/pi/service.d.ts +2 -16
- package/dist/engines/pi/service.js +2 -16
- package/dist/engines/pi/session-builder.d.ts +5 -9
- package/dist/engines/pi/session-builder.js +44 -82
- package/dist/engines/pi/session-control.js +286 -269
- package/dist/engines/pi/session-effects.d.ts +14 -0
- package/dist/engines/pi/session-effects.js +30 -0
- package/dist/engines/pi/session-inheritance.d.ts +9 -18
- package/dist/engines/pi/session-inheritance.js +103 -160
- package/dist/engines/pi/session-markers.d.ts +31 -30
- package/dist/engines/pi/session-markers.js +26 -25
- package/dist/engines/pi/session-settings.d.ts +6 -22
- package/dist/engines/pi/session-settings.js +10 -21
- package/dist/engines/pi/session-store.d.ts +26 -92
- package/dist/engines/pi/session-store.js +96 -310
- package/dist/engines/pi/tool-context.d.ts +17 -45
- package/dist/engines/pi/tool-context.js +7 -26
- package/dist/engines/pi/tool.d.ts +45 -36
- package/dist/engines/pi/tool.js +43 -57
- package/dist/engines/pi/turn-kit.d.ts +14 -52
- package/dist/engines/pi/turn-kit.js +18 -67
- package/dist/engines/pi/wake-tool.d.ts +2 -10
- package/dist/engines/pi/wake-tool.js +7 -27
- package/dist/env.d.ts +27 -28
- package/dist/env.js +45 -50
- package/dist/index.js +0 -2
- package/dist/lark.d.ts +1 -2
- package/dist/lark.js +1 -2
- package/dist/loader.d.ts +14 -55
- package/dist/loader.js +24 -43
- package/dist/log.d.ts +2 -12
- package/dist/log.js +3 -24
- package/dist/node.d.ts +2 -15
- package/dist/node.js +2 -18
- package/dist/observe.d.ts +2 -8
- package/dist/open-url.d.ts +1 -1
- package/dist/open-url.js +1 -1
- package/dist/paths.d.ts +70 -134
- package/dist/paths.js +165 -232
- package/dist/pi.d.ts +4 -6
- package/dist/pi.js +3 -3
- package/dist/proxy.d.ts +16 -6
- package/dist/proxy.js +52 -16
- package/dist/runtime.d.ts +18 -12
- package/dist/runtime.js +18 -11
- package/dist/scaffold/add-channel.d.ts +14 -31
- package/dist/scaffold/add-channel.js +48 -112
- package/dist/scaffold/init.d.ts +9 -31
- package/dist/scaffold/init.js +45 -138
- package/dist/scaffold/templates/env.example +6 -4
- package/dist/scaffold/templates/fastagent.config.ts +36 -0
- package/dist/scaffold/templates.d.ts +3 -3
- package/dist/scaffold/templates.js +5 -8
- package/dist/scaffold/vendor-skill.d.ts +2 -5
- package/dist/scaffold/vendor-skill.js +19 -29
- package/dist/schedule/cron.d.ts +9 -5
- package/dist/schedule/cron.js +9 -11
- package/dist/schedule/discover.d.ts +12 -12
- package/dist/schedule/discover.js +43 -34
- package/dist/schedule/routine.d.ts +43 -0
- package/dist/schedule/routine.js +32 -0
- package/dist/schedule/run.d.ts +55 -0
- package/dist/schedule/run.js +146 -0
- package/dist/schedule/scheduler.d.ts +44 -55
- package/dist/schedule/scheduler.js +291 -265
- package/dist/schedule/state.d.ts +105 -10
- package/dist/schedule/state.js +293 -19
- package/dist/schedule/wake-alarm.d.ts +10 -23
- package/dist/schedule/wake-alarm.js +91 -121
- package/dist/schedule/wakeups.d.ts +23 -39
- package/dist/schedule/wakeups.js +33 -75
- package/dist/secrets-gate.d.ts +32 -0
- package/dist/secrets-gate.js +33 -0
- package/dist/service.d.ts +137 -98
- package/dist/service.js +250 -252
- package/dist/session-remote.d.ts +33 -44
- package/dist/session-remote.js +203 -192
- package/dist/session.d.ts +153 -228
- package/dist/session.js +33 -55
- package/dist/tunnel.d.ts +8 -36
- package/dist/tunnel.js +24 -65
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +10 -16
- package/dist/channels/agentcore-state.d.ts +0 -77
- package/dist/channels/agentcore-state.js +0 -276
- package/dist/channels/github/github.d.ts +0 -31
- package/dist/channels/github/github.js +0 -89
- package/dist/channels/github/scaffold/channel.ts +0 -25
- package/dist/cli/commands/attach.d.ts +0 -108
- package/dist/cli/commands/attach.js +0 -637
- package/dist/cli/commands/fire.d.ts +0 -7
- package/dist/cli/commands/fire.js +0 -38
- package/dist/cli/commands/schedule.d.ts +0 -12
- package/dist/cli/commands/schedule.js +0 -87
- package/dist/github.d.ts +0 -3
- package/dist/github.js +0 -2
- package/dist/scaffold/templates/fastagent.config.mjs +0 -23
- package/dist/schedule/audit.d.ts +0 -18
- package/dist/schedule/audit.js +0 -57
- package/dist/schedule/schedule.d.ts +0 -35
- package/dist/schedule/schedule.js +0 -23
|
@@ -1,91 +1,98 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The wire between the forwarder Lambda and the container, in ONE place: the envelope every
|
|
3
|
-
* trigger arrives as, the reply a webhook rides back in, the presigned-URL pair the state snapshot
|
|
4
|
-
* uses, the wake-alarm request, and the forwarder's reserved paths. The forwarder itself is JavaScript
|
|
5
|
-
* (`deploy/agentcore/forwarder.js`) and cannot import this, so `agentcore-forwarder.test.ts` pins its
|
|
6
|
-
* literals to these — a rename here fails there, not on a live box.
|
|
7
|
-
*
|
|
8
|
-
* Pure types and constants: the adapter, the state sync, the wake sink and the deploy driver all
|
|
9
|
-
* read it, and none of them may pull the others in for it.
|
|
10
|
-
*/
|
|
11
1
|
/** Paths the forwarder answers ITSELF — never forwarded to a channel route. */
|
|
12
2
|
export declare const RESERVED_PATHS: {
|
|
13
3
|
/** The deploy driver's post-deploy verification (ingress secret). */
|
|
14
4
|
readonly probe: "/__fastagent/probe";
|
|
15
5
|
/** The container's wake-alarm mirror callback (wake secret). */
|
|
16
6
|
readonly wakeAlarm: "/__fastagent/wake-alarm";
|
|
17
|
-
/** Re-mint the state snapshot's presigned URLs with current Lambda credentials (ingress secret). */
|
|
18
|
-
readonly stateUrls: "/__fastagent/state-urls";
|
|
19
7
|
};
|
|
20
|
-
/**
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
*
|
|
8
|
+
/** Every kind the container's `POST /invocations` dispatches on. */
|
|
9
|
+
export declare const ENVELOPE_KINDS: readonly ["webhook", "routine-fire", "routine-run", "invoke", "wake-poke", "probe"];
|
|
10
|
+
/**
|
|
11
|
+
* Did this envelope come from the FORWARDER, rather than from some other principal holding
|
|
12
|
+
* `bedrock-agentcore:InvokeAgentRuntime`?
|
|
13
|
+
*
|
|
14
|
+
* WHAT IT ACTUALLY GUARDS, in one line: only the forwarder may tell the container where the forwarder is. That URL
|
|
15
|
+
* (`envelope.wake.url`) is where the container later POSTs its wake alarms, carrying `FASTAGENT_WAKE_SECRET` — so a
|
|
16
|
+
* principal who could set it would redirect that callback and collect the secret along with every pending wake-up.
|
|
17
|
+
*
|
|
18
|
+
* The other thing it gates — the non-`invoke` kinds — is DEPTH, not a boundary. Every one of them is weaker than
|
|
19
|
+
* the `invoke` the same caller may already send: `routine-fire` runs a prompt the definition wrote down,
|
|
20
|
+
* `webhook` reaches only channel routes that verify their own platform's signature, `wake-poke` wakes, `probe`
|
|
21
|
+
* reports. IAM is what decides who gets to ask at all.
|
|
22
|
+
*
|
|
23
|
+
* ONE reading, because the deferred wrapper answers a probe before the adapter exists and would otherwise spell the
|
|
24
|
+
* same rule twice (channels/agentcore-service.ts).
|
|
25
|
+
*/
|
|
26
|
+
export declare function fromForwarder(envelope: {
|
|
27
|
+
auth?: unknown;
|
|
28
|
+
} | undefined, expected: string | undefined): boolean;
|
|
29
|
+
/** What the forwarder Lambda / EventBridge deliver in the `/invocations` payload. */
|
|
36
30
|
export type AgentcoreEnvelope = {
|
|
37
|
-
/** Shared secret proving this envelope came from the forwarder (FASTAGENT_INGRESS_SECRET).
|
|
38
|
-
* public `invoke` data plane neither has nor needs it — and may not carry the fields below. */
|
|
31
|
+
/** Shared secret proving this envelope came from the forwarder (FASTAGENT_INGRESS_SECRET). */
|
|
39
32
|
auth?: string;
|
|
40
33
|
wake?: {
|
|
41
34
|
url: string;
|
|
42
35
|
};
|
|
43
|
-
state?: StateUrls;
|
|
44
36
|
} & ({
|
|
45
37
|
kind: "webhook";
|
|
46
|
-
/** Original webhook request line, verbatim.
|
|
38
|
+
/** Original webhook request line, verbatim. */
|
|
47
39
|
method: string;
|
|
48
40
|
path: string;
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
41
|
+
/**
|
|
42
|
+
* Original raw query string (no leading `?`) — "verbatim" includes it; a channel reading
|
|
43
|
+
* `request.url.searchParams` must see what the webhook sender sent.
|
|
44
|
+
*/
|
|
51
45
|
query?: string;
|
|
52
46
|
/** Original headers — signature material (secret tokens, Feishu signatures) rides here. */
|
|
53
47
|
headers?: Record<string, string>;
|
|
54
48
|
/** Original body, base64 (webhook bodies are JSON but the tunnel must be byte-exact). */
|
|
55
49
|
bodyB64?: string;
|
|
56
50
|
} | {
|
|
57
|
-
kind: "
|
|
51
|
+
kind: "routine-fire";
|
|
52
|
+
name: string;
|
|
53
|
+
/**
|
|
54
|
+
* The instant EventBridge scheduled this fire for — the clock's NAME for the occurrence, stable across its
|
|
55
|
+
* redeliveries (measured: 3 attempts of one fire, byte-identical payload). The container dedupes on it and
|
|
56
|
+
* never recomputes it.
|
|
57
|
+
*/
|
|
58
|
+
occurrence: string;
|
|
59
|
+
} | {
|
|
60
|
+
/**
|
|
61
|
+
* Run a declared routine BY NAME — the `POST /run` contract on the host that publishes no routes.
|
|
62
|
+
*
|
|
63
|
+
* On the IAM door, like `invoke`, and for the same reason: the forwarder never emits this, so it can only
|
|
64
|
+
* come from a direct `InvokeAgentRuntime` call, where AWS has already said who the caller is. That makes
|
|
65
|
+
* by-name running STRICTER here than on a host with a public URL, where the same route is anonymous.
|
|
66
|
+
*
|
|
67
|
+
* Separate from `routine-fire` because they are different contracts, not two spellings: a fire names an
|
|
68
|
+
* OCCURRENCE our own clock produced and claims it (dedup across redeliveries, a fire history, the overlap
|
|
69
|
+
* policy); this names only the work (schedule/run.ts).
|
|
70
|
+
*/
|
|
71
|
+
kind: "routine-run";
|
|
58
72
|
name: string;
|
|
59
|
-
/** The cron instant this fire is FOR (ISO) — the slot-idempotency key. */
|
|
60
|
-
slot: string;
|
|
61
73
|
} | {
|
|
62
74
|
kind: "invoke";
|
|
63
75
|
session: string;
|
|
64
76
|
text: string;
|
|
65
77
|
}
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
78
|
+
/**
|
|
79
|
+
* An EventBridge wake-up poke: the invocation ITSELF is the payload — it wakes the container, whose boot drain / 30s
|
|
80
|
+
* wake pump then fires whatever is due.
|
|
81
|
+
*/
|
|
68
82
|
| {
|
|
69
83
|
kind: "wake-poke";
|
|
70
84
|
}
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
| {
|
|
76
|
-
kind: "checkpoint";
|
|
77
|
-
}
|
|
78
|
-
/** The deploy driver's post-deploy verification (relayed by the forwarder's reserved probe path,
|
|
79
|
-
* which answers on EVERY forwarder topology — schedule-only URLs refuse ordinary public traffic).
|
|
80
|
-
* Runs restore + channel construction end to end and answers a TRANSPORT-200 structured verdict
|
|
81
|
-
* `{ ok, error? }`: the ordinary webhook path folds a non-200 transport into an opaque 502 at the
|
|
82
|
-
* forwarder, which would strip exactly the diagnostics this probe exists to carry. */
|
|
85
|
+
/**
|
|
86
|
+
* The deploy driver's post-deploy verification (relayed by the forwarder's reserved probe path, which answers on
|
|
87
|
+
* EVERY forwarder topology — schedule-only URLs refuse ordinary public traffic).
|
|
88
|
+
*/
|
|
83
89
|
| {
|
|
84
90
|
kind: "probe";
|
|
85
91
|
});
|
|
86
|
-
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
92
|
+
/**
|
|
93
|
+
* The webhook envelope's reply: the channel's real HTTP response, ridden inside a transport-200 body so the forwarder
|
|
94
|
+
* can re-emit it verbatim (AgentCore folds a container non-2xx into its own 424 RuntimeClientError).
|
|
95
|
+
*/
|
|
89
96
|
export interface WebhookReply {
|
|
90
97
|
status: number;
|
|
91
98
|
headers: Record<string, string>;
|
|
@@ -101,12 +108,14 @@ export interface WakeAlarmRequest {
|
|
|
101
108
|
secret: string;
|
|
102
109
|
alarms: WakeAlarm[];
|
|
103
110
|
}
|
|
104
|
-
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
111
|
+
/**
|
|
112
|
+
* What EventBridge hands the forwarder when a cron rule fires: which schedule, and which occurrence of it.
|
|
113
|
+
* `occurrence` is `<aws.scheduler.scheduled-time>` — the clock names its own fire, which is the only thing that
|
|
114
|
+
* survives a retry (schedule/run.ts).
|
|
115
|
+
*/
|
|
107
116
|
export interface ScheduleFireEvent {
|
|
108
117
|
scheduleFire: {
|
|
109
118
|
name: string;
|
|
110
|
-
|
|
119
|
+
occurrence: string;
|
|
111
120
|
};
|
|
112
121
|
}
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* trigger arrives as, the reply a webhook rides back in, the presigned-URL pair the state snapshot
|
|
4
|
-
* uses, the wake-alarm request, and the forwarder's reserved paths. The forwarder itself is JavaScript
|
|
5
|
-
* (`deploy/agentcore/forwarder.js`) and cannot import this, so `agentcore-forwarder.test.ts` pins its
|
|
6
|
-
* literals to these — a rename here fails there, not on a live box.
|
|
7
|
-
*
|
|
8
|
-
* Pure types and constants: the adapter, the state sync, the wake sink and the deploy driver all
|
|
9
|
-
* read it, and none of them may pull the others in for it.
|
|
10
|
-
*/
|
|
1
|
+
/** The wire between the forwarder Lambda and the container, in ONE place. */
|
|
2
|
+
import { secretEquals } from "./secret.js";
|
|
11
3
|
/** Paths the forwarder answers ITSELF — never forwarded to a channel route. */
|
|
12
4
|
export const RESERVED_PATHS = {
|
|
13
5
|
/** The deploy driver's post-deploy verification (ingress secret). */
|
|
14
6
|
probe: "/__fastagent/probe",
|
|
15
7
|
/** The container's wake-alarm mirror callback (wake secret). */
|
|
16
8
|
wakeAlarm: "/__fastagent/wake-alarm",
|
|
17
|
-
/** Re-mint the state snapshot's presigned URLs with current Lambda credentials (ingress secret). */
|
|
18
|
-
stateUrls: "/__fastagent/state-urls",
|
|
19
9
|
};
|
|
20
|
-
/** Every kind the container's `POST /invocations` dispatches on.
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
/** Every kind the container's `POST /invocations` dispatches on. */
|
|
11
|
+
export const ENVELOPE_KINDS = ["webhook", "routine-fire", "routine-run", "invoke", "wake-poke", "probe"];
|
|
12
|
+
/**
|
|
13
|
+
* Did this envelope come from the FORWARDER, rather than from some other principal holding
|
|
14
|
+
* `bedrock-agentcore:InvokeAgentRuntime`?
|
|
15
|
+
*
|
|
16
|
+
* WHAT IT ACTUALLY GUARDS, in one line: only the forwarder may tell the container where the forwarder is. That URL
|
|
17
|
+
* (`envelope.wake.url`) is where the container later POSTs its wake alarms, carrying `FASTAGENT_WAKE_SECRET` — so a
|
|
18
|
+
* principal who could set it would redirect that callback and collect the secret along with every pending wake-up.
|
|
19
|
+
*
|
|
20
|
+
* The other thing it gates — the non-`invoke` kinds — is DEPTH, not a boundary. Every one of them is weaker than
|
|
21
|
+
* the `invoke` the same caller may already send: `routine-fire` runs a prompt the definition wrote down,
|
|
22
|
+
* `webhook` reaches only channel routes that verify their own platform's signature, `wake-poke` wakes, `probe`
|
|
23
|
+
* reports. IAM is what decides who gets to ask at all.
|
|
24
|
+
*
|
|
25
|
+
* ONE reading, because the deferred wrapper answers a probe before the adapter exists and would otherwise spell the
|
|
26
|
+
* same rule twice (channels/agentcore-service.ts).
|
|
27
|
+
*/
|
|
28
|
+
export function fromForwarder(envelope, expected) {
|
|
29
|
+
return secretEquals(envelope?.auth, expected);
|
|
30
|
+
}
|
|
@@ -1,51 +1,37 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The AgentCore serving assembly — the same product as `mountAgentService`, built differently
|
|
3
|
-
* because the host is.
|
|
4
|
-
*
|
|
5
|
-
* Two facts drive every difference. There is no public URL (the adapter's `POST /invocations` is the
|
|
6
|
-
* only ingress, and cron slots arrive through it from an external clock, so no resident timers), and
|
|
7
|
-
* **the state mount at boot is PRE-RESTORE** — empty after every version update. Discovering channels
|
|
8
|
-
* eagerly would therefore cache that emptiness (thread participation, delivery dedup, pending turns)
|
|
9
|
-
* and then clobber the restored files with it, so channels are constructed lazily on the first
|
|
10
|
-
* envelope. Everywhere else the state root is durable at boot and a broken channel fails startup.
|
|
11
|
-
*
|
|
12
|
-
* That is a different assembly, not a flag on the shared one: handler shape, discovery timing, clock
|
|
13
|
-
* source, long-connection support and shutdown all differ. What it is NOT is a different product —
|
|
14
|
-
* it returns the same {@link AgentService}, so `start` picks an assembly once and everything after
|
|
15
|
-
* that point is common.
|
|
16
|
-
*/
|
|
1
|
+
/** The AgentCore serving assembly — the same product as `mountAgentService`, built differently because the host is. */
|
|
17
2
|
import type { Agent } from "../agent.ts";
|
|
18
3
|
import type { Routes } from "../channel.ts";
|
|
19
|
-
import type {
|
|
4
|
+
import type { LoadedRoutine } from "../schedule/routine.ts";
|
|
20
5
|
import { type AgentService, type MountableAgent } from "../service.ts";
|
|
21
6
|
import { type AgentcoreAdapterOptions } from "./agentcore.ts";
|
|
7
|
+
import type { ChannelHandler } from "../channel.ts";
|
|
8
|
+
/**
|
|
9
|
+
* Runtime filesystems appear on invocation, so even opening the definition must be deferred — in the TWO stages that
|
|
10
|
+
* differ in what a retry would cost.
|
|
11
|
+
*/
|
|
12
|
+
export declare function deferAgentcoreService<T>(stages: {
|
|
13
|
+
prepare: () => Promise<T>;
|
|
14
|
+
assemble: (prepared: T) => Promise<AgentService>;
|
|
15
|
+
}): {
|
|
16
|
+
handler: ChannelHandler;
|
|
17
|
+
close: () => Promise<void>;
|
|
18
|
+
};
|
|
22
19
|
export interface MountAgentcoreServiceOptions {
|
|
23
20
|
/** Wrap the opened agent before anything binds to it (the CLI's turn trace). */
|
|
24
21
|
wrapAgent?: (agent: Agent) => Agent;
|
|
25
|
-
/**
|
|
26
|
-
* its sink is a PROCESS-global: the process entry owns that, not a service that can be closed. */
|
|
22
|
+
/** The process entry owns the global wake-alarm sink; activation reconciles it once. */
|
|
27
23
|
onStateReady?: () => void;
|
|
28
24
|
}
|
|
29
|
-
/** Is this process running inside the AgentCore Runtime? Set by the generated deploy artifacts. */
|
|
30
|
-
export declare function isAgentcoreRuntime(): boolean;
|
|
31
25
|
export declare function mountAgentcoreService(opened: MountableAgent, options?: MountAgentcoreServiceOptions): Promise<AgentService>;
|
|
32
26
|
/**
|
|
33
|
-
* Mount the AgentCore Runtime adapter (`POST /invocations` + `GET /ping`) — the deployed container's
|
|
34
|
-
*
|
|
35
|
-
* by the generated deploy artifacts, never by hand).
|
|
36
|
-
*
|
|
37
|
-
* The adapter IS the surface: the agent's channels live in a table INSIDE the envelope dispatch, a
|
|
38
|
-
* separate namespace from these two paths, so a channel route named `/invocations` is reached
|
|
39
|
-
* through the Function URL as itself and cannot shadow anything.
|
|
27
|
+
* Mount the AgentCore Runtime adapter (`POST /invocations` + `GET /ping`) — the deployed container's ONLY reachable
|
|
28
|
+
* surface (channels/agentcore.ts).
|
|
40
29
|
*/
|
|
41
30
|
export declare function mountAgentcore(options: {
|
|
42
31
|
agent: Agent;
|
|
43
32
|
stateRoot: string;
|
|
44
|
-
|
|
33
|
+
routines: readonly LoadedRoutine[];
|
|
45
34
|
onStateReady?: () => void;
|
|
46
|
-
/**
|
|
47
|
-
signal?: AbortSignal;
|
|
48
|
-
/** The channel surface, constructed on the first envelope AFTER the state-snapshot restore — never
|
|
49
|
-
* at boot, where the mount is pre-restore (channels/agentcore.ts). */
|
|
35
|
+
/** The channel surface, initialized once by the adapter on trusted ingress. */
|
|
50
36
|
channels: AgentcoreAdapterOptions["channels"];
|
|
51
37
|
}): Routes;
|
|
@@ -1,102 +1,219 @@
|
|
|
1
|
+
import { fromForwarder } from "./agentcore-protocol.js";
|
|
1
2
|
import { log } from "../log.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { loadServingRoutines, routesFor, startSchedules } from "../service.js";
|
|
4
|
+
import { agentcoreRoutes, agentcorePing } from "./agentcore.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import { runRoutineByName, unknownRoutine } from "../schedule/run.js";
|
|
7
|
+
import { fireRoutineOnce } from "../schedule/scheduler.js";
|
|
8
|
+
import { text } from "./respond.js";
|
|
9
|
+
import { activeWork, beginWork } from "./busy.js";
|
|
7
10
|
import { router } from "./serve.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
import { readBodyCapped } from "./body.js";
|
|
12
|
+
import { MAX_ENVELOPE_BYTES } from "./agentcore-limits.js";
|
|
13
|
+
/**
|
|
14
|
+
* Runtime filesystems appear on invocation, so even opening the definition must be deferred — in the TWO stages that
|
|
15
|
+
* differ in what a retry would cost.
|
|
16
|
+
*/
|
|
17
|
+
export function deferAgentcoreService(stages) {
|
|
18
|
+
let service;
|
|
19
|
+
let prepared;
|
|
20
|
+
let assembling;
|
|
21
|
+
let closed = false;
|
|
22
|
+
let closing;
|
|
23
|
+
const ping = agentcorePing(() => activeWork() > 0);
|
|
24
|
+
const initialize = async () => {
|
|
25
|
+
const done = beginWork();
|
|
26
|
+
try {
|
|
27
|
+
prepared ??= stages.prepare().catch((error) => {
|
|
28
|
+
prepared = undefined; // nothing was taken, so the next envelope may ask again
|
|
29
|
+
throw error;
|
|
30
|
+
});
|
|
31
|
+
const taken = await prepared;
|
|
32
|
+
// Shutdown may have arrived while the workspace was being taken.
|
|
33
|
+
if (closed)
|
|
34
|
+
throw new Error("service closed during initialization");
|
|
35
|
+
assembling ??= stages.assemble(taken);
|
|
36
|
+
service = await assembling;
|
|
37
|
+
return service;
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
done();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
handler: async (request) => {
|
|
45
|
+
if (closed)
|
|
46
|
+
return new Response("service closed\n", { status: 503 });
|
|
47
|
+
if (service)
|
|
48
|
+
return service.handler(request);
|
|
49
|
+
const path = new URL(request.url).pathname;
|
|
50
|
+
if (path === "/ping" && request.method === "GET")
|
|
51
|
+
return ping(request);
|
|
52
|
+
if (path !== "/invocations" || request.method !== "POST")
|
|
53
|
+
return new Response("not found\n", { status: 404 });
|
|
54
|
+
let ready;
|
|
55
|
+
// Only the INITIALIZATION is caught here.
|
|
56
|
+
try {
|
|
57
|
+
ready = await initialize();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const message = `initialization failed: ${String(error)}`;
|
|
61
|
+
log.error(`[agentcore] ${message}`);
|
|
62
|
+
const body = await readBodyCapped(request, MAX_ENVELOPE_BYTES);
|
|
63
|
+
if (!("tooLarge" in body)) {
|
|
64
|
+
let envelope;
|
|
65
|
+
try {
|
|
66
|
+
envelope = JSON.parse(body.text);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Invalid envelopes retain the initialization error.
|
|
70
|
+
}
|
|
71
|
+
if (envelope?.kind === "probe" && fromForwarder(envelope, process.env.FASTAGENT_INGRESS_SECRET)) {
|
|
72
|
+
return Response.json({ ok: false, error: message });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return new Response(`${message}\n`, { status: 503 });
|
|
76
|
+
}
|
|
77
|
+
if (closed)
|
|
78
|
+
return new Response("service closed\n", { status: 503 });
|
|
79
|
+
return ready.handler(request);
|
|
80
|
+
},
|
|
81
|
+
close() {
|
|
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
|
+
},
|
|
89
|
+
};
|
|
11
90
|
}
|
|
12
91
|
export async function mountAgentcoreService(opened, options = {}) {
|
|
13
92
|
const { agentDir, workspace, stateRoot, sessionControl } = opened;
|
|
14
93
|
const agent = options.wrapAgent?.(opened.agent) ?? opened.agent;
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
94
|
+
// NO control plane on this host, and `sessionControl: true` cannot change that.
|
|
95
|
+
//
|
|
96
|
+
// The only public way in is the forwarder's Function URL, which relays an arbitrary `rawPath` verbatim as a
|
|
97
|
+
// webhook envelope (deploy/agentcore/forwarder.js) and attaches the ingress secret ITSELF — so every anonymous
|
|
98
|
+
// caller arrives as trusted ingress. A channel route survives that because the platform's signature is checked
|
|
99
|
+
// inside it; `/control/*` has no such check, and mounting it here made `GET /control/sessions` and
|
|
100
|
+
// `DELETE /control/sessions/{id}` answerable from the public URL with no credential at all.
|
|
101
|
+
//
|
|
102
|
+
// It is NOT that the two callers cannot be told apart. AWS already separates them and we already use that:
|
|
103
|
+
// `InvokeAgentRuntime` is IAM-gated, the forwarder builds its own envelopes and emits exactly four kinds, so a
|
|
104
|
+
// kind it never sends can only have come from a direct IAM call — which is how `kind: "invoke"` runs a turn here
|
|
105
|
+
// without the ingress secret. Adding `kind: "control"` on the same footing is small, and is the recipe if this is
|
|
106
|
+
// ever wanted.
|
|
107
|
+
//
|
|
108
|
+
// It is not built because nothing asks for it: `connectSessionControl` has no caller in this repo, and the
|
|
109
|
+
// envelope is request/response with a buffered body (see the webhook reply), so the one route a GUI actually
|
|
110
|
+
// renders from — the long-lived `GET /control/sessions/{id}/events` stream — could not ride it anyway. Half a
|
|
111
|
+
// control plane, for nobody, on a third transport.
|
|
112
|
+
// Every config key that cannot mean anything on this host says so. Silence here is how an operator concludes a
|
|
113
|
+
// setting took effect — `sessionControl` was the one that already warned, and the other two were just as inert.
|
|
114
|
+
if (opened.http?.cors) {
|
|
115
|
+
log.warn("[fastagent] agentcore: http.cors has no effect here — no browser reaches this container. Its ingress is the " +
|
|
116
|
+
"forwarder's Function URL (webhooks) and the Runtime's IAM-gated API, neither of which is a page.");
|
|
117
|
+
}
|
|
118
|
+
if (opened.http?.invoke !== undefined) {
|
|
119
|
+
log.warn("[fastagent] agentcore: http.invoke has no effect here — this host serves the Runtime's POST /invocations " +
|
|
120
|
+
"contract instead of our own /invoke, and reaching it already requires bedrock-agentcore:InvokeAgentRuntime.");
|
|
121
|
+
}
|
|
122
|
+
if (opened.http?.run !== undefined) {
|
|
123
|
+
log.warn("[fastagent] agentcore: http.run has no effect here — routines fire through the forwarder's " +
|
|
124
|
+
"routine-fire envelope, which is gated by the ingress secret rather than served as an anonymous route.");
|
|
125
|
+
}
|
|
126
|
+
if (opened.publishControl) {
|
|
127
|
+
log.warn("[fastagent] agentcore: sessionControl is ON but /control/* is NOT served here — this host's only public " +
|
|
128
|
+
"ingress relays anonymous traffic as trusted, so the plane is not assembled behind it " +
|
|
129
|
+
"(docs/design/session-control.md §14)");
|
|
130
|
+
}
|
|
131
|
+
// Started here, not deferred to an envelope.
|
|
132
|
+
const routines = await loadServingRoutines(agentDir);
|
|
133
|
+
const scheduled = startSchedules(agent, stateRoot, routines, { externalClock: true });
|
|
21
134
|
const lazyChannels = async () => {
|
|
22
|
-
const lazy = await routesFor(agentDir, agent, stateRoot, sessionControl, {
|
|
135
|
+
const lazy = await routesFor(agentDir, agent, stateRoot, sessionControl, { http: { invoke: false } });
|
|
23
136
|
if (lazy.longConnections.length > 0) {
|
|
24
137
|
throw new Error(`long-connection channel(s) ${lazy.longConnections.map((c) => c.name).join(", ")} cannot serve on ` +
|
|
25
138
|
`AgentCore (scale-to-zero severs resident connections) — use the channel's webhook form`);
|
|
26
139
|
}
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
assertNoControlPlaneCollision(lazy.routes, plane);
|
|
31
|
-
return { routes: lazy.routes, mounts: withControl.mounts };
|
|
140
|
+
// CHANNELS ONLY — nothing unverified rides the public relay. `lazy.unverified` (here just `GET /health`) is dropped
|
|
141
|
+
// with the control plane, for the same reason: what arrives through that URL is anonymous.
|
|
142
|
+
return { routes: lazy.selfVerifying };
|
|
32
143
|
};
|
|
33
|
-
// The adapter registers process-global listeners; this is what takes them down on close.
|
|
34
|
-
const closed = new AbortController();
|
|
35
144
|
const adapterRoutes = mountAgentcore({
|
|
36
|
-
signal: closed.signal,
|
|
37
145
|
agent,
|
|
38
146
|
stateRoot,
|
|
39
|
-
|
|
147
|
+
routines: scheduled.routines,
|
|
40
148
|
onStateReady: options.onStateReady,
|
|
41
149
|
channels: lazyChannels,
|
|
42
150
|
});
|
|
43
|
-
|
|
151
|
+
// SELF-VERIFYING, not unverified: both paths are reached only through `InvokeAgentRuntime`, which AWS gates with
|
|
152
|
+
// IAM. Putting them in the other table applied our JSON body gate to `POST /invocations` (whose content type is
|
|
153
|
+
// AWS's to set) and reported two IAM-protected paths as authenticating nobody.
|
|
154
|
+
const handler = router({ selfVerifying: adapterRoutes });
|
|
44
155
|
log.info(`[fastagent] agentcore: serving POST /invocations + GET /ping (FASTAGENT_AGENTCORE=1)`);
|
|
45
156
|
return {
|
|
46
157
|
handler,
|
|
47
158
|
agent,
|
|
48
|
-
// The adapter IS the surface here; the channel routes arrive lazily BEHIND it.
|
|
49
|
-
// would make the startup line claim nothing is served.
|
|
159
|
+
// The adapter IS the surface here; the channel routes arrive lazily BEHIND it.
|
|
50
160
|
routes: adapterRoutes,
|
|
51
161
|
agentDir,
|
|
52
162
|
workspace,
|
|
53
|
-
//
|
|
54
|
-
channels: { routes: [], longConnections: []
|
|
55
|
-
|
|
163
|
+
// Channels remain lazy until the adapter receives trusted ingress.
|
|
164
|
+
channels: { routes: [], longConnections: [] },
|
|
165
|
+
// NONE: the adapter's two paths are IAM-gated and the channels behind them verify their own platform.
|
|
166
|
+
unverifiedRoutes: [],
|
|
167
|
+
// No `controlPrefix`: it is not served here, so nothing may report a prefix a caller could dial.
|
|
168
|
+
routines: scheduled.routines,
|
|
56
169
|
ready: Promise.resolve(), // nothing to open: no port of our own, no resident connections
|
|
57
|
-
...(withControl.control ? { control: withControl.control } : {}),
|
|
58
170
|
async close() {
|
|
59
|
-
// UNTESTED, deliberately noted: no test observes these timers being cleared. Installing fake
|
|
60
|
-
// timers early enough to count them deadlocks the assembly's own IO. What IS tested is that
|
|
61
|
-
// close() runs and is idempotent; the stop itself rides on scheduler.stop()'s own tests.
|
|
62
171
|
scheduled.stop();
|
|
63
|
-
closed.abort();
|
|
64
172
|
},
|
|
65
173
|
};
|
|
66
174
|
}
|
|
67
175
|
/**
|
|
68
|
-
* Mount the AgentCore Runtime adapter (`POST /invocations` + `GET /ping`) — the deployed container's
|
|
69
|
-
*
|
|
70
|
-
* by the generated deploy artifacts, never by hand).
|
|
71
|
-
*
|
|
72
|
-
* The adapter IS the surface: the agent's channels live in a table INSIDE the envelope dispatch, a
|
|
73
|
-
* separate namespace from these two paths, so a channel route named `/invocations` is reached
|
|
74
|
-
* through the Function URL as itself and cannot shadow anything.
|
|
176
|
+
* Mount the AgentCore Runtime adapter (`POST /invocations` + `GET /ping`) — the deployed container's ONLY reachable
|
|
177
|
+
* surface (channels/agentcore.ts).
|
|
75
178
|
*/
|
|
76
179
|
export function mountAgentcore(options) {
|
|
77
|
-
const { agent, stateRoot,
|
|
180
|
+
const { agent, stateRoot, routines, onStateReady, channels } = options;
|
|
181
|
+
// OCCURRENCE SEMANTICS, because this host's clock is ours: `deploy` wrote the EventBridge rule and the forwarder
|
|
182
|
+
// relays its instant behind the ingress secret, so the slot is a real grid point of that schedule and a claim
|
|
183
|
+
// means something (dedup across redeliveries, a fire history, the overlap policy). `POST /run` — the
|
|
184
|
+
// unauthenticated API — is NOT this and is not served here at all.
|
|
185
|
+
const fireRoutine = async (name, occurrence) => {
|
|
186
|
+
const routine = routines.find((r) => r.name === name);
|
|
187
|
+
if (!routine)
|
|
188
|
+
return unknownRoutine(name, routines);
|
|
189
|
+
const outcome = await Effect.runPromise(fireRoutineOnce({ agent, stateRoot, routine, slot: occurrence }).pipe(Effect.mapError((e) => e.cause))).catch((cause) => {
|
|
190
|
+
// `fireRoutineOnce`'s only failure is a claim-state fault, which happens BEFORE any claim exists — the
|
|
191
|
+
// occurrence is unburned, so the forwarder's throw makes EventBridge retry it, which is the right answer.
|
|
192
|
+
log.error(`[schedule] firing "${name}" for ${occurrence.toISOString()} failed: ${String(cause)}`);
|
|
193
|
+
return undefined;
|
|
194
|
+
});
|
|
195
|
+
if (outcome === undefined) {
|
|
196
|
+
return text(`routine "${name}": claim state unavailable, nothing was claimed — retry\n`, 500);
|
|
197
|
+
}
|
|
198
|
+
return Response.json({ slot: occurrence.toISOString(), ...outcome });
|
|
199
|
+
};
|
|
200
|
+
// BY NAME, on the IAM door. `POST /run`'s contract, through the only ingress this host has for a caller AWS has
|
|
201
|
+
// already identified — which makes it stricter here than the anonymous route other hosts publish. A routine
|
|
202
|
+
// without a cron is reachable ONLY this way on this host, and that is the point: nothing is unreachable.
|
|
203
|
+
const runRoutine = async (name) => {
|
|
204
|
+
const routine = routines.find((r) => r.name === name);
|
|
205
|
+
if (!routine)
|
|
206
|
+
return unknownRoutine(name, routines);
|
|
207
|
+
return Response.json(await runRoutineByName(agent, routine));
|
|
208
|
+
};
|
|
78
209
|
return agentcoreRoutes({
|
|
79
210
|
channels,
|
|
80
211
|
agent,
|
|
81
212
|
stateRoot,
|
|
82
213
|
isBusy: () => activeWork() > 0,
|
|
83
|
-
//
|
|
84
|
-
// the state root is restored from (and pushed to) an S3 snapshot through presigned URLs the
|
|
85
|
-
// forwarder mints per envelope. Always wired on this path — the platform gives no other way to
|
|
86
|
-
// keep an agent's memory across a deploy.
|
|
87
|
-
stateSync: createStateSync({ stateRoot }),
|
|
88
|
-
// What separates a forwarder envelope from any IAM principal's InvokeAgentRuntime call. Absent =
|
|
89
|
-
// no forwarder in this topology, so only the public `invoke` kind is servable.
|
|
214
|
+
// What separates a forwarder envelope from any IAM principal's InvokeAgentRuntime call.
|
|
90
215
|
ingressSecret: process.env.FASTAGENT_INGRESS_SECRET,
|
|
91
216
|
onStateReady,
|
|
92
|
-
...(
|
|
93
|
-
fire: schedules.length === 0
|
|
94
|
-
? undefined
|
|
95
|
-
: (name, slot) => {
|
|
96
|
-
const schedule = schedules.find((s) => s.name === name);
|
|
97
|
-
if (!schedule)
|
|
98
|
-
throw new UnknownScheduleError(name);
|
|
99
|
-
return fireScheduleOnce({ agent, stateRoot, schedule, slot });
|
|
100
|
-
},
|
|
217
|
+
...(routines.length > 0 ? { fireRoutine, runRoutine } : {}),
|
|
101
218
|
});
|
|
102
219
|
}
|