@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { answerText } from "../../answer-text.js";
|
|
1
2
|
import { truncateCodePointPrefix, truncateCodePointSuffix } from "./text.js";
|
|
2
3
|
/** The customer-facing default: neutral, no leaked internals. */
|
|
3
4
|
export function defaultErrorMessage(failed) {
|
|
@@ -6,47 +7,67 @@ export function defaultErrorMessage(failed) {
|
|
|
6
7
|
: "⚠️ Sorry, something went wrong. Try rephrasing, or check I have access to what you need.";
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
|
-
* Customer-facing live-preview line for
|
|
10
|
+
* Customer-facing live-preview line for a harness-internal retry backoff (the advisory `retrying` event): neutral, no
|
|
10
11
|
* leaked internals — the reason stays in operator logs.
|
|
11
12
|
*/
|
|
12
13
|
export const RETRY_NOTICE = "⏳ Temporary problem — retrying…";
|
|
13
14
|
/** The placeholder shown before any reasoning/tool/text arrives. */
|
|
14
15
|
export const THINKING_PLACEHOLDER = "💭 Thinking…";
|
|
15
16
|
export function createTurnView() {
|
|
16
|
-
|
|
17
|
+
const view = {
|
|
18
|
+
thinking: "",
|
|
19
|
+
tools: [],
|
|
20
|
+
toolById: new Map(),
|
|
21
|
+
answer: "",
|
|
22
|
+
textOf: answerText(),
|
|
23
|
+
retrying: false,
|
|
24
|
+
};
|
|
25
|
+
return view;
|
|
17
26
|
}
|
|
27
|
+
/** Fold one event of a turn's invoke into its view; true when what a renderer shows changed. */
|
|
18
28
|
export function applyTurnEvent(view, e, now = Date.now()) {
|
|
19
29
|
const closedRetry = view.retrying && e.type !== "retrying";
|
|
20
30
|
if (closedRetry)
|
|
21
31
|
view.retrying = false;
|
|
32
|
+
const appended = (text) => {
|
|
33
|
+
if (text === "")
|
|
34
|
+
return closedRetry;
|
|
35
|
+
view.answer += text;
|
|
36
|
+
if (view.answerSince === undefined && view.answer.trim() !== "")
|
|
37
|
+
view.answerSince = now;
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
22
40
|
switch (e.type) {
|
|
23
|
-
case "
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return true;
|
|
28
|
-
case "thinking":
|
|
29
|
-
view.thinking += e.delta;
|
|
41
|
+
case "delta":
|
|
42
|
+
if (e.channel === "text")
|
|
43
|
+
return appended(view.textOf(e));
|
|
44
|
+
view.thinking += e.text;
|
|
30
45
|
return true;
|
|
31
46
|
case "tool_started": {
|
|
32
47
|
const arg = summarizeToolArgs(e.args);
|
|
33
48
|
const name = humanizeToolName(e.name);
|
|
34
49
|
const line = { label: arg ? `${name} ${arg}` : name, status: "running" };
|
|
35
50
|
view.tools.push(line);
|
|
36
|
-
view.toolById.set(e.
|
|
51
|
+
view.toolById.set(e.toolCallId, line);
|
|
37
52
|
return true;
|
|
38
53
|
}
|
|
39
54
|
case "tool_progress": {
|
|
40
|
-
const line = view.toolById.get(e.
|
|
55
|
+
const line = view.toolById.get(e.toolCallId);
|
|
41
56
|
if (line?.status !== "running")
|
|
42
57
|
return closedRetry;
|
|
43
58
|
line.progress = e.text;
|
|
44
59
|
return true;
|
|
45
60
|
}
|
|
46
|
-
case "
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
case "entry": {
|
|
62
|
+
if (e.entry.kind === "assistant")
|
|
63
|
+
return appended(view.textOf(e));
|
|
64
|
+
if (e.entry.kind !== "tool_result")
|
|
65
|
+
return closedRetry;
|
|
66
|
+
const data = e.entry.data;
|
|
67
|
+
const line = data.toolCallId === undefined ? undefined : view.toolById.get(data.toolCallId);
|
|
68
|
+
if (!line)
|
|
69
|
+
return closedRetry;
|
|
70
|
+
line.status = data.isError === true ? "error" : "ok";
|
|
50
71
|
return true;
|
|
51
72
|
}
|
|
52
73
|
case "retrying":
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** A channel's reads of its rooms' threads. Each resolves to text for the model; a failed read rejects. */
|
|
2
|
+
export interface ThreadReader {
|
|
3
|
+
/** The room's recent threads, newest first, each with its id. */
|
|
4
|
+
list(room: string): Promise<string>;
|
|
5
|
+
/** One thread of the room, as it reads now. Rejects for a thread that is not in this room. */
|
|
6
|
+
read(room: string, threadId: string): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
/** The rooms of a channel's running group turns, by session id, and how to read their threads. */
|
|
9
|
+
export interface RoomThreads {
|
|
10
|
+
/** The turn of `sessionId` runs in `room` until the returned function is called. */
|
|
11
|
+
enter(sessionId: string, room: string): () => void;
|
|
12
|
+
readonly reader: ThreadReader;
|
|
13
|
+
roomOf(sessionId: string): string | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare function createRoomThreads(reader: ThreadReader): RoomThreads;
|
|
16
|
+
export declare function registerRoomThreads(kind: string, stateDir: string, rooms: RoomThreads): void;
|
|
17
|
+
/** The threads the turn running in `sessionId` may read, from the `kind` channel it was asked through. */
|
|
18
|
+
export declare function roomThreads(kind: string, sessionId: string | undefined): {
|
|
19
|
+
list(): Promise<string>;
|
|
20
|
+
read(threadId: string): Promise<string>;
|
|
21
|
+
};
|
|
22
|
+
/** One thread of a room, as a list shows it. */
|
|
23
|
+
export interface ThreadSummary {
|
|
24
|
+
id: string;
|
|
25
|
+
/**
|
|
26
|
+
* The one time a list can vouch for, in milliseconds since the epoch: its newest message, or only when it started
|
|
27
|
+
* where its replies are not visible (a Feishu ordinary group lists a thread's root only).
|
|
28
|
+
*/
|
|
29
|
+
time: {
|
|
30
|
+
at: number;
|
|
31
|
+
is: "last active" | "started";
|
|
32
|
+
};
|
|
33
|
+
/** When the platform says (Slack); Feishu's listing does not. */
|
|
34
|
+
replies?: number;
|
|
35
|
+
/** Its first message the read saw: the thread's opener, wherever the platform lists it. */
|
|
36
|
+
first: {
|
|
37
|
+
label: string;
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The list a tool returns: newest first by the time each line shows, each with the id that reads it, and what was
|
|
43
|
+
* left out said. A thread shown by when it started may have gone on since, and the list says so, since whether a
|
|
44
|
+
* problem was resolved is what a reader most often wants to know.
|
|
45
|
+
*/
|
|
46
|
+
export declare function threadList(threads: readonly ThreadSummary[], scanned: string): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a thread-reading tool may reach: the threads of the room its turn was asked in, and nothing else
|
|
3
|
+
* (docs/design/place-history.md, #374).
|
|
4
|
+
*
|
|
5
|
+
* The room is never an argument. A model can be asked to read any chat ("what did they say in the other group?"), and
|
|
6
|
+
* the platform lets the bot read every chat it is in, so a room the tool were told would let one chat read another.
|
|
7
|
+
* The channel names the room instead: while a group turn runs, it registers that turn's room under the turn's session,
|
|
8
|
+
* and the tool, asking with its own session (`sessionIdOf`), gets that room or nothing. A place's turns run one at a
|
|
9
|
+
* time, so one session has one running turn and one room.
|
|
10
|
+
*/
|
|
11
|
+
import { truncateCodePointPrefix } from "./text.js";
|
|
12
|
+
export function createRoomThreads(reader) {
|
|
13
|
+
const rooms = new Map();
|
|
14
|
+
return {
|
|
15
|
+
enter(session, room) {
|
|
16
|
+
rooms.set(session, room);
|
|
17
|
+
return () => {
|
|
18
|
+
if (rooms.get(session) === room)
|
|
19
|
+
rooms.delete(session);
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
reader,
|
|
23
|
+
roomOf: (session) => rooms.get(session),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Per channel kind, per mounted channel (its `stateDir`): that channel's rooms. A remount of the channel replaces its
|
|
28
|
+
* entry, and two channel files of one kind keep one each. A session id names its storage, and a room is entered by the
|
|
29
|
+
* turn that runs in it, so a tool's session finds its own channel among every one of its kind in the process.
|
|
30
|
+
*/
|
|
31
|
+
const mounted = new Map();
|
|
32
|
+
export function registerRoomThreads(kind, stateDir, rooms) {
|
|
33
|
+
const byChannel = mounted.get(kind) ?? new Map();
|
|
34
|
+
byChannel.set(stateDir, rooms);
|
|
35
|
+
mounted.set(kind, byChannel);
|
|
36
|
+
}
|
|
37
|
+
/** The threads the turn running in `sessionId` may read, from the `kind` channel it was asked through. */
|
|
38
|
+
export function roomThreads(kind, sessionId) {
|
|
39
|
+
const serving = [...(mounted.get(kind)?.values() ?? [])];
|
|
40
|
+
if (serving.length === 0) {
|
|
41
|
+
throw new Error(`no ${kind} channel is serving in this process: threads are read only in a chat turn`);
|
|
42
|
+
}
|
|
43
|
+
const rooms = sessionId === undefined ? undefined : serving.find((each) => each.roomOf(sessionId) !== undefined);
|
|
44
|
+
const room = sessionId === undefined ? undefined : rooms?.roomOf(sessionId);
|
|
45
|
+
if (rooms === undefined || room === undefined) {
|
|
46
|
+
throw new Error(`this turn was not asked in a ${kind} group chat: threads are read only from the group a turn was asked in`);
|
|
47
|
+
}
|
|
48
|
+
return { list: () => rooms.reader.list(room), read: (threadId) => rooms.reader.read(room, threadId) };
|
|
49
|
+
}
|
|
50
|
+
/** Threads a list shows; a vague question reads every one listed, so the list is short. */
|
|
51
|
+
const MAX_THREADS = 20;
|
|
52
|
+
const PREVIEW_CHARS = 160;
|
|
53
|
+
/**
|
|
54
|
+
* The list a tool returns: newest first by the time each line shows, each with the id that reads it, and what was
|
|
55
|
+
* left out said. A thread shown by when it started may have gone on since, and the list says so, since whether a
|
|
56
|
+
* problem was resolved is what a reader most often wants to know.
|
|
57
|
+
*/
|
|
58
|
+
export function threadList(threads, scanned) {
|
|
59
|
+
if (threads.length === 0)
|
|
60
|
+
return `No threads among ${scanned}.`;
|
|
61
|
+
const shown = [...threads].sort((a, b) => b.time.at - a.time.at).slice(0, MAX_THREADS);
|
|
62
|
+
const when = (ms) => `${new Date(ms).toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
63
|
+
const lines = shown.map((thread) => {
|
|
64
|
+
const meta = [
|
|
65
|
+
thread.replies === undefined ? undefined : `${thread.replies} repl${thread.replies === 1 ? "y" : "ies"}`,
|
|
66
|
+
`${thread.time.is} ${when(thread.time.at)}`,
|
|
67
|
+
]
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.join(", ");
|
|
70
|
+
const text = truncateCodePointPrefix(thread.first.text.replace(/\s+/g, " ").trim(), PREVIEW_CHARS, "…");
|
|
71
|
+
return `- thread ${thread.id} (${meta}): ${thread.first.label}: ${text}`;
|
|
72
|
+
});
|
|
73
|
+
const more = threads.length - shown.length;
|
|
74
|
+
const startedOnly = shown.some((thread) => thread.time.is === "started");
|
|
75
|
+
return [
|
|
76
|
+
`Threads in this room, newest first by the time shown (among ${scanned}). Pass a thread's id to read it.`,
|
|
77
|
+
...(startedOnly ? ["A thread shown by when it started may have later replies this list cannot see: read it."] : []),
|
|
78
|
+
...lines,
|
|
79
|
+
...(more > 0 ? [`(${more} more not listed)`] : []),
|
|
80
|
+
].join("\n");
|
|
81
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Best-effort bounded durable dedup ring, recorded only after the caller's pre-ACK side effect is durable. */
|
|
2
2
|
import { log } from "../../log.js";
|
|
3
|
-
import { loadStateFile, saveStateFile } from "
|
|
3
|
+
import { loadStateFile, saveStateFile } from "../../state-file.js";
|
|
4
4
|
export function createSeenRing(path, label, cap = 2000) {
|
|
5
5
|
const raw = loadStateFile(path);
|
|
6
6
|
const order = raw === undefined
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Agent } from "../../agent-api.ts";
|
|
2
2
|
/** Bare stop word for summon-body matching (Slack/Feishu); Telegram uses its native `/stop` command. */
|
|
3
3
|
export declare function isStopText(text: string): boolean;
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Dispatch `cancel` for the place's session (none: nothing was ever said there) and map its answer to the
|
|
6
|
+
* customer-facing line: the asker is told, and the failure goes to the log.
|
|
7
|
+
*/
|
|
8
|
+
export declare function dispatchStop(agent: Pick<Agent, "sessions">, sessionId: string | undefined, label: string): Promise<string>;
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SHARED user-facing stop command: the chat channels (telegram, feishu/lark, slack) map an explicit "stop" ask onto
|
|
3
|
-
* the session
|
|
3
|
+
* the session's `cancel`.
|
|
4
4
|
*/
|
|
5
5
|
import { log } from "../../log.js";
|
|
6
|
-
import {
|
|
6
|
+
import { AgentError } from "../../agent-api.js";
|
|
7
7
|
/** Bare stop word for summon-body matching (Slack/Feishu); Telegram uses its native `/stop` command. */
|
|
8
8
|
export function isStopText(text) {
|
|
9
9
|
return /^(stop|cancel)[.!]?$/i.test(text.trim());
|
|
10
10
|
}
|
|
11
11
|
const STOPPED_NOTICE = "⏹ Stopped.";
|
|
12
12
|
const NOTHING_RUNNING_NOTICE = "Nothing is running.";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const UNKNOWN_NOTICE = "⚠️ Could not tell whether it stopped — check again in a moment.";
|
|
14
|
+
/**
|
|
15
|
+
* Dispatch `cancel` for the place's session (none: nothing was ever said there) and map its answer to the
|
|
16
|
+
* customer-facing line: the asker is told, and the failure goes to the log.
|
|
17
|
+
*/
|
|
18
|
+
export async function dispatchStop(agent, sessionId, label) {
|
|
19
|
+
if (sessionId === undefined)
|
|
20
|
+
return NOTHING_RUNNING_NOTICE;
|
|
20
21
|
try {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
return STOPPED_NOTICE;
|
|
24
|
-
if (result.error.code === NO_ACTIVE_RUN_CODE)
|
|
25
|
-
return NOTHING_RUNNING_NOTICE;
|
|
26
|
-
log.warn(`${label} stop rejected for ${session}: ${result.error.code} — ${result.error.message}`);
|
|
27
|
-
return `⚠️ Could not stop (${result.error.code}).`;
|
|
22
|
+
const { stopped } = await agent.sessions.cancel(sessionId);
|
|
23
|
+
return stopped.length > 0 ? STOPPED_NOTICE : NOTHING_RUNNING_NOTICE;
|
|
28
24
|
}
|
|
29
25
|
catch (error) {
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
if (error instanceof AgentError && error.code === "not_found")
|
|
27
|
+
return NOTHING_RUNNING_NOTICE;
|
|
28
|
+
const code = error instanceof AgentError ? error.code : undefined;
|
|
29
|
+
log.warn(`${label} stop failed for ${sessionId}: ${code ?? "error"} — ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
// The storage could not answer, or the call broke: the stop may have been recorded all the same (SPEC.md §4).
|
|
31
|
+
if (code === undefined || code === "unavailable")
|
|
32
|
+
return UNKNOWN_NOTICE;
|
|
33
|
+
return `⚠️ Could not stop (${code}).`;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a NEW thread starts from (participant-model.md §5, architecture.md §2): a fork of the room's session at
|
|
3
|
+
* the message the thread's reply chain names, or at the room's present when nothing in it names one. A thread that
|
|
4
|
+
* already exists is left alone.
|
|
5
|
+
*
|
|
6
|
+
* A user's chat message names the message it became by its key: a channel sends with the platform's message id as
|
|
7
|
+
* the `idempotencyKey`, so `messages.get({ sessionId: room, idempotencyKey })` finds it. The thread forks at that
|
|
8
|
+
* message's answer; at its own entry when it has none (still running, or ended without one); at the room's present
|
|
9
|
+
* when it is still queued, or was never taken, since it is not in the history.
|
|
10
|
+
*/
|
|
11
|
+
import { type Agent } from "../../agent-api.ts";
|
|
12
|
+
import type { ForkSource } from "../../channel.ts";
|
|
13
|
+
/**
|
|
14
|
+
* Where a new thread's session forks from: its room's session at the message the thread's reply chain names, or at the
|
|
15
|
+
* room's present when nothing in it names one. What `sessionFor`'s `forkFrom` answers for a thread.
|
|
16
|
+
*/
|
|
17
|
+
export declare function threadForkSource(agent: Agent, room: string, chain: readonly string[]): Promise<ForkSource>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a NEW thread starts from (participant-model.md §5, architecture.md §2): a fork of the room's session at
|
|
3
|
+
* the message the thread's reply chain names, or at the room's present when nothing in it names one. A thread that
|
|
4
|
+
* already exists is left alone.
|
|
5
|
+
*
|
|
6
|
+
* A user's chat message names the message it became by its key: a channel sends with the platform's message id as
|
|
7
|
+
* the `idempotencyKey`, so `messages.get({ sessionId: room, idempotencyKey })` finds it. The thread forks at that
|
|
8
|
+
* message's answer; at its own entry when it has none (still running, or ended without one); at the room's present
|
|
9
|
+
* when it is still queued, or was never taken, since it is not in the history.
|
|
10
|
+
*/
|
|
11
|
+
import { AgentError } from "../../agent-api.js";
|
|
12
|
+
/** A reply chain is ids walked from the platform, each one a `messages.get`: bounded like ids, not payloads. */
|
|
13
|
+
const MAX_CHAIN = 16;
|
|
14
|
+
const MAX_ID_CHARS = 128;
|
|
15
|
+
/** The entry a thread forks at for the message a chain names; undefined forks at the room's present. */
|
|
16
|
+
function forkPoint(message) {
|
|
17
|
+
if (message.status === "queued")
|
|
18
|
+
return undefined;
|
|
19
|
+
return message.answer?.entryId ?? message.entryId;
|
|
20
|
+
}
|
|
21
|
+
/** Where the first message the chain names (nearest first) puts the fork; undefined, the room's present. */
|
|
22
|
+
async function branchPoint(agent, room, chain) {
|
|
23
|
+
const usable = chain.filter((id) => id.length > 0 && id.length <= MAX_ID_CHARS).slice(0, MAX_CHAIN);
|
|
24
|
+
for (const idempotencyKey of usable) {
|
|
25
|
+
const message = await agent.messages.get({ sessionId: room, idempotencyKey }).catch((error) => {
|
|
26
|
+
// A platform message the agent was never sent (another bot's, or one before the agent joined) names nothing.
|
|
27
|
+
if (error instanceof AgentError && error.code === "not_found")
|
|
28
|
+
return undefined;
|
|
29
|
+
throw error;
|
|
30
|
+
});
|
|
31
|
+
if (message)
|
|
32
|
+
return forkPoint(message);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Where a new thread's session forks from: its room's session at the message the thread's reply chain names, or at the
|
|
38
|
+
* room's present when nothing in it names one. What `sessionFor`'s `forkFrom` answers for a thread.
|
|
39
|
+
*/
|
|
40
|
+
export async function threadForkSource(agent, room, chain) {
|
|
41
|
+
const at = await branchPoint(agent, room, chain);
|
|
42
|
+
return { sessionId: room, ...(at === undefined ? {} : { entryId: at }) };
|
|
43
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* that session's memory, so the two cannot be keyed independently.
|
|
5
5
|
*/
|
|
6
6
|
import { log } from "../../log.js";
|
|
7
|
-
import { loadStateFile, saveStateFile } from "
|
|
7
|
+
import { loadStateFile, saveStateFile } from "../../state-file.js";
|
|
8
8
|
const MAX_THREADS = 1000;
|
|
9
9
|
/** Cap on remembered humans per thread. */
|
|
10
10
|
const MAX_HUMANS = 2;
|
|
@@ -24,3 +24,8 @@ export interface CallOptions {
|
|
|
24
24
|
}
|
|
25
25
|
/** A write the next one supersedes: never wait out a rate limit for it. */
|
|
26
26
|
export declare const DROPPABLE_FRAME: CallOptions;
|
|
27
|
+
/**
|
|
28
|
+
* A read that only adds context to a turn (a place's history, its speakers' names): the turn runs without it and says
|
|
29
|
+
* so, and the asker sees nothing until it ends, so it never waits out a rate limit either.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CONTEXT_READ: CallOptions;
|
|
@@ -18,3 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
/** A write the next one supersedes: never wait out a rate limit for it. */
|
|
20
20
|
export const DROPPABLE_FRAME = { retries: 0 };
|
|
21
|
+
/**
|
|
22
|
+
* A read that only adds context to a turn (a place's history, its speakers' names): the turn runs without it and says
|
|
23
|
+
* so, and the asker sees nothing until it ends, so it never waits out a rate limit either.
|
|
24
|
+
*/
|
|
25
|
+
export const CONTEXT_READ = { retries: 0 };
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import type * as Scope from "effect/Scope";
|
|
4
4
|
import { type PortFailure } from "../../effect-port.ts";
|
|
5
|
-
import type {
|
|
5
|
+
import type { DiscussionSource } from "./place-history.ts";
|
|
6
6
|
import { type TurnRecordBase, type TurnStore } from "./turn-store.ts";
|
|
7
7
|
/**
|
|
8
8
|
* A pending turn is the persisted intent minus its attempt count, plus live-only fields the channel adds (a notice's
|
|
9
9
|
* message id).
|
|
10
10
|
*/
|
|
11
11
|
export type PendingBase<S extends TurnRecordBase> = Omit<S, "attempts">;
|
|
12
|
-
export interface TurnRunnerOptions<R extends PendingBase<S>, S extends TurnRecordBase, E> {
|
|
12
|
+
export interface TurnRunnerOptions<R extends PendingBase<S>, S extends TurnRecordBase, E, P = string> {
|
|
13
13
|
label: string;
|
|
14
14
|
store: TurnStore<S>;
|
|
15
|
-
|
|
15
|
+
/** What a turn folds beyond its own ask: the context buffer, or the place's history read from the platform. */
|
|
16
|
+
discussion: DiscussionSource<E, P>;
|
|
16
17
|
/** Delivery dedup by platform id, recorded post-persist (Slack, Feishu). */
|
|
17
18
|
seen?: {
|
|
18
19
|
add(id: string): void;
|
|
@@ -21,8 +22,8 @@ export interface TurnRunnerOptions<R extends PendingBase<S>, S extends TurnRecor
|
|
|
21
22
|
toStored(rec: R): S;
|
|
22
23
|
/** A recovered intent as a pending turn — live-only fields start absent. */
|
|
23
24
|
fromStored(stored: S): R;
|
|
24
|
-
/** The
|
|
25
|
-
|
|
25
|
+
/** The place this turn folds the discussion of (a buffer bucket, or a place to read); undefined folds nothing. */
|
|
26
|
+
discussionKey(rec: R): P | undefined;
|
|
26
27
|
/** The place, for the lifecycle log line (`chat=… thread=…`). */
|
|
27
28
|
where(rec: R): string;
|
|
28
29
|
/** Queue feedback when a turn is scheduled BEHIND an active one. */
|
|
@@ -50,7 +51,7 @@ export interface TurnRunnerOptions<R extends PendingBase<S>, S extends TurnRecor
|
|
|
50
51
|
* preview — the one the answer was written for died with its process.
|
|
51
52
|
*
|
|
52
53
|
* A notice THIS process put up is a different thing and must still be settled: a record recovered behind another
|
|
53
|
-
* turn in the same
|
|
54
|
+
* turn in the same conversation goes through `onQueuedBehind` like any other, so it can hold a fresh "queued" message
|
|
54
55
|
* or status that only this call can take over or clear.
|
|
55
56
|
*/
|
|
56
57
|
deliverAnswer(rec: R, answer: string): Effect.Effect<void, PortFailure, Scope.Scope>;
|
|
@@ -66,8 +67,8 @@ export interface TurnRunner<R, S> {
|
|
|
66
67
|
/** Resolve once no turn is in flight — the test/observability seam. */
|
|
67
68
|
idle(): Promise<void>;
|
|
68
69
|
}
|
|
69
|
-
export declare function runQueuedTurn<R extends PendingBase<S>, S extends TurnRecordBase, E>(options: TurnRunnerOptions<R, S, E>, rec: R): Effect.Effect<void, PortFailure>;
|
|
70
|
+
export declare function runQueuedTurn<R extends PendingBase<S>, S extends TurnRecordBase, E, P = string>(options: TurnRunnerOptions<R, S, E, P>, rec: R): Effect.Effect<void, PortFailure>;
|
|
70
71
|
export declare function createTurnRunner<R extends PendingBase<S> & {
|
|
71
72
|
id: string;
|
|
72
|
-
|
|
73
|
-
}, S extends TurnRecordBase, E>(options: TurnRunnerOptions<R, S, E>): TurnRunner<R, S>;
|
|
73
|
+
conversation: string;
|
|
74
|
+
}, S extends TurnRecordBase, E, P = string>(options: TurnRunnerOptions<R, S, E, P>): TurnRunner<R, S>;
|
|
@@ -3,11 +3,11 @@ import * as Effect from "effect/Effect";
|
|
|
3
3
|
import * as Fiber from "effect/Fiber";
|
|
4
4
|
import { log } from "../../log.js";
|
|
5
5
|
import { portError, portJoin } from "../../effect-port.js";
|
|
6
|
-
import { createTurnQueue } from "
|
|
6
|
+
import { createTurnQueue } from "../turn-queue.js";
|
|
7
7
|
import { commitAnsweredTurn } from "./turn-store.js";
|
|
8
8
|
export function runQueuedTurn(options, rec) {
|
|
9
9
|
return Effect.gen(function* () {
|
|
10
|
-
const { label, store,
|
|
10
|
+
const { label, store, discussion: source, beforeRun } = options;
|
|
11
11
|
if (beforeRun && !(yield* portJoin(() => beforeRun(rec))))
|
|
12
12
|
return;
|
|
13
13
|
const decision = store.startAttempt(rec.id);
|
|
@@ -24,10 +24,10 @@ export function runQueuedTurn(options, rec) {
|
|
|
24
24
|
// The store has already said, at error level, that this turn will not be run again. Silence after that
|
|
25
25
|
// reads as "nothing happened"; this line is what distinguishes it from a delivered answer.
|
|
26
26
|
onSuccess: () => Effect.sync(() => log.info(`${label} turn ${rec.id} hit the execution ceiling, but the answer it had already recorded was ` +
|
|
27
|
-
`delivered (
|
|
27
|
+
`delivered (conversation=${rec.conversation})`)),
|
|
28
28
|
onFailure: (error) => Effect.sync(() => {
|
|
29
29
|
log.error(`${label} turn ${rec.id} hit the execution ceiling and its recorded answer could not be delivered ` +
|
|
30
|
-
`either (
|
|
30
|
+
`either (conversation=${rec.conversation}) — the answer is gone: ${String(error.cause)}`);
|
|
31
31
|
// The record is off disk, so nothing will retry this. The asker is owed an ending — without one, a
|
|
32
32
|
// queue notice this turn put up stays in the chat forever reading "Queued".
|
|
33
33
|
options.notifyDropped(rec);
|
|
@@ -45,30 +45,28 @@ export function runQueuedTurn(options, rec) {
|
|
|
45
45
|
let answered = recovered !== undefined;
|
|
46
46
|
// One stable prefix, so every `turn done:`/`turn failed:` has a `turn start:` to grep back to; what kind of work
|
|
47
47
|
// it is rides along as a field.
|
|
48
|
-
log.info(`${label} turn start: turn=${rec.id}
|
|
48
|
+
log.info(`${label} turn start: turn=${rec.id} conversation=${rec.conversation} ${options.where(rec)}` +
|
|
49
49
|
`${recovered === undefined ? "" : " mode=re-delivery (answer recovered from a prior run)"}`);
|
|
50
50
|
const work = recovered !== undefined
|
|
51
51
|
? Effect.suspend(() => options.deliverAnswer(rec, recovered))
|
|
52
52
|
: Effect.suspend(() => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
return options.execute(rec, discussion, (answer) => {
|
|
53
|
+
const key = options.discussionKey(rec);
|
|
54
|
+
// `peek` never rejects (DiscussionSource): a source that cannot read says so in the text.
|
|
55
|
+
return Effect.promise(async () => key === undefined ? { text: "", consumed: [] } : source.peek(key)).pipe(Effect.flatMap((discussion) => options.execute(rec, discussion, (answer) => {
|
|
56
56
|
// Only a recorded answer is recoverable — an untracked run has no record to keep it in.
|
|
57
|
-
answered =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
});
|
|
57
|
+
answered =
|
|
58
|
+
key === undefined
|
|
59
|
+
? store.answered(rec.id, answer)
|
|
60
|
+
: commitAnsweredTurn(store, source, { id: rec.id, key, consumed: discussion.consumed, answer });
|
|
61
|
+
})));
|
|
64
62
|
});
|
|
65
63
|
const delivered = yield* Effect.scoped(work).pipe(Effect.matchEffect({
|
|
66
64
|
onSuccess: () => Effect.sync(() => {
|
|
67
|
-
log.info(`${label} turn done: turn=${rec.id}
|
|
65
|
+
log.info(`${label} turn done: turn=${rec.id} conversation=${rec.conversation} (${Date.now() - startedAt}ms)`);
|
|
68
66
|
return true;
|
|
69
67
|
}),
|
|
70
68
|
onFailure: (error) => Effect.sync(() => {
|
|
71
|
-
log.error(`${label} turn failed: turn=${rec.id}
|
|
69
|
+
log.error(`${label} turn failed: turn=${rec.id} conversation=${rec.conversation} (${Date.now() - startedAt}ms): ${String(error.cause)}`);
|
|
72
70
|
return false;
|
|
73
71
|
}),
|
|
74
72
|
}));
|
|
@@ -79,7 +77,7 @@ export function runQueuedTurn(options, rec) {
|
|
|
79
77
|
store.remove(rec.id);
|
|
80
78
|
return;
|
|
81
79
|
}
|
|
82
|
-
log.warn(`${label} turn ${rec.id} produced an answer that was not delivered (
|
|
80
|
+
log.warn(`${label} turn ${rec.id} produced an answer that was not delivered (conversation=${rec.conversation}) — ` +
|
|
83
81
|
`keeping it to re-deliver on the next start`);
|
|
84
82
|
});
|
|
85
83
|
}
|
|
@@ -92,7 +90,7 @@ export function createTurnRunner(options) {
|
|
|
92
90
|
((rec) => {
|
|
93
91
|
const notice = onQueuedBehind(rec);
|
|
94
92
|
// Observe rejection at acceptance, even if dequeue is minutes away.
|
|
95
|
-
const done = Effect.runFork(portJoin(() => notice.done).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn(`${label} queue notice failed: turn=${rec.id}
|
|
93
|
+
const done = Effect.runFork(portJoin(() => notice.done).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn(`${label} queue notice failed: turn=${rec.id} conversation=${rec.conversation}: ${String(portError(cause))}`)))));
|
|
96
94
|
notices.set(rec.id, { done, cancel: notice.cancel });
|
|
97
95
|
}),
|
|
98
96
|
run: (rec) => Effect.gen(function* () {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DiscussionSource } from "./place-history.ts";
|
|
2
2
|
/** What every persisted turn record carries regardless of channel. */
|
|
3
3
|
export interface TurnRecordBase {
|
|
4
4
|
id: string;
|
|
5
|
-
|
|
5
|
+
/** The conversation the turn belongs to: what its queue and its session are kept by. */
|
|
6
|
+
conversation: string;
|
|
6
7
|
attempts: number;
|
|
7
8
|
/**
|
|
8
9
|
* The completed reply, recorded BEFORE it is delivered. Its presence is the difference between the two things a
|
|
@@ -49,9 +50,9 @@ export interface TurnStoreOptions<T extends TurnRecordBase> {
|
|
|
49
50
|
*
|
|
50
51
|
* Returns whether the answer became recoverable, so a caller does not claim a recovery that has nothing on disk.
|
|
51
52
|
*/
|
|
52
|
-
export declare function commitAnsweredTurn<T extends TurnRecordBase, E>(store: TurnStore<T>,
|
|
53
|
+
export declare function commitAnsweredTurn<T extends TurnRecordBase, E, P>(store: TurnStore<T>, discussion: Pick<DiscussionSource<E, P>, "commit">, turn: {
|
|
53
54
|
id: string;
|
|
54
|
-
|
|
55
|
+
key: P;
|
|
55
56
|
consumed: E[];
|
|
56
57
|
answer: string;
|
|
57
58
|
}): boolean;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* exactly-once story needs the different backend above.
|
|
8
8
|
*/
|
|
9
9
|
import { log } from "../../log.js";
|
|
10
|
-
import { loadStateFile, saveStateFile } from "
|
|
10
|
+
import { loadStateFile, saveStateFile } from "../../state-file.js";
|
|
11
11
|
/** How many times a turn may START without finishing before it is dropped rather than run again. */
|
|
12
12
|
const MAX_TURN_ATTEMPTS = 3;
|
|
13
13
|
/**
|
|
@@ -17,9 +17,9 @@ const MAX_TURN_ATTEMPTS = 3;
|
|
|
17
17
|
*
|
|
18
18
|
* Returns whether the answer became recoverable, so a caller does not claim a recovery that has nothing on disk.
|
|
19
19
|
*/
|
|
20
|
-
export function commitAnsweredTurn(store,
|
|
20
|
+
export function commitAnsweredTurn(store, discussion, turn) {
|
|
21
21
|
const recorded = store.answered(turn.id, turn.answer);
|
|
22
|
-
|
|
22
|
+
discussion.commit(turn.key, turn.consumed);
|
|
23
23
|
return recorded;
|
|
24
24
|
}
|
|
25
25
|
export function createTurnStore(path, opts) {
|
|
@@ -98,7 +98,7 @@ export function createTurnStore(path, opts) {
|
|
|
98
98
|
// State the fact, not a cause the counter can't prove: a turn killed mid-run every time bumps this whether IT
|
|
99
99
|
// poisoned the process or a deploy/OOM took it down each time.
|
|
100
100
|
log.error(`${label} dropping turn ${id} after starting ${rec.attempts} time(s) without finishing ` +
|
|
101
|
-
`(
|
|
101
|
+
`(conversation=${rec.conversation}) — it may be crashing the process, or was killed mid-run each time; it will not ` +
|
|
102
102
|
`be run again`);
|
|
103
103
|
turns.delete(id);
|
|
104
104
|
persistBestEffort("drop");
|
|
@@ -113,7 +113,7 @@ export function createTurnStore(path, opts) {
|
|
|
113
113
|
// RE-RUNS this turn.
|
|
114
114
|
turns.set(id, rec);
|
|
115
115
|
log.error(`${label} cannot persist turn ${id}'s attempt count — deferring it to the next start rather ` +
|
|
116
|
-
`than run it untracked (
|
|
116
|
+
`than run it untracked (conversation=${rec.conversation}): ${String(e)}`);
|
|
117
117
|
return "defer";
|
|
118
118
|
}
|
|
119
119
|
return "run";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Lark-international compatibility adapter. */
|
|
2
2
|
import type { ChannelModule, LongConnectionChannelModule } from "../../channel.ts";
|
|
3
3
|
import { type FeishuChannelOptions, type FeishuFailure, type FeishuMessage, type FeishuMessageEvent, type FeishuRoute, type FeishuWebSocketChannelOptions, defaultFeishuRoute } from "../feishu/feishu.ts";
|
|
4
|
+
import { type FeishuSubscriptionMode } from "../feishu/setup-mode.ts";
|
|
4
5
|
export type LarkChannelOptions = FeishuChannelOptions;
|
|
5
6
|
export type LarkWebSocketChannelOptions = FeishuWebSocketChannelOptions;
|
|
6
7
|
export type LarkFailure = FeishuFailure;
|
|
@@ -10,4 +11,6 @@ export type LarkRoute = FeishuRoute;
|
|
|
10
11
|
export declare const defaultLarkRoute: typeof defaultFeishuRoute;
|
|
11
12
|
export declare function larkEnvelope(event: LarkMessageEvent): string;
|
|
12
13
|
export declare function larkChannel(opts: LarkChannelOptions): ChannelModule;
|
|
14
|
+
/** How this process receives the Lark channel: `LARK_INGRESS`, else WebSocket under `dev` and webhook otherwise. */
|
|
15
|
+
export declare function larkIngress(): FeishuSubscriptionMode;
|
|
13
16
|
export declare function larkWebSocketChannel(opts: LarkWebSocketChannelOptions): LongConnectionChannelModule;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LARK_COMPAT_CLOUD } from "../feishu/cloud.js";
|
|
2
2
|
import { buildFeishuChannel, buildFeishuWebSocketChannel, defaultFeishuRoute, } from "../feishu/feishu.js";
|
|
3
3
|
import { cloudEnvelope } from "../feishu/parse.js";
|
|
4
|
+
import { feishuIngressFor } from "../feishu/setup-mode.js";
|
|
4
5
|
export const defaultLarkRoute = defaultFeishuRoute;
|
|
5
6
|
export function larkEnvelope(event) {
|
|
6
7
|
return cloudEnvelope(event, "lark");
|
|
@@ -8,6 +9,10 @@ export function larkEnvelope(event) {
|
|
|
8
9
|
export function larkChannel(opts) {
|
|
9
10
|
return buildFeishuChannel(LARK_COMPAT_CLOUD, opts, larkChannel.name);
|
|
10
11
|
}
|
|
12
|
+
/** How this process receives the Lark channel: `LARK_INGRESS`, else WebSocket under `dev` and webhook otherwise. */
|
|
13
|
+
export function larkIngress() {
|
|
14
|
+
return feishuIngressFor("lark");
|
|
15
|
+
}
|
|
11
16
|
export function larkWebSocketChannel(opts) {
|
|
12
17
|
return buildFeishuWebSocketChannel(LARK_COMPAT_CLOUD, opts, larkWebSocketChannel.name);
|
|
13
18
|
}
|