@fastagent-sh/fastagent 0.24.3 → 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 -517
- 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 -83
- package/dist/engines/pi/turn-kit.js +0 -275
- package/dist/engines/pi/wake-tool.d.ts +0 -9
- package/dist/engines/pi/wake-tool.js +0 -92
- package/dist/loader.d.ts +0 -41
- package/dist/loader.js +0 -116
- package/dist/node.d.ts +0 -3
- package/dist/node.js +0 -4
- package/dist/observe.d.ts +0 -6
- package/dist/observe.js +0 -47
- package/dist/pi.d.ts +0 -25
- package/dist/pi.js +0 -15
- package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
- package/dist/schedule/discover.d.ts +0 -17
- package/dist/schedule/discover.js +0 -61
- package/dist/schedule/routine.d.ts +0 -43
- package/dist/schedule/routine.js +0 -32
- package/dist/schedule/run.d.ts +0 -55
- package/dist/schedule/run.js +0 -146
- package/dist/secrets-gate.d.ts +0 -32
- package/dist/secrets-gate.js +0 -33
- package/dist/session-remote.d.ts +0 -47
- package/dist/session-remote.js +0 -460
- package/dist/session.d.ts +0 -466
- package/dist/session.js +0 -57
- /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
package/dist/deploy/preflight.js
CHANGED
|
@@ -6,27 +6,82 @@ import { readFile } from "node:fs/promises";
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { basename, join, relative } from "node:path";
|
|
8
8
|
import { isModelSpec, isReleaseAgentName } from "./workspace.js";
|
|
9
|
-
import { providerOf } from "../engines/pi/config.js";
|
|
10
|
-
import { resolveAuthPath } from "../engines/pi/auth.js";
|
|
11
9
|
import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { createPiModelRuntime, definitionKeyOf, environmentAuthSource, literalKeyProviders, isBuiltinProvider, machineModels, globalCatalogPath, inGlobalCatalog, interactiveLoginKind, loginProviders, } from "../engines/pi/models.js";
|
|
10
|
+
import { resolveAuthPath } from "../harnesses/models/credentials.js";
|
|
11
|
+
import { environmentAuth, interactiveLoginKind, loginProviders, modelsOver, providerOf, } from "../harnesses/models/models.js";
|
|
12
|
+
import { modelEnvironment } from "../harnesses/models/environment.js";
|
|
13
|
+
import { buildDefinitionShape, definitionSecrets, prepareDefinition, } from "../harnesses/pi-durable/open.js";
|
|
14
|
+
import { LOCAL_CONTEXT_FILE, exists } from "../paths.js";
|
|
15
|
+
import { channelIngress } from "../channels/discover.js";
|
|
16
|
+
import { loadContent, loadEnvironment } from "../content/file.js";
|
|
20
17
|
import { CHANNEL_KINDS } from "../scaffold/add-channel.js";
|
|
21
|
-
import { detectRuntime, readPackageJson } from "../runtime.js";
|
|
18
|
+
import { RUNTIME_FLOOR, belowFloor, detectRuntime, listsFastagent, readPackageJson } from "../runtime.js";
|
|
22
19
|
import { fastagentVersion } from "../version.js";
|
|
23
20
|
import { isGeneratedDockerfile } from "./container.js";
|
|
24
21
|
import { buildContextPaths, checkKeptIgnoreFiles } from "./build-context.js";
|
|
25
|
-
import { CRON_CAN_BE_EXTERNAL, residencyFor } from "./residency.js";
|
|
26
22
|
import { dotEnvPath, loadEnvValues } from "../env.js";
|
|
27
23
|
import { deploymentSecrets, isEnvKey } from "./secrets.js";
|
|
28
|
-
import { DEFAULT_HTTP_PORT
|
|
29
|
-
import {
|
|
24
|
+
import { DEFAULT_HTTP_PORT } from "../service.js";
|
|
25
|
+
import { declaredVariables, declaresTool, runtimeAdvice } from "../environment/declare.js";
|
|
26
|
+
import { checkPackages, MISE_PACKAGE_NAMES, miseBinary, packageSpecs } from "../environment/mise.js";
|
|
27
|
+
/**
|
|
28
|
+
* What each content entry becomes on the host, one line each, so none is missing there unsaid (agent-model.md §3). A
|
|
29
|
+
* repository `context.json` declares is cloned there, as on this machine without a checkout bound. A directory only
|
|
30
|
+
* this machine binds stays here: the deployed agent works without it, which the line says and says how to change. One
|
|
31
|
+
* the agent works on is a warning, since the deployed agent lacks data it was meant to work on; one it only knows, a
|
|
32
|
+
* note.
|
|
33
|
+
*/
|
|
34
|
+
function checkContent({ declared, bound }, storageResets, report) {
|
|
35
|
+
for (const entry of declared) {
|
|
36
|
+
const fate = storageResets
|
|
37
|
+
? "cloned afresh on every deployment, since the host's storage starts over; what the agent did not push is lost"
|
|
38
|
+
: "cloned on the host, and brought up to date in place at each start";
|
|
39
|
+
report.note(`${roleOf(entry)} ${entry.name}: github ${entry.repo}${entry.ref ? `@${entry.ref}` : ""}, ${fate}`);
|
|
40
|
+
}
|
|
41
|
+
for (const binding of bound) {
|
|
42
|
+
if (binding.kind !== "local")
|
|
43
|
+
continue;
|
|
44
|
+
const stays = `${roleOf(binding)} ${binding.name}: a directory of this machine (${binding.path}, bound in ` +
|
|
45
|
+
`${LOCAL_CONTEXT_FILE}), stays here, and the deployed agent works without it`;
|
|
46
|
+
if (binding.readonly) {
|
|
47
|
+
report.note(`${stays}; to ship what the agent reads there, copy it into the agent directory, which every release carries`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
report.warn(`${stays}; to work on it from a host, move it to a GitHub repository and add it as github:owner/repo`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function roleOf(entry) {
|
|
55
|
+
return entry.readonly ? "knows" : "works on";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What the image needs to install the packages the agent declares: the agent's own mise for linux in its package.json,
|
|
59
|
+
* so the image's install brings it, packages FastAgent installs from, and a PyPI package's runtime. A machine without
|
|
60
|
+
* the agent's mise cannot check them, which is an issue (a warning without `--run`), as a code input that cannot load
|
|
61
|
+
* is.
|
|
62
|
+
*/
|
|
63
|
+
async function checkEnvironment(agentDir, packages, hasPackageJson, pkg, report) {
|
|
64
|
+
if (Object.keys(packages).length === 0)
|
|
65
|
+
return;
|
|
66
|
+
const linux = MISE_PACKAGE_NAMES.filter((name) => name.includes("-linux-"));
|
|
67
|
+
const missing = linux.filter((name) => !(name in (pkg.optionalDependencies ?? {})));
|
|
68
|
+
if (!hasPackageJson || missing.length > 0) {
|
|
69
|
+
report.issue(`context.json declares packages, and the image would have no mise to install them with: the agent's ` +
|
|
70
|
+
`package.json does not list ${missing.join(", ")}. Run \`fastagent env install\` in the agent directory.`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const bin = miseBinary(agentDir);
|
|
74
|
+
if (!bin) {
|
|
75
|
+
report.issue(`the agent's mise is not installed here, so the packages context.json declares cannot be checked. Run ` +
|
|
76
|
+
`\`fastagent env install\` in ${agentDir}, then deploy again.`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const { refusals, runtime } = await checkPackages(agentDir, bin, packages);
|
|
80
|
+
for (const refusal of refusals)
|
|
81
|
+
report.issue(refusal);
|
|
82
|
+
if (runtime)
|
|
83
|
+
report.issue(runtimeAdvice(runtime));
|
|
84
|
+
}
|
|
30
85
|
/** The pre-flight's one early exit: thrown by a check, turned into `{ ok: false, gate }` by {@link preflightDeploy}. */
|
|
31
86
|
class DeployGate {
|
|
32
87
|
text;
|
|
@@ -56,7 +111,7 @@ export async function preflightDeploy(input) {
|
|
|
56
111
|
}
|
|
57
112
|
}
|
|
58
113
|
async function gatherFacts(input, report) {
|
|
59
|
-
const {
|
|
114
|
+
const { agentDir, config, force, noResidentProcess, storageResets = false, servesApi = false } = input;
|
|
60
115
|
// The release manifest carries this name into the container, where it is joined onto the storage root — so `init`'s
|
|
61
116
|
// "one path segment" is not enough here.
|
|
62
117
|
if (!isReleaseAgentName(basename(agentDir))) {
|
|
@@ -64,18 +119,26 @@ async function gatherFacts(input, report) {
|
|
|
64
119
|
`may use only letters, digits, "-" and "_"; rename it (the fastagent.config.ts inside is what ` +
|
|
65
120
|
`makes it an agent, never its name)`);
|
|
66
121
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
//
|
|
122
|
+
const content = loadContent(agentDir);
|
|
123
|
+
checkContent(content, storageResets, report);
|
|
124
|
+
// The definition the box will load on every start, read here first: a refusal in it (a module that fails to import,
|
|
125
|
+
// two tools under one name) builds a perfectly good image that crash-loops on fly/railway/docker and fails every
|
|
70
126
|
// invocation on AgentCore. This is the boundary that turns that refusal into a deploy finding, so the error is
|
|
71
127
|
// carried whole into the issue rather than rethrown.
|
|
72
|
-
await
|
|
73
|
-
report.issue(`the definition does not load, so the deployed agent would not start: ${
|
|
128
|
+
const definition = await prepareDefinition(agentDir).catch((error) => {
|
|
129
|
+
report.issue(`the definition does not load, so the deployed agent would not start: ${messageOf(error)}`);
|
|
130
|
+
return undefined;
|
|
74
131
|
});
|
|
75
132
|
// The model this deployment will run on, and where it came from. Resolved HERE so the plan side and the run side
|
|
76
133
|
// cannot disagree about it.
|
|
77
|
-
const valueFile = relative(
|
|
134
|
+
const valueFile = relative(agentDir, dotEnvPath(agentDir));
|
|
78
135
|
const values = loadEnvValues(dotEnvPath(agentDir));
|
|
136
|
+
// A host has no credential of the author's: what it clones with is GITHUB_TOKEN, which travels like every value.
|
|
137
|
+
if (content.declared.length > 0 && !values.get("GITHUB_TOKEN")) {
|
|
138
|
+
report.note(`no GITHUB_TOKEN in ${valueFile}: the host clones without a credential, which reaches public repositories ` +
|
|
139
|
+
`only, and the agent cannot push from its clones. Set one there to give it access; it travels with the ` +
|
|
140
|
+
`other values`);
|
|
141
|
+
}
|
|
79
142
|
const model = resolveDeployModel(config, values, valueFile);
|
|
80
143
|
if (model.invalid !== undefined) {
|
|
81
144
|
// A gate rather than a warning even without `--run`: the release manifest validates the spec on the way out, so
|
|
@@ -99,32 +162,12 @@ async function gatherFacts(input, report) {
|
|
|
99
162
|
report.note(`model ${model.spec} (source: ${model.source})`);
|
|
100
163
|
}
|
|
101
164
|
const modelSpec = model.spec;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// What the PUBLIC host URL answers with no authentication of ours, NAMED FROM WHAT WILL ACTUALLY MOUNT (through the
|
|
109
|
-
// same `shouldServeRun` the assembly uses), never from the host alone: listing an endpoint this deployment does not
|
|
110
|
-
// serve is how an operator learns to skim past every deploy warning — the same reason `publicUrl` exists.
|
|
111
|
-
const unauthenticated = describeAnonymousSurface({
|
|
112
|
-
invoke: config.http?.invoke !== false,
|
|
113
|
-
run: loadedRoutines.routines.length > 0 && shouldServeRun(config.http),
|
|
114
|
-
...(config.sessionControl === true ? { controlPrefix: CONTROL_PREFIX } : {}),
|
|
115
|
-
});
|
|
116
|
-
if (publicUrl && unauthenticated.length > 0) {
|
|
117
|
-
report.warn(`the deployed box answers ${unauthenticated.join(" and ")} at its public URL, UNAUTHENTICATED — ` +
|
|
118
|
-
`fastagent authenticates nothing. Put a gateway, an IdP-backed proxy or a private network in front of that ` +
|
|
119
|
-
`URL (docs/design/session-control.md §14)`);
|
|
120
|
-
}
|
|
121
|
-
// Known channel kinds only — a custom channel's webhook (and, unless it declared them, its secrets) are unknown to
|
|
122
|
-
// us; note and let the author wire them.
|
|
123
|
-
const inspected = await inspectChannels(agentDir);
|
|
124
|
-
if (inspected.failures.length > 0) {
|
|
125
|
-
throw new Error(`cannot inspect channel modules: ${inspected.failures.map((failure) => `${failure.label}: ${failure.message}`).join("; ")}`);
|
|
126
|
-
}
|
|
127
|
-
const channels = inspected.channels;
|
|
165
|
+
const declaredSecrets = definition ? definitionSecrets(definition) : declaredVariables(loadEnvironment(agentDir));
|
|
166
|
+
const code = definition ? buildShapes(definition, values, report) : undefined;
|
|
167
|
+
const channels = [...(code?.channels ?? [])].map(([name, module]) => ({
|
|
168
|
+
name,
|
|
169
|
+
ingress: channelIngress(module),
|
|
170
|
+
}));
|
|
128
171
|
for (const { name, ingress } of channels) {
|
|
129
172
|
if (CHANNEL_KINDS.includes(name))
|
|
130
173
|
continue;
|
|
@@ -134,54 +177,34 @@ async function gatherFacts(input, report) {
|
|
|
134
177
|
: `route channel "${name}" is custom — ${secretsPart}; configure its webhook yourself`);
|
|
135
178
|
}
|
|
136
179
|
const longConnectionChannels = channels.filter((c) => c.ingress === "long-connection").map((c) => c.name);
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
// A FAILED file still counts, on the conservative side: it may well declare a cron, and a plan that scaled to
|
|
143
|
-
// zero because the file did not parse would hide that behind silence.
|
|
144
|
-
const hasCron = loadedRoutines.routines.some((r) => r.cron !== undefined) || loadedRoutines.failures.length > 0;
|
|
145
|
-
if (longConnectionChannels.length > 0 && !externalClock) {
|
|
180
|
+
// Counted from the registry when the code did not build: a plan that scaled to zero because a schedule failed to
|
|
181
|
+
// build on deploy day would sleep through it once it is fixed.
|
|
182
|
+
const hasCron = (code?.schedules.length ?? definition?.registry.schedules.length ?? 0) > 0;
|
|
183
|
+
if (longConnectionChannels.length > 0 && !noResidentProcess) {
|
|
146
184
|
report.note(`long-connection channel present (${longConnectionChannels.join(", ")}) — a GENERATED plan keeps one machine running ` +
|
|
147
185
|
`(an outbound connection cannot wake a scaled-to-zero service).`);
|
|
148
186
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (hasCron && !externalClock) {
|
|
152
|
-
const wayOut = residencyFor({ channels, hasCron })?.reason === CRON_CAN_BE_EXTERNAL;
|
|
153
|
-
report.note(`routines/ present — a GENERATED plan keeps one machine running (nothing wakes this box at a cron ` +
|
|
154
|
-
`instant).` +
|
|
155
|
-
(wayOut
|
|
156
|
-
? ` To scale to zero instead, keep the time in a scheduler you own and let it call ` +
|
|
157
|
-
`\`POST /run\` (an API that runs one declared unit of work by name — docs/api-reference.md#post-run).`
|
|
158
|
-
: ""));
|
|
187
|
+
if (hasCron && !noResidentProcess) {
|
|
188
|
+
report.note(`src/schedules/ present — a GENERATED plan keeps one machine running (nothing wakes this box at a cron instant).`);
|
|
159
189
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const deployed = await createPiModelRuntime({
|
|
165
|
-
agentDir,
|
|
166
|
-
credentials: new InMemoryCredentialStore(),
|
|
167
|
-
machineLayer: false,
|
|
168
|
-
});
|
|
169
|
-
if (modelSpec)
|
|
170
|
-
await checkGlobalCatalogModel(modelSpec, deployed, report);
|
|
171
|
-
const authPath = resolveAuthPath(agentDir);
|
|
172
|
-
// A model only the definition's extensions declare (a virtual model, or a provider one registers) is authenticated
|
|
173
|
-
// by that code on the box, per request: a virtual model's credential is whichever physical model it routes to.
|
|
174
|
-
// Asked of the catalog the box builds, extensions included, so deploy judges the model the box will run.
|
|
175
|
-
const fromExtension = modelSpec ? await extensionDeclared(agentDir, workspace, modelSpec, deployed) : false;
|
|
176
|
-
if (fromExtension && modelSpec) {
|
|
177
|
-
report.note(`${modelSpec} is declared by the definition's extensions, which resolve its credentials on the box — deploy ` +
|
|
178
|
-
`cannot check them. Set the keys the models it uses need in ${valueFile}`);
|
|
190
|
+
if (code !== undefined && channels.length === 0 && !hasCron && !servesApi) {
|
|
191
|
+
report.warn(`no channel in src/channels/, no schedule in src/schedules/, and no Agent API on this deployment: nothing ` +
|
|
192
|
+
`reaches it but its health check. Add a channel (\`fastagent add telegram|slack|feishu|lark\`, or your ` +
|
|
193
|
+
`own) or a schedule; an agent used only from an app is embedded in it (openAgent), not deployed`);
|
|
179
194
|
}
|
|
180
|
-
const
|
|
195
|
+
const providers = code?.providers ?? [];
|
|
196
|
+
const authPath = resolveAuthPath(agentDir);
|
|
197
|
+
const route = modelSpec ? await credentialRoute(modelSpec, values, providers) : {};
|
|
181
198
|
const modelAuth = route.envVar;
|
|
182
199
|
const boxLogin = route.boxLogin;
|
|
200
|
+
if (modelSpec && code) {
|
|
201
|
+
await checkModelExists(agentDir, modelSpec, providers, report, {
|
|
202
|
+
credentialShips: boxLogin === undefined,
|
|
203
|
+
valueFile,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
183
206
|
if (boxLogin !== undefined) {
|
|
184
|
-
if (!hasLogin(boxLogin)) {
|
|
207
|
+
if (!hasLogin(boxLogin, providers)) {
|
|
185
208
|
report.issue(`no credential for ${modelSpec} reaches the deployment, and "${boxLogin}" has no login to run there — set ` +
|
|
186
209
|
`its API key in ${valueFile}`);
|
|
187
210
|
}
|
|
@@ -191,39 +214,36 @@ async function gatherFacts(input, report) {
|
|
|
191
214
|
`(\`fastagent login ${boxLogin} --deployment\`). Or set ${boxLogin}'s API key in ${valueFile}`);
|
|
192
215
|
}
|
|
193
216
|
}
|
|
194
|
-
// Reported, not refused. Whether a string is a credential is the AUTHOR's knowledge: pi's docs prescribe
|
|
195
|
-
// `"apiKey": "ollama"` for a keyless local server, and no static rule separates that from a leaked key. The
|
|
196
|
-
// `.dockerignore` check next to this one IS a gate because it catches a packing rule putting FastAgent's OWN
|
|
197
|
-
// `.secrets/auth.json` into the image — the framework's doing. This is the author's own committed file.
|
|
198
|
-
// Asked of EVERY provider, not just the selected model's: the file ships whole.
|
|
199
|
-
const literalKeys = await literalKeyProviders(agentDir);
|
|
200
|
-
if (literalKeys.length > 0) {
|
|
201
|
-
report.warn(`${AGENT_MODELS_FILE} carries a literal apiKey for ${literalKeys.map((id) => `"${id}"`).join(", ")} — that ` +
|
|
202
|
-
`file ships inside the image, where anyone who can pull it reads the layer. If it is a credential, use ` +
|
|
203
|
-
`"$YOUR_ENV_VAR" (deploy carries it like any provider key) or "!command" (it runs on the box and never ` +
|
|
204
|
-
`travels); a placeholder for a keyless local server is fine as it is.`);
|
|
205
|
-
}
|
|
206
217
|
// Container facts (shared by every host) + the warnings that follow.
|
|
207
218
|
const hasPackageJson = await exists(join(agentDir, "package.json"));
|
|
208
219
|
const pkg = await readPackageJson(agentDir);
|
|
209
220
|
const { runtime, bunVersion, hasLockfile } = detectRuntime(agentDir, pkg);
|
|
221
|
+
const kept = await keptDockerfile(agentDir);
|
|
222
|
+
// Only the generated image runs the pinned Bun: a hand-written Dockerfile names its own base.
|
|
223
|
+
if (kept === undefined &&
|
|
224
|
+
runtime === "bun" &&
|
|
225
|
+
bunVersion !== undefined &&
|
|
226
|
+
belowFloor(bunVersion, RUNTIME_FLOOR.bun)) {
|
|
227
|
+
report.issue(`package.json pins bun@${bunVersion}, which the image runs (oven/bun:${bunVersion}), and the agent's storage is ` +
|
|
228
|
+
`node:sqlite, which Bun ships from ${RUNTIME_FLOOR.bun}: the container would fail at start. Pin ` +
|
|
229
|
+
`bun@${RUNTIME_FLOOR.bun} or later`);
|
|
230
|
+
}
|
|
210
231
|
const install = runtime === "bun" ? "bun install" : "npm install";
|
|
211
232
|
const runner = runtime === "bun" ? "bun run fastagent" : "./node_modules/.bin/fastagent";
|
|
212
233
|
const hasOtherLock = runtime === "node" &&
|
|
213
234
|
((await exists(join(agentDir, "pnpm-lock.yaml"))) || (await exists(join(agentDir, "yarn.lock"))));
|
|
214
|
-
// Does the baked
|
|
215
|
-
const shipsGit = await exists(join(
|
|
235
|
+
// Does the baked agent directory ship a `.git`?
|
|
236
|
+
const shipsGit = await exists(join(agentDir, ".git"));
|
|
216
237
|
// After the facts: the deps sentence must match the agent's actual shape (a markdown-only agent has no package.json
|
|
217
238
|
// and installs nothing — the note must not point at a file that doesn't exist).
|
|
218
239
|
const deps = hasPackageJson
|
|
219
|
-
? `
|
|
240
|
+
? `its package.json dependencies are installed in the image`
|
|
220
241
|
: `the agent has no package.json, so no deps are installed (the pinned global CLI serves the directory)`;
|
|
221
242
|
// What a RELEASE does — host-neutral, because how long the storage under it lives is the host's own answer and its
|
|
222
243
|
// runbook gives it (a Fly volume outlives every deploy; AgentCore's mount does not).
|
|
223
|
-
report.note(`the
|
|
224
|
-
`
|
|
225
|
-
`
|
|
226
|
-
`how long, see this host's storage note below`);
|
|
244
|
+
report.note(`the agent directory is baked as the definition (WYSIWYG — what you see is what ships, git or not, clean or ` +
|
|
245
|
+
`not); ${deps}. Every release replaces the deployed definition and leaves the instance's state and ` +
|
|
246
|
+
`credentials in place — for how long, see this host's storage note below`);
|
|
227
247
|
// A code agent with no lockfile builds via a non-frozen install (ranges resolve at build time) — not reproducible.
|
|
228
248
|
if (hasPackageJson && !hasLockfile) {
|
|
229
249
|
const lock = runtime === "bun" ? "bun.lock" : "package-lock.json";
|
|
@@ -234,17 +254,31 @@ async function gatherFacts(input, report) {
|
|
|
234
254
|
`Run \`${install}\` and commit the lockfile for pinned redeploys.`);
|
|
235
255
|
}
|
|
236
256
|
// The code-path Dockerfile runs `${runner}`.
|
|
237
|
-
if (hasPackageJson && !(
|
|
257
|
+
if (hasPackageJson && !listsFastagent(pkg)) {
|
|
238
258
|
report.warn(`package.json does not list @fastagent-sh/fastagent — the image's \`${runner}\` has no local bin to run, ` +
|
|
239
259
|
`so the container fails at start. Add it to dependencies and re-run \`${install}\`.`);
|
|
240
260
|
}
|
|
241
|
-
const paths = await buildContextPaths(
|
|
242
|
-
await checkKeptIgnoreFiles({
|
|
243
|
-
|
|
244
|
-
const
|
|
261
|
+
const paths = await buildContextPaths(agentDir, authPath);
|
|
262
|
+
await checkKeptIgnoreFiles({ agentDir, force, paths }, report);
|
|
263
|
+
const environment = loadEnvironment(agentDir);
|
|
264
|
+
const packages = environment?.packages ?? {};
|
|
265
|
+
await checkEnvironment(agentDir, packages, hasPackageJson, pkg, report);
|
|
266
|
+
const installsPackages = Object.keys(packages).length > 0;
|
|
267
|
+
// Write-back mechanics are fastagent's (the policy is the agent's prompt's). A repository content entry is cloned on the
|
|
268
|
+
// host, which takes git too. mise downloads over TLS, and the slim base image has no CA certificates; rustup, which
|
|
269
|
+
// mise installs rust with, downloads with curl. The agent's own apt packages join them, in one layer.
|
|
270
|
+
const needsGit = shipsGit || content.declared.length > 0;
|
|
271
|
+
const apt = [
|
|
272
|
+
...new Set([
|
|
273
|
+
...(needsGit ? ["git"] : []),
|
|
274
|
+
...(installsPackages ? ["ca-certificates"] : []),
|
|
275
|
+
...(declaresTool(packages, "rust") ? ["curl"] : []),
|
|
276
|
+
...(environment?.aptPackages ?? []),
|
|
277
|
+
]),
|
|
278
|
+
];
|
|
245
279
|
const container = {
|
|
246
280
|
releaseId: randomUUID(),
|
|
247
|
-
|
|
281
|
+
agent: basename(agentDir),
|
|
248
282
|
machineryPaths: paths.machineryPaths,
|
|
249
283
|
hasPackageJson,
|
|
250
284
|
runtime,
|
|
@@ -252,30 +286,13 @@ async function gatherFacts(input, report) {
|
|
|
252
286
|
hasLockfile,
|
|
253
287
|
version: await fastagentVersion(),
|
|
254
288
|
apt,
|
|
289
|
+
...(installsPackages ? { packages: packageSpecs(packages) } : {}),
|
|
255
290
|
...(model.envValue !== undefined ? { modelSpec: model.envValue } : {}),
|
|
256
291
|
shipsGit,
|
|
257
292
|
};
|
|
258
293
|
const port = config.http?.port ?? DEFAULT_HTTP_PORT;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// file's declaration is dropped in both places).
|
|
262
|
-
const resolvedTools = await resolveAgentTools(config, agentDir, workspace);
|
|
263
|
-
// A code input we could not READ is a code input whose declarations we cannot carry — and the box
|
|
264
|
-
// WILL read it (its deps are installed there), so its gate fires after the deploy reported success:
|
|
265
|
-
// a crash loop, which is the failure mode this whole mechanism exists to move to build time. Under
|
|
266
|
-
// `--run` that is a gate, like a channel that fails to inspect; generate-only warns, since the
|
|
267
|
-
// operator may be producing artifacts from a machine that never installed the agent's deps.
|
|
268
|
-
for (const failure of [...resolvedTools.toolFailures, ...loadedRoutines.failures]) {
|
|
269
|
-
const issue = `${failure.label} failed to load (${failure.message}) — any secrets it declares cannot be carried ` +
|
|
270
|
-
`to the host, so the deployed box would refuse to start`;
|
|
271
|
-
report.issue(issue);
|
|
272
|
-
}
|
|
273
|
-
const declaredSecrets = [
|
|
274
|
-
...allSecrets(resolvedTools.toolSecrets),
|
|
275
|
-
...allSecrets(loadedRoutines.secrets),
|
|
276
|
-
...allSecrets(inspected.secrets),
|
|
277
|
-
];
|
|
278
|
-
await checkKeptDockerfile(agentDir, config, model.envValue, valueFile, report);
|
|
294
|
+
if (kept !== undefined)
|
|
295
|
+
checkKeptDockerfile(kept, agentDir, model.envValue, valueFile, report);
|
|
279
296
|
return {
|
|
280
297
|
channels,
|
|
281
298
|
hasCron,
|
|
@@ -289,84 +306,89 @@ async function gatherFacts(input, report) {
|
|
|
289
306
|
secrets: deploymentSecrets(modelAuth, declaredSecrets, values, valueFile),
|
|
290
307
|
};
|
|
291
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* The definition's code built for its SHAPE with the value file's values (`buildDefinitionShape`): a generate-only
|
|
311
|
+
* deploy runs on a machine that may hold none of them, and whether the value file supplies them is the values gate's
|
|
312
|
+
* question, asked by name. A build that fails is an issue, as a module that fails to load is.
|
|
313
|
+
*/
|
|
314
|
+
function buildShapes(definition, values, report) {
|
|
315
|
+
try {
|
|
316
|
+
return buildDefinitionShape(definition, Object.fromEntries(values));
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
report.issue(`the definition does not build, so the deployed agent would not start: ${messageOf(error)}`);
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function messageOf(error) {
|
|
324
|
+
return error instanceof Error ? error.message : String(error);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* A model no provider of the deployed agent offers fails every message there. Its lists are this machine's: what it
|
|
328
|
+
* kept and what it fetches now, a provider's own list with whatever credential this machine's environment holds. A
|
|
329
|
+
* model only a fetched list has is the box's only once the box fetches that list, which a box with nothing kept does as
|
|
330
|
+
* it opens: pi's published catalog with no credential, a provider's own list with the one this deploy carries, since a
|
|
331
|
+
* login on the box comes after the start it would refuse. Whether a model is only in a provider's own list cannot be
|
|
332
|
+
* told here: a provider in code keeps what it fetched in its own object, which every `Models` over it then lists.
|
|
333
|
+
*/
|
|
334
|
+
async function checkModelExists(agentDir, spec, providers, report, box) {
|
|
335
|
+
const provider = providerOf(spec);
|
|
336
|
+
const modelId = spec.slice(provider.length + 1);
|
|
337
|
+
const { models } = await modelEnvironment(agentDir, { credentialStore: new InMemoryCredentialStore() }, providers);
|
|
338
|
+
if (models.getModel(provider, modelId)) {
|
|
339
|
+
if (models.getProvider(provider)?.refreshModels !== undefined) {
|
|
340
|
+
if (box.credentialShips)
|
|
341
|
+
return;
|
|
342
|
+
report.warn(`provider "${provider}" fetches its own model list, which a box with no list kept, as on a first deploy, ` +
|
|
343
|
+
`fetches as it starts, with a credential this deploy does not carry (a login on the box comes after the ` +
|
|
344
|
+
`start): if "${spec}" is only in that list, the box refuses to start on it until ${provider}'s API key is ` +
|
|
345
|
+
`in ${box.valueFile}`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (modelsOver(new InMemoryCredentialStore(), providers).getModel(provider, modelId))
|
|
349
|
+
return;
|
|
350
|
+
report.warn(`"${spec}" is in pi's published catalog, not in the pi-ai this FastAgent carries: a box with no catalog kept, ` +
|
|
351
|
+
`as on a first deploy, fetches it from pi.dev as it starts, and refuses to start on "${spec}" if it cannot`);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
report.issue(`no provider of the deployed agent offers "${spec}" — pi-ai's built-ins, pi's published catalog and the ` +
|
|
355
|
+
`providers fastagent.config.ts declares are all it has`);
|
|
356
|
+
}
|
|
292
357
|
/**
|
|
293
358
|
* HOW THE MODEL'S CREDENTIAL REACHES THE BOX, from what the deploy ships and nothing else. The box is the one authority
|
|
294
359
|
* on what it authenticates with (pi ranks a stored login above the environment, and only the box knows what it has
|
|
295
360
|
* stored, which platform variables it was given, or what role it runs as), so this never asks what authenticates the
|
|
296
361
|
* model on THIS machine: its stored logins and the shell running deploy do not travel. In order:
|
|
297
362
|
*
|
|
298
|
-
* 1.
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* 3. otherwise the box answers — `boxLogin`: after readiness, `--run` asks it, and it logs in only if it cannot
|
|
363
|
+
* 1. a credential the provider reads from the value file alone (a definition's provider included, which may resolve
|
|
364
|
+
* its key from a secret it declared): its key variable (which the values gate then requires, as it does every
|
|
365
|
+
* declared name), or a keyless one such as `AWS_ACCESS_KEY_ID` — never one that needs a file, which would be this
|
|
366
|
+
* machine's;
|
|
367
|
+
* 2. otherwise the box answers — `boxLogin`: after readiness, `--run` asks it, and it logs in only if it cannot
|
|
304
368
|
* already authenticate the provider.
|
|
305
369
|
*/
|
|
306
|
-
async function credentialRoute(
|
|
370
|
+
async function credentialRoute(spec, values, providers) {
|
|
307
371
|
const provider = providerOf(spec);
|
|
308
|
-
const declared = await definitionKeyOf(agentDir, provider);
|
|
309
|
-
if (declared)
|
|
310
|
-
return "reference" in declared ? { envVar: declared.reference } : {};
|
|
311
372
|
// No file of this machine's travels, so a source that needs one (Google ADC, an AWS profile) is the box's to find.
|
|
312
|
-
const fromValues = await
|
|
313
|
-
if (fromValues !== undefined)
|
|
314
|
-
return isEnvKey(fromValues) ? { envVar: fromValues } : {};
|
|
373
|
+
const fromValues = await environmentAuth(provider, Object.fromEntries(values), async () => false, providers);
|
|
374
|
+
if (fromValues !== undefined) {
|
|
375
|
+
return fromValues.source !== undefined && isEnvKey(fromValues.source) ? { envVar: fromValues.source } : {};
|
|
376
|
+
}
|
|
315
377
|
return { boxLogin: provider };
|
|
316
378
|
}
|
|
317
|
-
/** Whether `spec` exists only once the definition's extensions have registered their models. */
|
|
318
|
-
async function extensionDeclared(agentDir, workspace, spec, deployed) {
|
|
319
|
-
const provider = providerOf(spec);
|
|
320
|
-
const id = spec.slice(provider.length + 1);
|
|
321
|
-
if (deployed.getModel(provider, id))
|
|
322
|
-
return false;
|
|
323
|
-
const catalog = await agentModels(agentDir, { credentialStore: new InMemoryCredentialStore() }, { machineLayer: false, cwd: workspace }).runtime();
|
|
324
|
-
return catalog.getModel(provider, id) !== undefined;
|
|
325
|
-
}
|
|
326
379
|
/** Does `provider` offer an interactive login, i.e. can `fastagent login --deployment` authenticate it on the box? */
|
|
327
|
-
function hasLogin(providerId) {
|
|
328
|
-
const provider = loginProviders().find((p) => p.id === providerId);
|
|
380
|
+
function hasLogin(providerId, providers) {
|
|
381
|
+
const provider = loginProviders(providers).find((p) => p.id === providerId);
|
|
329
382
|
return provider !== undefined && interactiveLoginKind(provider) !== "none";
|
|
330
383
|
}
|
|
331
|
-
/**
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
async function checkMachineModels(agentDir, modelSpec, report) {
|
|
337
|
-
const machine = await machineModels(agentDir);
|
|
338
|
-
const provider = modelSpec ? providerOf(modelSpec) : undefined;
|
|
339
|
-
if (provider === undefined || !machine?.inherited.includes(provider))
|
|
340
|
-
return;
|
|
341
|
-
if (isBuiltinProvider(provider)) {
|
|
342
|
-
report.warn(`model "${modelSpec}" takes its "${provider}" entry from ${machine.path}, the machine's models.json, which ` +
|
|
343
|
-
`does not ship — the deployed agent runs pi's built-in "${provider}" without it. Declare the entry in the ` +
|
|
344
|
-
`agent's own ${AGENT_MODELS_FILE} to deploy it.`);
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
// No built-in to fall back on: the deployed agent cannot resolve the model at all.
|
|
348
|
-
const issue = `model "${modelSpec}" exists only in ${machine.path}, the machine's models.json, which does not ship — the ` +
|
|
349
|
-
`deployed agent would fail with an unknown model. Declare "${provider}" in the agent's own ` +
|
|
350
|
-
`${AGENT_MODELS_FILE} to deploy it.`;
|
|
351
|
-
report.issue(issue);
|
|
352
|
-
}
|
|
384
|
+
/** The agent's hand-written Dockerfile, which the deploy keeps; undefined when there is none, or it is the generated one. */
|
|
385
|
+
async function keptDockerfile(agentDir) {
|
|
386
|
+
const path = join(agentDir, "Dockerfile");
|
|
387
|
+
const text = (await exists(path)) ? await readFile(path, "utf8") : undefined;
|
|
388
|
+
return text === undefined || isGeneratedDockerfile(text) ? undefined : text;
|
|
353
389
|
}
|
|
354
390
|
/**
|
|
355
|
-
*
|
|
356
|
-
* catalog bundled with pi) is unknown wherever the agent is deployed. The agent's own catalog ships, so the remedy is
|
|
357
|
-
* one refresh in the agent.
|
|
358
|
-
*/
|
|
359
|
-
async function checkGlobalCatalogModel(modelSpec, deployed, report) {
|
|
360
|
-
const provider = providerOf(modelSpec);
|
|
361
|
-
const id = modelSpec.slice(provider.length + 1);
|
|
362
|
-
if (deployed.getModel(provider, id) || !(await inGlobalCatalog(provider, id)))
|
|
363
|
-
return;
|
|
364
|
-
report.issue(`model "${modelSpec}" is known only from ${globalCatalogPath()}, the machine's model catalog, which does not ` +
|
|
365
|
-
"ship — the deployed agent would fail with an unknown model. Run `fastagent models --refresh` in the agent " +
|
|
366
|
-
`to record it in its own ${AGENT_MODEL_CATALOG_FILE}, which ships.`);
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* What a KEPT hand-written Dockerfile drops. `deploy.apt` is the obvious one; the resolved model is the one that
|
|
391
|
+
* What a KEPT hand-written Dockerfile drops. The environment (context.json's) is the obvious one; the resolved model is the one that
|
|
370
392
|
* looks safe and is not: the manifest is always written, but only the generated Dockerfile sets
|
|
371
393
|
* FASTAGENT_RELEASE_FILE, and without it `prepareStartWorkspace` never reads the manifest — so a model that lives
|
|
372
394
|
* ONLY in the value file would be reported here and absent on the box.
|
|
@@ -375,16 +397,15 @@ async function checkGlobalCatalogModel(modelSpec, deployed, report) {
|
|
|
375
397
|
* hand-written Dockerfile survives `--force` and drops exactly the same things. Short-circuiting here let
|
|
376
398
|
* `--run --force` ship the crash-loop this gate exists to stop.
|
|
377
399
|
*/
|
|
378
|
-
|
|
400
|
+
function checkKeptDockerfile(dockerfileText, agentDir,
|
|
379
401
|
/** The model the release manifest carries (set only when the value file named it). */
|
|
380
402
|
modelFromValueFile, valueFile, report) {
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
`NOT applied; install those packages in your Dockerfile.`);
|
|
403
|
+
// What the image installs: the packages and apt packages. Variables travel with the value file, whatever the image.
|
|
404
|
+
const environment = loadEnvironment(agentDir);
|
|
405
|
+
if (Object.keys(environment?.packages ?? {}).length > 0 || (environment?.aptPackages.length ?? 0) > 0) {
|
|
406
|
+
report.warn(`kept your hand-written Dockerfile — it installs the environment context.json declares only if it says so: ` +
|
|
407
|
+
`a generated Dockerfile shows the layers (the apt packages, then \`mise install --system\` of the packages ` +
|
|
408
|
+
`context.json declares).`);
|
|
388
409
|
}
|
|
389
410
|
// The INSTRUCTION is the question, not the file's authorship: `prepareStartWorkspace` returns early without
|
|
390
411
|
// FASTAGENT_RELEASE_FILE, so a Dockerfile that sets it reads the manifest whoever wrote it. This gates rather
|
|
@@ -20,19 +20,21 @@ export interface RailwayPlanInput extends ContainerInput {
|
|
|
20
20
|
* carries.
|
|
21
21
|
*/
|
|
22
22
|
secrets?: readonly DeploymentSecret[];
|
|
23
|
-
/** `
|
|
23
|
+
/** `schedules/` declares a schedule — one of the things that forbids App Sleeping (deploy/residency.ts). */
|
|
24
24
|
hasCron: boolean;
|
|
25
25
|
}
|
|
26
26
|
export interface RailwayPlan {
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Dockerfile / .dockerignore — written by the CLI (skipped if present unless --force). No Railway config file: Railway
|
|
29
|
+
* builds from a root `Dockerfile` and restarts on failure by default, and the `/health` check that `railway.json`
|
|
30
|
+
* carried is the `--run` driver's own public probe now (Railway retires `railway.json`, and its replacement,
|
|
31
|
+
* `.railway/railway.ts`, would make every agent install Railway's SDK).
|
|
32
|
+
*/
|
|
28
33
|
artifacts: Artifact[];
|
|
29
34
|
/** The ordered, values-resolved deploy runbook — printed to stdout for the coding agent to execute. */
|
|
30
35
|
runbook: string[];
|
|
31
36
|
}
|
|
32
|
-
/** The
|
|
33
|
-
export declare const dockerfilePathVar: (prefix: string) => string;
|
|
34
|
-
/** The name this tool gives BOTH the project and the service, derived from the workspace directory. */
|
|
37
|
+
/** The name this tool gives BOTH the project and the service, derived from the agent directory's name. */
|
|
35
38
|
export declare function toRailwayName(basename: string): string;
|
|
36
|
-
export declare function isGeneratedRailwayJson(content: string): boolean;
|
|
37
39
|
/** Compute the Railway deploy plan from the resolved definition. */
|
|
38
40
|
export declare function planRailwayDeploy(input: RailwayPlanInput): RailwayPlan;
|