@fastagent-sh/fastagent 0.24.4 → 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 -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/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
|
@@ -12,7 +12,8 @@ export function eventStream(open, label) {
|
|
|
12
12
|
const close = iterator.return?.bind(iterator);
|
|
13
13
|
if (!close || exhausted)
|
|
14
14
|
return Effect.void;
|
|
15
|
-
|
|
15
|
+
// Logged, not raised: a failing release would make the consumer's typed failure a defect.
|
|
16
|
+
return Effect.promise(close).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn(`${label} source cleanup failed: ${String(Cause.squash(cause))}`))));
|
|
16
17
|
});
|
|
17
18
|
return Effect.gen(function* () {
|
|
18
19
|
const result = yield* Effect.tryPromise({
|
|
@@ -1,58 +1,57 @@
|
|
|
1
1
|
import * as Stream from "effect/Stream";
|
|
2
|
-
import { type Agent, type
|
|
2
|
+
import { type Agent, type AgentErrorData, type MessageStreamEvent, type SendRequest } from "../../agent-api.ts";
|
|
3
3
|
import { type PortFailure } from "../../effect-port.ts";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
* What a turn reads, in order: the message as it was recorded, or why there is none (`rejected`), then the events of
|
|
6
|
+
* its session that belong to it through its `done` (SPEC.md §6.2).
|
|
7
|
+
*/
|
|
8
|
+
export type TurnEvent = {
|
|
9
|
+
type: "rejected";
|
|
10
|
+
error: AgentErrorData;
|
|
11
|
+
} | MessageStreamEvent;
|
|
12
|
+
/** An image a channel read from its platform, as a message carries it. */
|
|
13
|
+
export interface ImageAttachment {
|
|
14
|
+
mimeType: string;
|
|
15
|
+
/** base64 */
|
|
16
|
+
data: string;
|
|
17
|
+
}
|
|
18
|
+
type MessageContent = Exclude<SendRequest["content"], string>;
|
|
19
|
+
/** A turn's text and images as the content of one message. */
|
|
20
|
+
export declare function promptContent(text: string, images: readonly ImageAttachment[] | undefined): MessageContent;
|
|
21
|
+
/**
|
|
22
|
+
* One turn, end to end: the platform inputs prepared, the session the turn's conversation talks in (created and
|
|
23
|
+
* recorded before anything is sent, conversation-sessions.ts), then the message sent and its answer read as it is written
|
|
24
|
+
* (`stream`).
|
|
9
25
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
26
|
+
* The message queues behind whatever runs in the session (a wake-up, a schedule): the channel's own turns never
|
|
27
|
+
* collide, its queue serializes them per conversation. Its key is the turn's, so a turn run again after a restart is the
|
|
28
|
+
* message it already sent: one that ended is read back, answer included.
|
|
29
|
+
*
|
|
30
|
+
* A failure to prepare is the turn's `rejected` event: nothing was sent. It must never throw out of the stream, since
|
|
31
|
+
* the turn's own settlement path is what tells the user; an exception here would take the reply with it.
|
|
13
32
|
*/
|
|
14
33
|
export declare function turnStream<R>(opts: {
|
|
15
34
|
agent: Agent;
|
|
16
35
|
label: string;
|
|
17
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* The session the turn's conversation talks in: the one it has, or one created and recorded before anything is
|
|
38
|
+
* sent. It gets the prepared inputs, since where a new thread forks from is what its reply chain names.
|
|
39
|
+
*/
|
|
40
|
+
session: (resolved: R) => Promise<string>;
|
|
41
|
+
/** While the turn runs, what its session reaches by its id (a thread-reading tool's room); released when it ends. */
|
|
42
|
+
enter?: (sessionId: string) => () => void;
|
|
43
|
+
/** Prepare this turn's inputs (download attachments, walk a reply chain). */
|
|
18
44
|
resolve: () => Promise<R>;
|
|
19
|
-
/** What the
|
|
45
|
+
/** What the prepared inputs make of the turn: what it asks, and the key it is known by. */
|
|
20
46
|
turn: (resolved: R) => {
|
|
21
|
-
|
|
22
|
-
|
|
47
|
+
content: MessageContent;
|
|
48
|
+
idempotencyKey: string;
|
|
23
49
|
};
|
|
24
|
-
/** Whether a failed
|
|
50
|
+
/** Whether a failed preparation is worth a platform redelivery. Stated per channel because it IS a
|
|
25
51
|
* platform question — Slack reads its API error's status, and a transport that cannot tell says
|
|
26
52
|
* yes, since a retried download is cheap next to an unanswered ask. */
|
|
27
53
|
retryableLoadFailure: (cause: unknown) => boolean;
|
|
28
|
-
|
|
29
|
-
}): Stream.Stream<AgentEvent, PortFailure>;
|
|
30
|
-
/** How the busy-wait paces: retry the invoke every `delayMs` while the session's lease is held by an
|
|
31
|
-
* EXTERNAL turn (a self-scheduled wake, a concurrent embedder invoke), up to `maxWaitMs` total. The
|
|
32
|
-
* channel's own turns never collide (the turn-queue serializes per session), so a busy reject here is
|
|
33
|
-
* always an outside holder — wait for it like a queued turn, instead of erroring at the user. */
|
|
34
|
-
export interface BusyRetry {
|
|
35
|
-
delayMs: number;
|
|
36
|
-
maxWaitMs: number;
|
|
37
|
-
}
|
|
38
|
-
export declare const DEFAULT_BUSY_RETRY: BusyRetry;
|
|
39
|
-
/**
|
|
40
|
-
* Stream one Agent turn with the shared busy-wait. The durable-commit point is NOT here: an answer becomes durable
|
|
41
|
-
* where its text is known and about to be written to the chat (the renderer's `onAnswered`), because "the model
|
|
42
|
-
* finished" and "the user has it" are different events. Source pulls remain demand-driven, including while final
|
|
43
|
-
* platform delivery runs.
|
|
44
|
-
*
|
|
45
|
-
* BUSY-WAIT: a `failed{code: session_busy}` FIRST event means an external turn holds this session's
|
|
46
|
-
* lease and OUR turn never started — replay-safe. Retry (bounded) instead of yielding it: the user
|
|
47
|
-
* sees the channel's "Thinking…" placeholder while waiting (the mirror of the scheduler deferring a
|
|
48
|
-
* wake INTO a busy session), and only an exhausted wait surfaces the busy failure. Only a FIRST-event
|
|
49
|
-
* busy retries — a fail-fast reject is the only shape the engine emits it in, so nothing that started
|
|
50
|
-
* is ever re-run.
|
|
51
|
-
*/
|
|
52
|
-
export declare function busyRetryStream(agent: Agent, scope: Scope, prompt: Prompt, { label, busyRetry }: {
|
|
53
|
-
label: string;
|
|
54
|
-
busyRetry?: BusyRetry;
|
|
55
|
-
}): Stream.Stream<AgentEvent, PortFailure>;
|
|
54
|
+
}): Stream.Stream<TurnEvent, PortFailure>;
|
|
56
55
|
/**
|
|
57
56
|
* Load the turn's BACKGROUND refs — what was folded in from earlier un-summoned discussion, not the
|
|
58
57
|
* ask itself. Per-ref degradation is the whole meaning of that tier: one expired file costs a warn
|
|
@@ -104,3 +103,4 @@ export declare function backgroundImagesManifest(primaryCount: number, refs: rea
|
|
|
104
103
|
* cap-skipped) — without it, the model holds fold references it silently cannot open and may
|
|
105
104
|
* pretend it read them. Neutral wording (platforms differ on WHY: expired file_ids, deleted files). */
|
|
106
105
|
export declare function missingAttachmentsNote(missing: number): string;
|
|
106
|
+
export {};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared pieces of the channels' invoke-turn modules (telegram/feishu/slack `invoke-turn.ts`) — the
|
|
3
|
-
* halves that are channel-independent, so a
|
|
3
|
+
* halves that are channel-independent, so a policy or prompt-wording change lands ONCE:
|
|
4
4
|
*
|
|
5
|
-
* - {@link turnStream}:
|
|
6
|
-
* EVENT rather than a thrown iteration
|
|
7
|
-
* - {@link busyRetryStream}: the busy-retry loop around `agent.invoke`;
|
|
5
|
+
* - {@link turnStream}: prepare the turn's inputs → ask the agent, with a preparation failure arriving as the
|
|
6
|
+
* invoke's `rejected` EVENT rather than a thrown iteration;
|
|
8
7
|
* - the prompt-suffix wording: {@link attachedFilesManifest}, {@link backgroundImagesManifest},
|
|
9
8
|
* {@link missingAttachmentsNote}, {@link attributedFileName}.
|
|
10
9
|
*
|
|
@@ -16,83 +15,75 @@
|
|
|
16
15
|
* message-scoped Feishu keys, Slack file objects) are real differences. The two TIERS' failure
|
|
17
16
|
* policies are not: see {@link loadBackground}.
|
|
18
17
|
*/
|
|
19
|
-
import * as Clock from "effect/Clock";
|
|
20
18
|
import * as Effect from "effect/Effect";
|
|
19
|
+
import * as Result from "effect/Result";
|
|
21
20
|
import * as Stream from "effect/Stream";
|
|
22
|
-
import {
|
|
23
|
-
import { eventStream } from "./event-stream.js";
|
|
24
|
-
import { portJoin } from "../../effect-port.js";
|
|
21
|
+
import { AgentError, } from "../../agent-api.js";
|
|
25
22
|
import { log } from "../../log.js";
|
|
23
|
+
import { eventStream } from "./event-stream.js";
|
|
24
|
+
import { portError, portJoin } from "../../effect-port.js";
|
|
25
|
+
/** A turn's text and images as the content of one message. */
|
|
26
|
+
export function promptContent(text, images) {
|
|
27
|
+
return [{ type: "text", text }, ...(images ?? []).map((image) => ({ type: "image", ...image }))];
|
|
28
|
+
}
|
|
26
29
|
/**
|
|
27
|
-
* One turn, end to end:
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* while agreeing on everything that is not.
|
|
30
|
+
* One turn, end to end: the platform inputs prepared, the session the turn's conversation talks in (created and
|
|
31
|
+
* recorded before anything is sent, conversation-sessions.ts), then the message sent and its answer read as it is written
|
|
32
|
+
* (`stream`).
|
|
31
33
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
34
|
+
* The message queues behind whatever runs in the session (a wake-up, a schedule): the channel's own turns never
|
|
35
|
+
* collide, its queue serializes them per conversation. Its key is the turn's, so a turn run again after a restart is the
|
|
36
|
+
* message it already sent: one that ended is read back, answer included.
|
|
37
|
+
*
|
|
38
|
+
* A failure to prepare is the turn's `rejected` event: nothing was sent. It must never throw out of the stream, since
|
|
39
|
+
* the turn's own settlement path is what tells the user; an exception here would take the reply with it.
|
|
35
40
|
*/
|
|
36
41
|
export function turnStream(opts) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
const rejected = (error) => Stream.succeed({ type: "rejected", error });
|
|
43
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
44
|
+
const prepared = yield* Effect.result(portJoin(opts.resolve));
|
|
45
|
+
if (Result.isFailure(prepared)) {
|
|
46
|
+
const { cause } = prepared.failure;
|
|
47
|
+
return rejected({
|
|
48
|
+
code: "unavailable",
|
|
49
|
+
message: `could not prepare the turn: ${String(cause)}`,
|
|
50
|
+
retryable: opts.retryableLoadFailure(cause),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const resolved = prepared.success;
|
|
54
|
+
// The Agent's own question from here on, so its own answer says whether a retry is worth it.
|
|
55
|
+
const session = yield* Effect.result(portJoin(() => opts.session(resolved)));
|
|
56
|
+
if (Result.isFailure(session))
|
|
57
|
+
return rejected(agentFailure("its session", session.failure.cause));
|
|
58
|
+
const sessionId = session.success;
|
|
59
|
+
const enter = opts.enter;
|
|
60
|
+
if (enter !== undefined)
|
|
61
|
+
yield* Effect.acquireRelease(Effect.sync(() => enter(sessionId)), (leave) => Effect.sync(leave));
|
|
62
|
+
const { content, idempotencyKey } = opts.turn(resolved);
|
|
63
|
+
const request = { sessionId, content, idempotencyKey, whenBusy: "queue" };
|
|
64
|
+
// A send the agent refused rejects the stream's first read: the turn's `rejected`, not a failure of its stream.
|
|
65
|
+
return eventStream(() => opts.agent.messages.stream(request), `${opts.label} (session=${sessionId})`).pipe(Stream.scoped, Stream.catchCause((cause) => {
|
|
66
|
+
const error = portError(cause);
|
|
67
|
+
return error instanceof AgentError ? rejected(agentFailure("its message", error)) : Stream.failCause(cause);
|
|
68
|
+
}));
|
|
69
|
+
}));
|
|
48
70
|
}
|
|
49
|
-
// Each retry is a lease-check-level reject (tryAcquire runs before the session is bound) — waiting is nearly
|
|
50
|
-
// free, and the loop exits within one delay of the holder finishing. So the cap is sized to outlast a
|
|
51
|
-
// real tool-using wake turn (minutes), not to be short: 10 min. CEILING: a holder that runs longer than
|
|
52
|
-
// this still surfaces the busy error to the user — the bound exists so a stuck lease can't hang a chat
|
|
53
|
-
// turn forever.
|
|
54
|
-
export const DEFAULT_BUSY_RETRY = { delayMs: 5_000, maxWaitMs: 600_000 };
|
|
55
71
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* platform delivery runs.
|
|
60
|
-
*
|
|
61
|
-
* BUSY-WAIT: a `failed{code: session_busy}` FIRST event means an external turn holds this session's
|
|
62
|
-
* lease and OUR turn never started — replay-safe. Retry (bounded) instead of yielding it: the user
|
|
63
|
-
* sees the channel's "Thinking…" placeholder while waiting (the mirror of the scheduler deferring a
|
|
64
|
-
* wake INTO a busy session), and only an exhausted wait surfaces the busy failure. Only a FIRST-event
|
|
65
|
-
* busy retries — a fail-fast reject is the only shape the engine emits it in, so nothing that started
|
|
66
|
-
* is ever re-run.
|
|
72
|
+
* A step of the Agent's that failed: its refusal as it gave it (an `AgentError`, with its own `retryable`), or a
|
|
73
|
+
* failure nothing was sent past, so worth a retry. A send that may have been recorded (`unconfirmed`) is worth one too:
|
|
74
|
+
* the turn's send carries its key, so sending it again finds the message rather than recording a second.
|
|
67
75
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
retryBusy = true;
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
first = false;
|
|
83
|
-
return true;
|
|
84
|
-
})), Stream.scoped,
|
|
85
|
-
// concat closes the attempt's scope (and its iterator) before the wait or next invoke.
|
|
86
|
-
Stream.concat(Stream.suspend(() => retryBusy
|
|
87
|
-
? Stream.unwrap(Effect.gen(function* () {
|
|
88
|
-
log.info(`${label} session ${scope.session} is busy (an external turn holds it) — retrying in ${busyRetry.delayMs}ms`);
|
|
89
|
-
yield* Effect.sleep(busyRetry.delayMs);
|
|
90
|
-
return attempt();
|
|
91
|
-
}))
|
|
92
|
-
: Stream.empty)));
|
|
93
|
-
});
|
|
94
|
-
return attempt();
|
|
95
|
-
}));
|
|
76
|
+
function agentFailure(what, cause) {
|
|
77
|
+
if (cause instanceof AgentError) {
|
|
78
|
+
const { code, message, retryable, details } = cause;
|
|
79
|
+
return {
|
|
80
|
+
code,
|
|
81
|
+
message: `could not start ${what}: ${message}`,
|
|
82
|
+
retryable: retryable || code === "unconfirmed",
|
|
83
|
+
...(details === undefined ? {} : { details }),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { code: "unavailable", message: `could not start ${what}: ${String(cause)}`, retryable: true };
|
|
96
87
|
}
|
|
97
88
|
/**
|
|
98
89
|
* Load the turn's BACKGROUND refs — what was folded in from earlier un-summoned discussion, not the
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a turn's discussion comes from. `peek` snapshots what this turn folds; `commit` runs when the turn's answer
|
|
3
|
+
* is recorded, so a failure or crash before then leaves the discussion to be folded again. `P` names what is read: a
|
|
4
|
+
* buffer bucket's key, or a place plus where this turn's read ends.
|
|
5
|
+
*/
|
|
6
|
+
export interface DiscussionSource<E, P = string> {
|
|
7
|
+
/** Never rejects: a source that cannot read says so in `text`, and the turn proceeds without it. */
|
|
8
|
+
peek(place: P): {
|
|
9
|
+
text: string;
|
|
10
|
+
consumed: E[];
|
|
11
|
+
} | Promise<{
|
|
12
|
+
text: string;
|
|
13
|
+
consumed: E[];
|
|
14
|
+
}>;
|
|
15
|
+
commit(place: P, consumed: E[]): void;
|
|
16
|
+
}
|
|
17
|
+
/** One message of a place, as the fold renders it. */
|
|
18
|
+
export interface PlaceMessage {
|
|
19
|
+
id: string;
|
|
20
|
+
/** Milliseconds since the epoch. */
|
|
21
|
+
at: number;
|
|
22
|
+
/** "self": this agent, outside its own turn answers (a schedule's digest, a post from another chat). */
|
|
23
|
+
from: {
|
|
24
|
+
kind: "human" | "self" | "bot";
|
|
25
|
+
label: string;
|
|
26
|
+
};
|
|
27
|
+
text: string;
|
|
28
|
+
replyTo?: string;
|
|
29
|
+
images: {
|
|
30
|
+
key: string;
|
|
31
|
+
}[];
|
|
32
|
+
files: {
|
|
33
|
+
key: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
}[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* What a turn reads: its place, up to and including its own ask. The bound is what keeps every later message out —
|
|
39
|
+
* an ask queued behind this one is its own turn, and folding it here would answer it twice — and it needs no record
|
|
40
|
+
* of which messages were asks, so a lost or cold state cannot break it. `C` is the platform's position: the newest
|
|
41
|
+
* message a read reached.
|
|
42
|
+
*/
|
|
43
|
+
export interface PlaceRead<C> {
|
|
44
|
+
key: string;
|
|
45
|
+
until: C;
|
|
46
|
+
}
|
|
47
|
+
/** One turn's read: where it reached, what it folded (whose attachments ride along), and which outputs it passed. */
|
|
48
|
+
export interface PlaceDiscussion<C> {
|
|
49
|
+
/** Absent when the read failed: the cursor then stays, and the next turn reads the same messages again. */
|
|
50
|
+
cursor?: C;
|
|
51
|
+
folded: PlaceMessage[];
|
|
52
|
+
outputsPassed: string[];
|
|
53
|
+
}
|
|
54
|
+
/** What a platform read of one place returns. */
|
|
55
|
+
export interface PlaceListing<C> {
|
|
56
|
+
/** Every message id the read covered, kept or dropped: what tells which recorded outputs it passed. */
|
|
57
|
+
covered: string[];
|
|
58
|
+
/** What to fold, oldest first: system messages and what `drop` named already left out. */
|
|
59
|
+
messages: PlaceMessage[];
|
|
60
|
+
/** The newest message listed. A bounded read's cursor is its bound instead, the ask it read up to. */
|
|
61
|
+
newest?: C;
|
|
62
|
+
/** Older messages exist that the read never reached. */
|
|
63
|
+
earlier: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface PlaceHistory<C> extends DiscussionSource<PlaceDiscussion<C>, PlaceRead<C>> {
|
|
66
|
+
/** A message a turn posted into this place: in the session already, so the place's next read leaves it out. */
|
|
67
|
+
recordOutput(key: string, messageId: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* A thread's room, read-only, for the thread's first turn (participant-model §8): the room's own next answered turn
|
|
70
|
+
* still reads it, so each place takes the discussion into its own memory.
|
|
71
|
+
*/
|
|
72
|
+
room(key: string): Promise<{
|
|
73
|
+
text: string;
|
|
74
|
+
folded: PlaceMessage[];
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* A place as it reads now, for a tool: its newest messages, with no cursor and nothing left out (another session's
|
|
78
|
+
* asks and answers are what that place said). Rejects when the read fails: a tool's failure is the model's to see.
|
|
79
|
+
*/
|
|
80
|
+
snapshot(key: string): Promise<{
|
|
81
|
+
text: string;
|
|
82
|
+
folded: PlaceMessage[];
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A place's history read from its platform (docs/design/place-history.md): what was said in a chat or a thread since
|
|
87
|
+
* this agent last answered there, up to the turn's own ask. The platform owns the read; this owns what a place
|
|
88
|
+
* remembers between turns — its cursor, and the messages turns posted there — and what a turn leaves out of its read:
|
|
89
|
+
* the messages a turn posted into the place (`recordOutput`: answers, queue notices, stop feedback), kept beside the
|
|
90
|
+
* cursor until a read has passed them, so a busy deployment cannot push a quiet place's last answer out of a shared
|
|
91
|
+
* ring; and, as prompt shaping only, what the channel took as input (`isTurnInput`). What the agent sent itself from a
|
|
92
|
+
* send tool stays — that is what a later "what did point 3 mean?" is about.
|
|
93
|
+
*/
|
|
94
|
+
export declare function createPlaceHistory<C>(deps: {
|
|
95
|
+
label: string;
|
|
96
|
+
/** Where each place's cursor and outputs persist. */
|
|
97
|
+
path: string;
|
|
98
|
+
isCursor(value: unknown): value is C;
|
|
99
|
+
/** Negative, zero or positive as `a` is before, at or after `b`. */
|
|
100
|
+
compare(a: C, b: C): number;
|
|
101
|
+
isTurnInput(key: string, messageId: string): boolean;
|
|
102
|
+
/** The place's messages after `cursor` (or its newest few, with none) and before `until`, oldest first. */
|
|
103
|
+
read(key: string, from: {
|
|
104
|
+
cursor?: C;
|
|
105
|
+
drop(messageId: string): boolean;
|
|
106
|
+
}, until?: C): Promise<PlaceListing<C>>;
|
|
107
|
+
}): PlaceHistory<C>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a turn learns about its place (a chat, or a thread in one) beyond its own ask — and the fold that renders it,
|
|
3
|
+
* shared by every channel whose platform can be read (docs/design/place-history.md).
|
|
4
|
+
*
|
|
5
|
+
* Two sources fill a turn's discussion: the context buffer (`context-buffer.ts`), for a platform with no history read,
|
|
6
|
+
* and the place's own history, read from the platform when the turn runs ({@link createPlaceHistory}). The turn
|
|
7
|
+
* runner sees only {@link DiscussionSource}.
|
|
8
|
+
*/
|
|
9
|
+
import { log } from "../../log.js";
|
|
10
|
+
import { BUFFER_LINE_MAX_CHARS, BUFFER_MAX_CHARS } from "./context-buffer.js";
|
|
11
|
+
import { loadStateFile, saveStateFile } from "../../state-file.js";
|
|
12
|
+
import { truncateCodePointPrefix } from "./text.js";
|
|
13
|
+
/**
|
|
14
|
+
* Per-message bound for the agent's own post. A digest runs to thousands of characters; the line bound would keep its
|
|
15
|
+
* title only. It stays inside the budget, so later discussion still pushes it out.
|
|
16
|
+
*/
|
|
17
|
+
const PLACE_OWN_MAX_CHARS = 2000;
|
|
18
|
+
function line(message) {
|
|
19
|
+
const own = message.from.kind === "self";
|
|
20
|
+
const body = truncateCodePointPrefix(message.text.replace(/\s+/g, " ").trim(), own ? PLACE_OWN_MAX_CHARS : BUFFER_LINE_MAX_CHARS, own ? " … (truncated)" : "…");
|
|
21
|
+
const meta = [`msg ${message.id}`, message.replyTo ? `reply to msg ${message.replyTo}` : undefined]
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.join(", ");
|
|
24
|
+
return `${message.from.label} (${meta}): ${body}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Render `messages` (oldest first) newest-first into the buffer's budget (the same bound, whichever the source). What the budget leaves out, and what the read never
|
|
28
|
+
* reached (`earlier`), is said in a line, never dropped silently.
|
|
29
|
+
*/
|
|
30
|
+
function foldPlace(messages, earlier) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
const folded = [];
|
|
33
|
+
let used = 0;
|
|
34
|
+
let cut = 0;
|
|
35
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
36
|
+
const message = messages[i];
|
|
37
|
+
const rendered = line(message);
|
|
38
|
+
if (cut > 0 || used + rendered.length + 1 > BUFFER_MAX_CHARS) {
|
|
39
|
+
cut++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
used += rendered.length + 1;
|
|
43
|
+
lines.unshift(rendered);
|
|
44
|
+
folded.unshift(message);
|
|
45
|
+
}
|
|
46
|
+
if (cut > 0 || earlier) {
|
|
47
|
+
lines.unshift(cut > 0
|
|
48
|
+
? `(${cut}${earlier ? "+" : ""} earlier message${cut === 1 && !earlier ? "" : "s"} here not shown)`
|
|
49
|
+
: "(earlier messages here not shown)");
|
|
50
|
+
}
|
|
51
|
+
return { text: folded.length > 0 || cut > 0 ? lines.join("\n") : "", folded };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Places kept, least recently used dropped. A dropped place costs one re-read of its newest messages, its earlier
|
|
55
|
+
* answers among them.
|
|
56
|
+
*/
|
|
57
|
+
const MAX_PLACES = 2000;
|
|
58
|
+
/**
|
|
59
|
+
* Of those, places no read has reached yet (only outputs, no cursor), least recently used dropped. Slack records an
|
|
60
|
+
* answer to a top-level ask in the thread it opens, and most such threads are never asked in again: their own bound
|
|
61
|
+
* keeps them from pushing out a quiet place's cursor, while a thread answered a moment ago is still among the newest
|
|
62
|
+
* when its follow-up comes.
|
|
63
|
+
*/
|
|
64
|
+
const MAX_UNREAD_PLACES = 500;
|
|
65
|
+
/**
|
|
66
|
+
* A place's outputs not yet passed by a read. One turn posts a handful (an answer, its chunks, a queue notice); the
|
|
67
|
+
* bound only stops a place whose reads keep failing from growing without end.
|
|
68
|
+
*/
|
|
69
|
+
const MAX_OUTPUTS = 200;
|
|
70
|
+
/**
|
|
71
|
+
* A place's history read from its platform (docs/design/place-history.md): what was said in a chat or a thread since
|
|
72
|
+
* this agent last answered there, up to the turn's own ask. The platform owns the read; this owns what a place
|
|
73
|
+
* remembers between turns — its cursor, and the messages turns posted there — and what a turn leaves out of its read:
|
|
74
|
+
* the messages a turn posted into the place (`recordOutput`: answers, queue notices, stop feedback), kept beside the
|
|
75
|
+
* cursor until a read has passed them, so a busy deployment cannot push a quiet place's last answer out of a shared
|
|
76
|
+
* ring; and, as prompt shaping only, what the channel took as input (`isTurnInput`). What the agent sent itself from a
|
|
77
|
+
* send tool stays — that is what a later "what did point 3 mean?" is about.
|
|
78
|
+
*/
|
|
79
|
+
export function createPlaceHistory(deps) {
|
|
80
|
+
const { label, path } = deps;
|
|
81
|
+
const places = loadPlaces(path, label, deps.isCursor);
|
|
82
|
+
/**
|
|
83
|
+
* Mark `key` most recently used (the map's order is the eviction order), apply both bounds, and persist every place.
|
|
84
|
+
*/
|
|
85
|
+
const save = (key, state, lost) => {
|
|
86
|
+
places.delete(key);
|
|
87
|
+
places.set(key, state);
|
|
88
|
+
const unread = [...places.keys()].filter((other) => places.get(other)?.cursor === undefined);
|
|
89
|
+
for (const victim of unread.slice(0, Math.max(0, unread.length - MAX_UNREAD_PLACES)))
|
|
90
|
+
places.delete(victim);
|
|
91
|
+
for (const victim of [...places.keys()].slice(0, Math.max(0, places.size - MAX_PLACES)))
|
|
92
|
+
places.delete(victim);
|
|
93
|
+
try {
|
|
94
|
+
saveStateFile(path, Object.fromEntries(places));
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// Both callers are past a platform write (a sent message, a recorded answer): throwing would report a send that
|
|
98
|
+
// happened as failed. The state holds in memory; only a restart loses it.
|
|
99
|
+
log.warn(`${label} could not write ${path} (${lost}): ${String(error)}`);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const read = async (key, until) => {
|
|
103
|
+
const state = places.get(key);
|
|
104
|
+
const outputs = new Set(state?.outputs);
|
|
105
|
+
const listing = await deps.read(key, {
|
|
106
|
+
...(state?.cursor !== undefined ? { cursor: state.cursor } : {}),
|
|
107
|
+
drop: (id) => outputs.has(id) || deps.isTurnInput(key, id),
|
|
108
|
+
}, until);
|
|
109
|
+
return {
|
|
110
|
+
...listing,
|
|
111
|
+
// A bounded read has accounted for everything up to its bound, the ask included: the next read starts after it.
|
|
112
|
+
newest: until ?? listing.newest,
|
|
113
|
+
outputsPassed: listing.covered.filter((id) => outputs.has(id)),
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
const unreadable = (key, error) => {
|
|
117
|
+
log.warn(`${label} could not read the history of place ${key}; the turn runs without it: ${String(error)}`);
|
|
118
|
+
return { text: `(could not read the recent discussion here: ${String(error)})`, folded: [] };
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
async peek({ key, until }) {
|
|
122
|
+
try {
|
|
123
|
+
const { messages, newest, earlier, outputsPassed } = await read(key, until);
|
|
124
|
+
const { text, folded } = foldPlace(messages, earlier);
|
|
125
|
+
return { text, consumed: [{ ...(newest !== undefined ? { cursor: newest } : {}), folded, outputsPassed }] };
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
const { text } = unreadable(key, error);
|
|
129
|
+
return { text, consumed: [{ folded: [], outputsPassed: [] }] };
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
commit({ key }, consumed) {
|
|
133
|
+
const done = consumed[0];
|
|
134
|
+
if (done?.cursor === undefined)
|
|
135
|
+
return;
|
|
136
|
+
// Only the outputs this read passed: the turn's own answer came after it and waits for the next read.
|
|
137
|
+
const passed = new Set(done.outputsPassed);
|
|
138
|
+
const state = places.get(key);
|
|
139
|
+
const outputs = (state?.outputs ?? []).filter((id) => !passed.has(id));
|
|
140
|
+
// Forward only. Turns in a place can finish out of ask order (a redelivered ask, a deferred turn); an earlier
|
|
141
|
+
// ask committing after a later one must not pull the cursor back over what that later turn already folded.
|
|
142
|
+
const cursor = state?.cursor !== undefined && deps.compare(state.cursor, done.cursor) >= 0 ? state.cursor : done.cursor;
|
|
143
|
+
save(key, { cursor, outputs }, "a restart may re-fold answered discussion");
|
|
144
|
+
},
|
|
145
|
+
recordOutput(key, messageId) {
|
|
146
|
+
const state = places.get(key);
|
|
147
|
+
const outputs = [...(state?.outputs ?? []), messageId].slice(-MAX_OUTPUTS);
|
|
148
|
+
save(key, { ...(state?.cursor !== undefined ? { cursor: state.cursor } : {}), outputs }, "after a restart this place's next read may fold the agent's own answer");
|
|
149
|
+
},
|
|
150
|
+
async room(key) {
|
|
151
|
+
try {
|
|
152
|
+
const { messages, earlier } = await read(key);
|
|
153
|
+
return foldPlace(messages, earlier);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return unreadable(key, error);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
async snapshot(key) {
|
|
160
|
+
const { messages, earlier } = await deps.read(key, { drop: () => false });
|
|
161
|
+
return foldPlace(messages, earlier);
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function loadPlaces(path, label, isCursor) {
|
|
166
|
+
const raw = loadStateFile(path);
|
|
167
|
+
if (raw === undefined)
|
|
168
|
+
return new Map();
|
|
169
|
+
const valid = (value) => {
|
|
170
|
+
const state = value;
|
|
171
|
+
return (typeof state === "object" &&
|
|
172
|
+
state !== null &&
|
|
173
|
+
(state.cursor === undefined || isCursor(state.cursor)) &&
|
|
174
|
+
Array.isArray(state.outputs) &&
|
|
175
|
+
state.outputs.every((id) => typeof id === "string"));
|
|
176
|
+
};
|
|
177
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw) && Object.values(raw).every(valid)) {
|
|
178
|
+
return new Map(Object.entries(raw));
|
|
179
|
+
}
|
|
180
|
+
log.warn(`${label} unexpected shape in ${path} — every place reads its recent history afresh`);
|
|
181
|
+
return new Map();
|
|
182
|
+
}
|
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
* Channel-neutral live-preview pieces shared by every messaging channel's preview renderer (telegram/preview.ts,
|
|
3
3
|
* feishu/preview.ts, slack/preview.ts).
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type { Json, SessionEvent } from "../../agent-api.ts";
|
|
6
|
+
import type { TurnEvent } from "./invoke-turn-kit.ts";
|
|
6
7
|
/** A terminal failure, as a channel hands it to its `onError`. */
|
|
7
8
|
export interface ChannelFailure {
|
|
8
9
|
details: string;
|
|
9
10
|
retryable: boolean;
|
|
10
|
-
/** The
|
|
11
|
+
/** The harness's failure code, when it set one. */
|
|
11
12
|
code?: string;
|
|
12
13
|
}
|
|
13
14
|
/** The customer-facing default: neutral, no leaked internals. */
|
|
14
15
|
export declare function defaultErrorMessage(failed: ChannelFailure): string;
|
|
15
16
|
/**
|
|
16
|
-
* Customer-facing live-preview line for
|
|
17
|
+
* Customer-facing live-preview line for a harness-internal retry backoff (the advisory `retrying` event): neutral, no
|
|
17
18
|
* leaked internals — the reason stays in operator logs.
|
|
18
19
|
*/
|
|
19
20
|
export declare const RETRY_NOTICE = "\u23F3 Temporary problem \u2014 retrying\u2026";
|
|
@@ -30,16 +31,19 @@ interface ToolLine {
|
|
|
30
31
|
export interface TurnView {
|
|
31
32
|
thinking: string;
|
|
32
33
|
tools: ToolLine[];
|
|
33
|
-
/** tool-call id → its line, for `
|
|
34
|
+
/** tool-call id → its line, for the `tool_result` status flips (bookkeeping; renderers read `tools`). */
|
|
34
35
|
toolById: Map<string, ToolLine>;
|
|
35
36
|
answer: string;
|
|
37
|
+
/** What each event adds to the answer: its delta, or the settled text no delta carried. */
|
|
38
|
+
textOf: (event: SessionEvent) => string;
|
|
36
39
|
/** Arrival time of the first non-empty answer delta; age-based reveal policies read it. */
|
|
37
40
|
answerSince?: number;
|
|
38
41
|
/** An advisory retry backoff is in progress (closed again by any subsequent progress event). */
|
|
39
42
|
retrying: boolean;
|
|
40
43
|
}
|
|
41
44
|
export declare function createTurnView(): TurnView;
|
|
42
|
-
|
|
45
|
+
/** Fold one event of a turn's invoke into its view; true when what a renderer shows changed. */
|
|
46
|
+
export declare function applyTurnEvent(view: TurnView, e: TurnEvent, now?: number): boolean;
|
|
43
47
|
/**
|
|
44
48
|
* The tool-activity block: one `🔧 label …/✓/✗` line per call, in call order. A running call shows its latest
|
|
45
49
|
* progress after the mark (`🔧 Bash npm test … › 42 passed`); a finished one drops it.
|