@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
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-turn capabilities shared by every FastAgent-defined tool. A tool is built once and reused across
|
|
3
|
-
* turns, so current cwd/session/activation bindings ride AsyncLocalStorage rather than definition
|
|
4
|
-
* closures. Deploy-time ambients a tool closes over at build time do NOT belong here.
|
|
5
|
-
*/
|
|
1
|
+
/** Per-turn capabilities shared by every FastAgent-defined tool. */
|
|
6
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
3
|
import type { SessionEntry as PiSessionEntry, AgentSession } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
/** FastAgent's read-only port over the current conversation manager.
|
|
9
|
-
* different concrete session implementations to this one tool-runtime contract. */
|
|
4
|
+
/** FastAgent's read-only port over the current conversation manager. */
|
|
10
5
|
export interface ReadonlySessionManager {
|
|
11
6
|
getSessionId(): string;
|
|
12
7
|
getHeader(): Promise<{
|
|
@@ -15,22 +10,16 @@ export interface ReadonlySessionManager {
|
|
|
15
10
|
}>;
|
|
16
11
|
getBranch(): Promise<PiSessionEntry[]>;
|
|
17
12
|
}
|
|
18
|
-
/**
|
|
19
|
-
* pi's AgentSession as the port above — the SAME adapter for both of its consumers: chat's resident
|
|
20
|
-
* session (session-builder.ts) and serving's per-invoke one (agent-session-factory.ts).
|
|
21
|
-
*
|
|
22
|
-
* `sessionId` is the CALLER's, not pi's. A tool correlates its own state by the id the channel
|
|
23
|
-
* minted; pi's is that id encoded into a filename-safe record name, and leaking the encoding here
|
|
24
|
-
* would hand a telegram tool `s-1001234567890` for a room it knows as `-1001234567890`.
|
|
25
|
-
*/
|
|
13
|
+
/** pi's AgentSession as the port above — the SAME adapter for both of its consumers. */
|
|
26
14
|
export declare function agentSessionManager(session: AgentSession, sessionId: string): ReadonlySessionManager;
|
|
27
15
|
/**
|
|
28
|
-
* The turn's tool-activation bridge — narrow closures over the CURRENT session (bound per turn), so
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
16
|
+
* The turn's tool-activation bridge — narrow closures over the CURRENT session (bound per turn), so a loader tool can
|
|
17
|
+
* activate deferred tools mid-turn without tool.ts importing the engine. pi anchors the addition in the transcript
|
|
18
|
+
* (a system message carrying `toolsAdded`, written after the batch of tool results the activating call belongs to),
|
|
19
|
+
* so providers with native deferred loading keep their
|
|
20
|
+
* prompt-cache prefix; that message describes the run that wrote it, so what carries an activation into LATER turns
|
|
21
|
+
* is fastagent's own `fastagent:tool-activation` entry, replayed by the per-invoke restore
|
|
22
|
+
* (agent-session-factory.ts).
|
|
34
23
|
*/
|
|
35
24
|
export interface ToolActivation {
|
|
36
25
|
/** Names of the currently ACTIVE tools. */
|
|
@@ -40,36 +29,19 @@ export interface ToolActivation {
|
|
|
40
29
|
name: string;
|
|
41
30
|
description: string;
|
|
42
31
|
}>;
|
|
43
|
-
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
* signature would need a lock to say the same thing, and the lock is what a previous version had
|
|
49
|
-
* — one rebuilt per tool call, so the parallel batch it existed for never met on it. If pi's
|
|
50
|
-
* setters ever become async, this signature is where that breaks, loudly. */
|
|
32
|
+
/**
|
|
33
|
+
* ADDITIVE activation. Returns ONLY the names this call actually added — a name a batch sibling activated first
|
|
34
|
+
* is not in it. That return value is the caller's truth for both what to report and what to charge an activation
|
|
35
|
+
* cap: the call is atomic, but an `active()` -> await -> `activate()` sequence around it is not.
|
|
36
|
+
*/
|
|
51
37
|
activate(names: string[]): string[];
|
|
52
38
|
}
|
|
53
|
-
/**
|
|
54
|
-
* The activation bridge over a live pi session — the ONE implementation, for both consumers.
|
|
55
|
-
*
|
|
56
|
-
* Serving (`agent-session-factory.ts`) and chat (`session-builder.ts`) had a copy each, identical
|
|
57
|
-
* but for the persistence line; the neighbouring `definitionResourceLoaderOptions` exists because
|
|
58
|
-
* that exact duplication drifted once before. The difference is a PARAMETER now: `onActivated` is
|
|
59
|
-
* what a served session uses to record the delta that carries the discovery into its next turn,
|
|
60
|
-
* and chat has nowhere to put one (pi's SessionContext has no active-tool set).
|
|
61
|
-
*
|
|
62
|
-
* Bind it to the SESSION, never to a tool call: the next call has to see what this one activated.
|
|
63
|
-
*/
|
|
39
|
+
/** The activation bridge over a live pi session — the ONE implementation, for both consumers. */
|
|
64
40
|
export declare function sessionToolActivation(session: AgentSession, onActivated?: (added: string[]) => void): ToolActivation;
|
|
65
41
|
export interface TurnContext {
|
|
66
|
-
/** Working directory for this execution.
|
|
42
|
+
/** Working directory for this execution. */
|
|
67
43
|
cwd?: string;
|
|
68
|
-
/** Current conversation manager. Absent outside a FastAgent-managed agent turn. */
|
|
69
44
|
sessionManager?: ReadonlySessionManager;
|
|
70
|
-
/** Tool activation for the current turn. Two producers, one consumer surface: agent-session-factory.ts
|
|
71
|
-
* bridges the served session; session-builder.ts bridges the resident one (chat emulates deferral —
|
|
72
|
-
* same loader, same semantics). Absent only outside any turn (a bare `fastagent tool` run). */
|
|
73
45
|
tools?: ToolActivation;
|
|
74
46
|
}
|
|
75
47
|
export declare const turnContext: AsyncLocalStorage<TurnContext>;
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-turn capabilities shared by every FastAgent-defined tool. A tool is built once and reused across
|
|
3
|
-
* turns, so current cwd/session/activation bindings ride AsyncLocalStorage rather than definition
|
|
4
|
-
* closures. Deploy-time ambients a tool closes over at build time do NOT belong here.
|
|
5
|
-
*/
|
|
1
|
+
/** Per-turn capabilities shared by every FastAgent-defined tool. */
|
|
6
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
-
/**
|
|
8
|
-
* pi's AgentSession as the port above — the SAME adapter for both of its consumers: chat's resident
|
|
9
|
-
* session (session-builder.ts) and serving's per-invoke one (agent-session-factory.ts).
|
|
10
|
-
*
|
|
11
|
-
* `sessionId` is the CALLER's, not pi's. A tool correlates its own state by the id the channel
|
|
12
|
-
* minted; pi's is that id encoded into a filename-safe record name, and leaking the encoding here
|
|
13
|
-
* would hand a telegram tool `s-1001234567890` for a room it knows as `-1001234567890`.
|
|
14
|
-
*/
|
|
3
|
+
/** pi's AgentSession as the port above — the SAME adapter for both of its consumers. */
|
|
15
4
|
export function agentSessionManager(session, sessionId) {
|
|
16
5
|
return {
|
|
17
6
|
getSessionId: () => sessionId,
|
|
@@ -26,17 +15,7 @@ export function agentSessionManager(session, sessionId) {
|
|
|
26
15
|
},
|
|
27
16
|
};
|
|
28
17
|
}
|
|
29
|
-
/**
|
|
30
|
-
* The activation bridge over a live pi session — the ONE implementation, for both consumers.
|
|
31
|
-
*
|
|
32
|
-
* Serving (`agent-session-factory.ts`) and chat (`session-builder.ts`) had a copy each, identical
|
|
33
|
-
* but for the persistence line; the neighbouring `definitionResourceLoaderOptions` exists because
|
|
34
|
-
* that exact duplication drifted once before. The difference is a PARAMETER now: `onActivated` is
|
|
35
|
-
* what a served session uses to record the delta that carries the discovery into its next turn,
|
|
36
|
-
* and chat has nowhere to put one (pi's SessionContext has no active-tool set).
|
|
37
|
-
*
|
|
38
|
-
* Bind it to the SESSION, never to a tool call: the next call has to see what this one activated.
|
|
39
|
-
*/
|
|
18
|
+
/** The activation bridge over a live pi session — the ONE implementation, for both consumers. */
|
|
40
19
|
export function sessionToolActivation(session, onActivated) {
|
|
41
20
|
return {
|
|
42
21
|
active: () => session.getActiveToolNames(),
|
|
@@ -53,8 +32,10 @@ export function sessionToolActivation(session, onActivated) {
|
|
|
53
32
|
};
|
|
54
33
|
}
|
|
55
34
|
export const turnContext = new AsyncLocalStorage();
|
|
56
|
-
/**
|
|
57
|
-
*
|
|
35
|
+
/**
|
|
36
|
+
* dedupe → keep registered names only (pi's setters THROW on unknown) → exclude already-active → the names to actually
|
|
37
|
+
* add (empty = nothing to set).
|
|
38
|
+
*/
|
|
58
39
|
function additiveActivation(registered, current, names) {
|
|
59
40
|
const known = new Set(registered);
|
|
60
41
|
const active = new Set(current);
|
|
@@ -2,77 +2,86 @@ import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
|
2
2
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { type ModuleLoadFailure } from "../../loader.ts";
|
|
5
|
+
import { type DeclaredSecret } from "../../declared-secrets.ts";
|
|
5
6
|
import { type ReadonlySessionManager, type ToolActivation } from "./tool-context.ts";
|
|
6
7
|
export interface ToolContext {
|
|
7
8
|
/** Working directory for this execution. */
|
|
8
9
|
cwd: string;
|
|
9
10
|
/** Abort signal for the current turn — honor it to cancel in-flight work on cancellation. */
|
|
10
11
|
signal?: AbortSignal;
|
|
11
|
-
/** Current conversation manager. Present during serving/chat; absent for sessionless direct calls. */
|
|
12
12
|
sessionManager?: ReadonlySessionManager;
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Tool activation for the current turn (a loader tool activates {@link DefineToolOptions.deferred} tools with it —
|
|
15
|
+
* the built-in `search_tools` is one consumer).
|
|
16
|
+
*/
|
|
17
17
|
tools?: ToolActivation;
|
|
18
|
+
/** THE values of {@link DefineToolOptions.secrets}, keyed by the names this tool declared — read
|
|
19
|
+
* from the process environment per call, so a value rotated THERE takes effect without a restart
|
|
20
|
+
* (one rotated in `.secrets/.env` does not: that file is loaded once at startup). This is how a
|
|
21
|
+
* tool gets a credential: reaching into `process.env` instead leaves the name undeclared, which
|
|
22
|
+
* means nothing carries it to a deployed box and nothing checks it before the call fails. */
|
|
23
|
+
secrets: Record<string, string>;
|
|
18
24
|
}
|
|
19
|
-
export interface DefineToolOptions<I extends z.ZodType> {
|
|
20
|
-
/** Explicit name. Usually omitted — a `tools/<name>.ts` tool is named from its filename. */
|
|
25
|
+
export interface DefineToolOptions<I extends z.ZodType, S extends readonly string[] = readonly []> {
|
|
21
26
|
name?: string;
|
|
22
27
|
description: string;
|
|
23
28
|
input: I;
|
|
24
29
|
/**
|
|
25
|
-
* Registered but NOT initially active: the tool's schema stays out of every request (and the model's
|
|
26
|
-
*
|
|
27
|
-
* exists — activates it mid-turn. For tool-heavy agents: fewer schemas per turn, and on providers
|
|
28
|
-
* with native deferred loading the activation preserves the prompt-cache prefix. The trade-off:
|
|
29
|
-
* discovery rides entirely on this description — write it for the search. Default: false.
|
|
30
|
+
* Registered but NOT initially active: the tool's schema stays out of every request (and the model's sight) until a
|
|
31
|
+
* loader.
|
|
30
32
|
*/
|
|
31
33
|
deferred?: boolean;
|
|
32
|
-
/** pi's per-tool execution mode: "sequential" makes pi run any batch containing this tool serially.
|
|
33
|
-
* REQUIRED for a tool that activates others (a loader): pi's own diff around SDK tools (chat)
|
|
34
|
-
* would attribute one activation to two parallel calls otherwise. */
|
|
34
|
+
/** pi's per-tool execution mode: "sequential" makes pi run any batch containing this tool serially. */
|
|
35
35
|
executionMode?: "sequential" | "parallel";
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Env vars this tool needs (`secrets: ["X_API_KEY"]`). Their values arrive as `ctx.secrets.X_API_KEY`,
|
|
38
|
+
* typed from this list. Declaring buys the two guarantees a bare `process.env` read cannot have:
|
|
39
|
+
* `deploy` carries the value to the host without it being listed anywhere else, and `dev`/`start`
|
|
40
|
+
* REFUSE TO START while it is unset, naming this file — instead of the tool failing on its first
|
|
41
|
+
* real call (see src/declared-secrets.ts).
|
|
42
|
+
*/
|
|
43
|
+
secrets?: S;
|
|
44
|
+
execute: (input: z.infer<I>, ctx: Omit<ToolContext, "secrets"> & {
|
|
45
|
+
secrets: Record<S[number], string>;
|
|
46
|
+
}) => unknown | Promise<unknown>;
|
|
37
47
|
}
|
|
38
48
|
/** AgentTool with Pi's optional per-call context; absent for sessionless CLI execution. */
|
|
39
49
|
export type MountedTool = Omit<AgentTool, "execute"> & {
|
|
40
50
|
execute(...args: [...Parameters<AgentTool["execute"]>, context?: ExtensionContext]): ReturnType<AgentTool["execute"]>;
|
|
41
51
|
};
|
|
42
|
-
/** An AgentTool with fastagent's deferral marker
|
|
43
|
-
* (`config.tools`, L1/L2 `tools`): plain `AgentTool` has no `deferred`, so an object literal with the
|
|
44
|
-
* marker would fail excess-property checking against upstream's type. `defineTool` produces it. */
|
|
52
|
+
/** An AgentTool with fastagent's deferral marker. */
|
|
45
53
|
export type FastagentTool = AgentTool & {
|
|
46
54
|
deferred?: boolean;
|
|
55
|
+
/** {@link DefineToolOptions.secrets} — read back by `readSecretDeclaration`; pi ignores it. */
|
|
56
|
+
secrets?: readonly string[];
|
|
47
57
|
};
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
58
|
+
/**
|
|
59
|
+
* Read the {@link DefineToolOptions.deferred} marker off a mounted tool (extra property on the AgentTool object — pi
|
|
60
|
+
* ignores it).
|
|
61
|
+
*/
|
|
50
62
|
export declare function isDeferredTool(tool: MountedTool): boolean;
|
|
51
|
-
/**
|
|
52
|
-
*
|
|
63
|
+
/**
|
|
64
|
+
* The same tool without the deferred marker — for a loader that must stay active (a deferred loader could never be
|
|
65
|
+
* activated and would strand every deferred tool).
|
|
66
|
+
*/
|
|
53
67
|
export declare function stripDeferredMarker(tool: MountedTool): MountedTool;
|
|
54
|
-
export declare function defineTool<I extends z.ZodType>(options: DefineToolOptions<I>): FastagentTool;
|
|
55
|
-
/** A discarded same-name tool (within `tools/`, or against an existing tool).
|
|
68
|
+
export declare function defineTool<I extends z.ZodType, const S extends readonly string[] = readonly []>(options: DefineToolOptions<I, S>): FastagentTool;
|
|
69
|
+
/** A discarded same-name tool (within `tools/`, or against an existing tool). */
|
|
56
70
|
export interface ToolCollision {
|
|
57
71
|
name: string;
|
|
58
72
|
source: string;
|
|
59
73
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its
|
|
62
|
-
* filename. A file broken for ANY reason — a failed import (from {@link loadModuleDir}) or not being a
|
|
63
|
-
* tool (no `execute`) — is ISOLATED into `failures` (skipped + reported, not thrown) so one broken file
|
|
64
|
-
* can't crash `start`; the agent serves the tools that loaded. A repo turned into an agent often has a
|
|
65
|
-
* `tools/` dir of its OWN scripts, which is exactly this case.
|
|
66
|
-
*/
|
|
74
|
+
/** Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its filename. */
|
|
67
75
|
export declare function loadTools(dir: string): Promise<{
|
|
68
76
|
tools: AgentTool[];
|
|
77
|
+
/** What each loaded tool declared it needs, BY TOOL NAME and attributed to the file. Per tool
|
|
78
|
+
* because whether a declaration counts depends on whether that tool ends up MOUNTED — a name
|
|
79
|
+
* shadowed by a coding tool never runs ({@link resolveAgentTools} makes that call). */
|
|
80
|
+
secrets: Map<string, DeclaredSecret[]>;
|
|
69
81
|
collisions: ToolCollision[];
|
|
70
82
|
failures: ModuleLoadFailure[];
|
|
71
83
|
}>;
|
|
72
|
-
/**
|
|
73
|
-
* Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name.
|
|
74
|
-
* Existing tools win; dropped discovered tools surface as collisions.
|
|
75
|
-
*/
|
|
84
|
+
/** Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name. */
|
|
76
85
|
export declare function mergeDiscoveredTools(existing: MountedTool[], discovered: AgentTool[]): {
|
|
77
86
|
tools: MountedTool[];
|
|
78
87
|
collisions: ToolCollision[];
|
package/dist/engines/pi/tool.js
CHANGED
|
@@ -1,33 +1,40 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool authoring: `defineTool` (the authoring surface) and `loadTools` (filesystem discovery).
|
|
3
|
-
* Drop a file in `tools/`, default-export `defineTool({...})`, and it is discovered, named from
|
|
4
|
-
* the filename, validated, and injected.
|
|
5
|
-
*
|
|
6
|
-
* // tools/lookup-order.ts → tool "lookup-order"
|
|
7
|
-
* export default defineTool({
|
|
8
|
-
* description: "Look up an order by id.",
|
|
9
|
-
* input: z.object({ orderId: z.string() }),
|
|
10
|
-
* async execute({ orderId }) { return await db.find(orderId); },
|
|
11
|
-
* });
|
|
12
|
-
*/
|
|
1
|
+
/** Tool authoring: `defineTool` (the authoring surface) and `loadTools` (filesystem discovery). */
|
|
13
2
|
import { join } from "node:path";
|
|
14
3
|
import { assertInsideAgentDir } from "../../paths.js";
|
|
15
4
|
import { z } from "zod";
|
|
16
5
|
import { loadModuleDir } from "../../loader.js";
|
|
6
|
+
import { readSecretDeclaration, secretValues } from "../../declared-secrets.js";
|
|
17
7
|
import { turnContext } from "./tool-context.js";
|
|
18
|
-
/**
|
|
19
|
-
*
|
|
8
|
+
/**
|
|
9
|
+
* Read the {@link DefineToolOptions.deferred} marker off a mounted tool (extra property on the AgentTool object — pi
|
|
10
|
+
* ignores it).
|
|
11
|
+
*/
|
|
20
12
|
export function isDeferredTool(tool) {
|
|
21
13
|
return tool.deferred === true;
|
|
22
14
|
}
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
15
|
+
/**
|
|
16
|
+
* The same tool without the deferred marker — for a loader that must stay active (a deferred loader could never be
|
|
17
|
+
* activated and would strand every deferred tool).
|
|
18
|
+
*/
|
|
25
19
|
export function stripDeferredMarker(tool) {
|
|
26
20
|
if (!isDeferredTool(tool))
|
|
27
21
|
return tool;
|
|
28
22
|
const { deferred: _drop, ...active } = tool;
|
|
29
23
|
return active;
|
|
30
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Ask the provider to constrain sampling to the tool's schema, the posture pi's own built-ins take. `"prefer"`
|
|
27
|
+
* rather than `"require"`: a schema pi cannot express strictly, or a provider without strict mode, silently falls
|
|
28
|
+
* back to an ordinary function tool instead of failing the turn.
|
|
29
|
+
*
|
|
30
|
+
* Nothing here has to undo pi's strict rewrite. To express "optional" strictly, pi marks every property required and
|
|
31
|
+
* unions the optional ones with `null`, so a constrained model emits `null` where it would have omitted the key — and
|
|
32
|
+
* pi-ai's `validateToolArguments` drops those nulls (`normalizeOptionalNulls`) before `execute` is called. It drops
|
|
33
|
+
* one only where the author's OWN schema rejects null, so any nullable property keeps its `null`, and a
|
|
34
|
+
* `.nullable().optional()` property can no longer distinguish "absent" from "null" — the one place this changes
|
|
35
|
+
* what an author's `execute` receives.
|
|
36
|
+
*/
|
|
37
|
+
const CONSTRAINED_SAMPLING = { type: "json_schema", strict: "prefer" };
|
|
31
38
|
/** Wrap a plain return value into pi's tool-result shape; pass a full result through unchanged. */
|
|
32
39
|
function wrapResult(value) {
|
|
33
40
|
if (value && typeof value === "object" && Array.isArray(value.content)) {
|
|
@@ -36,6 +43,10 @@ function wrapResult(value) {
|
|
|
36
43
|
const text = typeof value === "string" ? value : JSON.stringify(value ?? null);
|
|
37
44
|
return { content: [{ type: "text", text }], details: value };
|
|
38
45
|
}
|
|
46
|
+
// `S` defaults to the EMPTY tuple, not `readonly string[]`: a tool that declares nothing then gets
|
|
47
|
+
// `ctx.secrets` with no keys, so `ctx.secrets.X_API_KEY` fails to compile instead of type-checking as
|
|
48
|
+
// a `string` that is `undefined` at run time. "A typo is a compile error" has to hold for the tool
|
|
49
|
+
// that forgot to declare, which is the one making the mistake.
|
|
39
50
|
export function defineTool(options) {
|
|
40
51
|
const { $schema: _drop, ...parameters } = z.toJSONSchema(options.input);
|
|
41
52
|
const tool = {
|
|
@@ -43,8 +54,10 @@ export function defineTool(options) {
|
|
|
43
54
|
label: options.name ?? "",
|
|
44
55
|
description: options.description,
|
|
45
56
|
parameters,
|
|
57
|
+
constrainedSampling: CONSTRAINED_SAMPLING,
|
|
46
58
|
...(options.deferred ? { deferred: true } : {}),
|
|
47
59
|
...(options.executionMode ? { executionMode: options.executionMode } : {}),
|
|
60
|
+
...(options.secrets?.length ? { secrets: options.secrets } : {}),
|
|
48
61
|
async execute(_toolCallId, rawParams, signal) {
|
|
49
62
|
const parsed = options.input.safeParse(rawParams);
|
|
50
63
|
if (!parsed.success) {
|
|
@@ -53,73 +66,46 @@ export function defineTool(options) {
|
|
|
53
66
|
return { content: [{ type: "text", text: `Invalid arguments: ${detail}` }], details: { error: detail } };
|
|
54
67
|
}
|
|
55
68
|
const store = turnContext.getStore();
|
|
56
|
-
|
|
57
|
-
// deferred-loading providers add the definitions at this transcript position without
|
|
58
|
-
// invalidating the cached prompt prefix. The names come from this execute's OWN activate()
|
|
59
|
-
// calls (accumulated below), NOT from an active-set before/after diff: pi runs tool calls of a
|
|
60
|
-
// batch in parallel, and a snapshot diff would stamp a sibling's activation onto the wrong tool
|
|
61
|
-
// result, drifting the load point.
|
|
62
|
-
const added = [];
|
|
63
|
-
const tools = store?.tools
|
|
64
|
-
? {
|
|
65
|
-
...store.tools,
|
|
66
|
-
activate: (names) => {
|
|
67
|
-
// biome-ignore lint/style/noNonNullAssertion: guarded by the ternary above
|
|
68
|
-
const activated = store.tools.activate(names);
|
|
69
|
-
added.push(...activated);
|
|
70
|
-
return activated;
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
: undefined;
|
|
74
|
-
const result = wrapResult(await options.execute(parsed.data, {
|
|
69
|
+
return wrapResult(await options.execute(parsed.data, {
|
|
75
70
|
cwd: store?.cwd ?? process.cwd(),
|
|
76
71
|
signal,
|
|
77
72
|
sessionManager: store?.sessionManager,
|
|
78
|
-
tools,
|
|
73
|
+
tools: store?.tools,
|
|
74
|
+
secrets: secretValues(options.secrets),
|
|
79
75
|
}));
|
|
80
|
-
if (added.length > 0) {
|
|
81
|
-
// A copy, not a mutation: wrapResult passes a full AgentToolResult through by REFERENCE, and an
|
|
82
|
-
// author may legally return a shared/frozen result object — stamping in place would corrupt it
|
|
83
|
-
// across calls (or throw on frozen), only on the rare activating path.
|
|
84
|
-
return { ...result, addedToolNames: [...new Set([...(result.addedToolNames ?? []), ...added])] };
|
|
85
|
-
}
|
|
86
|
-
return result;
|
|
87
76
|
},
|
|
88
77
|
};
|
|
89
78
|
return tool;
|
|
90
79
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its
|
|
93
|
-
* filename. A file broken for ANY reason — a failed import (from {@link loadModuleDir}) or not being a
|
|
94
|
-
* tool (no `execute`) — is ISOLATED into `failures` (skipped + reported, not thrown) so one broken file
|
|
95
|
-
* can't crash `start`; the agent serves the tools that loaded. A repo turned into an agent often has a
|
|
96
|
-
* `tools/` dir of its OWN scripts, which is exactly this case.
|
|
97
|
-
*/
|
|
80
|
+
/** Discover code tools in `<dir>/tools/`: each `*.ts|.js|.mjs` default-exports a tool, named from its filename. */
|
|
98
81
|
export async function loadTools(dir) {
|
|
99
|
-
// The same containment guard channels/
|
|
100
|
-
// IMPORTED AND EXECUTED, so a symlink escaping the agent dir is exactly what it must refuse.
|
|
82
|
+
// The same containment guard channels/routines/skills get.
|
|
101
83
|
await assertInsideAgentDir(dir, "tools");
|
|
102
84
|
const { modules, failures } = await loadModuleDir(join(dir, "tools"));
|
|
103
85
|
const byName = new Map();
|
|
104
86
|
const collisions = [];
|
|
87
|
+
const secrets = new Map();
|
|
105
88
|
for (const { name, label, file, mod } of modules) {
|
|
106
89
|
const tool = mod.default;
|
|
107
90
|
if (!tool || typeof tool.execute !== "function") {
|
|
108
91
|
failures.push({ label, file, message: `${label} must default-export defineTool({...})` });
|
|
109
92
|
continue;
|
|
110
93
|
}
|
|
94
|
+
const declaration = readSecretDeclaration(tool, label);
|
|
95
|
+
if (declaration.error !== undefined) {
|
|
96
|
+
failures.push({ label, file, message: declaration.error });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
111
99
|
if (byName.has(name)) {
|
|
112
100
|
collisions.push({ name, source: label });
|
|
113
101
|
continue;
|
|
114
102
|
}
|
|
115
103
|
byName.set(name, { ...tool, name });
|
|
104
|
+
secrets.set(name, declaration.secrets);
|
|
116
105
|
}
|
|
117
|
-
return { tools: [...byName.values()], collisions, failures };
|
|
106
|
+
return { tools: [...byName.values()], secrets, collisions, failures };
|
|
118
107
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name.
|
|
121
|
-
* Existing tools win; dropped discovered tools surface as collisions.
|
|
122
|
-
*/
|
|
108
|
+
/** Merge resolved tools (pi coding tools + `config.tools`) with discovered `tools/`, deduped by name. */
|
|
123
109
|
export function mergeDiscoveredTools(existing, discovered) {
|
|
124
110
|
const names = new Set(existing.map((t) => t.name));
|
|
125
111
|
const tools = [...existing];
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The turn mechanism's ENGINE-agnostic half: the parts that describe a turn rather than pi. Not
|
|
3
|
-
* "engine-neutral" in this repo's sense — that term is reserved for code with no engine import at
|
|
4
|
-
* all (src/agent.ts), and the terminals here read pi's message shape. What they do not touch is how
|
|
5
|
-
* a turn is driven, which is why they survived the engine change unaltered.
|
|
6
|
-
*
|
|
7
|
-
* Lease — single-writer concurrency floor (injectable port + in-process default)
|
|
8
|
-
* Terminals — a settled pi message or a thrown error → the SPEC terminal, `retryable` included
|
|
9
|
-
* EventQueue — push→pull plumbing for engines that emit events beside their result
|
|
10
|
-
* Prompt prep — SPEC images → pi's ImageContent
|
|
11
|
-
* Projection — the rich SessionEvent stream → the narrow SPEC one
|
|
12
|
-
* Observation — the seam a control-plane hub attaches to (RunControls + SessionObserver)
|
|
13
|
-
*
|
|
14
|
-
* What is NOT neutral — pi's event vocabulary and how a turn is driven — stays in invoke-session.ts,
|
|
15
|
-
* the L0 that owns it.
|
|
16
|
-
*/
|
|
1
|
+
/** The turn mechanism's ENGINE-agnostic half: the parts that describe a turn rather than pi. */
|
|
17
2
|
import type { AssistantMessage, ImageContent } from "@earendil-works/pi-ai";
|
|
18
3
|
import { type AgentEvent, type Prompt } from "../../agent.ts";
|
|
19
4
|
import type { SessionEvent } from "../../session.ts";
|
|
20
5
|
export type Release = () => void;
|
|
21
6
|
export interface Lease {
|
|
22
|
-
/** Try to acquire exclusive write access for the session (fail-fast).
|
|
7
|
+
/** Try to acquire exclusive write access for the session (fail-fast). */
|
|
23
8
|
tryAcquire(session: string): Release | null;
|
|
24
9
|
}
|
|
25
10
|
export declare function inProcessLease(): Lease;
|
|
@@ -29,54 +14,31 @@ export declare function classifyRetryable(details: string, signal: {
|
|
|
29
14
|
code?: unknown;
|
|
30
15
|
}): boolean;
|
|
31
16
|
/**
|
|
32
|
-
* Terminal mapping, decided by the resolved message's stopReason: pi's prompt()
|
|
33
|
-
*
|
|
34
|
-
*
|
|
17
|
+
* Terminal mapping, decided by the resolved message's stopReason: pi's `prompt()` RESOLVES a message with stopReason
|
|
18
|
+
* "error"/"aborted" rather than throwing, so relying on catch alone would miss that whole failure class and violate
|
|
19
|
+
* SPEC MUST 1.
|
|
35
20
|
*/
|
|
36
21
|
export declare function toTerminal(message: AssistantMessage): AgentEvent;
|
|
37
22
|
export declare function errorToTerminal(error: unknown): Extract<AgentEvent, {
|
|
38
23
|
type: "failed";
|
|
39
24
|
}>;
|
|
40
25
|
/**
|
|
41
|
-
* Map prompt images to pi ImageContent
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
26
|
+
* Map prompt images to pi ImageContent. `prompt()` normalizes its images itself, with the bound model's resize
|
|
27
|
+
* profile (`inputLimits.images.resize`) and format conversion, so a `"prompt"` delivery hands them over as-is.
|
|
28
|
+
* `steer`/`followUp` do not, so a `"queued"` delivery resizes here with pi's default profile: the model is not known
|
|
29
|
+
* yet when a queued message is prepared. pi's resizer is lazy-imported so the no-image path never loads its module
|
|
30
|
+
* graph.
|
|
45
31
|
*/
|
|
46
|
-
export declare function toPiPromptOptions(prompt: Prompt): Promise<{
|
|
32
|
+
export declare function toPiPromptOptions(prompt: Prompt, delivery: "prompt" | "queued"): Promise<{
|
|
47
33
|
images?: ImageContent[];
|
|
48
34
|
} | undefined>;
|
|
49
|
-
/** Live modulation handles for one active run — what the control plane's `dispatch` routes to.
|
|
50
|
-
* Built inside the turn (it owns the engine instance); registered with the observer at
|
|
51
|
-
* run_started, gone after run_settled. RACE WINDOW (all three commands, symmetric): the run may
|
|
52
|
-
* resolve between the settled-check and the engine call landing — an accepted `abort` can still
|
|
53
|
-
* settle `completed`, and an accepted `steer`/`followUp` can settle without the prompt ever being
|
|
54
|
-
* consumed. Acceptance is not outcome; the settlement is the truth. */
|
|
35
|
+
/** Live modulation handles for one active run — what the control plane's `dispatch` routes to. */
|
|
55
36
|
export interface RunControls {
|
|
56
37
|
steer(prompt: Prompt): Promise<void>;
|
|
57
38
|
followUp(prompt: Prompt): Promise<void>;
|
|
58
39
|
abort(): Promise<void>;
|
|
59
40
|
}
|
|
60
|
-
/** The DATA-plane observation seam: every rich event of every run, pushed as it happens.
|
|
61
|
-
* carries the live {@link RunControls}, attached to the `run_started` event only. A hub
|
|
62
|
-
* (session-control.ts) implements this to serve `events()`/`state()`/`dispatch`; absent = zero
|
|
63
|
-
* overhead. Scope: RUN events only — the hub's own boundary-mutation events (`state_changed`,
|
|
64
|
-
* `compaction_*`) originate in the hub and reach full-vocabulary taps via the hub's `tap` option,
|
|
65
|
-
* not this seam. TRUST BOUNDARY: this seam hands every wired observer the run's modulation handles — it is the trusted hub seam, not a public fan-out point. Do not wire
|
|
66
|
-
* untrusted taps here; give third parties the read-only `events()` stream instead. */
|
|
41
|
+
/** The DATA-plane observation seam: every rich event of every run, pushed as it happens. */
|
|
67
42
|
export type SessionObserver = (session: string, event: SessionEvent, run?: RunControls) => void;
|
|
68
|
-
/** The SPEC projection of the rich stream.
|
|
69
|
-
* message boundaries, run boundaries) project to null — the invoke terminal is produced from the
|
|
70
|
-
* resolved message ({@link toTerminal}), not from `run_settled`. */
|
|
43
|
+
/** The SPEC projection of the rich stream. */
|
|
71
44
|
export declare function projectAgentEvent(se: SessionEvent): AgentEvent | null;
|
|
72
|
-
export declare class EventQueue<T> {
|
|
73
|
-
private buffer;
|
|
74
|
-
private wake?;
|
|
75
|
-
push(item: T): void;
|
|
76
|
-
/**
|
|
77
|
-
* Yield pushed events in order until `done` settles AND the buffer is drained. The terminal is
|
|
78
|
-
* produced separately (toTerminal); rejections of `done` are swallowed here (the caller awaits
|
|
79
|
-
* `run` itself) to avoid unhandled rejections.
|
|
80
|
-
*/
|
|
81
|
-
drainUntil(done: Promise<unknown>): AsyncGenerator<T>;
|
|
82
|
-
}
|