@fastagent-sh/fastagent 0.21.0 → 0.22.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 +9 -12
- package/dist/agent.d.ts +24 -37
- package/dist/agent.js +7 -15
- package/dist/atomic-write.d.ts +13 -17
- package/dist/atomic-write.js +18 -29
- package/dist/bind.d.ts +12 -22
- package/dist/bind.js +18 -27
- package/dist/channel.d.ts +11 -33
- package/dist/channels/agentcore-limits.d.ts +2 -13
- package/dist/channels/agentcore-limits.js +2 -13
- package/dist/channels/agentcore-protocol.d.ts +67 -58
- package/dist/channels/agentcore-protocol.js +23 -15
- package/dist/channels/agentcore-service.d.ts +19 -33
- package/dist/channels/agentcore-service.js +176 -59
- package/dist/channels/agentcore.d.ts +39 -33
- package/dist/channels/agentcore.js +90 -224
- package/dist/channels/body.d.ts +15 -2
- package/dist/channels/body.js +24 -2
- package/dist/channels/busy.d.ts +1 -21
- package/dist/channels/busy.js +2 -39
- package/dist/channels/control.d.ts +17 -62
- package/dist/channels/control.js +74 -182
- package/dist/channels/define-channel.d.ts +39 -0
- package/dist/channels/define-channel.js +9 -0
- package/dist/channels/discover.d.ts +10 -31
- package/dist/channels/discover.js +37 -31
- package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
- package/dist/channels/feishu/bootstrap-token.js +7 -23
- package/dist/channels/feishu/card.d.ts +14 -28
- package/dist/channels/feishu/card.js +16 -30
- package/dist/channels/feishu/cloud.d.ts +1 -5
- package/dist/channels/feishu/context-buffer.d.ts +4 -23
- package/dist/channels/feishu/context-buffer.js +8 -31
- package/dist/channels/feishu/crypto.d.ts +10 -5
- package/dist/channels/feishu/crypto.js +12 -19
- package/dist/channels/feishu/feishu-api.d.ts +2 -1
- package/dist/channels/feishu/feishu-api.js +5 -4
- package/dist/channels/feishu/feishu.d.ts +4 -10
- package/dist/channels/feishu/feishu.js +113 -227
- package/dist/channels/feishu/invoke-turn.d.ts +5 -4
- package/dist/channels/feishu/invoke-turn.js +36 -54
- package/dist/channels/feishu/model.d.ts +4 -21
- package/dist/channels/feishu/model.js +1 -8
- package/dist/channels/feishu/normalize.d.ts +2 -10
- package/dist/channels/feishu/normalize.js +7 -42
- package/dist/channels/feishu/parse.d.ts +9 -29
- package/dist/channels/feishu/parse.js +8 -26
- package/dist/channels/feishu/preview.d.ts +12 -14
- package/dist/channels/feishu/preview.js +108 -207
- package/dist/channels/feishu/register-app.d.ts +8 -23
- package/dist/channels/feishu/register-app.js +7 -20
- package/dist/channels/feishu/register-webhook.d.ts +2 -9
- package/dist/channels/feishu/register-webhook.js +9 -33
- package/dist/channels/feishu/scaffold/channel.ts +27 -19
- package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/feishu/setup-mode.d.ts +11 -36
- package/dist/channels/feishu/setup-mode.js +7 -26
- package/dist/channels/feishu/ws-ingress.d.ts +1 -2
- package/dist/channels/feishu/ws-ingress.js +6 -8
- package/dist/channels/http.d.ts +15 -15
- package/dist/channels/http.js +19 -9
- package/dist/channels/kit/attachment-path.d.ts +4 -5
- package/dist/channels/kit/attachment-path.js +10 -34
- package/dist/channels/kit/context-buffer.d.ts +14 -21
- package/dist/channels/kit/context-buffer.js +25 -38
- package/dist/channels/kit/delivery.d.ts +35 -0
- package/dist/channels/kit/delivery.js +129 -0
- package/dist/channels/kit/event-stream.d.ts +5 -0
- package/dist/channels/kit/event-stream.js +29 -0
- package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
- package/dist/channels/kit/invoke-turn-kit.js +89 -32
- package/dist/channels/kit/preview-kit.d.ts +18 -72
- package/dist/channels/kit/preview-kit.js +22 -88
- package/dist/channels/kit/signature.d.ts +3 -9
- package/dist/channels/kit/signature.js +3 -9
- package/dist/channels/kit/state.d.ts +1 -2
- package/dist/channels/kit/state.js +5 -14
- package/dist/channels/kit/stop-command.d.ts +1 -2
- package/dist/channels/kit/stop-command.js +6 -9
- package/dist/channels/kit/tasks.d.ts +1 -1
- package/dist/channels/kit/tasks.js +8 -21
- package/dist/channels/kit/text.d.ts +2 -16
- package/dist/channels/kit/text.js +23 -31
- package/dist/channels/kit/thread-participants.d.ts +4 -17
- package/dist/channels/kit/thread-participants.js +8 -55
- package/dist/channels/kit/transport.d.ts +26 -0
- package/dist/channels/kit/transport.js +20 -0
- package/dist/channels/kit/turn-queue.d.ts +7 -11
- package/dist/channels/kit/turn-queue.js +21 -39
- package/dist/channels/kit/turn-runner.d.ts +32 -18
- package/dist/channels/kit/turn-runner.js +98 -53
- package/dist/channels/kit/turn-store.d.ts +32 -32
- package/dist/channels/kit/turn-store.js +45 -88
- package/dist/channels/lark/lark.d.ts +1 -5
- package/dist/channels/lark/onboard.d.ts +3 -12
- package/dist/channels/lark/onboard.js +3 -13
- package/dist/channels/lark/scaffold/channel.ts +27 -19
- package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/registration.d.ts +9 -35
- package/dist/channels/registration.js +6 -37
- package/dist/channels/secret.js +2 -5
- package/dist/channels/serve.d.ts +47 -44
- package/dist/channels/serve.js +165 -101
- package/dist/channels/slack/config-api.d.ts +4 -27
- package/dist/channels/slack/config-api.js +5 -34
- package/dist/channels/slack/context-buffer.d.ts +2 -3
- package/dist/channels/slack/context-buffer.js +5 -5
- package/dist/channels/slack/invoke-turn.d.ts +3 -1
- package/dist/channels/slack/invoke-turn.js +25 -34
- package/dist/channels/slack/manifest.js +1 -3
- package/dist/channels/slack/model.d.ts +1 -2
- package/dist/channels/slack/model.js +1 -1
- package/dist/channels/slack/onboard.d.ts +4 -3
- package/dist/channels/slack/onboard.js +8 -11
- package/dist/channels/slack/onboarding-state.d.ts +3 -5
- package/dist/channels/slack/onboarding-state.js +9 -6
- package/dist/channels/slack/parse.d.ts +4 -13
- package/dist/channels/slack/parse.js +7 -28
- package/dist/channels/slack/preview.d.ts +12 -2
- package/dist/channels/slack/preview.js +292 -269
- package/dist/channels/slack/reaction.d.ts +11 -3
- package/dist/channels/slack/reaction.js +19 -8
- package/dist/channels/slack/register-webhook.js +4 -7
- package/dist/channels/slack/scaffold/channel.ts +20 -13
- package/dist/channels/slack/scaffold/slack-send.ts +2 -2
- package/dist/channels/slack/setup-server.d.ts +1 -5
- package/dist/channels/slack/setup-server.js +1 -5
- package/dist/channels/slack/shared-api.d.ts +1 -4
- package/dist/channels/slack/shared-api.js +2 -13
- package/dist/channels/slack/slack-api.d.ts +2 -1
- package/dist/channels/slack/slack-api.js +6 -4
- package/dist/channels/slack/slack.d.ts +11 -14
- package/dist/channels/slack/slack.js +121 -122
- package/dist/channels/sse.d.ts +1 -1
- package/dist/channels/sse.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +7 -22
- package/dist/channels/telegram/context-buffer.js +6 -23
- package/dist/channels/telegram/invoke-turn.d.ts +7 -7
- package/dist/channels/telegram/invoke-turn.js +24 -43
- package/dist/channels/telegram/parse.d.ts +24 -51
- package/dist/channels/telegram/parse.js +32 -84
- package/dist/channels/telegram/preview.d.ts +10 -1
- package/dist/channels/telegram/preview.js +79 -100
- package/dist/channels/telegram/register-webhook.js +5 -23
- package/dist/channels/telegram/scaffold/channel.ts +25 -18
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
- package/dist/channels/telegram/telegram-api.d.ts +4 -3
- package/dist/channels/telegram/telegram-api.js +9 -7
- package/dist/channels/telegram/telegram.d.ts +4 -18
- package/dist/channels/telegram/telegram.js +38 -94
- package/dist/channels/telegram/turn-store.d.ts +11 -14
- package/dist/channels/telegram/turn-store.js +8 -6
- package/dist/channels/wait-health.d.ts +4 -6
- package/dist/channels/wait-health.js +10 -14
- package/dist/cli/add-feishu.d.ts +2 -12
- package/dist/cli/add-feishu.js +24 -71
- package/dist/cli/add-slack.d.ts +5 -3
- package/dist/cli/add-slack.js +53 -76
- package/dist/cli/auth-view.d.ts +2 -16
- package/dist/cli/auth-view.js +2 -16
- package/dist/cli/commands/add.js +25 -43
- package/dist/cli/commands/chat.d.ts +0 -1
- package/dist/cli/commands/chat.js +5 -6
- package/dist/cli/commands/deploy/agentcore.js +60 -65
- package/dist/cli/commands/deploy/docker.js +50 -23
- package/dist/cli/commands/deploy/fly.js +23 -61
- package/dist/cli/commands/deploy/railway.js +23 -18
- package/dist/cli/commands/deploy/shared.d.ts +38 -57
- package/dist/cli/commands/deploy/shared.js +40 -59
- package/dist/cli/commands/deploy.d.ts +10 -27
- package/dist/cli/commands/deploy.js +38 -60
- package/dist/cli/commands/destroy.d.ts +4 -0
- package/dist/cli/commands/destroy.js +56 -0
- package/dist/cli/commands/dev.d.ts +2 -1
- package/dist/cli/commands/dev.js +20 -23
- package/dist/cli/commands/info.d.ts +0 -2
- package/dist/cli/commands/info.js +83 -49
- package/dist/cli/commands/init.d.ts +1 -2
- package/dist/cli/commands/init.js +16 -55
- package/dist/cli/commands/invoke.d.ts +0 -1
- package/dist/cli/commands/invoke.js +5 -7
- package/dist/cli/commands/login.d.ts +2 -1
- package/dist/cli/commands/login.js +33 -38
- package/dist/cli/commands/logs.js +3 -4
- package/dist/cli/commands/models.js +1 -5
- package/dist/cli/commands/routine-run.d.ts +6 -0
- package/dist/cli/commands/routine-run.js +47 -0
- package/dist/cli/commands/routine.d.ts +25 -0
- package/dist/cli/commands/routine.js +122 -0
- package/dist/cli/commands/start.d.ts +31 -3
- package/dist/cli/commands/start.js +131 -81
- package/dist/cli/commands/tool.js +26 -12
- package/dist/cli/fail.d.ts +13 -23
- package/dist/cli/fail.js +20 -23
- package/dist/cli/invoke-stream.d.ts +1 -8
- package/dist/cli/kernel.d.ts +6 -38
- package/dist/cli/kernel.js +14 -77
- package/dist/cli/models-view.d.ts +1 -9
- package/dist/cli/models-view.js +11 -22
- package/dist/cli/program.d.ts +5 -6
- package/dist/cli/program.js +120 -157
- package/dist/cli/serve.d.ts +57 -41
- package/dist/cli/serve.js +94 -121
- package/dist/cli/shared.d.ts +15 -61
- package/dist/cli/shared.js +55 -181
- package/dist/cli.js +1 -8
- package/dist/collect.d.ts +6 -0
- package/dist/collect.js +6 -0
- package/dist/core.d.ts +4 -1
- package/dist/core.js +9 -7
- package/dist/declared-secrets.d.ts +55 -0
- package/dist/declared-secrets.js +88 -0
- package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
- package/dist/deploy/agentcore/aws-cli.js +64 -0
- package/dist/deploy/agentcore/destroy.d.ts +51 -0
- package/dist/deploy/agentcore/destroy.js +273 -0
- package/dist/deploy/agentcore/forwarder.js +28 -79
- package/dist/deploy/agentcore/logs.d.ts +2 -18
- package/dist/deploy/agentcore/logs.js +27 -51
- package/dist/deploy/agentcore/plan.d.ts +85 -105
- package/dist/deploy/agentcore/plan.js +152 -314
- package/dist/deploy/agentcore/run.d.ts +28 -28
- package/dist/deploy/agentcore/run.js +227 -306
- package/dist/deploy/agentcore/zip.d.ts +2 -15
- package/dist/deploy/agentcore/zip.js +3 -17
- package/dist/deploy/build-context.d.ts +21 -0
- package/dist/deploy/build-context.js +137 -0
- package/dist/deploy/channel-ingress.d.ts +26 -39
- package/dist/deploy/channel-ingress.js +57 -28
- package/dist/deploy/container.d.ts +20 -34
- package/dist/deploy/container.js +49 -100
- package/dist/deploy/docker/plan.d.ts +12 -11
- package/dist/deploy/docker/plan.js +43 -24
- package/dist/deploy/docker/run.d.ts +22 -25
- package/dist/deploy/docker/run.js +39 -37
- package/dist/deploy/fly/plan.d.ts +15 -44
- package/dist/deploy/fly/plan.js +41 -73
- package/dist/deploy/fly/run.d.ts +16 -51
- package/dist/deploy/fly/run.js +42 -95
- package/dist/deploy/hosts.d.ts +24 -3
- package/dist/deploy/hosts.js +24 -3
- package/dist/deploy/preflight.d.ts +47 -41
- package/dist/deploy/preflight.js +282 -383
- package/dist/deploy/railway/plan.d.ts +16 -42
- package/dist/deploy/railway/plan.js +42 -60
- package/dist/deploy/railway/run.d.ts +30 -68
- package/dist/deploy/railway/run.js +86 -100
- package/dist/deploy/registration-gate.d.ts +6 -13
- package/dist/deploy/residency.d.ts +37 -0
- package/dist/deploy/residency.js +24 -0
- package/dist/deploy/runner.d.ts +10 -9
- package/dist/deploy/runner.js +7 -18
- package/dist/deploy/secrets.d.ts +40 -41
- package/dist/deploy/secrets.js +117 -103
- package/dist/deploy/workspace.d.ts +50 -0
- package/dist/deploy/workspace.js +197 -0
- package/dist/dev-supervisor.d.ts +1 -9
- package/dist/dev-supervisor.js +21 -64
- package/dist/effect-port.d.ts +50 -0
- package/dist/effect-port.js +77 -0
- package/dist/engines/pi/agent-session-factory.d.ts +50 -69
- package/dist/engines/pi/agent-session-factory.js +249 -146
- package/dist/engines/pi/auth.d.ts +15 -11
- package/dist/engines/pi/auth.js +96 -99
- package/dist/engines/pi/chat.d.ts +1 -5
- package/dist/engines/pi/chat.js +2 -14
- package/dist/engines/pi/config.d.ts +53 -51
- package/dist/engines/pi/config.js +83 -92
- package/dist/engines/pi/create.d.ts +60 -75
- package/dist/engines/pi/create.js +108 -141
- package/dist/engines/pi/definition.d.ts +14 -58
- package/dist/engines/pi/definition.js +21 -85
- package/dist/engines/pi/invoke-session.d.ts +5 -25
- package/dist/engines/pi/invoke-session.js +209 -244
- package/dist/engines/pi/login.d.ts +95 -35
- package/dist/engines/pi/login.js +259 -107
- package/dist/engines/pi/machine.d.ts +31 -0
- package/dist/engines/pi/machine.js +106 -0
- package/dist/engines/pi/models.d.ts +70 -89
- package/dist/engines/pi/models.js +188 -81
- package/dist/engines/pi/open.d.ts +65 -53
- package/dist/engines/pi/open.js +129 -69
- package/dist/engines/pi/report.d.ts +3 -12
- package/dist/engines/pi/report.js +2 -16
- package/dist/engines/pi/search-tools.d.ts +1 -9
- package/dist/engines/pi/search-tools.js +26 -57
- package/dist/engines/pi/service.d.ts +2 -16
- package/dist/engines/pi/service.js +2 -16
- package/dist/engines/pi/session-builder.d.ts +5 -9
- package/dist/engines/pi/session-builder.js +44 -82
- package/dist/engines/pi/session-control.js +286 -269
- package/dist/engines/pi/session-effects.d.ts +14 -0
- package/dist/engines/pi/session-effects.js +30 -0
- package/dist/engines/pi/session-inheritance.d.ts +9 -18
- package/dist/engines/pi/session-inheritance.js +103 -160
- package/dist/engines/pi/session-markers.d.ts +31 -30
- package/dist/engines/pi/session-markers.js +26 -25
- package/dist/engines/pi/session-settings.d.ts +6 -22
- package/dist/engines/pi/session-settings.js +10 -21
- package/dist/engines/pi/session-store.d.ts +26 -92
- package/dist/engines/pi/session-store.js +96 -310
- package/dist/engines/pi/tool-context.d.ts +17 -45
- package/dist/engines/pi/tool-context.js +7 -26
- package/dist/engines/pi/tool.d.ts +45 -36
- package/dist/engines/pi/tool.js +43 -57
- package/dist/engines/pi/turn-kit.d.ts +14 -52
- package/dist/engines/pi/turn-kit.js +18 -67
- package/dist/engines/pi/wake-tool.d.ts +2 -10
- package/dist/engines/pi/wake-tool.js +7 -27
- package/dist/env.d.ts +27 -28
- package/dist/env.js +45 -50
- package/dist/index.js +0 -2
- package/dist/lark.d.ts +1 -2
- package/dist/lark.js +1 -2
- package/dist/loader.d.ts +14 -55
- package/dist/loader.js +24 -43
- package/dist/log.d.ts +2 -12
- package/dist/log.js +3 -24
- package/dist/node.d.ts +2 -15
- package/dist/node.js +2 -18
- package/dist/observe.d.ts +2 -8
- package/dist/open-url.d.ts +1 -1
- package/dist/open-url.js +1 -1
- package/dist/paths.d.ts +70 -134
- package/dist/paths.js +165 -232
- package/dist/pi.d.ts +4 -6
- package/dist/pi.js +3 -3
- package/dist/proxy.d.ts +16 -6
- package/dist/proxy.js +52 -16
- package/dist/runtime.d.ts +18 -12
- package/dist/runtime.js +18 -11
- package/dist/scaffold/add-channel.d.ts +14 -31
- package/dist/scaffold/add-channel.js +48 -112
- package/dist/scaffold/init.d.ts +9 -31
- package/dist/scaffold/init.js +45 -138
- package/dist/scaffold/templates/env.example +6 -4
- package/dist/scaffold/templates/fastagent.config.ts +36 -0
- package/dist/scaffold/templates.d.ts +3 -3
- package/dist/scaffold/templates.js +5 -8
- package/dist/scaffold/vendor-skill.d.ts +2 -5
- package/dist/scaffold/vendor-skill.js +19 -29
- package/dist/schedule/cron.d.ts +9 -5
- package/dist/schedule/cron.js +9 -11
- package/dist/schedule/discover.d.ts +12 -12
- package/dist/schedule/discover.js +43 -34
- package/dist/schedule/routine.d.ts +43 -0
- package/dist/schedule/routine.js +32 -0
- package/dist/schedule/run.d.ts +55 -0
- package/dist/schedule/run.js +146 -0
- package/dist/schedule/scheduler.d.ts +44 -55
- package/dist/schedule/scheduler.js +291 -265
- package/dist/schedule/state.d.ts +105 -10
- package/dist/schedule/state.js +293 -19
- package/dist/schedule/wake-alarm.d.ts +10 -23
- package/dist/schedule/wake-alarm.js +91 -121
- package/dist/schedule/wakeups.d.ts +23 -39
- package/dist/schedule/wakeups.js +33 -75
- package/dist/secrets-gate.d.ts +32 -0
- package/dist/secrets-gate.js +33 -0
- package/dist/service.d.ts +137 -98
- package/dist/service.js +250 -252
- package/dist/session-remote.d.ts +33 -44
- package/dist/session-remote.js +203 -192
- package/dist/session.d.ts +153 -228
- package/dist/session.js +33 -55
- package/dist/tunnel.d.ts +8 -36
- package/dist/tunnel.js +24 -65
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +10 -16
- package/dist/channels/agentcore-state.d.ts +0 -77
- package/dist/channels/agentcore-state.js +0 -276
- package/dist/channels/github/github.d.ts +0 -31
- package/dist/channels/github/github.js +0 -89
- package/dist/channels/github/scaffold/channel.ts +0 -25
- package/dist/cli/commands/attach.d.ts +0 -108
- package/dist/cli/commands/attach.js +0 -637
- package/dist/cli/commands/fire.d.ts +0 -7
- package/dist/cli/commands/fire.js +0 -38
- package/dist/cli/commands/schedule.d.ts +0 -12
- package/dist/cli/commands/schedule.js +0 -87
- package/dist/github.d.ts +0 -3
- package/dist/github.js +0 -2
- package/dist/scaffold/templates/fastagent.config.mjs +0 -23
- package/dist/schedule/audit.d.ts +0 -18
- package/dist/schedule/audit.js +0 -57
- package/dist/schedule/schedule.d.ts +0 -35
- package/dist/schedule/schedule.js +0 -23
|
@@ -1,21 +1,17 @@
|
|
|
1
|
+
/** Per-session FIFO execution. */
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import type * as Scope from "effect/Scope";
|
|
1
4
|
export interface TurnQueue<T> {
|
|
2
|
-
/** Schedule onto the session's serial chain (runs after that session's previous turn). */
|
|
3
5
|
accept(rec: T): void;
|
|
4
|
-
/**
|
|
5
|
-
* — the production runtime does NOT drain on shutdown (SIGTERM exits mid-turn by design; see the module
|
|
6
|
-
* header), so this is not a graceful-drain hook; it just lets a caller await the fire-and-forget turns. */
|
|
6
|
+
/** Observation only: production shutdown does not drain channel turns. */
|
|
7
7
|
idle(): Promise<void>;
|
|
8
8
|
}
|
|
9
9
|
export declare function createTurnQueue<T extends {
|
|
10
10
|
session: string;
|
|
11
11
|
}>(opts: {
|
|
12
|
-
/** Log prefix naming the consumer (e.g. "[telegram]") — the queue itself is channel-neutral. */
|
|
13
12
|
label: string;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
run: (rec: T) => Promise<void>;
|
|
18
|
-
/** Fired synchronously when a record is scheduled BEHIND an active turn — the caller's hook for
|
|
19
|
-
* queue feedback to the user (e.g. an "⏳ queued" notice). */
|
|
13
|
+
/** The queue diagnoses escaped failures and closes this turn's resources before its successor runs. */
|
|
14
|
+
run: (rec: T) => Effect.Effect<void, unknown, Scope.Scope>;
|
|
15
|
+
/** Synchronous queue feedback for a turn behind an active or queued predecessor. */
|
|
20
16
|
onQueuedBehind?: (rec: T) => void;
|
|
21
17
|
}): TurnQueue<T>;
|
|
@@ -1,54 +1,36 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* is the corruption floor beneath this; this adds the group-UX queue (a second summon waits its turn
|
|
5
|
-
* instead of colliding on the lease and being dropped as "busy").
|
|
6
|
-
*
|
|
7
|
-
* Channel-neutral (records are opaque beyond a `session` key), shared by the stateful chat channels
|
|
8
|
-
* (Telegram, Feishu/Lark, Slack). Durability is layered ON TOP by the caller: turn-store.ts
|
|
9
|
-
* persists an accepted turn's intent pre-ACK and replays a crash-surviving one on the next start (L1,
|
|
10
|
-
* process-crash recovery, at-least-once). Exactly-once / deterministic step-replay (L2) is the K-axis
|
|
11
|
-
* backend — an external queue with distributed locking (SPEC §11) — not this in-memory queue.
|
|
12
|
-
*/
|
|
1
|
+
/** Per-session FIFO execution. */
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Fiber from "effect/Fiber";
|
|
13
4
|
import { log } from "../../log.js";
|
|
14
5
|
import { beginWork } from "../busy.js";
|
|
6
|
+
import { portError } from "../../effect-port.js";
|
|
15
7
|
export function createTurnQueue(opts) {
|
|
16
8
|
const { label, run, onQueuedBehind } = opts;
|
|
17
|
-
// Per-session serial chains: a second turn for the same session waits its turn (FIFO) instead of
|
|
18
|
-
// colliding on the engine lease and being dropped as "busy". Different sessions run concurrently.
|
|
19
9
|
const chains = new Map();
|
|
20
10
|
return {
|
|
21
11
|
accept(rec) {
|
|
22
12
|
if (chains.has(rec.session))
|
|
23
13
|
onQueuedBehind?.(rec);
|
|
24
|
-
|
|
25
|
-
//
|
|
26
|
-
// /ping) read it. Counted here, not in run(), so a QUEUED turn (accepted, waiting on the chain)
|
|
27
|
-
// already reads as busy.
|
|
14
|
+
const prev = chains.get(rec.session);
|
|
15
|
+
// Admission counts immediately, including while waiting behind another turn.
|
|
28
16
|
const workDone = beginWork();
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const next = prev.then(task, task); // run after this session's previous turn, in arrival order
|
|
39
|
-
chains.set(rec.session, next);
|
|
40
|
-
void next.finally(() => {
|
|
17
|
+
const fiber = Effect.runFork(Effect.scoped(Effect.gen(function* () {
|
|
18
|
+
// Publish the tail before execution can finish or submit another turn.
|
|
19
|
+
yield* Effect.yieldNow;
|
|
20
|
+
if (prev)
|
|
21
|
+
yield* Fiber.await(prev);
|
|
22
|
+
yield* run(rec);
|
|
23
|
+
})).pipe(Effect.catchCause((cause) => Effect.sync(() => log.error(`${label} turn runner rejected (session=${rec.session}; own your error surface in run()): ${String(portError(cause))}`))), Effect.ensuring(Effect.sync(() => {
|
|
41
24
|
workDone();
|
|
42
|
-
if (chains.get(rec.session) ===
|
|
43
|
-
chains.delete(rec.session);
|
|
44
|
-
});
|
|
25
|
+
if (chains.get(rec.session) === fiber)
|
|
26
|
+
chains.delete(rec.session);
|
|
27
|
+
}))));
|
|
28
|
+
chains.set(rec.session, fiber);
|
|
45
29
|
},
|
|
46
|
-
|
|
47
|
-
//
|
|
48
|
-
// deletes its own map entry on settle, so an emptied map means every turn ran to completion. The
|
|
49
|
-
// tasks catch their own rejections, but allSettled keeps idle() itself total regardless.
|
|
30
|
+
idle: () => Effect.runPromise(Effect.gen(function* () {
|
|
31
|
+
// A finishing turn may enqueue more work.
|
|
50
32
|
while (chains.size > 0)
|
|
51
|
-
|
|
52
|
-
},
|
|
33
|
+
yield* Fiber.awaitAll([...chains.values()]);
|
|
34
|
+
})),
|
|
53
35
|
};
|
|
54
36
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
/** The durable-turn LIFECYCLE the stateful chat channels share, over the kit's parts. */
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import type * as Scope from "effect/Scope";
|
|
4
|
+
import { type PortFailure } from "../../effect-port.ts";
|
|
1
5
|
import type { ContextBuffer } from "./context-buffer.ts";
|
|
2
6
|
import { type TurnRecordBase, type TurnStore } from "./turn-store.ts";
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
7
|
+
/**
|
|
8
|
+
* A pending turn is the persisted intent minus its attempt count, plus live-only fields the channel adds (a notice's
|
|
9
|
+
* message id).
|
|
10
|
+
*/
|
|
5
11
|
export type PendingBase<S extends TurnRecordBase> = Omit<S, "attempts">;
|
|
6
12
|
export interface TurnRunnerOptions<R extends PendingBase<S>, S extends TurnRecordBase, E> {
|
|
7
13
|
label: string;
|
|
@@ -19,40 +25,48 @@ export interface TurnRunnerOptions<R extends PendingBase<S>, S extends TurnRecor
|
|
|
19
25
|
bufferKey(rec: R): string;
|
|
20
26
|
/** The place, for the lifecycle log line (`chat=… thread=…`). */
|
|
21
27
|
where(rec: R): string;
|
|
22
|
-
/** Queue feedback when a turn is scheduled BEHIND an active one.
|
|
23
|
-
* dequeue (so the turn reliably takes the notice over instead of racing it) and, optionally, how
|
|
24
|
-
* to cancel a notice that has not fired yet. `done` may reject — posting the notice is a platform
|
|
25
|
-
* call — and the runner logs that and runs the turn anyway. */
|
|
28
|
+
/** Queue feedback when a turn is scheduled BEHIND an active one. */
|
|
26
29
|
onQueuedBehind?(rec: R): {
|
|
27
30
|
done: Promise<void>;
|
|
28
31
|
cancel?: () => void;
|
|
29
32
|
};
|
|
30
|
-
/** Runs before the attempt is counted.
|
|
31
|
-
* (Slack: its transport is known to be down, so an Agent turn now would have nowhere to answer). */
|
|
33
|
+
/** Runs before the attempt is counted. */
|
|
32
34
|
beforeRun?(rec: R): Promise<boolean>;
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
+
/**
|
|
36
|
+
* The attempt could not be recorded (disk failure): a restart replays the turn, so say so on any notice it holds
|
|
37
|
+
* rather than leaving it pinned at "Queued".
|
|
38
|
+
*/
|
|
35
39
|
onDeferred(rec: R): void;
|
|
36
40
|
/** The turn started the ceiling's worth of times without finishing: tell the asker. */
|
|
37
41
|
notifyDropped(rec: R): void;
|
|
38
|
-
/** Run the turn. `onCompleted` is the durable-commit point (the turn's `completed` event): it drops
|
|
39
|
-
* the intent and commits the folded discussion, in that order. A throw is logged as the turn's
|
|
40
|
-
* failure; the intent is dropped either way. */
|
|
41
42
|
execute(rec: R, discussion: {
|
|
42
43
|
text: string;
|
|
43
44
|
consumed: E[];
|
|
44
|
-
},
|
|
45
|
+
},
|
|
46
|
+
/** Called with the finished reply just before it is delivered — the point the answer becomes recoverable. */
|
|
47
|
+
onAnswered: (answer: string) => void): Effect.Effect<void, PortFailure, Scope.Scope>;
|
|
48
|
+
/**
|
|
49
|
+
* Deliver an answer a previous run generated but never confirmed delivering: no model, no tools, and no live
|
|
50
|
+
* preview — the one the answer was written for died with its process.
|
|
51
|
+
*
|
|
52
|
+
* A notice THIS process put up is a different thing and must still be settled: a record recovered behind another
|
|
53
|
+
* turn in the same session goes through `onQueuedBehind` like any other, so it can hold a fresh "queued" message
|
|
54
|
+
* or status that only this call can take over or clear.
|
|
55
|
+
*/
|
|
56
|
+
deliverAnswer(rec: R, answer: string): Effect.Effect<void, PortFailure, Scope.Scope>;
|
|
45
57
|
}
|
|
46
58
|
export interface TurnRunner<R, S> {
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
59
|
+
/**
|
|
60
|
+
* Accept a turn: persist its intent (pre-ACK — a failed write throws so the platform redelivers), record the
|
|
61
|
+
* delivery id, enqueue.
|
|
62
|
+
*/
|
|
49
63
|
submit(rec: R, persist: boolean): void;
|
|
50
|
-
/** Re-enqueue the turns a prior crash left mid-flight; returns them so a channel can continue its
|
|
51
|
-
* arrival counter. */
|
|
64
|
+
/** Re-enqueue the turns a prior crash left mid-flight; returns them so a channel can continue its arrival counter. */
|
|
52
65
|
recover(): S[];
|
|
53
66
|
/** Resolve once no turn is in flight — the test/observability seam. */
|
|
54
67
|
idle(): Promise<void>;
|
|
55
68
|
}
|
|
69
|
+
export declare function runQueuedTurn<R extends PendingBase<S>, S extends TurnRecordBase, E>(options: TurnRunnerOptions<R, S, E>, rec: R): Effect.Effect<void, PortFailure>;
|
|
56
70
|
export declare function createTurnRunner<R extends PendingBase<S> & {
|
|
57
71
|
id: string;
|
|
58
72
|
session: string;
|
|
@@ -1,66 +1,111 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* the poison ceiling, fold the buffered discussion) → execute → end (log, drop the intent). Telegram,
|
|
5
|
-
* Slack and Feishu each wrote this out; the copies had already drifted in small ways that were not
|
|
6
|
-
* decisions (which one deletes its notice on defer, which one logs the duration on failure).
|
|
7
|
-
*
|
|
8
|
-
* What stays with the platform is everything that names a platform object: how a queue notice is
|
|
9
|
-
* mounted and taken over, what the prompt looks like, how attachments resolve, what a dropped turn
|
|
10
|
-
* says and where. Those arrive as hooks; the ORDER they run in is this module's.
|
|
11
|
-
*/
|
|
1
|
+
/** The durable-turn LIFECYCLE the stateful chat channels share, over the kit's parts. */
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Fiber from "effect/Fiber";
|
|
12
4
|
import { log } from "../../log.js";
|
|
5
|
+
import { portError, portJoin } from "../../effect-port.js";
|
|
13
6
|
import { createTurnQueue } from "./turn-queue.js";
|
|
14
7
|
import { commitAnsweredTurn } from "./turn-store.js";
|
|
8
|
+
export function runQueuedTurn(options, rec) {
|
|
9
|
+
return Effect.gen(function* () {
|
|
10
|
+
const { label, store, buffer, beforeRun } = options;
|
|
11
|
+
if (beforeRun && !(yield* portJoin(() => beforeRun(rec))))
|
|
12
|
+
return;
|
|
13
|
+
const decision = store.startAttempt(rec.id);
|
|
14
|
+
if (decision === "exceeded") {
|
|
15
|
+
const stranded = rec.answer;
|
|
16
|
+
if (stranded === undefined) {
|
|
17
|
+
options.notifyDropped(rec);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// The ceiling is about EXECUTION — a turn that may be crashing the process must stop being run. Delivering a
|
|
21
|
+
// recorded answer costs one message and no model call, so the record the store just dropped gets one last
|
|
22
|
+
// send instead of "please ask again" over an answer that exists.
|
|
23
|
+
return yield* Effect.scoped(options.deliverAnswer(rec, stranded)).pipe(Effect.matchEffect({
|
|
24
|
+
// The store has already said, at error level, that this turn will not be run again. Silence after that
|
|
25
|
+
// reads as "nothing happened"; this line is what distinguishes it from a delivered answer.
|
|
26
|
+
onSuccess: () => Effect.sync(() => log.info(`${label} turn ${rec.id} hit the execution ceiling, but the answer it had already recorded was ` +
|
|
27
|
+
`delivered (session=${rec.session})`)),
|
|
28
|
+
onFailure: (error) => Effect.sync(() => {
|
|
29
|
+
log.error(`${label} turn ${rec.id} hit the execution ceiling and its recorded answer could not be delivered ` +
|
|
30
|
+
`either (session=${rec.session}) — the answer is gone: ${String(error.cause)}`);
|
|
31
|
+
// The record is off disk, so nothing will retry this. The asker is owed an ending — without one, a
|
|
32
|
+
// queue notice this turn put up stays in the chat forever reading "Queued".
|
|
33
|
+
options.notifyDropped(rec);
|
|
34
|
+
}),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
if (decision === "defer") {
|
|
38
|
+
options.onDeferred(rec);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const startedAt = Date.now();
|
|
42
|
+
// A recovered answer skips the buffer entirely: its discussion was committed when the answer was recorded, and
|
|
43
|
+
// peeking again would consume entries this turn never folded in.
|
|
44
|
+
const recovered = rec.answer;
|
|
45
|
+
let answered = recovered !== undefined;
|
|
46
|
+
// One stable prefix, so every `turn done:`/`turn failed:` has a `turn start:` to grep back to; what kind of work
|
|
47
|
+
// it is rides along as a field.
|
|
48
|
+
log.info(`${label} turn start: turn=${rec.id} session=${rec.session} ${options.where(rec)}` +
|
|
49
|
+
`${recovered === undefined ? "" : " mode=re-delivery (answer recovered from a prior run)"}`);
|
|
50
|
+
const work = recovered !== undefined
|
|
51
|
+
? Effect.suspend(() => options.deliverAnswer(rec, recovered))
|
|
52
|
+
: Effect.suspend(() => {
|
|
53
|
+
const bufferKey = options.bufferKey(rec);
|
|
54
|
+
const discussion = buffer.peek(bufferKey);
|
|
55
|
+
return options.execute(rec, discussion, (answer) => {
|
|
56
|
+
// Only a recorded answer is recoverable — an untracked run has no record to keep it in.
|
|
57
|
+
answered = commitAnsweredTurn(store, buffer, {
|
|
58
|
+
id: rec.id,
|
|
59
|
+
bufferKey,
|
|
60
|
+
consumed: discussion.consumed,
|
|
61
|
+
answer,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
const delivered = yield* Effect.scoped(work).pipe(Effect.matchEffect({
|
|
66
|
+
onSuccess: () => Effect.sync(() => {
|
|
67
|
+
log.info(`${label} turn done: turn=${rec.id} session=${rec.session} (${Date.now() - startedAt}ms)`);
|
|
68
|
+
return true;
|
|
69
|
+
}),
|
|
70
|
+
onFailure: (error) => Effect.sync(() => {
|
|
71
|
+
log.error(`${label} turn failed: turn=${rec.id} session=${rec.session} (${Date.now() - startedAt}ms): ${String(error.cause)}`);
|
|
72
|
+
return false;
|
|
73
|
+
}),
|
|
74
|
+
}));
|
|
75
|
+
// An answer that exists but did not reach the chat is the one failure worth another start: re-delivering it costs
|
|
76
|
+
// one message and no model call, and `startAttempt` bounds how often that is tried. Everything else — delivered,
|
|
77
|
+
// or failed before there was an answer — is done here. Interruption never reaches this removal.
|
|
78
|
+
if (delivered || !answered) {
|
|
79
|
+
store.remove(rec.id);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
log.warn(`${label} turn ${rec.id} produced an answer that was not delivered (session=${rec.session}) — ` +
|
|
83
|
+
`keeping it to re-deliver on the next start`);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
15
86
|
export function createTurnRunner(options) {
|
|
16
|
-
const { label, store,
|
|
87
|
+
const { label, store, seen, onQueuedBehind } = options;
|
|
17
88
|
const notices = new Map();
|
|
18
89
|
const queue = createTurnQueue({
|
|
19
90
|
label,
|
|
20
|
-
onQueuedBehind: onQueuedBehind &&
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
91
|
+
onQueuedBehind: onQueuedBehind &&
|
|
92
|
+
((rec) => {
|
|
93
|
+
const notice = onQueuedBehind(rec);
|
|
94
|
+
// Observe rejection at acceptance, even if dequeue is minutes away.
|
|
95
|
+
const done = Effect.runFork(portJoin(() => notice.done).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn(`${label} queue notice failed: turn=${rec.id} session=${rec.session}: ${String(portError(cause))}`)))));
|
|
96
|
+
notices.set(rec.id, { done, cancel: notice.cancel });
|
|
97
|
+
}),
|
|
98
|
+
run: (rec) => Effect.gen(function* () {
|
|
25
99
|
const notice = notices.get(rec.id);
|
|
26
|
-
notice?.cancel?.();
|
|
27
|
-
// The notice is the PLATFORM's feedback, not the turn: a failed post must not throw out of the
|
|
28
|
-
// run, which would leak the persisted intent (removed only below) into a replay that burns an
|
|
29
|
-
// attempt against the ceiling. Logged, then the turn proceeds without its handle.
|
|
30
|
-
await notice?.done.catch((error) => log.warn(`${label} queue notice failed: turn=${rec.id} session=${rec.session}: ${String(error)}`));
|
|
31
100
|
notices.delete(rec.id);
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return;
|
|
101
|
+
if (notice) {
|
|
102
|
+
// A broken cancel hook must still join any post already in flight.
|
|
103
|
+
yield* Effect.addFinalizer(() => Fiber.await(notice.done));
|
|
104
|
+
notice.cancel?.();
|
|
105
|
+
yield* Fiber.await(notice.done);
|
|
38
106
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
const startedAt = Date.now();
|
|
44
|
-
log.info(`${label} turn start: turn=${rec.id} session=${rec.session} ${options.where(rec)}`);
|
|
45
|
-
// Snapshot the discussion at dequeue; commit only this snapshot on `completed`, so a message
|
|
46
|
-
// arriving while the turn runs stays buffered for the next answered turn.
|
|
47
|
-
const bufferKey = options.bufferKey(rec);
|
|
48
|
-
const discussion = buffer.peek(bufferKey);
|
|
49
|
-
try {
|
|
50
|
-
await options.execute(rec, discussion, () => commitAnsweredTurn(store, buffer, { id: rec.id, bufferKey, consumed: discussion.consumed }));
|
|
51
|
-
log.info(`${label} turn done: turn=${rec.id} session=${rec.session} (${Date.now() - startedAt}ms)`);
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
log.error(`${label} turn failed: turn=${rec.id} session=${rec.session} (${Date.now() - startedAt}ms): ${String(error)}`);
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
// Fallback removal for the caught-error paths (a `failed` event or a transport throw): those
|
|
58
|
-
// never reach the completed hook. Idempotent. Only an INTERRUPTED run (this finally never
|
|
59
|
-
// runs — a crash or SIGTERM deploy) leaves the record for replay; a transport throw is
|
|
60
|
-
// dropped, not retried (safe retry needs an L2 delivery key).
|
|
61
|
-
store.remove(rec.id);
|
|
62
|
-
}
|
|
63
|
-
},
|
|
107
|
+
yield* runQueuedTurn(options, rec);
|
|
108
|
+
}),
|
|
64
109
|
});
|
|
65
110
|
const submit = (rec, persist) => {
|
|
66
111
|
if (persist) {
|
|
@@ -1,58 +1,58 @@
|
|
|
1
1
|
import type { ContextBuffer } from "./context-buffer.ts";
|
|
2
|
-
/** What every persisted turn record carries regardless of channel
|
|
3
|
-
* chain it runs on, and how many times it has STARTED executing without finishing (0 until its first
|
|
4
|
-
* run; bumped at each `startAttempt`). The channel's record type extends this with everything its
|
|
5
|
-
* runner needs to re-execute the turn — a new execution field added there propagates into the
|
|
6
|
-
* persisted record by construction. */
|
|
2
|
+
/** What every persisted turn record carries regardless of channel. */
|
|
7
3
|
export interface TurnRecordBase {
|
|
8
4
|
id: string;
|
|
9
5
|
session: string;
|
|
10
6
|
attempts: number;
|
|
7
|
+
/**
|
|
8
|
+
* The completed reply, recorded BEFORE it is delivered. Its presence is the difference between the two things a
|
|
9
|
+
* pending record can mean: absent = the turn still has to run, present = the model already ran and only delivery
|
|
10
|
+
* is owed. A restart therefore re-delivers an answer instead of paying for it again.
|
|
11
|
+
*/
|
|
12
|
+
answer?: string;
|
|
11
13
|
}
|
|
12
14
|
export interface TurnStore<T extends TurnRecordBase> {
|
|
13
|
-
/** Persist an accepted turn before the ACK.
|
|
15
|
+
/** Persist an accepted turn before the ACK. */
|
|
14
16
|
add(rec: T): void;
|
|
15
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Record the completed reply so a restart can deliver it without running the turn again. `false` = there was no
|
|
19
|
+
* record to write it to (an untracked run), so this answer has no recovery copy.
|
|
20
|
+
*/
|
|
21
|
+
answered(id: string, answer: string): boolean;
|
|
16
22
|
remove(id: string): void;
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Every persisted turn a crash left behind, in ARRIVAL order (the channel's `order`), to re-enqueue on the next
|
|
25
|
+
* start.
|
|
26
|
+
*/
|
|
21
27
|
recover(): T[];
|
|
22
|
-
/** Called when a turn is about to RUN (dequeued).
|
|
23
|
-
* - "run": bumped its persisted execution count; go ahead.
|
|
24
|
-
* - "exceeded": over {@link MAX_TURN_ATTEMPTS} starts without finishing (killed mid-run every time,
|
|
25
|
-
* whatever the cause); the record is dropped and the runner notifies the asker.
|
|
26
|
-
* - "defer": the bump could not be persisted — skip this cycle (fail closed: an unpersisted count
|
|
27
|
-
* would let a poison turn re-run forever); the record stays on disk and replays on the next start
|
|
28
|
-
* (a restart is required — disk recovery alone does not re-run it). The runner does NOT notify.
|
|
29
|
-
* An id with no record returns "run" (untracked): a completed turn's `remove` cleared it, so the
|
|
30
|
-
* redelivery-double-run tail (see the header's pre-ACK window) lands here. */
|
|
28
|
+
/** Called when a turn is about to RUN (dequeued). */
|
|
31
29
|
startAttempt(id: string): "run" | "exceeded" | "defer";
|
|
32
30
|
}
|
|
33
31
|
export interface TurnStoreOptions<T extends TurnRecordBase> {
|
|
34
32
|
/** Log prefix naming the consumer (e.g. "[telegram]") — the store itself is channel-neutral. */
|
|
35
33
|
label: string;
|
|
36
|
-
/**
|
|
37
|
-
*
|
|
34
|
+
/**
|
|
35
|
+
* Shape validation at the IO boundary: valid JSON of the WRONG SHAPE must degrade like a corrupt file (warn +
|
|
36
|
+
* empty), not flow in as trusted data.
|
|
37
|
+
*/
|
|
38
38
|
isRecord: (t: unknown) => t is T;
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
39
|
+
/**
|
|
40
|
+
* Arrival order for {@link TurnStore.recover} — the channel knows what its ids/fields encode (telegram: numeric
|
|
41
|
+
* update_id; lark: an explicit per-record seq).
|
|
42
|
+
*/
|
|
41
43
|
order: (a: T, b: T) => number;
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
46
|
+
* An ANSWERED turn: record the reply for delivery, then commit the discussion it folded in. The intent is NOT
|
|
47
|
+
* dropped here — generating an answer is not the same event as that answer reaching the chat, and the runner drops
|
|
48
|
+
* the record only once delivery has settled.
|
|
45
49
|
*
|
|
46
|
-
*
|
|
47
|
-
* site. A crash between the two writes may re-fold already-answered context into the next summon —
|
|
48
|
-
* additive and harmless. The reverse order leaves intent on disk with its context already consumed,
|
|
49
|
-
* so the replay runs the same turn with its context stripped.
|
|
50
|
-
*
|
|
51
|
-
* Called from the turn's `completed` event, when the fold provably lives in the durable session.
|
|
50
|
+
* Returns whether the answer became recoverable, so a caller does not claim a recovery that has nothing on disk.
|
|
52
51
|
*/
|
|
53
52
|
export declare function commitAnsweredTurn<T extends TurnRecordBase, E>(store: TurnStore<T>, buffer: ContextBuffer<E>, turn: {
|
|
54
53
|
id: string;
|
|
55
54
|
bufferKey: string;
|
|
56
55
|
consumed: E[];
|
|
57
|
-
|
|
56
|
+
answer: string;
|
|
57
|
+
}): boolean;
|
|
58
58
|
export declare function createTurnStore<T extends TurnRecordBase>(path: string, opts: TurnStoreOptions<T>): TurnStore<T>;
|