@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
package/dist/engines/pi/open.js
CHANGED
|
@@ -1,39 +1,90 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Open a definition directory into an agent — the single agent opener BOTH `fastagent dev` and
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* A thin command-posture composition over L2 `createPiAgentFromDefinition`: open the directory →
|
|
6
|
-
* resolve model (flag > env > config) and tools (append-after-defaults) → pick session storage →
|
|
7
|
-
* call L2. dev and start share the SAME assembly here (what you iterate is what you serve); they
|
|
8
|
-
* differ only at the CLI — dev watches and uses the in-tree sessions default, start runs without
|
|
9
|
-
* watch and can point sessions at a mounted volume.
|
|
2
|
+
* Open a definition directory into an agent — the single agent opener BOTH `fastagent dev` and `fastagent start`
|
|
3
|
+
* drive.
|
|
10
4
|
*/
|
|
11
5
|
import { mkdir } from "node:fs/promises";
|
|
12
|
-
import {
|
|
13
|
-
import { resolveStateRoot, resolvePlacement } from "../../paths.js";
|
|
14
|
-
import { agentOf, assemblePiFromDefinition, resolveAgentTools } from "./create.js";
|
|
6
|
+
import { loadConfig, resolveAuthLayers, resolveModelSpec, } from "./config.js";
|
|
7
|
+
import { resolveSessionsDir, resolveStateRoot, resolvePlacement } from "../../paths.js";
|
|
8
|
+
import { agentOf, assemblePiFromDefinition, definitionModelRuntime, resolveAgentTools } from "./create.js";
|
|
15
9
|
import { createPiSessionControl } from "./session-control.js";
|
|
16
10
|
import { withWakeTool } from "./wake-tool.js";
|
|
11
|
+
import { refuseBrokenDeclarations } from "../../loader.js";
|
|
17
12
|
import { loadAgentSkills } from "./definition.js";
|
|
13
|
+
import { servedExtensionCommands } from "./agent-session-factory.js";
|
|
18
14
|
import { reportFindingsIfChanged } from "./report.js";
|
|
15
|
+
import { readMachine, withMachine } from "./machine.js";
|
|
19
16
|
import { piSessionRecordStore } from "./session-store.js";
|
|
17
|
+
import { gateSecrets } from "../../secrets-gate.js";
|
|
18
|
+
/**
|
|
19
|
+
* The names a `/` composer completes: this agent's skills — the definition's, plus the ones its machine lends
|
|
20
|
+
* (machine.ts) —, the commands its `extensions/` register, and the machine's prompt templates.
|
|
21
|
+
*
|
|
22
|
+
* `source` says how a name is INVOKED, which is the one thing a client needs from it: a `skill` is sent as
|
|
23
|
+
* `/skill:<name>`, an `extension` command and a `prompt` as `/<name>` (docs/design/session-control.md §5.1.1).
|
|
24
|
+
*
|
|
25
|
+
* The definition is read live, like every turn reads it; the machine is the process's one read, the same snapshot a
|
|
26
|
+
* bound session runs on, so the menu cannot offer a name the next prompt would not expand. The same holds for
|
|
27
|
+
* extensions: `served` is what sessions load — the assembly's entry points, discovered once, and the runtime turns run
|
|
28
|
+
* on (asked only when there are extensions) — never a fresh scan of `extensions/`.
|
|
29
|
+
*/
|
|
30
|
+
export async function agentCommands(agentDir, workspace, served) {
|
|
31
|
+
const own = await loadAgentSkills(agentDir, { cwd: workspace });
|
|
32
|
+
// A skill whose frontmatter broke simply is not in `skills` — it would vanish from the composer with no signal.
|
|
33
|
+
reportFindingsIfChanged(own.dir, own);
|
|
34
|
+
const machine = await readMachine(workspace);
|
|
35
|
+
const { extensionPaths } = served;
|
|
36
|
+
const extensionCommands = extensionPaths.length === 0
|
|
37
|
+
? []
|
|
38
|
+
: await servedExtensionCommands({ cwd: workspace, modelRuntime: await served.modelRuntime(), extensionPaths });
|
|
39
|
+
// pi dispatches an extension command before it expands a template, so a template it shadows never runs.
|
|
40
|
+
const shadowed = new Set(extensionCommands.map((command) => command.invocationName));
|
|
41
|
+
return [
|
|
42
|
+
...withMachine(own.skills, machine.skills).map((skill) => ({
|
|
43
|
+
name: skill.name,
|
|
44
|
+
description: skill.description,
|
|
45
|
+
source: "skill",
|
|
46
|
+
})),
|
|
47
|
+
...extensionCommands.map((command) => ({
|
|
48
|
+
name: command.invocationName,
|
|
49
|
+
...(command.description ? { description: command.description } : {}),
|
|
50
|
+
source: "extension",
|
|
51
|
+
})),
|
|
52
|
+
...machine.prompts
|
|
53
|
+
.filter((prompt) => !shadowed.has(prompt.name))
|
|
54
|
+
.map((prompt) => ({
|
|
55
|
+
name: prompt.name,
|
|
56
|
+
...(prompt.description ? { description: prompt.description } : {}),
|
|
57
|
+
source: "prompt",
|
|
58
|
+
})),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
20
61
|
export async function resolveAgentAssembly(dir, options = {}) {
|
|
21
|
-
// Placement is structural (resolvePlacement): the AGENT DIR carries definition + config + machinery;
|
|
22
|
-
// its parent — the WORKSPACE — is what the agent works on: its cwd and the start of the ②-context
|
|
23
|
-
// walk (that is where it reads the project's AGENTS.md from).
|
|
62
|
+
// Placement is structural (resolvePlacement): the AGENT DIR carries definition + config + machinery; its parent.
|
|
24
63
|
const { agentDir, workspace } = resolvePlacement(dir);
|
|
25
64
|
const { config, path: configPath } = await loadConfig(agentDir);
|
|
26
65
|
const modelSpec = resolveModelSpec(options.model, config);
|
|
27
66
|
if (!modelSpec) {
|
|
28
67
|
throw new Error(`missing model: set --model, "model" in fastagent.config.ts, or FASTAGENT_MODEL (e.g. "openai-codex/gpt-5.5")`);
|
|
29
68
|
}
|
|
30
|
-
const { tools, toolNames, deferredToolNames, toolCollisions, toolFailures } = await resolveAgentTools(config, agentDir, workspace);
|
|
31
|
-
//
|
|
32
|
-
//
|
|
69
|
+
const { tools, toolNames, deferredToolNames, toolCollisions, toolFailures, toolSecrets } = await resolveAgentTools(config, agentDir, workspace);
|
|
70
|
+
// THE serving-path gate for tool declarations, in the order that costs the fewest round trips. A file that could
|
|
71
|
+
// not be imported declares nothing, so its `secrets:` are missing from `toolSecrets` — gating secrets first would
|
|
72
|
+
// report an incomplete set, and fixing the file could then reveal more missing values. Refuse the broken file
|
|
73
|
+
// first and one pass reports every secret the definition actually wants.
|
|
74
|
+
refuseBrokenDeclarations(toolFailures);
|
|
75
|
+
// Both gates live here rather than inside resolveAgentTools, which `info` and `fastagent tool` call to REPORT on a
|
|
76
|
+
// definition and must survive both faults; every path through this function is about to run ALL of the tools, so
|
|
77
|
+
// no `owner`.
|
|
78
|
+
//
|
|
79
|
+
// DEFERRED tools gate too, deliberately: `search_tools` can activate one mid-turn, so "registered"
|
|
80
|
+
// means "may run in this process" — letting it start would put the empty-credential failure back
|
|
81
|
+
// inside a turn. An author who does not want that opts out per tool by not declaring.
|
|
82
|
+
gateSecrets({ declared: toolSecrets, failures: [] });
|
|
83
|
+
// The state root: sessions/channel state/schedule state derive from it (FASTAGENT_STATE_DIR moves it in one knob —
|
|
84
|
+
// a container points it at its volume).
|
|
33
85
|
const stateRoot = resolveStateRoot(agentDir);
|
|
34
|
-
// The credentials file: project-level by default (under `<agentDir>/.secrets`)
|
|
35
|
-
|
|
36
|
-
const authPath = resolveAuthPath(agentDir, options.authPath);
|
|
86
|
+
// The credentials file: project-level by default (under `<agentDir>/.secrets`).
|
|
87
|
+
const auth = resolveAuthLayers(agentDir, options.authPath);
|
|
37
88
|
return {
|
|
38
89
|
config,
|
|
39
90
|
configPath,
|
|
@@ -41,26 +92,43 @@ export async function resolveAgentAssembly(dir, options = {}) {
|
|
|
41
92
|
agentDir,
|
|
42
93
|
workspace,
|
|
43
94
|
stateRoot,
|
|
44
|
-
|
|
95
|
+
auth,
|
|
45
96
|
tools,
|
|
46
97
|
toolNames,
|
|
47
98
|
deferredToolNames,
|
|
48
99
|
toolCollisions,
|
|
49
|
-
|
|
100
|
+
toolSecrets,
|
|
50
101
|
};
|
|
51
102
|
}
|
|
52
103
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
104
|
+
* The model specs `createPiAgentFromDir(dir, { authPath })` could run now: the agent's registry (pi's built-ins plus
|
|
105
|
+
* its `models.json`) filtered to the providers whose credentials are configured, sorted. The directory needs no model
|
|
106
|
+
* set, and the same placement and credential layers as the opener apply, so a listed spec authenticates there.
|
|
107
|
+
* Configuration is checked, not validity: no OAuth token is refreshed and no provider is called.
|
|
108
|
+
*
|
|
109
|
+
* `warn` reaches the credential store. An unreadable or corrupt credentials file otherwise reads as "nothing
|
|
110
|
+
* configured", so a client that must not show that as an empty list passes a sink that throws.
|
|
111
|
+
*/
|
|
112
|
+
export async function availableModelsFromDir(dir, options = {}) {
|
|
113
|
+
const { agentDir } = resolvePlacement(dir);
|
|
114
|
+
const models = await definitionModelRuntime(agentDir, {
|
|
115
|
+
auth: resolveAuthLayers(agentDir, options.authPath),
|
|
116
|
+
...(options.warn ? { warn: options.warn } : {}),
|
|
117
|
+
});
|
|
118
|
+
return (await models.getAvailable()).map((model) => `${model.provider}/${model.id}`).sort();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* "Point at a directory → agent": resolve the placement (`dir` may be either end — the workspace or the agent dir
|
|
122
|
+
* itself), load the config, resolve model and tools, then L2.
|
|
57
123
|
*/
|
|
58
124
|
export async function createPiAgentFromDir(dir, options = {}) {
|
|
59
|
-
const { config, configPath, modelSpec, agentDir, workspace, stateRoot,
|
|
60
|
-
//
|
|
61
|
-
// the
|
|
62
|
-
const mountedTools = withWakeTool(tools, stateRoot, !!options.serving
|
|
63
|
-
|
|
125
|
+
const { config, configPath, modelSpec, agentDir, workspace, stateRoot, auth, tools, toolNames, deferredToolNames, toolCollisions, } = await resolveAgentAssembly(dir, options);
|
|
126
|
+
// Every serve mounts the built-in `wake` tool: the agent's own follow-up work is a default capability, and a serve
|
|
127
|
+
// is where the poller that honors it runs. A one-shot `invoke` has no poller, so nothing there would fire it.
|
|
128
|
+
const mountedTools = withWakeTool(tools, stateRoot, !!options.serving);
|
|
129
|
+
// An explicit value is used as given (the store resolves a relative one against the WORKSPACE); without one, the
|
|
130
|
+
// resolution every reader shares (config.ts), so a serve and an `info` never report on different directories.
|
|
131
|
+
const sessionsDir = options.sessionsDir ?? resolveSessionsDir(agentDir);
|
|
64
132
|
await mkdir(sessionsDir, { recursive: true });
|
|
65
133
|
const sessions = piSessionRecordStore({ dir: sessionsDir, cwd: workspace });
|
|
66
134
|
const { assembly, definition } = await assemblePiFromDefinition(agentDir, {
|
|
@@ -68,51 +136,43 @@ export async function createPiAgentFromDir(dir, options = {}) {
|
|
|
68
136
|
thinkingLevel: config.thinkingLevel,
|
|
69
137
|
cwd: workspace,
|
|
70
138
|
tools: mountedTools,
|
|
71
|
-
|
|
139
|
+
auth,
|
|
72
140
|
// Skills are definition-only (the agent is its directory), so dev mirrors deployment exactly.
|
|
73
141
|
sessions,
|
|
74
142
|
});
|
|
75
|
-
// The hub is wired HERE because the store is created here (an external `createPiSessionControl`
|
|
76
|
-
//
|
|
77
|
-
// against its own registry — the value above is what makes "same" true. An extra caller observer
|
|
78
|
-
// composes after the hub's (TRUSTED seam).
|
|
143
|
+
// The hub is wired HERE because the store is created here (an external `createPiSessionControl` cannot exist before
|
|
144
|
+
// the store does).
|
|
79
145
|
const caller = options.observer;
|
|
80
|
-
|
|
146
|
+
// TWO decisions, not one. The hub is in-process bookkeeping over the run observer: a serve gets it unconditionally,
|
|
147
|
+
// because `/stop` in a chat is an ordinary thing to ask for and reaching the live run is the only way to answer it.
|
|
148
|
+
// Publishing `/control/*` — steer, rewrite, delete, unauthenticated at whatever URL this serves on — is the
|
|
149
|
+
// separate decision `config.sessionControl` makes, and it is the only one that also wires the boundary.
|
|
150
|
+
const publish = options.sessionControl ?? config.sessionControl === true;
|
|
151
|
+
const wantControl = publish || options.serving === true;
|
|
81
152
|
let hub;
|
|
82
153
|
if (wantControl) {
|
|
83
|
-
// The hub's surface is synchronous (`capabilities()` lists the allowed models), while building
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const { modelRuntime, model } = await assembly.engine();
|
|
88
|
-
hub = createPiSessionControl({
|
|
89
|
-
sessions,
|
|
90
|
-
boundary: {
|
|
154
|
+
// The hub's surface is synchronous (`capabilities()` lists the allowed models), while building the registry reads
|
|
155
|
+
// credentials and is not. Resolved only when the boundary is wired, so an ordinary serve does not pay for it.
|
|
156
|
+
const boundary = publish
|
|
157
|
+
? await assembly.engine().then(({ modelRuntime, model }) => ({
|
|
91
158
|
lease: assembly.lease,
|
|
92
159
|
models: modelRuntime,
|
|
93
160
|
sessionFactory: assembly.sessionFactory,
|
|
94
161
|
defaults: { model, thinkingLevel: assembly.thinkingLevel },
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
description: skill.description,
|
|
110
|
-
source: "skill",
|
|
111
|
-
}));
|
|
112
|
-
},
|
|
113
|
-
// The caller tap's boundary-event half: state_changed/compaction_* originate in the hub
|
|
114
|
-
// and never cross the data plane's observer seam — without this, an audit tap wired here
|
|
115
|
-
// would miss exactly the mutations it most needs to see (`update({ model })`).
|
|
162
|
+
}))
|
|
163
|
+
: undefined;
|
|
164
|
+
hub = createPiSessionControl({
|
|
165
|
+
sessions,
|
|
166
|
+
boundary,
|
|
167
|
+
// What a client offers is what a turn WOULD run, which since this agent inherits its machine is not the
|
|
168
|
+
// definition alone. Built through the same resource posture the turn uses, so the menu cannot list a name
|
|
169
|
+
// the prompt would not expand — a second reading of "which skills exist" is how those two come to disagree.
|
|
170
|
+
commands: () => agentCommands(agentDir, workspace, {
|
|
171
|
+
extensionPaths: assembly.extensionPaths,
|
|
172
|
+
modelRuntime: async () => (await assembly.engine()).modelRuntime,
|
|
173
|
+
}),
|
|
174
|
+
// The caller tap's boundary-event half: state_changed/compaction_* originate in the hub and never cross the
|
|
175
|
+
// data plane's observer seam.
|
|
116
176
|
tap: caller ? (session, event) => caller(session, event) : undefined,
|
|
117
177
|
});
|
|
118
178
|
}
|
|
@@ -130,7 +190,8 @@ export async function createPiAgentFromDir(dir, options = {}) {
|
|
|
130
190
|
definition,
|
|
131
191
|
sessions,
|
|
132
192
|
sessionControl: hub?.control,
|
|
133
|
-
|
|
193
|
+
publishControl: publish,
|
|
194
|
+
http: config.http,
|
|
134
195
|
agentDir,
|
|
135
196
|
workspace,
|
|
136
197
|
config,
|
|
@@ -138,10 +199,9 @@ export async function createPiAgentFromDir(dir, options = {}) {
|
|
|
138
199
|
modelSpec,
|
|
139
200
|
stateRoot,
|
|
140
201
|
sessionsDir,
|
|
141
|
-
|
|
202
|
+
auth,
|
|
142
203
|
toolNames,
|
|
143
204
|
deferredToolNames,
|
|
144
205
|
toolCollisions,
|
|
145
|
-
toolFailures,
|
|
146
206
|
};
|
|
147
207
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Render assembly warnings to stderr — the one place both the CLI runners and the `chat` runtime show
|
|
3
|
-
*
|
|
2
|
+
* Render assembly warnings to stderr — the one place both the CLI runners and the `chat` runtime show the non-fatal
|
|
3
|
+
* definition/tool findings the loaders return as data.
|
|
4
4
|
*/
|
|
5
5
|
import type { SkillDiagnostic } from "@earendil-works/pi-agent-core";
|
|
6
6
|
import type { SkillCollision } from "./definition.ts";
|
|
@@ -9,16 +9,7 @@ type Findings = {
|
|
|
9
9
|
collisions: SkillCollision[];
|
|
10
10
|
diagnostics: SkillDiagnostic[];
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
13
|
-
* THE door for definition findings: warns only when this dir's set CHANGED since the last report.
|
|
14
|
-
* Every reader calls it — boot, the per-turn live read, the control plane's command list — so a
|
|
15
|
-
* finding is announced when it appears and never repeated. There is deliberately no "record without
|
|
16
|
-
* printing" variant: a memo entry that trusts some other caller to have printed would silently
|
|
17
|
-
* swallow findings for a caller that does not.
|
|
18
|
-
*
|
|
19
|
-
* `dir` must be the RESOLVED definition root (`LoadedDefinition.dir`), or two spellings of one path
|
|
20
|
-
* become two memos and warn twice.
|
|
21
|
-
*/
|
|
12
|
+
/** THE door for definition findings: warns only when this dir's set CHANGED since the last report. */
|
|
22
13
|
export declare function reportFindingsIfChanged(dir: string, def: Findings): void;
|
|
23
14
|
export declare function reportDefinitionWarnings(collisions: SkillCollision[], diagnostics: SkillDiagnostic[]): void;
|
|
24
15
|
export declare function reportToolCollisions(collisions: ToolCollision[]): void;
|
|
@@ -5,23 +5,9 @@ function findingsSignature(def) {
|
|
|
5
5
|
const diagnostics = def.diagnostics.map((d) => `d:${d.code}:${d.path}`);
|
|
6
6
|
return [...collisions, ...diagnostics].sort().join("\n");
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* The last reported finding set PER DEFINITION DIR. One memo for every reader of a definition (the
|
|
10
|
-
* per-turn live read, the control plane's command list), because the thing being deduped is the
|
|
11
|
-
* FINDING, not the reader: a newly broken skill deserves one warning, not one per code path that
|
|
12
|
-
* noticed it, and a static one deserves none at all.
|
|
13
|
-
*/
|
|
8
|
+
/** The last reported finding set PER DEFINITION DIR. */
|
|
14
9
|
const lastFindings = new Map();
|
|
15
|
-
/**
|
|
16
|
-
* THE door for definition findings: warns only when this dir's set CHANGED since the last report.
|
|
17
|
-
* Every reader calls it — boot, the per-turn live read, the control plane's command list — so a
|
|
18
|
-
* finding is announced when it appears and never repeated. There is deliberately no "record without
|
|
19
|
-
* printing" variant: a memo entry that trusts some other caller to have printed would silently
|
|
20
|
-
* swallow findings for a caller that does not.
|
|
21
|
-
*
|
|
22
|
-
* `dir` must be the RESOLVED definition root (`LoadedDefinition.dir`), or two spellings of one path
|
|
23
|
-
* become two memos and warn twice.
|
|
24
|
-
*/
|
|
10
|
+
/** THE door for definition findings: warns only when this dir's set CHANGED since the last report. */
|
|
25
11
|
export function reportFindingsIfChanged(dir, def) {
|
|
26
12
|
const sig = findingsSignature(def);
|
|
27
13
|
if (lastFindings.get(dir) === sig)
|
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { type MountedTool } from "./tool.ts";
|
|
2
|
-
/** Mount the built-in loader iff any mounted tool is deferred and the author didn't define their own.
|
|
3
|
-
* Typed on the MOUNTED tool, not the authored one: it inspects names and deferral and never executes,
|
|
4
|
-
* so narrowing here would reject the very set it is handed (pi's defaults take the turn's context). */
|
|
2
|
+
/** Mount the built-in loader iff any mounted tool is deferred and the author didn't define their own. */
|
|
5
3
|
export declare function withSearchTool(tools: MountedTool[]): MountedTool[];
|
|
6
|
-
/** Build the `search_tools` loader. Keyword search over the inactive tools' name+description.
|
|
7
|
-
*
|
|
8
|
-
* `executionMode: "sequential"` — pi turns any batch containing a sequential tool serial. Required for
|
|
9
|
-
* correct load-point attribution everywhere an OUTER active-set diff exists: pi wraps SDK customTools
|
|
10
|
-
* (the chat path) in a before/after diff, and two parallel loader calls would both snapshot the
|
|
11
|
-
* pre-activation set and get stamped with the same activation. Custom loader authors must set it too. */
|
|
12
4
|
export declare function makeSearchToolsTool(): MountedTool;
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The built-in `search_tools` loader — the discovery surface for deferred tools (defineTool
|
|
3
|
-
* `deferred: true`). A deferred tool's schema is not in the request and the model cannot see it; this
|
|
4
|
-
* loader is how it finds and activates one. Mounted automatically (withSearchTool) only when a
|
|
5
|
-
* deferred tool exists; a workspace tool named `search_tools` wins — the author owns the concept then
|
|
6
|
-
* (same rule as the wake pair).
|
|
7
|
-
*/
|
|
1
|
+
/** The built-in `search_tools` loader — the discovery surface for deferred tools (defineTool `deferred: true`). */
|
|
8
2
|
import { z } from "zod";
|
|
9
3
|
import { log } from "../../log.js";
|
|
10
4
|
import { defineTool, isDeferredTool, stripDeferredMarker } from "./tool.js";
|
|
11
|
-
/** Mount the built-in loader iff any mounted tool is deferred and the author didn't define their own.
|
|
12
|
-
* Typed on the MOUNTED tool, not the authored one: it inspects names and deferral and never executes,
|
|
13
|
-
* so narrowing here would reject the very set it is handed (pi's defaults take the turn's context). */
|
|
5
|
+
/** Mount the built-in loader iff any mounted tool is deferred and the author didn't define their own. */
|
|
14
6
|
export function withSearchTool(tools) {
|
|
15
7
|
if (!tools.some(isDeferredTool))
|
|
16
8
|
return tools;
|
|
@@ -19,41 +11,23 @@ export function withSearchTool(tools) {
|
|
|
19
11
|
return [...tools, makeSearchToolsTool()];
|
|
20
12
|
let fixed = authored;
|
|
21
13
|
if (isDeferredTool(fixed)) {
|
|
22
|
-
// A deferred LOADER is a contradiction — it is the only entry point to the deferred tools, so
|
|
23
|
-
//
|
|
24
|
-
// unreachable. Ignore the marker and keep the loader active (fail visibly, keep the capability).
|
|
14
|
+
// A deferred LOADER is a contradiction — it is the only entry point to the deferred tools, so nothing could ever
|
|
15
|
+
// activate it (or, through it, them).
|
|
25
16
|
log.warn("[fastagent] search_tools is marked deferred — ignoring the marker: the loader must stay active, or no deferred tool could ever be activated");
|
|
26
17
|
fixed = stripDeferredMarker(fixed);
|
|
27
18
|
}
|
|
28
|
-
if (fixed.executionMode !== "sequential") {
|
|
29
|
-
// A non-sequential loader silently revives the parallel double-attribution (pi's diff around SDK
|
|
30
|
-
// tools in chat) — enforce the mode rather than hope the author read the docs; warn so they know.
|
|
31
|
-
log.warn('[fastagent] search_tools lacks executionMode: "sequential" — forcing it: parallel loader calls would misattribute activations');
|
|
32
|
-
fixed = { ...fixed, executionMode: "sequential" };
|
|
33
|
-
}
|
|
34
19
|
return fixed === authored ? tools : tools.map((t) => (t === authored ? fixed : t));
|
|
35
20
|
}
|
|
36
|
-
/** Activation cap per search: activation is additive, session-persisted, and has NO deactivate path
|
|
37
|
-
* without a cap, one broad token ("get", "file") would permanently activate half the catalog and
|
|
38
|
-
* silently spend the entire deferral benefit for the rest of the conversation. Over the cap nothing
|
|
39
|
-
* activates; the model gets the candidates and narrows the query. */
|
|
21
|
+
/** Activation cap per search: activation is additive, session-persisted, and has NO deactivate path. */
|
|
40
22
|
const MAX_ACTIVATIONS_PER_SEARCH = 5;
|
|
41
|
-
/** Miss-path listing cap — same rationale as the activation cap
|
|
42
|
-
* catalog (the thing deferral keeps OUT of the context) back in as a tool result. */
|
|
23
|
+
/** Miss-path listing cap — same rationale as the activation cap. */
|
|
43
24
|
const MAX_MISS_LISTING = 10;
|
|
44
|
-
/** Build the `search_tools` loader. Keyword search over the inactive tools' name+description.
|
|
45
|
-
*
|
|
46
|
-
* `executionMode: "sequential"` — pi turns any batch containing a sequential tool serial. Required for
|
|
47
|
-
* correct load-point attribution everywhere an OUTER active-set diff exists: pi wraps SDK customTools
|
|
48
|
-
* (the chat path) in a before/after diff, and two parallel loader calls would both snapshot the
|
|
49
|
-
* pre-activation set and get stamped with the same activation. Custom loader authors must set it too. */
|
|
50
25
|
export function makeSearchToolsTool() {
|
|
51
26
|
return defineTool({
|
|
52
27
|
name: "search_tools",
|
|
53
|
-
executionMode: "sequential",
|
|
54
28
|
description:
|
|
55
|
-
// First line short on purpose: the base prompt's tools list truncates at the first newline, and
|
|
56
|
-
//
|
|
29
|
+
// First line short on purpose: the base prompt's tools list truncates at the first newline, and the discovery
|
|
30
|
+
// guidance below would otherwise flood it (and duplicate its deferred note).
|
|
57
31
|
"Discover and activate additional tools.\n" +
|
|
58
32
|
"Part of this agent's toolset is inactive until needed: search by keywords (e.g. what you are " +
|
|
59
33
|
"trying to do), and matching inactive tools are activated and become callable from that point on " +
|
|
@@ -65,19 +39,18 @@ export function makeSearchToolsTool() {
|
|
|
65
39
|
async execute(input, ctx) {
|
|
66
40
|
if (!ctx.tools)
|
|
67
41
|
return "tool activation is unavailable outside a conversation turn.";
|
|
68
|
-
// Search the WHOLE registered catalog: the loader is the only discovery surface, and in a long
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// relevance and irreversibility; swap in scoring/embeddings if catalogs outgrow this.
|
|
42
|
+
// Search the WHOLE registered catalog: the loader is the only discovery surface, and in a long conversation the
|
|
43
|
+
// model does not remember what it activated.
|
|
44
|
+
// ponytail: naive keyword match (any query token as a case-insensitive substring of name+description) under a
|
|
45
|
+
// hard per-search activation cap — the two ceilings are relevance and irreversibility; swap in
|
|
46
|
+
// scoring/embeddings if catalogs outgrow this.
|
|
74
47
|
const tokens = input.query
|
|
75
48
|
.toLowerCase()
|
|
76
49
|
.split(/[^a-z0-9]+/)
|
|
77
50
|
.filter(Boolean);
|
|
78
|
-
// AND semantics: EVERY token must hit — "adding a word narrows" must actually hold, or the
|
|
79
|
-
//
|
|
80
|
-
//
|
|
51
|
+
// AND semantics: EVERY token must hit — "adding a word narrows" must actually hold, or the over-cap "narrow the
|
|
52
|
+
// query" instruction sends the model in circles (OR would widen with each word, and a shared prefix like
|
|
53
|
+
// "fetch" could make a whole tool family permanently over-cap).
|
|
81
54
|
const matchesQuery = (t) => {
|
|
82
55
|
const haystack = `${t.name} ${t.description}`.toLowerCase();
|
|
83
56
|
return tokens.every((token) => haystack.includes(token));
|
|
@@ -85,19 +58,16 @@ export function makeSearchToolsTool() {
|
|
|
85
58
|
const describe = (t) => `${t.name} — ${t.description.split("\n")[0]}`;
|
|
86
59
|
const active = new Set(ctx.tools.active());
|
|
87
60
|
const registered = ctx.tools.registered();
|
|
88
|
-
// A query with no searchable tokens (all punctuation/symbols) must not match
|
|
89
|
-
// empty token list is vacuously true, and a noise query would otherwise activate the catalog.
|
|
61
|
+
// A query with no searchable tokens (all punctuation/symbols) must not match.
|
|
90
62
|
if (tokens.length === 0)
|
|
91
63
|
return `"${input.query}" contains no searchable keywords — describe the capability you need.`;
|
|
92
|
-
// Exact-name shortcut: the guaranteed escape hatch from the cap — a query that IS an INACTIVE
|
|
93
|
-
//
|
|
94
|
-
// on an ACTIVE tool falls through to keyword matching: it must not swallow the discovery of
|
|
95
|
-
// other, still-inactive keyword matches.
|
|
64
|
+
// Exact-name shortcut: the guaranteed escape hatch from the cap — a query that IS an INACTIVE registered tool's
|
|
65
|
+
// name addresses that one tool, no keyword scoring in the way.
|
|
96
66
|
const exact = registered.find((t) => t.name.toLowerCase() === input.query.trim().toLowerCase());
|
|
97
67
|
const activeMatches = registered.filter((t) => active.has(t.name) && (t === exact || matchesQuery(t)));
|
|
98
68
|
const inactiveMatches = exact && !active.has(exact.name) ? [exact] : registered.filter((t) => !active.has(t.name) && matchesQuery(t));
|
|
99
|
-
// Same listing cap as every other branch — a wide token can match most of the ACTIVE set too
|
|
100
|
-
//
|
|
69
|
+
// Same listing cap as every other branch — a wide token can match most of the ACTIVE set too (in chat that
|
|
70
|
+
// includes pi's default tools), and no answer may pour a catalog into the context.
|
|
101
71
|
const listedActive = activeMatches.slice(0, MAX_MISS_LISTING);
|
|
102
72
|
const moreActive = activeMatches.length - listedActive.length;
|
|
103
73
|
const activeNote = activeMatches.length > 0
|
|
@@ -109,15 +79,14 @@ export function makeSearchToolsTool() {
|
|
|
109
79
|
const inactive = registered.filter((t) => !active.has(t.name));
|
|
110
80
|
if (inactive.length === 0)
|
|
111
81
|
return "All tools are already active — nothing to discover.";
|
|
112
|
-
// Cap the miss listing like the activation cap — both guard the same semantic (don't pour the
|
|
113
|
-
//
|
|
82
|
+
// Cap the miss listing like the activation cap — both guard the same semantic (don't pour the catalog back
|
|
83
|
+
// into the context the deferral exists to protect).
|
|
114
84
|
const listed = inactive.slice(0, MAX_MISS_LISTING);
|
|
115
85
|
const more = inactive.length - listed.length;
|
|
116
86
|
return `No tools matched "${input.query}". Inactive tools: ${listed.map(describe).join("; ")}${more > 0 ? ` … and ${more} more — search with different keywords.` : ""}`;
|
|
117
87
|
}
|
|
118
88
|
if (inactiveMatches.length > MAX_ACTIVATIONS_PER_SEARCH) {
|
|
119
|
-
// Same listing cap as the miss path — an over-cap answer must not pour the catalog into the
|
|
120
|
-
// context either. Names alone suffice: the exact-name escape only needs a name to query.
|
|
89
|
+
// Same listing cap as the miss path — an over-cap answer must not pour the catalog into the context either.
|
|
121
90
|
const listed = inactiveMatches.slice(0, MAX_MISS_LISTING);
|
|
122
91
|
const more = inactiveMatches.length - listed.length;
|
|
123
92
|
return `${inactiveMatches.length} inactive tools matched "${input.query}" — too many to activate at once (activation is permanent for this conversation). Narrow the query (or query an exact name). Matches: ${listed
|
|
@@ -125,8 +94,8 @@ export function makeSearchToolsTool() {
|
|
|
125
94
|
.join(", ")}${more > 0 ? ` … and ${more} more` : ""}.${activeNote ? ` ${activeNote}` : ""}`;
|
|
126
95
|
}
|
|
127
96
|
const activated = ctx.tools.activate(inactiveMatches.map((t) => t.name));
|
|
128
|
-
// Report what actually happened, not what was attempted: a parallel sibling call may have
|
|
129
|
-
//
|
|
97
|
+
// Report what actually happened, not what was attempted: a parallel sibling call may have activated the same
|
|
98
|
+
// matches first, leaving nothing new here.
|
|
130
99
|
if (activated.length === 0) {
|
|
131
100
|
return `Matched ${inactiveMatches.map((t) => t.name).join(", ")} — already active (possibly activated by a concurrent call). Call them directly.${activeNote ? ` ${activeNote}` : ""}`;
|
|
132
101
|
}
|
|
@@ -1,23 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `createAgentService` — the product as one call, with pi supplying the engine.
|
|
3
|
-
*
|
|
4
|
-
* The ASSEMBLY is engine-neutral and lives in `src/service.ts`: it takes a {@link MountableAgent}
|
|
5
|
-
* (the SPEC contract plus three paths) and knows nothing about how that agent was built. What is
|
|
6
|
-
* pi-specific is opening a DIRECTORY into one, which is why this shortcut lives here and the
|
|
7
|
-
* assembly does not. A second engine ships its own opener and reuses `mountAgentService` unchanged.
|
|
8
|
-
*/
|
|
1
|
+
/** `createAgentService` — the product as one call, with pi supplying the engine. */
|
|
9
2
|
import { type AgentService, type MountAgentServiceOptions } from "../../service.ts";
|
|
10
3
|
export interface CreateAgentServiceOptions extends MountAgentServiceOptions {
|
|
11
4
|
model?: string;
|
|
12
5
|
authPath?: string;
|
|
13
6
|
sessionsDir?: string;
|
|
14
7
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Open an agent directory as a live service: one handler, mounted wherever you serve.
|
|
17
|
-
*
|
|
18
|
-
* ```ts
|
|
19
|
-
* const service = await createAgentService("./my-agent");
|
|
20
|
-
* app.use("/agent", nodeListener(service.handler));
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
8
|
+
/** Open an agent directory as a live service: one handler, mounted wherever you serve. */
|
|
23
9
|
export declare function createAgentService(dir: string, options?: CreateAgentServiceOptions): Promise<AgentService>;
|
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `createAgentService` — the product as one call, with pi supplying the engine.
|
|
3
|
-
*
|
|
4
|
-
* The ASSEMBLY is engine-neutral and lives in `src/service.ts`: it takes a {@link MountableAgent}
|
|
5
|
-
* (the SPEC contract plus three paths) and knows nothing about how that agent was built. What is
|
|
6
|
-
* pi-specific is opening a DIRECTORY into one, which is why this shortcut lives here and the
|
|
7
|
-
* assembly does not. A second engine ships its own opener and reuses `mountAgentService` unchanged.
|
|
8
|
-
*/
|
|
1
|
+
/** `createAgentService` — the product as one call, with pi supplying the engine. */
|
|
9
2
|
import { mountAgentService } from "../../service.js";
|
|
10
3
|
import { createPiAgentFromDir } from "./open.js";
|
|
11
|
-
/**
|
|
12
|
-
* Open an agent directory as a live service: one handler, mounted wherever you serve.
|
|
13
|
-
*
|
|
14
|
-
* ```ts
|
|
15
|
-
* const service = await createAgentService("./my-agent");
|
|
16
|
-
* app.use("/agent", nodeListener(service.handler));
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
4
|
+
/** Open an agent directory as a live service: one handler, mounted wherever you serve. */
|
|
19
5
|
export async function createAgentService(dir, options = {}) {
|
|
20
6
|
const opened = await createPiAgentFromDir(dir, {
|
|
21
7
|
...(options.model !== undefined ? { model: options.model } : {}),
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { type AgentSessionRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
export interface BuildSessionRuntimeOptions {
|
|
3
|
-
/** Model spec override (the CLI --model flag).
|
|
3
|
+
/** Model spec override (the CLI --model flag). */
|
|
4
4
|
model?: string;
|
|
5
|
-
/** Credentials file override (the
|
|
6
|
-
* the agent's default `<agentDir>/.secrets/auth.json`. */
|
|
5
|
+
/** Credentials file override (the SDK `authPath` option; the CLI has only `FASTAGENT_AUTH_PATH`). */
|
|
7
6
|
authPath?: string;
|
|
8
7
|
}
|
|
9
8
|
/**
|
|
10
|
-
* Build pi's interactive runtime driven by fastagent's assembled agent (model, prompt, tools,
|
|
11
|
-
*
|
|
12
|
-
* assembly — the fidelity-critical part — is inspectable and reusable without launching a TUI.
|
|
9
|
+
* Build pi's interactive runtime driven by fastagent's assembled agent (model, prompt, tools, skills, and auth
|
|
10
|
+
* resolved exactly as the serving opener does).
|
|
13
11
|
*/
|
|
14
|
-
export declare function buildAgentSessionRuntime(dir: string, options?: BuildSessionRuntimeOptions,
|
|
15
|
-
/** Session backend. Defaults to pi's project-scoped store; tests inject SessionManager.inMemory(). */
|
|
16
|
-
sessionManager?: SessionManager): Promise<AgentSessionRuntime>;
|
|
12
|
+
export declare function buildAgentSessionRuntime(dir: string, options?: BuildSessionRuntimeOptions, sessionManager?: SessionManager): Promise<AgentSessionRuntime>;
|