@fastagent-sh/fastagent 0.24.3 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -82
- package/dist/agent-api.d.ts +452 -0
- package/dist/agent-api.js +201 -0
- package/dist/answer-text.d.ts +8 -0
- package/dist/answer-text.js +18 -0
- package/dist/atomic-write.d.ts +2 -2
- package/dist/atomic-write.js +2 -2
- package/dist/authoring.d.ts +77 -0
- package/dist/authoring.js +238 -0
- package/dist/channel.d.ts +29 -15
- package/dist/channel.js +4 -1
- package/dist/channels/agentcore-protocol.d.ts +36 -50
- package/dist/channels/agentcore-protocol.js +3 -3
- package/dist/channels/agentcore-service.d.ts +8 -18
- package/dist/channels/agentcore-service.js +108 -127
- package/dist/channels/agentcore.d.ts +8 -24
- package/dist/channels/agentcore.js +32 -65
- package/dist/channels/body.d.ts +0 -16
- package/dist/channels/body.js +0 -25
- package/dist/channels/busy.js +6 -1
- package/dist/channels/context.d.ts +4 -0
- package/dist/channels/context.js +35 -0
- package/dist/channels/discover.d.ts +18 -20
- package/dist/channels/discover.js +41 -99
- package/dist/channels/feishu/feishu-api.d.ts +54 -4
- package/dist/channels/feishu/feishu-api.js +192 -149
- package/dist/channels/feishu/feishu.d.ts +3 -0
- package/dist/channels/feishu/feishu.js +117 -106
- package/dist/channels/feishu/history.d.ts +55 -0
- package/dist/channels/feishu/history.js +199 -0
- package/dist/channels/feishu/invoke-turn.d.ts +13 -11
- package/dist/channels/feishu/invoke-turn.js +9 -10
- package/dist/channels/feishu/model.d.ts +2 -1
- package/dist/channels/feishu/normalize.js +100 -0
- package/dist/channels/feishu/preview.d.ts +2 -2
- package/dist/channels/feishu/register-webhook.d.ts +1 -0
- package/dist/channels/feishu/register-webhook.js +3 -2
- package/dist/channels/feishu/scaffold/channel.ts +65 -46
- package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
- package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
- package/dist/channels/feishu/setup-mode.d.ts +45 -25
- package/dist/channels/feishu/setup-mode.js +74 -23
- package/dist/channels/feishu/shared-api.d.ts +24 -6
- package/dist/channels/feishu/shared-api.js +30 -24
- package/dist/channels/kit/context-buffer.d.ts +11 -2
- package/dist/channels/kit/context-buffer.js +7 -4
- package/dist/channels/kit/delivery.d.ts +19 -3
- package/dist/channels/kit/delivery.js +74 -15
- package/dist/channels/kit/event-stream.js +2 -1
- package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
- package/dist/channels/kit/invoke-turn-kit.js +62 -71
- package/dist/channels/kit/place-history.d.ts +107 -0
- package/dist/channels/kit/place-history.js +182 -0
- package/dist/channels/kit/preview-kit.d.ts +9 -5
- package/dist/channels/kit/preview-kit.js +36 -15
- package/dist/channels/kit/room-threads.d.ts +46 -0
- package/dist/channels/kit/room-threads.js +81 -0
- package/dist/channels/kit/seen.js +1 -1
- package/dist/channels/kit/stop-command.d.ts +6 -3
- package/dist/channels/kit/stop-command.js +20 -18
- package/dist/channels/kit/thread-fork.d.ts +17 -0
- package/dist/channels/kit/thread-fork.js +43 -0
- package/dist/channels/kit/thread-participants.js +1 -1
- package/dist/channels/kit/transport.d.ts +5 -0
- package/dist/channels/kit/transport.js +5 -0
- package/dist/channels/kit/turn-runner.d.ts +10 -9
- package/dist/channels/kit/turn-runner.js +17 -19
- package/dist/channels/kit/turn-store.d.ts +5 -4
- package/dist/channels/kit/turn-store.js +5 -5
- package/dist/channels/lark/lark.d.ts +3 -0
- package/dist/channels/lark/lark.js +5 -0
- package/dist/channels/lark/onboard.d.ts +5 -3
- package/dist/channels/lark/onboard.js +14 -14
- package/dist/channels/lark/scaffold/channel.ts +67 -44
- package/dist/channels/lark/scaffold/lark-send.ts +38 -33
- package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
- package/dist/channels/respond.d.ts +0 -4
- package/dist/channels/respond.js +1 -1
- package/dist/channels/router.d.ts +18 -0
- package/dist/channels/router.js +68 -0
- package/dist/channels/serve.d.ts +5 -70
- package/dist/channels/serve.js +105 -287
- package/dist/channels/slack/history.d.ts +42 -0
- package/dist/channels/slack/history.js +282 -0
- package/dist/channels/slack/invoke-turn.d.ts +12 -5
- package/dist/channels/slack/invoke-turn.js +7 -5
- package/dist/channels/slack/manifest.d.ts +1 -4
- package/dist/channels/slack/manifest.js +22 -10
- package/dist/channels/slack/model.d.ts +2 -1
- package/dist/channels/slack/onboard.d.ts +0 -2
- package/dist/channels/slack/onboard.js +2 -4
- package/dist/channels/slack/onboarding-state.d.ts +0 -2
- package/dist/channels/slack/onboarding-state.js +0 -1
- package/dist/channels/slack/parse.d.ts +0 -5
- package/dist/channels/slack/parse.js +1 -16
- package/dist/channels/slack/preview.d.ts +2 -3
- package/dist/channels/slack/preview.js +78 -52
- package/dist/channels/slack/register-webhook.d.ts +1 -0
- package/dist/channels/slack/register-webhook.js +6 -5
- package/dist/channels/slack/scaffold/channel.ts +23 -24
- package/dist/channels/slack/scaffold/slack-send.ts +50 -43
- package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
- package/dist/channels/slack/setup-server.d.ts +19 -0
- package/dist/channels/slack/setup-server.js +113 -11
- package/dist/channels/slack/shared-api.d.ts +15 -4
- package/dist/channels/slack/shared-api.js +19 -14
- package/dist/channels/slack/slack-api.d.ts +61 -2
- package/dist/channels/slack/slack-api.js +28 -5
- package/dist/channels/slack/slack.js +97 -52
- package/dist/channels/slack/welcomed.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +6 -1
- package/dist/channels/telegram/context-buffer.js +19 -0
- package/dist/channels/telegram/invoke-turn.d.ts +8 -4
- package/dist/channels/telegram/invoke-turn.js +7 -7
- package/dist/channels/telegram/parse.d.ts +7 -2
- package/dist/channels/telegram/parse.js +7 -0
- package/dist/channels/telegram/preview.d.ts +2 -2
- package/dist/channels/telegram/register-webhook.d.ts +1 -0
- package/dist/channels/telegram/register-webhook.js +3 -2
- package/dist/channels/telegram/scaffold/channel.ts +27 -28
- package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
- package/dist/channels/telegram/shared-api.d.ts +34 -0
- package/dist/channels/telegram/shared-api.js +79 -0
- package/dist/channels/telegram/telegram-api.d.ts +15 -5
- package/dist/channels/telegram/telegram-api.js +10 -6
- package/dist/channels/telegram/telegram.js +27 -21
- package/dist/channels/telegram/turn-store.js +1 -1
- package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
- package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
- package/dist/cli/add-feishu.d.ts +42 -12
- package/dist/cli/add-feishu.js +177 -124
- package/dist/cli/add-slack.d.ts +8 -3
- package/dist/cli/add-slack.js +115 -63
- package/dist/cli/auth-view.d.ts +10 -3
- package/dist/cli/auth-view.js +8 -6
- package/dist/cli/box-login.d.ts +17 -7
- package/dist/cli/box-login.js +46 -12
- package/dist/cli/commands/add.d.ts +15 -4
- package/dist/cli/commands/add.js +252 -143
- package/dist/cli/commands/auth.d.ts +6 -0
- package/dist/cli/commands/auth.js +50 -0
- package/dist/cli/commands/content.d.ts +6 -0
- package/dist/cli/commands/content.js +45 -0
- package/dist/cli/commands/deploy/agentcore.js +50 -77
- package/dist/cli/commands/deploy/docker.js +38 -39
- package/dist/cli/commands/deploy/fly.js +21 -18
- package/dist/cli/commands/deploy/host-login.d.ts +26 -0
- package/dist/cli/commands/deploy/host-login.js +90 -0
- package/dist/cli/commands/deploy/railway.js +21 -26
- package/dist/cli/commands/deploy/shared.d.ts +45 -17
- package/dist/cli/commands/deploy/shared.js +34 -15
- package/dist/cli/commands/deploy.d.ts +0 -4
- package/dist/cli/commands/deploy.js +237 -50
- package/dist/cli/commands/destroy.js +8 -6
- package/dist/cli/commands/dev.d.ts +0 -2
- package/dist/cli/commands/dev.js +20 -19
- package/dist/cli/commands/env.d.ts +12 -0
- package/dist/cli/commands/env.js +190 -0
- package/dist/cli/commands/info.js +95 -156
- package/dist/cli/commands/init.d.ts +2 -2
- package/dist/cli/commands/init.js +35 -33
- package/dist/cli/commands/invoke.d.ts +6 -0
- package/dist/cli/commands/invoke.js +131 -16
- package/dist/cli/commands/login.d.ts +31 -2
- package/dist/cli/commands/login.js +413 -64
- package/dist/cli/commands/logout.d.ts +4 -0
- package/dist/cli/commands/logout.js +53 -0
- package/dist/cli/commands/logs.js +8 -6
- package/dist/cli/commands/models.d.ts +2 -1
- package/dist/cli/commands/models.js +39 -37
- package/dist/cli/commands/schedules.d.ts +21 -0
- package/dist/cli/commands/schedules.js +143 -0
- package/dist/cli/commands/start.d.ts +11 -10
- package/dist/cli/commands/start.js +35 -62
- package/dist/cli/content-view.d.ts +9 -0
- package/dist/cli/content-view.js +26 -0
- package/dist/cli/fail.d.ts +9 -12
- package/dist/cli/fail.js +17 -14
- package/dist/cli/invoke-stream.d.ts +12 -3
- package/dist/cli/invoke-stream.js +85 -23
- package/dist/cli/kernel.d.ts +9 -2
- package/dist/cli/kernel.js +15 -6
- package/dist/cli/login-relay.d.ts +3 -2
- package/dist/cli/login-relay.js +18 -6
- package/dist/cli/models-view.d.ts +5 -1
- package/dist/cli/models-view.js +1 -1
- package/dist/cli/person.d.ts +10 -0
- package/dist/cli/person.js +23 -0
- package/dist/cli/program.js +421 -203
- package/dist/cli/serve.d.ts +13 -63
- package/dist/cli/serve.js +31 -121
- package/dist/cli/shared.d.ts +21 -48
- package/dist/cli/shared.js +68 -89
- package/dist/cli/ticket.d.ts +136 -0
- package/dist/cli/ticket.js +476 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +2 -0
- package/dist/content/declare.d.ts +70 -0
- package/dist/content/declare.js +163 -0
- package/dist/content/file.d.ts +32 -0
- package/dist/content/file.js +119 -0
- package/dist/content/git.d.ts +41 -0
- package/dist/content/git.js +217 -0
- package/dist/content/mount.d.ts +2 -0
- package/dist/content/mount.js +15 -0
- package/dist/content/resolve.d.ts +52 -0
- package/dist/content/resolve.js +151 -0
- package/dist/content/source.d.ts +27 -0
- package/dist/content/source.js +53 -0
- package/dist/conversation-sessions.d.ts +26 -0
- package/dist/conversation-sessions.js +99 -0
- package/dist/declared-secrets.d.ts +19 -37
- package/dist/declared-secrets.js +16 -58
- package/dist/deploy/agentcore/destroy.d.ts +3 -3
- package/dist/deploy/agentcore/destroy.js +46 -28
- package/dist/deploy/agentcore/forwarder.js +105 -22
- package/dist/deploy/agentcore/logs.js +1 -1
- package/dist/deploy/agentcore/plan.d.ts +10 -24
- package/dist/deploy/agentcore/plan.js +31 -135
- package/dist/deploy/agentcore/run.d.ts +1 -1
- package/dist/deploy/agentcore/run.js +8 -5
- package/dist/deploy/agentcore/shell.d.ts +1 -1
- package/dist/deploy/agentcore/shell.js +5 -5
- package/dist/deploy/api.d.ts +34 -0
- package/dist/deploy/api.js +50 -0
- package/dist/deploy/box-shell.d.ts +6 -0
- package/dist/deploy/box-shell.js +7 -0
- package/dist/deploy/build-context.d.ts +7 -5
- package/dist/deploy/build-context.js +41 -35
- package/dist/deploy/channel-ingress.d.ts +6 -1
- package/dist/deploy/channel-ingress.js +18 -5
- package/dist/deploy/container.d.ts +22 -9
- package/dist/deploy/container.js +96 -55
- package/dist/deploy/docker/plan.d.ts +3 -2
- package/dist/deploy/docker/plan.js +13 -14
- package/dist/deploy/docker/run.d.ts +2 -2
- package/dist/deploy/docker/run.js +8 -4
- package/dist/deploy/fly/plan.d.ts +1 -1
- package/dist/deploy/fly/plan.js +12 -21
- package/dist/deploy/fly/run.d.ts +10 -6
- package/dist/deploy/fly/run.js +11 -6
- package/dist/deploy/hosts.d.ts +2 -1
- package/dist/deploy/hosts.js +2 -1
- package/dist/deploy/preflight.d.ts +21 -23
- package/dist/deploy/preflight.js +222 -201
- package/dist/deploy/railway/plan.d.ts +8 -6
- package/dist/deploy/railway/plan.js +15 -58
- package/dist/deploy/railway/run.d.ts +8 -3
- package/dist/deploy/railway/run.js +12 -8
- package/dist/deploy/residency.d.ts +5 -11
- package/dist/deploy/residency.js +0 -6
- package/dist/deploy/runner.d.ts +1 -1
- package/dist/deploy/runner.js +1 -1
- package/dist/deploy/secrets.d.ts +16 -2
- package/dist/deploy/secrets.js +33 -6
- package/dist/deploy/workspace.d.ts +11 -6
- package/dist/deploy/workspace.js +55 -38
- package/dist/dev-supervisor.d.ts +11 -2
- package/dist/dev-supervisor.js +51 -26
- package/dist/effect-port.d.ts +4 -4
- package/dist/effect-port.js +4 -4
- package/dist/environment/apt.d.ts +4 -0
- package/dist/environment/apt.js +32 -0
- package/dist/environment/declare.d.ts +59 -0
- package/dist/environment/declare.js +210 -0
- package/dist/environment/mise.d.ts +50 -0
- package/dist/environment/mise.js +268 -0
- package/dist/feishu.d.ts +2 -2
- package/dist/feishu.js +2 -2
- package/dist/git.d.ts +13 -0
- package/dist/git.js +55 -0
- package/dist/harnesses/models/catalog.d.ts +20 -0
- package/dist/harnesses/models/catalog.js +254 -0
- package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
- package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
- package/dist/harnesses/models/environment.d.ts +43 -0
- package/dist/harnesses/models/environment.js +82 -0
- package/dist/{engines/pi → harnesses/models}/login.d.ts +57 -26
- package/dist/{engines/pi → harnesses/models}/login.js +187 -79
- package/dist/harnesses/models/models.d.ts +83 -0
- package/dist/harnesses/models/models.js +158 -0
- package/dist/harnesses/pi-durable/agent.d.ts +42 -0
- package/dist/harnesses/pi-durable/agent.js +579 -0
- package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
- package/dist/harnesses/pi-durable/defaults.js +26 -0
- package/dist/harnesses/pi-durable/define.d.ts +77 -0
- package/dist/harnesses/pi-durable/define.js +28 -0
- package/dist/harnesses/pi-durable/entries.d.ts +32 -0
- package/dist/harnesses/pi-durable/entries.js +126 -0
- package/dist/harnesses/pi-durable/events.d.ts +25 -0
- package/dist/harnesses/pi-durable/events.js +238 -0
- package/dist/harnesses/pi-durable/ids.d.ts +16 -0
- package/dist/harnesses/pi-durable/ids.js +55 -0
- package/dist/harnesses/pi-durable/messages.d.ts +41 -0
- package/dist/harnesses/pi-durable/messages.js +116 -0
- package/dist/harnesses/pi-durable/open.d.ts +88 -0
- package/dist/harnesses/pi-durable/open.js +329 -0
- package/dist/harnesses/pi-durable/reads.d.ts +30 -0
- package/dist/harnesses/pi-durable/reads.js +94 -0
- package/dist/harnesses/pi-durable/registry.d.ts +18 -0
- package/dist/harnesses/pi-durable/registry.js +56 -0
- package/dist/harnesses/pi-durable/release.d.ts +13 -0
- package/dist/harnesses/pi-durable/release.js +40 -0
- package/dist/harnesses/pi-durable/serve.d.ts +80 -0
- package/dist/harnesses/pi-durable/serve.js +156 -0
- package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
- package/dist/harnesses/pi-durable/sessions.js +47 -0
- package/dist/harnesses/pi-durable/storage.d.ts +4 -0
- package/dist/harnesses/pi-durable/storage.js +49 -0
- package/dist/harnesses/pi-durable/stream.d.ts +20 -0
- package/dist/harnesses/pi-durable/stream.js +221 -0
- package/dist/harnesses/pi-durable/text.d.ts +30 -0
- package/dist/harnesses/pi-durable/text.js +292 -0
- package/dist/harnesses/pi-durable/wake.d.ts +4 -0
- package/dist/harnesses/pi-durable/wake.js +81 -0
- package/dist/http/client.d.ts +6 -0
- package/dist/http/client.js +256 -0
- package/dist/http/contract.d.ts +541 -0
- package/dist/http/contract.js +179 -0
- package/dist/http/server.d.ts +34 -0
- package/dist/http/server.js +194 -0
- package/dist/index.d.ts +17 -4
- package/dist/index.js +10 -5
- package/dist/lark.d.ts +2 -2
- package/dist/lark.js +2 -2
- package/dist/locked-file.d.ts +17 -0
- package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
- package/dist/once.d.ts +6 -0
- package/dist/once.js +23 -0
- package/dist/paths.d.ts +36 -52
- package/dist/paths.js +66 -182
- package/dist/runtime.d.ts +25 -2
- package/dist/runtime.js +27 -0
- package/dist/scaffold/add-channel.d.ts +14 -27
- package/dist/scaffold/add-channel.js +82 -180
- package/dist/scaffold/init.d.ts +17 -18
- package/dist/scaffold/init.js +87 -99
- package/dist/scaffold/templates/SYSTEM.md +12 -0
- package/dist/scaffold/templates/env.example +4 -10
- package/dist/scaffold/templates/fastagent.config.ts +21 -33
- package/dist/scaffold/templates/gitignore +8 -1
- package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
- package/dist/scaffold/templates.d.ts +9 -4
- package/dist/scaffold/templates.js +7 -5
- package/dist/scaffold/vendor-skill.d.ts +1 -4
- package/dist/scaffold/vendor-skill.js +5 -6
- package/dist/schedule/cron.d.ts +13 -0
- package/dist/schedule/cron.js +52 -0
- package/dist/schedule/eventbridge-cron.d.ts +17 -0
- package/dist/schedule/eventbridge-cron.js +88 -0
- package/dist/schedule/schedule.d.ts +23 -0
- package/dist/schedule/schedule.js +34 -0
- package/dist/schedule/scheduler.d.ts +34 -33
- package/dist/schedule/scheduler.js +251 -137
- package/dist/schedule/state.d.ts +4 -14
- package/dist/schedule/state.js +7 -7
- package/dist/schedule/wake-alarm.d.ts +22 -2
- package/dist/schedule/wake-alarm.js +66 -20
- package/dist/schedule/wakeups.d.ts +16 -15
- package/dist/schedule/wakeups.js +47 -45
- package/dist/service.d.ts +56 -169
- package/dist/service.js +91 -187
- package/dist/serving-command.d.ts +10 -0
- package/dist/serving-command.js +16 -0
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/dist/{channels/kit/state.js → state-file.js} +6 -6
- package/dist/telegram.d.ts +1 -0
- package/dist/telegram.js +1 -0
- package/dist/tunnel.d.ts +3 -0
- package/dist/tunnel.js +12 -4
- package/dist/value-file.d.ts +21 -0
- package/dist/value-file.js +108 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +14 -1
- package/package.json +18 -28
- package/dist/agent.d.ts +0 -106
- package/dist/agent.js +0 -21
- package/dist/channels/control.d.ts +0 -31
- package/dist/channels/control.js +0 -329
- package/dist/channels/define-channel.d.ts +0 -39
- package/dist/channels/define-channel.js +0 -9
- package/dist/channels/feishu/context-buffer.d.ts +0 -35
- package/dist/channels/feishu/context-buffer.js +0 -65
- package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/http.d.ts +0 -23
- package/dist/channels/http.js +0 -57
- package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/slack/context-buffer.d.ts +0 -23
- package/dist/channels/slack/context-buffer.js +0 -37
- package/dist/channels/sse.d.ts +0 -4
- package/dist/channels/sse.js +0 -66
- package/dist/cli/commands/chat.d.ts +0 -3
- package/dist/cli/commands/chat.js +0 -14
- package/dist/cli/commands/routine-run.d.ts +0 -6
- package/dist/cli/commands/routine-run.js +0 -46
- package/dist/cli/commands/routine.d.ts +0 -25
- package/dist/cli/commands/routine.js +0 -122
- package/dist/cli/commands/tool.d.ts +0 -1
- package/dist/cli/commands/tool.js +0 -57
- package/dist/collect.d.ts +0 -63
- package/dist/collect.js +0 -90
- package/dist/core.d.ts +0 -13
- package/dist/core.js +0 -15
- package/dist/engines/pi/agent-models.d.ts +0 -83
- package/dist/engines/pi/agent-models.js +0 -120
- package/dist/engines/pi/agent-session-factory.d.ts +0 -121
- package/dist/engines/pi/agent-session-factory.js +0 -517
- package/dist/engines/pi/chat.d.ts +0 -3
- package/dist/engines/pi/chat.js +0 -8
- package/dist/engines/pi/config.d.ts +0 -71
- package/dist/engines/pi/config.js +0 -199
- package/dist/engines/pi/create.d.ts +0 -170
- package/dist/engines/pi/create.js +0 -343
- package/dist/engines/pi/definition.d.ts +0 -74
- package/dist/engines/pi/definition.js +0 -236
- package/dist/engines/pi/entry-images.d.ts +0 -14
- package/dist/engines/pi/entry-images.js +0 -23
- package/dist/engines/pi/invoke-session.d.ts +0 -18
- package/dist/engines/pi/invoke-session.js +0 -403
- package/dist/engines/pi/locked-file.d.ts +0 -9
- package/dist/engines/pi/machine.d.ts +0 -53
- package/dist/engines/pi/machine.js +0 -137
- package/dist/engines/pi/models.d.ts +0 -200
- package/dist/engines/pi/models.js +0 -533
- package/dist/engines/pi/open.d.ts +0 -180
- package/dist/engines/pi/open.js +0 -270
- package/dist/engines/pi/openai-account-models.d.ts +0 -35
- package/dist/engines/pi/openai-account-models.js +0 -116
- package/dist/engines/pi/report.d.ts +0 -18
- package/dist/engines/pi/report.js +0 -59
- package/dist/engines/pi/retry-event.d.ts +0 -6
- package/dist/engines/pi/retry-event.js +0 -15
- package/dist/engines/pi/service.d.ts +0 -9
- package/dist/engines/pi/service.js +0 -14
- package/dist/engines/pi/session-builder.d.ts +0 -13
- package/dist/engines/pi/session-builder.js +0 -117
- package/dist/engines/pi/session-control.d.ts +0 -77
- package/dist/engines/pi/session-control.js +0 -908
- package/dist/engines/pi/session-effects.d.ts +0 -14
- package/dist/engines/pi/session-effects.js +0 -30
- package/dist/engines/pi/session-inheritance.d.ts +0 -21
- package/dist/engines/pi/session-inheritance.js +0 -204
- package/dist/engines/pi/session-markers.d.ts +0 -49
- package/dist/engines/pi/session-markers.js +0 -60
- package/dist/engines/pi/session-settings.d.ts +0 -67
- package/dist/engines/pi/session-settings.js +0 -119
- package/dist/engines/pi/session-store.d.ts +0 -69
- package/dist/engines/pi/session-store.js +0 -430
- package/dist/engines/pi/tool-context.d.ts +0 -45
- package/dist/engines/pi/tool-context.js +0 -31
- package/dist/engines/pi/tool.d.ts +0 -99
- package/dist/engines/pi/tool.js +0 -118
- package/dist/engines/pi/turn-kit.d.ts +0 -83
- package/dist/engines/pi/turn-kit.js +0 -275
- package/dist/engines/pi/wake-tool.d.ts +0 -9
- package/dist/engines/pi/wake-tool.js +0 -92
- package/dist/loader.d.ts +0 -41
- package/dist/loader.js +0 -116
- package/dist/node.d.ts +0 -3
- package/dist/node.js +0 -4
- package/dist/observe.d.ts +0 -6
- package/dist/observe.js +0 -47
- package/dist/pi.d.ts +0 -25
- package/dist/pi.js +0 -15
- package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
- package/dist/schedule/discover.d.ts +0 -17
- package/dist/schedule/discover.js +0 -61
- package/dist/schedule/routine.d.ts +0 -43
- package/dist/schedule/routine.js +0 -32
- package/dist/schedule/run.d.ts +0 -55
- package/dist/schedule/run.js +0 -146
- package/dist/secrets-gate.d.ts +0 -32
- package/dist/secrets-gate.js +0 -33
- package/dist/session-remote.d.ts +0 -47
- package/dist/session-remote.js +0 -460
- package/dist/session.d.ts +0 -466
- package/dist/session.js +0 -57
- /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Canonical Feishu bot-channel engine: verified webhook or official-SDK WebSocket → dedup → route → persist → enqueue
|
|
3
3
|
* → stream a live card.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { Effect } from "effect";
|
|
6
7
|
import { log } from "../../log.js";
|
|
7
8
|
import { readBodyCapped } from "../body.js";
|
|
8
9
|
import { text } from "../respond.js";
|
|
@@ -10,22 +11,24 @@ import { secretEquals } from "../secret.js";
|
|
|
10
11
|
import { createSeenRing } from "../kit/seen.js";
|
|
11
12
|
import { createTaskTracker } from "../kit/tasks.js";
|
|
12
13
|
import { attachmentsDir } from "../kit/attachment-path.js";
|
|
13
|
-
import {
|
|
14
|
+
import { loadStateFile, saveStateFile } from "../../state-file.js";
|
|
14
15
|
import { signatureIsFresh } from "../kit/signature.js";
|
|
15
16
|
import { dispatchStop, isStopText } from "../kit/stop-command.js";
|
|
17
|
+
import { createRoomThreads, registerRoomThreads } from "../kit/room-threads.js";
|
|
18
|
+
import { threadForkSource } from "../kit/thread-fork.js";
|
|
16
19
|
import { createTurnRunner } from "../kit/turn-runner.js";
|
|
17
20
|
import { portJoin } from "../../effect-port.js";
|
|
18
21
|
import { createTurnStore } from "../kit/turn-store.js";
|
|
19
22
|
import { discussionBlock } from "../kit/context-buffer.js";
|
|
20
23
|
import { FEISHU_CLOUD } from "./cloud.js";
|
|
21
|
-
import {
|
|
24
|
+
import { collectFoldedAttachments, createFeishuPlaceHistory, feishuHistoryKey, feishuPlaceOf, } from "./history.js";
|
|
22
25
|
import { decryptEvent, verifySignature } from "./crypto.js";
|
|
23
26
|
import { feishuTurnStream } from "./invoke-turn.js";
|
|
24
27
|
import { createFeishuApi } from "./feishu-api.js";
|
|
25
28
|
import { normalizeFeishuMessage } from "./normalize.js";
|
|
26
29
|
import { createThreadParticipants } from "../kit/thread-participants.js";
|
|
27
|
-
import {
|
|
28
|
-
import { cloudEnvelope, defaultFeishuRoute, feishuEnvelope, placeKey, senderId,
|
|
30
|
+
import { FEISHU_AGENT_SCOPES, feishuIngressFor, scopeSatisfied } from "./setup-mode.js";
|
|
31
|
+
import { cloudEnvelope, defaultFeishuRoute, feishuEnvelope, placeKey, senderId, } from "./parse.js";
|
|
29
32
|
import { defaultErrorMessage, mountFeishuPreview, settleFeishuPreview, feishuReply, } from "./preview.js";
|
|
30
33
|
import { connectFeishuWs } from "./ws-ingress.js";
|
|
31
34
|
import { registerFeishuApi } from "./shared-api.js";
|
|
@@ -52,16 +55,18 @@ function isStoredFeishuTurn(t) {
|
|
|
52
55
|
v.every((x) => typeof x.msg === "string" && typeof x.key === "string");
|
|
53
56
|
return (typeof r?.id === "string" &&
|
|
54
57
|
typeof r.seq === "number" &&
|
|
55
|
-
typeof r.
|
|
58
|
+
typeof r.conversation === "string" &&
|
|
56
59
|
typeof r.baseText === "string" &&
|
|
57
|
-
typeof r.
|
|
60
|
+
(r.historyKey === undefined || typeof r.historyKey === "string") &&
|
|
61
|
+
typeof r.group === "boolean" &&
|
|
62
|
+
typeof r.askAt === "number" &&
|
|
58
63
|
typeof r.chatId === "string" &&
|
|
59
64
|
(r.replyTo === undefined || typeof r.replyTo === "string") &&
|
|
60
65
|
(r.queueReplyTo === undefined || typeof r.queueReplyTo === "string") &&
|
|
61
66
|
(r.replyInThread === undefined || typeof r.replyInThread === "boolean") &&
|
|
62
67
|
(r.parentId === undefined || typeof r.parentId === "string") &&
|
|
63
|
-
(r.
|
|
64
|
-
(r.
|
|
68
|
+
(r.parentConversation === undefined || typeof r.parentConversation === "string") &&
|
|
69
|
+
(r.roomKey === undefined || typeof r.roomKey === "string") &&
|
|
65
70
|
refs(r.images) &&
|
|
66
71
|
refs(r.files) &&
|
|
67
72
|
typeof r.attempts === "number");
|
|
@@ -70,6 +75,10 @@ function isStoredFeishuTurn(t) {
|
|
|
70
75
|
export function feishuChannel(opts) {
|
|
71
76
|
return buildFeishuChannel(FEISHU_CLOUD, opts, feishuChannel.name);
|
|
72
77
|
}
|
|
78
|
+
/** How this process receives the Feishu channel: `FEISHU_INGRESS`, else WebSocket under `dev` and webhook otherwise. */
|
|
79
|
+
export function feishuIngress() {
|
|
80
|
+
return feishuIngressFor("feishu");
|
|
81
|
+
}
|
|
73
82
|
/** Build the canonical Feishu WebSocket long-connection channel. */
|
|
74
83
|
export function feishuWebSocketChannel(opts) {
|
|
75
84
|
return buildFeishuWebSocketChannel(FEISHU_CLOUD, opts, feishuWebSocketChannel.name);
|
|
@@ -125,50 +134,57 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
125
134
|
const baseUrl = opts.apiBaseUrl ?? profile.apiBase;
|
|
126
135
|
const { kind } = profile;
|
|
127
136
|
const label = `[${kind}]`;
|
|
128
|
-
return ({ agent,
|
|
137
|
+
return ({ agent, sessionFor, recordedSession, stateDir: stateHome }) => {
|
|
129
138
|
// Credential checks run when serving starts, not while the authored module is imported.
|
|
130
139
|
if (!appId || !appSecret) {
|
|
131
140
|
throw new Error(`${factoryName} requires appId + appSecret (developer console → Credentials & Basic Info)`);
|
|
132
141
|
}
|
|
133
142
|
const formatError = onError ?? defaultErrorMessage;
|
|
143
|
+
// Shared with the send tools (registered below). A turn's own posts go through `placeApi`.
|
|
134
144
|
const api = createFeishuApi({ kind, baseUrl, appId, appSecret });
|
|
135
145
|
void api.listAppScopes().then((scopes) => {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
log.info(`${label} group visibility: context-aware — buffered discussion enabled; bare replies work in a thread once the agent has been mentioned in it`);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
log.warn(`${label} group visibility: @mentions only — ${FEISHU_GROUP_CONTEXT_SCOPE} is not granted; bare replies in the agent's threads + group context buffering are unavailable`);
|
|
144
|
-
}
|
|
145
|
-
// Reported OUTSIDE the branch above: the quoted-message read runs in every chat type and every posture (a p2p
|
|
146
|
-
// thread's opening ask, any quoted @mention in a group), so pairing this warning with the group scope would
|
|
147
|
-
// leave a mention-only deployment silently losing every referent.
|
|
148
|
-
if (!scopeSatisfied(FEISHU_MESSAGE_READ_REQUEST, grantedScope)) {
|
|
149
|
-
log.warn(`${label} ${FEISHU_MESSAGE_READ_SCOPE} is not granted — a message quoted by an ask cannot be read, and degrades to a marker in the prompt`);
|
|
146
|
+
const granted = (name) => scopes.some((scope) => scope.name === name && scope.grantStatus === 1 && (scope.type === undefined || scope.type === "tenant"));
|
|
147
|
+
const missing = FEISHU_AGENT_SCOPES.filter((entry) => !scopeSatisfied(entry, granted));
|
|
148
|
+
if (missing.length === 0) {
|
|
149
|
+
log.info(`${label} permissions: every agent scope granted — the agent hears its group chats`);
|
|
150
150
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!isAbsolute(stateRoot)) {
|
|
155
|
-
throw new Error(`${factoryName} requires an absolute ctx.stateRoot, got "${stateRoot}"`);
|
|
156
|
-
}
|
|
157
|
-
const stateHome = join(stateRoot, "channels", kind);
|
|
158
|
-
ensureStateHome(stateHome); // buffers/files may carry chat content; the agent .gitignore covers .state/
|
|
151
|
+
for (const entry of missing)
|
|
152
|
+
log.warn(`${label} ${entry.request} is not granted — ${entry.withoutIt}`);
|
|
153
|
+
}, (error) => log.warn(`${label} could not inspect the app's permissions: ${String(error)}`));
|
|
159
154
|
const botOpenId = createBotIdentity({ api, appId, label, botFile: join(stateHome, "bot.json") });
|
|
160
155
|
const decide = route ?? ((event) => defaultFeishuRoute(event, { botOpenId: botOpenId() }));
|
|
161
156
|
const threadParticipants = createThreadParticipants(join(stateHome, "thread-participants.json"), label);
|
|
162
157
|
/** This channel's place key for a thread (the shared store is key-agnostic). */
|
|
163
158
|
// The SAME identity the session uses (`placeKey`) — a thread's place.
|
|
164
159
|
const threadKey = (chatId, threadId) => placeKey(kind, { chat_id: chatId, thread_id: threadId });
|
|
165
|
-
const buffer = createFeishuContextBuffer(join(stateHome, "buffers.json"), label);
|
|
166
160
|
const store = createTurnStore(join(stateHome, "turns.json"), {
|
|
167
161
|
label,
|
|
168
162
|
isRecord: isStoredFeishuTurn,
|
|
169
163
|
order: (a, b) => a.seq - b.seq,
|
|
170
164
|
});
|
|
165
|
+
// Every message this channel took as input (a turn, a /stop): the dedup ring. A history read also leaves these out,
|
|
166
|
+
// to shape the prompt only — what keeps a queued ask out of an earlier turn is the read's bound, not this ring.
|
|
171
167
|
const seen = createSeenRing(join(stateHome, "seen.json"), label);
|
|
168
|
+
const history = createFeishuPlaceHistory({
|
|
169
|
+
api,
|
|
170
|
+
appId,
|
|
171
|
+
label,
|
|
172
|
+
path: join(stateHome, "history.json"),
|
|
173
|
+
isTurnInput: (id) => seen.has(id),
|
|
174
|
+
});
|
|
175
|
+
// A group turn's room, for the thread-reading tool (`feishuThreads`): the chat it was asked in, and its threads.
|
|
176
|
+
const rooms = createRoomThreads({
|
|
177
|
+
list: (chatId) => history.threads(chatId),
|
|
178
|
+
read: async (chatId, threadId) => (await history.snapshot(feishuHistoryKey({ chatId, threadId }))).text ||
|
|
179
|
+
`(thread ${threadId} shows no messages)`,
|
|
180
|
+
});
|
|
181
|
+
registerRoomThreads(kind, stateHome, rooms);
|
|
182
|
+
/**
|
|
183
|
+
* The client a turn posts through: what it posts into its place (answer, queue notice, stop feedback) is in the
|
|
184
|
+
* session already, so the place records it and its next read leaves it out. The send tools use the plain client,
|
|
185
|
+
* so what the agent posts itself stays discussion. A routed turn has no place and reads no history.
|
|
186
|
+
*/
|
|
187
|
+
const placeApi = (historyKey) => historyKey === undefined ? api : api.recordingSends((id) => history.recordOutput(historyKey, id));
|
|
172
188
|
// Side tasks (stop feedback) run off the ingress path but drain in turnsIdle.
|
|
173
189
|
const sideTasks = createTaskTracker(label);
|
|
174
190
|
const targetOf = (r) => ({
|
|
@@ -185,16 +201,16 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
185
201
|
// operator log line.
|
|
186
202
|
const notifyDropped = (r) => {
|
|
187
203
|
const body = "⚠️ I couldn’t complete an earlier request — please ask again.";
|
|
188
|
-
void settleFeishuPreview(
|
|
204
|
+
void settleFeishuPreview(placeApi(r.historyKey), targetOf(r), r.preview, body).catch((e) => log.warn(`${label} could not notify a dropped turn (conversation=${r.conversation}): ${String(e)}`));
|
|
189
205
|
};
|
|
190
206
|
const runner = createTurnRunner({
|
|
191
207
|
label,
|
|
192
208
|
store,
|
|
193
|
-
|
|
209
|
+
discussion: history,
|
|
194
210
|
seen,
|
|
195
211
|
toStored: ({ preview: _live, ...intent }) => ({ ...intent, attempts: 0 }),
|
|
196
212
|
fromStored: ({ attempts: _a, ...intent }) => ({ ...intent, preview: undefined }),
|
|
197
|
-
|
|
213
|
+
discussionKey: (rec) => rec.historyKey === undefined ? undefined : { key: rec.historyKey, until: { at: rec.askAt, id: rec.id } },
|
|
198
214
|
where: (rec) => `chat=${rec.chatId}`,
|
|
199
215
|
// Queue feedback: mount that turn's preview early with a queue status.
|
|
200
216
|
onQueuedBehind: (rec) => {
|
|
@@ -205,7 +221,7 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
205
221
|
});
|
|
206
222
|
const mount = () => {
|
|
207
223
|
fired = true;
|
|
208
|
-
mountFeishuPreview(
|
|
224
|
+
mountFeishuPreview(placeApi(rec.historyKey), queueTargetOf(rec), QUEUED_PLACEHOLDER, label)
|
|
209
225
|
.then((preview) => {
|
|
210
226
|
rec.preview = preview;
|
|
211
227
|
}, (e) => log.warn(`${label} queue preview failed (the turn still runs): ${String(e)}`))
|
|
@@ -230,42 +246,54 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
230
246
|
// honest delayed status.
|
|
231
247
|
onDeferred: (rec) => {
|
|
232
248
|
if (rec.preview !== undefined) {
|
|
233
|
-
void settleFeishuPreview(
|
|
249
|
+
void settleFeishuPreview(placeApi(rec.historyKey), targetOf(rec), rec.preview, DEFERRED_PLACEHOLDER).catch((e) => log.warn(`${label} could not update a deferred turn's queue preview: ${String(e)}`));
|
|
234
250
|
}
|
|
235
251
|
},
|
|
236
252
|
notifyDropped,
|
|
237
253
|
// `rec.preview`: the queue card THIS process mounted if the record waited behind another turn.
|
|
238
|
-
deliverAnswer: (rec, answer) => portJoin(() => settleFeishuPreview(
|
|
239
|
-
execute: (rec, discussion, onAnswered) =>
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
// snapshot before either commits it. That fan-out loses nothing; claiming by buffer key would instead couple
|
|
243
|
-
// otherwise-independent root sessions and require failure rollback.
|
|
244
|
-
const room = rec.roomBufferKey !== undefined ? buffer.peek(rec.roomBufferKey) : undefined;
|
|
254
|
+
deliverAnswer: (rec, answer) => portJoin(() => settleFeishuPreview(placeApi(rec.historyKey), targetOf(rec), rec.preview, answer)),
|
|
255
|
+
execute: (rec, discussion, onAnswered) => Effect.promise(async () =>
|
|
256
|
+
// Read-only: the room's own next answered turn still reads it into the room's memory (§8).
|
|
257
|
+
rec.roomKey !== undefined ? history.room(rec.roomKey) : undefined).pipe(Effect.flatMap((room) => {
|
|
245
258
|
const roomBlock = room?.text
|
|
246
259
|
? `[recent discussion in the room this thread branched from — not yet answered there:\n${room.text}\n]\n\n`
|
|
247
260
|
: "";
|
|
248
261
|
const prompt = `${roomBlock}${discussionBlock(discussion.text)}${rec.baseText}`;
|
|
249
|
-
// Room
|
|
262
|
+
// Room messages FIRST: the collector keeps the TAIL under its cap, so the thread's own attachments win the
|
|
250
263
|
// slots.
|
|
251
|
-
const buffered =
|
|
264
|
+
const buffered = collectFoldedAttachments([...(room?.folded ?? []), ...discussion.consumed.flatMap((read) => read.folded)], {
|
|
252
265
|
images: rec.images.map((ref) => ({ messageId: ref.msg, key: ref.key })),
|
|
253
|
-
files: rec.files.map((ref) => ({ messageId: ref.msg, key: ref.key
|
|
266
|
+
files: rec.files.map((ref) => ({ messageId: ref.msg, key: ref.key })),
|
|
254
267
|
});
|
|
255
|
-
// Recorded at ingress (see submit)
|
|
256
|
-
// that only looks like a place key.
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
// Recorded at ingress (see submit), never re-derived from the conversation key, which may be a routed
|
|
269
|
+
// OPAQUE id that only looks like a place key.
|
|
270
|
+
const parent = rec.parentConversation;
|
|
271
|
+
// A group turn under the default routing reads its chat's threads; a DM has none to read.
|
|
272
|
+
const threadsRoom = rec.group && rec.historyKey !== undefined ? feishuPlaceOf(rec.historyKey).chatId : undefined;
|
|
273
|
+
const turnApi = placeApi(rec.historyKey);
|
|
274
|
+
return feishuReply(feishuTurnStream(agent, {
|
|
275
|
+
session: (chain) => sessionFor(rec.conversation, {
|
|
276
|
+
forkFrom: async () => {
|
|
277
|
+
// A thread of a room nothing was said in starts empty: there is nothing to fork.
|
|
278
|
+
const room = parent === undefined ? undefined : recordedSession(parent);
|
|
279
|
+
return room === undefined ? undefined : threadForkSource(agent, room, chain);
|
|
280
|
+
},
|
|
281
|
+
}),
|
|
282
|
+
...(threadsRoom === undefined
|
|
283
|
+
? {}
|
|
284
|
+
: { enter: (sessionId) => rooms.enter(sessionId, threadsRoom) }),
|
|
285
|
+
key: rec.id,
|
|
286
|
+
text: prompt,
|
|
287
|
+
}, {
|
|
288
|
+
api: turnApi,
|
|
260
289
|
chatId: rec.chatId,
|
|
261
290
|
filesDir: attachmentsDir(stateHome),
|
|
262
291
|
label,
|
|
263
292
|
appId,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
},
|
|
293
|
+
}, { primary: { images: rec.images, files: rec.files, parentId: rec.parentId }, buffered }), turnApi, targetOf(rec), formatError, rec.preview, label, onAnswered);
|
|
294
|
+
})),
|
|
267
295
|
});
|
|
268
|
-
registerFeishuApi(
|
|
296
|
+
registerFeishuApi(appId, kind, api);
|
|
269
297
|
let seqCounter = runner.recover().reduce((max, r) => Math.max(max, r.seq), 0);
|
|
270
298
|
// Who the agent has heard in a thread decides whether a bare message addresses it (participant model §3), and it
|
|
271
299
|
// comes from what this channel observed.
|
|
@@ -296,7 +324,6 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
296
324
|
const normalized = normalizeFeishuMessage(event);
|
|
297
325
|
if (!normalized)
|
|
298
326
|
return;
|
|
299
|
-
const bufferKey = feishuBufferPlaceKey(normalized.conversation);
|
|
300
327
|
const isHumanGroup = event.sender?.sender_type === "user" && m.chat_type === "group";
|
|
301
328
|
// Listening is not speaking: every message the channel can see refines who takes part in its thread, whether or
|
|
302
329
|
// not it is answered.
|
|
@@ -312,50 +339,26 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
312
339
|
r = {};
|
|
313
340
|
}
|
|
314
341
|
if (!r) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (bodyText) {
|
|
318
|
-
const resources = normalized.content.resources;
|
|
319
|
-
const images = resources
|
|
320
|
-
.filter((resource) => resource.kind === "image")
|
|
321
|
-
.map((resource) => ({ messageId: resource.messageId, key: resource.key }));
|
|
322
|
-
const files = resources
|
|
323
|
-
.filter((resource) => resource.kind === "file" || resource.kind === "audio" || resource.kind === "video")
|
|
324
|
-
.map((resource) => ({
|
|
325
|
-
messageId: resource.messageId,
|
|
326
|
-
key: resource.key,
|
|
327
|
-
name: resource.name,
|
|
328
|
-
}));
|
|
329
|
-
// A write failure escapes this boundary.
|
|
330
|
-
buffer.push(bufferKey, {
|
|
331
|
-
sender: senderLabel(event.sender) ?? "someone",
|
|
332
|
-
body: bodyText,
|
|
333
|
-
messageId: m.message_id,
|
|
334
|
-
replyTo: m.parent_id,
|
|
335
|
-
files: files.length ? files : undefined,
|
|
336
|
-
images: images.length ? images : undefined,
|
|
337
|
-
});
|
|
338
|
-
seen.add(m.message_id);
|
|
339
|
-
log.debug(`${label} buffered unsummoned group message ${m.message_id} (place ${bufferKey})`);
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
log.debug(`${label} not summoned — ignoring empty message ${m.message_id} (chat ${m.chat_id})`);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
else {
|
|
346
|
-
log.debug(`${label} not summoned — ignoring message ${m.message_id} (chat ${m.chat_id}, ${m.chat_type})`);
|
|
347
|
-
}
|
|
342
|
+
// Not lost: the turn that is summoned here reads it from the place's history.
|
|
343
|
+
log.debug(`${label} not summoned — message ${m.message_id} (chat ${m.chat_id}, ${m.chat_type})`);
|
|
348
344
|
return;
|
|
349
345
|
}
|
|
350
|
-
// Memory follows the place (participant model §5): one
|
|
351
|
-
const routed = r.
|
|
352
|
-
const
|
|
353
|
-
// Lineage is recorded ONLY for the default place-derived
|
|
354
|
-
const
|
|
346
|
+
// Memory follows the place (participant model §5): one conversation per chat, and one per thread.
|
|
347
|
+
const routed = r.conversation;
|
|
348
|
+
const conversation = routed ?? placeKey(kind, m);
|
|
349
|
+
// Lineage is recorded ONLY for the default place-derived conversation.
|
|
350
|
+
const parentConversation = routed === undefined && m.thread_id !== undefined ? placeKey(kind, { chat_id: m.chat_id }) : undefined;
|
|
355
351
|
// Read BEFORE this turn records its own participation below, or it is always true.
|
|
356
|
-
const
|
|
357
|
-
?
|
|
352
|
+
const roomKey = parentConversation !== undefined && !threadParticipants.agentSpokeIn(conversation)
|
|
353
|
+
? feishuHistoryKey({ chatId: m.chat_id })
|
|
358
354
|
: undefined;
|
|
355
|
+
// Every place's discussion under the default routing, a DM's too (what a send tool posted there is in no session);
|
|
356
|
+
// a routed session's place is the router's to decide.
|
|
357
|
+
const historyKey = route === undefined ? feishuHistoryKey(normalized.conversation) : undefined;
|
|
358
|
+
// The platform stamps every message; acceptance time stands in only if a payload lacks it, and is still a bound
|
|
359
|
+
// the ask was created before.
|
|
360
|
+
const created = Number(m.create_time);
|
|
361
|
+
const askAt = Number.isFinite(created) && created > 0 ? created : Date.now();
|
|
359
362
|
const chatId = r.chatId ?? m.chat_id;
|
|
360
363
|
const sameTarget = chatId === m.chat_id;
|
|
361
364
|
// Answer where asked (§4): quote in a group so the ask is identifiable among many speakers, stay plain in an
|
|
@@ -367,8 +370,10 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
367
370
|
// Explicit user stop: a control action, never a turn — it must not queue behind the run it stops.
|
|
368
371
|
if (isStopText(normalized.content.text.replace(/@\S+/g, " "))) {
|
|
369
372
|
seen.add(m.message_id);
|
|
370
|
-
sideTasks.track(dispatchStop(
|
|
371
|
-
.then((feedback) =>
|
|
373
|
+
sideTasks.track(dispatchStop(agent, recordedSession(conversation), label)
|
|
374
|
+
.then((feedback) => placeApi(historyKey)
|
|
375
|
+
.sendText({ chatId, replyTo, replyInThread }, feedback)
|
|
376
|
+
.then(() => undefined))
|
|
372
377
|
.catch((error) => log.warn(`${label} stop feedback failed: ${String(error)}`)));
|
|
373
378
|
return;
|
|
374
379
|
}
|
|
@@ -385,9 +390,11 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
385
390
|
runner.submit({
|
|
386
391
|
id: m.message_id,
|
|
387
392
|
seq: ++seqCounter, // arrival order; the turn store replays by it
|
|
388
|
-
|
|
393
|
+
conversation,
|
|
389
394
|
baseText,
|
|
390
|
-
|
|
395
|
+
...(historyKey !== undefined ? { historyKey } : {}),
|
|
396
|
+
group: m.chat_type === "group",
|
|
397
|
+
askAt,
|
|
391
398
|
chatId,
|
|
392
399
|
replyTo,
|
|
393
400
|
queueReplyTo,
|
|
@@ -395,14 +402,14 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
|
|
|
395
402
|
// A quote is the user explicitly pointing at something that may predate this session (§8 rung 2), so it is
|
|
396
403
|
// always loaded.
|
|
397
404
|
parentId: m.parent_id,
|
|
398
|
-
...(
|
|
399
|
-
...(
|
|
405
|
+
...(parentConversation !== undefined ? { parentConversation } : {}),
|
|
406
|
+
...(roomKey !== undefined && historyKey !== undefined ? { roomKey } : {}),
|
|
400
407
|
images,
|
|
401
408
|
files,
|
|
402
409
|
}, true);
|
|
403
410
|
// Answering inside a thread makes the agent a participant of it, which is what lets the NEXT bare message
|
|
404
411
|
// address it without a mention (§3).
|
|
405
|
-
if (heard && replyInThread === true && sameTarget && r.
|
|
412
|
+
if (heard && replyInThread === true && sameTarget && r.conversation === undefined) {
|
|
406
413
|
// Both halves in ONE merge, like Slack's: a record that needed an earlier merge to survive could otherwise
|
|
407
414
|
// say "answered here, heard nobody".
|
|
408
415
|
threadParticipants.merge(heard.key, { agentSpoke: true, humans: [heard.speaker] });
|
|
@@ -508,7 +515,11 @@ export function buildFeishuChannel(profile, opts, factoryName) {
|
|
|
508
515
|
const createRuntime = createFeishuRuntimeFactory(profile, opts, factoryName);
|
|
509
516
|
return (ctx) => {
|
|
510
517
|
if (!opts.verificationToken) {
|
|
511
|
-
|
|
518
|
+
// The scaffolded file reaches this under `start` with nothing set, so the error names both ways out.
|
|
519
|
+
throw new Error(`${factoryName} receives by webhook and needs a non-empty verificationToken: \`fastagent deploy <host> --run\` ` +
|
|
520
|
+
`or \`fastagent add ${profile.kind} --ingress webhook\` captures ${profile.envPrefix}_VERIFICATION_TOKEN ` +
|
|
521
|
+
`(console → Events & Callbacks has it too), or set ${profile.envPrefix}_INGRESS=websocket to receive by ` +
|
|
522
|
+
`WebSocket instead`);
|
|
512
523
|
}
|
|
513
524
|
return createFeishuWebhookRoutes(profile, opts, createRuntime(ctx));
|
|
514
525
|
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type PlaceDiscussion, type PlaceHistory, type PlaceMessage, type PlaceRead } from "../kit/place-history.ts";
|
|
2
|
+
import type { FeishuApi } from "./feishu-api.ts";
|
|
3
|
+
/** A message's position: `create_time` alone is not unique, so the id breaks ties. */
|
|
4
|
+
interface FeishuCursor {
|
|
5
|
+
at: number;
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
export type FeishuPlaceRead = PlaceRead<FeishuCursor>;
|
|
9
|
+
export type FeishuDiscussion = PlaceDiscussion<FeishuCursor>;
|
|
10
|
+
export interface FeishuPlaceHistory extends PlaceHistory<FeishuCursor> {
|
|
11
|
+
/** The chat's threads among its newest messages, for a tool (`kit/room-threads.ts`). */
|
|
12
|
+
threads(chatId: string): Promise<string>;
|
|
13
|
+
}
|
|
14
|
+
/** A background resource carried into a turn, with attribution for its prompt manifest. */
|
|
15
|
+
export interface FeishuBufferedRef {
|
|
16
|
+
messageId: string;
|
|
17
|
+
key: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
from: string;
|
|
20
|
+
}
|
|
21
|
+
/** A place: the chat, or a thread within it. The turn record carries it as this key. */
|
|
22
|
+
export declare function feishuHistoryKey(place: {
|
|
23
|
+
chatId: string;
|
|
24
|
+
threadId?: string;
|
|
25
|
+
}): string;
|
|
26
|
+
/** The inverse of {@link feishuHistoryKey}. */
|
|
27
|
+
export declare function feishuPlaceOf(key: string): {
|
|
28
|
+
chatId: string;
|
|
29
|
+
threadId?: string;
|
|
30
|
+
};
|
|
31
|
+
/** Select the most recent background resources of the folded messages, excluding the turn's own primary ones. */
|
|
32
|
+
export declare function collectFoldedAttachments(folded: readonly PlaceMessage[], primary: {
|
|
33
|
+
files: {
|
|
34
|
+
messageId: string;
|
|
35
|
+
key: string;
|
|
36
|
+
}[];
|
|
37
|
+
images: {
|
|
38
|
+
messageId: string;
|
|
39
|
+
key: string;
|
|
40
|
+
}[];
|
|
41
|
+
}): {
|
|
42
|
+
files: FeishuBufferedRef[];
|
|
43
|
+
images: FeishuBufferedRef[];
|
|
44
|
+
skipped: number;
|
|
45
|
+
};
|
|
46
|
+
export declare function createFeishuPlaceHistory(deps: {
|
|
47
|
+
api: Pick<FeishuApi, "listMessages" | "chatMemberNames">;
|
|
48
|
+
/** THIS app's id: a message whose sender is this app is the agent's own. */
|
|
49
|
+
appId: string;
|
|
50
|
+
label: string;
|
|
51
|
+
/** Where each place's cursor and outputs persist. */
|
|
52
|
+
path: string;
|
|
53
|
+
isTurnInput(messageId: string): boolean;
|
|
54
|
+
}): FeishuPlaceHistory;
|
|
55
|
+
export {};
|