@fastagent-sh/fastagent 0.21.1 → 0.22.1
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 -40
- 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 +233 -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 +166 -229
- 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
|
@@ -59,13 +59,7 @@ function errorSignal(error) {
|
|
|
59
59
|
const causeCode = e.cause && typeof e.cause === "object" ? e.cause.code : undefined;
|
|
60
60
|
return { status, code: e.code ?? causeCode };
|
|
61
61
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Pull the structured error `code` pi records on a failed message's diagnostics. `diagnostics`
|
|
64
|
-
* accumulates across attempts (`appendAssistantMessageDiagnostic`), so the terminal cause is the LAST
|
|
65
|
-
* code-bearing entry — `findLast`, not `find`: an earlier attempt's transient 503 must not classify a
|
|
66
|
-
* terminal 400/auth failure as retryable. (Reverse scan rather than `findLast` — the tsconfig lib is
|
|
67
|
-
* ES2022.)
|
|
68
|
-
*/
|
|
62
|
+
/** Pull the structured error `code` pi records on a failed message's diagnostics. */
|
|
69
63
|
function messageSignal(message) {
|
|
70
64
|
const diagnostics = message.diagnostics ?? [];
|
|
71
65
|
for (let i = diagnostics.length - 1; i >= 0; i--) {
|
|
@@ -76,14 +70,14 @@ function messageSignal(message) {
|
|
|
76
70
|
return {};
|
|
77
71
|
}
|
|
78
72
|
/**
|
|
79
|
-
* Terminal mapping, decided by the resolved message's stopReason: pi's prompt()
|
|
80
|
-
*
|
|
81
|
-
*
|
|
73
|
+
* Terminal mapping, decided by the resolved message's stopReason: pi's `prompt()` RESOLVES a message with stopReason
|
|
74
|
+
* "error"/"aborted" rather than throwing, so relying on catch alone would miss that whole failure class and violate
|
|
75
|
+
* SPEC MUST 1.
|
|
82
76
|
*/
|
|
83
77
|
export function toTerminal(message) {
|
|
84
78
|
if (message.stopReason === "aborted") {
|
|
85
|
-
// A deliberate stop (a control-plane or consumer abort), not an error — see {@link ABORTED_CODE}
|
|
86
|
-
//
|
|
79
|
+
// A deliberate stop (a control-plane or consumer abort), not an error — see {@link ABORTED_CODE} for the consumer
|
|
80
|
+
// contract (design §6).
|
|
87
81
|
const details = message.errorMessage ?? "run aborted";
|
|
88
82
|
return { type: "failed", details, retryable: false, code: ABORTED_CODE };
|
|
89
83
|
}
|
|
@@ -98,30 +92,28 @@ export function errorToTerminal(error) {
|
|
|
98
92
|
return { type: "failed", details, retryable: classifyRetryable(details, errorSignal(error)) };
|
|
99
93
|
}
|
|
100
94
|
/**
|
|
101
|
-
* Map prompt images to pi ImageContent
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
95
|
+
* Map prompt images to pi ImageContent. `prompt()` normalizes its images itself, with the bound model's resize
|
|
96
|
+
* profile (`inputLimits.images.resize`) and format conversion, so a `"prompt"` delivery hands them over as-is.
|
|
97
|
+
* `steer`/`followUp` do not, so a `"queued"` delivery resizes here with pi's default profile: the model is not known
|
|
98
|
+
* yet when a queued message is prepared. pi's resizer is lazy-imported so the no-image path never loads its module
|
|
99
|
+
* graph.
|
|
105
100
|
*/
|
|
106
|
-
export async function toPiPromptOptions(prompt) {
|
|
101
|
+
export async function toPiPromptOptions(prompt, delivery) {
|
|
107
102
|
if (!prompt.images || prompt.images.length === 0)
|
|
108
103
|
return undefined;
|
|
104
|
+
if (delivery === "prompt") {
|
|
105
|
+
return { images: prompt.images.map((img) => ({ type: "image", data: img.data, mimeType: img.mimeType })) };
|
|
106
|
+
}
|
|
109
107
|
const { resizeImage } = await import("@earendil-works/pi-coding-agent");
|
|
110
108
|
const images = await Promise.all(prompt.images.map(async (img) => {
|
|
111
|
-
const resized = await resizeImage(Buffer.from(img.data, "base64"), img.mimeType
|
|
112
|
-
maxWidth: 1568,
|
|
113
|
-
maxHeight: 1568,
|
|
114
|
-
maxBytes: 5 * 1024 * 1024,
|
|
115
|
-
}).catch(() => null);
|
|
109
|
+
const resized = await resizeImage(Buffer.from(img.data, "base64"), img.mimeType).catch(() => null);
|
|
116
110
|
return resized
|
|
117
111
|
? { type: "image", data: resized.data, mimeType: resized.mimeType }
|
|
118
112
|
: { type: "image", data: img.data, mimeType: img.mimeType };
|
|
119
113
|
}));
|
|
120
114
|
return { images };
|
|
121
115
|
}
|
|
122
|
-
/** The SPEC projection of the rich stream.
|
|
123
|
-
* message boundaries, run boundaries) project to null — the invoke terminal is produced from the
|
|
124
|
-
* resolved message ({@link toTerminal}), not from `run_settled`. */
|
|
116
|
+
/** The SPEC projection of the rich stream. */
|
|
125
117
|
export function projectAgentEvent(se) {
|
|
126
118
|
switch (se.type) {
|
|
127
119
|
case "message_delta": {
|
|
@@ -137,8 +129,7 @@ export function projectAgentEvent(se) {
|
|
|
137
129
|
return { type: "tool_ended", id: d.id, isError: d.isError, content: d.content };
|
|
138
130
|
}
|
|
139
131
|
case "retry_scheduled": {
|
|
140
|
-
// `operation` (compaction | branch_summary) stays session-plane vocabulary
|
|
141
|
-
// only needs "transient failure, retrying"; the engine detail lives in the control plane.
|
|
132
|
+
// `operation` (compaction | branch_summary) stays session-plane vocabulary.
|
|
142
133
|
const d = se.data;
|
|
143
134
|
return { type: "retrying", attempt: d.attempt, maxAttempts: d.maxAttempts, delayMs: d.delayMs, reason: d.error };
|
|
144
135
|
}
|
|
@@ -146,43 +137,3 @@ export function projectAgentEvent(se) {
|
|
|
146
137
|
return null;
|
|
147
138
|
}
|
|
148
139
|
}
|
|
149
|
-
// ── EventQueue: push→pull plumbing for a two-port engine ────────────────────
|
|
150
|
-
//
|
|
151
|
-
// Single-consumer async queue; single-threaded JS means no await interleaves between push and
|
|
152
|
-
// drain, so no locking. Engines that are natively async-iterable would not need it.
|
|
153
|
-
export class EventQueue {
|
|
154
|
-
buffer = [];
|
|
155
|
-
wake;
|
|
156
|
-
push(item) {
|
|
157
|
-
this.buffer.push(item);
|
|
158
|
-
const wake = this.wake;
|
|
159
|
-
this.wake = undefined;
|
|
160
|
-
wake?.();
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Yield pushed events in order until `done` settles AND the buffer is drained. The terminal is
|
|
164
|
-
* produced separately (toTerminal); rejections of `done` are swallowed here (the caller awaits
|
|
165
|
-
* `run` itself) to avoid unhandled rejections.
|
|
166
|
-
*/
|
|
167
|
-
async *drainUntil(done) {
|
|
168
|
-
let settled = false;
|
|
169
|
-
const onSettle = () => {
|
|
170
|
-
settled = true;
|
|
171
|
-
const wake = this.wake;
|
|
172
|
-
this.wake = undefined;
|
|
173
|
-
wake?.();
|
|
174
|
-
};
|
|
175
|
-
const finished = done.then(onSettle, onSettle);
|
|
176
|
-
while (true) {
|
|
177
|
-
while (this.buffer.length > 0) {
|
|
178
|
-
yield this.buffer.shift();
|
|
179
|
-
}
|
|
180
|
-
if (settled)
|
|
181
|
-
break;
|
|
182
|
-
await new Promise((resolve) => {
|
|
183
|
-
this.wake = resolve;
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
await finished;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { type MountedTool } from "./tool.ts";
|
|
2
2
|
/**
|
|
3
|
-
* Parse a delay to milliseconds: a number is SECONDS; a string MUST carry a unit — `"<n><s|m|h|d>"`
|
|
4
|
-
*
|
|
5
|
-
* encoding, one scale. Deliberate — an LLM freely emits a number OR a numeral-string, so letting a
|
|
6
|
-
* unitless string alias to a different unit than a number (120s vs 120min, 60x apart) is a silent
|
|
7
|
-
* footgun the guardrail (min 60s) would mask. A rejected value comes back to the model to fix.
|
|
3
|
+
* Parse a delay to milliseconds: a number is SECONDS; a string MUST carry a unit — `"<n><s|m|h|d>"` ("30m", "2h",
|
|
4
|
+
* "1d").
|
|
8
5
|
*/
|
|
9
6
|
export declare function parseDelayMs(input: string | number): number | undefined;
|
|
10
|
-
/**
|
|
11
|
-
* Append the built-in `wake` tool to `tools` — but only when `enabled` (the serving path, where the
|
|
12
|
-
* scheduler poller honors a wake-up) and only when the workspace hasn't defined its own `wake` (that
|
|
13
|
-
* wins, like any tool collision). The single place the mount decision + collision rule run.
|
|
14
|
-
*/
|
|
15
7
|
export declare function withWakeTool(tools: MountedTool[], stateRoot: string, enabled: boolean): MountedTool[];
|
|
16
8
|
/** Build the `wake` tool bound to `stateRoot` (where wake-ups persist). */
|
|
17
9
|
export declare function makeWakeTool(stateRoot: string, now?: () => Date): MountedTool;
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The built-in `wake` tool: the agent's self-scheduling surface. Calling it records a one-shot wake-up
|
|
3
|
-
* (wakeups.ts); the scheduler fires it back into the SAME session, so the agent resumes THIS
|
|
4
|
-
* conversation after a delay ("check the deploy in 10 minutes"). The session comes from the turn
|
|
5
|
-
* context (ToolContext.sessionManager, set around the turn); the state root is closed over at build
|
|
6
|
-
* time (where it is known — the workspace opener), never read from the turn.
|
|
7
|
-
*
|
|
8
|
-
* Mounted by the opener ONLY when `config.selfSchedule` is on AND on the serving path (`dev`/`start`, where
|
|
9
|
-
* the scheduler poller honors a wake-up) — never on the one-shot `invoke`/`fire` (they exit and never poll).
|
|
10
|
-
* To opt OUT: leave `selfSchedule` off (the default), or override the built-in by defining your own
|
|
11
|
-
* `tools/wake.ts` (a same-name collision wins — see {@link withWakeTool}). There is no name-based tool
|
|
12
|
-
* exclusion config today.
|
|
13
|
-
*/
|
|
1
|
+
/** The built-in `wake` tool: the agent's self-scheduling surface. */
|
|
14
2
|
import { z } from "zod";
|
|
15
3
|
import { addWakeup, removeWakeup } from "../../schedule/wakeups.js";
|
|
16
4
|
import { defineTool } from "./tool.js";
|
|
17
5
|
/**
|
|
18
|
-
* Parse a delay to milliseconds: a number is SECONDS; a string MUST carry a unit — `"<n><s|m|h|d>"`
|
|
19
|
-
*
|
|
20
|
-
* encoding, one scale. Deliberate — an LLM freely emits a number OR a numeral-string, so letting a
|
|
21
|
-
* unitless string alias to a different unit than a number (120s vs 120min, 60x apart) is a silent
|
|
22
|
-
* footgun the guardrail (min 60s) would mask. A rejected value comes back to the model to fix.
|
|
6
|
+
* Parse a delay to milliseconds: a number is SECONDS; a string MUST carry a unit — `"<n><s|m|h|d>"` ("30m", "2h",
|
|
7
|
+
* "1d").
|
|
23
8
|
*/
|
|
24
9
|
export function parseDelayMs(input) {
|
|
25
10
|
if (typeof input === "number")
|
|
@@ -30,17 +15,10 @@ export function parseDelayMs(input) {
|
|
|
30
15
|
const mult = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2].toLowerCase()];
|
|
31
16
|
return Number(m[1]) * mult;
|
|
32
17
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Append the built-in `wake` tool to `tools` — but only when `enabled` (the serving path, where the
|
|
35
|
-
* scheduler poller honors a wake-up) and only when the workspace hasn't defined its own `wake` (that
|
|
36
|
-
* wins, like any tool collision). The single place the mount decision + collision rule run.
|
|
37
|
-
*/
|
|
38
18
|
export function withWakeTool(tools, stateRoot, enabled) {
|
|
39
19
|
if (!enabled)
|
|
40
20
|
return tools;
|
|
41
21
|
// wake/unwake are a PAIR over one store: if the workspace defines EITHER name, mount NEITHER built-in.
|
|
42
|
-
// Mixing halves would mislead — an author's wake doesn't write our wakeups store, so our unwake could
|
|
43
|
-
// never cancel what it returns ("not yours" forever); the author owns the whole concept or none of it.
|
|
44
22
|
if (tools.some((t) => t.name === "wake" || t.name === "unwake"))
|
|
45
23
|
return tools;
|
|
46
24
|
return [...tools, makeWakeTool(stateRoot), makeUnwakeTool(stateRoot)];
|
|
@@ -92,8 +70,10 @@ export function makeWakeTool(stateRoot, now = () => new Date()) {
|
|
|
92
70
|
},
|
|
93
71
|
});
|
|
94
72
|
}
|
|
95
|
-
/**
|
|
96
|
-
*
|
|
73
|
+
/**
|
|
74
|
+
* Build the `unwake` tool: cancel one of THIS conversation's pending wake-ups by id (a wake/recurring that is no
|
|
75
|
+
* longer needed).
|
|
76
|
+
*/
|
|
97
77
|
function makeUnwakeTool(stateRoot) {
|
|
98
78
|
return defineTool({
|
|
99
79
|
name: "unwake",
|
package/dist/env.d.ts
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* `process.loadEnvFile`, so we parse the file ourselves rather than depend on a Node-only entry point.
|
|
6
|
-
*
|
|
7
|
-
* Two phases keep the two rules distinct: parse into a map (later line overrides earlier = last-wins),
|
|
8
|
-
* then apply only the keys `process.env` doesn't already have (env-wins). Minimal parser — `KEY=VALUE`
|
|
9
|
-
* per line, `#` comments and blank lines skipped, surrounding matched single/double quotes stripped:
|
|
10
|
-
* enough for the flat secret files fastagent reads (tokens, keys), not a full dotenv dialect (no
|
|
11
|
-
* multiline, no `export`, no interpolation). A missing file throws ENOENT for the caller to treat as
|
|
12
|
-
* "no .env"; any other read error propagates.
|
|
2
|
+
* Write parsed values into `process.env`, matching Node's `--env-file` / `process.loadEnvFile` precedence on BOTH
|
|
3
|
+
* axes (verified against Node in test/env.test.ts): a real env var wins over the file, and within the file the last
|
|
4
|
+
* occurrence wins ({@link parseEnvContent}).
|
|
13
5
|
*/
|
|
14
|
-
export declare function
|
|
15
|
-
/** Parse .env content into key → value (the dialect above; last occurrence of a key wins).
|
|
16
|
-
* anything else reading/deciding on .env content (e.g. `add`'s secret pre-fill) must use this, never a
|
|
17
|
-
* private re-implementation: two parsers of one dialect diverge silently. */
|
|
6
|
+
export declare function applyEnvValues(values: ReadonlyMap<string, string>): void;
|
|
7
|
+
/** Parse .env content into key → value (the dialect above; last occurrence of a key wins). */
|
|
18
8
|
export declare function parseEnvContent(content: string): Map<string, string>;
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
9
|
+
/**
|
|
10
|
+
* READ a value file without entering it: the values as data, never `process.env`. This is how code reasons about an
|
|
11
|
+
* environment OTHER than its own — `deploy` resolves configuration in the environment being deployed, which this
|
|
12
|
+
* file declares, and merging it into the running process would put the two environments in one bag. A missing file
|
|
13
|
+
* is no values, which is normal.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadEnvValues(file: string): Map<string, string>;
|
|
16
|
+
/** The agent's `.env` file: `<resolved secrets dir>/.env`. */
|
|
25
17
|
export declare function dotEnvPath(agentDir: string, env?: NodeJS.ProcessEnv): string;
|
|
26
|
-
/** The committable template: `<agentDir>/.secrets/.env.example
|
|
27
|
-
* `FASTAGENT_SECRETS_DIR`: it is authored agent surface that travels with the directory (the
|
|
28
|
-
* scaffolded `.secrets/.gitignore` un-ignores exactly it), while the real values follow the override. */
|
|
18
|
+
/** The committable template: `<agentDir>/.secrets/.env.example`. */
|
|
29
19
|
export declare function envExamplePath(agentDir: string): string;
|
|
30
20
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
21
|
+
* ENTER an agent directory's runtime environment: its `.env` becomes `process.env`, and this process's outbound fetch
|
|
22
|
+
* follows whatever proxy that (or the ambient environment) declares.
|
|
23
|
+
*
|
|
24
|
+
* ONE function because the two steps are one fact in a fixed order — the proxy can be declared in the `.env`, so it
|
|
25
|
+
* has to be read first, and `installProxyFetch` reads the environment at construction. A command that did only half
|
|
26
|
+
* would send an authored tool, a channel's app-creation flow, or a skill download DIRECT on a machine that has no
|
|
27
|
+
* direct route. Whether a given request then uses the proxy is the dispatcher's decision, not the
|
|
28
|
+
* caller's — see {@link installProxyFetch} on loopback.
|
|
29
|
+
*/
|
|
30
|
+
export declare function enterAgentEnv(agentDir: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Load the agent's `.env` ({@link dotEnvPath}) into `process.env` ({@link applyEnvValues}), treating a MISSING file as
|
|
33
|
+
* normal (no .env). Commands want {@link enterAgentEnv}, which is this plus the proxy that `.env` may declare.
|
|
35
34
|
*/
|
|
36
35
|
export declare function loadDotEnv(agentDir: string): void;
|
package/dist/env.js
CHANGED
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { log } from "./log.js";
|
|
4
|
+
import { installProxyFetch } from "./proxy.js";
|
|
4
5
|
import { SECRETS_DIRNAME, resolveSecretsDir } from "./paths.js";
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `process.loadEnvFile`, so we parse the file ourselves rather than depend on a Node-only entry point.
|
|
10
|
-
*
|
|
11
|
-
* Two phases keep the two rules distinct: parse into a map (later line overrides earlier = last-wins),
|
|
12
|
-
* then apply only the keys `process.env` doesn't already have (env-wins). Minimal parser — `KEY=VALUE`
|
|
13
|
-
* per line, `#` comments and blank lines skipped, surrounding matched single/double quotes stripped:
|
|
14
|
-
* enough for the flat secret files fastagent reads (tokens, keys), not a full dotenv dialect (no
|
|
15
|
-
* multiline, no `export`, no interpolation). A missing file throws ENOENT for the caller to treat as
|
|
16
|
-
* "no .env"; any other read error propagates.
|
|
7
|
+
* Write parsed values into `process.env`, matching Node's `--env-file` / `process.loadEnvFile` precedence on BOTH
|
|
8
|
+
* axes (verified against Node in test/env.test.ts): a real env var wins over the file, and within the file the last
|
|
9
|
+
* occurrence wins ({@link parseEnvContent}).
|
|
17
10
|
*/
|
|
18
|
-
export function
|
|
19
|
-
const
|
|
20
|
-
for (const [key, value] of parsed) {
|
|
11
|
+
export function applyEnvValues(values) {
|
|
12
|
+
for (const [key, value] of values)
|
|
21
13
|
if (!(key in process.env))
|
|
22
|
-
process.env[key] = value;
|
|
23
|
-
}
|
|
14
|
+
process.env[key] = value;
|
|
24
15
|
}
|
|
25
|
-
/** Parse .env content into key → value (the dialect above; last occurrence of a key wins).
|
|
26
|
-
* anything else reading/deciding on .env content (e.g. `add`'s secret pre-fill) must use this, never a
|
|
27
|
-
* private re-implementation: two parsers of one dialect diverge silently. */
|
|
16
|
+
/** Parse .env content into key → value (the dialect above; last occurrence of a key wins). */
|
|
28
17
|
export function parseEnvContent(content) {
|
|
29
18
|
const parsed = new Map();
|
|
30
19
|
for (const raw of content.split("\n")) {
|
|
@@ -43,50 +32,56 @@ export function parseEnvContent(content) {
|
|
|
43
32
|
}
|
|
44
33
|
return parsed;
|
|
45
34
|
}
|
|
46
|
-
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
35
|
+
/**
|
|
36
|
+
* READ a value file without entering it: the values as data, never `process.env`. This is how code reasons about an
|
|
37
|
+
* environment OTHER than its own — `deploy` resolves configuration in the environment being deployed, which this
|
|
38
|
+
* file declares, and merging it into the running process would put the two environments in one bag. A missing file
|
|
39
|
+
* is no values, which is normal.
|
|
40
|
+
*/
|
|
41
|
+
export function loadEnvValues(file) {
|
|
42
|
+
try {
|
|
43
|
+
return parseEnvContent(readFileSync(file, "utf8"));
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error.code === "ENOENT")
|
|
47
|
+
return new Map();
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** The agent's `.env` file: `<resolved secrets dir>/.env`. */
|
|
52
52
|
export function dotEnvPath(agentDir, env = process.env) {
|
|
53
53
|
return join(resolveSecretsDir(agentDir, env), ".env");
|
|
54
54
|
}
|
|
55
|
-
/** The committable template: `<agentDir>/.secrets/.env.example
|
|
56
|
-
* `FASTAGENT_SECRETS_DIR`: it is authored agent surface that travels with the directory (the
|
|
57
|
-
* scaffolded `.secrets/.gitignore` un-ignores exactly it), while the real values follow the override. */
|
|
55
|
+
/** The committable template: `<agentDir>/.secrets/.env.example`. */
|
|
58
56
|
export function envExamplePath(agentDir) {
|
|
59
57
|
return join(agentDir, SECRETS_DIRNAME, ".env.example");
|
|
60
58
|
}
|
|
61
59
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
60
|
+
* ENTER an agent directory's runtime environment: its `.env` becomes `process.env`, and this process's outbound fetch
|
|
61
|
+
* follows whatever proxy that (or the ambient environment) declares.
|
|
62
|
+
*
|
|
63
|
+
* ONE function because the two steps are one fact in a fixed order — the proxy can be declared in the `.env`, so it
|
|
64
|
+
* has to be read first, and `installProxyFetch` reads the environment at construction. A command that did only half
|
|
65
|
+
* would send an authored tool, a channel's app-creation flow, or a skill download DIRECT on a machine that has no
|
|
66
|
+
* direct route. Whether a given request then uses the proxy is the dispatcher's decision, not the
|
|
67
|
+
* caller's — see {@link installProxyFetch} on loopback.
|
|
68
|
+
*/
|
|
69
|
+
export function enterAgentEnv(agentDir) {
|
|
70
|
+
loadDotEnv(agentDir);
|
|
71
|
+
installProxyFetch();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Load the agent's `.env` ({@link dotEnvPath}) into `process.env` ({@link applyEnvValues}), treating a MISSING file as
|
|
75
|
+
* normal (no .env). Commands want {@link enterAgentEnv}, which is this plus the proxy that `.env` may declare.
|
|
66
76
|
*/
|
|
67
77
|
export function loadDotEnv(agentDir) {
|
|
68
78
|
const path = dotEnvPath(agentDir);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
if (error.code !== "ENOENT")
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
// A `.env` at the agent's root is the file habit puts there, and nothing reads it. Left silent, the
|
|
77
|
-
// symptom is a setting that appears configured and is not.
|
|
78
|
-
//
|
|
79
|
-
// Scoped to keys FASTAGENT itself reads, because that is the only case we can be sure about: an agent
|
|
80
|
-
// directory can be the author's repository too (the shape `--agent-dir .` exists for), where a root
|
|
81
|
-
// `.env` is their APPLICATION's — and "move the values" would break it. Nothing here can tell those
|
|
82
|
-
// apart, so the broader guess (warn whenever the agent has no env of its own) was a warning on every
|
|
83
|
-
// boot, with destructive advice, aimed at the population that never had the problem. Channel
|
|
84
|
-
// credentials are deliberately out of scope: a channel that cannot find its token reports that itself,
|
|
85
|
-
// and it is the one that knows the name.
|
|
79
|
+
applyEnvValues(loadEnvValues(path)); // ONE definition of "a missing value file is normal" (loadEnvValues)
|
|
80
|
+
// A `.env` at the agent's root is the file habit puts there, and nothing reads it.
|
|
86
81
|
const stray = join(agentDir, ".env");
|
|
87
82
|
if (stray === path || !existsSync(stray))
|
|
88
83
|
return;
|
|
89
|
-
const misplaced = [...parseEnvContent(readFileSync(stray, "utf8")).keys()]
|
|
84
|
+
const misplaced = [...parseEnvContent(readFileSync(stray, "utf8")).keys()];
|
|
90
85
|
if (misplaced.length > 0) {
|
|
91
86
|
log.warn(`[fastagent] ${stray} is NOT read — it sets ${misplaced.join(", ")}, and this agent's env lives at ` +
|
|
92
87
|
`${path}; move those values there`);
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
// Convenience entry: the engine-neutral core plus the pi reference implementation.
|
|
2
|
-
// Contract/channel-only consumers should prefer `@fastagent-sh/fastagent/core`; pi-specific consumers
|
|
3
|
-
// may use `@fastagent-sh/fastagent/pi`. The root remains the supported all-in-one surface.
|
|
4
2
|
export * from "./core.js";
|
|
5
3
|
export * from "./node.js";
|
|
6
4
|
export * from "./session.js";
|
package/dist/lark.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** `@fastagent-sh/fastagent/lark` — the Lark-international compatibility surface over the canonical
|
|
2
|
-
* Feishu engine. Feishu tenants use `@fastagent-sh/fastagent/feishu`. */
|
|
1
|
+
/** `@fastagent-sh/fastagent/lark` — the Lark-international compatibility surface over the canonical Feishu engine. */
|
|
3
2
|
export { larkChannel, larkWebSocketChannel, defaultLarkRoute, larkEnvelope, type LarkChannelOptions, type LarkWebSocketChannelOptions, type LarkMessageEvent, type LarkMessage, type LarkRoute, type LarkFailure, } from "./channels/lark/lark.ts";
|
|
4
3
|
export { larkTransport, type FeishuTransport as LarkTransport } from "./channels/feishu/shared-api.ts";
|
package/dist/lark.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** `@fastagent-sh/fastagent/lark` — the Lark-international compatibility surface over the canonical
|
|
2
|
-
* Feishu engine. Feishu tenants use `@fastagent-sh/fastagent/feishu`. */
|
|
1
|
+
/** `@fastagent-sh/fastagent/lark` — the Lark-international compatibility surface over the canonical Feishu engine. */
|
|
3
2
|
export { larkChannel, larkWebSocketChannel, defaultLarkRoute, larkEnvelope, } from "./channels/lark/lark.js";
|
|
4
3
|
export { larkTransport } from "./channels/feishu/shared-api.js";
|
package/dist/loader.d.ts
CHANGED
|
@@ -1,48 +1,3 @@
|
|
|
1
|
-
/** One module file a directory declares. `name` is what the domain knows it by; nothing is imported
|
|
2
|
-
* to produce this. */
|
|
3
|
-
export interface InventoryEntry {
|
|
4
|
-
/** Basename without extension — the authoritative name for tools/channels/schedules. */
|
|
5
|
-
name: string;
|
|
6
|
-
/** "tools/foo.ts"-style label for errors and collisions. */
|
|
7
|
-
label: string;
|
|
8
|
-
file: string;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* WHAT A CODE-INPUT DIRECTORY DECLARES — the single answer to "which files here are modules",
|
|
12
|
-
* without importing any of them.
|
|
13
|
-
*
|
|
14
|
-
* It exists because four consumers need that answer and only one of them may import: the loader
|
|
15
|
-
* below, `fastagent info`'s channel listing, `--tunnel`'s webhook registration, and the deploy
|
|
16
|
-
* pre-flight's schedule probe. When they each read the directory themselves they disagreed — on
|
|
17
|
-
* what counts as a module file, on how to strip the extension, on which errno means "no such
|
|
18
|
-
* directory" — and a fix had to be applied in four places, which is why the fourth kept being
|
|
19
|
-
* missed. There is one reading now; what to DO with a failure stays with the caller, because that
|
|
20
|
-
* genuinely differs (the loader throws, the tunnel cannot).
|
|
21
|
-
*
|
|
22
|
-
* A missing directory is an empty inventory. Everything else throws, ENOTDIR included: `channels`
|
|
23
|
-
* and `schedules` are named directories, so the path existing as a FILE is a mistake in the agent
|
|
24
|
-
* rather than an agent without one (`service.test.ts` pins that for `schedules`). `paths.ts` folds
|
|
25
|
-
* ENOTDIR into its empty scan for the opposite reason — it asks which children HAPPEN to be agent
|
|
26
|
-
* dirs, where a file simply is not one. `not_found` is a non-Node runtime's ENOENT.
|
|
27
|
-
*
|
|
28
|
-
* SYMLINKS ARE SKIPPED, and that is a boundary rather than an oversight: `assertInsideAgentDir`
|
|
29
|
-
* guards the code-input DIRECTORY against escaping the definition, and nothing guards the entries
|
|
30
|
-
* inside it, so following a link would import from anywhere on the box past the very check meant to
|
|
31
|
-
* prevent it. Do not "fix" this by following them — report them, which this does.
|
|
32
|
-
*
|
|
33
|
-
* The skip is WARNED HERE, not handed back, because "this file is not loadable" holds for all four
|
|
34
|
-
* consumers and only one of them imports — a listing that reports the name it cannot load reads as
|
|
35
|
-
* "I never created it". What to do about a load FAILURE does differ per caller, so that travels as
|
|
36
|
-
* data. One warning per READ, so a command that both lists and loads the same directory (`deploy
|
|
37
|
-
* agentcore` does) says it twice — both readings are true, and remembering what was already said
|
|
38
|
-
* would mean a restart stops mentioning a skip that is still there.
|
|
39
|
-
*
|
|
40
|
-
* A skip is therefore a warning ONLY, unlike a {@link ModuleLoadFailure}: it is absent from `info
|
|
41
|
-
* --json` and `deploy` does not gate on it. That is the deliberate cost of one report for four
|
|
42
|
-
* consumers, three of which list names and have nowhere to put data. To give a machine consumer the
|
|
43
|
-
* skips, return them beside the entries — do not reconstruct them from stderr.
|
|
44
|
-
*/
|
|
45
|
-
export declare function moduleInventory(subDir: string): Promise<InventoryEntry[]>;
|
|
46
1
|
export interface DiscoveredModule {
|
|
47
2
|
/** Basename without extension — the authoritative name for tools/channels. */
|
|
48
3
|
name: string;
|
|
@@ -53,9 +8,7 @@ export interface DiscoveredModule {
|
|
|
53
8
|
default?: unknown;
|
|
54
9
|
};
|
|
55
10
|
}
|
|
56
|
-
/** An agent module that failed to load, surfaced as data so its caller can report the exact file.
|
|
57
|
-
* `loadModuleDir` fills it for import failures; domain loaders add validation failures. The caller owns
|
|
58
|
-
* policy: tools/schedules may skip one bad file, while serving treats a broken declared channel as fatal. */
|
|
11
|
+
/** An agent module that failed to load, surfaced as data so its caller can report the exact file. */
|
|
59
12
|
export interface ModuleLoadFailure {
|
|
60
13
|
/** "tools/foo.ts"-style label. */
|
|
61
14
|
label: string;
|
|
@@ -63,20 +16,26 @@ export interface ModuleLoadFailure {
|
|
|
63
16
|
/** The failure message (an import error carries {@link moduleLoadHint}). */
|
|
64
17
|
message: string;
|
|
65
18
|
}
|
|
66
|
-
/** A module the loader skipped, said once, the same way for tools, channels and
|
|
19
|
+
/** A module the loader skipped, said once, the same way for tools, channels and routines. */
|
|
67
20
|
export declare function reportModuleLoadFailures(failures: readonly ModuleLoadFailure[]): void;
|
|
68
21
|
/**
|
|
69
|
-
*
|
|
70
|
-
* is
|
|
71
|
-
*
|
|
72
|
-
*
|
|
22
|
+
* THE REFUSAL every path that is about to RUN the agent shares: an enabled file under `tools/`, `channels/` or
|
|
23
|
+
* `routines/` is a declaration, so one that cannot load means the agent is missing something its author said it
|
|
24
|
+
* has. Starting anyway announces a ready service over an absent capability — a schedule that never fires, a tool the
|
|
25
|
+
* model simply never gets — with nothing but one warning to find it by. Every failure is reported before this
|
|
26
|
+
* throws, so a boot fixes all of them at once rather than one per restart.
|
|
27
|
+
*
|
|
28
|
+
* Inspecting a definition (`info`, `fastagent tool`) is NOT this path: it must survive a broken file in order to
|
|
29
|
+
* report it.
|
|
73
30
|
*/
|
|
31
|
+
export declare function refuseBrokenDeclarations(failures: readonly ModuleLoadFailure[]): void;
|
|
32
|
+
/** Import every module the directory declares ({@link moduleInventory}). */
|
|
74
33
|
export declare function loadModuleDir(subDir: string): Promise<{
|
|
75
34
|
modules: DiscoveredModule[];
|
|
76
35
|
failures: ModuleLoadFailure[];
|
|
77
36
|
}>;
|
|
78
37
|
/**
|
|
79
|
-
* A hint for the two common dynamic-import failures — an uninstalled dependency or a non-ESM
|
|
80
|
-
*
|
|
38
|
+
* A hint for the two common dynamic-import failures — an uninstalled dependency or a non-ESM package — and empty
|
|
39
|
+
* otherwise, so an unrelated error is reported on its own.
|
|
81
40
|
*/
|
|
82
41
|
export declare function moduleLoadHint(error: NodeJS.ErrnoException): string;
|
package/dist/loader.js
CHANGED
|
@@ -20,41 +20,10 @@ function moduleName(fileName) {
|
|
|
20
20
|
return basename(fileName, extname(fileName));
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* WHAT A CODE-INPUT DIRECTORY DECLARES — the single answer to "which files here are modules",
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* It exists because four consumers need that answer and only one of them may import: the loader
|
|
27
|
-
* below, `fastagent info`'s channel listing, `--tunnel`'s webhook registration, and the deploy
|
|
28
|
-
* pre-flight's schedule probe. When they each read the directory themselves they disagreed — on
|
|
29
|
-
* what counts as a module file, on how to strip the extension, on which errno means "no such
|
|
30
|
-
* directory" — and a fix had to be applied in four places, which is why the fourth kept being
|
|
31
|
-
* missed. There is one reading now; what to DO with a failure stays with the caller, because that
|
|
32
|
-
* genuinely differs (the loader throws, the tunnel cannot).
|
|
33
|
-
*
|
|
34
|
-
* A missing directory is an empty inventory. Everything else throws, ENOTDIR included: `channels`
|
|
35
|
-
* and `schedules` are named directories, so the path existing as a FILE is a mistake in the agent
|
|
36
|
-
* rather than an agent without one (`service.test.ts` pins that for `schedules`). `paths.ts` folds
|
|
37
|
-
* ENOTDIR into its empty scan for the opposite reason — it asks which children HAPPEN to be agent
|
|
38
|
-
* dirs, where a file simply is not one. `not_found` is a non-Node runtime's ENOENT.
|
|
39
|
-
*
|
|
40
|
-
* SYMLINKS ARE SKIPPED, and that is a boundary rather than an oversight: `assertInsideAgentDir`
|
|
41
|
-
* guards the code-input DIRECTORY against escaping the definition, and nothing guards the entries
|
|
42
|
-
* inside it, so following a link would import from anywhere on the box past the very check meant to
|
|
43
|
-
* prevent it. Do not "fix" this by following them — report them, which this does.
|
|
44
|
-
*
|
|
45
|
-
* The skip is WARNED HERE, not handed back, because "this file is not loadable" holds for all four
|
|
46
|
-
* consumers and only one of them imports — a listing that reports the name it cannot load reads as
|
|
47
|
-
* "I never created it". What to do about a load FAILURE does differ per caller, so that travels as
|
|
48
|
-
* data. One warning per READ, so a command that both lists and loads the same directory (`deploy
|
|
49
|
-
* agentcore` does) says it twice — both readings are true, and remembering what was already said
|
|
50
|
-
* would mean a restart stops mentioning a skip that is still there.
|
|
51
|
-
*
|
|
52
|
-
* A skip is therefore a warning ONLY, unlike a {@link ModuleLoadFailure}: it is absent from `info
|
|
53
|
-
* --json` and `deploy` does not gate on it. That is the deliberate cost of one report for four
|
|
54
|
-
* consumers, three of which list names and have nowhere to put data. To give a machine consumer the
|
|
55
|
-
* skips, return them beside the entries — do not reconstruct them from stderr.
|
|
23
|
+
* WHAT A CODE-INPUT DIRECTORY DECLARES — the single answer to "which files here are modules", without importing any of
|
|
24
|
+
* them. Internal: the loading functions below are its only consumers.
|
|
56
25
|
*/
|
|
57
|
-
|
|
26
|
+
async function moduleInventory(subDir) {
|
|
58
27
|
let dirents;
|
|
59
28
|
try {
|
|
60
29
|
dirents = await readdir(subDir, { withFileTypes: true });
|
|
@@ -68,8 +37,6 @@ export async function moduleInventory(subDir) {
|
|
|
68
37
|
const sub = basename(subDir);
|
|
69
38
|
const entries = [];
|
|
70
39
|
// Sorted by the NAME a consumer sees, so none of them re-sorts and none can disagree about order.
|
|
71
|
-
// Filename breaks a tie: `foo.js` and `foo.ts` both read as `foo`, and the domain loaders document
|
|
72
|
-
// that the FIRST wins — deciding that here keeps it from depending on readdir's order.
|
|
73
40
|
const byName = (a, b) => moduleName(a.name).localeCompare(moduleName(b.name)) || a.name.localeCompare(b.name);
|
|
74
41
|
for (const dirent of dirents.sort(byName)) {
|
|
75
42
|
if (!isModuleFile(dirent.name))
|
|
@@ -90,17 +57,31 @@ export async function moduleInventory(subDir) {
|
|
|
90
57
|
}
|
|
91
58
|
return entries;
|
|
92
59
|
}
|
|
93
|
-
/** A module the loader skipped, said once, the same way for tools, channels and
|
|
60
|
+
/** A module the loader skipped, said once, the same way for tools, channels and routines. */
|
|
94
61
|
export function reportModuleLoadFailures(failures) {
|
|
95
62
|
for (const f of failures)
|
|
96
63
|
log.warn(`[fastagent] ${f.label} failed to load, skipping it — ${f.message}`);
|
|
97
64
|
}
|
|
98
65
|
/**
|
|
99
|
-
*
|
|
100
|
-
* is
|
|
101
|
-
*
|
|
102
|
-
*
|
|
66
|
+
* THE REFUSAL every path that is about to RUN the agent shares: an enabled file under `tools/`, `channels/` or
|
|
67
|
+
* `routines/` is a declaration, so one that cannot load means the agent is missing something its author said it
|
|
68
|
+
* has. Starting anyway announces a ready service over an absent capability — a schedule that never fires, a tool the
|
|
69
|
+
* model simply never gets — with nothing but one warning to find it by. Every failure is reported before this
|
|
70
|
+
* throws, so a boot fixes all of them at once rather than one per restart.
|
|
71
|
+
*
|
|
72
|
+
* Inspecting a definition (`info`, `fastagent tool`) is NOT this path: it must survive a broken file in order to
|
|
73
|
+
* report it.
|
|
103
74
|
*/
|
|
75
|
+
export function refuseBrokenDeclarations(failures) {
|
|
76
|
+
if (failures.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
reportModuleLoadFailures(failures);
|
|
79
|
+
// The reasons are repeated from the warnings above because this message is all an embedder catching the rejection
|
|
80
|
+
// has, and all a deployment running at FASTAGENT_LOG_LEVEL=error sees.
|
|
81
|
+
throw new Error(`failed to load: ${failures.map((f) => `${f.label} (${f.message})`).join("; ")} — fix it, or rename an ` +
|
|
82
|
+
`intentionally disabled file to *.disabled`);
|
|
83
|
+
}
|
|
84
|
+
/** Import every module the directory declares ({@link moduleInventory}). */
|
|
104
85
|
export async function loadModuleDir(subDir) {
|
|
105
86
|
const entries = await moduleInventory(subDir);
|
|
106
87
|
const modules = [];
|
|
@@ -121,8 +102,8 @@ export async function loadModuleDir(subDir) {
|
|
|
121
102
|
return { modules, failures };
|
|
122
103
|
}
|
|
123
104
|
/**
|
|
124
|
-
* A hint for the two common dynamic-import failures — an uninstalled dependency or a non-ESM
|
|
125
|
-
*
|
|
105
|
+
* A hint for the two common dynamic-import failures — an uninstalled dependency or a non-ESM package — and empty
|
|
106
|
+
* otherwise, so an unrelated error is reported on its own.
|
|
126
107
|
*/
|
|
127
108
|
export function moduleLoadHint(error) {
|
|
128
109
|
if (error.code === "ERR_MODULE_NOT_FOUND" || /Cannot find (package|module)/.test(error.message)) {
|