@fastagent-sh/fastagent 0.28.0 → 0.30.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 +10 -4
- package/dist/atomic-write.d.ts +4 -5
- package/dist/atomic-write.js +4 -5
- package/dist/authoring.d.ts +32 -15
- package/dist/authoring.js +147 -127
- package/dist/channels/agentcore-protocol.d.ts +8 -3
- package/dist/channels/agentcore-protocol.js +3 -1
- package/dist/channels/agentcore-service.d.ts +1 -0
- package/dist/channels/agentcore-service.js +14 -4
- package/dist/channels/agentcore.d.ts +9 -0
- package/dist/channels/agentcore.js +29 -4
- package/dist/channels/context.d.ts +2 -0
- package/dist/channels/context.js +5 -1
- package/dist/channels/feishu/feishu-api.d.ts +3 -1
- package/dist/channels/feishu/feishu-api.js +4 -0
- package/dist/channels/feishu/feishu.d.ts +0 -3
- package/dist/channels/feishu/feishu.js +10 -10
- package/dist/channels/feishu/register-app.d.ts +2 -0
- package/dist/channels/feishu/register-app.js +4 -6
- package/dist/channels/feishu/scaffold/channel.ts +32 -34
- package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
- package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
- package/dist/channels/feishu/setup-mode.d.ts +2 -12
- package/dist/channels/feishu/setup-mode.js +2 -22
- package/dist/channels/feishu/setup.d.ts +12 -0
- package/dist/channels/feishu/setup.js +197 -0
- package/dist/channels/kit/abortable.d.ts +8 -0
- package/dist/channels/kit/abortable.js +22 -0
- package/dist/channels/lark/lark.d.ts +0 -3
- package/dist/channels/lark/lark.js +0 -5
- package/dist/channels/lark/scaffold/channel.ts +33 -35
- package/dist/channels/lark/scaffold/lark-send.ts +5 -5
- package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
- package/dist/channels/lark/setup.d.ts +9 -0
- package/dist/channels/lark/setup.js +57 -0
- package/dist/channels/setup.d.ts +112 -0
- package/dist/channels/setup.js +31 -0
- package/dist/channels/slack/history.js +1 -1
- package/dist/channels/slack/manifest.d.ts +17 -7
- package/dist/channels/slack/manifest.js +12 -16
- package/dist/channels/slack/scaffold/channel.ts +44 -30
- package/dist/channels/slack/scaffold/slack-send.ts +8 -8
- package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
- package/dist/channels/slack/setup.d.ts +7 -0
- package/dist/channels/slack/setup.js +119 -0
- package/dist/channels/slack/slack-api.d.ts +2 -0
- package/dist/channels/slack/slack-api.js +4 -0
- package/dist/channels/slack/slack.d.ts +21 -2
- package/dist/channels/slack/slack.js +398 -338
- package/dist/channels/slack/socket.d.ts +57 -0
- package/dist/channels/slack/socket.js +305 -0
- package/dist/channels/telegram/polling.d.ts +27 -0
- package/dist/channels/telegram/polling.js +105 -0
- package/dist/channels/telegram/scaffold/channel.ts +52 -32
- package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
- package/dist/channels/telegram/setup.d.ts +2 -0
- package/dist/channels/telegram/setup.js +42 -0
- package/dist/channels/telegram/telegram-api.d.ts +27 -1
- package/dist/channels/telegram/telegram-api.js +7 -6
- package/dist/channels/telegram/telegram.d.ts +11 -3
- package/dist/channels/telegram/telegram.js +184 -157
- package/dist/channels/wait-health.js +1 -1
- package/dist/channels/webhook-evidence.d.ts +11 -0
- package/dist/channels/webhook-evidence.js +71 -0
- package/dist/channels/webhooks.d.ts +37 -0
- package/dist/channels/webhooks.js +70 -0
- package/dist/child-env.d.ts +16 -0
- package/dist/child-env.js +30 -0
- package/dist/cli/box-login.js +4 -1
- package/dist/cli/channel-app.d.ts +3 -0
- package/dist/cli/channel-app.js +35 -0
- package/dist/cli/channel-setup.d.ts +38 -0
- package/dist/cli/channel-setup.js +195 -0
- package/dist/cli/commands/add.d.ts +6 -14
- package/dist/cli/commands/add.js +45 -310
- package/dist/cli/commands/auth.js +8 -2
- package/dist/cli/commands/build.d.ts +5 -0
- package/dist/cli/commands/build.js +16 -0
- package/dist/cli/commands/channels.d.ts +19 -0
- package/dist/cli/commands/channels.js +230 -0
- package/dist/cli/commands/content.d.ts +7 -3
- package/dist/cli/commands/content.js +16 -10
- package/dist/cli/commands/deploy/agentcore.js +55 -23
- package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
- package/dist/cli/commands/deploy/channel-apps.js +30 -0
- package/dist/cli/commands/deploy/docker.js +32 -65
- package/dist/cli/commands/deploy/fly.js +28 -12
- package/dist/cli/commands/deploy/railway.js +38 -13
- package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
- package/dist/cli/commands/deploy/secrets-step.js +66 -0
- package/dist/cli/commands/deploy/shared.d.ts +16 -5
- package/dist/cli/commands/deploy/shared.js +25 -8
- package/dist/cli/commands/deploy.js +53 -67
- package/dist/cli/commands/dev.d.ts +2 -0
- package/dist/cli/commands/dev.js +15 -3
- package/dist/cli/commands/env.js +1 -1
- package/dist/cli/commands/info.d.ts +2 -0
- package/dist/cli/commands/info.js +63 -24
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/invoke.d.ts +2 -0
- package/dist/cli/commands/invoke.js +20 -7
- package/dist/cli/commands/login.d.ts +0 -7
- package/dist/cli/commands/login.js +22 -10
- package/dist/cli/commands/logout.js +1 -1
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +164 -0
- package/dist/cli/commands/models.js +3 -2
- package/dist/cli/commands/schedules.d.ts +4 -2
- package/dist/cli/commands/schedules.js +14 -10
- package/dist/cli/commands/secrets.d.ts +14 -0
- package/dist/cli/commands/secrets.js +213 -0
- package/dist/cli/commands/start.d.ts +14 -14
- package/dist/cli/commands/start.js +46 -60
- package/dist/cli/content-view.d.ts +4 -6
- package/dist/cli/content-view.js +5 -11
- package/dist/cli/first-run-channels.d.ts +3 -0
- package/dist/cli/first-run-channels.js +63 -0
- package/dist/cli/instance.d.ts +41 -0
- package/dist/cli/instance.js +155 -0
- package/dist/cli/invoke-stream.d.ts +5 -2
- package/dist/cli/invoke-stream.js +7 -2
- package/dist/cli/program.js +339 -108
- package/dist/cli/serve.d.ts +4 -5
- package/dist/cli/serve.js +4 -7
- package/dist/cli/shared.d.ts +11 -2
- package/dist/cli/shared.js +32 -19
- package/dist/cli/ticket.js +50 -6
- package/dist/content/declare.d.ts +31 -37
- package/dist/content/declare.js +59 -60
- package/dist/content/file.d.ts +23 -12
- package/dist/content/file.js +48 -35
- package/dist/content/git.d.ts +0 -10
- package/dist/content/git.js +5 -39
- package/dist/content/resolve.d.ts +18 -22
- package/dist/content/resolve.js +32 -66
- package/dist/content/source.d.ts +7 -8
- package/dist/content/source.js +10 -28
- package/dist/credentials-page.d.ts +26 -0
- package/dist/credentials-page.js +185 -0
- package/dist/declared-secrets.d.ts +10 -3
- package/dist/declared-secrets.js +8 -4
- package/dist/deploy/agentcore/destroy.d.ts +4 -1
- package/dist/deploy/agentcore/destroy.js +26 -2
- package/dist/deploy/agentcore/forwarder.js +1 -1
- package/dist/deploy/agentcore/plan.d.ts +7 -21
- package/dist/deploy/agentcore/plan.js +47 -49
- package/dist/deploy/agentcore/run.d.ts +18 -10
- package/dist/deploy/agentcore/run.js +93 -70
- package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
- package/dist/deploy/agentcore/runtime-secrets.js +108 -0
- package/dist/deploy/agentcore/shell.js +12 -19
- package/dist/deploy/agentcore/sigv4.d.ts +7 -0
- package/dist/deploy/agentcore/sigv4.js +15 -0
- package/dist/deploy/agentcore/slr.d.ts +18 -0
- package/dist/deploy/agentcore/slr.js +27 -0
- package/dist/deploy/agentcore/store.d.ts +7 -0
- package/dist/deploy/agentcore/store.js +115 -0
- package/dist/deploy/api.d.ts +14 -14
- package/dist/deploy/api.js +26 -24
- package/dist/deploy/box-shell.d.ts +3 -1
- package/dist/deploy/build-context.d.ts +4 -4
- package/dist/deploy/build-context.js +11 -11
- package/dist/deploy/channel-ingress.d.ts +35 -9
- package/dist/deploy/channel-ingress.js +119 -22
- package/dist/deploy/container.d.ts +35 -10
- package/dist/deploy/container.js +80 -39
- package/dist/deploy/delivered-text.d.ts +13 -0
- package/dist/deploy/delivered-text.js +86 -0
- package/dist/deploy/docker/plan.d.ts +7 -8
- package/dist/deploy/docker/plan.js +31 -52
- package/dist/deploy/docker/run.d.ts +18 -43
- package/dist/deploy/docker/run.js +57 -79
- package/dist/deploy/file-store.d.ts +2 -0
- package/dist/deploy/file-store.js +17 -0
- package/dist/deploy/fly/plan.d.ts +3 -2
- package/dist/deploy/fly/plan.js +20 -9
- package/dist/deploy/fly/run.d.ts +18 -22
- package/dist/deploy/fly/run.js +25 -44
- package/dist/deploy/fly/store.d.ts +12 -0
- package/dist/deploy/fly/store.js +83 -0
- package/dist/deploy/hosts.d.ts +1 -2
- package/dist/deploy/hosts.js +1 -2
- package/dist/deploy/preflight.d.ts +18 -5
- package/dist/deploy/preflight.js +135 -63
- package/dist/deploy/railway/ignore.d.ts +8 -0
- package/dist/deploy/railway/ignore.js +57 -0
- package/dist/deploy/railway/plan.d.ts +1 -2
- package/dist/deploy/railway/plan.js +8 -6
- package/dist/deploy/railway/run.d.ts +21 -15
- package/dist/deploy/railway/run.js +32 -78
- package/dist/deploy/railway/store.d.ts +29 -0
- package/dist/deploy/railway/store.js +143 -0
- package/dist/deploy/runner.d.ts +5 -2
- package/dist/deploy/runner.js +4 -3
- package/dist/deploy/secret-store.d.ts +63 -0
- package/dist/deploy/secret-store.js +98 -0
- package/dist/deploy/secrets.d.ts +29 -46
- package/dist/deploy/secrets.js +49 -122
- package/dist/deploy/workspace.d.ts +6 -12
- package/dist/deploy/workspace.js +18 -94
- package/dist/dev-supervisor.js +6 -7
- package/dist/environment/declare.js +3 -2
- package/dist/environment/mise.d.ts +3 -1
- package/dist/environment/mise.js +4 -2
- package/dist/feishu.d.ts +1 -1
- package/dist/feishu.js +1 -1
- package/dist/harnesses/models/catalog.d.ts +1 -0
- package/dist/harnesses/models/catalog.js +1 -2
- package/dist/harnesses/models/environment.d.ts +7 -2
- package/dist/harnesses/models/environment.js +9 -5
- package/dist/harnesses/models/host.d.ts +0 -7
- package/dist/harnesses/models/host.js +10 -1
- package/dist/harnesses/models/login.d.ts +0 -8
- package/dist/harnesses/models/login.js +5 -4
- package/dist/harnesses/models/models.d.ts +7 -2
- package/dist/harnesses/models/models.js +28 -4
- package/dist/harnesses/pi-durable/agent.js +59 -38
- package/dist/harnesses/pi-durable/build.d.ts +12 -0
- package/dist/harnesses/pi-durable/build.js +270 -0
- package/dist/harnesses/pi-durable/built.d.ts +24 -0
- package/dist/harnesses/pi-durable/built.js +136 -0
- package/dist/harnesses/pi-durable/config.d.ts +81 -0
- package/dist/harnesses/pi-durable/config.js +266 -0
- package/dist/harnesses/pi-durable/define.d.ts +63 -13
- package/dist/harnesses/pi-durable/define.js +23 -2
- package/dist/harnesses/pi-durable/definition.d.ts +72 -0
- package/dist/harnesses/pi-durable/definition.js +143 -0
- package/dist/harnesses/pi-durable/events.d.ts +4 -3
- package/dist/harnesses/pi-durable/events.js +179 -136
- package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
- package/dist/harnesses/pi-durable/execution-env.js +11 -0
- package/dist/harnesses/pi-durable/mcp.d.ts +3 -0
- package/dist/harnesses/pi-durable/mcp.js +32 -0
- package/dist/harnesses/pi-durable/open.d.ts +36 -38
- package/dist/harnesses/pi-durable/open.js +97 -150
- package/dist/harnesses/pi-durable/reads.d.ts +0 -1
- package/dist/harnesses/pi-durable/reads.js +1 -3
- package/dist/harnesses/pi-durable/registry.d.ts +4 -6
- package/dist/harnesses/pi-durable/registry.js +29 -26
- package/dist/harnesses/pi-durable/release.d.ts +2 -2
- package/dist/harnesses/pi-durable/release.js +10 -7
- package/dist/harnesses/pi-durable/serve.d.ts +95 -15
- package/dist/harnesses/pi-durable/serve.js +151 -59
- package/dist/harnesses/pi-durable/sessions.js +15 -12
- package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
- package/dist/harnesses/pi-durable/text-sources.js +55 -0
- package/dist/harnesses/pi-durable/text.d.ts +16 -4
- package/dist/harnesses/pi-durable/text.js +115 -59
- package/dist/harnesses/pi-durable/wake.js +8 -8
- package/dist/http/client.js +9 -3
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +8 -1
- package/dist/instance-name.d.ts +7 -0
- package/dist/instance-name.js +26 -0
- package/dist/lark.d.ts +1 -1
- package/dist/lark.js +1 -1
- package/dist/locked-file.d.ts +2 -2
- package/dist/locked-file.js +8 -3
- package/dist/mcp/auth.d.ts +22 -0
- package/dist/mcp/auth.js +180 -0
- package/dist/mcp/client.d.ts +15 -0
- package/dist/mcp/client.js +179 -0
- package/dist/mcp/config.d.ts +86 -0
- package/dist/mcp/config.js +225 -0
- package/dist/mcp/grants.d.ts +4 -0
- package/dist/mcp/grants.js +132 -0
- package/dist/mcp/host.d.ts +19 -0
- package/dist/mcp/host.js +128 -0
- package/dist/mcp/network.d.ts +12 -0
- package/dist/mcp/network.js +150 -0
- package/dist/mcp/sign-in.d.ts +2 -0
- package/dist/mcp/sign-in.js +204 -0
- package/dist/mcp-api.d.ts +107 -0
- package/dist/mcp-api.js +10 -0
- package/dist/paths.d.ts +22 -8
- package/dist/paths.js +44 -11
- package/dist/runtime-websocket.d.ts +14 -0
- package/dist/runtime-websocket.js +39 -0
- package/dist/scaffold/add-channel.d.ts +9 -16
- package/dist/scaffold/add-channel.js +13 -83
- package/dist/scaffold/templates/env.example +4 -5
- package/dist/scaffold/templates/fastagent.config.ts +20 -14
- package/dist/scaffold/templates/gitignore +9 -6
- package/dist/scaffold/templates.d.ts +6 -2
- package/dist/scaffold/templates.js +7 -2
- package/dist/scaffold/vendor-skill.js +4 -2
- package/dist/service.d.ts +44 -6
- package/dist/service.js +110 -10
- package/dist/serving-command.d.ts +10 -0
- package/dist/serving-command.js +1 -0
- package/dist/setup-page.d.ts +51 -0
- package/dist/setup-page.js +149 -0
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/dist/strip-edges.d.ts +10 -0
- package/dist/strip-edges.js +22 -0
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +1 -1
- package/dist/tunnel.d.ts +0 -1
- package/dist/tunnel.js +2 -8
- package/dist/value-file.d.ts +12 -6
- package/dist/value-file.js +36 -25
- package/dist/version.d.ts +7 -1
- package/dist/version.js +9 -3
- package/package.json +8 -6
- package/dist/channels/lark/onboard.d.ts +0 -36
- package/dist/channels/lark/onboard.js +0 -56
- package/dist/channels/slack/config-api.d.ts +0 -69
- package/dist/channels/slack/config-api.js +0 -162
- package/dist/channels/slack/onboard.d.ts +0 -40
- package/dist/channels/slack/onboard.js +0 -136
- package/dist/channels/slack/onboarding-state.d.ts +0 -31
- package/dist/channels/slack/onboarding-state.js +0 -64
- package/dist/channels/slack/register-webhook.d.ts +0 -12
- package/dist/channels/slack/register-webhook.js +0 -67
- package/dist/channels/slack/setup-server.d.ts +0 -32
- package/dist/channels/slack/setup-server.js +0 -201
- package/dist/cli/add-feishu.d.ts +0 -50
- package/dist/cli/add-feishu.js +0 -367
- package/dist/cli/add-slack.d.ts +0 -18
- package/dist/cli/add-slack.js +0 -216
- package/dist/harnesses/pi-durable/parked.d.ts +0 -18
- package/dist/harnesses/pi-durable/parked.js +0 -27
|
@@ -3,13 +3,44 @@ import { type Harness } from "@earendil-works/pi-durable";
|
|
|
3
3
|
import type { CredentialSourceOptions } from "../models/credentials.ts";
|
|
4
4
|
import { type ModelEnvironment } from "../models/environment.ts";
|
|
5
5
|
import type { CredentialStore } from "../../sign-in-api.ts";
|
|
6
|
+
import type { McpOptions } from "../../mcp-api.ts";
|
|
7
|
+
import { type RunsAs } from "../../serving-command.ts";
|
|
6
8
|
import { type ResolvedContent } from "../../content/resolve.ts";
|
|
7
9
|
import type { Agent } from "../../agent-api.ts";
|
|
8
10
|
import { type Service } from "../../service.ts";
|
|
9
11
|
import type { DurableAgent, Unfinished } from "./agent.ts";
|
|
10
|
-
import { type
|
|
12
|
+
import { type EffectiveConfig, type InstanceConfig, type InstanceConfigSource } from "./config.ts";
|
|
13
|
+
import { type AgentDefinition } from "./definition.ts";
|
|
14
|
+
import { type BuiltDefinition } from "./open.ts";
|
|
11
15
|
import { type StorageLocking } from "./storage.ts";
|
|
12
16
|
export interface OpenServingOptions extends CredentialSourceOptions {
|
|
17
|
+
mcp?: McpOptions;
|
|
18
|
+
/** The instance's name, over the configuration's. Default: the configuration's, else the definition's as one segment. */
|
|
19
|
+
name?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The values the instance's code, its MCP servers and its model's authentication read by name (`needs`, `${VAR}`,
|
|
22
|
+
* a provider's variable): the one source of them, never this process's environment. A caller that wants its own
|
|
23
|
+
* passes it (`process.env`).
|
|
24
|
+
*/
|
|
25
|
+
secrets: Readonly<Record<string, string | undefined>>;
|
|
26
|
+
/**
|
|
27
|
+
* The base environment of the processes the agent starts (`baseEnv(process.env)` is the CLI's): the commands of
|
|
28
|
+
* its declared packages come ahead on its `PATH`, and the declared secrets are added to it. Nothing else of this
|
|
29
|
+
* process's reaches them.
|
|
30
|
+
*/
|
|
31
|
+
env: Readonly<Record<string, string>>;
|
|
32
|
+
/**
|
|
33
|
+
* The environment FastAgent's own work for the instance runs in: installing the declared packages, cloning content,
|
|
34
|
+
* finding `git` and `mise`, and where a CLI instance's files are. The CLI gives this process's; an app, its `env`.
|
|
35
|
+
* The instance's secrets are added to it.
|
|
36
|
+
*/
|
|
37
|
+
ownEnv: Readonly<Record<string, string | undefined>>;
|
|
38
|
+
/** Who runs the instance: what decides how a channel that names no way receives, and whether its prompt describes a deployment. */
|
|
39
|
+
runsAs: RunsAs;
|
|
40
|
+
/** Whether the model lists are refreshed without the network. Default: `ownEnv`'s `FASTAGENT_OFFLINE` is `1`. */
|
|
41
|
+
offline?: boolean;
|
|
42
|
+
/** What the instance sets over the program's defaults (`fastagent.json`, or an object); none runs on the defaults. */
|
|
43
|
+
config?: InstanceConfigSource;
|
|
13
44
|
/** What a deployment sets over the definition's defaults (a flag, `FASTAGENT_MODEL`). */
|
|
14
45
|
model?: string;
|
|
15
46
|
thinkingLevel?: ModelThinkingLevel;
|
|
@@ -23,8 +54,26 @@ export interface OpenServingOptions extends CredentialSourceOptions {
|
|
|
23
54
|
storage?: ServingStorage;
|
|
24
55
|
/** What becomes of the messages an earlier process left unfinished in the storage. Default `"resume"`. */
|
|
25
56
|
unfinished?: Unfinished;
|
|
26
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The instance's directory: its working directory, where its content is cloned, its secrets and, by default, its
|
|
59
|
+
* state are. Default: the definition's own, which is how a source project runs. A built package is read-only and is
|
|
60
|
+
* opened with a directory of its own.
|
|
61
|
+
*/
|
|
62
|
+
workdir?: string;
|
|
63
|
+
/**
|
|
64
|
+
* The directory whose `.secrets/` (values, model credentials, MCP grants) and default state
|
|
65
|
+
* root the instance reads. Default: the working directory. Temporary: secrets and content become inputs of their own.
|
|
66
|
+
*/
|
|
67
|
+
projectDir?: string;
|
|
68
|
+
/** The instance's state root, over `FASTAGENT_STATE_DIR` and the project directory's own. */
|
|
27
69
|
stateRoot?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Where the instance keeps its model credentials and MCP grants when it is given no store: `"secrets"`, the project's
|
|
72
|
+
* `.secrets/` over the machine's own credentials and as `FASTAGENT_AUTH_PATH` and `FASTAGENT_SECRETS_DIR` place them
|
|
73
|
+
* (the CLI's), or `"state"`, `auth.json` and `mcp-auth.json` in the state root and nothing else (an app's).
|
|
74
|
+
* Default `"secrets"`.
|
|
75
|
+
*/
|
|
76
|
+
keeps?: "secrets" | "state";
|
|
28
77
|
/** Whether a clock fires the instance's wake-ups now (`assembleAgent`'s `wakeTicking`). Default always. */
|
|
29
78
|
wakeTicking?: () => boolean;
|
|
30
79
|
}
|
|
@@ -37,11 +86,20 @@ type ServingStorage = {
|
|
|
37
86
|
export interface OpenedServing {
|
|
38
87
|
agent: DurableAgent;
|
|
39
88
|
definition: AgentDefinition;
|
|
89
|
+
/** The instance's name: what `info().name` reports. */
|
|
90
|
+
name: string;
|
|
40
91
|
code: BuiltDefinition;
|
|
92
|
+
/** The configuration this instance opened with: the program's defaults and the instance's over them. */
|
|
93
|
+
config: EffectiveConfig;
|
|
94
|
+
/** The secrets it was given that hold a value: what its code was built with, and its webhooks are pointed with. */
|
|
95
|
+
secrets: Readonly<Record<string, string>>;
|
|
41
96
|
models: ModelEnvironment;
|
|
42
97
|
/** What the agent works on, resolved for this instance. */
|
|
43
98
|
content: readonly ResolvedContent[];
|
|
99
|
+
/** The project directory: whose `.secrets/` the instance reads. */
|
|
44
100
|
agentDir: string;
|
|
101
|
+
/** The agent's working directory. */
|
|
102
|
+
workdir: string;
|
|
45
103
|
/** Where the instance's state is: its storage, its channels' and schedules' state, its wake-ups. */
|
|
46
104
|
stateRoot: string;
|
|
47
105
|
/** The definition's channels and schedules, as `serveAgent` serves them: `serveAgent(opened)`. */
|
|
@@ -53,7 +111,7 @@ export interface OpenedServing {
|
|
|
53
111
|
* instance's state outside a serve without losing a write to a serve's own.
|
|
54
112
|
*/
|
|
55
113
|
export declare function holdingInstance<T>(stateRoot: string, work: () => T): Promise<T>;
|
|
56
|
-
export declare function openServing(
|
|
114
|
+
export declare function openServing(definition: AgentDefinition, options: OpenServingOptions): Promise<OpenedServing>;
|
|
57
115
|
/** An agent opened in this process (SPEC.md §8): the Agent API, and how it is served and operated. */
|
|
58
116
|
export interface LocalAgent extends Agent {
|
|
59
117
|
/**
|
|
@@ -70,21 +128,43 @@ export interface ServeAgentOptions {
|
|
|
70
128
|
/** The keys a Caller of the Agent API may present (SPEC.md §9.1). */
|
|
71
129
|
apiKeys?: readonly string[];
|
|
72
130
|
}
|
|
73
|
-
|
|
131
|
+
/** The instance root, the agent's working directory with its state in `.state/`; or the two apart (SPEC.md §8). */
|
|
132
|
+
export type InstanceLocation = {
|
|
133
|
+
root: string;
|
|
134
|
+
} | {
|
|
135
|
+
stateDir: string;
|
|
136
|
+
workdir: string;
|
|
137
|
+
};
|
|
138
|
+
export type OpenAgentOptions<Settings = unknown> = InstanceLocation & {
|
|
139
|
+
/** The instance configuration, over the program's defaults: an object, or the absolute path of a `fastagent.json`. */
|
|
140
|
+
config?: InstanceConfig<Settings> | string;
|
|
141
|
+
/** The instance's name, over `config`'s: one path segment. Default: `config`'s, else the definition's as one. */
|
|
142
|
+
name?: string;
|
|
143
|
+
/**
|
|
144
|
+
* The values the agent's code (`needs`), its MCP servers (`${VAR}`) and its model's authentication read by name. The
|
|
145
|
+
* only source of them: nothing is read from the process's environment or from a file, so pass what the agent needs
|
|
146
|
+
* (`process.env` is a choice an app can make).
|
|
147
|
+
*/
|
|
148
|
+
secrets?: Readonly<Record<string, string | undefined>>;
|
|
149
|
+
/**
|
|
150
|
+
* The base environment of the processes the agent starts (its commands, stdio MCP servers, skill scripts). Default:
|
|
151
|
+
* empty; `baseEnv(process.env)` is an app's own. The commands of the agent's declared packages come ahead on its
|
|
152
|
+
* `PATH`, and the secrets the definition declares are added; nothing else of the host's reaches them.
|
|
153
|
+
*/
|
|
154
|
+
env?: Readonly<Record<string, string>>;
|
|
155
|
+
/** Per-instance MCP grants and interpolation values; no global credential discovery. */
|
|
156
|
+
mcp?: McpOptions;
|
|
74
157
|
/** `"provider/modelId"`, over the definition's: what a session that names none answers with. */
|
|
75
158
|
model?: string;
|
|
76
|
-
/** Where provider credentials are kept, instead of the
|
|
159
|
+
/** Where provider credentials are kept, instead of the state directory's `auth.json`. */
|
|
77
160
|
credentials?: CredentialStore;
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*/
|
|
82
|
-
stateDir?: string;
|
|
83
|
-
}
|
|
161
|
+
/** Refresh the model lists without the network: each is the one last kept, or pi-ai's own. Default: false. */
|
|
162
|
+
offline?: boolean;
|
|
163
|
+
};
|
|
84
164
|
/**
|
|
85
|
-
* Open
|
|
86
|
-
* the secrets it declares, its environment, its content and the instance's storage, which
|
|
87
|
-
* Nothing reaches the agent until `serve`.
|
|
165
|
+
* Open one instance of `definition`, the default export of an agent's package, the way `dev` and `start` do before they
|
|
166
|
+
* serve: its code built with the secrets it declares, its environment, its content and the instance's storage, which
|
|
167
|
+
* one process holds at a time. Nothing reaches the agent until `serve`.
|
|
88
168
|
*/
|
|
89
|
-
export declare function openAgent(
|
|
169
|
+
export declare function openAgent<Settings = unknown>(definition: AgentDefinition<Settings>, options: OpenAgentOptions<Settings>): Promise<LocalAgent>;
|
|
90
170
|
export {};
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* A definition opened: its code built with its secrets, its model environment, its storage, and the Agent over them.
|
|
3
|
+
* `openAgent` is how an app opens one (SPEC.md §8); `dev`, `start`, AgentCore, `invoke` and `schedules run` open it
|
|
4
|
+
* through `openServing`, which also takes where the Agent keeps its sessions and what becomes of an earlier process's
|
|
5
|
+
* messages.
|
|
6
6
|
*/
|
|
7
|
-
import { join } from "node:path";
|
|
7
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
8
8
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
9
9
|
import { MemoryStorage } from "@earendil-works/pi-durable";
|
|
10
10
|
import { modelEnvironment } from "../models/environment.js";
|
|
11
11
|
import { piCredentials } from "../models/host.js";
|
|
12
|
+
import { openMcpClients } from "../../mcp/client.js";
|
|
13
|
+
import { declareMcp, resolveMcpServers } from "../../mcp/config.js";
|
|
14
|
+
import { mcpAuthorization, mcpAuthorizationInState } from "../../mcp/host.js";
|
|
15
|
+
import { NOT_HOSTED } from "../../serving-command.js";
|
|
16
|
+
import { mcpTools } from "./mcp.js";
|
|
12
17
|
import { loadContent } from "../../content/file.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
18
|
+
import { cloneContent, resolveContent } from "../../content/resolve.js";
|
|
19
|
+
import { processEnvironment } from "../../child-env.js";
|
|
20
|
+
import { definedValues } from "../../declared-secrets.js";
|
|
15
21
|
import { prepareEnvironment } from "../../environment/mise.js";
|
|
16
22
|
import { log } from "../../log.js";
|
|
17
|
-
import {
|
|
23
|
+
import { STATE_DIRNAME, resolveStateRoot } from "../../paths.js";
|
|
18
24
|
import { serveAgent } from "../../service.js";
|
|
19
|
-
import {
|
|
25
|
+
import { readInstanceConfigFile, resolveConfig, } from "./config.js";
|
|
26
|
+
import { assertDefinition } from "./definition.js";
|
|
27
|
+
import { assembleAgent, buildDefinition } from "./open.js";
|
|
20
28
|
import { openLockedStorage } from "./storage.js";
|
|
29
|
+
import { openText } from "./text.js";
|
|
30
|
+
import { instanceName } from "../../instance-name.js";
|
|
21
31
|
/** Who opens the instance's storage, for the refusal of a second opener. */
|
|
22
32
|
const INSTANCE_HOLDERS = "another `fastagent dev`, `start` or `schedules` command of this agent (or of another agent with the same state root)";
|
|
23
33
|
/**
|
|
@@ -26,10 +36,11 @@ const INSTANCE_HOLDERS = "another `fastagent dev`, `start` or `schedules` comman
|
|
|
26
36
|
* up to date in place where git can do so without touching the agent's work, first; then resolved again as what is now
|
|
27
37
|
* on disk.
|
|
28
38
|
*/
|
|
29
|
-
async function contentForThisRun(dir, env) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
async function contentForThisRun(dir, instance, workdir, env) {
|
|
40
|
+
const declared = loadContent(dir, instance);
|
|
41
|
+
const places = { dir, workdir };
|
|
42
|
+
for (const entry of resolveContent(declared, places)) {
|
|
43
|
+
if (entry.kind !== "github")
|
|
33
44
|
continue;
|
|
34
45
|
const done = await cloneContent(entry, env);
|
|
35
46
|
if (done.outcome === "kept") {
|
|
@@ -40,9 +51,7 @@ async function contentForThisRun(dir, env) {
|
|
|
40
51
|
log.info(`[fastagent] github ${entry.repo}${entry.ref ? ` at ${entry.ref}` : ""}: ${said} in ${entry.location}`);
|
|
41
52
|
}
|
|
42
53
|
}
|
|
43
|
-
|
|
44
|
-
log.warn(`[fastagent] content ${absentNotice(absent)}`);
|
|
45
|
-
return resolveContent(dir, declaration);
|
|
54
|
+
return resolveContent(declared, places);
|
|
46
55
|
}
|
|
47
56
|
/** The instance's database, under its state root. */
|
|
48
57
|
const instanceStorageFile = (stateRoot) => join(stateRoot, "agent.sqlite");
|
|
@@ -53,7 +62,7 @@ const instanceStorageFile = (stateRoot) => join(stateRoot, "agent.sqlite");
|
|
|
53
62
|
export async function holdingInstance(stateRoot, work) {
|
|
54
63
|
const storage = await openLockedStorage(instanceStorageFile(stateRoot), "posix", INSTANCE_HOLDERS);
|
|
55
64
|
try {
|
|
56
|
-
return work();
|
|
65
|
+
return await work();
|
|
57
66
|
}
|
|
58
67
|
finally {
|
|
59
68
|
await storage.close(BACKGROUND_CONTEXT);
|
|
@@ -66,73 +75,156 @@ function openStorage(stateRoot, storage) {
|
|
|
66
75
|
return openLockedStorage(storage.file, "posix", storage.holders);
|
|
67
76
|
return openLockedStorage(instanceStorageFile(stateRoot), storage.instance, INSTANCE_HOLDERS);
|
|
68
77
|
}
|
|
69
|
-
export async function openServing(
|
|
70
|
-
const definition = await prepareDefinition(directory);
|
|
78
|
+
export async function openServing(definition, options) {
|
|
71
79
|
const { dir } = definition;
|
|
72
|
-
|
|
80
|
+
const workdir = options.workdir ?? dir;
|
|
81
|
+
const projectDir = options.projectDir ?? workdir;
|
|
82
|
+
const config = resolveConfig(definition, options.config);
|
|
83
|
+
const name = instanceName(definition.name, options.name ?? config.name);
|
|
84
|
+
const text = openText(dir, options.config);
|
|
85
|
+
const mcpServers = declareMcp(dir, options.config);
|
|
86
|
+
// The instance's values are ONE input every reader below is given, and this process's is never written: a process
|
|
73
87
|
// may run several agents, each with its own values and packages.
|
|
74
|
-
const
|
|
75
|
-
const code = buildDefinition(definition, { ...
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
const env = { ...
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
const secrets = definedValues(options.secrets);
|
|
89
|
+
const code = buildDefinition(definition, secrets, { ...config, dev: options.runsAs.dev });
|
|
90
|
+
// FastAgent's own work for the instance (installing packages, cloning content, finding its files) runs in this
|
|
91
|
+
// process's environment and the instance's secrets. What the AGENT starts gets an environment assembled from inputs.
|
|
92
|
+
const env = { ...options.ownEnv, ...secrets };
|
|
93
|
+
const packages = await prepareEnvironment(dir, env, { ...env, PATH: options.env.PATH });
|
|
94
|
+
const processEnv = processEnvironment({
|
|
95
|
+
base: options.env,
|
|
96
|
+
secrets,
|
|
97
|
+
declared: definition.inputs.secrets.map(({ name }) => name),
|
|
98
|
+
packages,
|
|
99
|
+
workdir,
|
|
100
|
+
}, dirname(workdir));
|
|
101
|
+
const content = await contentForThisRun(dir, options.config, workdir, env);
|
|
102
|
+
const stateRoot = options.stateRoot ?? resolveStateRoot(projectDir, env);
|
|
103
|
+
const authPath = options.authPath ??
|
|
104
|
+
(options.keeps === "state" && options.credentialStore === undefined ? join(stateRoot, "auth.json") : undefined);
|
|
105
|
+
const models = await modelEnvironment(projectDir, {
|
|
106
|
+
...(authPath === undefined ? {} : { authPath }),
|
|
83
107
|
...(options.credentialStore === undefined ? {} : { credentialStore: options.credentialStore }),
|
|
84
|
-
}, code.providers, { stateRoot, environment });
|
|
108
|
+
}, code.providers, { stateRoot, environment: secrets, env, offline: options.offline ?? env.FASTAGENT_OFFLINE === "1" });
|
|
85
109
|
const chosen = options.storage ?? { instance: "posix" };
|
|
86
110
|
const storage = await openStorage(stateRoot, chosen);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
let mcp;
|
|
112
|
+
let agent;
|
|
113
|
+
try {
|
|
114
|
+
const mcpEnvironment = { ...secrets, ...options.mcp?.variables };
|
|
115
|
+
mcp = await openMcpClients(resolveMcpServers(mcpServers, mcpEnvironment, undefined, workdir, processEnv), options.keeps === "state"
|
|
116
|
+
? mcpAuthorizationInState(stateRoot, options.mcp)
|
|
117
|
+
: mcpAuthorization(projectDir, env, stateRoot, options.mcp), options.mcp);
|
|
118
|
+
agent = await assembleAgent(definition, {
|
|
119
|
+
name,
|
|
120
|
+
mcpTools: mcpTools(mcp.tools),
|
|
121
|
+
runsAs: options.runsAs,
|
|
122
|
+
storage,
|
|
123
|
+
code,
|
|
124
|
+
config,
|
|
125
|
+
text,
|
|
126
|
+
content,
|
|
127
|
+
workdir,
|
|
128
|
+
processEnv,
|
|
129
|
+
models: models.models,
|
|
130
|
+
...(models.catalogProblem === undefined ? {} : { catalogProblem: models.catalogProblem }),
|
|
131
|
+
...(typeof chosen === "object" && "instance" in chosen ? { stateRoot } : {}),
|
|
132
|
+
...(options.model === undefined ? {} : { model: options.model }),
|
|
133
|
+
...(options.thinkingLevel === undefined ? {} : { thinkingLevel: options.thinkingLevel }),
|
|
134
|
+
...(options.unfinished === undefined ? {} : { unfinished: options.unfinished }),
|
|
135
|
+
...(options.wakeTicking === undefined ? {} : { wakeTicking: options.wakeTicking }),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const failures = [error];
|
|
140
|
+
if (mcp)
|
|
141
|
+
await mcp.close().catch((cleanup) => failures.push(cleanup));
|
|
142
|
+
await storage.close(BACKGROUND_CONTEXT).catch((cleanup) => failures.push(cleanup));
|
|
143
|
+
throw failures.length === 1 ? error : new AggregateError(failures, "Agent open and cleanup failed");
|
|
144
|
+
}
|
|
145
|
+
// A storage failure or a direct harness close also ends the resources this open acquired.
|
|
146
|
+
void agent.harness.closed
|
|
147
|
+
.then(() => mcp.close())
|
|
148
|
+
.catch((error) => {
|
|
149
|
+
log.warn(`[fastagent] MCP cleanup after harness shutdown failed: ${String(error)}`);
|
|
103
150
|
});
|
|
151
|
+
const closeAgent = agent.close.bind(agent);
|
|
152
|
+
let closing;
|
|
153
|
+
agent.close = () => (closing ??= (async () => {
|
|
154
|
+
const failures = [];
|
|
155
|
+
await closeAgent().catch((error) => failures.push(error));
|
|
156
|
+
await mcp.close().catch((error) => failures.push(error));
|
|
157
|
+
if (failures.length)
|
|
158
|
+
throw new AggregateError(failures, "Agent close failed");
|
|
159
|
+
})());
|
|
104
160
|
return {
|
|
105
161
|
agent,
|
|
106
162
|
definition,
|
|
163
|
+
name,
|
|
107
164
|
code,
|
|
165
|
+
config,
|
|
166
|
+
secrets,
|
|
108
167
|
models,
|
|
109
168
|
content,
|
|
110
|
-
agentDir:
|
|
169
|
+
agentDir: projectDir,
|
|
170
|
+
workdir,
|
|
111
171
|
stateRoot,
|
|
112
172
|
channels: code.channels,
|
|
113
173
|
schedules: code.schedules,
|
|
114
174
|
};
|
|
115
175
|
}
|
|
116
|
-
/** `
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
176
|
+
/** The `config` input as a source: an object as it is, a path (absolute, like every path here) read. */
|
|
177
|
+
async function configOf(config) {
|
|
178
|
+
if (config === undefined)
|
|
179
|
+
return undefined;
|
|
180
|
+
if (typeof config !== "string")
|
|
181
|
+
return { values: config, label: "the config object" };
|
|
182
|
+
if (!isAbsolute(config))
|
|
183
|
+
throw new Error(`config must be an absolute path, and is ${JSON.stringify(config)}`);
|
|
184
|
+
return readInstanceConfigFile(config);
|
|
185
|
+
}
|
|
186
|
+
/** Where the instance is, every path absolute: a relative one would be an input the process's cwd chooses. */
|
|
187
|
+
function locationOf(location) {
|
|
188
|
+
const absolute = (path, name) => {
|
|
189
|
+
if (typeof path !== "string" || !isAbsolute(path)) {
|
|
190
|
+
throw new Error(`${name} must be an absolute path, and is ${JSON.stringify(path)}`);
|
|
191
|
+
}
|
|
192
|
+
return path;
|
|
193
|
+
};
|
|
194
|
+
if ("root" in location && ("stateDir" in location || "workdir" in location)) {
|
|
195
|
+
throw new Error("give the instance a root, or a stateDir and a workdir, not both");
|
|
196
|
+
}
|
|
197
|
+
if ("root" in location) {
|
|
198
|
+
const root = absolute(location.root, "root");
|
|
199
|
+
return { workdir: root, stateRoot: join(root, STATE_DIRNAME) };
|
|
200
|
+
}
|
|
201
|
+
return { workdir: absolute(location.workdir, "workdir"), stateRoot: absolute(location.stateDir, "stateDir") };
|
|
122
202
|
}
|
|
123
203
|
/**
|
|
124
|
-
* Open
|
|
125
|
-
* the secrets it declares, its environment, its content and the instance's storage, which
|
|
126
|
-
* Nothing reaches the agent until `serve`.
|
|
204
|
+
* Open one instance of `definition`, the default export of an agent's package, the way `dev` and `start` do before they
|
|
205
|
+
* serve: its code built with the secrets it declares, its environment, its content and the instance's storage, which
|
|
206
|
+
* one process holds at a time. Nothing reaches the agent until `serve`.
|
|
127
207
|
*/
|
|
128
|
-
export async function openAgent(
|
|
208
|
+
export async function openAgent(definition, options) {
|
|
209
|
+
const { workdir, stateRoot } = locationOf(options);
|
|
210
|
+
const config = await configOf(options.config);
|
|
129
211
|
// An app may never serve the agent, and then no clock fires a wake-up: `wake` is refused until it does.
|
|
130
212
|
let ticking = false;
|
|
131
|
-
const opened = await openServing(
|
|
213
|
+
const opened = await openServing(assertDefinition(definition), {
|
|
214
|
+
workdir,
|
|
215
|
+
stateRoot,
|
|
216
|
+
secrets: options.secrets ?? {},
|
|
217
|
+
env: options.env ?? {},
|
|
218
|
+
ownEnv: options.env ?? {},
|
|
219
|
+
runsAs: NOT_HOSTED,
|
|
220
|
+
offline: options.offline === true,
|
|
221
|
+
...(options.name === undefined ? {} : { name: options.name }),
|
|
222
|
+
...(config === undefined ? {} : { config }),
|
|
132
223
|
wakeTicking: () => ticking,
|
|
224
|
+
keeps: "state",
|
|
225
|
+
...(options.mcp === undefined ? {} : { mcp: options.mcp }),
|
|
133
226
|
...(options.model === undefined ? {} : { model: options.model }),
|
|
134
227
|
...(options.credentials === undefined ? {} : { credentialStore: piCredentials(options.credentials) }),
|
|
135
|
-
...(options.stateDir === undefined ? {} : { stateRoot: stateDirOf(options.stateDir) }),
|
|
136
228
|
});
|
|
137
229
|
const { agent } = opened;
|
|
138
230
|
let served;
|
|
@@ -20,20 +20,23 @@ export const trackSessions = (harness, storage, context) => Effect.gen(function*
|
|
|
20
20
|
if (value > (newest.get(id) ?? 0))
|
|
21
21
|
newest.set(id, value);
|
|
22
22
|
};
|
|
23
|
-
//
|
|
23
|
+
// Observed before the scan, so a commit while it reads is not lost: either reports the newest id, and the
|
|
24
24
|
// larger stands. A change's conversation may be created in the same commit, so membership is not checked here.
|
|
25
|
-
yield* Effect.acquireRelease(Effect.sync(() => harness.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
yield* Effect.acquireRelease(Effect.sync(() => harness.observeCommits({
|
|
26
|
+
advance: ({ changes }) => {
|
|
27
|
+
for (const change of changes) {
|
|
28
|
+
if (change.type === "conversation") {
|
|
29
|
+
if (isSession(change.value))
|
|
30
|
+
sessions.add(change.value.id);
|
|
31
|
+
touch(change.value.id, change.value.id);
|
|
32
|
+
}
|
|
33
|
+
else if (change.type === "entry" || change.type === "submission") {
|
|
34
|
+
touch(change.value.conversationId, change.value.id);
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
})), (unsubscribe) => Effect.sync(unsubscribe));
|
|
37
|
+
},
|
|
38
|
+
end: () => { },
|
|
39
|
+
})), (observation) => Effect.sync(() => observation.detach()));
|
|
37
40
|
yield* conversations(storage, context).pipe(Stream.filter(isSession), Stream.runForEach((conversation) => storedActivity(storage, context, conversation).pipe(Effect.tap((activity) => Effect.sync(() => {
|
|
38
41
|
sessions.add(conversation.id);
|
|
39
42
|
touch(conversation.id, activity);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { InstanceConfigSource } from "./config.ts";
|
|
2
|
+
export interface TextSource {
|
|
3
|
+
/** Absolute: a file of instructions, or a directory of skills or of prompt templates. */
|
|
4
|
+
path: string;
|
|
5
|
+
/** The instance configuration that declared it, and so expects it to exist. None for the program's own. */
|
|
6
|
+
declaredBy?: string;
|
|
7
|
+
/** The path as the configuration wrote it. */
|
|
8
|
+
given?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Each kind's sources in the order they load: the program's first, so a later one never replaces its entries. */
|
|
11
|
+
export interface TextSources {
|
|
12
|
+
instructions: readonly TextSource[];
|
|
13
|
+
skills: readonly TextSource[];
|
|
14
|
+
prompts: readonly TextSource[];
|
|
15
|
+
}
|
|
16
|
+
export declare const NOUN: {
|
|
17
|
+
readonly instructions: "file";
|
|
18
|
+
readonly skills: "directory";
|
|
19
|
+
readonly prompts: "directory";
|
|
20
|
+
};
|
|
21
|
+
/** The program's own text, which `AGENTS.md` and `SYSTEM.md` already put in the prompt. */
|
|
22
|
+
export declare function programText(dir: string): TextSources;
|
|
23
|
+
/** What `path` is: a file, a directory, or nothing. */
|
|
24
|
+
export declare function whatIs(path: string): "file" | "directory" | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The text of the program in `dir` and of `instance` (whose shape `resolveConfig` has checked): each declared path
|
|
27
|
+
* resolved against the configuration's directory, and refused when it is not what its kind names or is text the
|
|
28
|
+
* program already loads, which would load twice.
|
|
29
|
+
*/
|
|
30
|
+
export declare function declareText(dir: string, instance: InstanceConfigSource | undefined): TextSources;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an instance's text is (agent-artifact.md §4.2, §7): the program's own `skills/` and `prompts/`, then what the
|
|
3
|
+
* instance configuration's `instructions`, `skills` and `prompts` name. Resolved once, at open, into absolute paths, so
|
|
4
|
+
* the readers (text.ts) read exactly these for each message and scan nothing else.
|
|
5
|
+
*/
|
|
6
|
+
import { statSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { configurationPath, isUnderDir } from "../../paths.js";
|
|
9
|
+
export const NOUN = { instructions: "file", skills: "directory", prompts: "directory" };
|
|
10
|
+
/** The program's own text, which `AGENTS.md` and `SYSTEM.md` already put in the prompt. */
|
|
11
|
+
export function programText(dir) {
|
|
12
|
+
return { instructions: [], skills: [{ path: join(dir, "skills") }], prompts: [{ path: join(dir, "prompts") }] };
|
|
13
|
+
}
|
|
14
|
+
/** What `path` is: a file, a directory, or nothing. */
|
|
15
|
+
export function whatIs(path) {
|
|
16
|
+
try {
|
|
17
|
+
return statSync(path).isDirectory() ? "directory" : "file";
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error.code === "ENOENT")
|
|
21
|
+
return undefined;
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The text of the program in `dir` and of `instance` (whose shape `resolveConfig` has checked): each declared path
|
|
27
|
+
* resolved against the configuration's directory, and refused when it is not what its kind names or is text the
|
|
28
|
+
* program already loads, which would load twice.
|
|
29
|
+
*/
|
|
30
|
+
export function declareText(dir, instance) {
|
|
31
|
+
const program = programText(dir);
|
|
32
|
+
const values = typeof instance?.values === "object" ? instance.values : {};
|
|
33
|
+
if (instance === undefined)
|
|
34
|
+
return program;
|
|
35
|
+
const programsOwn = ["SYSTEM.md", "AGENTS.md", "skills", "prompts"].map((name) => join(dir, name));
|
|
36
|
+
const declared = (kind) => (values[kind] ?? []).map((given) => {
|
|
37
|
+
const at = `${instance.label}: ${kind} "${given}"`;
|
|
38
|
+
const path = configurationPath(given, instance.directory);
|
|
39
|
+
if (path === undefined) {
|
|
40
|
+
throw new Error(`${at} must be absolute here, since ${instance.label} is not a file its directory could anchor`);
|
|
41
|
+
}
|
|
42
|
+
if (whatIs(path) !== NOUN[kind])
|
|
43
|
+
throw new Error(`${at} (${path}) is not a ${NOUN[kind]}`);
|
|
44
|
+
const own = programsOwn.find((text) => isUnderDir(path, text));
|
|
45
|
+
if (own !== undefined) {
|
|
46
|
+
throw new Error(`${at} (${path}) is the program's own text (${own}), which loads already: declare another`);
|
|
47
|
+
}
|
|
48
|
+
return { path, declaredBy: instance.label, given };
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
instructions: declared("instructions"),
|
|
52
|
+
skills: [...program.skills, ...declared("skills")],
|
|
53
|
+
prompts: [...program.prompts, ...declared("prompts")],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type PromptSection } from "@earendil-works/pi-durable";
|
|
2
2
|
import type { ResolvedContent } from "../../content/resolve.ts";
|
|
3
|
+
import type { RunsAs } from "../../serving-command.ts";
|
|
3
4
|
import type { AgentCommand } from "./agent.ts";
|
|
5
|
+
import type { InstanceConfigSource } from "./config.ts";
|
|
6
|
+
import { type TextSource, type TextSources } from "./text-sources.ts";
|
|
4
7
|
export interface Skill {
|
|
5
8
|
name: string;
|
|
6
9
|
description: string;
|
|
@@ -10,8 +13,13 @@ export interface Skill {
|
|
|
10
13
|
/** Offered only through `/skill:<name>`, never listed to the model. */
|
|
11
14
|
disableModelInvocation: boolean;
|
|
12
15
|
}
|
|
13
|
-
/** The
|
|
14
|
-
export declare
|
|
16
|
+
/** The skills the agent offers, reported and skipped as above. */
|
|
17
|
+
export declare const readSkills: (sources: readonly TextSource[]) => Skill[];
|
|
18
|
+
/**
|
|
19
|
+
* The instance's text as it will be read: declared, and refused when an instance entry takes a name the program (or an
|
|
20
|
+
* earlier instance entry) has. Called at open; what goes wrong afterwards is reported as each message reads it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function openText(dir: string, instance: InstanceConfigSource | undefined): TextSources;
|
|
15
23
|
/** Arguments as a shell splits them: by whitespace, with `"…"` or `'…'` keeping one argument together. */
|
|
16
24
|
export declare function parseArguments(text: string): string[];
|
|
17
25
|
/**
|
|
@@ -20,11 +28,15 @@ export declare function parseArguments(text: string): string[];
|
|
|
20
28
|
*/
|
|
21
29
|
export declare function expandTemplate(body: string, args: readonly string[]): string;
|
|
22
30
|
/** What a Caller may offer as `/<name>`: the prompt templates, then `skill:<name>` for each skill. */
|
|
23
|
-
export declare function readCommands(
|
|
31
|
+
export declare function readCommands(text: TextSources): AgentCommand[];
|
|
24
32
|
/**
|
|
25
33
|
* The prompt the definition contributes: `SYSTEM.md` as written, `AGENTS.md` as project context, the skills, where it
|
|
26
|
-
* works, and, deployed, what of a change it makes to itself lasts. `wake`: the agent has the wake tool
|
|
34
|
+
* works, and, deployed, what of a change it makes to itself lasts. `wake`: the agent has the wake tool; `runsAs` says
|
|
35
|
+
* whether it is deployed, and where.
|
|
27
36
|
*/
|
|
28
37
|
export declare function definitionSections(agentDir: string, content: readonly ResolvedContent[], options: {
|
|
29
38
|
wake: boolean;
|
|
39
|
+
runsAs: RunsAs;
|
|
40
|
+
workdir?: string;
|
|
41
|
+
text?: TextSources;
|
|
30
42
|
}): PromptSection[];
|