@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
|
@@ -7,35 +7,33 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
|
-
* A definition assembled onto pi-durable (architecture.md §3):
|
|
11
|
-
*
|
|
10
|
+
* A definition assembled onto pi-durable (architecture.md §3): a source project read as a definition, its secrets
|
|
11
|
+
* checked as one list, its code built with them, and an Agent opened over pi-ai's built-in models with the
|
|
12
12
|
* text, tools and extensions installed. `dev`, `start` and an embedder call the same function.
|
|
13
13
|
*/
|
|
14
14
|
import { existsSync } from "node:fs";
|
|
15
|
-
import { writeFileAtomic } from "../../atomic-write.js";
|
|
16
15
|
import { basename, join, resolve } from "node:path";
|
|
17
16
|
import { pathToFileURL } from "node:url";
|
|
18
17
|
import { createRegistry, defineExtension, } from "@earendil-works/pi-durable";
|
|
19
|
-
import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node";
|
|
20
18
|
import { CodingTools } from "@earendil-works/pi-durable/tools";
|
|
21
|
-
import { loadEnvironment } from "../../content/file.js";
|
|
22
19
|
import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
|
|
23
|
-
import {
|
|
24
|
-
import { channelModuleProblem } from "../../channels/discover.js";
|
|
20
|
+
import { channelIngress, channelModuleProblem } from "../../channels/discover.js";
|
|
25
21
|
import { AGENT_CONFIG_FILE as CONFIG_FILE } from "../../paths.js";
|
|
26
22
|
import { readPackageJson } from "../../runtime.js";
|
|
27
|
-
import {
|
|
23
|
+
import { fastagentVersion } from "../../version.js";
|
|
24
|
+
import { InstanceExecutionEnv } from "./execution-env.js";
|
|
25
|
+
import { instanceName } from "../../instance-name.js";
|
|
28
26
|
import { modelRefOf } from "../../model-ref.js";
|
|
29
27
|
import { MAX_SCHEDULES, scheduleConfigProblem } from "../../schedule/schedule.js";
|
|
30
28
|
import { openDurableAgent } from "./agent.js";
|
|
31
|
-
import { isNeeds } from "./define.js";
|
|
32
|
-
import {
|
|
29
|
+
import { isByIngress, isNeeds } from "./define.js";
|
|
30
|
+
import { checkProgramConfig, devInstanceConfig } from "./config.js";
|
|
31
|
+
import { DEFINITION_FORMAT, chosenIngress, codeSecrets, defineDefinition, } from "./definition.js";
|
|
32
|
+
import { importRegistry } from "./registry.js";
|
|
33
33
|
import { definitionSections, readCommands } from "./text.js";
|
|
34
|
+
import { programText } from "./text-sources.js";
|
|
34
35
|
import { WAKE_EXTENSION, WAKE_TOOLS, wakeExtension } from "./wake.js";
|
|
35
|
-
const SECRETS_MANIFEST_FILE = "secrets.json";
|
|
36
|
-
const CONFIG_KEYS = ["name", "description", "model", "thinkingLevel", "extensions", "providers", "http", "deploy"];
|
|
37
36
|
const DEFINITION_EXTENSION = "fastagent.definition";
|
|
38
|
-
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
39
37
|
async function importDefault(path, what) {
|
|
40
38
|
try {
|
|
41
39
|
return (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href))).default;
|
|
@@ -44,134 +42,43 @@ async function importDefault(path, what) {
|
|
|
44
42
|
throw new Error(`${what}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
45
43
|
}
|
|
46
44
|
}
|
|
47
|
-
/** An object whose keys are among `keys`, or the refusal naming the one that is not and the ones there are. */
|
|
48
|
-
function checkKeys(value, keys, where) {
|
|
49
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
50
|
-
throw new Error(`${where} must be an object`);
|
|
51
|
-
for (const key of Object.keys(value)) {
|
|
52
|
-
if (!keys.includes(key))
|
|
53
|
-
throw new Error(`${where}: unknown key "${key}" (keys: ${keys.join(", ")})`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/** Refuse what a type check would have: Node strips types without checking them, so a typo reaches here. */
|
|
57
|
-
function checkConfig(config, file) {
|
|
58
|
-
if (config === undefined)
|
|
59
|
-
return {};
|
|
60
|
-
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
61
|
-
throw new Error(`${file} must default-export defineAgent({ ... })`);
|
|
62
|
-
}
|
|
63
|
-
checkKeys(config, CONFIG_KEYS, file);
|
|
64
|
-
const { model, thinkingLevel, extensions, providers, http, deploy } = config;
|
|
65
|
-
if (http !== undefined) {
|
|
66
|
-
checkKeys(http, ["port", "api"], `${file}: "http"`);
|
|
67
|
-
const port = http.port;
|
|
68
|
-
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
69
|
-
throw new Error(`${file}: "http.port" must be an integer 0-65535`);
|
|
70
|
-
}
|
|
71
|
-
if (http.api !== undefined && typeof http.api !== "boolean") {
|
|
72
|
-
throw new Error(`${file}: "http.api" must be true or false`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (deploy !== undefined) {
|
|
76
|
-
checkKeys(deploy, ["agentcore"], `${file}: "deploy"`);
|
|
77
|
-
if (deploy.agentcore !== undefined) {
|
|
78
|
-
checkKeys(deploy.agentcore, ["idleTimeoutSeconds"], `${file}: "deploy.agentcore"`);
|
|
79
|
-
// AWS's own bounds for idleRuntimeSessionTimeout: a value outside them is refused by CloudFormation minutes
|
|
80
|
-
// into `deploy agentcore --run`, after the image build.
|
|
81
|
-
const idle = deploy.agentcore.idleTimeoutSeconds;
|
|
82
|
-
if (idle !== undefined && (!Number.isInteger(idle) || idle < 60 || idle > 1209600)) {
|
|
83
|
-
throw new Error(`${file}: "deploy.agentcore.idleTimeoutSeconds" must be an integer 60-1209600 (seconds)`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (model !== undefined)
|
|
88
|
-
modelRefOf(model, `${file}: "model"`);
|
|
89
|
-
if (thinkingLevel !== undefined && !THINKING_LEVELS.includes(thinkingLevel)) {
|
|
90
|
-
throw new Error(`${file}: "thinkingLevel" must be one of ${THINKING_LEVELS.join(", ")}`);
|
|
91
|
-
}
|
|
92
|
-
for (const [key, list] of [
|
|
93
|
-
["extensions", extensions],
|
|
94
|
-
["providers", providers],
|
|
95
|
-
]) {
|
|
96
|
-
if (list !== undefined && !Array.isArray(list))
|
|
97
|
-
throw new Error(`${file}: "${key}" must be an array`);
|
|
98
|
-
}
|
|
99
|
-
return config;
|
|
100
|
-
}
|
|
101
45
|
/** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
|
|
102
|
-
|
|
46
|
+
async function loadConfig(dir) {
|
|
103
47
|
const configPath = join(dir, CONFIG_FILE);
|
|
104
|
-
return
|
|
48
|
+
return checkProgramConfig(existsSync(configPath) ? await importDefault(configPath, CONFIG_FILE) : undefined, CONFIG_FILE);
|
|
105
49
|
}
|
|
106
50
|
/** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
|
|
107
51
|
export function modelSpecFor(config, flag, env = process.env) {
|
|
108
52
|
return flag ?? (env.FASTAGENT_MODEL || config.model);
|
|
109
53
|
}
|
|
110
|
-
/**
|
|
111
|
-
export async function
|
|
54
|
+
/** The development project in `dir`, as `dev` opens it: its source and the `fastagent.json` at its root, if any. */
|
|
55
|
+
export async function loadDevProject(dir) {
|
|
56
|
+
const definition = await loadSourceDefinition(dir);
|
|
57
|
+
const instance = await devInstanceConfig(dir);
|
|
58
|
+
return instance === undefined ? { definition } : { definition, instance };
|
|
59
|
+
}
|
|
60
|
+
/** The source project in `dir` as a definition: `fastagent.config.ts` (optional) and the modules of `src/`. */
|
|
61
|
+
export async function loadSourceDefinition(directory) {
|
|
112
62
|
// Absolute, since the prompt names the definition's files by it and the agent's tools resolve a relative path against
|
|
113
63
|
// the agent's own working directory, which is this one.
|
|
114
64
|
const dir = resolve(directory);
|
|
115
65
|
const config = await loadConfig(dir);
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
const packageName = (await readPackageJson(dir));
|
|
131
|
-
const name = config.name ?? (typeof packageName.name === "string" ? packageName.name : basename(dir));
|
|
132
|
-
return { dir, name, config, registry };
|
|
133
|
-
}
|
|
134
|
-
/** Every value the definition can hold `Needs` in, with where it is: modules by file, configuration by key. */
|
|
135
|
-
function declarers(definition) {
|
|
136
|
-
return [
|
|
137
|
-
...REGISTRY_KINDS.flatMap((kind) => definition.registry[kind].map(({ file, value }) => ({ source: file, value }))),
|
|
138
|
-
...(definition.config.extensions ?? []).map((value, i) => ({ source: `${CONFIG_FILE} extensions[${i}]`, value })),
|
|
139
|
-
...(definition.config.providers ?? []).map((value, i) => ({ source: `${CONFIG_FILE} providers[${i}]`, value })),
|
|
140
|
-
];
|
|
141
|
-
}
|
|
142
|
-
/** The secrets the definition declares, wherever: its code's `needs`, and `context.json`'s command variables. */
|
|
143
|
-
export function definitionSecrets(definition) {
|
|
144
|
-
return [
|
|
145
|
-
...declarers(definition).flatMap(({ source, value }) => isNeeds(value) ? value.secrets.map((secret) => ({ ...secret, source })) : []),
|
|
146
|
-
...declaredVariables(loadEnvironment(definition.dir)),
|
|
147
|
-
];
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* The list a person deploying or running the agent reads instead of its code (`.fastagent/secrets.json`): each name
|
|
151
|
-
* once, what it is for, whether the agent runs without it, and where it is declared.
|
|
152
|
-
*/
|
|
153
|
-
export function secretsManifest(definition) {
|
|
154
|
-
const byName = new Map();
|
|
155
|
-
for (const secret of definitionSecrets(definition)) {
|
|
156
|
-
const entry = byName.get(secret.name) ?? { name: secret.name, optional: true, declaredIn: [] };
|
|
157
|
-
entry.optional &&= secret.optional === true;
|
|
158
|
-
entry.declaredIn.push(secret.source);
|
|
159
|
-
if (entry.description === undefined && secret.description !== undefined)
|
|
160
|
-
entry.description = secret.description;
|
|
161
|
-
byName.set(secret.name, entry);
|
|
162
|
-
}
|
|
163
|
-
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
164
|
-
}
|
|
165
|
-
/** Generate the registry, read the definition, and write its secrets manifest beside the registry. */
|
|
166
|
-
export async function prepareDefinition(dir) {
|
|
167
|
-
await generateRegistry(dir);
|
|
168
|
-
const definition = await loadDefinition(dir);
|
|
169
|
-
writeFileAtomic(join(dir, GENERATED_DIR, SECRETS_MANIFEST_FILE), `${JSON.stringify(secretsManifest(definition), null, 2)}\n`, undefined, true);
|
|
170
|
-
return definition;
|
|
66
|
+
const registry = await importRegistry(dir);
|
|
67
|
+
const pkg = (await readPackageJson(dir));
|
|
68
|
+
return defineDefinition({
|
|
69
|
+
format: DEFINITION_FORMAT,
|
|
70
|
+
builtWith: await fastagentVersion(),
|
|
71
|
+
name: config.name ?? (typeof pkg.name === "string" ? pkg.name : basename(dir)),
|
|
72
|
+
// A source project need not be published, so it need not have a version.
|
|
73
|
+
version: typeof pkg.version === "string" ? pkg.version : "0.0.0",
|
|
74
|
+
dir,
|
|
75
|
+
config,
|
|
76
|
+
registry,
|
|
77
|
+
});
|
|
171
78
|
}
|
|
172
79
|
/** What one value is once its secrets are supplied, or why it is not what its place holds. */
|
|
173
|
-
function built(source, value, values, accept) {
|
|
174
|
-
const resolved = isNeeds(value) ? value.build(values) : value;
|
|
80
|
+
function built(source, value, values, settings, accept) {
|
|
81
|
+
const resolved = isNeeds(value) ? value.build(values, settings) : value;
|
|
175
82
|
const problem = accept(resolved);
|
|
176
83
|
if (problem !== undefined)
|
|
177
84
|
throw new Error(`${source}: ${problem}`);
|
|
@@ -194,12 +101,38 @@ const channelProblem = channelModuleProblem;
|
|
|
194
101
|
const scheduleProblem = (value) => value?.kind !== "fastagent.schedule"
|
|
195
102
|
? "must default-export defineSchedule({ ... }), or needs(...) around one"
|
|
196
103
|
: scheduleConfigProblem(value);
|
|
104
|
+
/**
|
|
105
|
+
* The channel `name` as `config` has it: the way it chooses of one that receives either way, which must build the
|
|
106
|
+
* module that way receives with; or the one module there is, which must receive the way `config` names, when it names
|
|
107
|
+
* one.
|
|
108
|
+
*/
|
|
109
|
+
function chosenChannel(file, name, value, config, build) {
|
|
110
|
+
const shapeOf = (module) => channelIngress(module) === "webhook" ? "webhook" : "connection";
|
|
111
|
+
const by = (way) => (way === "webhook" ? "webhook" : "a connection it opens");
|
|
112
|
+
if (isByIngress(value)) {
|
|
113
|
+
const way = chosenIngress(config, name);
|
|
114
|
+
const module = build(value[way], `${file} (byIngress ${way})`);
|
|
115
|
+
if (shapeOf(module) !== way) {
|
|
116
|
+
throw new Error(`${file}: its ${way} way builds a channel that receives by ${by(shapeOf(module))}`);
|
|
117
|
+
}
|
|
118
|
+
return module;
|
|
119
|
+
}
|
|
120
|
+
const module = build(value, file);
|
|
121
|
+
const named = config.channels?.[name]?.ingress;
|
|
122
|
+
if (named !== undefined && shapeOf(module) !== named) {
|
|
123
|
+
throw new Error(`channels.${name}.ingress is "${named}", and ${file} receives only by ${by(shapeOf(module))}: default-export ` +
|
|
124
|
+
`byIngress({ webhook, connection }) from it to let an instance choose`);
|
|
125
|
+
}
|
|
126
|
+
return module;
|
|
127
|
+
}
|
|
197
128
|
/**
|
|
198
129
|
* Refuse to run while a required secret has no value, naming where it is declared; otherwise build every module with
|
|
199
|
-
* the values it declared, and refuse one that is not what its directory holds.
|
|
130
|
+
* the values it declared, and refuse one that is not what its directory holds. A channel that receives either way is
|
|
131
|
+
* built the way `config` chooses, and only that way's secrets are asked for.
|
|
200
132
|
*/
|
|
201
|
-
export function buildDefinition(definition, env =
|
|
202
|
-
const
|
|
133
|
+
export function buildDefinition(definition, env, instance = { settings: {} }) {
|
|
134
|
+
const { settings } = instance;
|
|
135
|
+
const missing = missingSecrets(codeSecrets(definition, instance), env);
|
|
203
136
|
if (missing.length > 0) {
|
|
204
137
|
throw new Error(`missing required secrets: ${describeSecrets(missing)} (set them in the environment)`);
|
|
205
138
|
}
|
|
@@ -207,27 +140,27 @@ export function buildDefinition(definition, env = process.env) {
|
|
|
207
140
|
const { registry, config } = definition;
|
|
208
141
|
const tools = registry.tools.map(({ file, value }) => ({
|
|
209
142
|
source: file,
|
|
210
|
-
...named(built(file, value, values(value), toolProblem)),
|
|
143
|
+
...named(built(file, value, values(value), settings, toolProblem)),
|
|
211
144
|
}));
|
|
212
145
|
const extensions = [
|
|
213
146
|
...registry.extensions.map(({ file, value }) => ({
|
|
214
147
|
source: file,
|
|
215
|
-
...named(built(file, value, values(value), extensionProblem)),
|
|
148
|
+
...named(built(file, value, values(value), settings, extensionProblem)),
|
|
216
149
|
})),
|
|
217
150
|
...(config.extensions ?? []).map((value, i) => {
|
|
218
151
|
const source = `${CONFIG_FILE} extensions[${i}]`;
|
|
219
|
-
return { source, ...named(built(source, value, values(value), extensionProblem)) };
|
|
152
|
+
return { source, ...named(built(source, value, values(value), settings, extensionProblem)) };
|
|
220
153
|
}),
|
|
221
154
|
];
|
|
222
155
|
const channels = registry.channels.map(({ file, name, value }) => ({
|
|
223
156
|
source: file,
|
|
224
157
|
name,
|
|
225
|
-
value:
|
|
158
|
+
value: chosenChannel(file, name, value, instance, (way, source) => built(source, way, values(way), settings, channelProblem)),
|
|
226
159
|
}));
|
|
227
160
|
const schedules = registry.schedules.map(({ file, name, value }) => ({
|
|
228
161
|
source: file,
|
|
229
162
|
name,
|
|
230
|
-
value: built(file, value, values(value), scheduleProblem),
|
|
163
|
+
value: built(file, value, values(value), settings, scheduleProblem),
|
|
231
164
|
}));
|
|
232
165
|
refuseTwins("tool", tools);
|
|
233
166
|
// pi-durable replaces an extension installed under a name already taken, its tools and sections with it.
|
|
@@ -245,7 +178,7 @@ export function buildDefinition(definition, env = process.env) {
|
|
|
245
178
|
return {
|
|
246
179
|
tools: tools.map(({ value }) => value),
|
|
247
180
|
extensions: extensions.map(({ value }) => value),
|
|
248
|
-
providers: (config.providers ?? []).map((value, i) => built(`${CONFIG_FILE} providers[${i}]`, value, values(value), providerProblem)),
|
|
181
|
+
providers: (config.providers ?? []).map((value, i) => built(`${CONFIG_FILE} providers[${i}]`, value, values(value), settings, providerProblem)),
|
|
249
182
|
channels: new Map(channels.map(({ name, value }) => [name, value])),
|
|
250
183
|
schedules: schedules.map(({ name, value: { kind: _kind, ...config } }) => ({ name, ...config })),
|
|
251
184
|
};
|
|
@@ -255,12 +188,15 @@ export function buildDefinition(definition, env = process.env) {
|
|
|
255
188
|
* providers): a declared secret `env` holds no value for has a stand-in, since the shape must not depend on whether
|
|
256
189
|
* this machine holds the values. Whatever a build refuses, this refuses too.
|
|
257
190
|
*/
|
|
258
|
-
export function buildDefinitionShape(definition, env
|
|
259
|
-
|
|
191
|
+
export function buildDefinitionShape(definition, env, config = {
|
|
192
|
+
settings: definition.config.defaults?.settings ?? {},
|
|
193
|
+
...(definition.config.defaults?.channels === undefined ? {} : { channels: definition.config.defaults.channels }),
|
|
194
|
+
}) {
|
|
195
|
+
const values = Object.fromEntries(definition.inputs.secrets.map(({ name }) => [name, "(no value here)"]));
|
|
260
196
|
for (const [name, value] of Object.entries(env))
|
|
261
197
|
if (value)
|
|
262
198
|
values[name] = value;
|
|
263
|
-
return buildDefinition(definition, values);
|
|
199
|
+
return buildDefinition(definition, values, config);
|
|
264
200
|
}
|
|
265
201
|
/**
|
|
266
202
|
* The providers of the definition in `agentDir`, for a command that reads models without serving (`models`, `login`,
|
|
@@ -268,8 +204,8 @@ export function buildDefinitionShape(definition, env) {
|
|
|
268
204
|
* it needs no secret the serve would. Without these a provider in code, or one replacing a built-in's id, would be
|
|
269
205
|
* unknown there, or verified against the built-in's endpoint.
|
|
270
206
|
*/
|
|
271
|
-
export async function definitionProviders(agentDir, env
|
|
272
|
-
return buildDefinitionShape(await
|
|
207
|
+
export async function definitionProviders(agentDir, env) {
|
|
208
|
+
return buildDefinitionShape(await loadSourceDefinition(agentDir), env).providers;
|
|
273
209
|
}
|
|
274
210
|
const named = (value) => ({ name: value.name, value });
|
|
275
211
|
/** Refuse two of a kind under one name: whichever is installed or kept last would silently stand in for the other. */
|
|
@@ -284,8 +220,9 @@ function refuseTwins(kind, entries) {
|
|
|
284
220
|
}
|
|
285
221
|
/** Open the definition as an Agent over `models`: the coding tools, then its own. */
|
|
286
222
|
export async function assembleAgent(definition, options) {
|
|
287
|
-
const { dir
|
|
288
|
-
const { code, models } = options;
|
|
223
|
+
const { dir } = definition;
|
|
224
|
+
const { code, models, config } = options;
|
|
225
|
+
const text = options.text ?? programText(dir);
|
|
289
226
|
const spec = options.model ?? config.model;
|
|
290
227
|
const model = spec === undefined ? undefined : modelRefOf(spec, "the default model");
|
|
291
228
|
if (model !== undefined && models.getModel(model.provider, model.modelId) === undefined) {
|
|
@@ -293,13 +230,23 @@ export async function assembleAgent(definition, options) {
|
|
|
293
230
|
throw new Error(`unknown model "${spec}": no provider "${model.provider}" offers "${model.modelId}"${stale}`);
|
|
294
231
|
}
|
|
295
232
|
const ownWake = [...code.tools, ...code.extensions.flatMap((extension) => extension.tools ?? [])].some(({ name }) => WAKE_TOOLS.includes(name));
|
|
233
|
+
const authoredNames = new Set([...code.tools, ...code.extensions.flatMap((extension) => extension.tools ?? [])].map((tool) => tool.name));
|
|
234
|
+
for (const tool of options.mcpTools ?? []) {
|
|
235
|
+
if (authoredNames.has(tool.name))
|
|
236
|
+
throw new Error(`MCP tool ${tool.name} conflicts with an authored tool`);
|
|
237
|
+
}
|
|
296
238
|
const registry = createRegistry();
|
|
297
239
|
registry.install(CodingTools);
|
|
298
240
|
// After the coding tools, so a tool of the definition's own with one of their names replaces it.
|
|
299
241
|
registry.install(defineExtension({
|
|
300
242
|
name: DEFINITION_EXTENSION,
|
|
301
|
-
sections: definitionSections(dir, options.content ?? [], {
|
|
302
|
-
|
|
243
|
+
sections: definitionSections(dir, options.content ?? [], {
|
|
244
|
+
wake: ownWake || options.stateRoot !== undefined,
|
|
245
|
+
runsAs: options.runsAs,
|
|
246
|
+
workdir: options.workdir ?? dir,
|
|
247
|
+
text,
|
|
248
|
+
}),
|
|
249
|
+
tools: [...code.tools, ...(options.mcpTools ?? [])],
|
|
303
250
|
}));
|
|
304
251
|
for (const extension of code.extensions)
|
|
305
252
|
registry.install(extension);
|
|
@@ -311,14 +258,14 @@ export async function assembleAgent(definition, options) {
|
|
|
311
258
|
storage: options.storage,
|
|
312
259
|
models,
|
|
313
260
|
registry,
|
|
314
|
-
name: definition.name,
|
|
315
|
-
...(config.description === undefined ? {} : { description: config.description }),
|
|
261
|
+
name: options.name ?? instanceName(definition.name),
|
|
262
|
+
...(definition.config.description === undefined ? {} : { description: definition.config.description }),
|
|
316
263
|
...(model === undefined ? {} : { model }),
|
|
317
264
|
...(thinkingLevel === undefined ? {} : { thinkingLevel }),
|
|
318
265
|
...(options.settings === undefined ? {} : { settings: options.settings }),
|
|
319
266
|
...(options.unfinished === undefined ? {} : { unfinished: options.unfinished }),
|
|
320
267
|
// What the agent runs is not `dev`'s own serving, even when `dev` is what runs the agent.
|
|
321
|
-
env: ({ cwd }) => new
|
|
322
|
-
commands: () => readCommands(
|
|
268
|
+
env: ({ cwd }) => new InstanceExecutionEnv({ cwd: cwd ?? options.workdir ?? dir, processEnv: options.processEnv }),
|
|
269
|
+
commands: () => readCommands(text),
|
|
323
270
|
});
|
|
324
271
|
}
|
|
@@ -24,7 +24,6 @@ export declare function durableReads(harness: Harness, ids: Ids, sessions: Pick<
|
|
|
24
24
|
message: (record: InputRecord, known?: readonly EntryRecord[]) => Effect.Effect<Message, PortFailure>;
|
|
25
25
|
submission: (messageId: string) => Effect.Effect<InputRecord | undefined, PortFailure, never>;
|
|
26
26
|
submissionByKey: (conversation: Conversation, requestId: string) => Effect.Effect<InputRecord | undefined, PortFailure, never>;
|
|
27
|
-
inbox: (conversation: Conversation) => Effect.Effect<Readonly<import("@earendil-works/pi-durable").InboxState> | undefined, PortFailure, never>;
|
|
28
27
|
session: (conversation: Pick<Conversation, "id">) => Effect.Effect<Session, PortFailure>;
|
|
29
28
|
sessionsOf: (conversations: readonly ConversationId[]) => Effect.Effect<Session[], PortFailure>;
|
|
30
29
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentDoc,
|
|
1
|
+
import { AgentDoc, LiveDoc, UsageDoc, } from "@earendil-works/pi-durable";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Option from "effect/Option";
|
|
4
4
|
import * as Stream from "effect/Stream";
|
|
@@ -63,7 +63,6 @@ export function durableReads(harness, ids, sessions, context) {
|
|
|
63
63
|
return port(() => harness.submission(id, context)).pipe(Effect.flatMap((handle) => (handle === undefined ? Effect.undefined : port(() => handle.status(context)))), Effect.map((record) => (isInput(record) ? record : undefined)));
|
|
64
64
|
};
|
|
65
65
|
const submissionByKey = (conversation, requestId) => port(() => harness.commit((tx) => tx.submissionByRequest(conversation.id, requestId), context)).pipe(Effect.map((record) => (isInput(record) ? record : undefined)));
|
|
66
|
-
const inbox = (conversation) => port(() => harness.snapshot(InboxDoc, conversation.id, context));
|
|
67
66
|
/**
|
|
68
67
|
* The sessions as they are now, in one commit: each one's status, reply and settings of one moment, and a page of
|
|
69
68
|
* them one turn on pi-durable's commit line, which every running session writes through too.
|
|
@@ -87,7 +86,6 @@ export function durableReads(harness, ids, sessions, context) {
|
|
|
87
86
|
message,
|
|
88
87
|
submission,
|
|
89
88
|
submissionByKey,
|
|
90
|
-
inbox,
|
|
91
89
|
session,
|
|
92
90
|
sessionsOf,
|
|
93
91
|
};
|
|
@@ -9,10 +9,8 @@ interface RegisteredModule {
|
|
|
9
9
|
value: unknown;
|
|
10
10
|
}
|
|
11
11
|
export type Registry = Record<RegistryKind, readonly RegisteredModule[]>;
|
|
12
|
-
|
|
13
|
-
export declare
|
|
14
|
-
/**
|
|
15
|
-
export declare function
|
|
16
|
-
/** Write `.fastagent/registry.ts` for the agent in `dir`, and return its path. */
|
|
17
|
-
export declare function generateRegistry(dir: string): Promise<string>;
|
|
12
|
+
/** The module files of the source project in `dir`, project-relative, in the order the registry imports them. */
|
|
13
|
+
export declare function registryFiles(dir: string): Promise<string[]>;
|
|
14
|
+
/** Import every module of the source project in `dir`, in the order the files sort, so each opens the same way. */
|
|
15
|
+
export declare function importRegistry(dir: string): Promise<Registry>;
|
|
18
16
|
export {};
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
1
9
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* `src/
|
|
4
|
-
*
|
|
5
|
-
* module imports (a helper) lives outside these directories. Generated from the files, never edited.
|
|
10
|
+
* The registry (architecture.md §3): every module in `src/tools/`, `src/extensions/`, `src/channels/` and
|
|
11
|
+
* `src/schedules/` with what it default-exports, so nothing is imported by hand. A file directly in the directory is a
|
|
12
|
+
* module, and anything else a module imports (a helper) lives outside these directories.
|
|
6
13
|
*/
|
|
7
14
|
import { readdir } from "node:fs/promises";
|
|
8
|
-
import { writeFileAtomic } from "../../atomic-write.js";
|
|
9
15
|
import { join } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
10
17
|
export const REGISTRY_KINDS = ["tools", "extensions", "channels", "schedules"];
|
|
11
|
-
export const GENERATED_DIR = ".fastagent";
|
|
12
|
-
export const REGISTRY_FILE = "registry.ts";
|
|
13
18
|
const MODULE = /^([^.][^/]*)\.(ts|mts|js|mjs)$/;
|
|
14
19
|
const NOT_A_MODULE = /\.(d|test|spec)\.[a-z]+$/;
|
|
15
20
|
/** The module files of one directory of `src/`, sorted so the registry is the same wherever it is generated. */
|
|
@@ -30,27 +35,25 @@ async function modulesIn(dir, kind) {
|
|
|
30
35
|
: [{ file: `src/${kind}/${entry}`, name: match[1] }];
|
|
31
36
|
});
|
|
32
37
|
}
|
|
33
|
-
/** The
|
|
34
|
-
export async function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
/** The module files of the source project in `dir`, project-relative, in the order the registry imports them. */
|
|
39
|
+
export async function registryFiles(dir) {
|
|
40
|
+
return (await Promise.all(REGISTRY_KINDS.map((kind) => modulesIn(dir, kind)))).flatMap((files) => files.map(({ file }) => file));
|
|
41
|
+
}
|
|
42
|
+
/** Import every module of the source project in `dir`, in the order the files sort, so each opens the same way. */
|
|
43
|
+
export async function importRegistry(dir) {
|
|
44
|
+
const registry = {};
|
|
38
45
|
for (const kind of REGISTRY_KINDS) {
|
|
39
|
-
|
|
46
|
+
registry[kind] = [];
|
|
40
47
|
for (const { file, name } of await modulesIn(dir, kind)) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(dir, file)).href))).default;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
54
|
+
}
|
|
55
|
+
registry[kind].push({ file, name, value });
|
|
44
56
|
}
|
|
45
|
-
groups.push(`export const ${kind} = [\n${entries.join("\n")}${entries.length > 0 ? "\n" : ""}];`);
|
|
46
57
|
}
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
/** Write `.fastagent/registry.ts` for the agent in `dir`, and return its path. */
|
|
50
|
-
export async function generateRegistry(dir) {
|
|
51
|
-
const path = join(dir, GENERATED_DIR, REGISTRY_FILE);
|
|
52
|
-
// Every command that reads the definition writes it, `dev`'s worker importing it meanwhile: a reader must see a whole
|
|
53
|
-
// file, and two writers write the same bytes.
|
|
54
|
-
writeFileAtomic(path, await registrySource(dir), undefined, true);
|
|
55
|
-
return path;
|
|
58
|
+
return registry;
|
|
56
59
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Temporary: pi-durable leaves a conversation's queued messages waiting after a run that did not answer (a model
|
|
3
3
|
* failure, an abort, a message sent while an aborted run was still ending) until another submission places them, while
|
|
4
4
|
* the Agent API takes them whatever the message before them ended with (SPEC.md §5.3). A write of FastAgent's own
|
|
5
|
-
* kind places them. pi-durable 1.
|
|
6
|
-
*
|
|
5
|
+
* kind places them. pi-durable 1.5.0's `settings.afterFailedRun` covers a failed run (a model failure, a crash) and
|
|
6
|
+
* not an abort: remove this once it does.
|
|
7
7
|
*/
|
|
8
8
|
import type { Context } from "@earendil-works/chord/context";
|
|
9
9
|
import { type Harness } from "@earendil-works/pi-durable";
|
|
@@ -8,15 +8,18 @@ import { queuedInputs } from "./messages.js";
|
|
|
8
8
|
/** Release every conversation that waits now, and each one a run that did not answer leaves waiting, until the scope closes. */
|
|
9
9
|
export const releaseWaitingQueues = (harness, context) => Effect.gen(function* () {
|
|
10
10
|
const waiting = yield* Queue.unbounded();
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
const observation = harness.observeCommits({
|
|
12
|
+
advance: ({ changes }) => {
|
|
13
|
+
for (const change of changes) {
|
|
14
|
+
const record = change.type === "submission" ? change.value : undefined;
|
|
15
|
+
if (record?.type === "input" && record.status === "unanswered") {
|
|
16
|
+
Queue.offerUnsafe(waiting, record.conversationId);
|
|
17
|
+
}
|
|
16
18
|
}
|
|
17
|
-
}
|
|
19
|
+
},
|
|
20
|
+
end: () => { },
|
|
18
21
|
});
|
|
19
|
-
yield* Effect.addFinalizer(() => Effect.sync(
|
|
22
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => observation.detach()));
|
|
20
23
|
yield* Queue.take(waiting).pipe(Effect.flatMap((conversationId) => release(harness, conversationId, context).pipe(Effect.catchCause((cause) => Effect.sync(() => log.error(`[fastagent] could not take the messages waiting in conversation ${conversationId}: ${String(portError(cause))}`))))), Effect.forever, Effect.forkScoped);
|
|
21
24
|
const inspection = yield* port(() => harness.inspect(context));
|
|
22
25
|
for (const record of inspection.submissions) {
|