@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
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `fastagent add <channel>`: drop a `channels/<kind>.ts` adapter-glue file (+ any companion tool, +
|
|
2
|
+
* `fastagent add <channel>`: drop a `src/channels/<kind>.ts` adapter-glue file (+ any companion tool, +
|
|
3
3
|
* `.secrets/.env.example` vars) into an existing agent.
|
|
4
4
|
*/
|
|
5
5
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { detectRuntime } from "../runtime.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { assertInsideAgentDir, exists } from "../paths.js";
|
|
9
|
+
import { channelBundleFiles, channelTemplate } from "./templates.js";
|
|
10
|
+
import { writeValues } from "../value-file.js";
|
|
11
|
+
import { envExamplePath } from "../env.js";
|
|
12
|
+
import { FEISHU_AGENT_SCOPES } from "../channels/feishu/setup-mode.js";
|
|
13
|
+
import { slackBotScopes } from "../channels/slack/manifest.js";
|
|
14
|
+
const AGENT_SCOPE_LIST = FEISHU_AGENT_SCOPES.map((entry) => entry.request).join(", ");
|
|
15
|
+
const FEISHU_PERMISSION_STEP = `before publishing: the app needs ${AGENT_SCOPE_LIST} — \`add feishu\` requests them with the app and names any ` +
|
|
16
|
+
"your tenant withheld (approve those, then publish)";
|
|
17
|
+
/** The setting both clouds read, said the same way in `.env.example` and in `add`'s next steps. */
|
|
18
|
+
const ingressEnv = (prefix) => ({
|
|
19
|
+
name: `${prefix}_INGRESS`,
|
|
20
|
+
hint: "webhook | websocket — unset, `dev` connects by websocket, and `start` and every deployment receive by webhook",
|
|
21
|
+
required: false,
|
|
22
|
+
});
|
|
23
|
+
const LARK_PERMISSION_STEP = `before publishing: add ${AGENT_SCOPE_LIST} — \`add lark\` checks them and opens the permission page for any that ` +
|
|
24
|
+
"are missing";
|
|
11
25
|
const CHANNEL_SCAFFOLDS = {
|
|
12
26
|
telegram: {
|
|
13
27
|
env: [
|
|
@@ -30,10 +44,11 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
30
44
|
{ name: "SLACK_SIGNING_SECRET", hint: "Slack app → Basic Information → App Credentials", required: true },
|
|
31
45
|
],
|
|
32
46
|
steps: [
|
|
33
|
-
|
|
47
|
+
`Slack Bot Token Scopes: ${slackBotScopes().join(", ")}`,
|
|
34
48
|
"enable Agents (agent_view) and leave token rotation OFF (it cannot be turned off again); subscribe app_home_opened, app_context_changed, app_mention, message.im, message.channels, message.groups, message.mpim; set Request URL to <public-url>/slack",
|
|
35
49
|
"reinstall the app after changing scopes, then invite it to each channel it should read",
|
|
36
50
|
"the agent can send messages or files by calling the scaffolded {tools}/slack-send.ts tool",
|
|
51
|
+
"the agent reads the threads of the channel it is asked in via the scaffolded {tools}/slack-threads.ts tool",
|
|
37
52
|
],
|
|
38
53
|
},
|
|
39
54
|
// Feishu is the canonical engine/cloud; Lark international reuses its protocol through a degraded compatibility
|
|
@@ -52,21 +67,23 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
52
67
|
},
|
|
53
68
|
{
|
|
54
69
|
name: "FEISHU_VERIFICATION_TOKEN",
|
|
55
|
-
hint: "captured
|
|
70
|
+
hint: "webhook only (`start`, every deployment) — captured by `deploy --run` (console → Events & Callbacks)",
|
|
56
71
|
required: true,
|
|
57
72
|
},
|
|
58
73
|
{
|
|
59
74
|
name: "FEISHU_ENCRYPT_KEY",
|
|
60
|
-
hint: "optional but recommended — set one in the console and copy it here",
|
|
75
|
+
hint: "webhook only; optional but recommended — set one in the console and copy it here",
|
|
61
76
|
required: false,
|
|
62
77
|
},
|
|
78
|
+
ingressEnv("FEISHU"),
|
|
63
79
|
],
|
|
64
80
|
steps: [
|
|
65
|
-
|
|
81
|
+
FEISHU_PERMISSION_STEP,
|
|
66
82
|
"PUBLISH the app version in the developer console after permission approval — the switch to webhook mode takes effect on publish (one click, once ever; no API for it)",
|
|
67
83
|
"edit {channel} — routing policy (the header walks through the console setup, for hand-made apps)",
|
|
68
|
-
"
|
|
84
|
+
"for webhook ingress, run `{serve} --tunnel` locally or `deploy --run` to register the selected app's event Request URL",
|
|
69
85
|
"the agent can push messages from scheduled turns via the scaffolded {tools}/feishu-send.ts tool",
|
|
86
|
+
"the agent reads the threads of the group it is asked in via the scaffolded {tools}/feishu-threads.ts tool",
|
|
70
87
|
],
|
|
71
88
|
},
|
|
72
89
|
lark: {
|
|
@@ -75,79 +92,63 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
75
92
|
{ name: "LARK_APP_SECRET", hint: "developer console → Credentials & Basic Info", required: true },
|
|
76
93
|
{
|
|
77
94
|
name: "LARK_VERIFICATION_TOKEN",
|
|
78
|
-
hint: "console → Events & Callbacks; authenticates inbound events",
|
|
95
|
+
hint: "webhook only (`start`, every deployment) — console → Events & Callbacks; authenticates inbound events",
|
|
79
96
|
required: true,
|
|
80
97
|
},
|
|
81
98
|
{
|
|
82
99
|
name: "LARK_ENCRYPT_KEY",
|
|
83
|
-
hint: "optional but recommended — set one in the console and copy it here",
|
|
100
|
+
hint: "webhook only; optional but recommended — set one in the console and copy it here",
|
|
84
101
|
required: false,
|
|
85
102
|
},
|
|
103
|
+
ingressEnv("LARK"),
|
|
86
104
|
],
|
|
87
105
|
steps: [
|
|
88
106
|
"finish the console setup: enable Bot and add the required permissions + im.message.receive_v1 event listed in {channel} (do not publish yet)",
|
|
89
|
-
|
|
90
|
-
"run `
|
|
107
|
+
LARK_PERMISSION_STEP,
|
|
108
|
+
"for webhook ingress, run `{serve} --tunnel` and keep it running; if auto-registration reports a config-API 404, manually switch Subscription mode to webhook, set its printed https://…/lark Request URL, save, then create + publish a version",
|
|
91
109
|
"the agent can push messages from scheduled turns via the scaffolded {tools}/lark-send.ts tool",
|
|
110
|
+
"the agent reads the threads of the group it is asked in via the scaffolded {tools}/lark-threads.ts tool",
|
|
92
111
|
],
|
|
93
112
|
},
|
|
94
113
|
};
|
|
95
114
|
/** The channel kinds `fastagent add <kind>` can scaffold. */
|
|
96
115
|
export const CHANNEL_KINDS = Object.keys(CHANNEL_SCAFFOLDS);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
116
|
+
/** Local setup never changes the production app or ingress. */
|
|
117
|
+
const deploymentStep = (kind, pinned) => pinned
|
|
118
|
+
? `run \`{serve}\` (no tunnel needed: it connects by WebSocket); ${kind.toUpperCase()}_INGRESS=websocket is ` +
|
|
119
|
+
"set for this environment; production has its own app and ingress setting"
|
|
120
|
+
: "run `{serve}` (no tunnel needed: it connects by WebSocket); " +
|
|
121
|
+
"`deploy --run` creates a separate production app for webhook";
|
|
122
|
+
const websocketSetup = (kind, pinned) => {
|
|
123
|
+
const prefix = kind.toUpperCase();
|
|
124
|
+
return {
|
|
125
|
+
env: CHANNEL_SCAFFOLDS[kind].env.filter((entry) => [`${prefix}_APP_ID`, `${prefix}_APP_SECRET`, `${prefix}_INGRESS`].includes(entry.name)),
|
|
100
126
|
steps: [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
env: CHANNEL_SCAFFOLDS.lark.env.filter((entry) => ["LARK_APP_ID", "LARK_APP_SECRET"].includes(entry.name)),
|
|
110
|
-
steps: [
|
|
111
|
-
"before publishing: approve the sensitive im:message.group_msg permission for context-aware groups (add it manually if Lark's config API fallback was used); it delivers all group messages so bare replies in the agent's own threads can invoke and other unsummoned discussion can buffer. im:message:readonly is requested alongside it so a thread's opening ask can carry the message it quotes; both need the same approval",
|
|
112
|
-
"in Events & Callbacks choose long connection, subscribe im.message.receive_v1, then create + publish a version",
|
|
113
|
-
"edit {channel} — routing policy (the scaffold is already set to WebSocket ingress)",
|
|
114
|
-
"run `fastagent dev` without --tunnel; deployments must keep one process running (no scale-to-zero)",
|
|
115
|
-
"the agent can push messages from scheduled turns via the scaffolded {tools}/lark-send.ts tool",
|
|
127
|
+
kind === "feishu" ? FEISHU_PERMISSION_STEP : LARK_PERMISSION_STEP,
|
|
128
|
+
kind === "feishu"
|
|
129
|
+
? "PUBLISH the app version in the developer console after permission approval — long-connection event subscriptions become active with the published version"
|
|
130
|
+
: "in Events & Callbacks choose long connection, subscribe im.message.receive_v1, then create + publish a version",
|
|
131
|
+
"edit {channel} — routing policy",
|
|
132
|
+
deploymentStep(kind, pinned),
|
|
133
|
+
`the agent can push messages from scheduled turns via the scaffolded {tools}/${kind}-send.ts tool`,
|
|
134
|
+
`the agent reads the threads of the group it is asked in via the scaffolded {tools}/${kind}-threads.ts tool`,
|
|
116
135
|
],
|
|
117
|
-
}
|
|
136
|
+
};
|
|
118
137
|
};
|
|
119
138
|
/** The mode-specific env vars + next-step lines a scaffolded channel needs. */
|
|
120
|
-
export function channelSetup(kind, ingress = "webhook",
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
env: setup.env,
|
|
128
|
-
steps: setup.steps.map((step) => step.includes("im:message.group_msg")
|
|
129
|
-
? "group behavior: mention-only — do not grant im:message.group_msg; bare thread replies and group context buffering remain disabled. im:message:readonly is independent of this choice: add it if you want an @mention to carry the message it quotes (without it that quote degrades to a marker)"
|
|
130
|
-
: step),
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
if (kind === "slack" && behavior === "mentions") {
|
|
134
|
-
return {
|
|
135
|
-
env: setup.env,
|
|
136
|
-
steps: [
|
|
137
|
-
"Slack Bot Token Scopes: app_mentions:read, assistant:write, chat:write, im:history, files:read, files:write (no channel/group/mpim history scopes)",
|
|
138
|
-
"enable Agents (agent_view) and leave token rotation OFF (it cannot be turned off again); subscribe app_home_opened, app_context_changed, app_mention, and message.im; set Request URL to <public-url>/slack",
|
|
139
|
-
"group behavior: mention-only — bare thread replies and unsummoned group context remain disabled",
|
|
140
|
-
...setup.steps.slice(2),
|
|
141
|
-
],
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
return { env: setup.env, steps: setup.steps };
|
|
139
|
+
export function channelSetup(kind, ingress = "webhook",
|
|
140
|
+
/** Whether `<PREFIX>_INGRESS` is explicitly set in this environment. */
|
|
141
|
+
pinned = false) {
|
|
142
|
+
if (ingress !== "websocket" || (kind !== "feishu" && kind !== "lark"))
|
|
143
|
+
return CHANNEL_SCAFFOLDS[kind];
|
|
144
|
+
return websocketSetup(kind, pinned);
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
147
|
* Append a channel's env vars (commented placeholders + hints) to `.env.example`, so a developer who copies it to
|
|
148
|
-
* `.env` finds the vars already there.
|
|
148
|
+
* `.env` finds the vars already there. Every var the channel can declare, whichever way this machine receives it:
|
|
149
|
+
* the file is what a collaborator or CI fills for a deployment, which receives Feishu/Lark by webhook.
|
|
149
150
|
*/
|
|
150
|
-
export async function appendChannelEnv(dir, kind
|
|
151
|
+
export async function appendChannelEnv(dir, kind) {
|
|
151
152
|
const file = envExamplePath(dir);
|
|
152
153
|
let current;
|
|
153
154
|
try {
|
|
@@ -162,149 +163,50 @@ export async function appendChannelEnv(dir, kind, ingress = "webhook") {
|
|
|
162
163
|
if (current.includes(marker))
|
|
163
164
|
return false;
|
|
164
165
|
// Hint on its OWN line above the placeholder (like the base env.example template).
|
|
165
|
-
const block = `\n${marker}\n${
|
|
166
|
-
.env.map((e) => `# ${e.hint}\n# ${e.name}=`)
|
|
167
|
-
.join("\n")}\n`;
|
|
166
|
+
const block = `\n${marker}\n${CHANNEL_SCAFFOLDS[kind].env.map((e) => `# ${e.hint}\n# ${e.name}=`).join("\n")}\n`;
|
|
168
167
|
await appendFile(file, block);
|
|
169
168
|
return true;
|
|
170
169
|
}
|
|
171
|
-
/** Whether `.env` content carries a non-empty ACTIVE value for `name`. */
|
|
172
|
-
function hasActiveEnvValue(content, name) {
|
|
173
|
-
return (parseEnvContent(content).get(name)?.trim() ?? "") !== "";
|
|
174
|
-
}
|
|
175
|
-
function mentionsEnvName(content, name) {
|
|
176
|
-
return content.split("\n").some((line) => new RegExp(`^\\s*#?\\s*${name}\\s*=`).test(line));
|
|
177
|
-
}
|
|
178
170
|
/**
|
|
179
|
-
* Append generated channel secrets to the
|
|
180
|
-
* values are kept.
|
|
171
|
+
* Append generated channel secrets to the selected environment's value file, and a placeholder for each the channel
|
|
172
|
+
* reads that nothing generated. Existing non-empty values are kept.
|
|
181
173
|
*/
|
|
182
|
-
export
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
let unprotectedSecretsDir;
|
|
190
|
-
if (owned) {
|
|
191
|
-
// Only EEXIST is tolerable (already protected, or a concurrent writer).
|
|
192
|
-
await writeFile(join(secretsDir, ".gitignore"), baseTemplate("secrets.gitignore"), { flag: "wx" }).catch((e) => {
|
|
193
|
-
if (e.code !== "EEXIST")
|
|
194
|
-
throw e;
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
else if (!(await exists(join(secretsDir, ".gitignore")))) {
|
|
198
|
-
unprotectedSecretsDir = secretsDir;
|
|
199
|
-
}
|
|
200
|
-
let current = "";
|
|
201
|
-
try {
|
|
202
|
-
current = await readFile(file, "utf8");
|
|
203
|
-
}
|
|
204
|
-
catch (e) {
|
|
205
|
-
if (e.code !== "ENOENT")
|
|
206
|
-
throw e;
|
|
207
|
-
}
|
|
208
|
-
const env = channelSetup(kind, ingress).env;
|
|
209
|
-
const alreadySet = env
|
|
210
|
-
.filter((e) => !overwrite.includes(e.name) && hasActiveEnvValue(current, e.name))
|
|
211
|
-
.map((e) => e.name);
|
|
212
|
-
const lines = [];
|
|
213
|
-
const written = [];
|
|
214
|
-
const contentLines = current.split("\n");
|
|
215
|
-
let replacedInPlace = false;
|
|
216
|
-
for (const e of env) {
|
|
217
|
-
if (alreadySet.includes(e.name))
|
|
218
|
-
continue;
|
|
219
|
-
const value = generated[e.name];
|
|
220
|
-
if (value !== undefined) {
|
|
221
|
-
// An ACTIVE but EMPTY assignment already in the file (an uncommented, unfilled placeholder from `cp
|
|
222
|
-
// .env.example .env`) must be replaced IN PLACE.
|
|
223
|
-
let idx = -1;
|
|
224
|
-
for (let i = contentLines.length - 1; i >= 0; i--) {
|
|
225
|
-
if (parseEnvContent(contentLines[i]).has(e.name)) {
|
|
226
|
-
idx = i;
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (idx >= 0) {
|
|
231
|
-
contentLines[idx] = `${e.name}=${value}`;
|
|
232
|
-
replacedInPlace = true;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
lines.push(`${e.name}=${value}`);
|
|
236
|
-
}
|
|
237
|
-
written.push(e.name);
|
|
238
|
-
}
|
|
239
|
-
else if (!mentionsEnvName(current, e.name)) {
|
|
240
|
-
// Hint above, never inline after `=` — see appendChannelEnv (this IS the file loadDotEnv reads).
|
|
241
|
-
lines.push(`# ${e.hint}`, `# ${e.name}=`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
// 0600 on the file this call CREATES, and nothing at all about one it did not: a `.env` the operator made with
|
|
245
|
-
// `cp .secrets/.env.example .secrets/.env` carries the mode that command gave it, and re-deciding it would be
|
|
246
|
-
// fastagent changing the permissions of a file it does not own.
|
|
247
|
-
const secret = { mode: SECRET_FILE_MODE };
|
|
248
|
-
if (replacedInPlace) {
|
|
249
|
-
current = contentLines.join("\n");
|
|
250
|
-
await writeFile(file, current, secret);
|
|
251
|
-
}
|
|
252
|
-
if (lines.length > 0) {
|
|
253
|
-
const marker = `# --- ${kind} channel ---`;
|
|
254
|
-
if (current.includes(marker)) {
|
|
255
|
-
// A marker already present (e.g. a .env copied from .env.example) — slot the new lines under it instead of
|
|
256
|
-
// orphaning them at the end of the file.
|
|
257
|
-
await writeFile(file, current.replace(marker, `${marker}\n${lines.join("\n")}`), secret);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
const prefix = current === "" ? "" : current.endsWith("\n") ? "\n" : "\n\n";
|
|
261
|
-
await appendFile(file, `${prefix}${marker}\n${lines.join("\n")}\n`, secret);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return { written, alreadySet, unprotectedSecretsDir };
|
|
174
|
+
export function appendChannelDotEnv(dir, kind, generated, overwrite = [], ingress = "webhook") {
|
|
175
|
+
const entries = channelSetup(kind, ingress).env.map(({ name, hint }) => ({
|
|
176
|
+
name,
|
|
177
|
+
hint,
|
|
178
|
+
...(generated[name] === undefined ? {} : { value: generated[name] }),
|
|
179
|
+
}));
|
|
180
|
+
return writeValues(dir, `${kind} channel`, entries, overwrite);
|
|
265
181
|
}
|
|
266
182
|
/** The path `add <kind>` scaffolds to. */
|
|
267
183
|
function channelPath(dir, kind) {
|
|
268
|
-
return join(dir, "channels", `${kind}.ts`);
|
|
184
|
+
return join(dir, "src", "channels", `${kind}.ts`);
|
|
269
185
|
}
|
|
270
186
|
/** Whether a channel file already exists — checked before any mutation, so a no-clobber re-add is side-effect-free. */
|
|
271
187
|
export async function channelExists(dir, kind) {
|
|
272
188
|
return exists(channelPath(dir, kind));
|
|
273
189
|
}
|
|
274
|
-
/** Scaffold `channels/<kind>.ts` into {@link dir}. */
|
|
275
|
-
export async function scaffoldChannel(dir, kind
|
|
276
|
-
const channelsDir = join(dir, "channels");
|
|
277
|
-
// Don't write through a
|
|
278
|
-
await assertInsideAgentDir(dir, "
|
|
190
|
+
/** Scaffold `src/channels/<kind>.ts` into {@link dir}. */
|
|
191
|
+
export async function scaffoldChannel(dir, kind) {
|
|
192
|
+
const channelsDir = join(dir, "src", "channels");
|
|
193
|
+
// Don't write through a src/ symlink that escapes the agent dir; one inside it is fine.
|
|
194
|
+
await assertInsideAgentDir(dir, "src");
|
|
279
195
|
const file = channelPath(dir, kind);
|
|
280
196
|
if (await exists(file)) {
|
|
281
197
|
throw new Error(`${file} already exists — edit it, or remove it to re-scaffold`);
|
|
282
198
|
}
|
|
283
199
|
await mkdir(channelsDir, { recursive: true });
|
|
284
|
-
|
|
285
|
-
// has to be re-taught every time the template changes shape, and a missed anchor scaffolds a channel
|
|
286
|
-
// configured for the wrong ingress with nothing failing.
|
|
287
|
-
// Which templates exist is the BUNDLE's fact, asked here rather than assumed from the kind: only
|
|
288
|
-
// feishu/lark ship a long-connection variant today, and a caller asking for one that is missing
|
|
289
|
-
// must get that sentence, not the ENOENT of a path it never named.
|
|
290
|
-
const template = options.ingress === "websocket" ? "channel.websocket.ts" : "channel.ts";
|
|
291
|
-
// CHANNEL templates only: the bundle also holds companion tools (`telegram-send.ts`), and listing
|
|
292
|
-
// one as an available scaffold sends whoever reads this line looking for an ingress that is a tool.
|
|
293
|
-
const available = channelBundleFiles(kind).filter((f) => f.startsWith("channel."));
|
|
294
|
-
if (!available.includes(template)) {
|
|
295
|
-
throw new Error(`${kind} has no ${options.ingress ?? "webhook"} scaffold (${template} is not in its bundle) — ` +
|
|
296
|
-
`available: ${available.join(", ")}`);
|
|
297
|
-
}
|
|
298
|
-
await writeFile(file, channelTemplate(kind, template), { flag: "wx" });
|
|
200
|
+
await writeFile(file, channelTemplate(kind, "channel.ts"), { flag: "wx" });
|
|
299
201
|
return file;
|
|
300
202
|
}
|
|
301
|
-
/** The bundle's companion tools (every `.ts` beside `channel.ts` → `tools/<name>`). */
|
|
203
|
+
/** The bundle's companion tools (every `.ts` beside `channel.ts` → `src/tools/<name>`). */
|
|
302
204
|
export async function scaffoldCompanionTools(dir, kind) {
|
|
303
205
|
const written = [];
|
|
304
206
|
for (const name of channelBundleFiles(kind)) {
|
|
305
|
-
if (name
|
|
306
|
-
continue; //
|
|
307
|
-
const file = join(dir, "tools", name);
|
|
207
|
+
if (name === "channel.ts")
|
|
208
|
+
continue; // the channel, not a tool
|
|
209
|
+
const file = join(dir, "src", "tools", name);
|
|
308
210
|
await mkdir(dirname(file), { recursive: true });
|
|
309
211
|
await writeFile(file, channelTemplate(kind, name));
|
|
310
212
|
written.push(file);
|
|
@@ -326,7 +228,7 @@ export async function assertChannelReady(dir) {
|
|
|
326
228
|
// `dir` is the AGENT dir, so `fastagent init` here would nest a second agent inside it.
|
|
327
229
|
throw new Error(`${dir}: no package.json — a channel adapter is code and needs the agent's own manifest. ` +
|
|
328
230
|
`Add a package.json declaring @fastagent-sh/fastagent there (\`init\` scaffolds one), ` +
|
|
329
|
-
`or
|
|
231
|
+
`or create a fresh agent with \`fastagent init <dir>\``);
|
|
330
232
|
}
|
|
331
233
|
throw e;
|
|
332
234
|
}
|
package/dist/scaffold/init.d.ts
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
/** The agent directory name for a raw `--agent-dir` value: the default when unset, and `./bot` read as `bot`. */
|
|
2
|
-
export declare function agentDirName(raw: string | undefined): string;
|
|
3
|
-
/**
|
|
4
|
-
* Why `name` cannot be an agent directory name, or undefined when it can.
|
|
5
|
-
*
|
|
6
|
-
* `"."` is refused along with every path: the definition always lands in a SUBDIRECTORY, and the directory around it
|
|
7
|
-
* is the workspace. The subdirectory also carries its own `package.json`, so a `fastagent.config.ts` never loads under
|
|
8
|
-
* the host repo's module settings.
|
|
9
|
-
*/
|
|
10
|
-
export declare function agentDirNameError(name: string): string | undefined;
|
|
11
|
-
export interface ScaffoldOptions {
|
|
12
|
-
/** The agent directory's name inside `dir` — default {@link DEFAULT_AGENT_DIRNAME}. */
|
|
13
|
-
agentDir?: string;
|
|
14
|
-
}
|
|
15
1
|
export interface ScaffoldResult {
|
|
2
|
+
/** The agent directory: `dir` itself. */
|
|
16
3
|
dir: string;
|
|
17
|
-
/** The agent dir relative to `dir`: the {@link ScaffoldOptions.agentDir} that was used. */
|
|
18
|
-
agentDir: string;
|
|
19
4
|
/** Files written by this run (relative to `dir`). */
|
|
20
5
|
created: string[];
|
|
6
|
+
/** Remove what this run created, for a caller whose next step failed: the same rollback a failed write runs. */
|
|
7
|
+
undo(): Promise<void>;
|
|
21
8
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Scaffold a runnable agent INTO `dir`, which must be new or empty. An agent is a directory of its own: what it works
|
|
11
|
+
* on is declared, never the directory around it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function scaffoldAgent(dir: string): Promise<ScaffoldResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Make the new agent a git repository whose first commit is what creating it wrote (`createAgent`, which `init` and a
|
|
16
|
+
* client both run). An agent changes itself, and version control is how its author goes back to a version that worked;
|
|
17
|
+
* the scaffolded `.gitignore` already keeps its instance (`.state`, `.secrets`, `content`) out. Returns a sentence to
|
|
18
|
+
* show: when there is no repository of its own or no first commit, it says which and why. The catches are this
|
|
19
|
+
* step's boundary: the agent is created either way, so a git that is missing or refuses becomes that sentence rather
|
|
20
|
+
* than a failed create.
|
|
21
|
+
*/
|
|
22
|
+
export declare function initRepository(dir: string): Promise<string>;
|
package/dist/scaffold/init.js
CHANGED
|
@@ -1,117 +1,78 @@
|
|
|
1
|
-
/** Init: scaffold a runnable fastagent agent, offline. */
|
|
1
|
+
/** Init: scaffold a runnable fastagent agent, offline, and make it a git repository of its own. */
|
|
2
2
|
import { lstat, mkdir, readdir, rm, rmdir, writeFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
4
|
-
import { AGENT_CONFIG_FILE,
|
|
3
|
+
import { dirname, join, sep } from "node:path";
|
|
4
|
+
import { AGENT_CONFIG_FILE, SECRETS_DIRNAME, displayPath, enclosingAgentDir } from "../paths.js";
|
|
5
5
|
import { baseTemplate, packageJson, toPackageName } from "./templates.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return DEFAULT_AGENT_DIRNAME;
|
|
11
|
-
const trimmed = raw.replace(/^\.[/\\]/, "");
|
|
12
|
-
return trimmed === "" ? raw : trimmed;
|
|
13
|
-
}
|
|
6
|
+
import { fastagentBuild } from "../version.js";
|
|
7
|
+
import { GitNotInstalled, gitFor } from "../git.js";
|
|
8
|
+
/** Entries a directory may hold and still count as empty. */
|
|
9
|
+
const IGNORABLE = [".DS_Store", ".gitkeep", ".keep"];
|
|
14
10
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `"."` is refused along with every path: the definition always lands in a SUBDIRECTORY, and the directory around it
|
|
18
|
-
* is the workspace. The subdirectory also carries its own `package.json`, so a `fastagent.config.ts` never loads under
|
|
19
|
-
* the host repo's module settings.
|
|
11
|
+
* Scaffold a runnable agent INTO `dir`, which must be new or empty. An agent is a directory of its own: what it works
|
|
12
|
+
* on is declared, never the directory around it.
|
|
20
13
|
*/
|
|
21
|
-
export function
|
|
22
|
-
if (name !== "" && name !== "." && name !== ".." && name === basename(name))
|
|
23
|
-
return undefined;
|
|
24
|
-
return (`must be a single directory name — the definition lives in a subdirectory, and the directory around it is ` +
|
|
25
|
-
`the workspace the agent works on (a path, or ".", would put it elsewhere)`);
|
|
26
|
-
}
|
|
27
|
-
/** Scaffold a runnable agent into `<dir>/<agentDir>/` (both created if missing). */
|
|
28
|
-
export async function scaffoldAgent(dir, options = {}) {
|
|
29
|
-
const root = agentDirName(options.agentDir);
|
|
30
|
-
const invalid = agentDirNameError(root);
|
|
31
|
-
if (invalid)
|
|
32
|
-
throw new Error(`agentDir "${root}" ${invalid}`);
|
|
14
|
+
export async function scaffoldAgent(dir) {
|
|
33
15
|
const skill = (name) => ({
|
|
34
|
-
rel: join(
|
|
16
|
+
rel: join("skills", "writing-great-skills", name),
|
|
35
17
|
content: baseTemplate(`skills/writing-great-skills/${name}`),
|
|
36
18
|
});
|
|
37
19
|
const files = [
|
|
38
|
-
// Standing instructions
|
|
39
|
-
{ rel:
|
|
20
|
+
// Standing instructions: the agent's system prompt.
|
|
21
|
+
{ rel: "SYSTEM.md", content: baseTemplate("SYSTEM.md") },
|
|
40
22
|
// The example skill: how to author skills well — the core of self-iteration.
|
|
41
23
|
skill("SKILL.md"),
|
|
42
24
|
skill("GLOSSARY.md"),
|
|
43
25
|
skill("LICENSE"),
|
|
44
|
-
{ rel:
|
|
26
|
+
{ rel: AGENT_CONFIG_FILE, content: baseTemplate(AGENT_CONFIG_FILE) },
|
|
45
27
|
// Two ignore files, scaffolded ONCE and owned by the author from then on — no command rewrites, reads or verifies
|
|
46
28
|
// them.
|
|
47
|
-
{ rel:
|
|
48
|
-
{ rel: join(
|
|
49
|
-
{ rel: join(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// subdirectory of its own: a config file under a host repo's CommonJS manifest does not load.
|
|
53
|
-
{
|
|
54
|
-
rel: join(root, "package.json"),
|
|
55
|
-
content: packageJson(`${toPackageName(dir)}-agent`, await fastagentVersion()),
|
|
56
|
-
},
|
|
29
|
+
{ rel: ".gitignore", content: baseTemplate("gitignore") },
|
|
30
|
+
{ rel: join(SECRETS_DIRNAME, ".gitignore"), content: baseTemplate("secrets.gitignore") },
|
|
31
|
+
{ rel: join(SECRETS_DIRNAME, ".env.example"), content: baseTemplate("env.example") },
|
|
32
|
+
// The agent's own manifest, named after its directory.
|
|
33
|
+
{ rel: "package.json", content: packageJson(toPackageName(dir), await fastagentBuild()) },
|
|
57
34
|
];
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
|
|
35
|
+
const shown = displayPath(process.cwd(), dir) ?? dir;
|
|
36
|
+
// Inside another agent: that agent's directory is its own definition and working directory, so the new one would be
|
|
37
|
+
// read as part of it.
|
|
38
|
+
const owner = enclosingAgentDir(dir);
|
|
61
39
|
if (owner) {
|
|
62
|
-
throw new Error(`"${
|
|
63
|
-
`part of THAT agent's surface, not one of its own. Init outside it.`);
|
|
64
|
-
}
|
|
65
|
-
// Preflight scaffold parent dirs FIRST: a pre-existing non-directory there would make mkdir fail mid-loop AFTER the
|
|
66
|
-
// first write, leaving a half-scaffold.
|
|
67
|
-
const parents = new Set();
|
|
68
|
-
for (const file of files) {
|
|
69
|
-
let p = dirname(file.rel);
|
|
70
|
-
while (p !== "." && p !== "") {
|
|
71
|
-
parents.add(p);
|
|
72
|
-
p = dirname(p);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
for (const rel of parents) {
|
|
76
|
-
const st = await lstat(join(dir, rel)).catch(() => undefined);
|
|
77
|
-
if (st && !st.isDirectory()) {
|
|
78
|
-
throw new Error(`cannot scaffold: "${rel}" exists and is not a directory (a regular file or symlink) — remove it, or init elsewhere`);
|
|
79
|
-
}
|
|
40
|
+
throw new Error(`"${shown}" is inside the agent ${owner} — an agent is a directory of its own; create it outside that one`);
|
|
80
41
|
}
|
|
81
|
-
|
|
82
|
-
const occupants = (await readdir(join(dir, root)).catch((e) => {
|
|
42
|
+
const st = await lstat(dir).catch((e) => {
|
|
83
43
|
if (e.code === "ENOENT")
|
|
84
|
-
return
|
|
44
|
+
return undefined;
|
|
85
45
|
throw e;
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
if (shadowed.length > 0) {
|
|
91
|
-
throw new Error(`"${dir}" already resolves to ${shadowed.map((a) => displayPath(process.cwd(), a) ?? a).join(", ")} — ` +
|
|
92
|
-
`an agent scaffolded in ./${root}/ would be hidden by it and never served ` +
|
|
93
|
-
`from "${dir}" (an agent AT a directory wins over any inside it). Use that agent, move it away, ` +
|
|
94
|
-
`or init in a different directory.`);
|
|
95
|
-
}
|
|
96
|
-
// ONE coordinate system for both refusals — `displayPath` is the shared policy (relative inside the cwd, absolute
|
|
97
|
-
// when it climbs out).
|
|
98
|
-
const target = displayPath(process.cwd(), join(dir, root)) ?? join(dir, root);
|
|
46
|
+
});
|
|
47
|
+
if (st && !st.isDirectory())
|
|
48
|
+
throw new Error(`"${shown}" exists and is not a directory`);
|
|
49
|
+
const occupants = st ? (await readdir(dir)).filter((name) => !IGNORABLE.includes(name)) : [];
|
|
99
50
|
if (occupants.includes(AGENT_CONFIG_FILE)) {
|
|
100
|
-
throw new Error(`"${
|
|
51
|
+
throw new Error(`"${shown}" already has ${AGENT_CONFIG_FILE} — already a fastagent agent`);
|
|
101
52
|
}
|
|
102
53
|
if (occupants.length > 0) {
|
|
103
|
-
throw new Error(`"${
|
|
104
|
-
|
|
54
|
+
throw new Error(`"${shown}" is not empty (it holds ${occupants.join(", ")}) — an agent lives in a directory of its own; to ` +
|
|
55
|
+
`have one work on this directory: \`fastagent init <new directory> --content ${displayPath(process.cwd(), dir) ?? "."}\``);
|
|
56
|
+
}
|
|
57
|
+
const parents = new Set();
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
for (let p = dirname(file.rel); p !== "." && p !== ""; p = dirname(p))
|
|
60
|
+
parents.add(p);
|
|
105
61
|
}
|
|
106
|
-
|
|
107
|
-
const preexisting = new Set();
|
|
108
|
-
for (const rel of parents)
|
|
109
|
-
if (await exists(join(dir, rel)))
|
|
110
|
-
preexisting.add(rel);
|
|
111
|
-
const agentDirExisted = await exists(join(dir, root));
|
|
62
|
+
const dirExisted = st !== undefined;
|
|
112
63
|
await mkdir(dir, { recursive: true });
|
|
113
64
|
const created = [];
|
|
114
|
-
// ONE rollback
|
|
65
|
+
// ONE rollback: it removes what THIS run created — files AND the directories it made for them — and leaves a
|
|
66
|
+
// directory the caller handed over empty. Best-effort, because it runs while another failure is being reported.
|
|
67
|
+
const undo = async () => {
|
|
68
|
+
for (const rel of [...created].reverse())
|
|
69
|
+
await rm(join(dir, rel), { force: true }).catch(() => { });
|
|
70
|
+
for (const rel of [...parents].sort((a, b) => b.split(sep).length - a.split(sep).length)) {
|
|
71
|
+
await rmdir(join(dir, rel)).catch(() => { });
|
|
72
|
+
}
|
|
73
|
+
if (!dirExisted)
|
|
74
|
+
await rmdir(dir).catch(() => { });
|
|
75
|
+
};
|
|
115
76
|
// `wx` so a collision is a failure: the target was checked empty above, so anything here appeared mid-run.
|
|
116
77
|
try {
|
|
117
78
|
for (const file of files) {
|
|
@@ -122,16 +83,43 @@ export async function scaffoldAgent(dir, options = {}) {
|
|
|
122
83
|
}
|
|
123
84
|
}
|
|
124
85
|
catch (error) {
|
|
125
|
-
|
|
126
|
-
for (const rel of created.reverse())
|
|
127
|
-
await rm(join(dir, rel), { force: true }).catch(() => { });
|
|
128
|
-
for (const rel of [...parents].sort((a, b) => b.split(sep).length - a.split(sep).length)) {
|
|
129
|
-
if (rel !== root && !preexisting.has(rel))
|
|
130
|
-
await rmdir(join(dir, rel)).catch(() => { });
|
|
131
|
-
}
|
|
132
|
-
if (!agentDirExisted)
|
|
133
|
-
await rmdir(join(dir, root)).catch(() => { });
|
|
86
|
+
await undo();
|
|
134
87
|
throw error;
|
|
135
88
|
}
|
|
136
|
-
return { dir,
|
|
89
|
+
return { dir, created, undo };
|
|
90
|
+
}
|
|
91
|
+
const git = gitFor("no repository was created");
|
|
92
|
+
/**
|
|
93
|
+
* Make the new agent a git repository whose first commit is what creating it wrote (`createAgent`, which `init` and a
|
|
94
|
+
* client both run). An agent changes itself, and version control is how its author goes back to a version that worked;
|
|
95
|
+
* the scaffolded `.gitignore` already keeps its instance (`.state`, `.secrets`, `content`) out. Returns a sentence to
|
|
96
|
+
* show: when there is no repository of its own or no first commit, it says which and why. The catches are this
|
|
97
|
+
* step's boundary: the agent is created either way, so a git that is missing or refuses becomes that sentence rather
|
|
98
|
+
* than a failed create.
|
|
99
|
+
*/
|
|
100
|
+
export async function initRepository(dir) {
|
|
101
|
+
const reason = (error) => error.message.split("\n")[0];
|
|
102
|
+
try {
|
|
103
|
+
// A directory another repository TRACKS is that repository's: a second one inside it would hide the agent's files
|
|
104
|
+
// from the first. One it ignores (a home directory kept in git with `*` ignored) tracks nothing, so the agent gets
|
|
105
|
+
// its own.
|
|
106
|
+
const enclosing = git.answer(["rev-parse", "--show-toplevel"], dir);
|
|
107
|
+
if (enclosing !== undefined && git.answer(["check-ignore", "-q", "."], dir) === undefined) {
|
|
108
|
+
return `inside the git repository ${enclosing}: it tracks the agent, so none was created`;
|
|
109
|
+
}
|
|
110
|
+
await git.run(["init", "--quiet"], dir);
|
|
111
|
+
await git.run(["add", "--all"], dir);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error instanceof GitNotInstalled)
|
|
115
|
+
return error.message;
|
|
116
|
+
return `git could not create a repository: ${reason(error)}`;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
await git.run(["commit", "--quiet", "--message", "Create the agent with fastagent"], dir);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return `created a git repository, but its first commit failed (${reason(error)}): commit the scaffold yourself`;
|
|
123
|
+
}
|
|
124
|
+
return "created a git repository, with the scaffold as its first commit";
|
|
137
125
|
}
|