@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
|
@@ -2,20 +2,34 @@
|
|
|
2
2
|
* Signing in to a MODEL PROVIDER, for any front end: {@link login} runs one provider's flow over pi-ai's own
|
|
3
3
|
* `AuthInteraction` and persists the credential through the same {@link fastagentCredentialStore} the runtime reads
|
|
4
4
|
* (one writer, one lock/corruption semantics); {@link loginOptions} is the list a picker offers. `fastagent login` is
|
|
5
|
-
* one front end ({@link loginFlow}: terminal menus, then {@link login});
|
|
5
|
+
* one front end ({@link loginFlow}: terminal menus, then {@link login}); `login --deployment` drives the same flow on a
|
|
6
|
+
* deployed box over a relay (cli/login-relay.ts).
|
|
6
7
|
*/
|
|
7
8
|
import { InMemoryCredentialStore, } from "@earendil-works/pi-ai";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { writeFileAtomic } from "../../atomic-write.js";
|
|
13
|
+
import { assertOneCredentialSource, fastagentCredentialStore } from "./credentials.js";
|
|
14
|
+
import { UNNAMED_AUTH_SOURCE, interactiveAuth, loginProviders, modelsOver, probeApiKey } from "./models.js";
|
|
13
15
|
/** The user backed out of a prompt/menu, or the flow was aborted — a decision, not a failure. */
|
|
14
16
|
export class LoginCancelled extends Error {
|
|
15
17
|
}
|
|
18
|
+
/** A sign-in that cannot start as asked: an unknown provider, or a choice a caller without menus did not make. */
|
|
19
|
+
export class LoginRefused extends Error {
|
|
20
|
+
/** The sign-ins it could be instead, for a caller to choose from. */
|
|
21
|
+
choices;
|
|
22
|
+
constructor(message, choices) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.choices = choices;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const ACCOUNT_LABEL = "Sign in with an account";
|
|
28
|
+
const API_KEY_LABEL = "Sign in with an API key";
|
|
16
29
|
/**
|
|
17
|
-
* Every
|
|
18
|
-
*
|
|
30
|
+
* Every sign-in of pi's built-in providers, sorted by name as pi's `/login` lists them, with what authenticates each
|
|
31
|
+
* one here: a credential `credentials` (a credentials file, or the caller's own store) holds, else this process's
|
|
32
|
+
* environment, which a stored credential outranks.
|
|
19
33
|
*/
|
|
20
34
|
export function loginOptions(credentials) {
|
|
21
35
|
return loginOptionsOver(credentials);
|
|
@@ -27,23 +41,64 @@ export async function loginOptionsOver(credentials, providers) {
|
|
|
27
41
|
// ONE read of the store: `list` is metadata only, and reading per provider would repeat a corrupt file's warning for
|
|
28
42
|
// every provider.
|
|
29
43
|
const held = new Map((await storeOf(credentials).list()).map((info) => [info.providerId, info.type]));
|
|
44
|
+
// The environment alone, over no store: what authenticates a provider nothing is stored for.
|
|
45
|
+
const environment = modelsOver(new InMemoryCredentialStore(), providers);
|
|
30
46
|
const offered = [];
|
|
31
47
|
for (const provider of loginProviders(providers)) {
|
|
32
|
-
const methods = ["oauth", "api_key"].filter((method) =>
|
|
48
|
+
const methods = ["oauth", "api_key"].filter((method) => provider.auth[method === "oauth" ? "oauth" : "apiKey"]);
|
|
33
49
|
if (methods.length === 0)
|
|
34
50
|
continue;
|
|
35
51
|
const stored = held.get(provider.id);
|
|
52
|
+
let status;
|
|
53
|
+
let problem;
|
|
54
|
+
if (stored !== undefined) {
|
|
55
|
+
status = { type: stored, source: stored === "oauth" ? "OAuth" : "stored credential" };
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
try {
|
|
59
|
+
const found = await environment.checkAuth(provider.id);
|
|
60
|
+
if (found !== undefined)
|
|
61
|
+
status = { type: found.type, source: found.source ?? UNNAMED_AUTH_SOURCE };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
problem = error.message;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
36
67
|
for (const method of methods) {
|
|
37
68
|
offered.push({
|
|
38
69
|
provider: provider.id,
|
|
70
|
+
name: provider.name,
|
|
39
71
|
method,
|
|
40
|
-
label:
|
|
72
|
+
label: method === "oauth" ? (provider.auth.oauth?.loginLabel ?? ACCOUNT_LABEL) : API_KEY_LABEL,
|
|
41
73
|
subscription: method === "oauth" && provider.auth.oauth?.isSubscription === true,
|
|
42
|
-
|
|
74
|
+
external: interactiveAuth(provider, method) === undefined,
|
|
75
|
+
...(status ? { status } : {}),
|
|
76
|
+
...(problem ? { problem } : {}),
|
|
43
77
|
});
|
|
44
78
|
}
|
|
45
79
|
}
|
|
46
|
-
return offered;
|
|
80
|
+
return offered.sort((a, b) => a.name.localeCompare(b.name));
|
|
81
|
+
}
|
|
82
|
+
/** What a credential of `type` is called beside a provider's sign-in, as pi's picker calls it. */
|
|
83
|
+
function credentialKind(type, subscription) {
|
|
84
|
+
if (type === "api_key")
|
|
85
|
+
return "API key";
|
|
86
|
+
return subscription ? "subscription" : "account";
|
|
87
|
+
}
|
|
88
|
+
/** An option's status as pi's picker writes it after the provider's name. */
|
|
89
|
+
export function loginStatusText(option) {
|
|
90
|
+
if (option.problem !== undefined)
|
|
91
|
+
return `auth check failed: ${option.problem}`;
|
|
92
|
+
const { status } = option;
|
|
93
|
+
if (status === undefined)
|
|
94
|
+
return "not configured";
|
|
95
|
+
if (status.type !== option.method)
|
|
96
|
+
return `${credentialKind(status.type, option.subscription)} configured`;
|
|
97
|
+
if (status.source === "OAuth" || status.source === "stored credential")
|
|
98
|
+
return "✓ configured";
|
|
99
|
+
return /^[A-Z][A-Z0-9_]*(?:, [A-Z][A-Z0-9_]*)*$/.test(status.source)
|
|
100
|
+
? `✓ env: ${status.source}`
|
|
101
|
+
: `✓ ${status.source}`;
|
|
47
102
|
}
|
|
48
103
|
/** Combine present abort signals into one (no-op when none/one). */
|
|
49
104
|
function anySignal(...signals) {
|
|
@@ -60,7 +115,6 @@ function anySignal(...signals) {
|
|
|
60
115
|
* written. Credentials are provider-scoped, so any of the provider's models answers the question the check asks (does
|
|
61
116
|
* the provider refuse the key, HTTP 401); a key the provider rejects is never stored, and the provider's key flow runs
|
|
62
117
|
* again (the provider and method were not the mistake). Progress and outcome go out through `interaction.notify`.
|
|
63
|
-
* A provider an agent's models.json points elsewhere is not this call's: store that key with `fastagentCredentialStore`.
|
|
64
118
|
*
|
|
65
119
|
* Aborting `interaction.signal` at any point, the verification included, rejects with {@link LoginCancelled} and
|
|
66
120
|
* writes no credential (an OAuth device ID, the installation's identity, is kept once created). Every prompt carries a signal that aborts on it, on the prompt's own signal (a callback server
|
|
@@ -71,8 +125,9 @@ export function login(request) {
|
|
|
71
125
|
}
|
|
72
126
|
/** {@link login}, plus {@link LoginInternals}. */
|
|
73
127
|
export async function loginOver(request, internals = {}) {
|
|
74
|
-
const { providers, verifyWith
|
|
128
|
+
const { providers, verifyWith } = internals;
|
|
75
129
|
const { method, interaction } = request;
|
|
130
|
+
assertOneCredentialSource(request);
|
|
76
131
|
if (request.credentialStore === undefined && typeof request.authPath !== "string") {
|
|
77
132
|
throw new Error("login needs authPath or credentialStore: where the sign-in is written");
|
|
78
133
|
}
|
|
@@ -85,32 +140,15 @@ export async function loginOver(request, internals = {}) {
|
|
|
85
140
|
const flow = auth.login.bind(auth);
|
|
86
141
|
// The key under test lives here, not in the file, until it passes.
|
|
87
142
|
const trial = new InMemoryCredentialStore();
|
|
88
|
-
// Only a key needs the registry
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
? undefined
|
|
92
|
-
: agentDir
|
|
93
|
-
? await createPiModelRuntime({
|
|
94
|
-
agentDir,
|
|
95
|
-
credentials: trial,
|
|
96
|
-
...(providers ? { providers: [...providers] } : {}),
|
|
97
|
-
})
|
|
98
|
-
: piModelsOver(trial, providers);
|
|
99
|
-
const store = agentModels(undefined, request).credentials;
|
|
143
|
+
// Only a key needs the registry: it is checked with one request before it is kept.
|
|
144
|
+
const models = method === "api_key" ? modelsOver(trial, providers) : undefined;
|
|
145
|
+
const store = request.credentialStore ?? fastagentCredentialStore(request.authPath);
|
|
100
146
|
await store.modify(provider.id, async () => undefined);
|
|
101
147
|
const signal = interaction.signal ?? new AbortController().signal;
|
|
102
148
|
const notify = (event) => interaction.notify(event);
|
|
103
149
|
let getDeviceId = request.getDeviceId;
|
|
104
150
|
if (method === "oauth" && !getDeviceId && request.authPath !== undefined) {
|
|
105
|
-
|
|
106
|
-
const settings = SettingsManager.create(dirname(request.authPath), dirname(request.authPath), {
|
|
107
|
-
projectTrusted: false,
|
|
108
|
-
});
|
|
109
|
-
const deviceId = settings.getOrCreateDeviceId();
|
|
110
|
-
await settings.flush();
|
|
111
|
-
const errors = settings.drainErrors();
|
|
112
|
-
if (errors.length)
|
|
113
|
-
throw new Error(`could not persist login device ID: ${errors.map(({ error }) => error.message).join("; ")}`);
|
|
151
|
+
const deviceId = await deviceIdBeside(request.authPath);
|
|
114
152
|
getDeviceId = () => deviceId;
|
|
115
153
|
}
|
|
116
154
|
for (;;) {
|
|
@@ -129,6 +167,39 @@ export async function loginOver(request, internals = {}) {
|
|
|
129
167
|
return { provider: provider.id, method, verified };
|
|
130
168
|
}
|
|
131
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* The installation's identity, kept beside the credentials file it signs in to: created once, then read. Settled before
|
|
172
|
+
* the flow runs, so a sign-in never completes with an identity that was not kept.
|
|
173
|
+
*/
|
|
174
|
+
async function deviceIdBeside(authPath) {
|
|
175
|
+
const path = join(dirname(authPath), "device.json");
|
|
176
|
+
try {
|
|
177
|
+
return await readOrCreateDeviceId(path);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
throw new Error(`could not persist login device ID in ${path}: ${error.message}`, { cause: error });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function readOrCreateDeviceId(path) {
|
|
184
|
+
let text;
|
|
185
|
+
try {
|
|
186
|
+
text = await readFile(path, "utf8");
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (error.code !== "ENOENT")
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (text !== undefined) {
|
|
193
|
+
const stored = JSON.parse(text).deviceId;
|
|
194
|
+
if (typeof stored !== "string" || stored === "")
|
|
195
|
+
throw new Error("it holds no deviceId");
|
|
196
|
+
return stored;
|
|
197
|
+
}
|
|
198
|
+
const deviceId = randomUUID();
|
|
199
|
+
await mkdir(dirname(path), { recursive: true });
|
|
200
|
+
writeFileAtomic(path, `${JSON.stringify({ deviceId })}\n`);
|
|
201
|
+
return deviceId;
|
|
202
|
+
}
|
|
132
203
|
async function runFlow(flow, interaction, signal) {
|
|
133
204
|
if (signal.aborted)
|
|
134
205
|
throw new LoginCancelled("cancelled");
|
|
@@ -165,10 +236,10 @@ export const PI_DEFAULT_MODELS = {
|
|
|
165
236
|
"ant-ling": "Ring-2.6-1T",
|
|
166
237
|
anthropic: "claude-opus-4-8",
|
|
167
238
|
openai: "gpt-5.5",
|
|
168
|
-
|
|
239
|
+
azure: "gpt-5.4",
|
|
169
240
|
"openai-codex": "gpt-6.1-sol",
|
|
170
241
|
radius: "balanced",
|
|
171
|
-
nvidia: "nvidia/nemotron-3-
|
|
242
|
+
nvidia: "nvidia/nemotron-3-ultra-550b-a55b",
|
|
172
243
|
deepseek: "deepseek-v4-pro",
|
|
173
244
|
google: "gemini-3.1-pro-preview",
|
|
174
245
|
"google-vertex": "gemini-3.1-pro-preview",
|
|
@@ -255,7 +326,11 @@ function terminalInteraction(io, signal) {
|
|
|
255
326
|
label: o.label,
|
|
256
327
|
...(o.description ? { hint: o.description } : {}),
|
|
257
328
|
})))
|
|
258
|
-
: await io.prompt(p.message, {
|
|
329
|
+
: await io.prompt(p.message, {
|
|
330
|
+
hidden: p.type === "secret",
|
|
331
|
+
redirect: p.type === "manual_code",
|
|
332
|
+
...(p.signal ? { signal: p.signal } : {}),
|
|
333
|
+
});
|
|
259
334
|
if (v === undefined)
|
|
260
335
|
throw new LoginCancelled("cancelled");
|
|
261
336
|
return v;
|
|
@@ -269,7 +344,7 @@ function terminalInteraction(io, signal) {
|
|
|
269
344
|
}
|
|
270
345
|
else if (e.type === "device_code") {
|
|
271
346
|
io.note(`Go to ${e.verificationUri} and enter the code: ${e.userCode}`);
|
|
272
|
-
io.openUrl(e.verificationUri);
|
|
347
|
+
io.openUrl(e.verificationUri, e.userCode);
|
|
273
348
|
}
|
|
274
349
|
else {
|
|
275
350
|
io.note(e.message);
|
|
@@ -277,55 +352,88 @@ function terminalInteraction(io, signal) {
|
|
|
277
352
|
},
|
|
278
353
|
};
|
|
279
354
|
}
|
|
280
|
-
async function selectMethod(io) {
|
|
281
|
-
const v = await io.select("Authentication method", [
|
|
282
|
-
{ value: "oauth", label: "Use a subscription (OAuth)" },
|
|
283
|
-
{ value: "api_key", label: "Use an API key" },
|
|
284
|
-
]);
|
|
285
|
-
if (v !== "oauth" && v !== "api_key")
|
|
286
|
-
throw new LoginCancelled("no authentication method selected");
|
|
287
|
-
return v;
|
|
288
|
-
}
|
|
289
355
|
/**
|
|
290
|
-
* `fastagent login
|
|
291
|
-
*
|
|
356
|
+
* `fastagent login` in a terminal: ask only what was not given, as pi's `/login` asks it (how to sign in, then the
|
|
357
|
+
* provider; a provider named by its id or its name, then how, when it offers both), then {@link login}.
|
|
292
358
|
*/
|
|
293
359
|
export async function loginFlow(io, options) {
|
|
294
|
-
const offered = await loginOptionsOver(options.authPath, options.providers);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const methods = offered.filter((o) => o.provider === provider).map((o) => o.method);
|
|
299
|
-
if (methods.length === 0) {
|
|
300
|
-
const known = loginProviders(options.providers).some((p) => p.id === provider);
|
|
301
|
-
throw new Error(known
|
|
302
|
-
? `provider "${provider}" has no interactive login — set its API key via the provider's env var`
|
|
303
|
-
: `unknown provider "${provider}"`);
|
|
304
|
-
}
|
|
305
|
-
method ??= methods.length > 1 ? await selectMethod(io) : methods[0];
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
const chosenMethod = method ?? (await selectMethod(io));
|
|
309
|
-
method = chosenMethod;
|
|
310
|
-
const candidates = offered.filter((o) => o.method === chosenMethod);
|
|
311
|
-
if (candidates.length === 0)
|
|
312
|
-
throw new Error(`no provider supports ${chosenMethod} login`);
|
|
313
|
-
provider = await io.select("Select a provider", candidates.map((o) => ({
|
|
314
|
-
value: o.provider,
|
|
315
|
-
label: o.label,
|
|
316
|
-
...(o.stored ? { hint: `configured (${o.stored})` } : {}),
|
|
317
|
-
})));
|
|
318
|
-
if (!candidates.some((o) => o.provider === provider))
|
|
319
|
-
throw new LoginCancelled("no provider selected");
|
|
360
|
+
const offered = await loginOptionsOver(options.statusFrom ?? options.authPath, options.providers);
|
|
361
|
+
const choice = await chooseLogin(offered, options, io);
|
|
362
|
+
if (choice.external) {
|
|
363
|
+
throw new LoginRefused(`${choice.name} is configured outside fastagent: set its key in the environment`, []);
|
|
320
364
|
}
|
|
321
365
|
return loginOver({
|
|
322
|
-
provider: provider,
|
|
323
|
-
method: method,
|
|
366
|
+
provider: choice.provider,
|
|
367
|
+
method: choice.method,
|
|
324
368
|
authPath: options.authPath,
|
|
325
369
|
interaction: terminalInteraction(io, options.signal),
|
|
326
370
|
}, {
|
|
327
371
|
...(options.providers ? { providers: options.providers } : {}),
|
|
328
372
|
...(options.verifyWith ? { verifyWith: options.verifyWith } : {}),
|
|
329
|
-
...(options.agentDir ? { agentDir: options.agentDir } : {}),
|
|
330
373
|
});
|
|
331
374
|
}
|
|
375
|
+
/** The flag that gives a method, as a refusal names it. */
|
|
376
|
+
const METHOD_FLAGS = "--method account or --method api-key";
|
|
377
|
+
/**
|
|
378
|
+
* The sign-in `given` names, or, with `menus`, the one a person picks; without them a choice not given refuses
|
|
379
|
+
* ({@link LoginRefused}) naming how to give it.
|
|
380
|
+
*/
|
|
381
|
+
export async function chooseLogin(offered, given, menus) {
|
|
382
|
+
const picked = async (message, options, from, by) => {
|
|
383
|
+
const value = await menus.select(message, options);
|
|
384
|
+
const option = from.find((o) => by(o) === value);
|
|
385
|
+
if (option === undefined)
|
|
386
|
+
throw new LoginCancelled("nothing selected");
|
|
387
|
+
return option;
|
|
388
|
+
};
|
|
389
|
+
if (given.provider !== undefined) {
|
|
390
|
+
const named = given.provider.trim().toLowerCase();
|
|
391
|
+
const ofProvider = offered.filter((o) => o.provider.toLowerCase() === named || o.name.toLowerCase() === named);
|
|
392
|
+
if (ofProvider.length === 0) {
|
|
393
|
+
throw new LoginRefused(`unknown provider "${given.provider}": ${providerIds(offered)}`, offered);
|
|
394
|
+
}
|
|
395
|
+
const name = ofProvider[0].name;
|
|
396
|
+
const ofMethod = given.method === undefined ? ofProvider : ofProvider.filter((o) => o.method === given.method);
|
|
397
|
+
if (ofMethod.length === 0) {
|
|
398
|
+
const only = ofProvider[0].method === "oauth" ? "an account" : "an API key";
|
|
399
|
+
throw new LoginRefused(`${name} signs in only with ${only}`, ofProvider);
|
|
400
|
+
}
|
|
401
|
+
if (ofMethod.length === 1)
|
|
402
|
+
return ofMethod[0];
|
|
403
|
+
if (menus === undefined)
|
|
404
|
+
throw new LoginRefused(`${name} signs in with an account or an API key: pass ${METHOD_FLAGS}`, ofMethod);
|
|
405
|
+
return picked(`Select authentication method for ${name}:`, ofMethod.map((o) => ({ value: o.method, label: o.label })), ofMethod, (o) => o.method);
|
|
406
|
+
}
|
|
407
|
+
if (menus === undefined) {
|
|
408
|
+
throw new LoginRefused(`name the provider to sign in to (${providerIds(offered)}), and ${METHOD_FLAGS} for one that offers both`, given.method === undefined ? offered : offered.filter((o) => o.method === given.method));
|
|
409
|
+
}
|
|
410
|
+
let method = given.method;
|
|
411
|
+
if (method === undefined) {
|
|
412
|
+
// pi offers its own gateway beside the two ways of signing in.
|
|
413
|
+
const radius = offered.find((o) => o.provider === "radius" && o.method === "oauth");
|
|
414
|
+
const ways = [
|
|
415
|
+
...(offered.some((o) => o.method === "oauth") ? [{ value: "oauth", label: ACCOUNT_LABEL }] : []),
|
|
416
|
+
...(offered.some((o) => o.method === "api_key") ? [{ value: "api_key", label: API_KEY_LABEL }] : []),
|
|
417
|
+
...(radius ? [{ value: "radius", label: `Sign in with ${radius.name}${statusSuffix(radius)}` }] : []),
|
|
418
|
+
];
|
|
419
|
+
const way = await menus.select("Select authentication method:", ways);
|
|
420
|
+
if (way === "radius" && radius)
|
|
421
|
+
return radius;
|
|
422
|
+
if (way !== "oauth" && way !== "api_key")
|
|
423
|
+
throw new LoginCancelled("no authentication method selected");
|
|
424
|
+
method = way;
|
|
425
|
+
}
|
|
426
|
+
const candidates = offered.filter((o) => o.method === method);
|
|
427
|
+
if (candidates.length === 0)
|
|
428
|
+
throw new LoginRefused(`no provider signs in with ${method === "oauth" ? "an account" : "an API key"}`, []);
|
|
429
|
+
return picked("Select provider to configure:", candidates.map((o) => ({ value: o.provider, label: `${o.name}${statusSuffix(o)}` })), candidates, (o) => o.provider);
|
|
430
|
+
}
|
|
431
|
+
/** An option's status after its name on every row, as pi's picker writes it: ` ✓ configured`, ` • not configured`. */
|
|
432
|
+
function statusSuffix(option) {
|
|
433
|
+
const text = loginStatusText(option);
|
|
434
|
+
return text.startsWith("✓") ? ` ${text}` : ` • ${text}`;
|
|
435
|
+
}
|
|
436
|
+
/** Every provider id a sign-in can name, for a refusal that must say which. */
|
|
437
|
+
function providerIds(offered) {
|
|
438
|
+
return `one of ${[...new Set(offered.map((o) => o.provider))].sort().join(", ")}`;
|
|
439
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-ai's model collection as FastAgent composes it: the built-in providers over a credential store, kept current by
|
|
3
|
+
* the catalogs (catalog.ts), and the definition's own providers; and the reads a report, a login or a picker make of
|
|
4
|
+
* it. A custom model is a provider in code (`defineAgent({ providers })`), never a models file or a per-account list
|
|
5
|
+
* (architecture.md §3).
|
|
6
|
+
*/
|
|
7
|
+
import { type Api, type CredentialStore, type Model, type Models, type Provider } from "@earendil-works/pi-ai";
|
|
8
|
+
import type { Catalogs } from "./catalog.ts";
|
|
9
|
+
/** The provider of a `"provider/modelId"` spec. */
|
|
10
|
+
export declare function providerOf(spec: string): string;
|
|
11
|
+
/** Every chat model `models` offers, as `"provider/modelId"`, sorted. */
|
|
12
|
+
export declare function listModels(models: Models): string[];
|
|
13
|
+
/**
|
|
14
|
+
* The built-ins plus `providers` (a same id replaces a built-in), over any credential store. With `catalogs`, the lists
|
|
15
|
+
* its refresh brings up to date; without, the ones this pi-ai was built with.
|
|
16
|
+
*/
|
|
17
|
+
export declare function modelsOver(credentials: CredentialStore, providers?: readonly Provider[], catalogs?: Catalogs): Models;
|
|
18
|
+
/**
|
|
19
|
+
* Whether `env` alone satisfies a provider, by pi's own check, and with what when the provider says (its API-key
|
|
20
|
+
* variable, or an ambient source such as an AWS profile); undefined when it does not. A provider in code may
|
|
21
|
+
* authenticate without naming a source. Passed explicitly rather than read from `process.env`: only the environment a
|
|
22
|
+
* SHELL hands down is shared by every agent on the machine, and a process that has loaded an agent's `.secrets/.env`
|
|
23
|
+
* holds that agent's variables too. What the shell shares outranks the global credentials file (environment.ts), so a
|
|
24
|
+
* global login for such a provider is not used.
|
|
25
|
+
*
|
|
26
|
+
* `fileExists` answers for the files a source points at (an AWS profile, Google ADC): this machine's by default, which
|
|
27
|
+
* is right wherever the answer is about THIS machine. A deployment passes one that finds nothing, because no file of
|
|
28
|
+
* the builder's travels with it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function environmentAuth(providerId: string, env: NodeJS.ProcessEnv, fileExists?: (path: string) => Promise<boolean>, providers?: readonly Provider[]): Promise<{
|
|
31
|
+
source?: string;
|
|
32
|
+
} | undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* The environment variables a provider reads its key from, in the order it reads them: what a person sets for it to
|
|
35
|
+
* authenticate with no sign-in. pi-ai keeps the names inside each provider's auth, so they are learnt by asking the
|
|
36
|
+
* provider to authenticate over an environment that records every name asked for and holds none. Its side-effect-free
|
|
37
|
+
* `check` when it has one, since its `resolve` may then run commands.
|
|
38
|
+
*/
|
|
39
|
+
export declare function keyVariables(provider: Provider): Promise<string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* What a provider can offer as an interactive login: an OAuth flow, an API-key ENTRY prompt, or nothing ("none" — the
|
|
42
|
+
* key must come from the provider's env var).
|
|
43
|
+
*/
|
|
44
|
+
export type InteractiveLoginKind = "oauth" | "api_key" | "none";
|
|
45
|
+
export declare function interactiveLoginKind(p: Provider): InteractiveLoginKind;
|
|
46
|
+
/** THE rule for "can this method be signed in to interactively": the auth to run for it, or undefined. */
|
|
47
|
+
export declare function interactiveAuth(provider: Provider, method: Exclude<InteractiveLoginKind, "none">): import("@earendil-works/pi-ai").ApiKeyAuth | import("@earendil-works/pi-ai").OAuthAuth | undefined;
|
|
48
|
+
/** The providers a sign-in can target: pi's built-ins plus `extra`, composed exactly as {@link modelsOver} does. */
|
|
49
|
+
export declare function loginProviders(extra?: readonly Provider[]): readonly Provider[];
|
|
50
|
+
/** Per-provider auth status for the first-run model picker. */
|
|
51
|
+
export type ProviderAuthStatus = {
|
|
52
|
+
state: "ready";
|
|
53
|
+
source?: string;
|
|
54
|
+
} | {
|
|
55
|
+
state: "unconfigured";
|
|
56
|
+
login: InteractiveLoginKind;
|
|
57
|
+
} | {
|
|
58
|
+
state: "broken";
|
|
59
|
+
message: string;
|
|
60
|
+
login: InteractiveLoginKind;
|
|
61
|
+
};
|
|
62
|
+
/** Probe every provider's auth once (auth is provider-scoped, so any of its models works as the probe). */
|
|
63
|
+
export declare function providerAuthStatuses(models: Models): Promise<Map<string, ProviderAuthStatus>>;
|
|
64
|
+
/** The source of a provider that authenticates without naming one: a definition's provider resolving its own key. */
|
|
65
|
+
export declare const UNNAMED_AUTH_SOURCE = "the provider's own auth";
|
|
66
|
+
/**
|
|
67
|
+
* Which source currently satisfies auth for `spec`, or undefined when none does (or the spec is not in `models`).
|
|
68
|
+
* Rejects when resolving fails — a corrupt entry, a refresh the provider refused — which is a different answer from
|
|
69
|
+
* "nothing configured" and must reach whoever reports it.
|
|
70
|
+
*/
|
|
71
|
+
export declare function probeAuthSource(models: Models, spec: string): Promise<string | undefined>;
|
|
72
|
+
/**
|
|
73
|
+
* Verdict of {@link probeApiKey}: `rejected` is DEFINITIVE (the provider answered HTTP 401 — the key is wrong);
|
|
74
|
+
* everything else non-ok is `unknown`.
|
|
75
|
+
*/
|
|
76
|
+
export type KeyProbe = {
|
|
77
|
+
state: "ok";
|
|
78
|
+
} | {
|
|
79
|
+
state: "rejected" | "unknown";
|
|
80
|
+
message: string;
|
|
81
|
+
};
|
|
82
|
+
/** Quick-fail probe for a just-stored API key. */
|
|
83
|
+
export declare function probeApiKey(models: Models, model: Model<Api>, signal?: AbortSignal): Promise<KeyProbe>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-ai's model collection as FastAgent composes it: the built-in providers over a credential store, kept current by
|
|
3
|
+
* the catalogs (catalog.ts), and the definition's own providers; and the reads a report, a login or a picker make of
|
|
4
|
+
* it. A custom model is a provider in code (`defineAgent({ providers })`), never a models file or a per-account list
|
|
5
|
+
* (architecture.md §3).
|
|
6
|
+
*/
|
|
7
|
+
import { InMemoryCredentialStore, createModels, defaultProviderAuthContext, } from "@earendil-works/pi-ai";
|
|
8
|
+
import { builtinModels, builtinProviders } from "@earendil-works/pi-ai/providers/all";
|
|
9
|
+
/** The provider of a `"provider/modelId"` spec. */
|
|
10
|
+
export function providerOf(spec) {
|
|
11
|
+
const slash = spec.indexOf("/");
|
|
12
|
+
return slash > 0 ? spec.slice(0, slash) : spec;
|
|
13
|
+
}
|
|
14
|
+
/** Every chat model `models` offers, as `"provider/modelId"`, sorted. */
|
|
15
|
+
export function listModels(models) {
|
|
16
|
+
const specs = [];
|
|
17
|
+
for (const provider of models.getProviders()) {
|
|
18
|
+
for (const model of provider.getModels())
|
|
19
|
+
specs.push(`${provider.id}/${model.id}`);
|
|
20
|
+
}
|
|
21
|
+
return specs.sort();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The built-ins plus `providers` (a same id replaces a built-in), over any credential store. With `catalogs`, the lists
|
|
25
|
+
* its refresh brings up to date; without, the ones this pi-ai was built with.
|
|
26
|
+
*/
|
|
27
|
+
export function modelsOver(credentials, providers = [], catalogs) {
|
|
28
|
+
const models = createModels({
|
|
29
|
+
credentials,
|
|
30
|
+
authContext: defaultProviderAuthContext(),
|
|
31
|
+
...(catalogs ? { modelsStore: catalogs.fetched } : {}),
|
|
32
|
+
});
|
|
33
|
+
for (const builtIn of builtinProviders())
|
|
34
|
+
models.setProvider(catalogs ? catalogs.over(builtIn) : builtIn);
|
|
35
|
+
for (const provider of providers)
|
|
36
|
+
models.setProvider(provider);
|
|
37
|
+
return models;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether `env` alone satisfies a provider, by pi's own check, and with what when the provider says (its API-key
|
|
41
|
+
* variable, or an ambient source such as an AWS profile); undefined when it does not. A provider in code may
|
|
42
|
+
* authenticate without naming a source. Passed explicitly rather than read from `process.env`: only the environment a
|
|
43
|
+
* SHELL hands down is shared by every agent on the machine, and a process that has loaded an agent's `.secrets/.env`
|
|
44
|
+
* holds that agent's variables too. What the shell shares outranks the global credentials file (environment.ts), so a
|
|
45
|
+
* global login for such a provider is not used.
|
|
46
|
+
*
|
|
47
|
+
* `fileExists` answers for the files a source points at (an AWS profile, Google ADC): this machine's by default, which
|
|
48
|
+
* is right wherever the answer is about THIS machine. A deployment passes one that finds nothing, because no file of
|
|
49
|
+
* the builder's travels with it.
|
|
50
|
+
*/
|
|
51
|
+
export async function environmentAuth(providerId, env, fileExists = defaultProviderAuthContext().fileExists, providers = []) {
|
|
52
|
+
const models = builtinModels({
|
|
53
|
+
credentials: new InMemoryCredentialStore(),
|
|
54
|
+
authContext: {
|
|
55
|
+
// pi's own reading of a variable (a blank one is unset), over the given environment.
|
|
56
|
+
env: async (name) => (env[name]?.trim() ? env[name] : undefined),
|
|
57
|
+
fileExists,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
for (const provider of providers)
|
|
61
|
+
models.setProvider(provider);
|
|
62
|
+
return models.checkAuth(providerId);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The environment variables a provider reads its key from, in the order it reads them: what a person sets for it to
|
|
66
|
+
* authenticate with no sign-in. pi-ai keeps the names inside each provider's auth, so they are learnt by asking the
|
|
67
|
+
* provider to authenticate over an environment that records every name asked for and holds none. Its side-effect-free
|
|
68
|
+
* `check` when it has one, since its `resolve` may then run commands.
|
|
69
|
+
*/
|
|
70
|
+
export async function keyVariables(provider) {
|
|
71
|
+
const apiKey = provider.auth.apiKey;
|
|
72
|
+
if (apiKey === undefined)
|
|
73
|
+
return [];
|
|
74
|
+
const asked = new Set();
|
|
75
|
+
const ctx = {
|
|
76
|
+
env: async (name) => void asked.add(name),
|
|
77
|
+
fileExists: async () => false,
|
|
78
|
+
};
|
|
79
|
+
const signal = new AbortController().signal;
|
|
80
|
+
await (apiKey.check ? apiKey.check({ ctx, signal }) : apiKey.resolve({ ctx, signal }));
|
|
81
|
+
return [...asked];
|
|
82
|
+
}
|
|
83
|
+
export function interactiveLoginKind(p) {
|
|
84
|
+
if (interactiveAuth(p, "oauth"))
|
|
85
|
+
return "oauth";
|
|
86
|
+
return interactiveAuth(p, "api_key") ? "api_key" : "none";
|
|
87
|
+
}
|
|
88
|
+
/** THE rule for "can this method be signed in to interactively": the auth to run for it, or undefined. */
|
|
89
|
+
export function interactiveAuth(provider, method) {
|
|
90
|
+
return method === "oauth" ? provider.auth.oauth : provider.auth.apiKey?.login ? provider.auth.apiKey : undefined;
|
|
91
|
+
}
|
|
92
|
+
/** The providers a sign-in can target: pi's built-ins plus `extra`, composed exactly as {@link modelsOver} does. */
|
|
93
|
+
export function loginProviders(extra) {
|
|
94
|
+
return modelsOver(new InMemoryCredentialStore(), extra).getProviders();
|
|
95
|
+
}
|
|
96
|
+
/** Probe every provider's auth once (auth is provider-scoped, so any of its models works as the probe). */
|
|
97
|
+
export async function providerAuthStatuses(models) {
|
|
98
|
+
const statuses = new Map();
|
|
99
|
+
for (const provider of models.getProviders()) {
|
|
100
|
+
const [probe] = provider.getModels();
|
|
101
|
+
if (!probe)
|
|
102
|
+
continue;
|
|
103
|
+
const login = interactiveLoginKind(provider);
|
|
104
|
+
try {
|
|
105
|
+
const auth = await models.getAuth(probe);
|
|
106
|
+
statuses.set(provider.id, auth ? { state: "ready", source: auth.source } : { state: "unconfigured", login });
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
statuses.set(provider.id, { state: "broken", message: error.message, login });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return statuses;
|
|
113
|
+
}
|
|
114
|
+
/** The source of a provider that authenticates without naming one: a definition's provider resolving its own key. */
|
|
115
|
+
export const UNNAMED_AUTH_SOURCE = "the provider's own auth";
|
|
116
|
+
/**
|
|
117
|
+
* Which source currently satisfies auth for `spec`, or undefined when none does (or the spec is not in `models`).
|
|
118
|
+
* Rejects when resolving fails — a corrupt entry, a refresh the provider refused — which is a different answer from
|
|
119
|
+
* "nothing configured" and must reach whoever reports it.
|
|
120
|
+
*/
|
|
121
|
+
export async function probeAuthSource(models, spec) {
|
|
122
|
+
const slash = spec.indexOf("/");
|
|
123
|
+
if (slash < 1)
|
|
124
|
+
return undefined;
|
|
125
|
+
const model = models.getModel(spec.slice(0, slash), spec.slice(slash + 1));
|
|
126
|
+
if (!model)
|
|
127
|
+
return undefined;
|
|
128
|
+
// A virtual selection authenticates the physical model after routing, not its catalog entry.
|
|
129
|
+
if (model.api === "pi-virtual")
|
|
130
|
+
return "virtual";
|
|
131
|
+
const auth = await models.getAuth(model);
|
|
132
|
+
return auth === undefined ? undefined : (auth.source ?? UNNAMED_AUTH_SOURCE);
|
|
133
|
+
}
|
|
134
|
+
/** Quick-fail probe for a just-stored API key. */
|
|
135
|
+
export async function probeApiKey(models, model, signal) {
|
|
136
|
+
let status;
|
|
137
|
+
let reply;
|
|
138
|
+
try {
|
|
139
|
+
reply = await models.complete(model, { messages: [{ role: "user", content: "ping", timestamp: Date.now() }] }, {
|
|
140
|
+
maxTokens: 16,
|
|
141
|
+
timeoutMs: 15_000,
|
|
142
|
+
maxRetries: 0,
|
|
143
|
+
...(signal ? { signal } : {}),
|
|
144
|
+
onResponse: (r) => {
|
|
145
|
+
status = r.status;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
// Thrown = before/around the request (auth resolution, transport setup) — not a provider verdict.
|
|
151
|
+
return { state: "unknown", message: error.message };
|
|
152
|
+
}
|
|
153
|
+
if (reply.stopReason !== "error" && reply.stopReason !== "aborted")
|
|
154
|
+
return { state: "ok" };
|
|
155
|
+
const message = reply.errorMessage ?? `stopReason "${reply.stopReason}"`;
|
|
156
|
+
const unauthorized = status === 401 || (status === undefined && /(^|\D)401(\D|$)/.test(message));
|
|
157
|
+
return { state: unauthorized ? "rejected" : "unknown", message };
|
|
158
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Models, ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type Conversation, Harness, type HarnessOptions, type HarnessSettings, type ModelRef, type RegistryReader, type Storage } from "@earendil-works/pi-durable";
|
|
3
|
+
import { type Agent } from "../../agent-api.ts";
|
|
4
|
+
/** What a Caller may offer as `/<name>`: a prompt template or a skill, expanded for the model (SPEC.md §5.1). */
|
|
5
|
+
export interface AgentCommand {
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
expand(args: string): string;
|
|
9
|
+
}
|
|
10
|
+
export interface DurableAgentOptions {
|
|
11
|
+
storage: Storage;
|
|
12
|
+
models: Models;
|
|
13
|
+
registry: RegistryReader;
|
|
14
|
+
settings?: HarnessSettings;
|
|
15
|
+
env?: HarnessOptions["env"];
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
/** Read for every `info()` and send, so a command the definition gains is offered without a restart. */
|
|
19
|
+
commands?: () => readonly AgentCommand[];
|
|
20
|
+
/** What a conversation answers with when its own settings name none (defaults.ts). */
|
|
21
|
+
model?: ModelRef;
|
|
22
|
+
thinkingLevel?: ModelThinkingLevel;
|
|
23
|
+
inFlightPerProcess?: number;
|
|
24
|
+
/** What becomes of the messages an earlier process accepted and did not end ({@link Unfinished}). */
|
|
25
|
+
unfinished?: Unfinished;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What a process does with the messages an earlier one accepted and did not end (it was stopped, or killed). `"resume"`
|
|
29
|
+
* (the default, a serve's): they run on in this one, as its own work. `"abort"`: they end aborted before anything runs,
|
|
30
|
+
* for a Caller whose messages do not outlive it (`fastagent invoke`), where running one again would run what its user
|
|
31
|
+
* stopped. `"refuse"`: the open fails naming them, for a process that runs one thing and would cut the rest off
|
|
32
|
+
* (`fastagent schedules run`).
|
|
33
|
+
*/
|
|
34
|
+
export type Unfinished = "resume" | "abort" | "refuse";
|
|
35
|
+
export interface DurableAgent extends Agent {
|
|
36
|
+
/** pi-durable's own API, for operating the agent. */
|
|
37
|
+
readonly harness: Harness;
|
|
38
|
+
/** The conversation a session id names, to operate it through `harness`. */
|
|
39
|
+
conversation(sessionId: string): Promise<Conversation | undefined>;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare function openDurableAgent(options: DurableAgentOptions): Promise<DurableAgent>;
|