@fastagent-sh/fastagent 0.22.1 → 0.24.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/dist/agent.d.ts +10 -0
- package/dist/channels/agentcore-protocol.d.ts +2 -1
- package/dist/channels/agentcore-service.js +10 -18
- package/dist/channels/agentcore.js +6 -3
- package/dist/channels/control.js +23 -0
- package/dist/channels/kit/preview-kit.d.ts +6 -1
- package/dist/channels/kit/preview-kit.js +21 -4
- package/dist/channels/slack/config-api.d.ts +2 -2
- package/dist/channels/slack/config-api.js +2 -2
- package/dist/channels/slack/onboard.js +2 -2
- package/dist/channels/slack/preview.js +2 -1
- package/dist/channels/slack/register-webhook.js +2 -2
- package/dist/channels/slack/slack-api.js +4 -4
- package/dist/cli/add-feishu.js +4 -2
- package/dist/cli/auth-view.d.ts +15 -6
- package/dist/cli/auth-view.js +24 -8
- package/dist/cli/box-login.d.ts +33 -0
- package/dist/cli/box-login.js +162 -0
- package/dist/cli/commands/deploy/agentcore.js +48 -11
- package/dist/cli/commands/deploy/docker.js +24 -13
- package/dist/cli/commands/deploy/fly.js +30 -7
- package/dist/cli/commands/deploy/railway.js +21 -7
- package/dist/cli/commands/deploy/shared.d.ts +18 -17
- package/dist/cli/commands/deploy/shared.js +20 -21
- package/dist/cli/commands/destroy.js +2 -2
- package/dist/cli/commands/info.js +15 -12
- package/dist/cli/commands/invoke.js +2 -2
- package/dist/cli/commands/login.d.ts +7 -1
- package/dist/cli/commands/login.js +123 -10
- package/dist/cli/commands/logs.js +2 -2
- package/dist/cli/commands/models.d.ts +4 -1
- package/dist/cli/commands/models.js +37 -4
- package/dist/cli/commands/routine-run.js +2 -2
- package/dist/cli/commands/start.d.ts +0 -5
- package/dist/cli/commands/start.js +3 -13
- package/dist/cli/invoke-stream.js +14 -0
- package/dist/cli/kernel.d.ts +2 -0
- package/dist/cli/kernel.js +1 -1
- package/dist/cli/login-relay.d.ts +38 -0
- package/dist/cli/login-relay.js +121 -0
- package/dist/cli/program.js +46 -8
- package/dist/cli/shared.d.ts +6 -5
- package/dist/cli/shared.js +29 -36
- package/dist/deploy/agentcore/plan.d.ts +10 -11
- package/dist/deploy/agentcore/plan.js +8 -14
- package/dist/deploy/agentcore/run.d.ts +8 -1
- package/dist/deploy/agentcore/run.js +20 -14
- package/dist/deploy/agentcore/shell.d.ts +25 -0
- package/dist/deploy/agentcore/shell.js +202 -0
- package/dist/deploy/box-shell.d.ts +43 -0
- package/dist/deploy/box-shell.js +28 -0
- package/dist/deploy/build-context.js +16 -7
- package/dist/deploy/channel-ingress.d.ts +20 -1
- package/dist/deploy/channel-ingress.js +34 -7
- package/dist/deploy/container.d.ts +14 -0
- package/dist/deploy/container.js +45 -1
- package/dist/deploy/docker/plan.d.ts +7 -3
- package/dist/deploy/docker/plan.js +9 -14
- package/dist/deploy/docker/run.d.ts +14 -7
- package/dist/deploy/docker/run.js +58 -35
- package/dist/deploy/fly/plan.d.ts +6 -3
- package/dist/deploy/fly/plan.js +6 -8
- package/dist/deploy/fly/run.d.ts +8 -2
- package/dist/deploy/fly/run.js +15 -25
- package/dist/deploy/preflight.d.ts +9 -7
- package/dist/deploy/preflight.js +92 -20
- package/dist/deploy/railway/plan.d.ts +6 -3
- package/dist/deploy/railway/plan.js +7 -9
- package/dist/deploy/railway/run.d.ts +10 -6
- package/dist/deploy/railway/run.js +29 -13
- package/dist/deploy/runner.d.ts +16 -0
- package/dist/deploy/runner.js +31 -0
- package/dist/deploy/secrets.d.ts +4 -13
- package/dist/deploy/secrets.js +8 -27
- package/dist/dev-supervisor.js +7 -4
- package/dist/engines/pi/agent-models.d.ts +83 -0
- package/dist/engines/pi/agent-models.js +120 -0
- package/dist/engines/pi/agent-session-factory.d.ts +13 -13
- package/dist/engines/pi/agent-session-factory.js +132 -159
- package/dist/engines/pi/auth.d.ts +56 -3
- package/dist/engines/pi/auth.js +68 -109
- package/dist/engines/pi/config.d.ts +0 -25
- package/dist/engines/pi/config.js +3 -37
- package/dist/engines/pi/create.d.ts +34 -21
- package/dist/engines/pi/create.js +90 -57
- package/dist/engines/pi/entry-images.d.ts +14 -0
- package/dist/engines/pi/entry-images.js +23 -0
- package/dist/engines/pi/invoke-session.js +88 -20
- package/dist/engines/pi/locked-file.d.ts +9 -0
- package/dist/engines/pi/locked-file.js +102 -0
- package/dist/engines/pi/login.d.ts +27 -14
- package/dist/engines/pi/login.js +41 -18
- package/dist/engines/pi/machine.d.ts +23 -0
- package/dist/engines/pi/machine.js +24 -2
- package/dist/engines/pi/models.d.ts +120 -36
- package/dist/engines/pi/models.js +316 -66
- package/dist/engines/pi/open.d.ts +65 -20
- package/dist/engines/pi/open.js +88 -34
- package/dist/engines/pi/report.d.ts +3 -0
- package/dist/engines/pi/report.js +11 -0
- package/dist/engines/pi/service.d.ts +2 -2
- package/dist/engines/pi/service.js +1 -0
- package/dist/engines/pi/session-builder.d.ts +3 -2
- package/dist/engines/pi/session-builder.js +30 -76
- package/dist/engines/pi/session-control.d.ts +4 -4
- package/dist/engines/pi/session-control.js +86 -43
- package/dist/engines/pi/session-settings.d.ts +9 -0
- package/dist/engines/pi/session-settings.js +17 -0
- package/dist/engines/pi/session-store.d.ts +2 -2
- package/dist/engines/pi/tool-context.d.ts +3 -5
- package/dist/engines/pi/tool-context.js +4 -16
- package/dist/engines/pi/tool.d.ts +32 -21
- package/dist/engines/pi/tool.js +21 -25
- package/dist/engines/pi/turn-kit.d.ts +18 -9
- package/dist/engines/pi/turn-kit.js +115 -32
- package/dist/open-url.d.ts +15 -1
- package/dist/open-url.js +35 -3
- package/dist/paths.d.ts +4 -2
- package/dist/paths.js +6 -2
- package/dist/pi.d.ts +5 -3
- package/dist/pi.js +3 -2
- package/dist/service.d.ts +5 -8
- package/dist/session-remote.js +21 -0
- package/dist/session.d.ts +88 -15
- package/dist/session.js +5 -1
- package/package.json +6 -5
- package/dist/engines/pi/search-tools.d.ts +0 -4
- package/dist/engines/pi/search-tools.js +0 -109
package/dist/deploy/secrets.js
CHANGED
|
@@ -30,7 +30,7 @@ const DEPLOY_OWNED = new Set([
|
|
|
30
30
|
]);
|
|
31
31
|
/** Is `name` one the deployment sets itself (see {@link DEPLOY_OWNED}), including the chunked carriers? */
|
|
32
32
|
function isDeployOwned(name) {
|
|
33
|
-
return DEPLOY_OWNED.has(name) || /^
|
|
33
|
+
return DEPLOY_OWNED.has(name) || /^FASTAGENT_ENV(_\d+)?$/.test(name);
|
|
34
34
|
}
|
|
35
35
|
/** The value-file variables that travel: everything with a value that the deployment does not set itself. */
|
|
36
36
|
function carriedValues(values) {
|
|
@@ -59,36 +59,25 @@ export function deploymentSecrets(modelAuth, declared, values, valueFile) {
|
|
|
59
59
|
return secrets;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
* Assemble the VALUES a `--run` deploy sets on the host: every variable the value file carries
|
|
63
|
-
* credential. Never minted here.
|
|
62
|
+
* Assemble the VALUES a `--run` deploy sets on the host: every variable the value file carries. Never minted here.
|
|
64
63
|
*
|
|
65
64
|
* `values` is the selected value file, read as data — **the environment running `deploy` is not a source**. A
|
|
66
65
|
* deployment must be reproducible from what it carries, and a variable that happens to be exported on the builder is
|
|
67
66
|
* written down nowhere. CI supplies values by writing the file before running the command.
|
|
67
|
+
*
|
|
68
|
+
* No credentials file travels: a model that does not authenticate through a variable (or its own definition) logs in
|
|
69
|
+
* on the deployment itself (`fastagent login --deployment`), so the box is the only holder of its grant.
|
|
68
70
|
*/
|
|
69
71
|
export function assembleSecrets(input) {
|
|
70
72
|
const secrets = carriedValues(input.values);
|
|
71
73
|
const missingSecrets = [];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (!secrets[input.modelAuth])
|
|
75
|
-
missingSecrets.push(input.modelAuth); // `.env` remediation fits
|
|
76
|
-
}
|
|
77
|
-
else if (input.authFile) {
|
|
78
|
-
secrets.FASTAGENT_AUTH_SEED = input.authFile.toString("base64");
|
|
79
|
-
}
|
|
80
|
-
else if (input.modelKeyInDefinition) {
|
|
81
|
-
// The definition authenticates itself (models.json literal key, or a command run on the host), so it travels in
|
|
82
|
-
// the image with everything else.
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
needsModelCredential = true; // no env key, no auth.json — `fastagent login` remediation
|
|
86
|
-
}
|
|
74
|
+
if (isEnvKey(input.modelAuth) && !secrets[input.modelAuth])
|
|
75
|
+
missingSecrets.push(input.modelAuth);
|
|
87
76
|
for (const { name } of dedupeSecrets(input.declared ?? [])) {
|
|
88
77
|
if (!secrets[name] && !missingSecrets.includes(name))
|
|
89
78
|
missingSecrets.push(name);
|
|
90
79
|
}
|
|
91
|
-
return { secrets, missingSecrets
|
|
80
|
+
return { secrets, missingSecrets };
|
|
92
81
|
}
|
|
93
82
|
/**
|
|
94
83
|
* The ONE refusal for declared names the deployed environment does not supply a value for — every host reaches it
|
|
@@ -101,10 +90,6 @@ export function missingValuesGate(missing, valueFile) {
|
|
|
101
90
|
return (`no value for: ${missing.join(", ")} — the deployed environment is declared by ${valueFile}, and this deploy ` +
|
|
102
91
|
`reads only that file (exporting the variable here does not reach the deployment). Add them there and re-run`);
|
|
103
92
|
}
|
|
104
|
-
/** The bytes to seed to the auth file, or undefined to leave it alone. */
|
|
105
|
-
export function authSeedBytes(seed, fileExists) {
|
|
106
|
-
return !seed || fileExists ? undefined : Buffer.from(seed, "base64");
|
|
107
|
-
}
|
|
108
93
|
/**
|
|
109
94
|
* Collect a (possibly CHUNKED) carrier from the environment: `<name>` plus numbered continuations (`_2`, `_3`, …)
|
|
110
95
|
* concatenated in order. A host whose env values have a length cap splits a long value this way.
|
|
@@ -122,10 +107,6 @@ function collectChunked(env, name) {
|
|
|
122
107
|
}
|
|
123
108
|
return value;
|
|
124
109
|
}
|
|
125
|
-
/** `FASTAGENT_AUTH_SEED` (+ `_2`, …): the base64 auth.json a deploy carries. */
|
|
126
|
-
export function collectAuthSeed(env) {
|
|
127
|
-
return collectChunked(env, "FASTAGENT_AUTH_SEED");
|
|
128
|
-
}
|
|
129
110
|
/**
|
|
130
111
|
* `FASTAGENT_ENV` (+ `_2`, …): the carried variables as ONE base64 JSON object, for a host whose deployment artifact
|
|
131
112
|
* must not depend on which names the value file holds (AgentCore's template is committed and gated on drift). The
|
package/dist/dev-supervisor.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { relative, sep } from "node:path";
|
|
7
7
|
import { watch as watchTree } from "chokidar";
|
|
8
|
-
import { AGENT_CONFIG_FILE, AGENT_MODELS_FILE, resolveStateRoot, isUnderDir } from "./paths.js";
|
|
8
|
+
import { AGENT_CONFIG_FILE, AGENT_MODEL_CATALOG_FILE, AGENT_MODELS_FILE, resolveStateRoot, isUnderDir, } from "./paths.js";
|
|
9
9
|
import { dotEnvPath } from "./env.js";
|
|
10
10
|
import { log } from "./log.js";
|
|
11
11
|
import { openExternalUrl } from "./open-url.js";
|
|
@@ -14,7 +14,7 @@ import { announceWebhooks, startCloudflareTunnel } from "./tunnel.js";
|
|
|
14
14
|
/** What the dev watcher restarts on (agent-dir-relative): the process-bound code inputs only. */
|
|
15
15
|
/** The agent-dir directories loaded ONCE per worker: a restart is their only re-read. */
|
|
16
16
|
const CODE_INPUT_DIRS = ["tools", "channels", "routines", "extensions"];
|
|
17
|
-
const WATCHED_HINT = `${CODE_INPUT_DIRS.map((dir) => `${dir}/`).join(", ")}, package.json, fastagent.config.ts, models.json, .secrets/.env`;
|
|
17
|
+
const WATCHED_HINT = `${CODE_INPUT_DIRS.map((dir) => `${dir}/`).join(", ")}, package.json, fastagent.config.ts, models.json, models-store.json, .secrets/.env`;
|
|
18
18
|
/** chokidar `ignored` matcher for the narrow watch scope (true = ignore), rooted at the AGENT DIR. */
|
|
19
19
|
export function devWatchIgnored(root, envFile) {
|
|
20
20
|
// The `.env` is allow-listed by its RESOLVED path, not by the `.secrets` name.
|
|
@@ -29,10 +29,13 @@ export function devWatchIgnored(root, envFile) {
|
|
|
29
29
|
return false;
|
|
30
30
|
if (rel === "package.json")
|
|
31
31
|
return false;
|
|
32
|
-
// models.json is read ONCE per worker (
|
|
33
|
-
// any other code input.
|
|
32
|
+
// models.json is read ONCE per worker (agentModels' snapshot, which every session's runtime is built from), so an
|
|
33
|
+
// edit needs a restart like any other code input.
|
|
34
34
|
if (rel === AGENT_MODELS_FILE)
|
|
35
35
|
return false;
|
|
36
|
+
// So is the model catalog: a `models --refresh` during `dev` restarts the worker onto the new models.
|
|
37
|
+
if (rel === AGENT_MODEL_CATALOG_FILE)
|
|
38
|
+
return false;
|
|
36
39
|
const segments = rel.split(sep);
|
|
37
40
|
if (CODE_INPUT_DIRS.includes(segments[0]))
|
|
38
41
|
return false;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An agent's MODEL ENVIRONMENT as one value: the credential store its turns authenticate through, the registry they
|
|
3
|
+
* resolve models against, and what authenticates a provider there. Every reader builds it through {@link agentModels}
|
|
4
|
+
* — the opener and L2, the startup report and a deployed box's `login --if-missing`, `info`, the first-run picker,
|
|
5
|
+
* login, the model list — so none of them can read other credential layers or another registry than the runtime
|
|
6
|
+
* does. Each used to compose these parts itself, and two of those copies drifted from the rule (#636, #660).
|
|
7
|
+
*/
|
|
8
|
+
import type { ExecutionEnv } from "@earendil-works/pi-agent-core";
|
|
9
|
+
import type { Credential, CredentialStore, Models, Provider } from "@earendil-works/pi-ai";
|
|
10
|
+
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import { type AuthLayers, type CredentialSourceOptions, type FastagentAuthOptions } from "./auth.ts";
|
|
12
|
+
/** What authenticates one provider for an agent ({@link AgentModels.authStatus}). */
|
|
13
|
+
interface AuthStatus {
|
|
14
|
+
/** What satisfies it now (`OAuth`, `stored credential`, an env variable's name), after a due refresh. */
|
|
15
|
+
source?: string;
|
|
16
|
+
/** Why resolving it failed (a corrupt entry, a refresh the provider refused), when it did. */
|
|
17
|
+
error?: string;
|
|
18
|
+
/** The file that holds the stored credential, or the one a login would write; absent over a caller's store. */
|
|
19
|
+
path?: string;
|
|
20
|
+
/** The kind of credential the store holds for it, usable or not. */
|
|
21
|
+
stored?: Credential["type"];
|
|
22
|
+
/**
|
|
23
|
+
* An env variable that also authenticates it but goes unused, because pi lets a stored credential own its provider
|
|
24
|
+
* — a key added to a deployment's value file after the box was logged in, typically.
|
|
25
|
+
*/
|
|
26
|
+
shadowed?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentModels {
|
|
29
|
+
/** The credentials files behind {@link credentials}; absent when the caller supplied its own store. */
|
|
30
|
+
auth?: AuthLayers;
|
|
31
|
+
credentials: CredentialStore;
|
|
32
|
+
/**
|
|
33
|
+
* The registry: pi's built-ins, plus (with a directory) the agent's `models.json` and model catalog over the
|
|
34
|
+
* machine's, plus `providers` and extension model declarations. An unbound catalog, built on first use and shared
|
|
35
|
+
* after; session bindings use createRuntime and their own extension instances.
|
|
36
|
+
*/
|
|
37
|
+
runtime(): Promise<ModelRuntime>;
|
|
38
|
+
/** A session-local registry, before its extensions are loaded. Credentials remain shared. */
|
|
39
|
+
createRuntime(): Promise<ModelRuntime>;
|
|
40
|
+
/**
|
|
41
|
+
* What authenticates `provider` here, resolved exactly as a turn resolves it: the ONE answer the startup report and a
|
|
42
|
+
* deployed box's `login --if-missing` both give. `modelId` picks the model to probe with; the provider's first one
|
|
43
|
+
* otherwise (auth is provider-scoped).
|
|
44
|
+
*/
|
|
45
|
+
authStatus(provider: string, modelId?: string): Promise<AuthStatus>;
|
|
46
|
+
/**
|
|
47
|
+
* The definition's extension entry points, discovered once: what this catalog registers models from and what every
|
|
48
|
+
* session of the assembly built over it loads. Empty without a directory.
|
|
49
|
+
*/
|
|
50
|
+
extensionPaths(): Promise<readonly string[]>;
|
|
51
|
+
}
|
|
52
|
+
export interface AgentModelsOptions {
|
|
53
|
+
/** Workspace for extension model registration; defaults to agentDir for standalone definitions. */
|
|
54
|
+
cwd?: string;
|
|
55
|
+
/** Where `extensions/` is listed from; Node's filesystem by default. */
|
|
56
|
+
env?: ExecutionEnv;
|
|
57
|
+
/** Extra providers registered on top of the built-ins (a same id replaces one). */
|
|
58
|
+
providers?: readonly Provider[];
|
|
59
|
+
/**
|
|
60
|
+
* Layer the machine's models.json and model catalog under the agent's (default). Off for the registry a DEPLOYED
|
|
61
|
+
* agent has, since neither ships.
|
|
62
|
+
*/
|
|
63
|
+
machineLayer?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The model environment of the agent in `agentDir` (or, without one, of a caller with no directory: pi's built-ins
|
|
67
|
+
* over the named or global credentials file). A supplied `credentialStore` is used as given; otherwise the files:
|
|
68
|
+
* the directory's own layers ({@link resolveAuthLayers}), or the named file or the global one.
|
|
69
|
+
*/
|
|
70
|
+
export declare function agentModels(agentDir: string | undefined, source?: {
|
|
71
|
+
authPath?: string;
|
|
72
|
+
credentialStore?: undefined;
|
|
73
|
+
} & FastagentAuthOptions, options?: AgentModelsOptions): AgentModels & {
|
|
74
|
+
auth: AuthLayers;
|
|
75
|
+
};
|
|
76
|
+
export declare function agentModels(agentDir: string | undefined, source?: CredentialSourceOptions & FastagentAuthOptions, options?: AgentModelsOptions): AgentModels;
|
|
77
|
+
export interface CreatePiModelsOptions extends FastagentAuthOptions, CredentialSourceOptions {
|
|
78
|
+
/** Extra providers registered on top of the built-ins (same id overrides a built-in). */
|
|
79
|
+
providers?: Provider[];
|
|
80
|
+
}
|
|
81
|
+
/** A `Models` with every built-in pi provider, wired to fastagent's auth. */
|
|
82
|
+
export declare function createPiModels(options?: CreatePiModelsOptions): Models;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { definitionServices } from "./agent-session-factory.js";
|
|
3
|
+
import { loadExtensionPaths } from "./definition.js";
|
|
4
|
+
import { GLOBAL_AUTH_PATH, assertOneCredentialSource, fastagentCredentialStore, resolveAuthLayers, } from "./auth.js";
|
|
5
|
+
import { createPiModelRuntime, environmentAuthSource, modelRuntimeFiles, piModelsOver, probeAuthSource, } from "./models.js";
|
|
6
|
+
export function agentModels(agentDir, source = {}, options = {}) {
|
|
7
|
+
assertOneCredentialSource(source);
|
|
8
|
+
const { providers, machineLayer } = options;
|
|
9
|
+
const files = source.credentialStore
|
|
10
|
+
? undefined
|
|
11
|
+
: agentDir
|
|
12
|
+
? resolveAuthLayers(agentDir, source.authPath)
|
|
13
|
+
: { path: source.authPath ?? GLOBAL_AUTH_PATH };
|
|
14
|
+
// The model files, read once for every runtime this agent builds: a session's and the control plane's.
|
|
15
|
+
let modelFiles;
|
|
16
|
+
const readModelFiles = () => (modelFiles ??= modelRuntimeFiles({
|
|
17
|
+
...(agentDir ? { agentDir } : {}),
|
|
18
|
+
...(machineLayer !== undefined ? { machineLayer } : {}),
|
|
19
|
+
}));
|
|
20
|
+
const store = files
|
|
21
|
+
? agentCredentialStore(files, readModelFiles, { providers, warn: source.warn })
|
|
22
|
+
: undefined;
|
|
23
|
+
const credentials = source.credentialStore ?? store;
|
|
24
|
+
const createRuntime = async () => createPiModelRuntime({
|
|
25
|
+
credentials,
|
|
26
|
+
files: await readModelFiles(),
|
|
27
|
+
...(agentDir ? { agentDir } : {}),
|
|
28
|
+
...(providers ? { providers } : {}),
|
|
29
|
+
...(machineLayer !== undefined ? { machineLayer } : {}),
|
|
30
|
+
});
|
|
31
|
+
const cwd = options.cwd ?? agentDir;
|
|
32
|
+
let discovered;
|
|
33
|
+
const extensionPaths = () => (discovered ??= agentDir
|
|
34
|
+
? loadExtensionPaths(agentDir, { ...(cwd ? { cwd } : {}), ...(options.env ? { env: options.env } : {}) })
|
|
35
|
+
: Promise.resolve([]));
|
|
36
|
+
let registry;
|
|
37
|
+
const runtime = () => {
|
|
38
|
+
registry ??= createRuntime().then(async (models) => {
|
|
39
|
+
if (agentDir) {
|
|
40
|
+
await definitionServices({
|
|
41
|
+
cwd: cwd ?? agentDir,
|
|
42
|
+
modelRuntime: models,
|
|
43
|
+
definition: { skills: [] },
|
|
44
|
+
extensionPaths: await extensionPaths(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return models;
|
|
48
|
+
});
|
|
49
|
+
return registry;
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
...(files ? { auth: files } : {}),
|
|
53
|
+
credentials,
|
|
54
|
+
runtime,
|
|
55
|
+
createRuntime,
|
|
56
|
+
extensionPaths,
|
|
57
|
+
async authStatus(provider, modelId) {
|
|
58
|
+
const models = await runtime();
|
|
59
|
+
const id = modelId ?? models.getProvider(provider)?.getModels()[0]?.id;
|
|
60
|
+
let resolved = {};
|
|
61
|
+
if (id !== undefined) {
|
|
62
|
+
try {
|
|
63
|
+
const found = await probeAuthSource(models, `${provider}/${id}`);
|
|
64
|
+
if (found !== undefined)
|
|
65
|
+
resolved = { source: found };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
// The first line: pi folds the request's stack into the message of a failed refresh.
|
|
69
|
+
resolved = { error: String(error.message).split("\n")[0] };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// `read` never refreshes: this is the kind on file, whatever became of it.
|
|
73
|
+
const stored = (await credentials.read(provider))?.type;
|
|
74
|
+
const fromEnvironment = stored === undefined ? undefined : await environmentAuthSource(provider, process.env);
|
|
75
|
+
const shadowed = resolved.source !== undefined && fromEnvironment !== resolved.source ? fromEnvironment : undefined;
|
|
76
|
+
return {
|
|
77
|
+
...(store ? { path: await store.layerOf(provider) } : {}),
|
|
78
|
+
...resolved,
|
|
79
|
+
...(stored !== undefined ? { stored } : {}),
|
|
80
|
+
...(shadowed !== undefined ? { shadowed } : {}),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** A `Models` with every built-in pi provider, wired to fastagent's auth. */
|
|
86
|
+
export function createPiModels(options = {}) {
|
|
87
|
+
return piModelsOver(agentModels(undefined, options).credentials, options.providers);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The credential store an agent reads through: its own file, then the user-global one for a provider the PROJECT
|
|
91
|
+
* authenticates no other way (its own auth file, a models.json key, or the environment). "No other way" is answered
|
|
92
|
+
* by pi's own resolution over a registry that has no global layer, built the first time a provider is looked up
|
|
93
|
+
* there. A deployment never has the global file, so a global login that outranked, say, an `ANTHROPIC_API_KEY` in
|
|
94
|
+
* `.secrets/.env` would run one credential here and a different one deployed.
|
|
95
|
+
*/
|
|
96
|
+
function agentCredentialStore(auth, modelFiles, options) {
|
|
97
|
+
const { warn, providers = [] } = options;
|
|
98
|
+
const fallback = auth.fallback;
|
|
99
|
+
if (fallback === undefined)
|
|
100
|
+
return fastagentCredentialStore(auth.path, { warn });
|
|
101
|
+
let project;
|
|
102
|
+
const projectRuntime = () => {
|
|
103
|
+
project ??= (async () => {
|
|
104
|
+
const runtime = await ModelRuntime.create({
|
|
105
|
+
credentials: fastagentCredentialStore(auth.path, { warn }),
|
|
106
|
+
...(await (await modelFiles()).create()),
|
|
107
|
+
refreshOnCreate: false,
|
|
108
|
+
});
|
|
109
|
+
for (const provider of providers)
|
|
110
|
+
runtime.registerNativeProvider(provider);
|
|
111
|
+
return runtime;
|
|
112
|
+
})();
|
|
113
|
+
return project;
|
|
114
|
+
};
|
|
115
|
+
return fastagentCredentialStore(auth.path, {
|
|
116
|
+
warn,
|
|
117
|
+
fallbackPath: fallback,
|
|
118
|
+
projectAuthenticates: async (providerId) => (await (await projectRuntime()).checkAuth(providerId)) !== undefined,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { Skill, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
|
3
3
|
import { type AgentSession, type AgentSessionServices, type CompactionResult, type CreateAgentSessionServicesOptions, type ModelRuntime, type ResolvedCommand, SessionManager, createAgentSessionFromServices } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import type { PiAgentSessionFactory } from "./invoke-session.ts";
|
|
5
5
|
import type { PiSessionRecordStore } from "./session-store.ts";
|
|
6
|
-
import {
|
|
6
|
+
import type { MountedTool } from "./tool.ts";
|
|
7
7
|
import { type AnyModel } from "./models.ts";
|
|
8
8
|
interface PiSessionDefinition {
|
|
9
9
|
systemPrompt?: string;
|
|
@@ -12,11 +12,12 @@ interface PiSessionDefinition {
|
|
|
12
12
|
export interface PiAgentSessionFactoryOptions {
|
|
13
13
|
/** Where conversations live. */
|
|
14
14
|
sessions: PiSessionRecordStore;
|
|
15
|
-
/**
|
|
15
|
+
/** A fresh model runtime per binding: extension routers and provider registrations belong to this session. */
|
|
16
16
|
engine: () => Promise<{
|
|
17
17
|
modelRuntime: ModelRuntime;
|
|
18
|
-
model: AnyModel;
|
|
19
18
|
}>;
|
|
19
|
+
/** Resolved after extensions register their models. */
|
|
20
|
+
modelSpec: string;
|
|
20
21
|
thinkingLevel?: ThinkingLevel;
|
|
21
22
|
tools?: MountedTool[];
|
|
22
23
|
/** Read once per binding; prompt and skills come from the same definition read. */
|
|
@@ -42,19 +43,11 @@ export interface BindPiSessionOptions {
|
|
|
42
43
|
excludedToolNames?: readonly string[];
|
|
43
44
|
/** The CALLER's session id — what a tool asking which conversation it is in hears. */
|
|
44
45
|
sessionId?: string;
|
|
45
|
-
/** Record each discovered activation on the session, so the next bind restores it. */
|
|
46
|
-
recordActivations: boolean;
|
|
47
46
|
}
|
|
48
47
|
/**
|
|
49
|
-
* Bind ONE pi session to a record:
|
|
50
|
-
* copies kept off, and deferral applied.
|
|
48
|
+
* Bind ONE pi session to a record: authored tools replace same-name built-ins; Pi owns exposure and discovery.
|
|
51
49
|
*/
|
|
52
50
|
export declare function bindPiSession(options: BindPiSessionOptions): ReturnType<typeof createAgentSessionFromServices>;
|
|
53
|
-
/**
|
|
54
|
-
* Announce extensions pi failed to load. pi collects them into `LoadExtensionsResult.errors` and carries on with the
|
|
55
|
-
* rest.
|
|
56
|
-
*/
|
|
57
|
-
export declare function reportExtensionErrors(services: AgentSessionServices): void;
|
|
58
51
|
/** What pi is allowed to discover, minus the parts each assembly fills in itself. */
|
|
59
52
|
type DefinitionLoaderOptions = NonNullable<CreateAgentSessionServicesOptions["resourceLoaderOptions"]>;
|
|
60
53
|
/** The resource posture a fastagent definition asks pi for — ONE definition of it, for both assemblies. */
|
|
@@ -81,9 +74,16 @@ export declare function startCompaction(session: AgentSession, instructions: str
|
|
|
81
74
|
admission: Promise<CompactAdmission>;
|
|
82
75
|
done: Promise<CompactionResult>;
|
|
83
76
|
};
|
|
77
|
+
/** A fresh loader and Pi's native model registration, over a session-local runtime. */
|
|
78
|
+
export declare function definitionServices(options: {
|
|
79
|
+
cwd: string;
|
|
80
|
+
modelRuntime: ModelRuntime;
|
|
81
|
+
definition: PiSessionDefinition;
|
|
82
|
+
extensionPaths: readonly string[];
|
|
83
|
+
}): Promise<AgentSessionServices>;
|
|
84
84
|
/**
|
|
85
85
|
* The `/name` commands a served session dispatches, named the way pi resolves them (a name two extensions share gets
|
|
86
|
-
* a `:N` suffix). Loaded through the same {@link
|
|
86
|
+
* a `:N` suffix). Loaded through the same {@link definitionServices} a turn binds, so the menu and the dispatch cannot
|
|
87
87
|
* disagree. Loading runs the extensions' factories; no session opens, so `session_start` does not fire, and the
|
|
88
88
|
* instances are never bound (any action they call throws).
|
|
89
89
|
*/
|