@fastagent-sh/fastagent 0.14.0 → 0.16.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 +16 -6
- package/dist/agent.d.ts +20 -0
- package/dist/agent.js +9 -0
- package/dist/channels/agentcore-limits.d.ts +7 -0
- package/dist/channels/agentcore-limits.js +9 -0
- package/dist/channels/agentcore-state.d.ts +83 -0
- package/dist/channels/agentcore-state.js +258 -0
- package/dist/channels/agentcore.d.ts +98 -0
- package/dist/channels/agentcore.js +260 -0
- package/dist/channels/busy.d.ts +23 -0
- package/dist/channels/busy.js +53 -0
- package/dist/channels/context-buffer.d.ts +35 -0
- package/dist/channels/context-buffer.js +103 -0
- package/dist/channels/control.d.ts +28 -0
- package/dist/channels/control.js +214 -0
- package/dist/channels/feishu/card.js +1 -1
- package/dist/channels/feishu/context-buffer.d.ts +15 -13
- package/dist/channels/feishu/context-buffer.js +62 -69
- package/dist/channels/feishu/feishu-api.d.ts +11 -3
- package/dist/channels/feishu/feishu-api.js +24 -3
- package/dist/channels/feishu/feishu.d.ts +27 -27
- package/dist/channels/feishu/feishu.js +318 -207
- package/dist/channels/feishu/invoke-turn.d.ts +7 -20
- package/dist/channels/feishu/invoke-turn.js +44 -79
- package/dist/channels/feishu/model.d.ts +2 -2
- package/dist/channels/feishu/normalize.d.ts +2 -1
- package/dist/channels/feishu/normalize.js +0 -1
- package/dist/channels/feishu/parse.d.ts +22 -8
- package/dist/channels/feishu/parse.js +24 -7
- package/dist/channels/feishu/preview.d.ts +4 -7
- package/dist/channels/feishu/preview.js +27 -142
- package/dist/channels/feishu/register-app.d.ts +2 -1
- package/dist/channels/feishu/register-webhook.d.ts +2 -1
- package/dist/channels/feishu/scaffold/channel.ts +9 -8
- package/dist/channels/feishu/scaffold/feishu-send.ts +6 -4
- package/dist/channels/feishu/setup-mode.d.ts +37 -0
- package/dist/channels/feishu/setup-mode.js +28 -0
- package/dist/channels/feishu/ws-ingress.d.ts +28 -0
- package/dist/channels/feishu/ws-ingress.js +136 -0
- package/dist/channels/github/github.js +16 -7
- package/dist/channels/http.d.ts +14 -0
- package/dist/channels/http.js +36 -3
- package/dist/channels/invoke-turn-kit.d.ts +65 -0
- package/dist/channels/invoke-turn-kit.js +87 -0
- package/dist/channels/lark/lark.d.ts +4 -2
- package/dist/channels/lark/lark.js +4 -1
- package/dist/channels/lark/onboard.d.ts +8 -4
- package/dist/channels/lark/onboard.js +8 -0
- package/dist/channels/lark/scaffold/channel.ts +9 -8
- package/dist/channels/lark/scaffold/lark-send.ts +6 -4
- package/dist/channels/preview-kit.d.ts +115 -0
- package/dist/channels/preview-kit.js +184 -0
- package/dist/channels/seen.d.ts +5 -0
- package/dist/channels/seen.js +35 -0
- package/dist/channels/slack/bot-auth.d.ts +15 -0
- package/dist/channels/slack/bot-auth.js +146 -0
- package/dist/channels/slack/config-api.d.ts +60 -0
- package/dist/channels/slack/config-api.js +149 -0
- package/dist/channels/slack/context-buffer.d.ts +24 -0
- package/dist/channels/slack/context-buffer.js +37 -0
- package/dist/channels/slack/invoke-turn.d.ts +19 -0
- package/dist/channels/slack/invoke-turn.js +63 -0
- package/dist/channels/slack/manifest.d.ts +49 -0
- package/dist/channels/slack/manifest.js +69 -0
- package/dist/channels/slack/model.d.ts +67 -0
- package/dist/channels/slack/model.js +2 -0
- package/dist/channels/slack/onboard.d.ts +41 -0
- package/dist/channels/slack/onboard.js +120 -0
- package/dist/channels/slack/onboarding-state.d.ts +31 -0
- package/dist/channels/slack/onboarding-state.js +69 -0
- package/dist/channels/slack/parse.d.ts +38 -0
- package/dist/channels/slack/parse.js +124 -0
- package/dist/channels/slack/preview.d.ts +23 -0
- package/dist/channels/slack/preview.js +403 -0
- package/dist/channels/slack/reaction.d.ts +24 -0
- package/dist/channels/slack/reaction.js +62 -0
- package/dist/channels/slack/register-webhook.d.ts +10 -0
- package/dist/channels/slack/register-webhook.js +49 -0
- package/dist/channels/slack/scaffold/channel.ts +33 -0
- package/dist/channels/slack/scaffold/slack-send.ts +171 -0
- package/dist/channels/slack/setup-server.d.ts +17 -0
- package/dist/channels/slack/setup-server.js +103 -0
- package/dist/channels/slack/slack-api.d.ts +57 -0
- package/dist/channels/slack/slack-api.js +399 -0
- package/dist/channels/slack/slack.d.ts +51 -0
- package/dist/channels/slack/slack.js +496 -0
- package/dist/channels/slack/welcomed.d.ts +5 -0
- package/dist/channels/slack/welcomed.js +32 -0
- package/dist/channels/state.d.ts +11 -4
- package/dist/channels/state.js +22 -15
- package/dist/channels/stop-command.d.ts +6 -0
- package/dist/channels/stop-command.js +36 -0
- package/dist/channels/tasks.d.ts +7 -0
- package/dist/channels/tasks.js +25 -0
- package/dist/channels/telegram/context-buffer.d.ts +8 -17
- package/dist/channels/telegram/context-buffer.js +6 -85
- package/dist/channels/telegram/invoke-turn.d.ts +5 -22
- package/dist/channels/telegram/invoke-turn.js +11 -58
- package/dist/channels/telegram/parse.d.ts +0 -7
- package/dist/channels/telegram/parse.js +4 -2
- package/dist/channels/telegram/preview.d.ts +4 -7
- package/dist/channels/telegram/preview.js +24 -142
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -3
- package/dist/channels/telegram/telegram.js +24 -10
- package/dist/channels/{feishu/text.d.ts → text.d.ts} +15 -1
- package/dist/channels/{feishu/text.js → text.js} +15 -1
- package/dist/channels/thread-participants.d.ts +21 -0
- package/dist/channels/thread-participants.js +132 -0
- package/dist/channels/turn-queue.js +8 -1
- package/dist/channels/turn-store.d.ts +1 -1
- package/dist/channels/turn-store.js +2 -3
- package/dist/cli/add-feishu.d.ts +30 -0
- package/dist/cli/add-feishu.js +366 -0
- package/dist/cli/add-slack.d.ts +11 -0
- package/dist/cli/add-slack.js +199 -0
- package/dist/{cli-auth.d.ts → cli/auth-view.d.ts} +1 -1
- package/dist/{cli-auth.js → cli/auth-view.js} +1 -1
- package/dist/cli/commands/add.d.ts +4 -0
- package/dist/cli/commands/add.js +191 -60
- package/dist/cli/commands/attach.d.ts +82 -0
- package/dist/cli/commands/attach.js +563 -0
- package/dist/cli/commands/chat.d.ts +1 -0
- package/dist/cli/commands/chat.js +12 -7
- package/dist/cli/commands/deploy.d.ts +3 -1
- package/dist/cli/commands/deploy.js +384 -82
- package/dist/cli/commands/dev.js +34 -26
- package/dist/cli/commands/fire.js +17 -18
- package/dist/cli/commands/info.js +36 -29
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.js +65 -53
- package/dist/cli/commands/invoke.js +11 -9
- package/dist/cli/commands/login.js +48 -45
- package/dist/cli/commands/models.js +1 -1
- package/dist/cli/commands/schedule.js +6 -8
- package/dist/cli/commands/start.js +102 -39
- package/dist/cli/commands/tool.js +23 -18
- package/dist/cli/fail.d.ts +17 -2
- package/dist/cli/fail.js +25 -1
- package/dist/{invoke-stream.d.ts → cli/invoke-stream.d.ts} +1 -1
- package/dist/{invoke-stream.js → cli/invoke-stream.js} +4 -0
- package/dist/cli/kernel.d.ts +3 -2
- package/dist/cli/models-view.d.ts +21 -0
- package/dist/cli/models-view.js +66 -0
- package/dist/cli/program.js +135 -56
- package/dist/cli/serve.d.ts +60 -20
- package/dist/cli/serve.js +252 -47
- package/dist/cli/shared.d.ts +40 -7
- package/dist/cli/shared.js +180 -38
- package/dist/collect.d.ts +14 -3
- package/dist/collect.js +24 -0
- package/dist/core.d.ts +3 -1
- package/dist/core.js +2 -0
- package/dist/deploy/agentcore/plan.d.ts +117 -0
- package/dist/deploy/agentcore/plan.js +721 -0
- package/dist/deploy/agentcore/run.d.ts +73 -0
- package/dist/deploy/agentcore/run.js +412 -0
- package/dist/deploy/agentcore/zip.d.ts +17 -0
- package/dist/deploy/agentcore/zip.js +68 -0
- package/dist/deploy/container.d.ts +26 -25
- package/dist/deploy/container.js +93 -89
- package/dist/deploy/docker/plan.d.ts +3 -3
- package/dist/deploy/docker/plan.js +20 -23
- package/dist/deploy/fly/plan.d.ts +5 -1
- package/dist/deploy/fly/plan.js +39 -26
- package/dist/deploy/fly/run.d.ts +17 -5
- package/dist/deploy/fly/run.js +52 -7
- package/dist/deploy/preflight.d.ts +20 -8
- package/dist/deploy/preflight.js +267 -71
- package/dist/deploy/railway/plan.d.ts +10 -1
- package/dist/deploy/railway/plan.js +53 -20
- package/dist/deploy/railway/run.d.ts +13 -5
- package/dist/deploy/railway/run.js +22 -7
- package/dist/deploy/runner.d.ts +7 -3
- package/dist/deploy/runner.js +9 -3
- package/dist/deploy/secrets.d.ts +2 -1
- package/dist/deploy/secrets.js +23 -3
- package/dist/dev-supervisor.d.ts +11 -10
- package/dist/dev-supervisor.js +56 -50
- package/dist/engines/pi/auth.d.ts +8 -7
- package/dist/engines/pi/auth.js +12 -10
- package/dist/engines/pi/channel.d.ts +22 -16
- package/dist/engines/pi/channel.js +92 -62
- package/dist/engines/pi/chat.d.ts +4 -16
- package/dist/engines/pi/chat.js +8 -261
- package/dist/engines/pi/config.d.ts +20 -55
- package/dist/engines/pi/config.js +49 -121
- package/dist/engines/pi/create.d.ts +30 -10
- package/dist/engines/pi/create.js +33 -26
- package/dist/engines/pi/definition.d.ts +7 -26
- package/dist/engines/pi/definition.js +8 -54
- package/dist/engines/pi/harness.d.ts +49 -0
- package/dist/engines/pi/harness.js +90 -2
- package/dist/engines/pi/invoke.d.ts +55 -2
- package/dist/engines/pi/invoke.js +304 -19
- package/dist/engines/pi/login.d.ts +12 -1
- package/dist/engines/pi/login.js +16 -4
- package/dist/engines/pi/models.d.ts +59 -13
- package/dist/engines/pi/models.js +62 -24
- package/dist/engines/pi/open.d.ts +114 -0
- package/dist/engines/pi/open.js +127 -0
- package/dist/engines/pi/session-builder.d.ts +16 -0
- package/dist/engines/pi/session-builder.js +308 -0
- package/dist/engines/pi/session-control.d.ts +50 -0
- package/dist/engines/pi/session-control.js +604 -0
- package/dist/engines/pi/sessions.d.ts +17 -2
- package/dist/engines/pi/sessions.js +9 -0
- package/dist/engines/pi/tool-context.d.ts +18 -11
- package/dist/engines/pi/tool-context.js +3 -9
- package/dist/engines/pi/tool.d.ts +6 -8
- package/dist/engines/pi/tool.js +10 -1
- package/dist/engines/pi/wake-tool.d.ts +0 -3
- package/dist/engines/pi/wake-tool.js +9 -7
- package/dist/env.d.ts +16 -4
- package/dist/env.js +43 -5
- package/dist/feishu.d.ts +1 -1
- package/dist/feishu.js +1 -1
- package/dist/host/node.d.ts +25 -8
- package/dist/host/node.js +5 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lark.d.ts +1 -1
- package/dist/lark.js +1 -1
- package/dist/loader.d.ts +2 -2
- package/dist/loader.js +3 -3
- package/dist/log.d.ts +1 -1
- package/dist/log.js +1 -1
- package/dist/observe.js +3 -0
- package/dist/paths.d.ts +138 -0
- package/dist/paths.js +326 -0
- package/dist/pi.d.ts +6 -5
- package/dist/pi.js +4 -3
- package/dist/runtime.d.ts +7 -5
- package/dist/runtime.js +2 -2
- package/dist/scaffold/add-channel.d.ts +26 -9
- package/dist/scaffold/add-channel.js +175 -40
- package/dist/scaffold/init.d.ts +32 -41
- package/dist/scaffold/init.js +161 -185
- package/dist/scaffold/templates/env.example +15 -6
- package/dist/scaffold/templates/fastagent.config.mjs +5 -4
- package/dist/scaffold/templates/gitignore +14 -6
- package/dist/scaffold/templates/persona.md +4 -2
- package/dist/scaffold/templates/secrets.gitignore +5 -0
- package/dist/scaffold/templates.d.ts +1 -7
- package/dist/scaffold/templates.js +3 -25
- package/dist/scaffold/vendor-skill.d.ts +2 -2
- package/dist/scaffold/vendor-skill.js +13 -13
- package/dist/schedule/discover.js +4 -4
- package/dist/schedule/scheduler.d.ts +40 -1
- package/dist/schedule/scheduler.js +89 -56
- package/dist/schedule/state.js +1 -1
- package/dist/schedule/wake-alarm.d.ts +47 -0
- package/dist/schedule/wake-alarm.js +136 -0
- package/dist/schedule/wakeups.d.ts +1 -3
- package/dist/schedule/wakeups.js +19 -1
- package/dist/session-remote.d.ts +53 -0
- package/dist/session-remote.js +336 -0
- package/dist/session.d.ts +265 -0
- package/dist/session.js +37 -0
- package/dist/slack.d.ts +2 -0
- package/dist/slack.js +2 -0
- package/dist/tunnel.d.ts +7 -4
- package/dist/tunnel.js +23 -12
- package/package.json +22 -7
- package/dist/channels/feishu/owned-threads.d.ts +0 -11
- package/dist/channels/feishu/owned-threads.js +0 -47
- package/dist/channels/feishu/seen.d.ts +0 -5
- package/dist/channels/feishu/seen.js +0 -47
- package/dist/cli-add-feishu.d.ts +0 -8
- package/dist/cli-add-feishu.js +0 -223
- package/dist/cli-models.d.ts +0 -11
- package/dist/cli-models.js +0 -20
- package/dist/engines/pi/workspace.d.ts +0 -56
- package/dist/engines/pi/workspace.js +0 -72
- package/dist/scaffold/templates/gitignore.agentdir-root +0 -5
- package/dist/scaffold/templates/gitignore.kit +0 -2
- package/dist/workspace.d.ts +0 -9
- package/dist/workspace.js +0 -45
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { defaultProviderAuthContext } from "@earendil-works/pi-ai";
|
|
8
8
|
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
|
|
9
|
-
import {
|
|
9
|
+
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { fastagentCredentialStore } from "./auth.js";
|
|
11
|
+
import { interactiveLoginKind } from "./login.js";
|
|
11
12
|
/**
|
|
12
13
|
* A `Models` with every built-in pi provider, wired to fastagent's auth: stored credentials from the
|
|
13
14
|
* {@link CreatePiModelsOptions.authPath} file (via {@link fastagentCredentialStore}; the global
|
|
14
|
-
* `~/.fastagent/auth.json` unless the opener passes a project-level path), then ambient env vars. A
|
|
15
|
+
* `~/.fastagent/.secrets/auth.json` unless the opener passes a project-level path), then ambient env vars. A
|
|
15
16
|
* stored credential owns the provider; env is consulted only when nothing is stored (resolution order
|
|
16
17
|
* is upstream-owned).
|
|
17
18
|
*/
|
|
@@ -25,38 +26,44 @@ export function createPiModels(options = {}) {
|
|
|
25
26
|
return models;
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
|
-
* The
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* to what the user can use and lets them choose (mirroring pi-coding-agent's select-then-persist).
|
|
29
|
+
* The `ModelRuntime`-shaped sibling of {@link createPiModels} — the SAME hub semantics (built-in
|
|
30
|
+
* providers + fastagent's credential store at `authPath`) in the type pi's session services require
|
|
31
|
+
* (`createAgentSessionServices({ modelRuntime })`). Builtins only (`modelsPath: null` — pi's
|
|
32
|
+
* machine-global models.json is definition-foreign) and no availability network, so the model
|
|
33
|
+
* surface equals serving's. No `providers` option: `ModelRuntime` registers providers by config
|
|
34
|
+
* record, not `Provider` instance — accepting the option and dropping it would be a silent no-op;
|
|
35
|
+
* add the mapping when a consumer actually needs it.
|
|
36
36
|
*/
|
|
37
|
-
export
|
|
38
|
-
|
|
37
|
+
export function createPiModelRuntime(options = {}) {
|
|
38
|
+
return ModelRuntime.create({
|
|
39
|
+
credentials: fastagentCredentialStore(options.authPath, { warn: options.warn }),
|
|
40
|
+
modelsPath: null,
|
|
41
|
+
allowModelNetwork: false,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Probe every provider's auth once (auth is provider-scoped, so any of its models works as the probe)
|
|
46
|
+
* — the status map behind the first-run model picker (`fastagent dev`/`start`/`invoke` with no model
|
|
47
|
+
* set). The picker shows the FULL catalog annotated with these statuses, so "what fastagent supports"
|
|
48
|
+
* and "what is authenticated on this machine" stay distinguishable; a needs-login choice triggers an
|
|
49
|
+
* inline `loginFlow`. Providers with no models are omitted (nothing to pick).
|
|
50
|
+
*/
|
|
51
|
+
export async function providerAuthStatuses(models) {
|
|
52
|
+
const statuses = new Map();
|
|
39
53
|
for (const provider of models.getProviders()) {
|
|
40
54
|
const [probe] = provider.getModels();
|
|
41
55
|
if (!probe)
|
|
42
56
|
continue;
|
|
43
|
-
|
|
57
|
+
const login = interactiveLoginKind(provider);
|
|
44
58
|
try {
|
|
45
|
-
|
|
59
|
+
const auth = await models.getAuth(probe);
|
|
60
|
+
statuses.set(provider.id, auth ? { state: "ready", source: auth.source } : { state: "unconfigured", login });
|
|
46
61
|
}
|
|
47
62
|
catch (error) {
|
|
48
|
-
|
|
49
|
-
// from the menu, but SAY so — a silent disappearance is the same fail-visibly gap the caller
|
|
50
|
-
// guards against for the top-level enumeration. `undefined` (plainly unconfigured) stays quiet.
|
|
51
|
-
log.warn(`[fastagent] skipping provider "${provider.id}": auth check failed (${error.message})`);
|
|
52
|
-
continue;
|
|
63
|
+
statuses.set(provider.id, { state: "broken", message: error.message, login });
|
|
53
64
|
}
|
|
54
|
-
if (!usable)
|
|
55
|
-
continue;
|
|
56
|
-
for (const model of provider.getModels())
|
|
57
|
-
specs.push(`${provider.id}/${model.id}`);
|
|
58
65
|
}
|
|
59
|
-
return
|
|
66
|
+
return statuses;
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
62
69
|
* Which source currently satisfies auth for `spec` — a startup diagnostic. Returns the upstream
|
|
@@ -74,3 +81,34 @@ export async function probeAuthSource(models, spec) {
|
|
|
74
81
|
const auth = await models.getAuth(model).catch(() => undefined);
|
|
75
82
|
return auth?.source;
|
|
76
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Quick-fail probe for a just-stored API key: one minimal real request through the standard auth
|
|
86
|
+
* resolution path (the same path invokes take), so a mistyped key surfaces at login time, not at the
|
|
87
|
+
* first turn. `complete` reports provider errors as `stopReason: "error"` rather than throwing; the
|
|
88
|
+
* HTTP status arrives via `onResponse` — when a provider path never calls it (SDK transports), fall
|
|
89
|
+
* back to a conservative "401" match in the error text. Short timeout, no retries: feedback speed
|
|
90
|
+
* over transient-failure tolerance (a transient lands on `unknown`, which keeps the key).
|
|
91
|
+
*/
|
|
92
|
+
export async function probeApiKey(models, model) {
|
|
93
|
+
let status;
|
|
94
|
+
let reply;
|
|
95
|
+
try {
|
|
96
|
+
reply = await models.complete(model, { messages: [{ role: "user", content: "ping", timestamp: Date.now() }] }, {
|
|
97
|
+
maxTokens: 16,
|
|
98
|
+
timeoutMs: 15_000,
|
|
99
|
+
maxRetries: 0,
|
|
100
|
+
onResponse: (r) => {
|
|
101
|
+
status = r.status;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
// Thrown = before/around the request (auth resolution, transport setup) — not a provider verdict.
|
|
107
|
+
return { state: "unknown", message: error.message };
|
|
108
|
+
}
|
|
109
|
+
if (reply.stopReason !== "error" && reply.stopReason !== "aborted")
|
|
110
|
+
return { state: "ok" };
|
|
111
|
+
const message = reply.errorMessage ?? `stopReason "${reply.stopReason}"`;
|
|
112
|
+
const unauthorized = status === 401 || (status === undefined && /(^|\D)401(\D|$)/.test(message));
|
|
113
|
+
return { state: unauthorized ? "rejected" : "unknown", message };
|
|
114
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { Agent } from "../../agent.ts";
|
|
3
|
+
import { type FastagentConfig } from "./config.ts";
|
|
4
|
+
import type { SessionControl } from "../../session.ts";
|
|
5
|
+
import type { SessionObserver } from "./invoke.ts";
|
|
6
|
+
import type { PiSessionReader, PiSessionStore } from "./sessions.ts";
|
|
7
|
+
import type { ModuleLoadFailure } from "../../loader.ts";
|
|
8
|
+
import type { LoadedDefinition } from "./definition.ts";
|
|
9
|
+
import type { ToolCollision } from "./tool.ts";
|
|
10
|
+
export interface CreatePiAgentFromDirOptions {
|
|
11
|
+
/** Model spec override (e.g. the CLI --model flag). Precedence: this > FASTAGENT_MODEL > config.model. */
|
|
12
|
+
model?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Session store directory. Default `<agentDir>/.state/sessions` (machine state). `start`
|
|
15
|
+
* overrides it (--sessions-dir / FASTAGENT_SESSIONS_DIR / a mounted volume) so production continuity
|
|
16
|
+
* survives redeploys.
|
|
17
|
+
*/
|
|
18
|
+
sessionsDir?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Credentials file override. Default `<agentDir>/.secrets/auth.json` (project-level, under the
|
|
21
|
+
* `.secrets/`). Override via --auth-path / FASTAGENT_AUTH_PATH; point it at
|
|
22
|
+
* the global `~/.fastagent/.secrets/auth.json` to share one credential across projects.
|
|
23
|
+
*/
|
|
24
|
+
authPath?: string;
|
|
25
|
+
/**
|
|
26
|
+
* This is a long-running SERVE (`dev`/`start`), where the scheduler poller runs — so a self-scheduled
|
|
27
|
+
* wake-up is actually honored. One-shot entries (`invoke`/`fire`) leave it off (they exit after the turn
|
|
28
|
+
* and never poll). The built-in `wake` tool mounts only when this is set AND `config.selfSchedule` is on.
|
|
29
|
+
*/
|
|
30
|
+
serving?: boolean;
|
|
31
|
+
/** Assemble the session control plane over this agent's session store and return it as
|
|
32
|
+
* {@link sessionControl} — the store is created inside this opener, so the hub must be wired
|
|
33
|
+
* here too (an external `createPiSessionControl` cannot exist before the store does).
|
|
34
|
+
* Default: `config.sessionControl` AND {@link serving} — the config key means "serve the control
|
|
35
|
+
* plane", so one-shot commands (invoke/fire) do not assemble an unused hub. Pass explicitly to
|
|
36
|
+
* override either way. */
|
|
37
|
+
sessionControl?: boolean;
|
|
38
|
+
/** Additional raw tap with the FULL vocabulary: run events composed after the
|
|
39
|
+
* {@link sessionControl} hub's observer, plus the hub's own boundary-mutation events
|
|
40
|
+
* (`state_changed`/`compaction_*`) via the hub's tap. TRUSTED seam: since Phase 2a an observer
|
|
41
|
+
* receives each run's live modulation handles (see `SessionObserver`) — for read-only consumers
|
|
42
|
+
* use the hub's `events()` stream instead. */
|
|
43
|
+
observer?: SessionObserver;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The agent assembly FRONT HALF — everything that is independent of how pi consumes the
|
|
47
|
+
* definition (transient harness for serving vs resident AgentSession for chat / session control):
|
|
48
|
+
* placement resolution → config → model spec → the full tool surface ({@link resolveAgentTools} — the
|
|
49
|
+
* ONE place it is computed) → state root → auth path. Both {@link createPiAgentFromDir} and the
|
|
50
|
+
* session builder (session-builder.ts) consume this, so THESE inputs cannot drift between the two
|
|
51
|
+
* consumption shapes. (Definition loading and prompt assembly stay per-consumer: serving re-reads
|
|
52
|
+
* them live per invoke, the session builder snapshots at startup and lets pi append skills/env.)
|
|
53
|
+
*/
|
|
54
|
+
export interface AgentAssembly {
|
|
55
|
+
config: FastagentConfig;
|
|
56
|
+
configPath?: string;
|
|
57
|
+
/** The resolved "provider/modelId" spec in use. */
|
|
58
|
+
modelSpec: string;
|
|
59
|
+
/** Absolute agent dir — definition + config + machinery live here (resolvePlacement().agentDir). */
|
|
60
|
+
agentDir: string;
|
|
61
|
+
/** Absolute workspace — the agent's cwd and the start of the ②-context walk: the agent dir's parent
|
|
62
|
+
* when the agent sits inside it, the agent dir ITSELF when you point at the agent. */
|
|
63
|
+
workspace: string;
|
|
64
|
+
/** Absolute state root (FASTAGENT_STATE_DIR > <agentDir>/.state). */
|
|
65
|
+
stateRoot: string;
|
|
66
|
+
/** Absolute credentials file (--auth-path/authPath option > FASTAGENT_AUTH_PATH > <agentDir>/.secrets/auth.json). */
|
|
67
|
+
authPath: string;
|
|
68
|
+
/** The full mounted tool surface (config.tools + discovered tools/, search_tools applied). */
|
|
69
|
+
tools: AgentTool[];
|
|
70
|
+
toolNames: string[];
|
|
71
|
+
deferredToolNames: string[];
|
|
72
|
+
toolCollisions: ToolCollision[];
|
|
73
|
+
toolFailures: ModuleLoadFailure[];
|
|
74
|
+
}
|
|
75
|
+
export declare function resolveAgentAssembly(dir: string, options?: {
|
|
76
|
+
model?: string;
|
|
77
|
+
authPath?: string;
|
|
78
|
+
}): Promise<AgentAssembly>;
|
|
79
|
+
/**
|
|
80
|
+
* "Point at a directory → agent": resolve the placement (`dir` may be either end — the workspace or
|
|
81
|
+
* the agent dir itself), load the config, resolve model and tools, then L2. Throws a clear error when
|
|
82
|
+
* no model source is set (fail visibly at startup). Returns everything an entry point needs to report
|
|
83
|
+
* what it assembled.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createPiAgentFromDir(dir: string, options?: CreatePiAgentFromDirOptions): Promise<{
|
|
86
|
+
agent: Agent;
|
|
87
|
+
definition: LoadedDefinition;
|
|
88
|
+
config: FastagentConfig;
|
|
89
|
+
configPath?: string;
|
|
90
|
+
/** The resolved "provider/modelId" spec actually in use. */
|
|
91
|
+
modelSpec: string;
|
|
92
|
+
/** Absolute agent dir in use — channels/tools/persona come from here. */
|
|
93
|
+
agentDir: string;
|
|
94
|
+
/** Absolute workspace in use — the agent's cwd: ALWAYS the directory that was pointed at. */
|
|
95
|
+
workspace: string;
|
|
96
|
+
/** Absolute state root in use (FASTAGENT_STATE_DIR > <agentDir>/.state) — the ChannelContext's stateRoot. */
|
|
97
|
+
stateRoot: string;
|
|
98
|
+
/** Absolute session store directory in use (for the startup report). */
|
|
99
|
+
sessionsDir: string;
|
|
100
|
+
/** Absolute credentials file in use (for the startup report). */
|
|
101
|
+
authPath: string;
|
|
102
|
+
/** The session store in use — also a {@link PiSessionReader}. */
|
|
103
|
+
sessions: PiSessionStore & PiSessionReader;
|
|
104
|
+
/** The observation plane over this agent's sessions; present iff `options.sessionControl`. */
|
|
105
|
+
sessionControl?: SessionControl;
|
|
106
|
+
/** Non-default, active-by-default tool names in effect: config.tools + discovered tools/. Each name
|
|
107
|
+
* lives in exactly one report slot — deferred names are in {@link deferredToolNames} instead. */
|
|
108
|
+
toolNames: string[];
|
|
109
|
+
/** Tools registered but not initially active (deferred) — activated via search_tools. */
|
|
110
|
+
deferredToolNames: string[];
|
|
111
|
+
toolCollisions: ToolCollision[];
|
|
112
|
+
/** `tools/` files that failed to import — skipped, reported by the caller, never fatal. */
|
|
113
|
+
toolFailures: ModuleLoadFailure[];
|
|
114
|
+
}>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Open a definition directory into an agent — the single agent opener BOTH `fastagent dev` and
|
|
3
|
+
* `fastagent start` drive.
|
|
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.
|
|
10
|
+
*/
|
|
11
|
+
import { mkdir } from "node:fs/promises";
|
|
12
|
+
import { defaultSessionsDir, loadConfig, resolveAuthPath, resolveModelSpec, } from "./config.js";
|
|
13
|
+
import { resolveStateRoot, resolvePlacement } from "../../paths.js";
|
|
14
|
+
import { createPiAgentFromDefinition, resolveAgentTools } from "./create.js";
|
|
15
|
+
import { createPiSessionControl } from "./session-control.js";
|
|
16
|
+
import { withWakeTool } from "./wake-tool.js";
|
|
17
|
+
import { jsonlSessionStore } from "./sessions.js";
|
|
18
|
+
export async function resolveAgentAssembly(dir, options = {}) {
|
|
19
|
+
// Placement is structural (resolvePlacement): the AGENT DIR carries definition + config + machinery;
|
|
20
|
+
// its parent — the WORKSPACE — is what the agent works on: its cwd and the start of the ②-context
|
|
21
|
+
// walk (that is where it reads the project's AGENTS.md from).
|
|
22
|
+
const { agentDir, workspace } = resolvePlacement(dir);
|
|
23
|
+
const { config, path: configPath } = await loadConfig(agentDir);
|
|
24
|
+
const modelSpec = resolveModelSpec(options.model, config);
|
|
25
|
+
if (!modelSpec) {
|
|
26
|
+
throw new Error(`missing model: set --model, "model" in fastagent.config.ts, or FASTAGENT_MODEL (e.g. "openai-codex/gpt-5.5")`);
|
|
27
|
+
}
|
|
28
|
+
const { tools, toolNames, deferredToolNames, toolCollisions, toolFailures } = await resolveAgentTools(config, agentDir, workspace);
|
|
29
|
+
// The state root: sessions/channel state/schedule state derive from it (FASTAGENT_STATE_DIR moves it
|
|
30
|
+
// in one knob — a container points it at its volume); the finer overrides below still win.
|
|
31
|
+
const stateRoot = resolveStateRoot(agentDir);
|
|
32
|
+
// The credentials file: project-level by default (under `<agentDir>/.secrets`); only resolved here, never
|
|
33
|
+
// created (a missing file reads as not-configured — `fastagent login` creates it).
|
|
34
|
+
const authPath = resolveAuthPath(agentDir, options.authPath);
|
|
35
|
+
return {
|
|
36
|
+
config,
|
|
37
|
+
configPath,
|
|
38
|
+
modelSpec,
|
|
39
|
+
agentDir,
|
|
40
|
+
workspace,
|
|
41
|
+
stateRoot,
|
|
42
|
+
authPath,
|
|
43
|
+
tools,
|
|
44
|
+
toolNames,
|
|
45
|
+
deferredToolNames,
|
|
46
|
+
toolCollisions,
|
|
47
|
+
toolFailures,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* "Point at a directory → agent": resolve the placement (`dir` may be either end — the workspace or
|
|
52
|
+
* the agent dir itself), load the config, resolve model and tools, then L2. Throws a clear error when
|
|
53
|
+
* no model source is set (fail visibly at startup). Returns everything an entry point needs to report
|
|
54
|
+
* what it assembled.
|
|
55
|
+
*/
|
|
56
|
+
export async function createPiAgentFromDir(dir, options = {}) {
|
|
57
|
+
const { config, configPath, modelSpec, agentDir, workspace, stateRoot, authPath, tools, toolNames, deferredToolNames, toolCollisions, toolFailures, } = await resolveAgentAssembly(dir, options);
|
|
58
|
+
// Mount the built-in `wake` tool only when BOTH: this is a long-running serve (the poller honors it) AND
|
|
59
|
+
// the author opted into self-scheduling (config.selfSchedule). The agent's own `wake` wins if defined.
|
|
60
|
+
const mountedTools = withWakeTool(tools, stateRoot, !!options.serving && !!config.selfSchedule);
|
|
61
|
+
const sessionsDir = options.sessionsDir ?? defaultSessionsDir(stateRoot);
|
|
62
|
+
await mkdir(sessionsDir, { recursive: true });
|
|
63
|
+
const sessions = jsonlSessionStore({ dir: sessionsDir, cwd: workspace });
|
|
64
|
+
// The hub is wired HERE because the store is created here: chicken-and-egg otherwise (the hub
|
|
65
|
+
// needs the store; the agent needs the hub's observer). Boundary parts (models/factory/lease)
|
|
66
|
+
// only exist after the assembly below — the hub takes them as a lazy thunk, filled by the
|
|
67
|
+
// assembly's onAssembly callback (assembly completes before this function returns, so every
|
|
68
|
+
// dispatch sees them). An extra caller observer composes after the hub's (TRUSTED seam).
|
|
69
|
+
let boundaryParts;
|
|
70
|
+
const caller = options.observer;
|
|
71
|
+
const wantControl = options.sessionControl ?? (config.sessionControl === true && options.serving === true);
|
|
72
|
+
const hub = wantControl
|
|
73
|
+
? createPiSessionControl({
|
|
74
|
+
sessions,
|
|
75
|
+
boundary: () => boundaryParts,
|
|
76
|
+
// The caller tap's boundary-event half: state_changed/compaction_* originate in the hub
|
|
77
|
+
// and never cross the data plane's observer seam — without this, an audit tap wired here
|
|
78
|
+
// would miss exactly the mutations it most needs to see (set_model).
|
|
79
|
+
tap: caller ? (session, event) => caller(session, event) : undefined,
|
|
80
|
+
})
|
|
81
|
+
: undefined;
|
|
82
|
+
const observer = hub
|
|
83
|
+
? caller
|
|
84
|
+
? (session, event, run) => {
|
|
85
|
+
hub.observer(session, event, run);
|
|
86
|
+
caller(session, event, run);
|
|
87
|
+
}
|
|
88
|
+
: hub.observer
|
|
89
|
+
: caller;
|
|
90
|
+
const { agent, definition } = await createPiAgentFromDefinition(agentDir, {
|
|
91
|
+
model: modelSpec,
|
|
92
|
+
thinkingLevel: config.thinkingLevel,
|
|
93
|
+
cwd: workspace,
|
|
94
|
+
tools: mountedTools,
|
|
95
|
+
authPath,
|
|
96
|
+
// Skills are definition-only (the agent is its directory), so dev mirrors deployment exactly.
|
|
97
|
+
sessions,
|
|
98
|
+
observer,
|
|
99
|
+
onAssembly: hub
|
|
100
|
+
? (parts) => {
|
|
101
|
+
boundaryParts = {
|
|
102
|
+
lease: parts.lease,
|
|
103
|
+
models: parts.models,
|
|
104
|
+
harnessFactory: parts.harnessFactory,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
: undefined,
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
agent,
|
|
111
|
+
definition,
|
|
112
|
+
sessions,
|
|
113
|
+
sessionControl: hub?.control,
|
|
114
|
+
agentDir,
|
|
115
|
+
workspace,
|
|
116
|
+
config,
|
|
117
|
+
configPath,
|
|
118
|
+
modelSpec,
|
|
119
|
+
stateRoot,
|
|
120
|
+
sessionsDir,
|
|
121
|
+
authPath,
|
|
122
|
+
toolNames,
|
|
123
|
+
deferredToolNames,
|
|
124
|
+
toolCollisions,
|
|
125
|
+
toolFailures,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AgentSessionRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export interface BuildSessionRuntimeOptions {
|
|
3
|
+
/** Model spec override (the CLI --model flag). Precedence: this > FASTAGENT_MODEL > config.model. */
|
|
4
|
+
model?: string;
|
|
5
|
+
/** Credentials file override (the CLI --auth-path flag). Precedence: this > FASTAGENT_AUTH_PATH >
|
|
6
|
+
* the agent's default `<agentDir>/.secrets/auth.json`. */
|
|
7
|
+
authPath?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Build pi's interactive runtime driven by fastagent's assembled agent (model, prompt, tools,
|
|
11
|
+
* skills, and auth resolved exactly as the serving opener does). Split from the TUI launcher so the
|
|
12
|
+
* assembly — the fidelity-critical part — is inspectable and reusable without launching a TUI.
|
|
13
|
+
*/
|
|
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>;
|