@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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import type * as Scope from "effect/Scope";
|
|
3
|
+
import * as Stream from "effect/Stream";
|
|
4
|
+
import type { AgentEvent } from "../../agent.ts";
|
|
5
|
+
import type { ChannelFailure } from "./preview-kit.ts";
|
|
6
|
+
import { PortFailure } from "../../effect-port.ts";
|
|
7
|
+
/** Snapshot/edit renderers share terminal ownership; platform-specific settle policies stay injected. */
|
|
8
|
+
export declare function renderReply(events: Stream.Stream<AgentEvent, PortFailure>, opts: {
|
|
9
|
+
label: string;
|
|
10
|
+
onEvent: (event: AgentEvent) => void;
|
|
11
|
+
finish: Effect.Effect<void>;
|
|
12
|
+
answer: () => string;
|
|
13
|
+
settle: (text: string) => Effect.Effect<void, PortFailure>;
|
|
14
|
+
formatError: (failure: ChannelFailure) => string | undefined;
|
|
15
|
+
/** The finished reply, handed over before it is delivered — where a channel records it for recovery. */
|
|
16
|
+
onAnswered?: (answer: string) => void;
|
|
17
|
+
}): Effect.Effect<void, PortFailure, Scope.Scope>;
|
|
18
|
+
export interface PreviewPump {
|
|
19
|
+
touch(): void;
|
|
20
|
+
/** Cancel pacing, join an issued write, and prevent any subsequent frame. */
|
|
21
|
+
finish: Effect.Effect<void>;
|
|
22
|
+
}
|
|
23
|
+
export declare function previewPump(opts: {
|
|
24
|
+
flush: () => Promise<void>;
|
|
25
|
+
throttleMs: number;
|
|
26
|
+
/** Slack's mutation slot precedes a write; stopping the pump can discard this pending frame. */
|
|
27
|
+
beforeFlush?: Effect.Effect<void>;
|
|
28
|
+
onError: (error: unknown) => void;
|
|
29
|
+
}): Effect.Effect<PreviewPump, never, Scope.Scope>;
|
|
30
|
+
export interface SerialWriter {
|
|
31
|
+
enqueue(work: () => Promise<void>): void;
|
|
32
|
+
/** End admission and join every accepted operation, including its caller-owned failure policy. */
|
|
33
|
+
finish: Effect.Effect<void, PortFailure>;
|
|
34
|
+
}
|
|
35
|
+
export declare function serialWriter(): Effect.Effect<SerialWriter, never, Scope.Scope>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/** Scoped outbound writers. Frames coalesce; native append/status operations retain every queued write. */
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
5
|
+
import * as Fiber from "effect/Fiber";
|
|
6
|
+
import * as Queue from "effect/Queue";
|
|
7
|
+
import * as Stream from "effect/Stream";
|
|
8
|
+
import { log } from "../../log.js";
|
|
9
|
+
import { PortFailure, portJoin } from "../../effect-port.js";
|
|
10
|
+
/** Snapshot/edit renderers share terminal ownership; platform-specific settle policies stay injected. */
|
|
11
|
+
export function renderReply(events, opts) {
|
|
12
|
+
return Effect.gen(function* () {
|
|
13
|
+
let finalized = false;
|
|
14
|
+
const notify = (failure, phase) => Effect.try({ try: () => opts.formatError(failure) ?? "", catch: (cause) => new PortFailure(cause) }).pipe(Effect.flatMap(opts.settle), Effect.catchTag("PortFailure", (error) => Effect.sync(() => log.error(`${opts.label} failed to deliver the ${phase} notice: ${String(error.cause)}`))));
|
|
15
|
+
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
16
|
+
yield* opts.finish;
|
|
17
|
+
if (!finalized)
|
|
18
|
+
yield* notify({ details: "the turn ended without completing", retryable: false }, "abnormal-turn");
|
|
19
|
+
}));
|
|
20
|
+
yield* Stream.runForEachWhile(events, (event) => {
|
|
21
|
+
if (event.type !== "completed" && event.type !== "failed") {
|
|
22
|
+
return Effect.try({
|
|
23
|
+
try: () => {
|
|
24
|
+
opts.onEvent(event);
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
catch: (cause) => new PortFailure(cause),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return Effect.gen(function* () {
|
|
31
|
+
yield* opts.finish;
|
|
32
|
+
finalized = true;
|
|
33
|
+
if (event.type === "completed") {
|
|
34
|
+
const answer = opts.answer();
|
|
35
|
+
yield* Effect.try({
|
|
36
|
+
try: () => opts.onAnswered?.(answer),
|
|
37
|
+
catch: (cause) => new PortFailure(cause),
|
|
38
|
+
});
|
|
39
|
+
yield* opts.settle(answer);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
yield* notify({
|
|
43
|
+
details: event.details,
|
|
44
|
+
retryable: event.retryable,
|
|
45
|
+
...(event.code !== undefined ? { code: event.code } : {}),
|
|
46
|
+
}, "agent-failure");
|
|
47
|
+
return yield* Effect.fail(new PortFailure(new Error(`agent failed: ${event.details} (retryable=${event.retryable})`)));
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}).pipe(Effect.uninterruptible);
|
|
51
|
+
});
|
|
52
|
+
if (!finalized)
|
|
53
|
+
yield* Effect.fail(new PortFailure(new Error("stream ended without a terminal event")));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function previewPump(opts) {
|
|
57
|
+
return Effect.gen(function* () {
|
|
58
|
+
const scope = yield* Effect.scope;
|
|
59
|
+
const run = Effect.runSyncWith(yield* Effect.context());
|
|
60
|
+
let fiber;
|
|
61
|
+
let stopped = false;
|
|
62
|
+
let dirty = false;
|
|
63
|
+
let reported = false;
|
|
64
|
+
const work = Effect.gen(function* () {
|
|
65
|
+
while (dirty && !stopped) {
|
|
66
|
+
dirty = false;
|
|
67
|
+
if (opts.beforeFlush)
|
|
68
|
+
yield* opts.beforeFlush;
|
|
69
|
+
// An issued mutation may have reached the platform. Join it, including publishing its id
|
|
70
|
+
// and diagnosing its outcome, before the final writer takes over.
|
|
71
|
+
yield* portJoin(opts.flush).pipe(Effect.catchTag("PortFailure", (error) => Effect.sync(() => {
|
|
72
|
+
if (!reported) {
|
|
73
|
+
reported = true;
|
|
74
|
+
opts.onError(error.cause);
|
|
75
|
+
}
|
|
76
|
+
})), Effect.uninterruptible);
|
|
77
|
+
if (dirty && !stopped)
|
|
78
|
+
yield* Effect.sleep(opts.throttleMs);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const finish = Effect.gen(function* () {
|
|
82
|
+
stopped = true;
|
|
83
|
+
if (!fiber)
|
|
84
|
+
return;
|
|
85
|
+
yield* Fiber.interrupt(fiber);
|
|
86
|
+
const exit = yield* Fiber.await(fiber);
|
|
87
|
+
if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause))
|
|
88
|
+
yield* Effect.failCause(exit.cause);
|
|
89
|
+
});
|
|
90
|
+
yield* Effect.addFinalizer(() => finish);
|
|
91
|
+
return {
|
|
92
|
+
touch: () => {
|
|
93
|
+
if (stopped)
|
|
94
|
+
return;
|
|
95
|
+
dirty = true;
|
|
96
|
+
const exit = fiber?.pollUnsafe();
|
|
97
|
+
if (fiber && (!exit || Exit.isFailure(exit)))
|
|
98
|
+
return;
|
|
99
|
+
// The first placeholder starts before a fast source can reach its terminal event.
|
|
100
|
+
fiber = run(Effect.forkIn(work, scope, { startImmediately: true }));
|
|
101
|
+
},
|
|
102
|
+
finish,
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export function serialWriter() {
|
|
107
|
+
return Effect.gen(function* () {
|
|
108
|
+
const queue = yield* Queue.unbounded();
|
|
109
|
+
const work = Effect.gen(function* () {
|
|
110
|
+
for (;;) {
|
|
111
|
+
const next = yield* Queue.take(queue);
|
|
112
|
+
yield* portJoin(next).pipe(Effect.uninterruptible);
|
|
113
|
+
}
|
|
114
|
+
}).pipe(Effect.catchTag("Done", () => Effect.void));
|
|
115
|
+
const fiber = yield* Effect.forkScoped(work);
|
|
116
|
+
const finish = Effect.sync(() => {
|
|
117
|
+
Queue.endUnsafe(queue);
|
|
118
|
+
}).pipe(Effect.andThen(Fiber.join(fiber)));
|
|
119
|
+
// This finalizer precedes forkScoped's interruption: accepted append/status writes must drain.
|
|
120
|
+
yield* Effect.addFinalizer(() => finish.pipe(Effect.orDie));
|
|
121
|
+
return {
|
|
122
|
+
enqueue: (next) => {
|
|
123
|
+
if (!Queue.offerUnsafe(queue, next))
|
|
124
|
+
throw new Error("delivery writer is closed");
|
|
125
|
+
},
|
|
126
|
+
finish,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type * as Scope from "effect/Scope";
|
|
2
|
+
import * as Stream from "effect/Stream";
|
|
3
|
+
import { PortFailure } from "../../effect-port.ts";
|
|
4
|
+
/** Acquire lazily; interruption closes the source, while natural exhaustion needs no extra return. */
|
|
5
|
+
export declare function eventStream<A>(open: () => AsyncIterable<A>, label: string): Stream.Stream<A, PortFailure, Scope.Scope>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Own an Agent's AsyncIterable inside a typed, demand-driven channel stream. */
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Stream from "effect/Stream";
|
|
5
|
+
import { log } from "../../log.js";
|
|
6
|
+
import { PortFailure } from "../../effect-port.js";
|
|
7
|
+
/** Acquire lazily; interruption closes the source, while natural exhaustion needs no extra return. */
|
|
8
|
+
export function eventStream(open, label) {
|
|
9
|
+
return Stream.fromPull(Effect.gen(function* () {
|
|
10
|
+
let exhausted = false;
|
|
11
|
+
const iterator = yield* Effect.acquireRelease(Effect.try({ try: () => open()[Symbol.asyncIterator](), catch: (cause) => new PortFailure(cause) }), (iterator) => {
|
|
12
|
+
const close = iterator.return?.bind(iterator);
|
|
13
|
+
if (!close || exhausted)
|
|
14
|
+
return Effect.void;
|
|
15
|
+
return Effect.promise(close).pipe(Effect.onError((cause) => Effect.sync(() => log.warn(`${label} source cleanup failed: ${String(Cause.squash(cause))}`))));
|
|
16
|
+
});
|
|
17
|
+
return Effect.gen(function* () {
|
|
18
|
+
const result = yield* Effect.tryPromise({
|
|
19
|
+
try: () => iterator.next(),
|
|
20
|
+
catch: (cause) => new PortFailure(cause),
|
|
21
|
+
});
|
|
22
|
+
if (result.done) {
|
|
23
|
+
exhausted = true;
|
|
24
|
+
return yield* Cause.done();
|
|
25
|
+
}
|
|
26
|
+
return [result.value];
|
|
27
|
+
});
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
@@ -1,20 +1,32 @@
|
|
|
1
|
+
import * as Stream from "effect/Stream";
|
|
2
|
+
import { type Agent, type AgentEvent, type Prompt, type Scope } from "../../agent.ts";
|
|
3
|
+
import { type PortFailure } from "../../effect-port.ts";
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `onCompleted` durable-commit point;
|
|
7
|
-
* - the prompt-suffix wording: {@link attachedFilesManifest}, {@link backgroundImagesManifest},
|
|
8
|
-
* {@link missingAttachmentsNote}, {@link attributedFileName}.
|
|
9
|
-
*
|
|
10
|
-
* None of it asks what the agent can DO with an attachment. A channel resolves platform resources and
|
|
11
|
-
* states what it found; deciding whether to open a file is the agent's, and one assembled without a
|
|
12
|
-
* file tool answers that it cannot — visibly, at the moment it is asked.
|
|
5
|
+
* One turn, end to end: resolve the platform inputs, then stream `agent.invoke` with the shared
|
|
6
|
+
* busy-wait. All three chat channels wrote this out, and the copies had already disagreed about the
|
|
7
|
+
* one thing that is actually a per-platform judgement (whether a load failure is worth a redelivery)
|
|
8
|
+
* while agreeing on everything that is not.
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* A resolution failure becomes a `failed` EVENT. It must never throw out of the stream: the SPEC
|
|
11
|
+
* forbids it (MUST 2), and the turn's own settlement path is what tells the user — an exception here
|
|
12
|
+
* would take the reply with it.
|
|
16
13
|
*/
|
|
17
|
-
|
|
14
|
+
export declare function turnStream<R>(opts: {
|
|
15
|
+
agent: Agent;
|
|
16
|
+
label: string;
|
|
17
|
+
/** Resolve this turn's platform inputs (download attachments, walk a reply chain). */
|
|
18
|
+
resolve: () => Promise<R>;
|
|
19
|
+
/** What the resolved inputs make of the turn: where it runs, and what it asks. */
|
|
20
|
+
turn: (resolved: R) => {
|
|
21
|
+
scope: Scope;
|
|
22
|
+
prompt: Prompt;
|
|
23
|
+
};
|
|
24
|
+
/** Whether a failed resolution is worth a platform redelivery. Stated per channel because it IS a
|
|
25
|
+
* platform question — Slack reads its API error's status, and a transport that cannot tell says
|
|
26
|
+
* yes, since a retried download is cheap next to an unanswered ask. */
|
|
27
|
+
retryableLoadFailure: (cause: unknown) => boolean;
|
|
28
|
+
busyRetry?: BusyRetry;
|
|
29
|
+
}): Stream.Stream<AgentEvent, PortFailure>;
|
|
18
30
|
/** How the busy-wait paces: retry the invoke every `delayMs` while the session's lease is held by an
|
|
19
31
|
* EXTERNAL turn (a self-scheduled wake, a concurrent embedder invoke), up to `maxWaitMs` total. The
|
|
20
32
|
* channel's own turns never collide (the turn-queue serializes per session), so a busy reject here is
|
|
@@ -25,12 +37,10 @@ export interface BusyRetry {
|
|
|
25
37
|
}
|
|
26
38
|
export declare const DEFAULT_BUSY_RETRY: BusyRetry;
|
|
27
39
|
/**
|
|
28
|
-
* Stream one Agent turn with the shared busy-wait.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* the context buffer, in that order, so a crash between the two clears cannot replay a
|
|
33
|
-
* context-stripped turn.
|
|
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.
|
|
34
44
|
*
|
|
35
45
|
* BUSY-WAIT: a `failed{code: session_busy}` FIRST event means an external turn holds this session's
|
|
36
46
|
* lease and OUR turn never started — replay-safe. Retry (bounded) instead of yielding it: the user
|
|
@@ -39,14 +49,31 @@ export declare const DEFAULT_BUSY_RETRY: BusyRetry;
|
|
|
39
49
|
* busy retries — a fail-fast reject is the only shape the engine emits it in, so nothing that started
|
|
40
50
|
* is ever re-run.
|
|
41
51
|
*/
|
|
42
|
-
export declare function
|
|
43
|
-
/** The full scope, not a session string — channels that set extension fields (lineage) pass them
|
|
44
|
-
* through here; channels that don't pass `{ session }` and nothing changes. */
|
|
45
|
-
scope: Scope, prompt: Prompt, options: {
|
|
52
|
+
export declare function busyRetryStream(agent: Agent, scope: Scope, prompt: Prompt, { label, busyRetry }: {
|
|
46
53
|
label: string;
|
|
47
|
-
onCompleted?: () => void;
|
|
48
54
|
busyRetry?: BusyRetry;
|
|
49
|
-
}):
|
|
55
|
+
}): Stream.Stream<AgentEvent, PortFailure>;
|
|
56
|
+
/**
|
|
57
|
+
* Load the turn's BACKGROUND refs — what was folded in from earlier un-summoned discussion, not the
|
|
58
|
+
* ask itself. Per-ref degradation is the whole meaning of that tier: one expired file costs a warn
|
|
59
|
+
* and a place in the missing-attachments count ({@link missingAttachmentsNote}), never the answer it
|
|
60
|
+
* merely accompanies, and never its still-readable siblings. Parallel, input order kept.
|
|
61
|
+
*
|
|
62
|
+
* PRIMARY refs are the opposite policy and stay with their channel: a failure there throws, so the
|
|
63
|
+
* agent never runs on an input the user pointed at and we failed to load.
|
|
64
|
+
*
|
|
65
|
+
* Shared by all three channels; `what` is the only part that is the platform's.
|
|
66
|
+
*/
|
|
67
|
+
export declare function loadBackground<R, T>(refs: readonly R[], load: (ref: R) => Promise<T>, opts: {
|
|
68
|
+
label: string;
|
|
69
|
+
what: string;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
loaded: {
|
|
72
|
+
ref: R;
|
|
73
|
+
value: T;
|
|
74
|
+
}[];
|
|
75
|
+
lost: number;
|
|
76
|
+
}>;
|
|
50
77
|
/** What the attached-files manifest renders per file: display name, byte size, absolute local path. */
|
|
51
78
|
export interface ManifestFile {
|
|
52
79
|
name: string;
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Shared pieces of the channels' invoke-turn modules (telegram/feishu/slack `invoke-turn.ts`) — the
|
|
3
3
|
* halves that are channel-independent, so a retry-policy or prompt-wording change lands ONCE:
|
|
4
4
|
*
|
|
5
|
-
* - {@link
|
|
6
|
-
*
|
|
5
|
+
* - {@link turnStream}: resolve inputs → ask the agent, with a load failure arriving as a `failed`
|
|
6
|
+
* EVENT rather than a thrown iteration (SPEC MUST 2);
|
|
7
|
+
* - {@link busyRetryStream}: the busy-retry loop around `agent.invoke`;
|
|
7
8
|
* - the prompt-suffix wording: {@link attachedFilesManifest}, {@link backgroundImagesManifest},
|
|
8
9
|
* {@link missingAttachmentsNote}, {@link attributedFileName}.
|
|
9
10
|
*
|
|
@@ -12,10 +13,39 @@
|
|
|
12
13
|
* file tool answers that it cannot — visibly, at the moment it is asked.
|
|
13
14
|
*
|
|
14
15
|
* Attachment RESOLUTION stays per channel — the platform resource models (Bot API file_ids,
|
|
15
|
-
* message-scoped Feishu keys, Slack file objects) are real differences.
|
|
16
|
+
* message-scoped Feishu keys, Slack file objects) are real differences. The two TIERS' failure
|
|
17
|
+
* policies are not: see {@link loadBackground}.
|
|
16
18
|
*/
|
|
19
|
+
import * as Clock from "effect/Clock";
|
|
20
|
+
import * as Effect from "effect/Effect";
|
|
21
|
+
import * as Stream from "effect/Stream";
|
|
17
22
|
import { SESSION_BUSY_CODE } from "../../agent.js";
|
|
23
|
+
import { eventStream } from "./event-stream.js";
|
|
24
|
+
import { portJoin } from "../../effect-port.js";
|
|
18
25
|
import { log } from "../../log.js";
|
|
26
|
+
/**
|
|
27
|
+
* One turn, end to end: resolve the platform inputs, then stream `agent.invoke` with the shared
|
|
28
|
+
* busy-wait. All three chat channels wrote this out, and the copies had already disagreed about the
|
|
29
|
+
* one thing that is actually a per-platform judgement (whether a load failure is worth a redelivery)
|
|
30
|
+
* while agreeing on everything that is not.
|
|
31
|
+
*
|
|
32
|
+
* A resolution failure becomes a `failed` EVENT. It must never throw out of the stream: the SPEC
|
|
33
|
+
* forbids it (MUST 2), and the turn's own settlement path is what tells the user — an exception here
|
|
34
|
+
* would take the reply with it.
|
|
35
|
+
*/
|
|
36
|
+
export function turnStream(opts) {
|
|
37
|
+
return Stream.unwrap(portJoin(opts.resolve).pipe(Effect.map((resolved) => {
|
|
38
|
+
const { scope, prompt } = opts.turn(resolved);
|
|
39
|
+
return busyRetryStream(opts.agent, scope, prompt, {
|
|
40
|
+
label: opts.label,
|
|
41
|
+
...(opts.busyRetry ? { busyRetry: opts.busyRetry } : {}),
|
|
42
|
+
});
|
|
43
|
+
}), Effect.catchTag("PortFailure", ({ cause }) => Effect.succeed(Stream.succeed({
|
|
44
|
+
type: "failed",
|
|
45
|
+
details: `could not load attachment: ${String(cause)}`,
|
|
46
|
+
retryable: opts.retryableLoadFailure(cause),
|
|
47
|
+
})))));
|
|
48
|
+
}
|
|
19
49
|
// Each retry is a lease-check-level reject (tryAcquire runs before the session is bound) — waiting is nearly
|
|
20
50
|
// free, and the loop exits within one delay of the holder finishing. So the cap is sized to outlast a
|
|
21
51
|
// real tool-using wake turn (minutes), not to be short: 10 min. CEILING: a holder that runs longer than
|
|
@@ -23,12 +53,10 @@ import { log } from "../../log.js";
|
|
|
23
53
|
// turn forever.
|
|
24
54
|
export const DEFAULT_BUSY_RETRY = { delayMs: 5_000, maxWaitMs: 600_000 };
|
|
25
55
|
/**
|
|
26
|
-
* Stream one Agent turn with the shared busy-wait.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* the context buffer, in that order, so a crash between the two clears cannot replay a
|
|
31
|
-
* context-stripped turn.
|
|
56
|
+
* Stream one Agent turn with the shared busy-wait. The durable-commit point is NOT here: an answer becomes durable
|
|
57
|
+
* where its text is known and about to be written to the chat (the renderer's `onAnswered`), because "the model
|
|
58
|
+
* finished" and "the user has it" are different events. Source pulls remain demand-driven, including while final
|
|
59
|
+
* platform delivery runs.
|
|
32
60
|
*
|
|
33
61
|
* BUSY-WAIT: a `failed{code: session_busy}` FIRST event means an external turn holds this session's
|
|
34
62
|
* lease and OUR turn never started — replay-safe. Retry (bounded) instead of yielding it: the user
|
|
@@ -37,31 +65,60 @@ export const DEFAULT_BUSY_RETRY = { delayMs: 5_000, maxWaitMs: 600_000 };
|
|
|
37
65
|
* busy retries — a fail-fast reject is the only shape the engine emits it in, so nothing that started
|
|
38
66
|
* is ever re-run.
|
|
39
67
|
*/
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
export function busyRetryStream(agent, scope, prompt, { label, busyRetry = DEFAULT_BUSY_RETRY }) {
|
|
69
|
+
return Stream.unwrap(Effect.map(Clock.currentTimeMillis, (started) => {
|
|
70
|
+
const deadline = started + busyRetry.maxWaitMs;
|
|
71
|
+
const attempt = () => Stream.suspend(() => {
|
|
72
|
+
let retryBusy = false;
|
|
73
|
+
let first = true;
|
|
74
|
+
return eventStream(() => agent.invoke(scope, prompt), `${label} (session=${scope.session})`).pipe(Stream.takeWhileEffect((event) => Effect.gen(function* () {
|
|
75
|
+
if (first &&
|
|
76
|
+
event.type === "failed" &&
|
|
77
|
+
event.code === SESSION_BUSY_CODE &&
|
|
78
|
+
(yield* Clock.currentTimeMillis) + busyRetry.delayMs < deadline) {
|
|
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
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Load the turn's BACKGROUND refs — what was folded in from earlier un-summoned discussion, not the
|
|
99
|
+
* ask itself. Per-ref degradation is the whole meaning of that tier: one expired file costs a warn
|
|
100
|
+
* and a place in the missing-attachments count ({@link missingAttachmentsNote}), never the answer it
|
|
101
|
+
* merely accompanies, and never its still-readable siblings. Parallel, input order kept.
|
|
102
|
+
*
|
|
103
|
+
* PRIMARY refs are the opposite policy and stay with their channel: a failure there throws, so the
|
|
104
|
+
* agent never runs on an input the user pointed at and we failed to load.
|
|
105
|
+
*
|
|
106
|
+
* Shared by all three channels; `what` is the only part that is the platform's.
|
|
107
|
+
*/
|
|
108
|
+
export async function loadBackground(refs, load, opts) {
|
|
109
|
+
const loaded = [];
|
|
110
|
+
let lost = 0;
|
|
111
|
+
const results = await Promise.allSettled(refs.map(load));
|
|
112
|
+
for (const [index, result] of results.entries()) {
|
|
113
|
+
const ref = refs[index];
|
|
114
|
+
if (result.status === "fulfilled")
|
|
115
|
+
loaded.push({ ref, value: result.value });
|
|
116
|
+
else {
|
|
117
|
+
lost++;
|
|
118
|
+
log.warn(`${opts.label} could not load an earlier (buffered) ${opts.what}: ${String(result.reason)}`);
|
|
59
119
|
}
|
|
60
|
-
if (!retryBusy)
|
|
61
|
-
return;
|
|
62
|
-
log.info(`${label} session ${session} is busy (an external turn holds it) — retrying in ${busyRetry.delayMs}ms`);
|
|
63
|
-
await new Promise((r) => setTimeout(r, busyRetry.delayMs));
|
|
64
120
|
}
|
|
121
|
+
return { loaded, lost };
|
|
65
122
|
}
|
|
66
123
|
/**
|
|
67
124
|
* The downloaded-file manifest appended to the prompt: name, size, path. Empty input renders nothing.
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Channel-neutral live-preview pieces shared by every messaging channel's preview renderer
|
|
3
|
-
*
|
|
4
|
-
* event → view-state machine every renderer consumes), its line renderers, the terminal-failure
|
|
5
|
-
* shape a channel hands to its `onError`, the customer-facing wording for it, and the serialized
|
|
6
|
-
* single-writer pump. DELIVERY stays per-platform — message edits vs streaming cards vs chat.update,
|
|
7
|
-
* pacing constants, reveal timing, and terminal-write policies are real platform differences — but
|
|
8
|
-
* everything platform-independent lives here, so a new event type or a wording change lands in ONE
|
|
9
|
-
* place instead of one hunk per channel.
|
|
2
|
+
* Channel-neutral live-preview pieces shared by every messaging channel's preview renderer (telegram/preview.ts,
|
|
3
|
+
* feishu/preview.ts, slack/preview.ts).
|
|
10
4
|
*/
|
|
11
5
|
import type { AgentEvent, Json } from "../../agent.ts";
|
|
12
6
|
/** A terminal failure, as a channel hands it to its `onError`. */
|
|
@@ -16,13 +10,12 @@ export interface ChannelFailure {
|
|
|
16
10
|
/** The engine's failure code, when it set one. */
|
|
17
11
|
code?: string;
|
|
18
12
|
}
|
|
19
|
-
/** The customer-facing default: neutral, no leaked internals.
|
|
20
|
-
* offer a next step (Slack agent-design: never leave the user with a dead-end "something went wrong").
|
|
21
|
-
* The non-retryable branch keeps the "something went wrong" phrase deliberately — it is neutral (we only
|
|
22
|
-
* know a boolean, never the specific limitation) and shared verbatim across channels. */
|
|
13
|
+
/** The customer-facing default: neutral, no leaked internals. */
|
|
23
14
|
export declare function defaultErrorMessage(failed: ChannelFailure): string;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
15
|
+
/**
|
|
16
|
+
* Customer-facing live-preview line for an engine-internal retry backoff (the advisory `retrying` event): neutral, no
|
|
17
|
+
* leaked internals — the reason stays in operator logs.
|
|
18
|
+
*/
|
|
26
19
|
export declare const RETRY_NOTICE = "\u23F3 Temporary problem \u2014 retrying\u2026";
|
|
27
20
|
/** The placeholder shown before any reasoning/tool/text arrives. */
|
|
28
21
|
export declare const THINKING_PLACEHOLDER = "\uD83D\uDCAD Thinking\u2026";
|
|
@@ -31,12 +24,7 @@ interface ToolLine {
|
|
|
31
24
|
label: string;
|
|
32
25
|
status: "running" | "ok" | "error";
|
|
33
26
|
}
|
|
34
|
-
/**
|
|
35
|
-
* The channel-neutral view STATE of one in-flight turn. Renderers own everything after this state:
|
|
36
|
-
* when to reveal the young answer (age vs timer), how to format (HTML / card markdown / mrkdwn),
|
|
37
|
-
* and how to deliver frames. Terminal events are deliberately NOT view state — completed/failed
|
|
38
|
-
* resolve the preview into a final write, which is each platform's terminal-write policy.
|
|
39
|
-
*/
|
|
27
|
+
/** The channel-neutral view STATE of one in-flight turn. */
|
|
40
28
|
export interface TurnView {
|
|
41
29
|
thinking: string;
|
|
42
30
|
tools: ToolLine[];
|
|
@@ -49,69 +37,27 @@ export interface TurnView {
|
|
|
49
37
|
retrying: boolean;
|
|
50
38
|
}
|
|
51
39
|
export declare function createTurnView(): TurnView;
|
|
52
|
-
|
|
53
|
-
* Apply one event to the view state. Returns true when the view changed (the caller repaints).
|
|
54
|
-
* This is the ONE place the shared view rules live: tool labels are humanized with a compact arg
|
|
55
|
-
* summary, and any progress event closes an open retry notice (a stale "retrying" line must never
|
|
56
|
-
* outlive actual progress). Terminal events only close the notice — they are the caller's business.
|
|
57
|
-
*/
|
|
58
|
-
export declare function applyTurnEvent(view: TurnView, e: AgentEvent): boolean;
|
|
40
|
+
export declare function applyTurnEvent(view: TurnView, e: AgentEvent, now?: number): boolean;
|
|
59
41
|
/** The tool-activity block: one `🔧 label …/✓/✗` line per call, in call order. */
|
|
60
42
|
export declare function toolLines(view: TurnView): string;
|
|
61
|
-
/** The reasoning peek: the most recent tail of the (growing) reasoning, one line, code-point safe.
|
|
62
|
-
* Process, not the answer — renderers show it live only, never in the persisted final message. */
|
|
43
|
+
/** The reasoning peek: the most recent tail of the (growing) reasoning, one line, code-point safe. */
|
|
63
44
|
export declare function thinkingLine(view: TurnView, maxTail: number): string;
|
|
64
45
|
/**
|
|
65
|
-
* The shared answer-reveal policy: the answer stays hidden until its first delta has aged one
|
|
66
|
-
*
|
|
67
|
-
* flush would otherwise turn the very first content delta (often a lone character or unbalanced
|
|
68
|
-
* markup) into its own frame — the short-reply flicker (placeholder → "O" → "OK."). Aging is
|
|
69
|
-
* anchored at delta ARRIVAL (`answerSince`, set by the reducer) so an in-flight write can't skew the
|
|
70
|
-
* clock, and there is deliberately NO timer at the boundary: a young answer surfaces on the next
|
|
71
|
-
* content-driven pass, so a turn completing within the window delivers the final answer only.
|
|
46
|
+
* The shared answer-reveal policy: the answer stays hidden until its first delta has aged one throttle window (`ageMs`
|
|
47
|
+
* — each platform passes its own pacing constant).
|
|
72
48
|
*/
|
|
73
|
-
export declare function revealedAnswer(view: TurnView, ageMs: number): string;
|
|
49
|
+
export declare function revealedAnswer(view: TurnView, ageMs: number, now?: number): string;
|
|
74
50
|
/** Compose body parts (thinking/tools/retry/answer) into one frame: skip empties, blank-line joins. */
|
|
75
51
|
export declare function composeTurnBody(parts: readonly string[]): string;
|
|
76
52
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* flight, paced by `throttleMs`. One-in-flight is the whole point: concurrent writes can land out
|
|
80
|
-
* of order — an older frame over a newer one is the "shows 3-4 steps, blanks, re-fills" flicker —
|
|
81
|
-
* so a single writer keeps frames monotonic (and makes feishu's strictly-increasing card `sequence`
|
|
82
|
-
* correct by construction). NOT used by slack-classic: its pacing lives inside the flush (the 3s
|
|
83
|
-
* chat.update rate slot), not at the frame boundary.
|
|
84
|
-
*/
|
|
85
|
-
export interface PreviewPump {
|
|
86
|
-
/** Mark the view dirty and ensure the single writer runs (an in-flight write picks the new state
|
|
87
|
-
* up on its next loop). Synchronous — callers never await a network write. */
|
|
88
|
-
touch(): void;
|
|
89
|
-
/** Stop the pump, cut an in-flight throttle short, and await any in-flight write — so the
|
|
90
|
-
* caller's terminal write is strictly the LAST one (no stale frame landing after the answer). */
|
|
91
|
-
finish(): Promise<void>;
|
|
92
|
-
}
|
|
93
|
-
export declare function createPreviewPump(opts: {
|
|
94
|
-
/** Write the LATEST view. Best-effort — the terminal write is authoritative. */
|
|
95
|
-
flush: () => Promise<void>;
|
|
96
|
-
/** Pace + coalesce a burst into one write; finish() interrupts a throttle in progress. */
|
|
97
|
-
throttleMs: number;
|
|
98
|
-
/** Called for the FIRST failing flush only (the pump keeps running): a never-rendering preview
|
|
99
|
-
* must be diagnosable, not silent — and not a log flood. */
|
|
100
|
-
onError: (error: unknown) => void;
|
|
101
|
-
}): PreviewPump;
|
|
102
|
-
/**
|
|
103
|
-
* A compact, human-readable preview of a tool call's args so the live view reads `🔧 read AGENTS.md`
|
|
104
|
-
* rather than just `🔧 read`. Generic (a channel knows no tool schemas): show the salient value — the
|
|
105
|
-
* first primitive field, conventionally the subject (path / command / query / url) — else compact JSON.
|
|
53
|
+
* A compact, human-readable preview of a tool call's args so the live view reads `🔧 read AGENTS.md` rather than just
|
|
54
|
+
* `🔧 read`.
|
|
106
55
|
*/
|
|
107
56
|
export declare function summarizeToolArgs(args: Json): string;
|
|
108
57
|
/**
|
|
109
|
-
* A plain-language label for a tool call, following Slack's agent-design guidance to name what a tool
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* identifier string — it never invents semantics and never exposes arguments. An `mcp__server__tool`
|
|
113
|
-
* identifier becomes `server: tool`; any other identifier has its separators normalized to spaces and
|
|
114
|
-
* its first letter capitalized.
|
|
58
|
+
* A plain-language label for a tool call, following Slack's agent-design guidance to name what a tool does rather than
|
|
59
|
+
* expose a raw identifier ("Create issue", not "create_issue"; "Github: create issue", not
|
|
60
|
+
* "mcp__github__create_issue").
|
|
115
61
|
*/
|
|
116
62
|
export declare function humanizeToolName(name: string): string;
|
|
117
63
|
export {};
|