@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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `messages.stream` (SPEC.md §6.2): send a message and deliver it as it is written, over the Agent's own reads.
|
|
3
|
+
* The session is watched before the message is sent, so nothing it writes afterwards is missed; a send whose key the
|
|
4
|
+
* session has seen delivers that message from wherever it is.
|
|
5
|
+
*/
|
|
6
|
+
import { type Entry, type Message, type MessageStreamEvent, type Page, type SendRequest, type SessionEvent } from "../../agent-api.ts";
|
|
7
|
+
export interface StreamSource {
|
|
8
|
+
events(sessionId: string, after?: string): AsyncIterable<SessionEvent>;
|
|
9
|
+
/** A send, and whether its key had already been sent: the message is then delivered from where it is. */
|
|
10
|
+
admit(request: SendRequest): Promise<{
|
|
11
|
+
message: Message;
|
|
12
|
+
replayed: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
get(sessionId: string, messageId: string): Promise<Message>;
|
|
15
|
+
entry(sessionId: string, entryId: string): Promise<Entry>;
|
|
16
|
+
entries(sessionId: string, options: {
|
|
17
|
+
after: string;
|
|
18
|
+
}): Promise<Page<Entry>>;
|
|
19
|
+
}
|
|
20
|
+
export declare function messageStream(source: StreamSource, request: SendRequest, signal: AbortSignal | undefined): AsyncIterable<MessageStreamEvent>;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `messages.stream` (SPEC.md §6.2): send a message and deliver it as it is written, over the Agent's own reads.
|
|
3
|
+
* The session is watched before the message is sent, so nothing it writes afterwards is missed; a send whose key the
|
|
4
|
+
* session has seen delivers that message from wherever it is.
|
|
5
|
+
*/
|
|
6
|
+
import { AgentError, agentError, errorData, hasEnded, } from "../../agent-api.js";
|
|
7
|
+
const messageOf = (entry) => entry.kind === "user" ? entry.data.messageId : undefined;
|
|
8
|
+
/**
|
|
9
|
+
* The next of `events`, a read that failed as the `stream_error` it is once the message has been delivered: a session
|
|
10
|
+
* watched again cannot reject the stream then, or a Caller would take a message already recorded for one refused.
|
|
11
|
+
*/
|
|
12
|
+
const nextOf = (events) => events.next().catch((error) => ({ done: false, value: failedRead(error) }));
|
|
13
|
+
const failedRead = (error) => ({
|
|
14
|
+
type: "stream_error",
|
|
15
|
+
error: errorData(error instanceof AgentError
|
|
16
|
+
? error
|
|
17
|
+
: agentError("internal", error instanceof Error ? error.message : String(error))),
|
|
18
|
+
});
|
|
19
|
+
export function messageStream(source, request, signal) {
|
|
20
|
+
const { sessionId } = request;
|
|
21
|
+
let active;
|
|
22
|
+
const leave = () => {
|
|
23
|
+
const current = active;
|
|
24
|
+
active = undefined;
|
|
25
|
+
void current?.return?.();
|
|
26
|
+
};
|
|
27
|
+
const follow = (after) => {
|
|
28
|
+
leave();
|
|
29
|
+
active = source.events(sessionId, after)[Symbol.asyncIterator]();
|
|
30
|
+
return active;
|
|
31
|
+
};
|
|
32
|
+
/** From the message's `user` entry, which arrives live, through its `done`. */
|
|
33
|
+
async function* live(watching, id) {
|
|
34
|
+
let opened = false;
|
|
35
|
+
for (let next = await watching.next(); !next.done; next = await watching.next()) {
|
|
36
|
+
const event = next.value;
|
|
37
|
+
if (event.type === "stream_error")
|
|
38
|
+
return yield event;
|
|
39
|
+
// A queued message withdrawn by a cancel ends without ever being taken.
|
|
40
|
+
if (event.type === "done" && event.message.id === id)
|
|
41
|
+
return yield event;
|
|
42
|
+
if (!opened && event.type === "entry" && messageOf(event.entry) === id)
|
|
43
|
+
opened = true;
|
|
44
|
+
if (opened)
|
|
45
|
+
yield event;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A running message sent again, whose `user` entry was written before: its entries so far, then live. What the
|
|
50
|
+
* session replays is held until the message is read again, since one that ended meanwhile may have been followed by
|
|
51
|
+
* the next message's entries.
|
|
52
|
+
*/
|
|
53
|
+
async function* running(message) {
|
|
54
|
+
let opening;
|
|
55
|
+
try {
|
|
56
|
+
opening = await source.entry(sessionId, message.entryId);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return yield failedRead(error);
|
|
60
|
+
}
|
|
61
|
+
const after = follow(message.entryId);
|
|
62
|
+
const held = [];
|
|
63
|
+
for (let next = await nextOf(after); !next.done; next = await nextOf(after)) {
|
|
64
|
+
const event = next.value;
|
|
65
|
+
if (event.type === "stream_error")
|
|
66
|
+
return yield event;
|
|
67
|
+
if (event.type !== "state") {
|
|
68
|
+
held.push(event);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
let now;
|
|
72
|
+
try {
|
|
73
|
+
now = await source.get(sessionId, message.id);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return yield failedRead(error);
|
|
77
|
+
}
|
|
78
|
+
if (hasEnded(now)) {
|
|
79
|
+
leave();
|
|
80
|
+
return yield* ended(now, opening, held);
|
|
81
|
+
}
|
|
82
|
+
yield { type: "entry", entry: opening };
|
|
83
|
+
yield* held;
|
|
84
|
+
yield event;
|
|
85
|
+
for (let next = await nextOf(after); !next.done; next = await nextOf(after)) {
|
|
86
|
+
const event = next.value;
|
|
87
|
+
yield event;
|
|
88
|
+
if (event.type === "stream_error")
|
|
89
|
+
return;
|
|
90
|
+
if (event.type === "done" && event.message.id === message.id)
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** An ended message: its entries through its answer, then its `done`. */
|
|
97
|
+
async function* ended(done, opening, held) {
|
|
98
|
+
const { entryId, answer } = done;
|
|
99
|
+
if (entryId === undefined)
|
|
100
|
+
return yield { type: "done", message: done };
|
|
101
|
+
try {
|
|
102
|
+
yield { type: "entry", entry: opening ?? (await source.entry(sessionId, entryId)) };
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return yield failedRead(error);
|
|
106
|
+
}
|
|
107
|
+
if (answer === undefined)
|
|
108
|
+
return yield { type: "done", message: done };
|
|
109
|
+
const through = held?.findIndex((event) => event.type === "entry" && event.entry.id === answer.entryId) ?? -1;
|
|
110
|
+
if (held !== undefined && through !== -1) {
|
|
111
|
+
yield* held.slice(0, through + 1).filter((event) => event.type === "entry");
|
|
112
|
+
return yield { type: "done", message: done };
|
|
113
|
+
}
|
|
114
|
+
let cursor = entryId;
|
|
115
|
+
for (;;) {
|
|
116
|
+
let page;
|
|
117
|
+
try {
|
|
118
|
+
page = await source.entries(sessionId, { after: cursor });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return yield failedRead(error);
|
|
122
|
+
}
|
|
123
|
+
for (const entry of page.data) {
|
|
124
|
+
yield { type: "entry", entry };
|
|
125
|
+
if (entry.id === answer.entryId)
|
|
126
|
+
return yield { type: "done", message: done };
|
|
127
|
+
}
|
|
128
|
+
if (page.next === undefined) {
|
|
129
|
+
return yield failedRead(agentError("internal", `the answer ${answer.entryId} of message ${done.id} is not in its session`));
|
|
130
|
+
}
|
|
131
|
+
cursor = page.next;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function* events() {
|
|
135
|
+
try {
|
|
136
|
+
const watching = follow();
|
|
137
|
+
const snapshot = await watching.next();
|
|
138
|
+
if (snapshot.done || snapshot.value.type !== "state") {
|
|
139
|
+
if (!snapshot.done && snapshot.value.type === "stream_error")
|
|
140
|
+
throw new AgentError(snapshot.value.error);
|
|
141
|
+
throw agentError("internal", "the session's events did not begin with its state");
|
|
142
|
+
}
|
|
143
|
+
const { message, replayed } = await source.admit(request);
|
|
144
|
+
yield { type: "message", message };
|
|
145
|
+
if (hasEnded(message)) {
|
|
146
|
+
leave();
|
|
147
|
+
yield* ended(message);
|
|
148
|
+
}
|
|
149
|
+
else if (replayed && message.status === "running" && message.entryId !== undefined) {
|
|
150
|
+
yield* running({ ...message, entryId: message.entryId });
|
|
151
|
+
}
|
|
152
|
+
else
|
|
153
|
+
yield* live(watching, message.id);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
leave();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { [Symbol.asyncIterator]: () => leavableIterator(events(), leave, signal) };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A stream the Caller can leave at any point. An async generator suspended on an await parks inside it, and
|
|
163
|
+
* `gen.return()` queues behind the pending `next()` (async-generator semantics), so leaving first ends what it awaits
|
|
164
|
+
* (`leave`), then returns the generator. Leaving also silences it: a generator on its way out can still reach a
|
|
165
|
+
* `yield`, which must not hand an event to a Caller that left. A `signal` leaves the same way, and the read pending
|
|
166
|
+
* then, and any after it, rejects with the signal's reason.
|
|
167
|
+
*/
|
|
168
|
+
function leavableIterator(gen, leave, signal) {
|
|
169
|
+
let left = false;
|
|
170
|
+
const teardown = async () => {
|
|
171
|
+
left = true;
|
|
172
|
+
leave();
|
|
173
|
+
await gen.return(undefined).catch(() => { });
|
|
174
|
+
};
|
|
175
|
+
const released = () => {
|
|
176
|
+
throw signal?.reason;
|
|
177
|
+
};
|
|
178
|
+
let onAbort;
|
|
179
|
+
const aborted = signal === undefined
|
|
180
|
+
? undefined
|
|
181
|
+
: new Promise((_, reject) => {
|
|
182
|
+
onAbort = () => {
|
|
183
|
+
void teardown();
|
|
184
|
+
reject(signal.reason);
|
|
185
|
+
};
|
|
186
|
+
if (signal.aborted)
|
|
187
|
+
onAbort();
|
|
188
|
+
else
|
|
189
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
190
|
+
});
|
|
191
|
+
aborted?.catch(() => { });
|
|
192
|
+
const stopListening = () => {
|
|
193
|
+
if (onAbort !== undefined)
|
|
194
|
+
signal?.removeEventListener("abort", onAbort);
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
async next() {
|
|
198
|
+
if (signal?.aborted)
|
|
199
|
+
released();
|
|
200
|
+
const result = await (aborted === undefined ? gen.next() : Promise.race([gen.next(), aborted]));
|
|
201
|
+
if (left) {
|
|
202
|
+
if (signal?.aborted)
|
|
203
|
+
released();
|
|
204
|
+
return { done: true, value: undefined };
|
|
205
|
+
}
|
|
206
|
+
if (result.done)
|
|
207
|
+
stopListening();
|
|
208
|
+
return result;
|
|
209
|
+
},
|
|
210
|
+
async return() {
|
|
211
|
+
stopListening();
|
|
212
|
+
await teardown();
|
|
213
|
+
return { done: true, value: undefined };
|
|
214
|
+
},
|
|
215
|
+
async throw(error) {
|
|
216
|
+
stopListening();
|
|
217
|
+
await teardown();
|
|
218
|
+
throw error;
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type PromptSection } from "@earendil-works/pi-durable";
|
|
2
|
+
import type { ResolvedContent } from "../../content/resolve.ts";
|
|
3
|
+
import type { AgentCommand } from "./agent.ts";
|
|
4
|
+
export interface Skill {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
/** Absolute: what the model's `read` opens. */
|
|
8
|
+
filePath: string;
|
|
9
|
+
baseDir: string;
|
|
10
|
+
/** Offered only through `/skill:<name>`, never listed to the model. */
|
|
11
|
+
disableModelInvocation: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** The agent's skills: `skills/<name>/SKILL.md`. One that breaks the specification is reported and skipped. */
|
|
14
|
+
export declare function readSkills(agentDir: string): Skill[];
|
|
15
|
+
/** Arguments as a shell splits them: by whitespace, with `"…"` or `'…'` keeping one argument together. */
|
|
16
|
+
export declare function parseArguments(text: string): string[];
|
|
17
|
+
/**
|
|
18
|
+
* A template's text with its arguments, in pi's format: `$1`… positional, `$@` and `$ARGUMENTS` all of them,
|
|
19
|
+
* `${N:-default}` a positional one or its default, `${@:N}` and `${@:N:L}` a slice from the Nth.
|
|
20
|
+
*/
|
|
21
|
+
export declare function expandTemplate(body: string, args: readonly string[]): string;
|
|
22
|
+
/** What a Caller may offer as `/<name>`: the prompt templates, then `skill:<name>` for each skill. */
|
|
23
|
+
export declare function readCommands(agentDir: string): AgentCommand[];
|
|
24
|
+
/**
|
|
25
|
+
* The prompt the definition contributes: `SYSTEM.md` as written, `AGENTS.md` as project context, the skills, where it
|
|
26
|
+
* works, and, deployed, what of a change it makes to itself lasts. `wake`: the agent has the wake tool.
|
|
27
|
+
*/
|
|
28
|
+
export declare function definitionSections(agentDir: string, content: readonly ResolvedContent[], options: {
|
|
29
|
+
wake: boolean;
|
|
30
|
+
}): PromptSection[];
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The definition's text (architecture.md §3), read for each request so an edit takes effect on the next answer:
|
|
3
|
+
* `SYSTEM.md` and `AGENTS.md` as prompt sections, `skills/` as Agent Skills (agentskills.io) and `prompts/` as prompt
|
|
4
|
+
* templates. A skill is loaded as pi does it: the prompt lists each one's name, description and location, the model
|
|
5
|
+
* reads its `SKILL.md` when a task matches, and `/skill:<name>` loads it into the message.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
8
|
+
import { basename, join } from "node:path";
|
|
9
|
+
import { section } from "@earendil-works/pi-durable";
|
|
10
|
+
import { parse as parseYaml } from "yaml";
|
|
11
|
+
import { log } from "../../log.js";
|
|
12
|
+
import { isAgentcoreRuntime, isDeployedWorkspace } from "../../paths.js";
|
|
13
|
+
const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
14
|
+
const SKILL_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
15
|
+
/** A file's YAML frontmatter and its body. A file without frontmatter is all body. */
|
|
16
|
+
function frontmatter(text, file) {
|
|
17
|
+
const match = FRONTMATTER.exec(text);
|
|
18
|
+
if (match === null)
|
|
19
|
+
return { fields: {}, body: text };
|
|
20
|
+
const fields = parseYaml(match[1]);
|
|
21
|
+
if (fields !== null && (typeof fields !== "object" || Array.isArray(fields))) {
|
|
22
|
+
throw new Error(`${file}: its frontmatter is not a set of fields`);
|
|
23
|
+
}
|
|
24
|
+
return { fields: (fields ?? {}), body: text.slice(match[0].length) };
|
|
25
|
+
}
|
|
26
|
+
/** Each problem once per process: the text is read for every request, and a broken file should not flood the log. */
|
|
27
|
+
const reported = new Set();
|
|
28
|
+
function report(problem) {
|
|
29
|
+
if (reported.has(problem))
|
|
30
|
+
return;
|
|
31
|
+
reported.add(problem);
|
|
32
|
+
log.warn(`[fastagent] ${problem}; it is skipped`);
|
|
33
|
+
}
|
|
34
|
+
/** The directories in `dir`. An entry that cannot be read (a symlink to nothing) is reported and skipped. */
|
|
35
|
+
function directories(dir) {
|
|
36
|
+
if (!existsSync(dir))
|
|
37
|
+
return [];
|
|
38
|
+
return readdirSync(dir)
|
|
39
|
+
.sort()
|
|
40
|
+
.filter((name) => {
|
|
41
|
+
if (name.startsWith("."))
|
|
42
|
+
return false;
|
|
43
|
+
try {
|
|
44
|
+
return statSync(join(dir, name)).isDirectory();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
report(`${join(basename(dir), name)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** The agent's skills: `skills/<name>/SKILL.md`. One that breaks the specification is reported and skipped. */
|
|
53
|
+
export function readSkills(agentDir) {
|
|
54
|
+
const root = join(agentDir, "skills");
|
|
55
|
+
const skills = [];
|
|
56
|
+
for (const entry of directories(root)) {
|
|
57
|
+
const baseDir = join(root, entry);
|
|
58
|
+
const filePath = join(baseDir, "SKILL.md");
|
|
59
|
+
const label = `skills/${entry}/SKILL.md`;
|
|
60
|
+
if (!existsSync(filePath))
|
|
61
|
+
continue;
|
|
62
|
+
let fields;
|
|
63
|
+
try {
|
|
64
|
+
({ fields } = frontmatter(readFileSync(filePath, "utf8"), label));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
report(`${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const name = fields.name ?? entry;
|
|
71
|
+
const { description } = fields;
|
|
72
|
+
if (typeof name !== "string" || name.length > 64 || !SKILL_NAME.test(name)) {
|
|
73
|
+
report(`${label}: "name" must be lowercase letters, digits and single hyphens, at most 64 characters`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (typeof description !== "string" || description.trim() === "" || description.length > 1024) {
|
|
77
|
+
report(`${label}: "description" must be a text of at most 1024 characters`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (skills.some((skill) => skill.name === name)) {
|
|
81
|
+
report(`${label}: another skill is named "${name}"`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
skills.push({
|
|
85
|
+
name,
|
|
86
|
+
description: description.trim(),
|
|
87
|
+
filePath,
|
|
88
|
+
baseDir,
|
|
89
|
+
disableModelInvocation: fields["disable-model-invocation"] === true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return skills;
|
|
93
|
+
}
|
|
94
|
+
/** The agent's prompt templates: `prompts/<name>.md`, a `description` in the frontmatter and the body as the text. */
|
|
95
|
+
function readTemplates(agentDir) {
|
|
96
|
+
const root = join(agentDir, "prompts");
|
|
97
|
+
if (!existsSync(root))
|
|
98
|
+
return [];
|
|
99
|
+
return readdirSync(root)
|
|
100
|
+
.sort()
|
|
101
|
+
.flatMap((entry) => {
|
|
102
|
+
if (!entry.endsWith(".md"))
|
|
103
|
+
return [];
|
|
104
|
+
const label = `prompts/${entry}`;
|
|
105
|
+
try {
|
|
106
|
+
const { fields, body } = frontmatter(readFileSync(join(root, entry), "utf8"), label);
|
|
107
|
+
const description = typeof fields.description === "string" ? fields.description : undefined;
|
|
108
|
+
return [{ name: entry.slice(0, -3), ...(description === undefined ? {} : { description }), body }];
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
report(`${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** Arguments as a shell splits them: by whitespace, with `"…"` or `'…'` keeping one argument together. */
|
|
117
|
+
export function parseArguments(text) {
|
|
118
|
+
const args = [];
|
|
119
|
+
let current = "";
|
|
120
|
+
let started = false;
|
|
121
|
+
let quote;
|
|
122
|
+
for (const char of text) {
|
|
123
|
+
if (quote !== undefined) {
|
|
124
|
+
if (char === quote)
|
|
125
|
+
quote = undefined;
|
|
126
|
+
else
|
|
127
|
+
current += char;
|
|
128
|
+
}
|
|
129
|
+
else if (char === '"' || char === "'") {
|
|
130
|
+
quote = char;
|
|
131
|
+
started = true;
|
|
132
|
+
}
|
|
133
|
+
else if (/\s/.test(char)) {
|
|
134
|
+
if (started)
|
|
135
|
+
args.push(current);
|
|
136
|
+
current = "";
|
|
137
|
+
started = false;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
current += char;
|
|
141
|
+
started = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (started)
|
|
145
|
+
args.push(current);
|
|
146
|
+
return args;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* A template's text with its arguments, in pi's format: `$1`… positional, `$@` and `$ARGUMENTS` all of them,
|
|
150
|
+
* `${N:-default}` a positional one or its default, `${@:N}` and `${@:N:L}` a slice from the Nth.
|
|
151
|
+
*/
|
|
152
|
+
export function expandTemplate(body, args) {
|
|
153
|
+
const all = args.join(" ");
|
|
154
|
+
return body.replace(/\$\{(\d+|@|ARGUMENTS):-([^}]*)\}|\$\{@:(\d+)(?::(\d+))?\}|\$(ARGUMENTS|@|\d+)/g, (_match, target, fallback, from, length, simple) => {
|
|
155
|
+
if (target !== undefined) {
|
|
156
|
+
const value = target === "@" || target === "ARGUMENTS" ? all : args[Number(target) - 1];
|
|
157
|
+
return value ? value : (fallback ?? "");
|
|
158
|
+
}
|
|
159
|
+
if (from !== undefined) {
|
|
160
|
+
const start = Math.max(0, Number(from) - 1);
|
|
161
|
+
return (length === undefined ? args.slice(start) : args.slice(start, start + Number(length))).join(" ");
|
|
162
|
+
}
|
|
163
|
+
return simple === "@" || simple === "ARGUMENTS" ? all : (args[Number(simple) - 1] ?? "");
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** A skill's instructions as a message carries them when `/skill:<name>` loads it, as pi does. */
|
|
167
|
+
function loadedSkill(skill, args) {
|
|
168
|
+
const { body } = frontmatter(readFileSync(skill.filePath, "utf8"), skill.filePath);
|
|
169
|
+
const block = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body.trim()}\n</skill>`;
|
|
170
|
+
return args.trim() === "" ? block : `${block}\n\n${args.trim()}`;
|
|
171
|
+
}
|
|
172
|
+
/** What a Caller may offer as `/<name>`: the prompt templates, then `skill:<name>` for each skill. */
|
|
173
|
+
export function readCommands(agentDir) {
|
|
174
|
+
return [
|
|
175
|
+
...readTemplates(agentDir).map((template) => ({
|
|
176
|
+
name: template.name,
|
|
177
|
+
...(template.description === undefined ? {} : { description: template.description }),
|
|
178
|
+
expand: (args) => expandTemplate(template.body, parseArguments(args)),
|
|
179
|
+
})),
|
|
180
|
+
...readSkills(agentDir).map((skill) => ({
|
|
181
|
+
name: `skill:${skill.name}`,
|
|
182
|
+
description: skill.description,
|
|
183
|
+
expand: (args) => loadedSkill(skill, args),
|
|
184
|
+
})),
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
const escapeXml = (text) => text
|
|
188
|
+
.replace(/&/g, "&")
|
|
189
|
+
.replace(/</g, "<")
|
|
190
|
+
.replace(/>/g, ">")
|
|
191
|
+
.replace(/"/g, """)
|
|
192
|
+
.replace(/'/g, "'");
|
|
193
|
+
/** The skills the model may choose, as pi lists them: a skill's instructions are loaded only when a task needs them. */
|
|
194
|
+
function skillsPrompt(skills) {
|
|
195
|
+
const listed = skills.filter((skill) => !skill.disableModelInvocation);
|
|
196
|
+
if (listed.length === 0)
|
|
197
|
+
return undefined;
|
|
198
|
+
return [
|
|
199
|
+
"The following skills provide specialized instructions for specific tasks.",
|
|
200
|
+
"Use the read tool to load a skill's file when the task matches its description.",
|
|
201
|
+
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
|
|
202
|
+
"",
|
|
203
|
+
"<available_skills>",
|
|
204
|
+
...listed.flatMap((skill) => [
|
|
205
|
+
" <skill>",
|
|
206
|
+
` <name>${escapeXml(skill.name)}</name>`,
|
|
207
|
+
` <description>${escapeXml(skill.description)}</description>`,
|
|
208
|
+
` <location>${escapeXml(skill.filePath)}</location>`,
|
|
209
|
+
" </skill>",
|
|
210
|
+
]),
|
|
211
|
+
"</available_skills>",
|
|
212
|
+
].join("\n");
|
|
213
|
+
}
|
|
214
|
+
const fileText = (path) => {
|
|
215
|
+
if (!existsSync(path))
|
|
216
|
+
return undefined;
|
|
217
|
+
const text = readFileSync(path, "utf8").trim();
|
|
218
|
+
return text === "" ? undefined : text;
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* The prompt the definition contributes: `SYSTEM.md` as written, `AGENTS.md` as project context, the skills, where it
|
|
222
|
+
* works, and, deployed, what of a change it makes to itself lasts. `wake`: the agent has the wake tool.
|
|
223
|
+
*/
|
|
224
|
+
export function definitionSections(agentDir, content, options) {
|
|
225
|
+
return [
|
|
226
|
+
section("system", () => fileText(join(agentDir, "SYSTEM.md")), { tag: false }),
|
|
227
|
+
section("project_context", () => fileText(join(agentDir, "AGENTS.md"))),
|
|
228
|
+
section("skills", () => skillsPrompt(readSkills(agentDir))),
|
|
229
|
+
section("content", () => contentSection({ agentDir, content })),
|
|
230
|
+
section("self_change", () => (isDeployedWorkspace() ? selfChangeSection(options.wake) : undefined)),
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* What takes effect when, and the agent's own path to a new capability, are the same on every host; how long the
|
|
235
|
+
* storage lives is the host's answer: AgentCore's managed mount is reset by every deploy, so telling that agent to keep
|
|
236
|
+
* work "outside the definition" would name a location its next deploy erases. The skill it writes lives in the
|
|
237
|
+
* definition directory, which the next deployment replaces (or, on AgentCore, erases with everything else), so the
|
|
238
|
+
* sentence says so, and where a skill that should outlast it has to go.
|
|
239
|
+
*/
|
|
240
|
+
function selfChangeSection(wake) {
|
|
241
|
+
const deployment = isAgentcoreRuntime()
|
|
242
|
+
? "Every deployment of a new version resets this host's storage entirely"
|
|
243
|
+
: "Each deployment replaces your directory with the author's release";
|
|
244
|
+
return (`Your directory survives restarts, including uncommitted work; /tmp does not. ${deployment}, so anything that ` +
|
|
245
|
+
`must outlive a deployment belongs in an external system (a git remote, an issue tracker, a database). ` +
|
|
246
|
+
`SYSTEM.md, AGENTS.md, skills/ and prompts/ are read for every message; changes to code in src/ (tools, ` +
|
|
247
|
+
`extensions, channels, schedules) or to configuration (fastagent.config.ts, context.json, package.json) take ` +
|
|
248
|
+
`effect when the service restarts. A tool you install yourself (apt-get, npm install -g) belongs to this machine ` +
|
|
249
|
+
`and is gone when its container is replaced. To give yourself a new capability now, write a skill — a SKILL.md in ` +
|
|
250
|
+
`your definition's skills/ directory, with any script it needs run through bash. It lasts until the next ` +
|
|
251
|
+
`deployment replaces that directory, so a capability that should outlast it belongs in the author's release: ` +
|
|
252
|
+
`propose it to them.` +
|
|
253
|
+
// Named only when it has the tool: naming a tool the model does not have invites calls to it.
|
|
254
|
+
(wake ? " To schedule your own follow-up work, use the wake tool." : ""));
|
|
255
|
+
}
|
|
256
|
+
/** What a content entry of this kind is, said to the agent: where it is, and whether another instance shares it. */
|
|
257
|
+
function contentKind(c) {
|
|
258
|
+
if (c.kind !== "github")
|
|
259
|
+
return "a directory on this machine";
|
|
260
|
+
if (!c.clone)
|
|
261
|
+
return `a checkout of github ${c.repo} on this machine`;
|
|
262
|
+
// Brought up to date in place at each start where git can do so without touching the agent's work; what the agent
|
|
263
|
+
// changes stays, and reaches anyone else only when pushed. The declared ref is what it follows, not a claim about
|
|
264
|
+
// where it is: a clone the agent moved elsewhere is left there.
|
|
265
|
+
const clone = `a shallow clone of github ${c.repo}${c.ref ? ` (declared ${c.ref})` : ""} in your own storage`;
|
|
266
|
+
return c.readonly
|
|
267
|
+
? `${clone}, brought up to date each time you start`
|
|
268
|
+
: `${clone}: what you change in it stays, and each time you start it is brought up to date where that touches nothing of yours; push to share a change`;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Where the agent is and what it works on (agent-model.md §4): its own directory is itself, and each content entry is named
|
|
272
|
+
* with its location, whether it is the agent's to change, and where a change there goes. A result written where it
|
|
273
|
+
* does not last is how knowledge gets lost, so the agent is told which is which.
|
|
274
|
+
*/
|
|
275
|
+
function contentSection({ agentDir, content }) {
|
|
276
|
+
const own = `Your working directory, ${agentDir}, is your own directory: it holds your definition, and files you create ` +
|
|
277
|
+
`land there unless you put them elsewhere.`;
|
|
278
|
+
if (content.length === 0)
|
|
279
|
+
return `${own} Nothing else is declared for you: you work only in your own directory.`;
|
|
280
|
+
const line = (c) => `- ${c.name}: ${c.location} (${contentKind(c)})${c.description !== undefined ? ` ${c.description}` : ""}`;
|
|
281
|
+
const worksOn = content.filter((c) => !c.readonly);
|
|
282
|
+
const knows = content.filter((c) => c.readonly);
|
|
283
|
+
return [
|
|
284
|
+
own,
|
|
285
|
+
...(worksOn.length > 0 ? ["You work on:", ...worksOn.map(line)] : []),
|
|
286
|
+
...(knows.length > 0 ? ["You know, and do not write:", ...knows.map(line)] : []),
|
|
287
|
+
`Run a command in one of these directories with \`cd <its location> && …\`; file tools take the location directly.` +
|
|
288
|
+
(worksOn.length > 0
|
|
289
|
+
? " A result worth keeping belongs in a directory you work on, not in your own directory."
|
|
290
|
+
: " You write only in your own directory: the directories above are for reading."),
|
|
291
|
+
].join("\n");
|
|
292
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Extension } from "@earendil-works/pi-durable";
|
|
2
|
+
export declare const WAKE_EXTENSION = "fastagent.wake";
|
|
3
|
+
export declare const WAKE_TOOLS: string[];
|
|
4
|
+
/**
|
|
5
|
+
* `ticking` says whether a clock fires the wake-ups kept in `stateRoot` now; a wake-up nothing would fire is refused.
|
|
6
|
+
* Without it, one always does: `dev`, `start` and AgentCore serve whatever they open.
|
|
7
|
+
*/
|
|
8
|
+
export declare function wakeExtension(stateRoot: string, options?: {
|
|
9
|
+
now?: () => Date;
|
|
10
|
+
ticking?: () => boolean;
|
|
11
|
+
}): Extension;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `wake` and `unwake`: the agent schedules itself back into the conversation it is in, one-shot or recurring, kept in
|
|
3
|
+
* the instance's state (schedule/wakeups.ts), which the scheduler fires. A pair over one store: a definition that
|
|
4
|
+
* defines either name gets neither.
|
|
5
|
+
*/
|
|
6
|
+
import { Type } from "@earendil-works/pi-ai";
|
|
7
|
+
import { defineExtension, defineTool } from "@earendil-works/pi-durable";
|
|
8
|
+
import { addWakeup, parseDelayMs, removeWakeup } from "../../schedule/wakeups.js";
|
|
9
|
+
import { sessionIdOf } from "./ids.js";
|
|
10
|
+
export const WAKE_EXTENSION = "fastagent.wake";
|
|
11
|
+
export const WAKE_TOOLS = ["wake", "unwake"];
|
|
12
|
+
const said = (text) => ({ output: [{ type: "text", text }] });
|
|
13
|
+
const refused = (text) => ({ output: [{ type: "text", text }], isError: true });
|
|
14
|
+
const NOT_A_SESSION = "wake-ups belong to a session, and this conversation is not one (a subagent's): nothing would wake it.";
|
|
15
|
+
const NO_CLOCK = "nothing fires wake-ups for this agent now: the app that opened it does not serve it, so no clock runs. Do the " +
|
|
16
|
+
"work now, or say it cannot be scheduled.";
|
|
17
|
+
/**
|
|
18
|
+
* `ticking` says whether a clock fires the wake-ups kept in `stateRoot` now; a wake-up nothing would fire is refused.
|
|
19
|
+
* Without it, one always does: `dev`, `start` and AgentCore serve whatever they open.
|
|
20
|
+
*/
|
|
21
|
+
export function wakeExtension(stateRoot, options = {}) {
|
|
22
|
+
const now = options.now ?? (() => new Date());
|
|
23
|
+
return defineExtension({
|
|
24
|
+
name: WAKE_EXTENSION,
|
|
25
|
+
tools: [
|
|
26
|
+
defineTool({
|
|
27
|
+
name: "wake",
|
|
28
|
+
description: "Schedule yourself to wake up later and continue in THIS conversation. ONE-SHOT: pass `in`, a duration " +
|
|
29
|
+
'string with a unit ("30m", "2h", "1d") or a number of seconds, to resume a task after a delay. RECURRING: ' +
|
|
30
|
+
"pass `cron` (5-field, optional `tz`) to wake repeatedly; use it sparingly, and `unwake` with the returned " +
|
|
31
|
+
"id when the job is done. Exactly one of `in`/`cron`. When the time comes, a new message arrives in this " +
|
|
32
|
+
"same conversation with `prompt` as its instruction, tagged with the wake-up's id so you can tell it from a " +
|
|
33
|
+
"user's, and you keep the full context of this conversation. Its plain reply is delivered to no one: to " +
|
|
34
|
+
"reach a person it must call a delivery tool (a channel's send tool), as a scheduled job does.",
|
|
35
|
+
parameters: Type.Object({
|
|
36
|
+
in: Type.Optional(Type.Union([Type.String(), Type.Number()], {
|
|
37
|
+
description: 'one-shot delay: a duration string with a unit ("30m" / "2h" / "1d"), or a number of seconds',
|
|
38
|
+
})),
|
|
39
|
+
cron: Type.Optional(Type.String({ description: 'recurring: a 5-field cron expression (e.g. "0 9 * * *")' })),
|
|
40
|
+
tz: Type.Optional(Type.String({ description: 'IANA timezone for `cron` (default "UTC")' })),
|
|
41
|
+
prompt: Type.String({
|
|
42
|
+
minLength: 1,
|
|
43
|
+
description: "the instruction for the woken message (it arrives in this same conversation)",
|
|
44
|
+
}),
|
|
45
|
+
}),
|
|
46
|
+
execute: async (args, api, context) => {
|
|
47
|
+
if (options.ticking?.() === false)
|
|
48
|
+
return refused(NO_CLOCK);
|
|
49
|
+
if ((args.in === undefined) === (args.cron === undefined)) {
|
|
50
|
+
return refused("pass exactly one of `in` (one-shot) or `cron` (recurring).");
|
|
51
|
+
}
|
|
52
|
+
const session = await sessionIdOf(api, context);
|
|
53
|
+
if (session === undefined)
|
|
54
|
+
return refused(NOT_A_SESSION);
|
|
55
|
+
const at = now();
|
|
56
|
+
if (args.cron !== undefined) {
|
|
57
|
+
const added = addWakeup(stateRoot, { session, prompt: args.prompt, cron: args.cron, tz: args.tz }, at);
|
|
58
|
+
if (!added.ok)
|
|
59
|
+
return refused(added.error);
|
|
60
|
+
return said(`OK — recurring wake ${added.id} (cron "${args.cron}"${args.tz ? ` ${args.tz}` : ""}), first at ${added.fireAt}: ${args.prompt}. Use unwake({ id: "${added.id}" }) to stop it.`);
|
|
61
|
+
}
|
|
62
|
+
const ms = parseDelayMs(args.in);
|
|
63
|
+
if (ms === undefined) {
|
|
64
|
+
return refused(`couldn't parse "in" (${JSON.stringify(args.in)}): use a unit like "30m" / "2h" / "1d", or a number of seconds (a bare number as text like "120" is rejected).`);
|
|
65
|
+
}
|
|
66
|
+
const added = addWakeup(stateRoot, { session, prompt: args.prompt, fireAt: new Date(at.getTime() + ms) }, at);
|
|
67
|
+
if (!added.ok)
|
|
68
|
+
return refused(added.error);
|
|
69
|
+
return said(`OK — I'll wake up at ${added.fireAt} (id ${added.id}) to: ${args.prompt}. Use unwake({ id: "${added.id}" }) if it becomes unnecessary.`);
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
defineTool({
|
|
73
|
+
name: "unwake",
|
|
74
|
+
description: "Cancel one of YOUR pending wake-ups (one-shot or recurring) by the id `wake` returned. Use it when a " +
|
|
75
|
+
"scheduled follow-up is no longer needed, especially to stop a recurring wake once its job is done.",
|
|
76
|
+
parameters: Type.Object({
|
|
77
|
+
id: Type.String({ minLength: 1, description: "the wake-up id (returned by `wake`)" }),
|
|
78
|
+
}),
|
|
79
|
+
execute: async (args, api, context) => {
|
|
80
|
+
const session = await sessionIdOf(api, context);
|
|
81
|
+
if (session === undefined)
|
|
82
|
+
return refused(NOT_A_SESSION);
|
|
83
|
+
return removeWakeup(stateRoot, args.id, session)
|
|
84
|
+
? said(`OK — wake-up ${args.id} cancelled.`)
|
|
85
|
+
: refused(`no pending wake-up ${args.id} in this conversation (already fired, or not yours).`);
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
}
|