@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,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The model lists a pi-ai release does not carry yet. pi publishes the catalog of every built-in provider at pi.dev;
|
|
3
|
+
* when it is newer than the data this pi-ai was built with, each built-in provider lists its models too, so a model a
|
|
4
|
+
* provider releases can be named without a FastAgent release. A provider that fetches its own list (radius, a provider
|
|
5
|
+
* in code with `fetchModels`) is refreshed by pi-ai, with the credential that needs. Both are kept in the instance's
|
|
6
|
+
* state, so a start without the network, or whose refresh fails, has the lists it last had.
|
|
7
|
+
*
|
|
8
|
+
* Adapted from pi-coding-agent's `remote-catalog-provider` (MIT). Two differences: one request answers for every
|
|
9
|
+
* provider, and the published catalog is fetched whatever this machine authenticates (pi-ai's refresh asks the network
|
|
10
|
+
* only for a provider holding a credential), because a deployed box opens its agent, default model checked, before
|
|
11
|
+
* anyone logs in on it.
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
14
|
+
import { dirname, join } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { InMemoryModelsStore, getModelType, isModelType, } from "@earendil-works/pi-ai";
|
|
17
|
+
import { getBuiltinModelDataGeneratedAt } from "@earendil-works/pi-ai/providers/all";
|
|
18
|
+
import { writeFileAtomic } from "../../atomic-write.js";
|
|
19
|
+
const PUBLISHED_URL = "https://pi.dev/api/models";
|
|
20
|
+
/** The model types this pi-ai takes; asked for, since pi.dev answers a client that names none with chat models only. */
|
|
21
|
+
const MODEL_TYPES = ["chat", "image", "classifier"];
|
|
22
|
+
/** How long a fetched catalog counts as current, so a start inside it asks the network nothing. pi's own. */
|
|
23
|
+
const FRESH_MS = 4 * 60 * 60 * 1000;
|
|
24
|
+
const TIMEOUT_MS = 5_000;
|
|
25
|
+
/** `1`: no model list is fetched, and each is the one last kept. */
|
|
26
|
+
const OFFLINE_VARIABLE = "FASTAGENT_OFFLINE";
|
|
27
|
+
/** The catalogs kept in `dir`, or, without one, for this process alone. `url` and `timeoutMs` are for tests. */
|
|
28
|
+
export function catalogsIn(dir, options = {}) {
|
|
29
|
+
const builtAt = getBuiltinModelDataGeneratedAt();
|
|
30
|
+
const timeoutMs = options.timeoutMs ?? TIMEOUT_MS;
|
|
31
|
+
const fetched = dir === undefined ? new InMemoryModelsStore() : providerFiles(join(dir, "providers"));
|
|
32
|
+
const publishedPath = dir === undefined ? undefined : join(dir, "published.json");
|
|
33
|
+
let inMemory;
|
|
34
|
+
let listed = {};
|
|
35
|
+
const show = (kept) => {
|
|
36
|
+
listed = kept && (builtAt === undefined || kept.lastModified > builtAt) ? kept.models : {};
|
|
37
|
+
};
|
|
38
|
+
const keep = (published) => {
|
|
39
|
+
if (publishedPath === undefined)
|
|
40
|
+
inMemory = published;
|
|
41
|
+
// Shared writers: a serve and an `invoke` may refresh at once, and either whole catalog is a good one.
|
|
42
|
+
else
|
|
43
|
+
writeFileAtomic(publishedPath, JSON.stringify(published), undefined, true);
|
|
44
|
+
};
|
|
45
|
+
async function refreshPublished(network, force, signal) {
|
|
46
|
+
const piAi = piAiVersion();
|
|
47
|
+
const found = publishedPath === undefined ? inMemory : readJson(publishedPath);
|
|
48
|
+
const kept = found?.piAi === piAi ? found : undefined;
|
|
49
|
+
show(kept);
|
|
50
|
+
if (!network || (!force && kept && Date.now() - kept.checkedAt < FRESH_MS))
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
await askPublished(piAi, kept, signal);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// A failed ask counts as one: the catalog kept stays, and is not asked for again for FRESH_MS. With none kept,
|
|
57
|
+
// the next open asks, since a default model may need it.
|
|
58
|
+
if (kept)
|
|
59
|
+
keep({ ...kept, checkedAt: Date.now() });
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function askPublished(piAi, kept, signal) {
|
|
64
|
+
const url = new URL(options.url ?? PUBLISHED_URL);
|
|
65
|
+
url.searchParams.set("types", MODEL_TYPES.join(","));
|
|
66
|
+
url.searchParams.set("pi-version", piAi);
|
|
67
|
+
const validator = kept?.etag;
|
|
68
|
+
const response = await fetch(url, {
|
|
69
|
+
headers: { accept: "application/json", ...(validator === undefined ? {} : { "if-none-match": validator }) },
|
|
70
|
+
signal,
|
|
71
|
+
});
|
|
72
|
+
if (response.status === 304 && kept) {
|
|
73
|
+
keep({ ...kept, checkedAt: Date.now() });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (response.status === 404 || response.status === 501) {
|
|
77
|
+
// pi.dev publishes no catalog for this pi-ai, so its own lists are the current ones.
|
|
78
|
+
const none = { piAi, models: {}, lastModified: 0, checkedAt: Date.now() };
|
|
79
|
+
keep(none);
|
|
80
|
+
show(none);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!response.ok)
|
|
84
|
+
throw new Error(`${url.origin} answered ${response.status}`);
|
|
85
|
+
const needs = response.headers.get("x-pi-model-catalog-minimum-version");
|
|
86
|
+
if (needs !== null && olderThan(piAi, needs)) {
|
|
87
|
+
throw new Error(`${url.origin} answered a catalog for pi-ai ${needs} or later, and this is ${piAi}`);
|
|
88
|
+
}
|
|
89
|
+
const lastModified = Date.parse(response.headers.get("last-modified") ?? "");
|
|
90
|
+
const etag = response.headers.get("etag");
|
|
91
|
+
const published = {
|
|
92
|
+
piAi,
|
|
93
|
+
models: parseCatalog(await response.json()),
|
|
94
|
+
lastModified: Number.isNaN(lastModified) ? 0 : lastModified,
|
|
95
|
+
...(etag === null ? {} : { etag }),
|
|
96
|
+
checkedAt: Date.now(),
|
|
97
|
+
};
|
|
98
|
+
keep(published);
|
|
99
|
+
show(published);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
over(provider) {
|
|
103
|
+
if (provider.refreshModels !== undefined)
|
|
104
|
+
return provider;
|
|
105
|
+
const added = () => listed[provider.id] ?? [];
|
|
106
|
+
return {
|
|
107
|
+
...provider,
|
|
108
|
+
getModels: () => merged(provider.getModels(), added().filter((model) => isModelType(model, "chat"))),
|
|
109
|
+
getAllModels: () => merged(provider.getAllModels?.() ?? provider.getModels(), added()),
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
fetched,
|
|
113
|
+
async refresh(models, { force = false } = {}) {
|
|
114
|
+
const offline = process.env[OFFLINE_VARIABLE] === "1";
|
|
115
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
116
|
+
const why = (error) => signal.aborted ? `no answer within ${timeoutMs / 1000}s` : messageOf(error);
|
|
117
|
+
const [published, own] = await Promise.all([
|
|
118
|
+
refreshPublished(!offline, force, signal).then(() => undefined, (error) => `pi's published catalog: ${why(error)}`),
|
|
119
|
+
refreshOwn(models, !offline, force, signal),
|
|
120
|
+
]);
|
|
121
|
+
const problems = [
|
|
122
|
+
...(offline && force ? [`${OFFLINE_VARIABLE}=1 is set`] : []),
|
|
123
|
+
...(published === undefined ? [] : [published]),
|
|
124
|
+
...[...own.errors].map(([provider, error]) => `${provider}: ${messageOf(error)}`),
|
|
125
|
+
...(own.aborted ? [`the providers that fetch their own: ${why(undefined)}`] : []),
|
|
126
|
+
];
|
|
127
|
+
if (problems.length === 0)
|
|
128
|
+
return undefined;
|
|
129
|
+
const skip = offline ? "" : `; ${OFFLINE_VARIABLE}=1 skips the refresh`;
|
|
130
|
+
return `model lists not refreshed (${problems.join("; ")}): each lists what it last had${skip}`;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* The providers that fetch their own list, each asked only once what it last fetched is {@link FRESH_MS} old: pi-ai
|
|
135
|
+
* asks every one of them each refresh it allows the network, and keeps when it last did beside the list.
|
|
136
|
+
*/
|
|
137
|
+
async function refreshOwn(models, network, force, signal) {
|
|
138
|
+
// An entry it cannot read counts as none: pi-ai's refresh reads it again, and reports why it could not.
|
|
139
|
+
const keptOf = (id) => fetched.read(id).then((entry) => entry, () => undefined);
|
|
140
|
+
const own = models.getProviders().filter((provider) => provider.refreshModels !== undefined);
|
|
141
|
+
const stale = new Map();
|
|
142
|
+
const fresh = [];
|
|
143
|
+
for (const { id } of own) {
|
|
144
|
+
const kept = await keptOf(id);
|
|
145
|
+
const checkedAt = kept?.checkedAt;
|
|
146
|
+
if (force || checkedAt === undefined || Date.now() - checkedAt >= FRESH_MS)
|
|
147
|
+
stale.set(id, kept);
|
|
148
|
+
else
|
|
149
|
+
fresh.push(id);
|
|
150
|
+
}
|
|
151
|
+
const [restored, asked] = await Promise.all([
|
|
152
|
+
models.refresh({ allowNetwork: false, providers: fresh, signal }),
|
|
153
|
+
models.refresh({ allowNetwork: network, providers: [...stale.keys()], force, signal }),
|
|
154
|
+
]);
|
|
155
|
+
// A failed ask counts as one, as for the published catalog: pi-ai replaces an entry only when it fetched a list, so
|
|
156
|
+
// one it left as it was is a list asked for and kept, not asked for again for FRESH_MS.
|
|
157
|
+
const unwritten = new Map();
|
|
158
|
+
for (const [id, kept] of network ? stale : []) {
|
|
159
|
+
if (kept === undefined || (await keptOf(id))?.checkedAt !== kept.checkedAt)
|
|
160
|
+
continue;
|
|
161
|
+
await fetched
|
|
162
|
+
.write(id, { ...kept, checkedAt: Date.now() })
|
|
163
|
+
.catch((error) => void unwritten.set(id, error));
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
aborted: restored.aborted || asked.aborted,
|
|
167
|
+
errors: new Map([...restored.errors, ...asked.errors, ...unwritten]),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** The pi-ai this process runs, from its own package.json, which its exports do not reach. */
|
|
172
|
+
function piAiVersion() {
|
|
173
|
+
let dir = dirname(fileURLToPath(import.meta.resolve("@earendil-works/pi-ai")));
|
|
174
|
+
for (;;) {
|
|
175
|
+
const pkg = readJson(join(dir, "package.json"));
|
|
176
|
+
if (pkg?.name === "@earendil-works/pi-ai" && typeof pkg.version === "string")
|
|
177
|
+
return pkg.version;
|
|
178
|
+
const parent = dirname(dir);
|
|
179
|
+
if (parent === dir)
|
|
180
|
+
throw new Error("no package.json of @earendil-works/pi-ai above its entry");
|
|
181
|
+
dir = parent;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Whether release `version` is older than `than`, by `major.minor.patch`. */
|
|
185
|
+
function olderThan(version, than) {
|
|
186
|
+
const parts = (release) => {
|
|
187
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(release);
|
|
188
|
+
if (match === null)
|
|
189
|
+
throw new Error(`"${release}" is not a version`);
|
|
190
|
+
return match.slice(1).map(Number);
|
|
191
|
+
};
|
|
192
|
+
const [ours, theirs] = [parts(version), parts(than)];
|
|
193
|
+
for (const [index, part] of ours.entries()) {
|
|
194
|
+
const other = theirs[index];
|
|
195
|
+
if (part !== other)
|
|
196
|
+
return part < other;
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
/** Published models override built-in ones of the same type and id, as in pi. */
|
|
201
|
+
function merged(builtIn, published) {
|
|
202
|
+
const byKey = new Map();
|
|
203
|
+
for (const model of [...builtIn, ...published])
|
|
204
|
+
byKey.set(`${getModelType(model)}\0${model.id}`, model);
|
|
205
|
+
return [...byKey.values()];
|
|
206
|
+
}
|
|
207
|
+
/** pi.dev's answer: each provider's models, as a list (with `types`) or keyed by id (without). */
|
|
208
|
+
function parseCatalog(value) {
|
|
209
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
210
|
+
throw new Error("the answer is not a model catalog");
|
|
211
|
+
}
|
|
212
|
+
const catalog = {};
|
|
213
|
+
for (const [provider, models] of Object.entries(value)) {
|
|
214
|
+
const entries = Array.isArray(models) ? models : typeof models === "object" ? Object.values(models) : [];
|
|
215
|
+
catalog[provider] = entries
|
|
216
|
+
.filter((entry) => typeof entry === "object" &&
|
|
217
|
+
entry !== null &&
|
|
218
|
+
typeof entry.id === "string" &&
|
|
219
|
+
[undefined, ...MODEL_TYPES].includes(entry.type))
|
|
220
|
+
.map((model) => ({ ...model, provider }));
|
|
221
|
+
}
|
|
222
|
+
return catalog;
|
|
223
|
+
}
|
|
224
|
+
/** One file per provider: each is written by its own refresh, so none waits on another's lock. */
|
|
225
|
+
function providerFiles(dir) {
|
|
226
|
+
const path = (providerId) => join(dir, `${encodeURIComponent(providerId)}.json`);
|
|
227
|
+
return {
|
|
228
|
+
read: async (providerId) => readJson(path(providerId)),
|
|
229
|
+
write: async (providerId, entry) => writeFileAtomic(path(providerId), JSON.stringify(entry), undefined, true),
|
|
230
|
+
delete: async (providerId) => rmSync(path(providerId), { force: true }),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function readJson(path) {
|
|
234
|
+
let text;
|
|
235
|
+
try {
|
|
236
|
+
text = readFileSync(path, "utf8");
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
if (error.code === "ENOENT")
|
|
240
|
+
return undefined;
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
return JSON.parse(text);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
throw new Error(`${path} is not JSON: delete it, and the next refresh fetches it again`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/** pi-ai and `fetch` put the reason in the cause ("fetch failed", "Model refresh failed for radius"). */
|
|
251
|
+
function messageOf(error) {
|
|
252
|
+
const cause = error.cause instanceof Error ? `: ${error.cause.message}` : "";
|
|
253
|
+
return `${error.message}${cause}`;
|
|
254
|
+
}
|
|
@@ -11,8 +11,8 @@ export interface FastagentAuthOptions {
|
|
|
11
11
|
/**
|
|
12
12
|
* Where a caller's model credentials live: fastagent's JSON file (`authPath`, or the default layers when neither is
|
|
13
13
|
* given), or a {@link CredentialStore} the caller owns (an OS keychain, Electron `safeStorage`). A supplied store
|
|
14
|
-
* replaces the FILES: none is read or written, and no global layer applies. Env variables and
|
|
15
|
-
* apply, below a stored credential, exactly as with `authPath`. The store owns what the file store does
|
|
14
|
+
* replaces the FILES: none is read or written, and no global layer applies. Env variables and a provider's own auth
|
|
15
|
+
* still apply, below a stored credential, exactly as with `authPath`. The store owns what the file store does
|
|
16
16
|
* for its file: `modify` for one provider must run one at a time, because an OAuth refresh happens inside it and a
|
|
17
17
|
* rotated refresh token must never be used twice; and a store it cannot parse must not be answered as empty, or the
|
|
18
18
|
* next write replaces every provider's credential.
|
|
@@ -45,7 +45,7 @@ export type FastagentCredentialStore = CredentialStore & {
|
|
|
45
45
|
* to avoid. A provider present in neither layer is new, and new credentials belong to the primary.
|
|
46
46
|
*
|
|
47
47
|
* The fallback also yields to `projectAuthenticates`: a provider the project authenticates some other way (an env key,
|
|
48
|
-
* its
|
|
48
|
+
* a provider in its config that authenticates itself) never reaches it — for reading, listing or writing back.
|
|
49
49
|
*/
|
|
50
50
|
export declare function fastagentCredentialStore(path?: string, options?: FastagentAuthOptions & {
|
|
51
51
|
fallbackPath?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auth for the pi
|
|
2
|
+
* Auth for the pi harness: a read-WRITE {@link CredentialStore} over a fastagent credentials file, consumed by the
|
|
3
3
|
* `Models` collection (models.ts), and which of those files an agent reads. The write path refuses to overwrite a
|
|
4
4
|
* corrupt file, so a torn read never clobbers the other providers' credentials.
|
|
5
5
|
*/
|
|
@@ -7,7 +7,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { globalHome, SECRETS_DIRNAME, resolveOverridePath, resolveSecretsDir } from "../../paths.js";
|
|
9
9
|
import { log } from "../../log.js";
|
|
10
|
-
import { withLockedFile } from "
|
|
10
|
+
import { withLockedFile } from "../../locked-file.js";
|
|
11
11
|
/**
|
|
12
12
|
* The GLOBAL fastagent credentials file (distinct from pi's `~/.pi`), under the user-global machinery home
|
|
13
13
|
* `~/.fastagent/`.
|
|
@@ -80,7 +80,7 @@ function parseForWrite(raw, where) {
|
|
|
80
80
|
* to avoid. A provider present in neither layer is new, and new credentials belong to the primary.
|
|
81
81
|
*
|
|
82
82
|
* The fallback also yields to `projectAuthenticates`: a provider the project authenticates some other way (an env key,
|
|
83
|
-
* its
|
|
83
|
+
* a provider in its config that authenticates itself) never reaches it — for reading, listing or writing back.
|
|
84
84
|
*/
|
|
85
85
|
export function fastagentCredentialStore(path, options = {}) {
|
|
86
86
|
const warn = options.warn ?? ((message) => log.warn(message));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Credential, CredentialStore, Models, Provider } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type AuthLayers, type CredentialSourceOptions, type FastagentAuthOptions } from "./credentials.ts";
|
|
3
|
+
/** What authenticates one provider for an agent ({@link ModelEnvironment.authStatus}). */
|
|
4
|
+
interface AuthStatus {
|
|
5
|
+
/** What satisfies it now (`OAuth`, `stored credential`, an env variable's name), after a due refresh. */
|
|
6
|
+
source?: string;
|
|
7
|
+
/** Why resolving it failed (a corrupt entry, a refresh the provider refused), when it did. */
|
|
8
|
+
error?: string;
|
|
9
|
+
/** The file that holds the stored credential, or the one a login would write; absent over a caller's store. */
|
|
10
|
+
path?: string;
|
|
11
|
+
/** The kind of credential the store holds for it, usable or not. */
|
|
12
|
+
stored?: Credential["type"];
|
|
13
|
+
/**
|
|
14
|
+
* An env variable that also authenticates it but goes unused, because a stored credential owns its provider: a key
|
|
15
|
+
* added to a deployment's value file after the box was logged in, typically.
|
|
16
|
+
*/
|
|
17
|
+
shadowed?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ModelEnvironment {
|
|
20
|
+
/** The credentials files behind {@link credentials}; absent when the caller supplied its own store. */
|
|
21
|
+
auth?: AuthLayers;
|
|
22
|
+
credentials: CredentialStore;
|
|
23
|
+
/** pi's built-ins and the definition's providers, over {@link credentials}. */
|
|
24
|
+
models: Models;
|
|
25
|
+
/** Why a model list is not current, when one is not (logged already): each lists what it last had. */
|
|
26
|
+
catalogProblem?: string;
|
|
27
|
+
/**
|
|
28
|
+
* What authenticates `provider` here, resolved exactly as a message resolves it: the one answer the startup report
|
|
29
|
+
* and a deployed box's `login --if-missing` both give. `modelId` picks the model to probe with; the provider's first
|
|
30
|
+
* one otherwise (auth is provider-scoped).
|
|
31
|
+
*/
|
|
32
|
+
authStatus(provider: string, modelId?: string): Promise<AuthStatus>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: the named or global
|
|
36
|
+
* credentials file). A supplied `credentialStore` is used as given; otherwise the files: the directory's own layers
|
|
37
|
+
* ({@link resolveAuthLayers}), or the named file, or the global one. `providers` are the definition's. `environment` is
|
|
38
|
+
* what the agent's environment adds to this process's: its keys, the requests' settings, and where its files are
|
|
39
|
+
* (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) are all read there first. The model lists are refreshed before it
|
|
40
|
+
* resolves, kept in the instance's state root (`stateRoot`, else the directory's); `force` asks the network even when
|
|
41
|
+
* they are fresh.
|
|
42
|
+
*/
|
|
43
|
+
export declare function modelEnvironment(agentDir: string | undefined, source?: CredentialSourceOptions & FastagentAuthOptions, providers?: readonly Provider[], options?: {
|
|
44
|
+
force?: boolean;
|
|
45
|
+
stateRoot?: string;
|
|
46
|
+
environment?: Readonly<Record<string, string>>;
|
|
47
|
+
}): Promise<ModelEnvironment>;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An agent's MODEL ENVIRONMENT as one value: the credential store its messages authenticate through, the models they
|
|
3
|
+
* resolve against (models.ts) with their lists refreshed (catalog.ts), and what authenticates a provider there. Every
|
|
4
|
+
* reader builds it here (the serve, the startup report, a deployed box's `login --if-missing`, `info`, `login`, the
|
|
5
|
+
* model list, `deploy`'s pre-flight), so none can read other credential layers, another registry, or older lists than
|
|
6
|
+
* the Agent does.
|
|
7
|
+
*/
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { log } from "../../log.js";
|
|
10
|
+
import { resolveStateRoot } from "../../paths.js";
|
|
11
|
+
import { catalogsIn } from "./catalog.js";
|
|
12
|
+
import { GLOBAL_AUTH_PATH, assertOneCredentialSource, fastagentCredentialStore, resolveAuthLayers, } from "./credentials.js";
|
|
13
|
+
import { environmentAuth, modelsOver, probeAuthSource } from "./models.js";
|
|
14
|
+
/**
|
|
15
|
+
* The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: the named or global
|
|
16
|
+
* credentials file). A supplied `credentialStore` is used as given; otherwise the files: the directory's own layers
|
|
17
|
+
* ({@link resolveAuthLayers}), or the named file, or the global one. `providers` are the definition's. `environment` is
|
|
18
|
+
* what the agent's environment adds to this process's: its keys, the requests' settings, and where its files are
|
|
19
|
+
* (`FASTAGENT_AUTH_PATH`, `FASTAGENT_STATE_DIR`) are all read there first. The model lists are refreshed before it
|
|
20
|
+
* resolves, kept in the instance's state root (`stateRoot`, else the directory's); `force` asks the network even when
|
|
21
|
+
* they are fresh.
|
|
22
|
+
*/
|
|
23
|
+
export async function modelEnvironment(agentDir, source = {}, providers = [], options = {}) {
|
|
24
|
+
const environment = options.environment ?? {};
|
|
25
|
+
const env = { ...process.env, ...environment };
|
|
26
|
+
assertOneCredentialSource(source);
|
|
27
|
+
const files = source.credentialStore
|
|
28
|
+
? undefined
|
|
29
|
+
: agentDir
|
|
30
|
+
? resolveAuthLayers(agentDir, source.authPath, env)
|
|
31
|
+
: { path: source.authPath ?? GLOBAL_AUTH_PATH };
|
|
32
|
+
const store = files === undefined ? undefined : layeredStore(files, providers, source.warn, environment);
|
|
33
|
+
const credentials = source.credentialStore ?? store;
|
|
34
|
+
const stateRoot = options.stateRoot ?? (agentDir === undefined ? undefined : resolveStateRoot(agentDir, env));
|
|
35
|
+
const catalogs = catalogsIn(stateRoot === undefined ? undefined : join(stateRoot, "models"));
|
|
36
|
+
const models = modelsOver(credentials, providers, catalogs, environment);
|
|
37
|
+
const catalogProblem = await catalogs.refresh(models, { force: options.force === true });
|
|
38
|
+
if (catalogProblem !== undefined)
|
|
39
|
+
log.warn(`[fastagent] ${catalogProblem}`);
|
|
40
|
+
return {
|
|
41
|
+
...(files ? { auth: files } : {}),
|
|
42
|
+
credentials,
|
|
43
|
+
models,
|
|
44
|
+
...(catalogProblem === undefined ? {} : { catalogProblem }),
|
|
45
|
+
async authStatus(provider, modelId) {
|
|
46
|
+
const id = modelId ?? models.getProvider(provider)?.getModels()[0]?.id;
|
|
47
|
+
let resolved = {};
|
|
48
|
+
if (id !== undefined) {
|
|
49
|
+
try {
|
|
50
|
+
const found = await probeAuthSource(models, `${provider}/${id}`);
|
|
51
|
+
if (found !== undefined)
|
|
52
|
+
resolved = { source: found };
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
// The first line: pi folds the request's stack into the message of a failed refresh.
|
|
56
|
+
resolved = { error: String(error.message).split("\n")[0] };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// `read` never refreshes: this is the kind on file, whatever became of it.
|
|
60
|
+
const stored = (await credentials.read(provider))?.type;
|
|
61
|
+
const fromEnvironment = stored === undefined ? undefined : (await environmentAuth(provider, env, undefined, providers))?.source;
|
|
62
|
+
const shadowed = resolved.source !== undefined && fromEnvironment !== resolved.source ? fromEnvironment : undefined;
|
|
63
|
+
return {
|
|
64
|
+
...(store ? { path: await store.layerOf(provider) } : {}),
|
|
65
|
+
...resolved,
|
|
66
|
+
...(stored !== undefined ? { stored } : {}),
|
|
67
|
+
...(shadowed !== undefined ? { shadowed } : {}),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The credential store an agent reads through: its own file, then the user-global one for a provider the PROJECT
|
|
74
|
+
* authenticates no other way (its own file, a provider in code that needs no key, or the environment). A deployment
|
|
75
|
+
* never has the global file, so a global login that outranked, say, an `ANTHROPIC_API_KEY` in `.secrets/.env` would
|
|
76
|
+
* run one credential here and a different one deployed.
|
|
77
|
+
*/
|
|
78
|
+
function layeredStore(auth, providers, warn, environment) {
|
|
79
|
+
const own = () => fastagentCredentialStore(auth.path, { ...(warn ? { warn } : {}) });
|
|
80
|
+
if (auth.fallback === undefined)
|
|
81
|
+
return own();
|
|
82
|
+
const project = modelsOver(own(), providers, undefined, environment);
|
|
83
|
+
return fastagentCredentialStore(auth.path, {
|
|
84
|
+
...(warn ? { warn } : {}),
|
|
85
|
+
fallbackPath: auth.fallback,
|
|
86
|
+
projectAuthenticates: async (providerId) => (await project.checkAuth(providerId)) !== undefined,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an embedding host calls for model credentials (sign-in-api.ts has the types): a store kept where the host
|
|
3
|
+
* chooses, a sign-in in its own UI, and what authenticates a provider. Thin over what `fastagent login`, `logout` and
|
|
4
|
+
* `auth check` run, so each rule keeps one implementation. The one translation between FastAgent's types and pi-ai's
|
|
5
|
+
* is here, written without casts and with every function checked as a function, so it stops compiling when the two
|
|
6
|
+
* drift apart.
|
|
7
|
+
*/
|
|
8
|
+
import type { CredentialStore as PiCredentialStore } from "@earendil-works/pi-ai";
|
|
9
|
+
import type { CredentialStore, ProviderAuth, SignInOption, SignInRequest, SignInResult } from "../../sign-in-api.ts";
|
|
10
|
+
/**
|
|
11
|
+
* FastAgent's locked JSON credentials file at `path` (`~` is the home directory, a relative one resolves against the
|
|
12
|
+
* working directory now). A corrupt or unreadable file rejects every call.
|
|
13
|
+
*/
|
|
14
|
+
export declare function credentialFile(path: string): CredentialStore;
|
|
15
|
+
/** A host's store as pi-ai reads it. */
|
|
16
|
+
export declare function piCredentials(store: CredentialStore): PiCredentialStore;
|
|
17
|
+
/** Every sign-in of the built-in providers, by name, with what authenticates each one now. */
|
|
18
|
+
export declare function signInOptions(credentials: CredentialStore): Promise<SignInOption[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Sign in to a built-in provider and store the credential. The store is written to first, so a sign-in never runs
|
|
21
|
+
* toward a store that refuses it; an API key is checked with one request before it is stored, and one the provider
|
|
22
|
+
* rejects is asked for again. Aborting the interaction's signal rejects with `SignInCancelled` and stores nothing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function signIn(request: SignInRequest): Promise<SignInResult>;
|
|
25
|
+
/**
|
|
26
|
+
* What a request to a built-in provider authenticates with, resolved as a message resolves it: a stored credential
|
|
27
|
+
* (refreshing an OAuth token that is due, through the store's `modify`), else the environment. Undefined when nothing
|
|
28
|
+
* does; rejects when resolving fails, such as a refresh the provider refused.
|
|
29
|
+
*/
|
|
30
|
+
export declare function providerAuth(credentials: CredentialStore, provider: string): Promise<ProviderAuth | undefined>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { resolveOverridePath } from "../../paths.js";
|
|
2
|
+
import { fastagentCredentialStore } from "./credentials.js";
|
|
3
|
+
import { loginOptionsOver, loginOver } from "./login.js";
|
|
4
|
+
import { modelsOver } from "./models.js";
|
|
5
|
+
/** The file behind each store {@link credentialFile} made: a sign-in keeps the installation's id beside it. */
|
|
6
|
+
const files = new WeakMap();
|
|
7
|
+
/**
|
|
8
|
+
* FastAgent's locked JSON credentials file at `path` (`~` is the home directory, a relative one resolves against the
|
|
9
|
+
* working directory now). A corrupt or unreadable file rejects every call.
|
|
10
|
+
*/
|
|
11
|
+
export function credentialFile(path) {
|
|
12
|
+
// Resolved once, so the credentials and the installation's id beside them stay in one directory.
|
|
13
|
+
const resolved = resolveOverridePath(path);
|
|
14
|
+
if (resolved === undefined)
|
|
15
|
+
throw new Error("credentialFile needs the path of a credentials file");
|
|
16
|
+
const file = fastagentCredentialStore(resolved, {
|
|
17
|
+
warn: (message) => {
|
|
18
|
+
throw new Error(message);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
const store = {
|
|
22
|
+
read: (provider) => file.read(provider),
|
|
23
|
+
list: async () => (await file.list()).map(({ providerId, type }) => ({ provider: providerId, type })),
|
|
24
|
+
modify: (provider, change) => file.modify(provider, change),
|
|
25
|
+
delete: (provider) => file.delete(provider),
|
|
26
|
+
};
|
|
27
|
+
files.set(store, resolved);
|
|
28
|
+
return store;
|
|
29
|
+
}
|
|
30
|
+
/** A host's store as pi-ai reads it. */
|
|
31
|
+
export function piCredentials(store) {
|
|
32
|
+
return {
|
|
33
|
+
read: (provider) => store.read(provider),
|
|
34
|
+
list: async () => (await store.list()).map(({ provider, type }) => ({ providerId: provider, type })),
|
|
35
|
+
modify: (provider, change) => store.modify(provider, change),
|
|
36
|
+
delete: (provider) => store.delete(provider),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A host's interaction as pi-ai drives it. Arrow functions, not the object as it is: both declare `prompt` and
|
|
41
|
+
* `notify` as methods, whose parameters TypeScript checks in either direction, so a kind of prompt or event pi-ai adds
|
|
42
|
+
* would pass unseen; a call checks it.
|
|
43
|
+
*/
|
|
44
|
+
function piInteraction(interaction) {
|
|
45
|
+
return {
|
|
46
|
+
...(interaction.signal === undefined ? {} : { signal: interaction.signal }),
|
|
47
|
+
prompt: (prompt) => interaction.prompt(prompt),
|
|
48
|
+
notify: (event) => interaction.notify(event),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Every sign-in of the built-in providers, by name, with what authenticates each one now. */
|
|
52
|
+
export function signInOptions(credentials) {
|
|
53
|
+
return loginOptionsOver(piCredentials(credentials));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sign in to a built-in provider and store the credential. The store is written to first, so a sign-in never runs
|
|
57
|
+
* toward a store that refuses it; an API key is checked with one request before it is stored, and one the provider
|
|
58
|
+
* rejects is asked for again. Aborting the interaction's signal rejects with `SignInCancelled` and stores nothing.
|
|
59
|
+
*/
|
|
60
|
+
export function signIn(request) {
|
|
61
|
+
const { credentials, provider, method, interaction, deviceId } = request;
|
|
62
|
+
const file = files.get(credentials);
|
|
63
|
+
return loginOver({
|
|
64
|
+
provider,
|
|
65
|
+
method,
|
|
66
|
+
interaction: piInteraction(interaction),
|
|
67
|
+
...(file === undefined ? { credentialStore: piCredentials(credentials) } : { authPath: file }),
|
|
68
|
+
...(deviceId === undefined ? {} : { getDeviceId: () => deviceId }),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What a request to a built-in provider authenticates with, resolved as a message resolves it: a stored credential
|
|
73
|
+
* (refreshing an OAuth token that is due, through the store's `modify`), else the environment. Undefined when nothing
|
|
74
|
+
* does; rejects when resolving fails, such as a refresh the provider refused.
|
|
75
|
+
*/
|
|
76
|
+
export async function providerAuth(credentials, provider) {
|
|
77
|
+
const models = modelsOver(piCredentials(credentials));
|
|
78
|
+
if (models.getProvider(provider) === undefined)
|
|
79
|
+
throw new Error(`unknown provider "${provider}"`);
|
|
80
|
+
const checked = await models.checkAuth(provider);
|
|
81
|
+
if (checked === undefined)
|
|
82
|
+
return undefined;
|
|
83
|
+
const resolved = await models.getAuth(provider);
|
|
84
|
+
if (resolved === undefined)
|
|
85
|
+
return undefined;
|
|
86
|
+
const source = resolved.source ?? checked.source;
|
|
87
|
+
const { apiKey, baseUrl } = resolved.auth;
|
|
88
|
+
// pi's null header is one a request leaves out, which is what a header not given does.
|
|
89
|
+
const headers = Object.entries(resolved.auth.headers ?? {}).flatMap(([name, value]) => value === null ? [] : [[name, value]]);
|
|
90
|
+
return {
|
|
91
|
+
type: checked.type,
|
|
92
|
+
...(source === undefined ? {} : { source }),
|
|
93
|
+
...(apiKey === undefined ? {} : { apiKey }),
|
|
94
|
+
...(headers.length === 0 ? {} : { headers: Object.fromEntries(headers) }),
|
|
95
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
96
|
+
};
|
|
97
|
+
}
|