@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
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an agent's author writes (architecture.md §3): `defineAgent()` in `fastagent.config.ts`, `needs()` around the
|
|
3
|
+
* code that reads a secret, and `defineSchedule()` in `src/schedules/`. Each returns what it was given, typed; the
|
|
4
|
+
* assembly (open.ts) reads them back.
|
|
5
|
+
*/
|
|
6
|
+
import type { ModelThinkingLevel, Provider } from "@earendil-works/pi-ai";
|
|
7
|
+
import type { Extension } from "@earendil-works/pi-durable";
|
|
8
|
+
import type { ScheduleConfig } from "../../schedule/schedule.ts";
|
|
9
|
+
export interface AgentConfig {
|
|
10
|
+
/** Default: `package.json`'s name. */
|
|
11
|
+
name?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
/** `"provider/modelId"`: what a session that names none answers with; a deployment may override it. */
|
|
14
|
+
model?: string;
|
|
15
|
+
thinkingLevel?: ModelThinkingLevel;
|
|
16
|
+
/** Third-party capabilities, installed after `src/extensions/`. */
|
|
17
|
+
extensions?: readonly (Extension | Needs<Extension>)[];
|
|
18
|
+
/** Model providers beyond pi-ai's built-ins (a self-hosted endpoint, a gateway); a built-in's id replaces it. */
|
|
19
|
+
providers?: readonly (Provider | Needs<Provider>)[];
|
|
20
|
+
http?: {
|
|
21
|
+
/** The port a serve listens on, for its channels' webhooks and its health check. */
|
|
22
|
+
port?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a serve answers the Agent API under `/v1` with a key (default true): `dev`, `start`, and every deployment,
|
|
25
|
+
* which `deploy` gives a key. `false` keeps it to its channels and schedules, whatever key its environment holds.
|
|
26
|
+
*/
|
|
27
|
+
api?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** How the agent is deployed, where a host needs to be told. */
|
|
30
|
+
deploy?: {
|
|
31
|
+
agentcore?: {
|
|
32
|
+
idleTimeoutSeconds?: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** @public */
|
|
37
|
+
export declare function defineAgent(config: AgentConfig): AgentConfig;
|
|
38
|
+
/** One secret a piece of code reads: by name, or with what it is for and whether the code runs without it. */
|
|
39
|
+
/** @public */
|
|
40
|
+
export type SecretSpec = string | {
|
|
41
|
+
name: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
optional?: boolean;
|
|
44
|
+
};
|
|
45
|
+
type NameOf<S> = S extends string ? S : S extends {
|
|
46
|
+
name: infer N extends string;
|
|
47
|
+
} ? N : never;
|
|
48
|
+
type IsOptional<S> = S extends {
|
|
49
|
+
optional: true;
|
|
50
|
+
} ? true : false;
|
|
51
|
+
/** The values a `needs` hands its code: a required secret is a string, an optional one may be absent. */
|
|
52
|
+
/** @public */
|
|
53
|
+
export type SecretValues<S extends readonly SecretSpec[]> = {
|
|
54
|
+
[K in S[number] as IsOptional<K> extends true ? never : NameOf<K>]: string;
|
|
55
|
+
} & {
|
|
56
|
+
[K in S[number] as IsOptional<K> extends true ? NameOf<K> : never]?: string;
|
|
57
|
+
};
|
|
58
|
+
/** Code that needs secrets, built once their values are known: the declaration cannot drift from the read. */
|
|
59
|
+
export interface Needs<T> {
|
|
60
|
+
readonly kind: "fastagent.needs";
|
|
61
|
+
readonly secrets: readonly {
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
optional: boolean;
|
|
65
|
+
}[];
|
|
66
|
+
build(values: Record<string, string | undefined>): T;
|
|
67
|
+
}
|
|
68
|
+
/** @public */
|
|
69
|
+
export declare function needs<const S extends readonly SecretSpec[], T>(secrets: S, build: (values: SecretValues<S>) => T): Needs<T>;
|
|
70
|
+
export declare function isNeeds(value: unknown): value is Needs<unknown>;
|
|
71
|
+
/** What `src/schedules/<name>.ts` default-exports. */
|
|
72
|
+
export interface DefinedSchedule extends ScheduleConfig {
|
|
73
|
+
readonly kind: "fastagent.schedule";
|
|
74
|
+
}
|
|
75
|
+
/** @public */
|
|
76
|
+
export declare function defineSchedule(config: ScheduleConfig): DefinedSchedule;
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { isEnvName } from "../../declared-secrets.js";
|
|
2
|
+
/** @public */
|
|
3
|
+
export function defineAgent(config) {
|
|
4
|
+
return config;
|
|
5
|
+
}
|
|
6
|
+
/** @public */
|
|
7
|
+
export function needs(secrets, build) {
|
|
8
|
+
const declared = secrets.map((spec) => typeof spec === "string"
|
|
9
|
+
? { name: spec, optional: false }
|
|
10
|
+
: {
|
|
11
|
+
name: spec.name,
|
|
12
|
+
optional: spec.optional === true,
|
|
13
|
+
...(spec.description === undefined ? {} : { description: spec.description }),
|
|
14
|
+
});
|
|
15
|
+
for (const { name } of declared) {
|
|
16
|
+
// Written verbatim into env files and deploy artifacts, where a malformed name breaks somewhere far away.
|
|
17
|
+
if (!isEnvName(name))
|
|
18
|
+
throw new Error(`needs: ${JSON.stringify(name)} is not an environment variable name`);
|
|
19
|
+
}
|
|
20
|
+
return { kind: "fastagent.needs", secrets: declared, build: (values) => build(values) };
|
|
21
|
+
}
|
|
22
|
+
export function isNeeds(value) {
|
|
23
|
+
return value?.kind === "fastagent.needs";
|
|
24
|
+
}
|
|
25
|
+
/** @public */
|
|
26
|
+
export function defineSchedule(config) {
|
|
27
|
+
return { kind: "fastagent.schedule", ...config };
|
|
28
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-durable's entries as the Agent API publishes them (SPEC.md §3): the conversation's messages, its tool results
|
|
3
|
+
* and its compactions. The harness's own entries (`pi.system`, resets, FastAgent's bookkeeping) are not published.
|
|
4
|
+
*/
|
|
5
|
+
import type { AssistantMessage, ImageContent, Usage as PiUsage } from "@earendil-works/pi-ai";
|
|
6
|
+
import type { EntryRecord } from "@earendil-works/pi-durable";
|
|
7
|
+
import type { Entry, Tokens } from "../../agent-api.ts";
|
|
8
|
+
import type { Ids } from "./ids.ts";
|
|
9
|
+
/** A message as it was taken: the model saw the record's content, a Caller sees what it sent (`sent`). */
|
|
10
|
+
export declare const USER_ENTRY = "fastagent.user";
|
|
11
|
+
/** A write that only places the queued inputs a run that did not answer left waiting (release.ts). */
|
|
12
|
+
export declare const RELEASE_ENTRY = "fastagent.release";
|
|
13
|
+
export type UserEntryData = {
|
|
14
|
+
requestId: string;
|
|
15
|
+
/** The first text part as sent, when a command expanded it for the model. */
|
|
16
|
+
sent?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function isPublished(record: EntryRecord): boolean;
|
|
19
|
+
/** The request id a `fastagent.user` entry was submitted with: how the message it names is found. */
|
|
20
|
+
export declare function requestIdOf(record: EntryRecord): string | undefined;
|
|
21
|
+
/** `messageId`: the message a user entry names, when it names one. */
|
|
22
|
+
export declare function publishEntry(record: EntryRecord, ids: Ids, messageId: number | undefined): Entry;
|
|
23
|
+
export declare function replyText(message: Pick<AssistantMessage, "content">): string;
|
|
24
|
+
/** The model's reasoning as a Caller may show it: a redacted block's placeholder is not reasoning. */
|
|
25
|
+
export declare function thinkingOf(message: Pick<AssistantMessage, "content">): string | undefined;
|
|
26
|
+
export declare function hasToolCalls(message: AssistantMessage): boolean;
|
|
27
|
+
export declare function usageOf(usage: Pick<PiUsage, "input" | "output" | "cacheRead" | "cacheWrite" | "cost">): Tokens;
|
|
28
|
+
export declare function parseImageRef(ref: string): {
|
|
29
|
+
entryId: string;
|
|
30
|
+
index: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
export declare function imageAt(record: EntryRecord, index: number): ImageContent | undefined;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** A message as it was taken: the model saw the record's content, a Caller sees what it sent (`sent`). */
|
|
2
|
+
export const USER_ENTRY = "fastagent.user";
|
|
3
|
+
/** A write that only places the queued inputs a run that did not answer left waiting (release.ts). */
|
|
4
|
+
export const RELEASE_ENTRY = "fastagent.release";
|
|
5
|
+
const PUBLISHED = new Set([USER_ENTRY, "pi.user", "pi.assistant", "pi.tool-result", "pi.compaction"]);
|
|
6
|
+
export function isPublished(record) {
|
|
7
|
+
return PUBLISHED.has(record.kind) && record.model?.[0] !== undefined;
|
|
8
|
+
}
|
|
9
|
+
/** The request id a `fastagent.user` entry was submitted with: how the message it names is found. */
|
|
10
|
+
export function requestIdOf(record) {
|
|
11
|
+
return record.kind === USER_ENTRY ? record.data.requestId : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** `messageId`: the message a user entry names, when it names one. */
|
|
14
|
+
export function publishEntry(record, ids, messageId) {
|
|
15
|
+
const model = record.model?.[0];
|
|
16
|
+
const ref = (index) => imageRef(ids.encode(record.id), index);
|
|
17
|
+
return {
|
|
18
|
+
id: ids.encode(record.id),
|
|
19
|
+
timestamp: model.timestamp,
|
|
20
|
+
...published(record, model, ref, ids, messageId),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function published(record, model, ref, ids, messageId) {
|
|
24
|
+
switch (model.role) {
|
|
25
|
+
case "user": {
|
|
26
|
+
if (record.kind === "pi.compaction")
|
|
27
|
+
return { kind: "compaction", data: { summary: textOf(model.content) } };
|
|
28
|
+
const content = contentParts(model.content, ref);
|
|
29
|
+
const sent = record.kind === USER_ENTRY ? record.data.sent : undefined;
|
|
30
|
+
const first = content[0];
|
|
31
|
+
if (sent !== undefined && first?.type === "text")
|
|
32
|
+
content[0] = { type: "text", text: sent };
|
|
33
|
+
return {
|
|
34
|
+
kind: "user",
|
|
35
|
+
data: { content: content, ...(messageId === undefined ? {} : { messageId: ids.encode(messageId) }) },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
case "assistant":
|
|
39
|
+
return { kind: "assistant", data: assistantData(model) };
|
|
40
|
+
case "toolResult":
|
|
41
|
+
return {
|
|
42
|
+
kind: "tool_result",
|
|
43
|
+
data: {
|
|
44
|
+
toolCallId: model.toolCallId,
|
|
45
|
+
toolName: model.toolName,
|
|
46
|
+
isError: model.isError,
|
|
47
|
+
content: contentParts(model.content, ref),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
default:
|
|
51
|
+
throw new Error(`entry ${record.id} of kind ${record.kind} holds a ${model.role} message`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function assistantData(message) {
|
|
55
|
+
const toolCalls = message.content.flatMap((block) => block.type === "toolCall" ? [{ id: block.id, name: block.name, args: block.arguments }] : []);
|
|
56
|
+
const thinking = thinkingOf(message);
|
|
57
|
+
const stop = STOPS[message.stopReason];
|
|
58
|
+
return {
|
|
59
|
+
text: replyText(message),
|
|
60
|
+
...(thinking === undefined ? {} : { thinking }),
|
|
61
|
+
...(toolCalls.length > 0 ? { toolCalls } : {}),
|
|
62
|
+
...(stop === undefined ? {} : { stop }),
|
|
63
|
+
...(stop === undefined || message.errorMessage === undefined ? {} : { error: message.errorMessage }),
|
|
64
|
+
usage: { ...usageOf(message.usage) },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** How a reply stopped, when it did not stop as an answer or a tool call does. */
|
|
68
|
+
const STOPS = {
|
|
69
|
+
length: "max_tokens",
|
|
70
|
+
error: "failed",
|
|
71
|
+
aborted: "aborted",
|
|
72
|
+
};
|
|
73
|
+
export function replyText(message) {
|
|
74
|
+
return message.content.flatMap((block) => (block.type === "text" ? [block.text] : [])).join("");
|
|
75
|
+
}
|
|
76
|
+
/** The model's reasoning as a Caller may show it: a redacted block's placeholder is not reasoning. */
|
|
77
|
+
export function thinkingOf(message) {
|
|
78
|
+
const text = message.content
|
|
79
|
+
.flatMap((block) => (block.type === "thinking" && block.redacted !== true ? [block.thinking] : []))
|
|
80
|
+
.join("");
|
|
81
|
+
return text === "" ? undefined : text;
|
|
82
|
+
}
|
|
83
|
+
export function hasToolCalls(message) {
|
|
84
|
+
return message.content.some((block) => block.type === "toolCall");
|
|
85
|
+
}
|
|
86
|
+
export function usageOf(usage) {
|
|
87
|
+
return {
|
|
88
|
+
inputTokens: usage.input,
|
|
89
|
+
outputTokens: usage.output,
|
|
90
|
+
cacheReadTokens: usage.cacheRead,
|
|
91
|
+
cacheWriteTokens: usage.cacheWrite,
|
|
92
|
+
cost: usage.cost.total,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function textOf(content) {
|
|
96
|
+
return typeof content === "string"
|
|
97
|
+
? content
|
|
98
|
+
: content.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("");
|
|
99
|
+
}
|
|
100
|
+
function contentParts(content, ref) {
|
|
101
|
+
if (typeof content === "string")
|
|
102
|
+
return [{ type: "text", text: content }];
|
|
103
|
+
return content.map((part, index) => part.type === "text"
|
|
104
|
+
? { type: "text", text: part.text }
|
|
105
|
+
: { type: "image", mimeType: part.mimeType, ref: ref(index) });
|
|
106
|
+
}
|
|
107
|
+
/** A ref names the entry and the part of its content that holds the image: images live inline in the entry. */
|
|
108
|
+
function imageRef(entryId, index) {
|
|
109
|
+
return `${entryId}:${index}`;
|
|
110
|
+
}
|
|
111
|
+
export function parseImageRef(ref) {
|
|
112
|
+
const at = ref.lastIndexOf(":");
|
|
113
|
+
const index = ref.slice(at + 1);
|
|
114
|
+
if (at <= 0 || !/^(0|[1-9][0-9]{0,5})$/.test(index))
|
|
115
|
+
return undefined;
|
|
116
|
+
return { entryId: ref.slice(0, at), index: Number(index) };
|
|
117
|
+
}
|
|
118
|
+
export function imageAt(record, index) {
|
|
119
|
+
const model = record.model?.[0];
|
|
120
|
+
if (!isPublished(record) || (model?.role !== "user" && model?.role !== "toolResult"))
|
|
121
|
+
return undefined;
|
|
122
|
+
if (typeof model.content === "string")
|
|
123
|
+
return undefined;
|
|
124
|
+
const part = model.content[index];
|
|
125
|
+
return part?.type === "image" ? part : undefined;
|
|
126
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sessions.events` (SPEC.md §6.1) over pi-durable's event stream of one conversation: the entries asked for, a
|
|
3
|
+
* `state` snapshot, then what each commit adds. pi-durable captures its snapshot and the registration for later
|
|
4
|
+
* commits atomically, so the replay ends at the snapshot's newest entry and the live part starts after it.
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
7
|
+
import { type Harness } from "@earendil-works/pi-durable";
|
|
8
|
+
import * as Stream from "effect/Stream";
|
|
9
|
+
import { AgentError, type SessionEvent } from "../../agent-api.ts";
|
|
10
|
+
import type { Ids } from "./ids.ts";
|
|
11
|
+
import type { Reads } from "./reads.ts";
|
|
12
|
+
export interface EventsOptions {
|
|
13
|
+
harness: Harness;
|
|
14
|
+
reads: Reads;
|
|
15
|
+
ids: Ids;
|
|
16
|
+
context: Context;
|
|
17
|
+
failureOf: (error: unknown) => AgentError;
|
|
18
|
+
/** The retry budget a `retrying` reports. */
|
|
19
|
+
maxRetries: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The session's events after the entry `after` names (`"start"`: every entry; absent: none). An unknown session or
|
|
23
|
+
* entry, and a storage that fails before the snapshot, fail the stream; a failure after it is a `stream_error`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sessionEvents({ harness, reads, ids, context, failureOf, maxRetries }: EventsOptions, sessionId: string, afterEntry: string | undefined): Stream.Stream<SessionEvent, AgentError>;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { watchEvents, } from "@earendil-works/pi-durable";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Queue from "effect/Queue";
|
|
4
|
+
import * as Stream from "effect/Stream";
|
|
5
|
+
import { AgentError, agentError, errorData } from "../../agent-api.js";
|
|
6
|
+
import { port, portError } from "../../effect-port.js";
|
|
7
|
+
import { isPublished, replyText, thinkingOf } from "./entries.js";
|
|
8
|
+
import { hasEnded, isInput, sessionOf } from "./messages.js";
|
|
9
|
+
/**
|
|
10
|
+
* The session's events after the entry `after` names (`"start"`: every entry; absent: none). An unknown session or
|
|
11
|
+
* entry, and a storage that fails before the snapshot, fail the stream; a failure after it is a `stream_error`.
|
|
12
|
+
*/
|
|
13
|
+
export function sessionEvents({ harness, reads, ids, context, failureOf, maxRetries }, sessionId, afterEntry) {
|
|
14
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
15
|
+
const conversation = yield* reads.conversation(sessionId);
|
|
16
|
+
if (conversation === undefined)
|
|
17
|
+
return yield* agentError("not_found", `No session ${sessionId}`);
|
|
18
|
+
let after;
|
|
19
|
+
if (afterEntry === "start")
|
|
20
|
+
after = 0;
|
|
21
|
+
else if (afterEntry !== undefined) {
|
|
22
|
+
const entry = yield* reads.visibleEntry(conversation, afterEntry);
|
|
23
|
+
if (entry === undefined) {
|
|
24
|
+
return yield* agentError("not_found", `No entry ${afterEntry} in session ${sessionId}`);
|
|
25
|
+
}
|
|
26
|
+
after = entry.id;
|
|
27
|
+
}
|
|
28
|
+
const batches = yield* Effect.acquireRelease(Queue.bounded(1), Queue.shutdown);
|
|
29
|
+
const watch = yield* Effect.acquireRelease(port(() => watchEvents(harness, conversation.id, context)), (stream) => Effect.promise(() => stream.stop()));
|
|
30
|
+
// Taking one batch at a time keeps pi-durable's own bound on what a slow Caller leaves undelivered.
|
|
31
|
+
watch.start((events) => Effect.runPromiseExit(Queue.offer(batches, { events })).then(() => undefined));
|
|
32
|
+
void watch.closed.then((end) => Effect.runPromiseExit(Queue.offer(batches, { end })));
|
|
33
|
+
const { snapshot } = watch;
|
|
34
|
+
const newest = snapshot.entries.reduce((max, entry) => (max === undefined || entry.id > max ? entry.id : max), undefined);
|
|
35
|
+
const live = liveTranslator(reads, maxRetries, snapshot);
|
|
36
|
+
const replay = after === undefined || newest === undefined || after >= newest
|
|
37
|
+
? Stream.empty
|
|
38
|
+
: replayed(reads, conversation, after, newest);
|
|
39
|
+
const present = Stream.succeed({
|
|
40
|
+
type: "state",
|
|
41
|
+
session: sessionOf(ids.encode(conversation.id), snapshot, snapshot.usage, snapshot.agent),
|
|
42
|
+
});
|
|
43
|
+
const events = Stream.fromQueue(batches).pipe(Stream.mapEffect((batch) => "end" in batch ? Effect.succeed([ended(batch.end)]) : live.translate(batch.events)), Stream.flattenIterable);
|
|
44
|
+
return Stream.concat(Stream.concat(replay, present), events).pipe(Stream.catchCause((cause) => Stream.succeed(streamError(failureOf(portError(cause))))), Stream.takeUntil((event) => event.type === "stream_error"));
|
|
45
|
+
}).pipe(Effect.catchCause((cause) => {
|
|
46
|
+
const error = portError(cause);
|
|
47
|
+
return Effect.fail(error instanceof AgentError ? error : failureOf(error));
|
|
48
|
+
})));
|
|
49
|
+
}
|
|
50
|
+
const streamError = (error) => ({ type: "stream_error", error: errorData(error) });
|
|
51
|
+
function ended(end) {
|
|
52
|
+
switch (end.reason) {
|
|
53
|
+
case "session_failed":
|
|
54
|
+
return streamError(agentError("unavailable", `The agent's storage failed: ${String(end.error)}`));
|
|
55
|
+
case "listener_error":
|
|
56
|
+
return streamError(agentError("internal", `Following the session failed: ${end.error.message}`));
|
|
57
|
+
default:
|
|
58
|
+
return streamError(agentError("unavailable", `The agent stopped serving this session (${end.reason})`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function replayed(reads, conversation, after, through) {
|
|
62
|
+
return reads
|
|
63
|
+
.publishedPages(conversation, {
|
|
64
|
+
order: "ascending",
|
|
65
|
+
minEntryId: (after + 1),
|
|
66
|
+
maxEntryId: through,
|
|
67
|
+
})
|
|
68
|
+
.pipe(Stream.mapEffect(reads.publish), Stream.flattenIterable, Stream.map(entryEvent));
|
|
69
|
+
}
|
|
70
|
+
const entryEvent = (entry) => ({ type: "entry", entry });
|
|
71
|
+
/**
|
|
72
|
+
* What one commit's events add for a Caller: the reply's deltas, the tool calls that start and their status lines,
|
|
73
|
+
* retries, the published entries, and a `done` for each message that ended, before the next message's entry.
|
|
74
|
+
*/
|
|
75
|
+
function liveTranslator(reads, maxRetries, snapshot) {
|
|
76
|
+
let reply = [...(snapshot.generation?.message?.content ?? [])];
|
|
77
|
+
let shown = { text: replyText({ content: reply }), thinking: thinkingOf({ content: reply }) ?? "" };
|
|
78
|
+
const outputs = new Map(snapshot.tools.map((slot) => [slot.callId, slot.output ?? ""]));
|
|
79
|
+
const lines = new Map();
|
|
80
|
+
const outer = new Map(snapshot.nestedTools.map((slot) => [slot.callId, slot.parentCallId]));
|
|
81
|
+
const outermost = (callId) => {
|
|
82
|
+
const parent = outer.get(callId);
|
|
83
|
+
return parent === undefined ? callId : outermost(parent);
|
|
84
|
+
};
|
|
85
|
+
const restart = () => {
|
|
86
|
+
reply = [];
|
|
87
|
+
shown = { text: "", thinking: "" };
|
|
88
|
+
};
|
|
89
|
+
const deltas = () => {
|
|
90
|
+
const now = { text: replyText({ content: reply }), thinking: thinkingOf({ content: reply }) ?? "" };
|
|
91
|
+
const added = (channel) => now[channel].length > shown[channel].length && now[channel].startsWith(shown[channel])
|
|
92
|
+
? [{ type: "delta", channel, text: now[channel].slice(shown[channel].length) }]
|
|
93
|
+
: [];
|
|
94
|
+
const events = [...added("thinking"), ...added("text")];
|
|
95
|
+
shown = now;
|
|
96
|
+
return events;
|
|
97
|
+
};
|
|
98
|
+
const apply = (changes) => {
|
|
99
|
+
for (const change of changes) {
|
|
100
|
+
switch (change.type) {
|
|
101
|
+
case "message":
|
|
102
|
+
reply = [...change.message.content];
|
|
103
|
+
break;
|
|
104
|
+
case "text_start":
|
|
105
|
+
case "thinking_start":
|
|
106
|
+
case "toolcall_start":
|
|
107
|
+
case "block":
|
|
108
|
+
reply[change.contentIndex] = change.block;
|
|
109
|
+
break;
|
|
110
|
+
case "text_delta": {
|
|
111
|
+
const block = reply[change.contentIndex];
|
|
112
|
+
if (block?.type === "text")
|
|
113
|
+
reply[change.contentIndex] = { ...block, text: block.text + change.delta };
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "thinking_delta": {
|
|
117
|
+
const block = reply[change.contentIndex];
|
|
118
|
+
if (block?.type === "thinking") {
|
|
119
|
+
reply[change.contentIndex] = { ...block, thinking: block.thinking + change.delta };
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "toolcall_delta":
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const progress = (toolCallId, text) => {
|
|
129
|
+
if (text === undefined || lines.get(toolCallId) === text)
|
|
130
|
+
return [];
|
|
131
|
+
lines.set(toolCallId, text);
|
|
132
|
+
return [{ type: "tool_progress", toolCallId, text }];
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
translate: (events) => Effect.gen(function* () {
|
|
136
|
+
const before = [];
|
|
137
|
+
const entries = [];
|
|
138
|
+
const settled = [];
|
|
139
|
+
for (const [index, event] of events.entries()) {
|
|
140
|
+
switch (event.type) {
|
|
141
|
+
case "snapshot":
|
|
142
|
+
return [
|
|
143
|
+
streamError(agentError("unavailable", "This stream fell behind the session; continue after its last entry")),
|
|
144
|
+
];
|
|
145
|
+
case "message_start":
|
|
146
|
+
// An entry's own start comes right before its end; any other is a new reply being written.
|
|
147
|
+
if (events[index + 1]?.type !== "message_end" && event.message.role === "assistant") {
|
|
148
|
+
reply = [...event.message.content];
|
|
149
|
+
shown = { text: "", thinking: "" };
|
|
150
|
+
before.push(...deltas());
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case "message_update":
|
|
154
|
+
apply(event.changes);
|
|
155
|
+
before.push(...deltas());
|
|
156
|
+
break;
|
|
157
|
+
case "message_end":
|
|
158
|
+
case "entry_appended":
|
|
159
|
+
entries.push(event.entry);
|
|
160
|
+
if (event.entry.model?.[0]?.role === "assistant")
|
|
161
|
+
restart();
|
|
162
|
+
break;
|
|
163
|
+
case "tool_execution_start":
|
|
164
|
+
if (event.parentToolCallId === undefined) {
|
|
165
|
+
before.push({
|
|
166
|
+
type: "tool_started",
|
|
167
|
+
toolCallId: event.toolCallId,
|
|
168
|
+
name: event.toolName,
|
|
169
|
+
args: event.args,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
outer.set(event.toolCallId, event.parentToolCallId);
|
|
174
|
+
before.push(...progress(outermost(event.toolCallId), callLine(event.toolName, event.args)));
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case "tool_execution_update": {
|
|
178
|
+
if (event.parentToolCallId !== undefined || event.output === undefined)
|
|
179
|
+
break;
|
|
180
|
+
const output = event.output;
|
|
181
|
+
const current = outputs.get(event.toolCallId) ?? "";
|
|
182
|
+
const next = "set" in output ? output.set : current.slice(output.trimStart ?? 0) + (output.append ?? "");
|
|
183
|
+
outputs.set(event.toolCallId, next);
|
|
184
|
+
before.push(...progress(event.toolCallId, lastLine(next)));
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case "auto_retry_start":
|
|
188
|
+
restart();
|
|
189
|
+
before.push({
|
|
190
|
+
type: "retrying",
|
|
191
|
+
attempt: event.attempt,
|
|
192
|
+
maxAttempts: maxRetries,
|
|
193
|
+
delayMs: Math.max(0, event.at - Date.now()),
|
|
194
|
+
reason: event.errorMessage,
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
case "tool_execution_end":
|
|
198
|
+
outputs.delete(event.toolCallId);
|
|
199
|
+
lines.delete(event.toolCallId);
|
|
200
|
+
outer.delete(event.toolCallId);
|
|
201
|
+
break;
|
|
202
|
+
case "submission":
|
|
203
|
+
if (isInput(event.record) && hasEnded(event.record))
|
|
204
|
+
settled.push(event.record);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const published = yield* reads.publish(entries.filter(isPublished));
|
|
209
|
+
const dones = yield* Effect.forEach(settled, (record) => reads.message(record, entries));
|
|
210
|
+
// pi-durable places the next queued message in the commit that ends a run: its message's `done` goes first.
|
|
211
|
+
const next = published.findIndex((entry) => entry.kind === "user");
|
|
212
|
+
const at = next === -1 ? published.length : next;
|
|
213
|
+
return [
|
|
214
|
+
...before,
|
|
215
|
+
...published.slice(0, at).map(entryEvent),
|
|
216
|
+
...dones.map((message) => ({ type: "done", message })),
|
|
217
|
+
...published.slice(at).map(entryEvent),
|
|
218
|
+
];
|
|
219
|
+
}),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** The last line with text, a running tool's status. pi-durable's output already has no control characters. */
|
|
223
|
+
function lastLine(text) {
|
|
224
|
+
const lines = text.split("\n");
|
|
225
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
226
|
+
const line = lines[i]?.trim();
|
|
227
|
+
if (line)
|
|
228
|
+
return line;
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
/** A nested call as its caller's status: the tool and its first plain argument, on one line (`weather London`). */
|
|
233
|
+
function callLine(name, args) {
|
|
234
|
+
const first = args !== null && typeof args === "object" && !Array.isArray(args)
|
|
235
|
+
? Object.values(args).find((value) => typeof value === "string" || typeof value === "number")
|
|
236
|
+
: undefined;
|
|
237
|
+
return first === undefined ? name : `${name} ${String(first).replace(/\s+/g, " ").trim()}`.trim();
|
|
238
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
2
|
+
import { type Harness, type ToolExecutionApi } from "@earendil-works/pi-durable";
|
|
3
|
+
export interface Ids {
|
|
4
|
+
encode(id: number): string;
|
|
5
|
+
/** The pi-durable id `text` names in this storage, or undefined for any other text. */
|
|
6
|
+
decode(text: string): number | undefined;
|
|
7
|
+
/** A position among this storage's ids as a cursor, which no id reads as. */
|
|
8
|
+
encodeCursor(position: number): string;
|
|
9
|
+
decodeCursor(text: string): number | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare function storageIds(harness: Harness, context: Context): Promise<Ids>;
|
|
12
|
+
/**
|
|
13
|
+
* The session id of the conversation a tool runs in, as the Agent hands it out, or undefined when that conversation is
|
|
14
|
+
* not a session (a subagent's): from the storage the tool runs on.
|
|
15
|
+
*/
|
|
16
|
+
export declare function sessionIdOf(api: Pick<ToolExecutionApi, "conversationId" | "snapshot" | "commit">, context: Context): Promise<string | undefined>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ids the Agent hands out: the storage's incarnation, then pi-durable's id. pi-durable counts ids per storage and a
|
|
3
|
+
* new storage counts from the start again, so an id kept from a storage that is gone must not name what the new one
|
|
4
|
+
* gives the same number (architecture.md §2). An encoding, not a lookup: nothing is stored per id.
|
|
5
|
+
*/
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
7
|
+
import { defineDoc } from "@earendil-works/pi-durable";
|
|
8
|
+
import { isSession } from "./sessions.js";
|
|
9
|
+
const StorageDoc = defineDoc({
|
|
10
|
+
kind: "fastagent.storage",
|
|
11
|
+
version: 1,
|
|
12
|
+
scope: "session",
|
|
13
|
+
initial: () => ({ incarnation: "" }),
|
|
14
|
+
});
|
|
15
|
+
export async function storageIds(harness, context) {
|
|
16
|
+
const incarnation = await harness.commit(async (tx) => {
|
|
17
|
+
const doc = await tx.doc(StorageDoc);
|
|
18
|
+
if (doc.incarnation === "")
|
|
19
|
+
doc.incarnation = randomBytes(8).toString("base64url");
|
|
20
|
+
return doc.incarnation;
|
|
21
|
+
}, context);
|
|
22
|
+
return idsOf(incarnation);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The session id of the conversation a tool runs in, as the Agent hands it out, or undefined when that conversation is
|
|
26
|
+
* not a session (a subagent's): from the storage the tool runs on.
|
|
27
|
+
*/
|
|
28
|
+
export async function sessionIdOf(api, context) {
|
|
29
|
+
const record = await api.commit((tx) => tx.conversation(api.conversationId), context);
|
|
30
|
+
if (record === undefined || !isSession(record))
|
|
31
|
+
return undefined;
|
|
32
|
+
const incarnation = (await api.snapshot(StorageDoc, context))?.incarnation;
|
|
33
|
+
if (!incarnation)
|
|
34
|
+
throw new Error("this storage has no incarnation: the Agent did not open it");
|
|
35
|
+
return idsOf(incarnation).encode(api.conversationId);
|
|
36
|
+
}
|
|
37
|
+
function idsOf(incarnation) {
|
|
38
|
+
const prefix = `${incarnation}.`;
|
|
39
|
+
const cursorPrefix = `${incarnation}~`;
|
|
40
|
+
return {
|
|
41
|
+
encode: (id) => `${prefix}${id}`,
|
|
42
|
+
decode: (text) => numberAfter(prefix, text),
|
|
43
|
+
encodeCursor: (position) => `${cursorPrefix}${position}`,
|
|
44
|
+
decodeCursor: (text) => numberAfter(cursorPrefix, text),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function numberAfter(prefix, text) {
|
|
48
|
+
if (!text.startsWith(prefix))
|
|
49
|
+
return undefined;
|
|
50
|
+
const digits = text.slice(prefix.length);
|
|
51
|
+
if (!/^[1-9][0-9]{0,15}$/.test(digits))
|
|
52
|
+
return undefined;
|
|
53
|
+
const id = Number(digits);
|
|
54
|
+
return Number.isSafeInteger(id) ? id : undefined;
|
|
55
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AgentState, EntryRecord, SubmissionRecord } from "@earendil-works/pi-durable";
|
|
2
|
+
import { type Message, type Session, type Usage } from "../../agent-api.ts";
|
|
3
|
+
import { usageOf } from "./entries.ts";
|
|
4
|
+
import type { Ids } from "./ids.ts";
|
|
5
|
+
export type InputRecord = Extract<SubmissionRecord, {
|
|
6
|
+
type: "input";
|
|
7
|
+
}>;
|
|
8
|
+
export type EndedRecord = Extract<InputRecord, {
|
|
9
|
+
status: "done" | "unanswered";
|
|
10
|
+
}>;
|
|
11
|
+
/** Every message is submitted with a request id, so its entry can name it: the Caller's key, or one of our own. */
|
|
12
|
+
export declare function requestIdFor(idempotencyKey: string | undefined): string;
|
|
13
|
+
export declare function isInput(record: SubmissionRecord | undefined): record is InputRecord;
|
|
14
|
+
export declare function hasEnded(record: InputRecord): record is EndedRecord;
|
|
15
|
+
/** What a conversation's `pi.inbox`, `pi.usage` and `pi.live` say, as the documents and an event snapshot hold them. */
|
|
16
|
+
export interface InboxView {
|
|
17
|
+
readonly items: readonly {
|
|
18
|
+
readonly id: number;
|
|
19
|
+
readonly mode: string;
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
22
|
+
export interface UsageView {
|
|
23
|
+
readonly models: Readonly<Record<string, Parameters<typeof usageOf>[0]>>;
|
|
24
|
+
readonly tools: Readonly<Record<string, Parameters<typeof usageOf>[0]>>;
|
|
25
|
+
}
|
|
26
|
+
export interface LiveView {
|
|
27
|
+
readonly run?: {
|
|
28
|
+
readonly inputs: readonly number[];
|
|
29
|
+
};
|
|
30
|
+
readonly generation?: {
|
|
31
|
+
readonly message?: unknown;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** The queued inputs in the order they will be taken: steers before follow-ups, each in arrival order. */
|
|
35
|
+
export declare function queuedInputs(inbox: InboxView | undefined): number[];
|
|
36
|
+
/** A message as the API has it. An ended one needs its answer entry: the entry pi-durable settled it with. */
|
|
37
|
+
export declare function messageOf(record: InputRecord, ids: Ids, answer?: EntryRecord): Message;
|
|
38
|
+
/** The session as it is now, from its documents as one commit or one event snapshot holds them. */
|
|
39
|
+
export declare function sessionOf(id: string, live: LiveView | undefined, usage: UsageView | undefined, agent: Pick<AgentState, "model" | "thinkingLevel" | "instructions" | "tools"> | undefined): Session;
|
|
40
|
+
/** What a conversation's `pi.usage`, or the instance's total, says it spent. */
|
|
41
|
+
export declare function usageOfState(usage: UsageView | undefined): Usage;
|