@fastagent-sh/fastagent 0.29.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 +9 -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 +2 -1
- package/dist/channels/feishu/feishu.d.ts +0 -3
- package/dist/channels/feishu/feishu.js +10 -10
- package/dist/channels/feishu/register-app.js +3 -5
- 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/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 -303
- 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 -60
- 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 +60 -25
- 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.js +14 -6
- 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 +282 -110
- 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 +25 -1
- 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 -15
- 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 +79 -37
- 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 +5 -43
- package/dist/deploy/docker/run.js +9 -84
- 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 -8
- package/dist/deploy/fly/run.js +24 -42
- 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 -8
- 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.js +1 -3
- package/dist/harnesses/pi-durable/open.d.ts +35 -37
- package/dist/harnesses/pi-durable/open.js +91 -153
- 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 +91 -15
- package/dist/harnesses/pi-durable/serve.js +109 -47
- 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/contract.d.ts +3 -3
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +4 -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/mcp/client.js +11 -2
- package/dist/mcp/config.d.ts +70 -18
- package/dist/mcp/config.js +136 -53
- package/dist/mcp/host.d.ts +5 -2
- package/dist/mcp/host.js +14 -6
- package/dist/mcp/sign-in.js +2 -2
- package/dist/mcp-api.d.ts +14 -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/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 +11 -6
- package/dist/value-file.js +21 -9
- package/dist/version.d.ts +7 -1
- package/dist/version.js +9 -3
- package/package.json +6 -5
- package/dist/channels/lark/credentials-page.d.ts +0 -25
- package/dist/channels/lark/credentials-page.js +0 -206
- package/dist/channels/lark/onboard.d.ts +0 -52
- package/dist/channels/lark/onboard.js +0 -86
- 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 -246
- package/dist/cli/add-feishu.d.ts +0 -41
- package/dist/cli/add-feishu.js +0 -391
- 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,36 +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
|
-
import { mcpSecrets } from "../../mcp/config.js";
|
|
23
19
|
import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
|
|
24
|
-
import {
|
|
25
|
-
import { channelModuleProblem } from "../../channels/discover.js";
|
|
20
|
+
import { channelIngress, channelModuleProblem } from "../../channels/discover.js";
|
|
26
21
|
import { AGENT_CONFIG_FILE as CONFIG_FILE } from "../../paths.js";
|
|
27
22
|
import { readPackageJson } from "../../runtime.js";
|
|
28
|
-
import {
|
|
23
|
+
import { fastagentVersion } from "../../version.js";
|
|
24
|
+
import { InstanceExecutionEnv } from "./execution-env.js";
|
|
25
|
+
import { instanceName } from "../../instance-name.js";
|
|
29
26
|
import { modelRefOf } from "../../model-ref.js";
|
|
30
27
|
import { MAX_SCHEDULES, scheduleConfigProblem } from "../../schedule/schedule.js";
|
|
31
28
|
import { openDurableAgent } from "./agent.js";
|
|
32
|
-
import { isNeeds } from "./define.js";
|
|
33
|
-
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";
|
|
34
33
|
import { definitionSections, readCommands } from "./text.js";
|
|
34
|
+
import { programText } from "./text-sources.js";
|
|
35
35
|
import { WAKE_EXTENSION, WAKE_TOOLS, wakeExtension } from "./wake.js";
|
|
36
|
-
const SECRETS_MANIFEST_FILE = "secrets.json";
|
|
37
|
-
const CONFIG_KEYS = ["name", "description", "model", "thinkingLevel", "extensions", "providers", "http", "deploy"];
|
|
38
36
|
const DEFINITION_EXTENSION = "fastagent.definition";
|
|
39
|
-
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
40
37
|
async function importDefault(path, what) {
|
|
41
38
|
try {
|
|
42
39
|
return (await import(__rewriteRelativeImportExtension(pathToFileURL(path).href))).default;
|
|
@@ -45,137 +42,43 @@ async function importDefault(path, what) {
|
|
|
45
42
|
throw new Error(`${what}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
|
-
/** An object whose keys are among `keys`, or the refusal naming the one that is not and the ones there are. */
|
|
49
|
-
function checkKeys(value, keys, where) {
|
|
50
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
51
|
-
throw new Error(`${where} must be an object`);
|
|
52
|
-
for (const key of Object.keys(value)) {
|
|
53
|
-
if (!keys.includes(key))
|
|
54
|
-
throw new Error(`${where}: unknown key "${key}" (keys: ${keys.join(", ")})`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/** Refuse what a type check would have: Node strips types without checking them, so a typo reaches here. */
|
|
58
|
-
function checkConfig(config, file) {
|
|
59
|
-
if (config === undefined)
|
|
60
|
-
return {};
|
|
61
|
-
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
62
|
-
throw new Error(`${file} must default-export defineAgent({ ... })`);
|
|
63
|
-
}
|
|
64
|
-
checkKeys(config, CONFIG_KEYS, file);
|
|
65
|
-
const { model, thinkingLevel, extensions, providers, http, deploy } = config;
|
|
66
|
-
if (http !== undefined) {
|
|
67
|
-
checkKeys(http, ["port", "api"], `${file}: "http"`);
|
|
68
|
-
const port = http.port;
|
|
69
|
-
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
70
|
-
throw new Error(`${file}: "http.port" must be an integer 0-65535`);
|
|
71
|
-
}
|
|
72
|
-
if (http.api !== undefined && typeof http.api !== "boolean") {
|
|
73
|
-
throw new Error(`${file}: "http.api" must be true or false`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (deploy !== undefined) {
|
|
77
|
-
checkKeys(deploy, ["agentcore"], `${file}: "deploy"`);
|
|
78
|
-
if (deploy.agentcore !== undefined) {
|
|
79
|
-
checkKeys(deploy.agentcore, ["idleTimeoutSeconds"], `${file}: "deploy.agentcore"`);
|
|
80
|
-
// AWS's own bounds for idleRuntimeSessionTimeout: a value outside them is refused by CloudFormation minutes
|
|
81
|
-
// into `deploy agentcore --run`, after the image build.
|
|
82
|
-
const idle = deploy.agentcore.idleTimeoutSeconds;
|
|
83
|
-
if (idle !== undefined && (!Number.isInteger(idle) || idle < 60 || idle > 1209600)) {
|
|
84
|
-
throw new Error(`${file}: "deploy.agentcore.idleTimeoutSeconds" must be an integer 60-1209600 (seconds)`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (model !== undefined)
|
|
89
|
-
modelRefOf(model, `${file}: "model"`);
|
|
90
|
-
if (thinkingLevel !== undefined && !THINKING_LEVELS.includes(thinkingLevel)) {
|
|
91
|
-
throw new Error(`${file}: "thinkingLevel" must be one of ${THINKING_LEVELS.join(", ")}`);
|
|
92
|
-
}
|
|
93
|
-
for (const [key, list] of [
|
|
94
|
-
["extensions", extensions],
|
|
95
|
-
["providers", providers],
|
|
96
|
-
]) {
|
|
97
|
-
if (list !== undefined && !Array.isArray(list))
|
|
98
|
-
throw new Error(`${file}: "${key}" must be an array`);
|
|
99
|
-
}
|
|
100
|
-
return config;
|
|
101
|
-
}
|
|
102
45
|
/** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
|
|
103
|
-
|
|
46
|
+
async function loadConfig(dir) {
|
|
104
47
|
const configPath = join(dir, CONFIG_FILE);
|
|
105
|
-
return
|
|
48
|
+
return checkProgramConfig(existsSync(configPath) ? await importDefault(configPath, CONFIG_FILE) : undefined, CONFIG_FILE);
|
|
106
49
|
}
|
|
107
50
|
/** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
|
|
108
51
|
export function modelSpecFor(config, flag, env = process.env) {
|
|
109
52
|
return flag ?? (env.FASTAGENT_MODEL || config.model);
|
|
110
53
|
}
|
|
111
|
-
/**
|
|
112
|
-
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) {
|
|
113
62
|
// Absolute, since the prompt names the definition's files by it and the agent's tools resolve a relative path against
|
|
114
63
|
// the agent's own working directory, which is this one.
|
|
115
64
|
const dir = resolve(directory);
|
|
116
65
|
const config = await loadConfig(dir);
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
const packageName = (await readPackageJson(dir));
|
|
132
|
-
const name = config.name ?? (typeof packageName.name === "string" ? packageName.name : basename(dir));
|
|
133
|
-
return { dir, name, config, registry };
|
|
134
|
-
}
|
|
135
|
-
/** Every value the definition can hold `Needs` in, with where it is: modules by file, configuration by key. */
|
|
136
|
-
function declarers(definition) {
|
|
137
|
-
return [
|
|
138
|
-
...REGISTRY_KINDS.flatMap((kind) => definition.registry[kind].map(({ file, value }) => ({ source: file, value }))),
|
|
139
|
-
...(definition.config.extensions ?? []).map((value, i) => ({ source: `${CONFIG_FILE} extensions[${i}]`, value })),
|
|
140
|
-
...(definition.config.providers ?? []).map((value, i) => ({ source: `${CONFIG_FILE} providers[${i}]`, value })),
|
|
141
|
-
];
|
|
142
|
-
}
|
|
143
|
-
/** The secrets the definition declares, wherever: its code's `needs`, and `context.json`'s command variables. */
|
|
144
|
-
function codeSecrets(definition) {
|
|
145
|
-
return [
|
|
146
|
-
...declarers(definition).flatMap(({ source, value }) => isNeeds(value) ? value.secrets.map((secret) => ({ ...secret, source })) : []),
|
|
147
|
-
...declaredVariables(loadEnvironment(definition.dir)),
|
|
148
|
-
];
|
|
149
|
-
}
|
|
150
|
-
export function definitionSecrets(definition) {
|
|
151
|
-
return [...codeSecrets(definition), ...mcpSecrets(definition.dir)];
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* The list a person deploying or running the agent reads instead of its code (`.fastagent/secrets.json`): each name
|
|
155
|
-
* once, what it is for, whether the agent runs without it, and where it is declared.
|
|
156
|
-
*/
|
|
157
|
-
export function secretsManifest(definition) {
|
|
158
|
-
const byName = new Map();
|
|
159
|
-
for (const secret of definitionSecrets(definition)) {
|
|
160
|
-
const entry = byName.get(secret.name) ?? { name: secret.name, optional: true, declaredIn: [] };
|
|
161
|
-
entry.optional &&= secret.optional === true;
|
|
162
|
-
entry.declaredIn.push(secret.source);
|
|
163
|
-
if (entry.description === undefined && secret.description !== undefined)
|
|
164
|
-
entry.description = secret.description;
|
|
165
|
-
byName.set(secret.name, entry);
|
|
166
|
-
}
|
|
167
|
-
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
168
|
-
}
|
|
169
|
-
/** Generate the registry, read the definition, and write its secrets manifest beside the registry. */
|
|
170
|
-
export async function prepareDefinition(dir) {
|
|
171
|
-
await generateRegistry(dir);
|
|
172
|
-
const definition = await loadDefinition(dir);
|
|
173
|
-
writeFileAtomic(join(dir, GENERATED_DIR, SECRETS_MANIFEST_FILE), `${JSON.stringify(secretsManifest(definition), null, 2)}\n`, undefined, true);
|
|
174
|
-
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
|
+
});
|
|
175
78
|
}
|
|
176
79
|
/** What one value is once its secrets are supplied, or why it is not what its place holds. */
|
|
177
|
-
function built(source, value, values, accept) {
|
|
178
|
-
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;
|
|
179
82
|
const problem = accept(resolved);
|
|
180
83
|
if (problem !== undefined)
|
|
181
84
|
throw new Error(`${source}: ${problem}`);
|
|
@@ -198,12 +101,38 @@ const channelProblem = channelModuleProblem;
|
|
|
198
101
|
const scheduleProblem = (value) => value?.kind !== "fastagent.schedule"
|
|
199
102
|
? "must default-export defineSchedule({ ... }), or needs(...) around one"
|
|
200
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
|
+
}
|
|
201
128
|
/**
|
|
202
129
|
* Refuse to run while a required secret has no value, naming where it is declared; otherwise build every module with
|
|
203
|
-
* 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.
|
|
204
132
|
*/
|
|
205
|
-
export function buildDefinition(definition, env =
|
|
206
|
-
const
|
|
133
|
+
export function buildDefinition(definition, env, instance = { settings: {} }) {
|
|
134
|
+
const { settings } = instance;
|
|
135
|
+
const missing = missingSecrets(codeSecrets(definition, instance), env);
|
|
207
136
|
if (missing.length > 0) {
|
|
208
137
|
throw new Error(`missing required secrets: ${describeSecrets(missing)} (set them in the environment)`);
|
|
209
138
|
}
|
|
@@ -211,27 +140,27 @@ export function buildDefinition(definition, env = process.env) {
|
|
|
211
140
|
const { registry, config } = definition;
|
|
212
141
|
const tools = registry.tools.map(({ file, value }) => ({
|
|
213
142
|
source: file,
|
|
214
|
-
...named(built(file, value, values(value), toolProblem)),
|
|
143
|
+
...named(built(file, value, values(value), settings, toolProblem)),
|
|
215
144
|
}));
|
|
216
145
|
const extensions = [
|
|
217
146
|
...registry.extensions.map(({ file, value }) => ({
|
|
218
147
|
source: file,
|
|
219
|
-
...named(built(file, value, values(value), extensionProblem)),
|
|
148
|
+
...named(built(file, value, values(value), settings, extensionProblem)),
|
|
220
149
|
})),
|
|
221
150
|
...(config.extensions ?? []).map((value, i) => {
|
|
222
151
|
const source = `${CONFIG_FILE} extensions[${i}]`;
|
|
223
|
-
return { source, ...named(built(source, value, values(value), extensionProblem)) };
|
|
152
|
+
return { source, ...named(built(source, value, values(value), settings, extensionProblem)) };
|
|
224
153
|
}),
|
|
225
154
|
];
|
|
226
155
|
const channels = registry.channels.map(({ file, name, value }) => ({
|
|
227
156
|
source: file,
|
|
228
157
|
name,
|
|
229
|
-
value:
|
|
158
|
+
value: chosenChannel(file, name, value, instance, (way, source) => built(source, way, values(way), settings, channelProblem)),
|
|
230
159
|
}));
|
|
231
160
|
const schedules = registry.schedules.map(({ file, name, value }) => ({
|
|
232
161
|
source: file,
|
|
233
162
|
name,
|
|
234
|
-
value: built(file, value, values(value), scheduleProblem),
|
|
163
|
+
value: built(file, value, values(value), settings, scheduleProblem),
|
|
235
164
|
}));
|
|
236
165
|
refuseTwins("tool", tools);
|
|
237
166
|
// pi-durable replaces an extension installed under a name already taken, its tools and sections with it.
|
|
@@ -249,7 +178,7 @@ export function buildDefinition(definition, env = process.env) {
|
|
|
249
178
|
return {
|
|
250
179
|
tools: tools.map(({ value }) => value),
|
|
251
180
|
extensions: extensions.map(({ value }) => value),
|
|
252
|
-
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)),
|
|
253
182
|
channels: new Map(channels.map(({ name, value }) => [name, value])),
|
|
254
183
|
schedules: schedules.map(({ name, value: { kind: _kind, ...config } }) => ({ name, ...config })),
|
|
255
184
|
};
|
|
@@ -259,12 +188,15 @@ export function buildDefinition(definition, env = process.env) {
|
|
|
259
188
|
* providers): a declared secret `env` holds no value for has a stand-in, since the shape must not depend on whether
|
|
260
189
|
* this machine holds the values. Whatever a build refuses, this refuses too.
|
|
261
190
|
*/
|
|
262
|
-
export function buildDefinitionShape(definition, env
|
|
263
|
-
|
|
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)"]));
|
|
264
196
|
for (const [name, value] of Object.entries(env))
|
|
265
197
|
if (value)
|
|
266
198
|
values[name] = value;
|
|
267
|
-
return buildDefinition(definition, values);
|
|
199
|
+
return buildDefinition(definition, values, config);
|
|
268
200
|
}
|
|
269
201
|
/**
|
|
270
202
|
* The providers of the definition in `agentDir`, for a command that reads models without serving (`models`, `login`,
|
|
@@ -272,8 +204,8 @@ export function buildDefinitionShape(definition, env) {
|
|
|
272
204
|
* it needs no secret the serve would. Without these a provider in code, or one replacing a built-in's id, would be
|
|
273
205
|
* unknown there, or verified against the built-in's endpoint.
|
|
274
206
|
*/
|
|
275
|
-
export async function definitionProviders(agentDir, env
|
|
276
|
-
return buildDefinitionShape(await
|
|
207
|
+
export async function definitionProviders(agentDir, env) {
|
|
208
|
+
return buildDefinitionShape(await loadSourceDefinition(agentDir), env).providers;
|
|
277
209
|
}
|
|
278
210
|
const named = (value) => ({ name: value.name, value });
|
|
279
211
|
/** Refuse two of a kind under one name: whichever is installed or kept last would silently stand in for the other. */
|
|
@@ -288,8 +220,9 @@ function refuseTwins(kind, entries) {
|
|
|
288
220
|
}
|
|
289
221
|
/** Open the definition as an Agent over `models`: the coding tools, then its own. */
|
|
290
222
|
export async function assembleAgent(definition, options) {
|
|
291
|
-
const { dir
|
|
292
|
-
const { code, models } = options;
|
|
223
|
+
const { dir } = definition;
|
|
224
|
+
const { code, models, config } = options;
|
|
225
|
+
const text = options.text ?? programText(dir);
|
|
293
226
|
const spec = options.model ?? config.model;
|
|
294
227
|
const model = spec === undefined ? undefined : modelRefOf(spec, "the default model");
|
|
295
228
|
if (model !== undefined && models.getModel(model.provider, model.modelId) === undefined) {
|
|
@@ -307,7 +240,12 @@ export async function assembleAgent(definition, options) {
|
|
|
307
240
|
// After the coding tools, so a tool of the definition's own with one of their names replaces it.
|
|
308
241
|
registry.install(defineExtension({
|
|
309
242
|
name: DEFINITION_EXTENSION,
|
|
310
|
-
sections: definitionSections(dir, options.content ?? [], {
|
|
243
|
+
sections: definitionSections(dir, options.content ?? [], {
|
|
244
|
+
wake: ownWake || options.stateRoot !== undefined,
|
|
245
|
+
runsAs: options.runsAs,
|
|
246
|
+
workdir: options.workdir ?? dir,
|
|
247
|
+
text,
|
|
248
|
+
}),
|
|
311
249
|
tools: [...code.tools, ...(options.mcpTools ?? [])],
|
|
312
250
|
}));
|
|
313
251
|
for (const extension of code.extensions)
|
|
@@ -320,14 +258,14 @@ export async function assembleAgent(definition, options) {
|
|
|
320
258
|
storage: options.storage,
|
|
321
259
|
models,
|
|
322
260
|
registry,
|
|
323
|
-
name: definition.name,
|
|
324
|
-
...(config.description === undefined ? {} : { description: config.description }),
|
|
261
|
+
name: options.name ?? instanceName(definition.name),
|
|
262
|
+
...(definition.config.description === undefined ? {} : { description: definition.config.description }),
|
|
325
263
|
...(model === undefined ? {} : { model }),
|
|
326
264
|
...(thinkingLevel === undefined ? {} : { thinkingLevel }),
|
|
327
265
|
...(options.settings === undefined ? {} : { settings: options.settings }),
|
|
328
266
|
...(options.unfinished === undefined ? {} : { unfinished: options.unfinished }),
|
|
329
267
|
// What the agent runs is not `dev`'s own serving, even when `dev` is what runs the agent.
|
|
330
|
-
env: ({ cwd }) => new
|
|
331
|
-
commands: () => readCommands(
|
|
268
|
+
env: ({ cwd }) => new InstanceExecutionEnv({ cwd: cwd ?? options.workdir ?? dir, processEnv: options.processEnv }),
|
|
269
|
+
commands: () => readCommands(text),
|
|
332
270
|
});
|
|
333
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) {
|