@fastagent-sh/fastagent 0.24.4 → 0.26.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 +54 -82
- package/dist/agent-api.d.ts +460 -0
- package/dist/agent-api.js +209 -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 +81 -0
- package/dist/authoring.js +245 -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 +116 -64
- package/dist/cli/auth-view.d.ts +10 -3
- package/dist/cli/auth-view.js +8 -6
- package/dist/cli/box-login.d.ts +18 -7
- package/dist/cli/box-login.js +46 -12
- package/dist/cli/commands/add.d.ts +15 -4
- package/dist/cli/commands/add.js +253 -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 +191 -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 +132 -16
- package/dist/cli/commands/login.d.ts +31 -2
- package/dist/cli/commands/login.js +416 -65
- 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 +6 -4
- package/dist/cli/login-relay.js +19 -7
- 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 +70 -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 +220 -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 +223 -202
- 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 +52 -27
- package/dist/effect-port.d.ts +4 -4
- package/dist/effect-port.js +4 -4
- package/dist/env.d.ts +0 -13
- package/dist/env.js +1 -44
- 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 +51 -0
- package/dist/environment/mise.js +272 -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 +48 -0
- package/dist/harnesses/models/environment.js +88 -0
- package/dist/harnesses/models/host.d.ts +30 -0
- package/dist/harnesses/models/host.js +97 -0
- package/dist/{engines/pi → harnesses/models}/login.d.ts +66 -70
- package/dist/harnesses/models/login.js +439 -0
- package/dist/harnesses/models/models.d.ts +84 -0
- package/dist/harnesses/models/models.js +197 -0
- package/dist/harnesses/pi-durable/agent.d.ts +42 -0
- package/dist/harnesses/pi-durable/agent.js +581 -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 +95 -0
- package/dist/harnesses/pi-durable/open.js +330 -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 +90 -0
- package/dist/harnesses/pi-durable/serve.js +186 -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 +11 -0
- package/dist/harnesses/pi-durable/wake.js +90 -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 +19 -4
- package/dist/index.js +13 -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/sign-in-api.d.ts +147 -0
- package/dist/sign-in-api.js +9 -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 +14 -5
- package/dist/value-file.d.ts +40 -0
- package/dist/value-file.js +164 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +14 -1
- package/package.json +18 -28
- package/dist/agent.d.ts +0 -106
- package/dist/agent.js +0 -21
- package/dist/channels/control.d.ts +0 -31
- package/dist/channels/control.js +0 -329
- package/dist/channels/define-channel.d.ts +0 -39
- package/dist/channels/define-channel.js +0 -9
- package/dist/channels/feishu/context-buffer.d.ts +0 -35
- package/dist/channels/feishu/context-buffer.js +0 -65
- package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/http.d.ts +0 -23
- package/dist/channels/http.js +0 -57
- package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/slack/context-buffer.d.ts +0 -23
- package/dist/channels/slack/context-buffer.js +0 -37
- package/dist/channels/sse.d.ts +0 -4
- package/dist/channels/sse.js +0 -66
- package/dist/cli/commands/chat.d.ts +0 -3
- package/dist/cli/commands/chat.js +0 -14
- package/dist/cli/commands/routine-run.d.ts +0 -6
- package/dist/cli/commands/routine-run.js +0 -46
- package/dist/cli/commands/routine.d.ts +0 -25
- package/dist/cli/commands/routine.js +0 -122
- package/dist/cli/commands/tool.d.ts +0 -1
- package/dist/cli/commands/tool.js +0 -57
- package/dist/collect.d.ts +0 -63
- package/dist/collect.js +0 -90
- package/dist/core.d.ts +0 -13
- package/dist/core.js +0 -15
- package/dist/engines/pi/agent-models.d.ts +0 -83
- package/dist/engines/pi/agent-models.js +0 -120
- package/dist/engines/pi/agent-session-factory.d.ts +0 -121
- package/dist/engines/pi/agent-session-factory.js +0 -535
- package/dist/engines/pi/chat.d.ts +0 -3
- package/dist/engines/pi/chat.js +0 -8
- package/dist/engines/pi/config.d.ts +0 -71
- package/dist/engines/pi/config.js +0 -199
- package/dist/engines/pi/create.d.ts +0 -170
- package/dist/engines/pi/create.js +0 -343
- package/dist/engines/pi/definition.d.ts +0 -74
- package/dist/engines/pi/definition.js +0 -236
- package/dist/engines/pi/entry-images.d.ts +0 -14
- package/dist/engines/pi/entry-images.js +0 -23
- package/dist/engines/pi/invoke-session.d.ts +0 -18
- package/dist/engines/pi/invoke-session.js +0 -403
- package/dist/engines/pi/locked-file.d.ts +0 -9
- package/dist/engines/pi/login.js +0 -331
- package/dist/engines/pi/machine.d.ts +0 -53
- package/dist/engines/pi/machine.js +0 -137
- package/dist/engines/pi/models.d.ts +0 -200
- package/dist/engines/pi/models.js +0 -533
- package/dist/engines/pi/open.d.ts +0 -180
- package/dist/engines/pi/open.js +0 -270
- package/dist/engines/pi/openai-account-models.d.ts +0 -35
- package/dist/engines/pi/openai-account-models.js +0 -116
- package/dist/engines/pi/report.d.ts +0 -18
- package/dist/engines/pi/report.js +0 -59
- package/dist/engines/pi/retry-event.d.ts +0 -6
- package/dist/engines/pi/retry-event.js +0 -15
- package/dist/engines/pi/service.d.ts +0 -9
- package/dist/engines/pi/service.js +0 -14
- package/dist/engines/pi/session-builder.d.ts +0 -13
- package/dist/engines/pi/session-builder.js +0 -117
- package/dist/engines/pi/session-control.d.ts +0 -77
- package/dist/engines/pi/session-control.js +0 -908
- package/dist/engines/pi/session-effects.d.ts +0 -14
- package/dist/engines/pi/session-effects.js +0 -30
- package/dist/engines/pi/session-inheritance.d.ts +0 -21
- package/dist/engines/pi/session-inheritance.js +0 -204
- package/dist/engines/pi/session-markers.d.ts +0 -49
- package/dist/engines/pi/session-markers.js +0 -60
- package/dist/engines/pi/session-settings.d.ts +0 -67
- package/dist/engines/pi/session-settings.js +0 -119
- package/dist/engines/pi/session-store.d.ts +0 -69
- package/dist/engines/pi/session-store.js +0 -430
- package/dist/engines/pi/tool-context.d.ts +0 -45
- package/dist/engines/pi/tool-context.js +0 -31
- package/dist/engines/pi/tool.d.ts +0 -99
- package/dist/engines/pi/tool.js +0 -118
- package/dist/engines/pi/turn-kit.d.ts +0 -92
- package/dist/engines/pi/turn-kit.js +0 -288
- package/dist/engines/pi/wake-tool.d.ts +0 -9
- package/dist/engines/pi/wake-tool.js +0 -92
- package/dist/loader.d.ts +0 -41
- package/dist/loader.js +0 -116
- package/dist/node.d.ts +0 -3
- package/dist/node.js +0 -4
- package/dist/observe.d.ts +0 -6
- package/dist/observe.js +0 -47
- package/dist/pi.d.ts +0 -25
- package/dist/pi.js +0 -15
- package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
- package/dist/schedule/discover.d.ts +0 -17
- package/dist/schedule/discover.js +0 -61
- package/dist/schedule/routine.d.ts +0 -43
- package/dist/schedule/routine.js +0 -32
- package/dist/schedule/run.d.ts +0 -55
- package/dist/schedule/run.js +0 -146
- package/dist/secrets-gate.d.ts +0 -32
- package/dist/secrets-gate.js +0 -33
- package/dist/session-remote.d.ts +0 -47
- package/dist/session-remote.js +0 -460
- package/dist/session.d.ts +0 -466
- package/dist/session.js +0 -57
- /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-durable's input submissions as the Agent API's messages (SPEC.md §5): a submission is a message, its request
|
|
3
|
+
* id carries the Caller's key, and its settlement is the message's end.
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { isRetryableAssistantError } from "@earendil-works/pi-ai/utils/retry";
|
|
7
|
+
import { agentError, errorData, } from "../../agent-api.js";
|
|
8
|
+
import { hasToolCalls, replyText, thinkingOf, usageOf } from "./entries.js";
|
|
9
|
+
const KEY = "key:";
|
|
10
|
+
/**
|
|
11
|
+
* How pi-ai's `Models.applyAuth` says a provider has no credential (`ModelsError` "auth"). The settled submission keeps
|
|
12
|
+
* only that text, and the outcome is read from what was recorded, so it does not change once a credential is added.
|
|
13
|
+
*/
|
|
14
|
+
const NOT_CONFIGURED = "Provider is not configured: ";
|
|
15
|
+
/** Every message is submitted with a request id, so its entry can name it: the Caller's key, or one of our own. */
|
|
16
|
+
export function requestIdFor(idempotencyKey) {
|
|
17
|
+
return idempotencyKey === undefined ? `id:${randomUUID()}` : `${KEY}${idempotencyKey}`;
|
|
18
|
+
}
|
|
19
|
+
function idempotencyKeyOf(requestId) {
|
|
20
|
+
return requestId?.startsWith(KEY) ? requestId.slice(KEY.length) : undefined;
|
|
21
|
+
}
|
|
22
|
+
export function isInput(record) {
|
|
23
|
+
return record?.type === "input";
|
|
24
|
+
}
|
|
25
|
+
export function hasEnded(record) {
|
|
26
|
+
return record.status === "done" || record.status === "unanswered";
|
|
27
|
+
}
|
|
28
|
+
/** The queued inputs in the order they will be taken: steers before follow-ups, each in arrival order. */
|
|
29
|
+
export function queuedInputs(inbox) {
|
|
30
|
+
const items = inbox?.items ?? [];
|
|
31
|
+
const of = (mode) => items.filter((item) => item.mode === mode).map((item) => item.id);
|
|
32
|
+
return [...of("steer"), ...of("followUp")];
|
|
33
|
+
}
|
|
34
|
+
/** A message as the API has it. An ended one needs its answer entry: the entry pi-durable settled it with. */
|
|
35
|
+
export function messageOf(record, ids, answer) {
|
|
36
|
+
const key = idempotencyKeyOf(record.requestId);
|
|
37
|
+
const identity = {
|
|
38
|
+
id: ids.encode(record.id),
|
|
39
|
+
sessionId: ids.encode(record.conversationId),
|
|
40
|
+
...(key === undefined ? {} : { idempotencyKey: key }),
|
|
41
|
+
};
|
|
42
|
+
switch (record.status) {
|
|
43
|
+
case "queued":
|
|
44
|
+
return { ...identity, status: "queued" };
|
|
45
|
+
case "placed":
|
|
46
|
+
return { ...identity, status: "running", entryId: ids.encode(record.entry) };
|
|
47
|
+
default:
|
|
48
|
+
return {
|
|
49
|
+
...identity,
|
|
50
|
+
...(record.entry === undefined ? {} : { entryId: ids.encode(record.entry) }),
|
|
51
|
+
...end(record, ids, answer),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function end(record, ids, answer) {
|
|
56
|
+
if (record.status === "unanswered")
|
|
57
|
+
return unanswered(record);
|
|
58
|
+
const reply = answer?.model?.[0];
|
|
59
|
+
if (reply?.role !== "assistant") {
|
|
60
|
+
throw new Error(`message ${record.id} was answered by entry ${record.answer}, which holds no reply`);
|
|
61
|
+
}
|
|
62
|
+
// A run a tool ended (`control.terminate`) settles on the reply that called it, which is not an answer (§5.4).
|
|
63
|
+
if (hasToolCalls(reply))
|
|
64
|
+
return { status: "completed" };
|
|
65
|
+
return {
|
|
66
|
+
status: reply.stopReason === "length" ? "incomplete" : "completed",
|
|
67
|
+
answer: { entryId: ids.encode(record.answer), text: replyText(reply) },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function unanswered(record) {
|
|
71
|
+
const detail = typeof record.detail === "string" ? record.detail : undefined;
|
|
72
|
+
const failed = (error) => ({ status: "failed", error: errorData(error) });
|
|
73
|
+
switch (record.reason) {
|
|
74
|
+
case "aborted":
|
|
75
|
+
return { status: "canceled" };
|
|
76
|
+
case "no_model":
|
|
77
|
+
return failed(agentError("missing_model", "The session's model was not among the agent's models when it was to answer"));
|
|
78
|
+
case "model_error": {
|
|
79
|
+
const message = detail ?? "The model call failed";
|
|
80
|
+
if (message.startsWith(NOT_CONFIGURED))
|
|
81
|
+
return failed(agentError("auth_required", message));
|
|
82
|
+
// pi-ai classifies a failed reply by its error text alone, which is all a settled submission keeps.
|
|
83
|
+
const retryable = isRetryableAssistantError({ stopReason: "error", errorMessage: message });
|
|
84
|
+
return failed({ ...errorData(agentError("model_error", message)), retryable });
|
|
85
|
+
}
|
|
86
|
+
case "reset":
|
|
87
|
+
return failed(agentError("internal", "The session was reset before this message was answered"));
|
|
88
|
+
default:
|
|
89
|
+
return failed(agentError("internal", detail ?? `The message was not answered: ${record.reason}`, { reason: record.reason }));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** The session as it is now, from its documents as one commit or one event snapshot holds them. */
|
|
93
|
+
export function sessionOf(id, live, usage, agent) {
|
|
94
|
+
const partial = live?.generation?.message;
|
|
95
|
+
const thinking = partial === undefined ? undefined : thinkingOf(partial);
|
|
96
|
+
return {
|
|
97
|
+
id,
|
|
98
|
+
status: live?.run === undefined ? "idle" : "running",
|
|
99
|
+
...(agent?.model === undefined ? {} : { model: { provider: agent.model.provider, modelId: agent.model.modelId } }),
|
|
100
|
+
...(agent?.thinkingLevel === undefined ? {} : { thinkingLevel: agent.thinkingLevel }),
|
|
101
|
+
...(agent?.instructions === undefined ? {} : { instructions: agent.instructions }),
|
|
102
|
+
// Only the list this API sets; pi-durable's other form (`{ remove }`) is set through `harness`, not here.
|
|
103
|
+
...(Array.isArray(agent?.tools) ? { tools: [...agent.tools] } : {}),
|
|
104
|
+
...(partial === undefined
|
|
105
|
+
? {}
|
|
106
|
+
: { reply: { text: replyText(partial), ...(thinking === undefined ? {} : { thinking }) } }),
|
|
107
|
+
usage: usageOfState(usage),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** What a conversation's `pi.usage`, or the instance's total, says it spent. */
|
|
111
|
+
export function usageOfState(usage) {
|
|
112
|
+
return { models: tokensOf(usage?.models), tools: tokensOf(usage?.tools) };
|
|
113
|
+
}
|
|
114
|
+
function tokensOf(bucket) {
|
|
115
|
+
return Object.fromEntries(Object.entries(bucket ?? {}).map(([key, value]) => [key, usageOf(value)]));
|
|
116
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ModelThinkingLevel, Models, Provider } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type Extension, type HarnessSettings, type ModelRef, type Storage, type ToolRegistration } from "@earendil-works/pi-durable";
|
|
3
|
+
import type { ResolvedContent } from "../../content/resolve.ts";
|
|
4
|
+
import { type DeclaredSecret } from "../../declared-secrets.ts";
|
|
5
|
+
import type { ChannelModule, LongConnectionChannelModule } from "../../channel.ts";
|
|
6
|
+
import { type Schedule } from "../../schedule/schedule.ts";
|
|
7
|
+
import { type DurableAgent, type Unfinished } from "./agent.ts";
|
|
8
|
+
import { type AgentConfig } from "./define.ts";
|
|
9
|
+
import { type Registry } from "./registry.ts";
|
|
10
|
+
/** An agent package as it was read: where it is, its configuration, and the modules its registry lists. */
|
|
11
|
+
export interface AgentDefinition {
|
|
12
|
+
dir: string;
|
|
13
|
+
name: string;
|
|
14
|
+
config: AgentConfig;
|
|
15
|
+
registry: Registry;
|
|
16
|
+
}
|
|
17
|
+
export declare function modelRefOf(spec: string, where: string): ModelRef;
|
|
18
|
+
/** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
|
|
19
|
+
export declare function loadConfig(dir: string): Promise<AgentConfig>;
|
|
20
|
+
/** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
|
|
21
|
+
export declare function modelSpecFor(config: AgentConfig, flag: string | undefined, env?: NodeJS.ProcessEnv): string | undefined;
|
|
22
|
+
/** Read the agent package in `dir`: `fastagent.config.ts` (optional) and `.fastagent/registry.ts`, generated first. */
|
|
23
|
+
export declare function loadDefinition(directory: string): Promise<AgentDefinition>;
|
|
24
|
+
/** The secrets the definition declares, wherever: its code's `needs`, and `context.json`'s command variables. */
|
|
25
|
+
export declare function definitionSecrets(definition: AgentDefinition): DeclaredSecret[];
|
|
26
|
+
/**
|
|
27
|
+
* The list a person deploying or running the agent reads instead of its code (`.fastagent/secrets.json`): each name
|
|
28
|
+
* once, what it is for, whether the agent runs without it, and where it is declared.
|
|
29
|
+
*/
|
|
30
|
+
export declare function secretsManifest(definition: AgentDefinition): {
|
|
31
|
+
name: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
optional: boolean;
|
|
34
|
+
declaredIn: string[];
|
|
35
|
+
}[];
|
|
36
|
+
/** Generate the registry, read the definition, and write its secrets manifest beside the registry. */
|
|
37
|
+
export declare function prepareDefinition(dir: string): Promise<AgentDefinition>;
|
|
38
|
+
/** The definition's code, built with the values of the secrets it declares. */
|
|
39
|
+
export interface BuiltDefinition {
|
|
40
|
+
tools: ToolRegistration[];
|
|
41
|
+
extensions: Extension[];
|
|
42
|
+
providers: Provider[];
|
|
43
|
+
/** By name, the file's: what a serve mounts or connects. */
|
|
44
|
+
channels: Map<string, ChannelModule | LongConnectionChannelModule>;
|
|
45
|
+
/** What the scheduler fires, each named by its file. */
|
|
46
|
+
schedules: Schedule[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Refuse to run while a required secret has no value, naming where it is declared; otherwise build every module with
|
|
50
|
+
* the values it declared, and refuse one that is not what its directory holds.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildDefinition(definition: AgentDefinition, env?: NodeJS.ProcessEnv): BuiltDefinition;
|
|
53
|
+
/**
|
|
54
|
+
* The definition's code built only to read its shape (which channels, with which ingress, which schedules, which
|
|
55
|
+
* providers): a declared secret `env` holds no value for has a stand-in, since the shape must not depend on whether
|
|
56
|
+
* this machine holds the values. Whatever a build refuses, this refuses too.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildDefinitionShape(definition: AgentDefinition, env: NodeJS.ProcessEnv): BuiltDefinition;
|
|
59
|
+
/**
|
|
60
|
+
* The providers of the definition in `agentDir`, for a command that reads models without serving (`models`, `login`,
|
|
61
|
+
* the first-run picker): with the values `env` holds and stand-ins for the rest, so listing a model or signing in to
|
|
62
|
+
* it needs no secret the serve would. Without these a provider in code, or one replacing a built-in's id, would be
|
|
63
|
+
* unknown there, or verified against the built-in's endpoint.
|
|
64
|
+
*/
|
|
65
|
+
export declare function definitionProviders(agentDir: string, env?: NodeJS.ProcessEnv): Promise<Provider[]>;
|
|
66
|
+
export interface AssembleAgentOptions {
|
|
67
|
+
storage: Storage;
|
|
68
|
+
/** What the agent works on and knows (`context.json`'s content), resolved for this instance. */
|
|
69
|
+
content?: readonly ResolvedContent[];
|
|
70
|
+
/** The definition's code, built once with its secrets (`buildDefinition`). */
|
|
71
|
+
code: BuiltDefinition;
|
|
72
|
+
/**
|
|
73
|
+
* pi-ai's built-ins and the definition's providers over the instance's credentials, lists refreshed: the model
|
|
74
|
+
* environment's (environment.ts), so the Agent runs on the lists its startup report reads.
|
|
75
|
+
*/
|
|
76
|
+
models: Models;
|
|
77
|
+
/** Why the lists of {@link models} are not current, when they are not: said with a default model none offers. */
|
|
78
|
+
catalogProblem?: string;
|
|
79
|
+
/** What a deployment sets over the definition's defaults (a flag, `FASTAGENT_MODEL`). */
|
|
80
|
+
model?: string;
|
|
81
|
+
thinkingLevel?: ModelThinkingLevel;
|
|
82
|
+
settings?: HarnessSettings;
|
|
83
|
+
/** Where the instance's state is: given by a serve, whose scheduler fires the wake-ups `wake` keeps there. */
|
|
84
|
+
stateRoot?: string;
|
|
85
|
+
/** Whether that scheduler runs now: an app's `openAgent` runs one only while it serves. Default always. */
|
|
86
|
+
wakeTicking?: () => boolean;
|
|
87
|
+
unfinished?: Unfinished;
|
|
88
|
+
/**
|
|
89
|
+
* What the agent's environment adds to this process's, for the commands it runs: its value file's variables and its
|
|
90
|
+
* packages' PATH.
|
|
91
|
+
*/
|
|
92
|
+
environment?: Readonly<Record<string, string>>;
|
|
93
|
+
}
|
|
94
|
+
/** Open the definition as an Agent over `models`: the coding tools, then its own. */
|
|
95
|
+
export declare function assembleAgent(definition: AgentDefinition, options: AssembleAgentOptions): Promise<DurableAgent>;
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A definition assembled onto pi-durable (architecture.md §3): its configuration and generated registry read, its
|
|
11
|
+
* secrets checked as one list, its code built with them, and an Agent opened over pi-ai's built-in models with the
|
|
12
|
+
* text, tools and extensions installed. `dev`, `start` and an embedder call the same function.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { writeFileAtomic } from "../../atomic-write.js";
|
|
16
|
+
import { basename, join, resolve } from "node:path";
|
|
17
|
+
import { pathToFileURL } from "node:url";
|
|
18
|
+
import { createRegistry, defineExtension, } from "@earendil-works/pi-durable";
|
|
19
|
+
import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node";
|
|
20
|
+
import { CodingTools } from "@earendil-works/pi-durable/tools";
|
|
21
|
+
import { loadEnvironment } from "../../content/file.js";
|
|
22
|
+
import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
|
|
23
|
+
import { declaredVariables } from "../../environment/declare.js";
|
|
24
|
+
import { channelModuleProblem } from "../../channels/discover.js";
|
|
25
|
+
import { AGENT_CONFIG_FILE as CONFIG_FILE } from "../../paths.js";
|
|
26
|
+
import { readPackageJson } from "../../runtime.js";
|
|
27
|
+
import { DEV_SERVE_ENV } from "../../serving-command.js";
|
|
28
|
+
import { MAX_SCHEDULES, scheduleConfigProblem } from "../../schedule/schedule.js";
|
|
29
|
+
import { openDurableAgent } from "./agent.js";
|
|
30
|
+
import { isNeeds } from "./define.js";
|
|
31
|
+
import { GENERATED_DIR, REGISTRY_FILE, REGISTRY_KINDS, generateRegistry } from "./registry.js";
|
|
32
|
+
import { definitionSections, readCommands } from "./text.js";
|
|
33
|
+
import { WAKE_EXTENSION, WAKE_TOOLS, wakeExtension } from "./wake.js";
|
|
34
|
+
const SECRETS_MANIFEST_FILE = "secrets.json";
|
|
35
|
+
const CONFIG_KEYS = ["name", "description", "model", "thinkingLevel", "extensions", "providers", "http", "deploy"];
|
|
36
|
+
const DEFINITION_EXTENSION = "fastagent.definition";
|
|
37
|
+
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
38
|
+
async function importDefault(path, what) {
|
|
39
|
+
try {
|
|
40
|
+
return (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href))).default;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new Error(`${what}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** An object whose keys are among `keys`, or the refusal naming the one that is not and the ones there are. */
|
|
47
|
+
function checkKeys(value, keys, where) {
|
|
48
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
49
|
+
throw new Error(`${where} must be an object`);
|
|
50
|
+
for (const key of Object.keys(value)) {
|
|
51
|
+
if (!keys.includes(key))
|
|
52
|
+
throw new Error(`${where}: unknown key "${key}" (keys: ${keys.join(", ")})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Refuse what a type check would have: Node strips types without checking them, so a typo reaches here. */
|
|
56
|
+
function checkConfig(config, file) {
|
|
57
|
+
if (config === undefined)
|
|
58
|
+
return {};
|
|
59
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
60
|
+
throw new Error(`${file} must default-export defineAgent({ ... })`);
|
|
61
|
+
}
|
|
62
|
+
checkKeys(config, CONFIG_KEYS, file);
|
|
63
|
+
const { model, thinkingLevel, extensions, providers, http, deploy } = config;
|
|
64
|
+
if (http !== undefined) {
|
|
65
|
+
checkKeys(http, ["port", "api"], `${file}: "http"`);
|
|
66
|
+
const port = http.port;
|
|
67
|
+
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
68
|
+
throw new Error(`${file}: "http.port" must be an integer 0-65535`);
|
|
69
|
+
}
|
|
70
|
+
if (http.api !== undefined && typeof http.api !== "boolean") {
|
|
71
|
+
throw new Error(`${file}: "http.api" must be true or false`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (deploy !== undefined) {
|
|
75
|
+
checkKeys(deploy, ["agentcore"], `${file}: "deploy"`);
|
|
76
|
+
if (deploy.agentcore !== undefined) {
|
|
77
|
+
checkKeys(deploy.agentcore, ["idleTimeoutSeconds"], `${file}: "deploy.agentcore"`);
|
|
78
|
+
// AWS's own bounds for idleRuntimeSessionTimeout: a value outside them is refused by CloudFormation minutes
|
|
79
|
+
// into `deploy agentcore --run`, after the image build.
|
|
80
|
+
const idle = deploy.agentcore.idleTimeoutSeconds;
|
|
81
|
+
if (idle !== undefined && (!Number.isInteger(idle) || idle < 60 || idle > 1209600)) {
|
|
82
|
+
throw new Error(`${file}: "deploy.agentcore.idleTimeoutSeconds" must be an integer 60-1209600 (seconds)`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (model !== undefined)
|
|
87
|
+
modelRefOf(model, `${file}: "model"`);
|
|
88
|
+
if (thinkingLevel !== undefined && !THINKING_LEVELS.includes(thinkingLevel)) {
|
|
89
|
+
throw new Error(`${file}: "thinkingLevel" must be one of ${THINKING_LEVELS.join(", ")}`);
|
|
90
|
+
}
|
|
91
|
+
for (const [key, list] of [
|
|
92
|
+
["extensions", extensions],
|
|
93
|
+
["providers", providers],
|
|
94
|
+
]) {
|
|
95
|
+
if (list !== undefined && !Array.isArray(list))
|
|
96
|
+
throw new Error(`${file}: "${key}" must be an array`);
|
|
97
|
+
}
|
|
98
|
+
return config;
|
|
99
|
+
}
|
|
100
|
+
export function modelRefOf(spec, where) {
|
|
101
|
+
const slash = typeof spec === "string" ? spec.indexOf("/") : -1;
|
|
102
|
+
if (slash < 1 || slash === spec.length - 1) {
|
|
103
|
+
throw new Error(`${where} must be "provider/modelId", got ${JSON.stringify(spec)}`);
|
|
104
|
+
}
|
|
105
|
+
return { provider: spec.slice(0, slash), modelId: spec.slice(slash + 1) };
|
|
106
|
+
}
|
|
107
|
+
/** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
|
|
108
|
+
export async function loadConfig(dir) {
|
|
109
|
+
const configPath = join(dir, CONFIG_FILE);
|
|
110
|
+
return checkConfig(existsSync(configPath) ? await importDefault(configPath, CONFIG_FILE) : undefined, CONFIG_FILE);
|
|
111
|
+
}
|
|
112
|
+
/** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
|
|
113
|
+
export function modelSpecFor(config, flag, env = process.env) {
|
|
114
|
+
return flag ?? (env.FASTAGENT_MODEL || config.model);
|
|
115
|
+
}
|
|
116
|
+
/** Read the agent package in `dir`: `fastagent.config.ts` (optional) and `.fastagent/registry.ts`, generated first. */
|
|
117
|
+
export async function loadDefinition(directory) {
|
|
118
|
+
// Absolute, since the prompt names the definition's files by it and the agent's tools resolve a relative path against
|
|
119
|
+
// the agent's own working directory, which is this one.
|
|
120
|
+
const dir = resolve(directory);
|
|
121
|
+
const config = await loadConfig(dir);
|
|
122
|
+
const registryPath = join(dir, GENERATED_DIR, REGISTRY_FILE);
|
|
123
|
+
if (!existsSync(registryPath)) {
|
|
124
|
+
throw new Error(`${GENERATED_DIR}/${REGISTRY_FILE} is missing: generate it from src/ before opening the agent`);
|
|
125
|
+
}
|
|
126
|
+
let registry;
|
|
127
|
+
try {
|
|
128
|
+
registry = (await import(__rewriteRelativeImportExtension(pathToFileURL(registryPath).href)));
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
// The registry imports every module, so a module that fails to load (or has no default export) fails here.
|
|
132
|
+
throw new Error(`loading src/ through ${GENERATED_DIR}/${REGISTRY_FILE}: ${error.message}`, {
|
|
133
|
+
cause: error,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const packageName = (await readPackageJson(dir));
|
|
137
|
+
const name = config.name ?? (typeof packageName.name === "string" ? packageName.name : basename(dir));
|
|
138
|
+
return { dir, name, config, registry };
|
|
139
|
+
}
|
|
140
|
+
/** Every value the definition can hold `Needs` in, with where it is: modules by file, configuration by key. */
|
|
141
|
+
function declarers(definition) {
|
|
142
|
+
return [
|
|
143
|
+
...REGISTRY_KINDS.flatMap((kind) => definition.registry[kind].map(({ file, value }) => ({ source: file, value }))),
|
|
144
|
+
...(definition.config.extensions ?? []).map((value, i) => ({ source: `${CONFIG_FILE} extensions[${i}]`, value })),
|
|
145
|
+
...(definition.config.providers ?? []).map((value, i) => ({ source: `${CONFIG_FILE} providers[${i}]`, value })),
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
/** The secrets the definition declares, wherever: its code's `needs`, and `context.json`'s command variables. */
|
|
149
|
+
export function definitionSecrets(definition) {
|
|
150
|
+
return [
|
|
151
|
+
...declarers(definition).flatMap(({ source, value }) => isNeeds(value) ? value.secrets.map((secret) => ({ ...secret, source })) : []),
|
|
152
|
+
...declaredVariables(loadEnvironment(definition.dir)),
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The list a person deploying or running the agent reads instead of its code (`.fastagent/secrets.json`): each name
|
|
157
|
+
* once, what it is for, whether the agent runs without it, and where it is declared.
|
|
158
|
+
*/
|
|
159
|
+
export function secretsManifest(definition) {
|
|
160
|
+
const byName = new Map();
|
|
161
|
+
for (const secret of definitionSecrets(definition)) {
|
|
162
|
+
const entry = byName.get(secret.name) ?? { name: secret.name, optional: true, declaredIn: [] };
|
|
163
|
+
entry.optional &&= secret.optional === true;
|
|
164
|
+
entry.declaredIn.push(secret.source);
|
|
165
|
+
if (entry.description === undefined && secret.description !== undefined)
|
|
166
|
+
entry.description = secret.description;
|
|
167
|
+
byName.set(secret.name, entry);
|
|
168
|
+
}
|
|
169
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
170
|
+
}
|
|
171
|
+
/** Generate the registry, read the definition, and write its secrets manifest beside the registry. */
|
|
172
|
+
export async function prepareDefinition(dir) {
|
|
173
|
+
await generateRegistry(dir);
|
|
174
|
+
const definition = await loadDefinition(dir);
|
|
175
|
+
writeFileAtomic(join(dir, GENERATED_DIR, SECRETS_MANIFEST_FILE), `${JSON.stringify(secretsManifest(definition), null, 2)}\n`, undefined, true);
|
|
176
|
+
return definition;
|
|
177
|
+
}
|
|
178
|
+
/** What one value is once its secrets are supplied, or why it is not what its place holds. */
|
|
179
|
+
function built(source, value, values, accept) {
|
|
180
|
+
const resolved = isNeeds(value) ? value.build(values) : value;
|
|
181
|
+
const problem = accept(resolved);
|
|
182
|
+
if (problem !== undefined)
|
|
183
|
+
throw new Error(`${source}: ${problem}`);
|
|
184
|
+
return resolved;
|
|
185
|
+
}
|
|
186
|
+
const toolProblem = (value) => {
|
|
187
|
+
const tool = value;
|
|
188
|
+
return typeof tool?.name === "string" &&
|
|
189
|
+
typeof tool.description === "string" &&
|
|
190
|
+
typeof tool.parameters === "object" &&
|
|
191
|
+
typeof tool.execute === "function"
|
|
192
|
+
? undefined
|
|
193
|
+
: "must default-export a tool (defineTool from @earendil-works/pi-durable), or needs(...) around one";
|
|
194
|
+
};
|
|
195
|
+
const extensionProblem = (value) => typeof value?.name === "string"
|
|
196
|
+
? undefined
|
|
197
|
+
: "must be an extension (defineExtension from @earendil-works/pi-durable), or needs(...) around one";
|
|
198
|
+
const providerProblem = (value) => typeof value?.id === "string" ? undefined : "must be a pi-ai provider (createProvider)";
|
|
199
|
+
const channelProblem = channelModuleProblem;
|
|
200
|
+
const scheduleProblem = (value) => value?.kind !== "fastagent.schedule"
|
|
201
|
+
? "must default-export defineSchedule({ ... }), or needs(...) around one"
|
|
202
|
+
: scheduleConfigProblem(value);
|
|
203
|
+
/**
|
|
204
|
+
* Refuse to run while a required secret has no value, naming where it is declared; otherwise build every module with
|
|
205
|
+
* the values it declared, and refuse one that is not what its directory holds.
|
|
206
|
+
*/
|
|
207
|
+
export function buildDefinition(definition, env = process.env) {
|
|
208
|
+
const missing = missingSecrets(definitionSecrets(definition), env);
|
|
209
|
+
if (missing.length > 0) {
|
|
210
|
+
throw new Error(`missing required secrets: ${describeSecrets(missing)} (set them in the environment)`);
|
|
211
|
+
}
|
|
212
|
+
const values = (value) => isNeeds(value) ? Object.fromEntries(value.secrets.map(({ name }) => [name, env[name] || undefined])) : {};
|
|
213
|
+
const { registry, config } = definition;
|
|
214
|
+
const tools = registry.tools.map(({ file, value }) => ({
|
|
215
|
+
source: file,
|
|
216
|
+
...named(built(file, value, values(value), toolProblem)),
|
|
217
|
+
}));
|
|
218
|
+
const extensions = [
|
|
219
|
+
...registry.extensions.map(({ file, value }) => ({
|
|
220
|
+
source: file,
|
|
221
|
+
...named(built(file, value, values(value), extensionProblem)),
|
|
222
|
+
})),
|
|
223
|
+
...(config.extensions ?? []).map((value, i) => {
|
|
224
|
+
const source = `${CONFIG_FILE} extensions[${i}]`;
|
|
225
|
+
return { source, ...named(built(source, value, values(value), extensionProblem)) };
|
|
226
|
+
}),
|
|
227
|
+
];
|
|
228
|
+
const channels = registry.channels.map(({ file, name, value }) => ({
|
|
229
|
+
source: file,
|
|
230
|
+
name,
|
|
231
|
+
value: built(file, value, values(value), channelProblem),
|
|
232
|
+
}));
|
|
233
|
+
const schedules = registry.schedules.map(({ file, name, value }) => ({
|
|
234
|
+
source: file,
|
|
235
|
+
name,
|
|
236
|
+
value: built(file, value, values(value), scheduleProblem),
|
|
237
|
+
}));
|
|
238
|
+
refuseTwins("tool", tools);
|
|
239
|
+
// pi-durable replaces an extension installed under a name already taken, its tools and sections with it.
|
|
240
|
+
refuseTwins("extension", [
|
|
241
|
+
{ source: "pi-durable's coding tools", name: CodingTools.name },
|
|
242
|
+
{ source: "FastAgent's definition extension", name: DEFINITION_EXTENSION },
|
|
243
|
+
{ source: "FastAgent's wake tools", name: WAKE_EXTENSION },
|
|
244
|
+
...extensions,
|
|
245
|
+
]);
|
|
246
|
+
refuseTwins("channel", channels);
|
|
247
|
+
refuseTwins("schedule", schedules);
|
|
248
|
+
if (schedules.length > MAX_SCHEDULES) {
|
|
249
|
+
throw new Error(`${schedules.length} schedules in src/schedules/: an agent runs at most ${MAX_SCHEDULES}`);
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
tools: tools.map(({ value }) => value),
|
|
253
|
+
extensions: extensions.map(({ value }) => value),
|
|
254
|
+
providers: (config.providers ?? []).map((value, i) => built(`${CONFIG_FILE} providers[${i}]`, value, values(value), providerProblem)),
|
|
255
|
+
channels: new Map(channels.map(({ name, value }) => [name, value])),
|
|
256
|
+
schedules: schedules.map(({ name, value: { kind: _kind, ...config } }) => ({ name, ...config })),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* The definition's code built only to read its shape (which channels, with which ingress, which schedules, which
|
|
261
|
+
* providers): a declared secret `env` holds no value for has a stand-in, since the shape must not depend on whether
|
|
262
|
+
* this machine holds the values. Whatever a build refuses, this refuses too.
|
|
263
|
+
*/
|
|
264
|
+
export function buildDefinitionShape(definition, env) {
|
|
265
|
+
const values = Object.fromEntries(definitionSecrets(definition).map(({ name }) => [name, "(no value here)"]));
|
|
266
|
+
for (const [name, value] of Object.entries(env))
|
|
267
|
+
if (value)
|
|
268
|
+
values[name] = value;
|
|
269
|
+
return buildDefinition(definition, values);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The providers of the definition in `agentDir`, for a command that reads models without serving (`models`, `login`,
|
|
273
|
+
* the first-run picker): with the values `env` holds and stand-ins for the rest, so listing a model or signing in to
|
|
274
|
+
* it needs no secret the serve would. Without these a provider in code, or one replacing a built-in's id, would be
|
|
275
|
+
* unknown there, or verified against the built-in's endpoint.
|
|
276
|
+
*/
|
|
277
|
+
export async function definitionProviders(agentDir, env = process.env) {
|
|
278
|
+
return buildDefinitionShape(await prepareDefinition(agentDir), env).providers;
|
|
279
|
+
}
|
|
280
|
+
const named = (value) => ({ name: value.name, value });
|
|
281
|
+
/** Refuse two of a kind under one name: whichever is installed or kept last would silently stand in for the other. */
|
|
282
|
+
function refuseTwins(kind, entries) {
|
|
283
|
+
const first = new Map();
|
|
284
|
+
for (const { source, name } of entries) {
|
|
285
|
+
const earlier = first.get(name);
|
|
286
|
+
if (earlier !== undefined)
|
|
287
|
+
throw new Error(`${earlier} and ${source} both define the ${kind} "${name}"`);
|
|
288
|
+
first.set(name, source);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/** Open the definition as an Agent over `models`: the coding tools, then its own. */
|
|
292
|
+
export async function assembleAgent(definition, options) {
|
|
293
|
+
const { dir, config } = definition;
|
|
294
|
+
const { code, models } = options;
|
|
295
|
+
const spec = options.model ?? config.model;
|
|
296
|
+
const model = spec === undefined ? undefined : modelRefOf(spec, "the default model");
|
|
297
|
+
if (model !== undefined && models.getModel(model.provider, model.modelId) === undefined) {
|
|
298
|
+
const stale = options.catalogProblem === undefined ? "" : `, and ${options.catalogProblem}`;
|
|
299
|
+
throw new Error(`unknown model "${spec}": no provider "${model.provider}" offers "${model.modelId}"${stale}`);
|
|
300
|
+
}
|
|
301
|
+
const ownWake = [...code.tools, ...code.extensions.flatMap((extension) => extension.tools ?? [])].some(({ name }) => WAKE_TOOLS.includes(name));
|
|
302
|
+
const registry = createRegistry();
|
|
303
|
+
registry.install(CodingTools);
|
|
304
|
+
// After the coding tools, so a tool of the definition's own with one of their names replaces it.
|
|
305
|
+
registry.install(defineExtension({
|
|
306
|
+
name: DEFINITION_EXTENSION,
|
|
307
|
+
sections: definitionSections(dir, options.content ?? [], { wake: ownWake || options.stateRoot !== undefined }),
|
|
308
|
+
tools: code.tools,
|
|
309
|
+
}));
|
|
310
|
+
for (const extension of code.extensions)
|
|
311
|
+
registry.install(extension);
|
|
312
|
+
if (options.stateRoot !== undefined && !ownWake) {
|
|
313
|
+
registry.install(wakeExtension(options.stateRoot, options.wakeTicking === undefined ? {} : { ticking: options.wakeTicking }));
|
|
314
|
+
}
|
|
315
|
+
const thinkingLevel = options.thinkingLevel ?? config.thinkingLevel;
|
|
316
|
+
return openDurableAgent({
|
|
317
|
+
storage: options.storage,
|
|
318
|
+
models,
|
|
319
|
+
registry,
|
|
320
|
+
name: definition.name,
|
|
321
|
+
...(config.description === undefined ? {} : { description: config.description }),
|
|
322
|
+
...(model === undefined ? {} : { model }),
|
|
323
|
+
...(thinkingLevel === undefined ? {} : { thinkingLevel }),
|
|
324
|
+
...(options.settings === undefined ? {} : { settings: options.settings }),
|
|
325
|
+
...(options.unfinished === undefined ? {} : { unfinished: options.unfinished }),
|
|
326
|
+
// What the agent runs is not `dev`'s own serving, even when `dev` is what runs the agent.
|
|
327
|
+
env: ({ cwd }) => new NodeExecutionEnv({ cwd: cwd ?? dir, shellEnv: { ...options.environment, [DEV_SERVE_ENV]: undefined } }),
|
|
328
|
+
commands: () => readCommands(dir),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the Agent reads from pi-durable, as Effects: a session's conversation, its published entries in either
|
|
3
|
+
* direction, a message as it is now, and the session. The operations and the event streams share them, so a Caller
|
|
4
|
+
* reads the same facts through each.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
7
|
+
import { type Conversation, type ConversationId, type EntryQuery, type EntryRecord, type Harness } from "@earendil-works/pi-durable";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Stream from "effect/Stream";
|
|
10
|
+
import type { Entry, Message, Session } from "../../agent-api.ts";
|
|
11
|
+
import { type PortFailure } from "../../effect-port.ts";
|
|
12
|
+
import type { Ids } from "./ids.ts";
|
|
13
|
+
import type { Sessions } from "./sessions.ts";
|
|
14
|
+
import { type InputRecord } from "./messages.ts";
|
|
15
|
+
export type Reads = ReturnType<typeof durableReads>;
|
|
16
|
+
export declare function durableReads(harness: Harness, ids: Ids, sessions: Pick<Sessions, "has">, context: Context): {
|
|
17
|
+
conversation: (sessionId: string) => Effect.Effect<Conversation | undefined, PortFailure>;
|
|
18
|
+
history: (conversation: Conversation, query: Omit<EntryQuery, "conversationId">) => Stream.Stream<EntryRecord, PortFailure, never>;
|
|
19
|
+
published: (conversation: Conversation, query: Omit<EntryQuery, "conversationId">) => Stream.Stream<EntryRecord, PortFailure, never>;
|
|
20
|
+
publishedPages: (conversation: Conversation, query: Omit<EntryQuery, "conversationId">) => Stream.Stream<EntryRecord[], PortFailure, never>;
|
|
21
|
+
visibleEntry: (conversation: Conversation, entryId: string) => Effect.Effect<EntryRecord | undefined, PortFailure, never>;
|
|
22
|
+
newestPublished: (conversation: Conversation, atOrBefore: number | undefined) => Effect.Effect<import("@earendil-works/pi-durable").EntryId | undefined, PortFailure, never>;
|
|
23
|
+
publish: (records: readonly EntryRecord[]) => Effect.Effect<Entry[], PortFailure>;
|
|
24
|
+
message: (record: InputRecord, known?: readonly EntryRecord[]) => Effect.Effect<Message, PortFailure>;
|
|
25
|
+
submission: (messageId: string) => Effect.Effect<InputRecord | undefined, PortFailure, never>;
|
|
26
|
+
submissionByKey: (conversation: Conversation, requestId: string) => Effect.Effect<InputRecord | undefined, PortFailure, never>;
|
|
27
|
+
inbox: (conversation: Conversation) => Effect.Effect<Readonly<import("@earendil-works/pi-durable").InboxState> | undefined, PortFailure, never>;
|
|
28
|
+
session: (conversation: Pick<Conversation, "id">) => Effect.Effect<Session, PortFailure>;
|
|
29
|
+
sessionsOf: (conversations: readonly ConversationId[]) => Effect.Effect<Session[], PortFailure>;
|
|
30
|
+
};
|