@fastagent-sh/fastagent 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -7
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/atomic-write.d.ts +23 -0
- package/dist/atomic-write.js +53 -0
- package/dist/{host/node.d.ts → channel.d.ts} +24 -28
- package/dist/channel.js +1 -0
- package/dist/channels/agentcore-limits.d.ts +9 -0
- package/dist/channels/agentcore-limits.js +9 -0
- package/dist/channels/agentcore-service.d.ts +55 -0
- package/dist/channels/agentcore-service.js +107 -0
- package/dist/channels/agentcore.d.ts +22 -20
- package/dist/channels/agentcore.js +20 -11
- package/dist/channels/busy.d.ts +1 -1
- package/dist/channels/busy.js +1 -1
- package/dist/channels/control.d.ts +55 -7
- package/dist/channels/control.js +312 -67
- package/dist/channels/discover.d.ts +59 -0
- package/dist/{engines/pi/channel.js → channels/discover.js} +19 -34
- package/dist/channels/feishu/card.js +1 -1
- package/dist/channels/feishu/context-buffer.d.ts +1 -1
- package/dist/channels/feishu/context-buffer.js +4 -4
- package/dist/channels/feishu/feishu-api.js +6 -9
- package/dist/channels/feishu/feishu.d.ts +1 -1
- package/dist/channels/feishu/feishu.js +41 -24
- package/dist/channels/feishu/invoke-turn.d.ts +1 -1
- package/dist/channels/feishu/invoke-turn.js +3 -4
- package/dist/channels/feishu/preview.d.ts +1 -1
- package/dist/channels/feishu/preview.js +7 -3
- package/dist/channels/feishu/ws-ingress.d.ts +1 -1
- package/dist/channels/github/github.d.ts +1 -1
- package/dist/channels/http.d.ts +11 -7
- package/dist/channels/http.js +1 -109
- package/dist/channels/kit/attachment-path.d.ts +12 -0
- package/dist/channels/kit/attachment-path.js +43 -0
- package/dist/channels/{context-buffer.d.ts → kit/context-buffer.d.ts} +9 -0
- package/dist/channels/{context-buffer.js → kit/context-buffer.js} +13 -2
- package/dist/channels/{invoke-turn-kit.d.ts → kit/invoke-turn-kit.d.ts} +14 -3
- package/dist/channels/{invoke-turn-kit.js → kit/invoke-turn-kit.js} +17 -6
- package/dist/channels/{preview-kit.d.ts → kit/preview-kit.d.ts} +3 -1
- package/dist/channels/{seen.js → kit/seen.js} +1 -1
- package/dist/channels/kit/signature.d.ts +12 -0
- package/dist/channels/kit/signature.js +17 -0
- package/dist/channels/{state.js → kit/state.js} +4 -7
- package/dist/channels/{stop-command.d.ts → kit/stop-command.d.ts} +1 -1
- package/dist/channels/{stop-command.js → kit/stop-command.js} +5 -5
- package/dist/channels/{tasks.d.ts → kit/tasks.d.ts} +1 -1
- package/dist/channels/{tasks.js → kit/tasks.js} +11 -5
- package/dist/channels/{thread-participants.js → kit/thread-participants.js} +1 -1
- package/dist/channels/{turn-queue.js → kit/turn-queue.js} +2 -2
- package/dist/channels/{turn-store.d.ts → kit/turn-store.d.ts} +19 -3
- package/dist/channels/{turn-store.js → kit/turn-store.js} +30 -3
- package/dist/channels/lark/lark.d.ts +1 -1
- package/dist/channels/serve.d.ts +73 -0
- package/dist/channels/serve.js +243 -0
- package/dist/channels/slack/bot-auth.js +3 -14
- package/dist/channels/slack/context-buffer.d.ts +2 -2
- package/dist/channels/slack/context-buffer.js +2 -2
- package/dist/channels/slack/invoke-turn.d.ts +2 -2
- package/dist/channels/slack/invoke-turn.js +13 -6
- package/dist/channels/slack/onboard.js +5 -5
- package/dist/channels/slack/onboarding-state.d.ts +7 -3
- package/dist/channels/slack/onboarding-state.js +13 -20
- package/dist/channels/slack/parse.js +2 -2
- package/dist/channels/slack/preview.d.ts +1 -1
- package/dist/channels/slack/preview.js +11 -3
- package/dist/channels/slack/register-webhook.js +1 -1
- package/dist/channels/slack/scaffold/slack-send.ts +2 -17
- package/dist/channels/slack/slack-api.js +23 -55
- package/dist/channels/slack/slack.d.ts +1 -1
- package/dist/channels/slack/slack.js +20 -20
- package/dist/channels/slack/welcomed.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +2 -2
- package/dist/channels/telegram/context-buffer.js +2 -2
- package/dist/channels/telegram/invoke-turn.d.ts +1 -1
- package/dist/channels/telegram/invoke-turn.js +1 -1
- package/dist/channels/telegram/parse.d.ts +21 -1
- package/dist/channels/telegram/parse.js +67 -13
- package/dist/channels/telegram/preview.d.ts +1 -1
- package/dist/channels/telegram/preview.js +6 -2
- package/dist/channels/telegram/scaffold/channel.ts +7 -3
- package/dist/channels/telegram/telegram-api.js +5 -6
- package/dist/channels/telegram/telegram.d.ts +3 -3
- package/dist/channels/telegram/telegram.js +60 -72
- package/dist/channels/telegram/turn-store.d.ts +2 -2
- package/dist/channels/telegram/turn-store.js +2 -2
- package/dist/cli/add-slack.js +6 -5
- package/dist/cli/commands/add.js +1 -5
- package/dist/cli/commands/attach.d.ts +2 -4
- package/dist/cli/commands/attach.js +17 -15
- package/dist/cli/commands/deploy.d.ts +50 -5
- package/dist/cli/commands/deploy.js +183 -134
- package/dist/cli/commands/dev.js +25 -40
- package/dist/cli/commands/fire.js +1 -1
- package/dist/cli/commands/info.js +7 -5
- package/dist/cli/commands/init.js +1 -2
- package/dist/cli/commands/schedule.js +1 -1
- package/dist/cli/commands/start.js +80 -121
- package/dist/cli/commands/tool.js +5 -7
- package/dist/cli/program.js +1 -1
- package/dist/cli/serve.d.ts +22 -70
- package/dist/cli/serve.js +66 -257
- package/dist/cli/shared.d.ts +40 -10
- package/dist/cli/shared.js +53 -6
- package/dist/core.d.ts +3 -5
- package/dist/core.js +7 -5
- package/dist/deploy/agentcore/logs.d.ts +2 -2
- package/dist/deploy/agentcore/logs.js +2 -2
- package/dist/deploy/agentcore/plan.d.ts +12 -10
- package/dist/deploy/agentcore/plan.js +39 -31
- package/dist/deploy/agentcore/run.d.ts +5 -4
- package/dist/deploy/agentcore/run.js +13 -36
- package/dist/deploy/channel-ingress.d.ts +73 -0
- package/dist/deploy/channel-ingress.js +101 -0
- package/dist/deploy/docker/plan.d.ts +3 -7
- package/dist/deploy/docker/plan.js +4 -15
- package/dist/deploy/docker/run.d.ts +18 -1
- package/dist/deploy/docker/run.js +10 -0
- package/dist/deploy/fly/plan.d.ts +4 -5
- package/dist/deploy/fly/plan.js +7 -22
- package/dist/deploy/fly/run.d.ts +9 -23
- package/dist/deploy/fly/run.js +32 -68
- package/dist/deploy/preflight.d.ts +6 -7
- package/dist/deploy/preflight.js +27 -23
- package/dist/deploy/railway/plan.d.ts +4 -5
- package/dist/deploy/railway/plan.js +11 -26
- package/dist/deploy/railway/run.d.ts +9 -9
- package/dist/deploy/railway/run.js +40 -40
- package/dist/deploy/runner.js +22 -2
- package/dist/deploy/secrets.d.ts +18 -9
- package/dist/deploy/secrets.js +64 -12
- package/dist/dev-supervisor.js +11 -6
- package/dist/engines/pi/agent-session-factory.d.ts +104 -0
- package/dist/engines/pi/agent-session-factory.js +295 -0
- package/dist/engines/pi/auth.js +43 -43
- package/dist/engines/pi/config.d.ts +5 -5
- package/dist/engines/pi/config.js +1 -1
- package/dist/engines/pi/create.d.ts +67 -42
- package/dist/engines/pi/create.js +142 -72
- package/dist/engines/pi/definition.d.ts +42 -8
- package/dist/engines/pi/definition.js +114 -11
- package/dist/engines/pi/invoke-session.d.ts +22 -19
- package/dist/engines/pi/invoke-session.js +286 -100
- package/dist/engines/pi/login.js +39 -20
- package/dist/engines/pi/models.d.ts +14 -0
- package/dist/engines/pi/models.js +9 -1
- package/dist/engines/pi/open.d.ts +10 -8
- package/dist/engines/pi/open.js +21 -11
- package/dist/engines/pi/report.d.ts +0 -4
- package/dist/engines/pi/report.js +0 -7
- package/dist/engines/pi/search-tools.js +1 -1
- package/dist/engines/pi/service.d.ts +23 -0
- package/dist/engines/pi/service.js +27 -0
- package/dist/engines/pi/session-builder.js +87 -118
- package/dist/engines/pi/session-control.d.ts +28 -14
- package/dist/engines/pi/session-control.js +570 -438
- package/dist/engines/pi/session-inheritance.d.ts +30 -0
- package/dist/engines/pi/session-inheritance.js +261 -0
- package/dist/engines/pi/session-markers.d.ts +48 -0
- package/dist/engines/pi/session-markers.js +59 -0
- package/dist/engines/pi/session-settings.d.ts +15 -3
- package/dist/engines/pi/session-settings.js +22 -1
- package/dist/engines/pi/session-store.d.ts +140 -0
- package/dist/engines/pi/session-store.js +666 -0
- package/dist/engines/pi/tool-context.d.ts +36 -13
- package/dist/engines/pi/tool-context.js +51 -4
- package/dist/engines/pi/tool.d.ts +5 -5
- package/dist/engines/pi/tool.js +3 -3
- package/dist/engines/pi/turn-kit.d.ts +32 -6
- package/dist/engines/pi/turn-kit.js +28 -1
- package/dist/engines/pi/wake-tool.js +1 -1
- package/dist/env.js +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/loader.d.ts +49 -7
- package/dist/loader.js +79 -18
- package/dist/log.d.ts +3 -0
- package/dist/log.js +5 -1
- package/dist/node.d.ts +16 -0
- package/dist/node.js +20 -0
- package/dist/paths.d.ts +26 -0
- package/dist/paths.js +37 -2
- package/dist/pi.d.ts +14 -8
- package/dist/pi.js +6 -5
- package/dist/scaffold/add-channel.js +2 -2
- package/dist/scaffold/init.js +7 -2
- package/dist/scaffold/templates/persona.md +2 -2
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -2
- package/dist/schedule/discover.js +3 -15
- package/dist/schedule/state.js +4 -9
- package/dist/schedule/wake-alarm.d.ts +13 -7
- package/dist/schedule/wake-alarm.js +88 -46
- package/dist/schedule/wakeups.d.ts +2 -2
- package/dist/schedule/wakeups.js +11 -8
- package/dist/service.d.ts +157 -0
- package/dist/service.js +376 -0
- package/dist/session-remote.d.ts +8 -3
- package/dist/session-remote.js +162 -92
- package/dist/session.d.ts +229 -93
- package/dist/session.js +61 -23
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +1 -1
- package/dist/tunnel.d.ts +18 -3
- package/dist/tunnel.js +32 -40
- package/package.json +14 -5
- package/dist/engines/pi/channel.d.ts +0 -37
- package/dist/engines/pi/harness.d.ts +0 -115
- package/dist/engines/pi/harness.js +0 -154
- package/dist/engines/pi/invoke.d.ts +0 -60
- package/dist/engines/pi/invoke.js +0 -459
- package/dist/engines/pi/read-image.d.ts +0 -4
- package/dist/engines/pi/read-image.js +0 -62
- package/dist/engines/pi/sessions.d.ts +0 -60
- package/dist/engines/pi/sessions.js +0 -464
- package/dist/host/node.js +0 -52
- /package/dist/channels/{preview-kit.js → kit/preview-kit.js} +0 -0
- /package/dist/channels/{seen.d.ts → kit/seen.d.ts} +0 -0
- /package/dist/channels/{state.d.ts → kit/state.d.ts} +0 -0
- /package/dist/channels/{text.d.ts → kit/text.d.ts} +0 -0
- /package/dist/channels/{text.js → kit/text.js} +0 -0
- /package/dist/channels/{thread-participants.d.ts → kit/thread-participants.d.ts} +0 -0
- /package/dist/channels/{turn-queue.d.ts → kit/turn-queue.d.ts} +0 -0
package/dist/tunnel.js
CHANGED
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
* Process orchestration, not assembly — lives outside the engine, beside dev-supervisor.ts.
|
|
8
8
|
*/
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
|
-
import { readdirSync } from "node:fs";
|
|
11
|
-
import { join } from "node:path";
|
|
12
10
|
import { registerFeishuWebhook } from "./channels/feishu/register-webhook.js";
|
|
13
11
|
import { registerSlackWebhook } from "./channels/slack/register-webhook.js";
|
|
14
12
|
import { registerTelegramWebhook } from "./channels/telegram/register-webhook.js";
|
|
13
|
+
import { pointChannelsAt } from "./deploy/channel-ingress.js";
|
|
15
14
|
import { dotEnvPath, loadDotEnv } from "./env.js";
|
|
16
15
|
import { resolveStateRoot } from "./paths.js";
|
|
17
16
|
import { log } from "./log.js";
|
|
@@ -106,23 +105,23 @@ function lastErrorLine(tail) {
|
|
|
106
105
|
.filter(Boolean);
|
|
107
106
|
return ([...lines].reverse().find((l) => /err|error|failed/i.test(l)) ?? lines.at(-1) ?? "").slice(0, 200);
|
|
108
107
|
}
|
|
109
|
-
/** Channel basenames present in `<dir>/channels/`. */
|
|
110
|
-
function channelBasenames(dir) {
|
|
111
|
-
try {
|
|
112
|
-
return readdirSync(join(dir, "channels"))
|
|
113
|
-
.filter((n) => /\.(ts|js|mjs)$/.test(n) && !n.endsWith(".d.ts"))
|
|
114
|
-
.map((n) => n.replace(/\.(ts|js|mjs)$/, ""));
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return [];
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
108
|
/**
|
|
121
109
|
* Print the public URL and wire up first-party webhook channels found under `dir` (the agent
|
|
122
110
|
* ROOT): Telegram and Feishu/Lark use runtime credentials; onboarded Slack uses its owner-local config
|
|
123
111
|
* token; GitHub and a manually scaffolded Slack app receive explicit console URLs.
|
|
112
|
+
*
|
|
113
|
+
* Returns what each registrar ANSWERED, because two kinds of caller need different things from a
|
|
114
|
+
* failure. `dev`/`start` are long-running: a webhook that did not register is a logged problem, not
|
|
115
|
+
* a reason to stop serving, and they void this. `deploy … --run` is a command that exits, and an
|
|
116
|
+
* exit 0 there tells its caller the deployment is reachable — so it feeds these through
|
|
117
|
+
* `registrationGate`, exactly as the fly/railway/agentcore runners feed their own registrar calls
|
|
118
|
+
* (docker used to be the one host that could not, because this returned nothing).
|
|
124
119
|
*/
|
|
125
|
-
export async function announceWebhooks(dir, baseUrl,
|
|
120
|
+
export async function announceWebhooks(dir, baseUrl,
|
|
121
|
+
/** Every declared channel with its ingress. Not a pre-filtered list: this used to accept "the route
|
|
122
|
+
* channels" and default to every basename in `channels/`, which pointed a webhook at a
|
|
123
|
+
* long-connection channel whenever a caller forgot to filter. {@link pointChannelsAt} filters. */
|
|
124
|
+
channels, opts = {}) {
|
|
126
125
|
log.info(`[fastagent] public URL: ${baseUrl}`);
|
|
127
126
|
try {
|
|
128
127
|
loadDotEnv(dir); // webhook registrars read channel credentials from .env
|
|
@@ -135,33 +134,26 @@ export async function announceWebhooks(dir, baseUrl, opts = {}) {
|
|
|
135
134
|
// missing-credential guidance. loadDotEnv keeps throwing for the synchronous command callers.
|
|
136
135
|
log.warn(`[fastagent] could not read ${dotEnvPath(dir)}: ${error.message} — continuing without it`);
|
|
137
136
|
}
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
// its origin connects, and the automatic registrars poll /health before configuring the platform (the
|
|
145
|
-
// same wait the deploy runners rely on). GitHub needs no wait — the operator adds that webhook by hand.
|
|
146
|
-
if (routeChannels.includes("telegram"))
|
|
147
|
-
await registerTelegramWebhook(baseUrl);
|
|
148
|
-
if (routeChannels.includes("github")) {
|
|
149
|
-
log.info(`[fastagent] github: add a webhook in your repo (Settings → Webhooks): Payload URL = ${baseUrl}/webhook, content type application/json, secret = GITHUB_WEBHOOK_SECRET`);
|
|
150
|
-
}
|
|
151
|
-
if (routeChannels.includes("slack")) {
|
|
152
|
-
await registerSlackWebhook(baseUrl, {
|
|
153
|
-
stateRoot: opts.stateRoot ?? resolveStateRoot(dir),
|
|
154
|
-
log: (message) => log.info(message),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
// feishu/lark register programmatically too (application-v7 config PATCH — telegram-setWebhook
|
|
158
|
-
// parity), once per mounted kind (each kind is its own app with its own credentials); the registrar
|
|
159
|
-
// owns its own health wait and degrades to the manual console instruction.
|
|
137
|
+
// Readiness is the registrar's job: a fresh quick tunnel returns Cloudflare 530 for ~20-30s before its
|
|
138
|
+
// origin connects, and the automatic registrars poll /health before configuring the platform (the same
|
|
139
|
+
// wait the deploy runners rely on). GitHub needs no wait — the operator adds that webhook by hand.
|
|
140
|
+
//
|
|
141
|
+
// The registrars differ from the deploy path's in what they carry, not in which channels they answer
|
|
142
|
+
// for: this one narrates to the log and opens the console for a manual Feishu step.
|
|
160
143
|
const feishuOptions = {
|
|
161
144
|
onManualRegistration: ({ consoleUrl }) => opts.openUrl?.(consoleUrl),
|
|
162
145
|
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
146
|
+
return pointChannelsAt({
|
|
147
|
+
baseUrl,
|
|
148
|
+
channels,
|
|
149
|
+
log: (message) => log.info(`[fastagent] ${message}`),
|
|
150
|
+
registrars: {
|
|
151
|
+
telegram: (url) => registerTelegramWebhook(url),
|
|
152
|
+
slack: (url) => registerSlackWebhook(url, {
|
|
153
|
+
stateRoot: opts.stateRoot ?? resolveStateRoot(dir),
|
|
154
|
+
log: (message) => log.info(message),
|
|
155
|
+
}),
|
|
156
|
+
feishu: (url, kind) => registerFeishuWebhook(url, kind, feishuOptions),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
167
159
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastagent-sh/fastagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Vibe first. Then FastAgent: turn a local agent directory into a live service in your app, on GitHub, Telegram, Slack, or behind any channel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
"types": "./dist/core.d.ts",
|
|
44
44
|
"default": "./dist/core.js"
|
|
45
45
|
},
|
|
46
|
+
"./node": {
|
|
47
|
+
"types": "./dist/node.d.ts",
|
|
48
|
+
"default": "./dist/node.js"
|
|
49
|
+
},
|
|
46
50
|
"./session": {
|
|
47
51
|
"types": "./dist/session.d.ts",
|
|
48
52
|
"default": "./dist/session.js"
|
|
@@ -101,9 +105,10 @@
|
|
|
101
105
|
},
|
|
102
106
|
"dependencies": {
|
|
103
107
|
"@clack/prompts": "^1.6.0",
|
|
104
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
105
|
-
"@earendil-works/pi-ai": "^0.
|
|
106
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
108
|
+
"@earendil-works/pi-agent-core": "^0.84.3",
|
|
109
|
+
"@earendil-works/pi-ai": "^0.84.3",
|
|
110
|
+
"@earendil-works/pi-coding-agent": "^0.84.3",
|
|
111
|
+
"@hono/node-server": "^2.1.1",
|
|
107
112
|
"@larksuiteoapi/node-sdk": "^1.71.1",
|
|
108
113
|
"@octokit/webhooks-methods": "^6.0.0",
|
|
109
114
|
"@octokit/webhooks-types": "^7.6.1",
|
|
@@ -118,9 +123,13 @@
|
|
|
118
123
|
},
|
|
119
124
|
"devDependencies": {
|
|
120
125
|
"@biomejs/biome": "^2.5.2",
|
|
126
|
+
"@types/express": "^5.0.6",
|
|
121
127
|
"@types/proper-lockfile": "^4.1.4",
|
|
128
|
+
"express": "^5.2.1",
|
|
129
|
+
"fastify": "^5.12.1",
|
|
122
130
|
"knip": "^6.29.0",
|
|
123
131
|
"typescript": "^7.0.2",
|
|
124
|
-
"vitest": "^4.1.9"
|
|
132
|
+
"vitest": "^4.1.9",
|
|
133
|
+
"yaml": "^2.9.0"
|
|
125
134
|
}
|
|
126
135
|
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { type ChannelContext, type LongConnection, type Routes } from "../../host/node.ts";
|
|
2
|
-
import { type ModuleLoadFailure } from "../../loader.ts";
|
|
3
|
-
/** A dropped route: two channels claim the same key. Surfaced, never silent. */
|
|
4
|
-
export interface ChannelCollision {
|
|
5
|
-
route: string;
|
|
6
|
-
source: string;
|
|
7
|
-
}
|
|
8
|
-
/** A long-connection module bound to the same context route factories receive. Internal serving shape. */
|
|
9
|
-
export interface LoadedLongConnectionChannel {
|
|
10
|
-
name: string;
|
|
11
|
-
connect(signal: AbortSignal): LongConnection;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Import channel files without mounting route factories or opening connections. Deployment needs only
|
|
15
|
-
* the authored structural fact: function exports are route channels; `{ connect() }` exports are
|
|
16
|
-
* long-connection channels. There is no second ingress/lifecycle declaration to keep in sync.
|
|
17
|
-
*/
|
|
18
|
-
export declare function inspectChannels(dir: string): Promise<{
|
|
19
|
-
channels: string[];
|
|
20
|
-
routeChannels: string[];
|
|
21
|
-
longConnectionChannels: string[];
|
|
22
|
-
failures: ModuleLoadFailure[];
|
|
23
|
-
}>;
|
|
24
|
-
/**
|
|
25
|
-
* Channel file basenames under `<dir>/channels/` — the authoring view (`fastagent info`), which lists
|
|
26
|
-
* WITHOUT importing. A symlinked channels directory must remain inside the agent dir.
|
|
27
|
-
*/
|
|
28
|
-
export declare function discoverChannelFiles(dir: string): Promise<string[]>;
|
|
29
|
-
/** Discover, validate, and bind all channel modules. No long connection is opened here; the CLI owns it. */
|
|
30
|
-
export declare function loadChannels(dir: string, ctx: ChannelContext): Promise<{
|
|
31
|
-
routes: Routes;
|
|
32
|
-
longConnections: LoadedLongConnectionChannel[];
|
|
33
|
-
routeChannels: string[];
|
|
34
|
-
longConnectionChannels: string[];
|
|
35
|
-
collisions: ChannelCollision[];
|
|
36
|
-
failures: ModuleLoadFailure[];
|
|
37
|
-
}>;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pi harness wiring: construct one pi `AgentHarness` per session. The agent definition (AGENTS.md +
|
|
3
|
-
* skills) is content fed INTO the harness (see definition.ts), not part of it.
|
|
4
|
-
*
|
|
5
|
-
* Under the stateless design the harness is discarded after each use; continuity comes from
|
|
6
|
-
* persisting the session (PiSessionStore) and re-opening it per invoke — pi's prompt() folds the
|
|
7
|
-
* historical entries back into context via buildContext().
|
|
8
|
-
*/
|
|
9
|
-
import { AgentHarness } from "@earendil-works/pi-agent-core";
|
|
10
|
-
import type { ExecutionEnv, ExecutionToolContext, Skill, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
11
|
-
import type { Model, Models } from "@earendil-works/pi-ai";
|
|
12
|
-
import { type PiSessionStore, type SessionInheritance } from "./sessions.ts";
|
|
13
|
-
import { type MountedTool } from "./tool.ts";
|
|
14
|
-
import { type OverrideEntryLike } from "./session-settings.ts";
|
|
15
|
-
/**
|
|
16
|
-
* The session custom-entry type recording ONE activation delta: `{ names }` — exactly the deferred
|
|
17
|
-
* tools a loader activated in that call. The DEDICATED record the resolve below reads: pi's own
|
|
18
|
-
* `active_tools_change` entries are full active-set SNAPSHOTS (setActiveTools persists everything
|
|
19
|
-
* active at that moment), and reinterpreting a snapshot as activations would keep a tool active in
|
|
20
|
-
* old sessions after the author flips it to `deferred` — the session never discovered it. Deltas
|
|
21
|
-
* carry only what was actually discovered.
|
|
22
|
-
*/
|
|
23
|
-
export declare const TOOL_ACTIVATION_ENTRY = "fastagent:tool-activation";
|
|
24
|
-
/** The session a factory-built harness is bound to — the seam the activation bridge (invoke.ts) uses
|
|
25
|
-
* to write {@link TOOL_ACTIVATION_ENTRY} deltas (pi's harness keeps its session private). Absent for
|
|
26
|
-
* a harness built outside {@link piHarnessFactory}: activation still works in-turn there, but is not
|
|
27
|
-
* recorded — the factory owns persistence. */
|
|
28
|
-
type AnyHarness = AgentHarness<any>;
|
|
29
|
-
export type PiSession = Awaited<ReturnType<PiSessionStore["openOrCreate"]>>;
|
|
30
|
-
export declare function harnessSession(harness: AnyHarness): PiSession | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* pi's Model with the API-shape generic erased — fastagent only passes models through to the
|
|
33
|
-
* harness, so the generic carries no information. One alias keeps the `any` auditable.
|
|
34
|
-
*/
|
|
35
|
-
export type AnyModel = Model<any>;
|
|
36
|
-
/** Builds a pi harness bound to the given session — called once per invoke. */
|
|
37
|
-
/** The harness fastagent builds: context-typed on {@link ExecutionToolContext}, because that is what
|
|
38
|
-
* pi's env-backed default tools read (pi 0.83). Custom tools are context-FREE and stay assignable — a
|
|
39
|
-
* four-parameter `execute` satisfies the five-parameter one, so `defineTool` is untouched by this. */
|
|
40
|
-
type PiHarness = AgentHarness<ExecutionToolContext>;
|
|
41
|
-
export type PiHarnessFactory = (session: string,
|
|
42
|
-
/** Where a NEW session starts from (sessions.ts) — an existing session ignores it. */
|
|
43
|
-
inherit?: SessionInheritance) => PiHarness | Promise<PiHarness>;
|
|
44
|
-
export interface PiHarnessFactoryOptions {
|
|
45
|
-
/** Session persistence. Continuity = same backing store + same session id. */
|
|
46
|
-
sessions: PiSessionStore;
|
|
47
|
-
/** Filesystem/process environment for the default coding tools. Handed to the harness as the TURN's
|
|
48
|
-
* tool context (pi 0.83), which is how read/bash/edit/write reach the machine at all — so this is the
|
|
49
|
-
* ONE seam a sandbox adapter implements, not a knob beside the tools that ignore it. */
|
|
50
|
-
env: ExecutionEnv;
|
|
51
|
-
/** Provider collection for all model requests; {@link model} must belong to it (same provider id). */
|
|
52
|
-
models: Models;
|
|
53
|
-
model: AnyModel;
|
|
54
|
-
/** Reasoning effort for the model (pi's scale). Unset = fastagent's pinned default ("medium", pi
|
|
55
|
-
* TUI parity — see {@link DEFAULT_THINKING_LEVEL}); unsupported levels are clamped by pi per model. */
|
|
56
|
-
thinkingLevel?: ThinkingLevel;
|
|
57
|
-
tools?: MountedTool[];
|
|
58
|
-
/**
|
|
59
|
-
* Final assembled prompt, or a SYNC factory re-evaluated per invoke (how L1 serves dynamic
|
|
60
|
-
* `instructions` + the skills listing). Distinct from {@link live}, which is the directory rung's
|
|
61
|
-
* ASYNC re-read of prompt AND skills as one pair — both are exercised, by different rungs.
|
|
62
|
-
*/
|
|
63
|
-
systemPrompt?: string | (() => string);
|
|
64
|
-
/** Skills visible to the model / explicitly invokable (injected as harness resources). */
|
|
65
|
-
skills?: Skill[];
|
|
66
|
-
/**
|
|
67
|
-
* Per-invoke source for the prompt+skills PAIR, re-evaluated on every harness build. When set it
|
|
68
|
-
* supersedes {@link systemPrompt}/{@link skills} — one call yields both, so the skills listing
|
|
69
|
-
* inside the prompt and the mounted skill resources can never come from two different reads. The
|
|
70
|
-
* directory rung (L2) uses it to re-read the definition, so AGENTS.md/skills edits — the author's or
|
|
71
|
-
* the agent's own — take effect on the next turn without a process restart. A rejection surfaces
|
|
72
|
-
* as that invoke's `failed` event (the factory throw path), never a crash.
|
|
73
|
-
*/
|
|
74
|
-
live?: () => Promise<{
|
|
75
|
-
systemPrompt?: string;
|
|
76
|
-
skills?: Skill[];
|
|
77
|
-
}>;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Retry policy for generated compaction/branch-summary model calls (pi ≥0.81.1, #6901). OPT-IN
|
|
81
|
-
* upstream — an undefined policy means no retries — so both compaction paths pass it explicitly:
|
|
82
|
-
* the harness config (auto-compaction inside a run) and the manual `compact()` dispatch in
|
|
83
|
-
* session-control. Values mirror pi's own app defaults (maxRetries 3, base 2s exponential).
|
|
84
|
-
*/
|
|
85
|
-
export declare const SUMMARIZATION_RETRY_POLICY: {
|
|
86
|
-
readonly enabled: true;
|
|
87
|
-
readonly maxRetries: 3;
|
|
88
|
-
readonly baseDelayMs: 2000;
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* The serving default for reasoning effort, pinned to what pi's TUI defaults to (its
|
|
92
|
-
* DEFAULT_THINKING_LEVEL) — NOT inherited from the bare harness, whose own fallback is "off": an
|
|
93
|
-
* author vibes at "medium" in pi and must get "medium" when served (fidelity), and pinning the value
|
|
94
|
-
* here means an upstream default change in either place cannot silently alter deployments. Models
|
|
95
|
-
* that don't support a level are clamped by pi per model.
|
|
96
|
-
*/
|
|
97
|
-
export declare const DEFAULT_THINKING_LEVEL: ThinkingLevel;
|
|
98
|
-
/**
|
|
99
|
-
* {@link resolveSessionSettings} plus the warn only the execution path owes: a recorded pair can stop
|
|
100
|
-
* being executable with no control-plane command involved (pi appends these entries itself; a
|
|
101
|
-
* deployment's configured model can change between restarts). Deduped per session+cause — it would
|
|
102
|
-
* otherwise repeat every turn.
|
|
103
|
-
*/
|
|
104
|
-
export declare function resolveHarnessOverrides(entries: OverrideEntryLike[], models: Models, defaults: {
|
|
105
|
-
model: AnyModel;
|
|
106
|
-
thinkingLevel: ThinkingLevel;
|
|
107
|
-
}, sessionId: string): {
|
|
108
|
-
model: AnyModel;
|
|
109
|
-
thinkingLevel: ThinkingLevel;
|
|
110
|
-
};
|
|
111
|
-
export declare function resolveHarnessActiveToolNames(recorded: string[] | null, tools: MountedTool[], sessionId: string): string[] | undefined;
|
|
112
|
-
/** Open-or-create the session per invoke: existing → open (history via buildContext); missing →
|
|
113
|
-
* create, seeded from `inherit` when the scope names a parent. */
|
|
114
|
-
export declare function piHarnessFactory(options: PiHarnessFactoryOptions): PiHarnessFactory;
|
|
115
|
-
export {};
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pi harness wiring: construct one pi `AgentHarness` per session. The agent definition (AGENTS.md +
|
|
3
|
-
* skills) is content fed INTO the harness (see definition.ts), not part of it.
|
|
4
|
-
*
|
|
5
|
-
* Under the stateless design the harness is discarded after each use; continuity comes from
|
|
6
|
-
* persisting the session (PiSessionStore) and re-opening it per invoke — pi's prompt() folds the
|
|
7
|
-
* historical entries back into context via buildContext().
|
|
8
|
-
*/
|
|
9
|
-
import { AgentHarness } from "@earendil-works/pi-agent-core";
|
|
10
|
-
import { log } from "../../log.js";
|
|
11
|
-
import { activePathEntries } from "./sessions.js";
|
|
12
|
-
import { isDeferredTool } from "./tool.js";
|
|
13
|
-
import { resolveSessionSettings } from "./session-settings.js";
|
|
14
|
-
/**
|
|
15
|
-
* The session custom-entry type recording ONE activation delta: `{ names }` — exactly the deferred
|
|
16
|
-
* tools a loader activated in that call. The DEDICATED record the resolve below reads: pi's own
|
|
17
|
-
* `active_tools_change` entries are full active-set SNAPSHOTS (setActiveTools persists everything
|
|
18
|
-
* active at that moment), and reinterpreting a snapshot as activations would keep a tool active in
|
|
19
|
-
* old sessions after the author flips it to `deferred` — the session never discovered it. Deltas
|
|
20
|
-
* carry only what was actually discovered.
|
|
21
|
-
*/
|
|
22
|
-
export const TOOL_ACTIVATION_ENTRY = "fastagent:tool-activation";
|
|
23
|
-
const harnessSessions = new WeakMap();
|
|
24
|
-
export function harnessSession(harness) {
|
|
25
|
-
return harnessSessions.get(harness);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Provider request retries. The OpenAI-family / Anthropic / Azure / Codex pi-ai adapters
|
|
29
|
-
* implement client-side retries (429/5xx/request-phase network failures with backoff, honoring
|
|
30
|
-
* Retry-After; a Codex websocket that fails before the stream starts falls back to SSE) but all
|
|
31
|
-
* default maxRetries to 0 (even SDK-backed ones override the SDK default), so a single transient
|
|
32
|
-
* `fetch failed` would otherwise kill the whole turn; 2 matches the OpenAI/Anthropic SDK default.
|
|
33
|
-
* The google / vertex / bedrock / mistral adapters ignore this option (a pi-ai upstream gap) —
|
|
34
|
-
* transients there still fail the turn. Mid-stream drops are deliberately NOT retried anywhere on
|
|
35
|
-
* this path — partial output was already streamed and the SPEC event stream cannot retract it,
|
|
36
|
-
* so a mid-stream failure surfaces as a `failed` event.
|
|
37
|
-
*/
|
|
38
|
-
const PROVIDER_MAX_RETRIES = 2;
|
|
39
|
-
/**
|
|
40
|
-
* Retry policy for generated compaction/branch-summary model calls (pi ≥0.81.1, #6901). OPT-IN
|
|
41
|
-
* upstream — an undefined policy means no retries — so both compaction paths pass it explicitly:
|
|
42
|
-
* the harness config (auto-compaction inside a run) and the manual `compact()` dispatch in
|
|
43
|
-
* session-control. Values mirror pi's own app defaults (maxRetries 3, base 2s exponential).
|
|
44
|
-
*/
|
|
45
|
-
export const SUMMARIZATION_RETRY_POLICY = { enabled: true, maxRetries: 3, baseDelayMs: 2000 };
|
|
46
|
-
/**
|
|
47
|
-
* The serving default for reasoning effort, pinned to what pi's TUI defaults to (its
|
|
48
|
-
* DEFAULT_THINKING_LEVEL) — NOT inherited from the bare harness, whose own fallback is "off": an
|
|
49
|
-
* author vibes at "medium" in pi and must get "medium" when served (fidelity), and pinning the value
|
|
50
|
-
* here means an upstream default change in either place cannot silently alter deployments. Models
|
|
51
|
-
* that don't support a level are clamped by pi per model.
|
|
52
|
-
*/
|
|
53
|
-
export const DEFAULT_THINKING_LEVEL = "medium";
|
|
54
|
-
/**
|
|
55
|
-
* Resolve the active-tool set for a fresh harness — the ONE place both fallbacks live. pi's harness
|
|
56
|
-
* WRITES active-tool changes to the session (`setActiveTools` → `active_tools_change`) but its
|
|
57
|
-
* constructor never reads them back — pi's long-lived TUI harness keeps the set in memory, while
|
|
58
|
-
* fastagent builds a FRESH harness per invoke, which would silently reset the session's active set
|
|
59
|
-
* every turn.
|
|
60
|
-
*
|
|
61
|
-
* No record (`null`) → the INITIAL set: every non-deferred tool; undefined when nothing is deferred
|
|
62
|
-
* (pi's default — all active — applies, and no session entry is ever written; tool-sets without
|
|
63
|
-
* deferral behave exactly as before deferral existed).
|
|
64
|
-
*
|
|
65
|
-
* A record is NOT replayed as a frozen snapshot — the active set is rebuilt as the UNION of the
|
|
66
|
-
* initial set and the recorded names (filtered to the mounted tools: the constructor THROWS on
|
|
67
|
-
* unknown names, so a recorded-but-removed tool would otherwise brick every future invoke of that
|
|
68
|
-
* session). On the serving path only the additive activation bridge writes records, so a record's
|
|
69
|
-
* real semantic is "which deferred tools this session activated" — layered on top of whatever the
|
|
70
|
-
* workspace mounts TODAY. A snapshot replay would silently freeze a later-added non-deferred tool
|
|
71
|
-
* out of every session the loader ever touched. Missing recorded names are logged (fail visibly) —
|
|
72
|
-
* ONCE per session+missing set: a fresh harness is built per invoke and channel sessions live for
|
|
73
|
-
* weeks, so an un-deduped warn would repeat every turn and dilute its own signal. A log-dedup memo
|
|
74
|
-
* (like L2's findings memo), not session state — the resolve stays derived from the session.
|
|
75
|
-
*/
|
|
76
|
-
const warnedRestores = new Set();
|
|
77
|
-
/**
|
|
78
|
-
* {@link resolveSessionSettings} plus the warn only the execution path owes: a recorded pair can stop
|
|
79
|
-
* being executable with no control-plane command involved (pi appends these entries itself; a
|
|
80
|
-
* deployment's configured model can change between restarts). Deduped per session+cause — it would
|
|
81
|
-
* otherwise repeat every turn.
|
|
82
|
-
*/
|
|
83
|
-
export function resolveHarnessOverrides(entries, models, defaults, sessionId) {
|
|
84
|
-
const settings = resolveSessionSettings(entries, models, defaults);
|
|
85
|
-
const warnOnce = (key, message) => {
|
|
86
|
-
const emit = warnedRestores.has(key) ? log.debug : log.warn;
|
|
87
|
-
warnedRestores.add(key);
|
|
88
|
-
emit(message);
|
|
89
|
-
};
|
|
90
|
-
const dropped = settings.dropped;
|
|
91
|
-
if (dropped?.model) {
|
|
92
|
-
warnOnce(`${sessionId}\u0000model\u0000${dropped.model}`, `[fastagent] session ${sessionId}: recorded model override ${dropped.model} is not in this deployment's registry — using the configured default`);
|
|
93
|
-
}
|
|
94
|
-
if (dropped?.thinkingLevel) {
|
|
95
|
-
const { recorded, running, known } = dropped.thinkingLevel;
|
|
96
|
-
warnOnce(`${sessionId}\u0000thinking\u0000${settings.model.provider}/${settings.model.id}\u0000${recorded}`, known
|
|
97
|
-
? `[fastagent] session ${sessionId}: recorded thinking level "${recorded}" is not supported by ${settings.model.provider}/${settings.model.id} — running at "${running}"`
|
|
98
|
-
: `[fastagent] session ${sessionId}: recorded thinking level "${recorded}" is unknown — using the configured default`);
|
|
99
|
-
}
|
|
100
|
-
return { model: settings.model, thinkingLevel: settings.thinkingLevel };
|
|
101
|
-
}
|
|
102
|
-
export function resolveHarnessActiveToolNames(recorded, tools, sessionId) {
|
|
103
|
-
const anyDeferred = tools.some(isDeferredTool);
|
|
104
|
-
const initial = tools.filter((t) => !isDeferredTool(t)).map((t) => t.name);
|
|
105
|
-
if (recorded === null)
|
|
106
|
-
return anyDeferred ? initial : undefined;
|
|
107
|
-
const mounted = new Set(tools.map((t) => t.name));
|
|
108
|
-
const known = recorded.filter((name) => mounted.has(name));
|
|
109
|
-
const missing = recorded.filter((name) => !mounted.has(name));
|
|
110
|
-
if (missing.length > 0) {
|
|
111
|
-
const emit = warnedRestores.has(`${sessionId}\u0000${missing.join(",")}`) ? log.debug : log.warn;
|
|
112
|
-
warnedRestores.add(`${sessionId}\u0000${missing.join(",")}`);
|
|
113
|
-
emit(`[fastagent] session ${sessionId}: dropping recorded activation(s) no longer mounted: ${missing.join(", ")}`);
|
|
114
|
-
}
|
|
115
|
-
return [...new Set([...initial, ...known])];
|
|
116
|
-
}
|
|
117
|
-
/** Open-or-create the session per invoke: existing → open (history via buildContext); missing →
|
|
118
|
-
* create, seeded from `inherit` when the scope names a parent. */
|
|
119
|
-
export function piHarnessFactory(options) {
|
|
120
|
-
return async (sessionId, inherit) => {
|
|
121
|
-
const session = await options.sessions.openOrCreate(sessionId, inherit);
|
|
122
|
-
// One extra entry walk per invoke to collect the activation deltas — negligible against the model
|
|
123
|
-
// call, same trade as L2's per-invoke definition re-read. The walk is over the ACTIVE PATH, not
|
|
124
|
-
// the flat journal: `navigate` moves the leaf, so the tree can hold an abandoned branch whose
|
|
125
|
-
// activations and overrides this session has left behind.
|
|
126
|
-
const entries = await activePathEntries(session);
|
|
127
|
-
const activated = entries.flatMap((e) => e.type === "custom" && e.customType === TOOL_ACTIVATION_ENTRY
|
|
128
|
-
? (e.data?.names ?? [])
|
|
129
|
-
: []);
|
|
130
|
-
const fresh = options.live ? await options.live() : undefined;
|
|
131
|
-
const { systemPrompt } = options;
|
|
132
|
-
const prompt = fresh ? fresh.systemPrompt : typeof systemPrompt === "function" ? systemPrompt() : systemPrompt;
|
|
133
|
-
const skills = fresh ? fresh.skills : options.skills;
|
|
134
|
-
// Session overrides (set_model / set_thinking) win over the assembly defaults — same entry walk.
|
|
135
|
-
const overrides = resolveHarnessOverrides(entries, options.models, { model: options.model, thinkingLevel: options.thinkingLevel ?? DEFAULT_THINKING_LEVEL }, sessionId);
|
|
136
|
-
const harness = new AgentHarness({
|
|
137
|
-
// Static, not a per-turn provider: the env is fixed for the agent's lifetime, and resolving a
|
|
138
|
-
// constant per turn would only add a promise to the turn's critical path.
|
|
139
|
-
toolContext: { env: options.env },
|
|
140
|
-
session,
|
|
141
|
-
models: options.models,
|
|
142
|
-
model: overrides.model,
|
|
143
|
-
thinkingLevel: overrides.thinkingLevel,
|
|
144
|
-
tools: options.tools,
|
|
145
|
-
activeToolNames: resolveHarnessActiveToolNames(activated.length > 0 ? activated : null, options.tools ?? [], sessionId),
|
|
146
|
-
systemPrompt: prompt,
|
|
147
|
-
resources: skills ? { skills } : undefined,
|
|
148
|
-
streamOptions: { maxRetries: PROVIDER_MAX_RETRIES },
|
|
149
|
-
retry: SUMMARIZATION_RETRY_POLICY,
|
|
150
|
-
});
|
|
151
|
-
harnessSessions.set(harness, session);
|
|
152
|
-
return harness;
|
|
153
|
-
};
|
|
154
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The harness L0: fan pi AgentHarness's two ports (subscribe event side-channel + prompt final
|
|
3
|
-
* value) into SPEC's single event stream, under a single-writer-per-session lease.
|
|
4
|
-
*
|
|
5
|
-
* The half that does not care which pi class runs the turn — lease, terminals, event queue, prompt
|
|
6
|
-
* prep — is turn-kit.ts. What lives here is what only the harness has: its event vocabulary, translated ONCE
|
|
7
|
-
* into the rich `SessionEvent` layer, and the observation plane that layer feeds.
|
|
8
|
-
*
|
|
9
|
-
* Concurrency: at most one in-flight turn per session; a second invoke fails fast with
|
|
10
|
-
* `failed{retryable}` ("session busy"), leaving dedupe/queueing/steering to the channel. Each
|
|
11
|
-
* invoke builds a fresh harness bound to the session and discards it (stateless multi-session).
|
|
12
|
-
*/
|
|
13
|
-
import type { AgentHarnessEvent } from "@earendil-works/pi-agent-core";
|
|
14
|
-
import { type Agent, type AgentEvent, type Prompt } from "../../agent.ts";
|
|
15
|
-
import type { SessionEvent } from "../../session.ts";
|
|
16
|
-
import { type PiHarnessFactory } from "./harness.ts";
|
|
17
|
-
import { type Lease } from "./turn-kit.ts";
|
|
18
|
-
/**
|
|
19
|
-
* In-stream event mapping — pi events are translated ONCE into the rich `SessionEvent` vocabulary;
|
|
20
|
-
* the SPEC `AgentEvent` stream is a narrow {@link projectAgentEvent} of it (design §6: one
|
|
21
|
-
* translation plus one projection, never two parallel translations). pi events with no session
|
|
22
|
-
* vocabulary yet (turn_start, agent_start, …) are dropped.
|
|
23
|
-
*/
|
|
24
|
-
export declare function toSessionEvent(pe: AgentHarnessEvent, runId: string): SessionEvent | null;
|
|
25
|
-
/** The SPEC projection of the rich stream. Events with no `AgentEvent` counterpart (progress,
|
|
26
|
-
* message boundaries, run boundaries) project to null — the invoke terminal is produced from the
|
|
27
|
-
* resolved message ({@link toTerminal}), not from `run_settled`. */
|
|
28
|
-
export declare function projectAgentEvent(se: SessionEvent): AgentEvent | null;
|
|
29
|
-
/** Live modulation handles for one active run — what the control plane's `dispatch` routes to.
|
|
30
|
-
* Built inside the invoke closure (it owns the harness); registered with the observer at
|
|
31
|
-
* run_started, gone after run_settled. RACE WINDOW (all three commands, symmetric): the run may
|
|
32
|
-
* resolve between the settled-check and the engine call landing — an accepted `abort` can still
|
|
33
|
-
* settle `completed`, and an accepted `steer`/`followUp` can settle without the prompt ever being
|
|
34
|
-
* consumed. Acceptance is not outcome; the settlement is the truth. */
|
|
35
|
-
export interface RunControls {
|
|
36
|
-
steer(prompt: Prompt): Promise<void>;
|
|
37
|
-
followUp(prompt: Prompt): Promise<void>;
|
|
38
|
-
abort(): Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
/** The DATA-plane observation seam: every rich event of every run, pushed as it happens. `run`
|
|
41
|
-
* carries the live {@link RunControls}, attached to the `run_started` event only. A hub
|
|
42
|
-
* (session-control.ts) implements this to serve `events()`/`state()`/`dispatch`; absent = zero
|
|
43
|
-
* overhead. Scope: RUN events only — the hub's own boundary-mutation events (`state_changed`,
|
|
44
|
-
* `compaction_*`) originate in the hub and reach full-vocabulary taps via the hub's `tap` option,
|
|
45
|
-
* not this seam. TRUST BOUNDARY: since Phase 2a this seam hands every wired observer the run's
|
|
46
|
-
* modulation handles — it is the trusted hub seam, not a public fan-out point. Do not wire
|
|
47
|
-
* untrusted taps here; give third parties the read-only `events()` stream instead. */
|
|
48
|
-
export type SessionObserver = (session: string, event: SessionEvent, run?: RunControls) => void;
|
|
49
|
-
export interface CreatePiAgentFromHarnessOptions {
|
|
50
|
-
harnessFactory: PiHarnessFactory;
|
|
51
|
-
/** Single-writer lease. Defaults to the in-process per-session fail-fast lease. */
|
|
52
|
-
lease?: Lease;
|
|
53
|
-
/** Observation-plane tap (see {@link SessionObserver}). Optional; invoke behavior is identical
|
|
54
|
-
* with or without it — the SPEC stream is a projection of what the observer sees. */
|
|
55
|
-
observer?: SessionObserver;
|
|
56
|
-
/** Working directory exposed to FastAgent-defined tools. Defaults to process.cwd(). */
|
|
57
|
-
cwd?: string;
|
|
58
|
-
}
|
|
59
|
-
/** "From a harness factory": engine wired by the caller; adds only the concurrency/stream shell. */
|
|
60
|
-
export declare function createPiAgentFromHarness(options: CreatePiAgentFromHarnessOptions): Agent;
|