@fastagent-sh/fastagent 0.27.0 → 0.28.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 +2 -2
- package/dist/authoring.d.ts +14 -7
- package/dist/authoring.js +11 -7
- package/dist/cli/commands/content.js +1 -1
- package/dist/cli/commands/invoke.js +5 -3
- package/dist/cli/content-view.d.ts +1 -1
- package/dist/cli/content-view.js +2 -2
- package/dist/cli/program.js +6 -9
- package/dist/content/declare.d.ts +1 -6
- package/dist/content/declare.js +13 -18
- package/dist/content/resolve.d.ts +0 -2
- package/dist/content/resolve.js +2 -4
- package/dist/content/source.d.ts +0 -1
- package/dist/content/source.js +0 -1
- package/dist/deploy/preflight.js +6 -14
- package/dist/harnesses/pi-durable/open.d.ts +2 -3
- package/dist/harnesses/pi-durable/open.js +1 -7
- package/dist/harnesses/pi-durable/serve.d.ts +1 -1
- package/dist/harnesses/pi-durable/text.js +7 -13
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -1
- package/dist/model-ref.d.ts +3 -0
- package/dist/model-ref.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -152,9 +152,9 @@ The package exports the supported surface only, from one entry per kind of user,
|
|
|
152
152
|
| The Agent API | `Agent`, `AgentError` ([Agent API](docs/SPEC.md)) | Stable within its version |
|
|
153
153
|
| Directory → agent → service | `openAgent`, `LocalAgent`, `serve()`'s `Service` (`fetch`, `listen`) | Usable now, may tighten before 1.0 |
|
|
154
154
|
| Over HTTP | `connectAgent` (`/client`), the `/v1` routes ([Agent API](docs/SPEC.md) §9) | Stable within its version |
|
|
155
|
-
| A host embedding agents | `createAgent`, `CredentialStore`, `credentialFile`, `signIn`, `signInOptions`, `providerAuth` ([embedding](docs/embedding.md#6-a-desktop-host)) | Usable now, may tighten before 1.0 |
|
|
155
|
+
| A host embedding agents | `createAgent`, `addContent`, `removeContent`, `listContent`, `CredentialStore`, `credentialFile`, `signIn`, `signInOptions`, `providerAuth` ([embedding](docs/embedding.md#6-a-desktop-host)) | Usable now, may tighten before 1.0 |
|
|
156
156
|
| Authoring | `defineAgent`, `defineChannel`, `defineSchedule`, `needs`, `sessionIdOf`, `readBodyCapped`, `text`, `secretEquals` | Usable now, may tighten before 1.0 |
|
|
157
|
-
| Not exported | The assembly's parts (`router`, `createScheduler`),
|
|
157
|
+
| Not exported | The assembly's parts (`router`, `createScheduler`), the environment's edit (`writeEnvironment`), the model environment, harness internals | No compatibility promise |
|
|
158
158
|
|
|
159
159
|
A tool or an extension is pi-durable's own (`defineTool`, `defineExtension` from `@earendil-works/pi-durable`), and a
|
|
160
160
|
custom provider is pi-ai's (`createProvider` from `@earendil-works/pi-ai`): `init` adds both packages to the agent.
|
package/dist/authoring.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare class ContentNameError extends Error {
|
|
|
10
10
|
name: string;
|
|
11
11
|
}
|
|
12
12
|
export interface InitAgentOptions {
|
|
13
|
-
/** What the agent works on
|
|
13
|
+
/** What the agent works on: repositories into `context.json`, this machine's directories into
|
|
14
14
|
* `context.local.json`. */
|
|
15
15
|
content?: ContentAddition[];
|
|
16
16
|
/**
|
|
@@ -36,26 +36,33 @@ export interface CreatedAgent {
|
|
|
36
36
|
* Create an agent in `dir` as `fastagent init` does, which must be new or empty: the whole scaffold, and a git
|
|
37
37
|
* repository whose first commit is what was written, since the agent changes itself and version control is how its
|
|
38
38
|
* author goes back to a version that worked. It installs no dependencies; the agent opens without them.
|
|
39
|
+
*
|
|
40
|
+
* `content` gives it any number of entries, each a directory of this machine or a GitHub repository. They are all
|
|
41
|
+
* checked before anything is written, and one that is refused leaves no directory. Two entries may bind the same
|
|
42
|
+
* directory, or one inside the other; the agent then reaches it by both names.
|
|
39
43
|
*/
|
|
40
|
-
export declare function createAgent(dir: string
|
|
44
|
+
export declare function createAgent(dir: string, options?: {
|
|
45
|
+
content?: ContentAddition[];
|
|
46
|
+
}): Promise<CreatedAgent & {
|
|
47
|
+
content: ResolvedContent[];
|
|
48
|
+
}>;
|
|
41
49
|
/**
|
|
42
|
-
* {@link createAgent} with what `fastagent init` adds:
|
|
50
|
+
* {@link createAgent} with what `fastagent init` adds: the install. Every content entry is checked before
|
|
43
51
|
* anything is written, and the scaffold is removed again when writing the content fails.
|
|
44
52
|
*/
|
|
45
53
|
export declare function initAgent(dir: string, options?: InitAgentOptions): Promise<CreatedAgent & {
|
|
46
54
|
content: ResolvedContent[];
|
|
47
55
|
}>;
|
|
48
56
|
/**
|
|
49
|
-
* The
|
|
50
|
-
*
|
|
57
|
+
* The on-disk declaration resolved for this machine, not an open agent's startup snapshot. Missing local directories
|
|
58
|
+
* are listed separately; an agent opened here is not told of them.
|
|
51
59
|
*/
|
|
52
60
|
export declare function listContent(agentDir: string): Promise<{
|
|
53
61
|
content: ResolvedContent[];
|
|
54
62
|
absent: BoundContent[];
|
|
55
63
|
}>;
|
|
56
64
|
/**
|
|
57
|
-
*
|
|
58
|
-
* user about what was left on this machine.
|
|
65
|
+
* The edited declaration resolved for this machine; an already-open agent keeps its startup snapshot until reopened.
|
|
59
66
|
*/
|
|
60
67
|
export interface ContentEdit {
|
|
61
68
|
name: string;
|
package/dist/authoring.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Creating an agent and editing its content: `fastagent init` and `fastagent content` are thin wrappers over these (and
|
|
3
3
|
* `fastagent env` writes its declaration through {@link writeEnvironment}), so the rules live here, not in a command.
|
|
4
4
|
* Nothing here prints or exits; every refusal is thrown with the message the CLI shows. The package exports
|
|
5
|
-
* {@link createAgent}
|
|
5
|
+
* {@link createAgent} and the content edits ({@link addContent}, {@link removeContent}, {@link listContent}), which
|
|
6
|
+
* give an agent its own entries, without an alias prefix; the environment's edit is not exported.
|
|
6
7
|
*/
|
|
7
8
|
import { existsSync, lstatSync, readFileSync, statSync } from "node:fs";
|
|
8
9
|
import { rm } from "node:fs/promises";
|
|
@@ -28,13 +29,16 @@ export class ContentNameError extends Error {
|
|
|
28
29
|
* Create an agent in `dir` as `fastagent init` does, which must be new or empty: the whole scaffold, and a git
|
|
29
30
|
* repository whose first commit is what was written, since the agent changes itself and version control is how its
|
|
30
31
|
* author goes back to a version that worked. It installs no dependencies; the agent opens without them.
|
|
32
|
+
*
|
|
33
|
+
* `content` gives it any number of entries, each a directory of this machine or a GitHub repository. They are all
|
|
34
|
+
* checked before anything is written, and one that is refused leaves no directory. Two entries may bind the same
|
|
35
|
+
* directory, or one inside the other; the agent then reaches it by both names.
|
|
31
36
|
*/
|
|
32
|
-
export async function createAgent(dir) {
|
|
33
|
-
|
|
34
|
-
return created;
|
|
37
|
+
export async function createAgent(dir, options = {}) {
|
|
38
|
+
return initAgent(dir, options.content === undefined ? {} : { content: options.content });
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
|
-
* {@link createAgent} with what `fastagent init` adds:
|
|
41
|
+
* {@link createAgent} with what `fastagent init` adds: the install. Every content entry is checked before
|
|
38
42
|
* anything is written, and the scaffold is removed again when writing the content fails.
|
|
39
43
|
*/
|
|
40
44
|
export async function initAgent(dir, options = {}) {
|
|
@@ -69,8 +73,8 @@ export async function initAgent(dir, options = {}) {
|
|
|
69
73
|
return { dir: agentDir, created, content, repository: await initRepository(agentDir) };
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
|
-
* The
|
|
73
|
-
*
|
|
76
|
+
* The on-disk declaration resolved for this machine, not an open agent's startup snapshot. Missing local directories
|
|
77
|
+
* are listed separately; an agent opened here is not told of them.
|
|
74
78
|
*/
|
|
75
79
|
export async function listContent(agentDir) {
|
|
76
80
|
const dir = resolveAgentDir(agentDir);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `fastagent content list|add|remove`: what the agent works on
|
|
2
|
+
* `fastagent content list|add|remove`: what the agent works on, as `context.json` declares it and
|
|
3
3
|
* `context.local.json` binds it on this machine.
|
|
4
4
|
*/
|
|
5
5
|
import { resolve } from "node:path";
|
|
@@ -8,14 +8,12 @@ import { AgentError } from "../../agent-api.js";
|
|
|
8
8
|
import { enterAgentEnv } from "../../env.js";
|
|
9
9
|
import { dotEnvPath, loadEnvValues } from "../../value-file.js";
|
|
10
10
|
import { installProxyFetch } from "../../proxy.js";
|
|
11
|
-
import { modelRefOf } from "../../
|
|
12
|
-
import { openServing } from "../../harnesses/pi-durable/serve.js";
|
|
11
|
+
import { modelRefOf } from "../../model-ref.js";
|
|
13
12
|
import { connectAgent } from "../../http/client.js";
|
|
14
13
|
import { API_KEY_VARIABLE, apiKeysFrom } from "../../http/server.js";
|
|
15
14
|
import { resolveStateRoot, selectAgentEnvironment } from "../../paths.js";
|
|
16
15
|
import { runInvokeJson, runInvokeStream } from "../invoke-stream.js";
|
|
17
16
|
import { failStartup, failUsage, optionalAgentDirOrExit } from "../fail.js";
|
|
18
|
-
import { enterAgentCommand, reportAuth } from "../shared.js";
|
|
19
17
|
/**
|
|
20
18
|
* `invoke`'s sessions, apart from the instance's storage, so `invoke` runs beside a serve of this directory, which holds
|
|
21
19
|
* that one; one `invoke` at a time holds this one.
|
|
@@ -112,6 +110,10 @@ export async function runInvoke(message, dirArg, opts) {
|
|
|
112
110
|
return runRemoteInvoke(message, dirArg, { ...opts, url: opts.url });
|
|
113
111
|
if (opts.key !== undefined)
|
|
114
112
|
failUsage("--key is the key --url calls with: pass --url too");
|
|
113
|
+
const [{ openServing }, { enterAgentCommand, reportAuth }] = await Promise.all([
|
|
114
|
+
import("../../harnesses/pi-durable/serve.js"),
|
|
115
|
+
import("../shared.js"),
|
|
116
|
+
]);
|
|
115
117
|
const { agentDir, modelSpec } = await enterAgentCommand(dirArg, opts);
|
|
116
118
|
const file = invokeStorageFile(resolveStateRoot(agentDir));
|
|
117
119
|
const { agent, models, definition } = await openServing(agentDir, {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { BoundContent } from "../content/declare.ts";
|
|
3
3
|
import { type ResolvedContent } from "../content/resolve.ts";
|
|
4
4
|
/**
|
|
5
|
-
* The report lines for an agent's content — `works on
|
|
5
|
+
* The report lines for an agent's content — `works on`, one per entry, then a `notice` line for each
|
|
6
6
|
* thing its resolution has to say, then one for each directory this machine binds that is not here — shared by the
|
|
7
7
|
* startup report, `info` and `content list`, so the three say the same thing.
|
|
8
8
|
*/
|
package/dist/cli/content-view.js
CHANGED
|
@@ -8,13 +8,13 @@ function contentKind(c) {
|
|
|
8
8
|
return c.clone ? `${github}, a clone brought up to date at each start` : `${github}, your checkout bound here`;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* The report lines for an agent's content — `works on
|
|
11
|
+
* The report lines for an agent's content — `works on`, one per entry, then a `notice` line for each
|
|
12
12
|
* thing its resolution has to say, then one for each directory this machine binds that is not here — shared by the
|
|
13
13
|
* startup report, `info` and `content list`, so the three say the same thing.
|
|
14
14
|
*/
|
|
15
15
|
export function contentLines(content, absent = []) {
|
|
16
16
|
const lines = content.flatMap((c) => [
|
|
17
|
-
[
|
|
17
|
+
["works on", `${c.name.padEnd(width(content))} ${c.location} (${contentKind(c)})`],
|
|
18
18
|
...c.notices.map((notice) => ["notice", `${c.name}: ${notice}`]),
|
|
19
19
|
]);
|
|
20
20
|
for (const binding of absent)
|
package/dist/cli/program.js
CHANGED
|
@@ -56,7 +56,7 @@ const init = {
|
|
|
56
56
|
],
|
|
57
57
|
notes: "An agent is a directory holding a fastagent.config.ts, and it is also the agent's working directory. " +
|
|
58
58
|
"It lives in a directory of its own, never inside a project or another agent: what it works on is declared " +
|
|
59
|
-
"as its content. Add a directory
|
|
59
|
+
"as its content. Add a directory later with `fastagent content add <dir>`.",
|
|
60
60
|
run: async (args, f) => (await import("./commands/init.js")).runInit(args[0], {
|
|
61
61
|
install: f.install !== false,
|
|
62
62
|
content: f.content ?? [],
|
|
@@ -434,14 +434,14 @@ const env = {
|
|
|
434
434
|
};
|
|
435
435
|
const content = {
|
|
436
436
|
name: "content",
|
|
437
|
-
summary: "list, add or remove what the agent works on
|
|
438
|
-
description: "Content is a directory the agent works on
|
|
437
|
+
summary: "list, add or remove what the agent works on",
|
|
438
|
+
description: "Content is a directory the agent works on: a GitHub repository declared " +
|
|
439
439
|
"by name in context.json, shared with the agent and cloned at content/<name>, or a directory of this " +
|
|
440
440
|
"machine bound in context.local.json, which never leaves it.",
|
|
441
441
|
subcommands: [
|
|
442
442
|
{
|
|
443
443
|
name: "list",
|
|
444
|
-
summary: "each content entry
|
|
444
|
+
summary: "each content entry and where it is",
|
|
445
445
|
args: [AGENT_ARG],
|
|
446
446
|
flags: [JSON_FLAG],
|
|
447
447
|
examples: [{ cmd: "fastagent content list" }],
|
|
@@ -452,15 +452,13 @@ const content = {
|
|
|
452
452
|
summary: "add a GitHub repository, or bind a directory of this machine, as content",
|
|
453
453
|
args: [{ name: "<source>", description: "a directory, or github:owner/repo" }, AGENT_ARG],
|
|
454
454
|
flags: [
|
|
455
|
-
{ flags: "--readonly", description: "the agent knows it and does not write it" },
|
|
456
455
|
{ flags: "--ref <ref>", description: "a repository: the branch, tag or commit to clone" },
|
|
457
456
|
{ flags: "--name <name>", description: "its name (default: the directory's or repository's)" },
|
|
458
457
|
{ flags: "--description <text>", description: "one sentence for the agent: what it is, how to treat it" },
|
|
459
458
|
],
|
|
460
459
|
examples: [
|
|
461
|
-
{ cmd: "fastagent content add ~/code/app", note: "
|
|
462
|
-
{ cmd: "fastagent content add
|
|
463
|
-
{ cmd: "fastagent content add github:acme/docs --readonly", note: "knows a clone" },
|
|
460
|
+
{ cmd: "fastagent content add ~/code/app", note: "a directory of this machine" },
|
|
461
|
+
{ cmd: "fastagent content add github:acme/docs", note: "a clone" },
|
|
464
462
|
],
|
|
465
463
|
notes: "The root of a GitHub checkout is declared in context.json as that repository, and bound in " +
|
|
466
464
|
"context.local.json to the checkout, used as it is on this machine. github:owner/repo is cloned into " +
|
|
@@ -469,7 +467,6 @@ const content = {
|
|
|
469
467
|
"Any other directory is bound in context.local.json only: a deployed instance works without it. A bound " +
|
|
470
468
|
"directory may not contain the agent directory, nor sit inside it.",
|
|
471
469
|
run: async (args, f) => (await import("./commands/content.js")).runContentAdd(args[0], args[1], {
|
|
472
|
-
readonly: f.readonly === true,
|
|
473
470
|
...(typeof f.ref === "string" ? { ref: f.ref } : {}),
|
|
474
471
|
...(typeof f.name === "string" ? { name: f.name } : {}),
|
|
475
472
|
...(typeof f.description === "string" ? { description: f.description } : {}),
|
|
@@ -4,8 +4,6 @@ export interface ContentEntry {
|
|
|
4
4
|
github: string;
|
|
5
5
|
/** The branch, tag or commit to clone. */
|
|
6
6
|
ref?: string;
|
|
7
|
-
/** The agent knows it and does not write it. */
|
|
8
|
-
readonly?: boolean;
|
|
9
7
|
/** One sentence for the agent: what this is, and how to treat it. */
|
|
10
8
|
description?: string;
|
|
11
9
|
}
|
|
@@ -15,7 +13,6 @@ export interface DeclaredContent {
|
|
|
15
13
|
/** `owner/repo`. */
|
|
16
14
|
repo: string;
|
|
17
15
|
ref?: string;
|
|
18
|
-
readonly: boolean;
|
|
19
16
|
description?: string;
|
|
20
17
|
}
|
|
21
18
|
/**
|
|
@@ -25,7 +22,6 @@ export interface DeclaredContent {
|
|
|
25
22
|
export interface ContentBinding {
|
|
26
23
|
/** Absolute, `~/`, or relative to the agent directory. */
|
|
27
24
|
path: string;
|
|
28
|
-
readonly?: boolean;
|
|
29
25
|
description?: string;
|
|
30
26
|
}
|
|
31
27
|
/** A binding, read: `path` absolute. */
|
|
@@ -38,7 +34,6 @@ export type BoundContent = {
|
|
|
38
34
|
} | {
|
|
39
35
|
/** A directory only this machine has. */
|
|
40
36
|
kind: "local";
|
|
41
|
-
readonly: boolean;
|
|
42
37
|
description?: string;
|
|
43
38
|
});
|
|
44
39
|
/** What an agent's content is declared as: the shared declaration, and this machine's bindings. */
|
|
@@ -64,7 +59,7 @@ export declare function declareContent(raw: unknown): DeclaredContent[];
|
|
|
64
59
|
export declare function declareBindings(raw: unknown, declared: readonly DeclaredContent[], agentDir: string): BoundContent[];
|
|
65
60
|
/**
|
|
66
61
|
* A directory the agent is bound to and the agent directory are kept apart (agent-model.md §2): a definition is
|
|
67
|
-
* released,
|
|
62
|
+
* released, content is kept, and one directory cannot be both. The ONE statement of the rule; resolve.ts asks
|
|
68
63
|
* it of the real paths.
|
|
69
64
|
*/
|
|
70
65
|
export declare function nestingError(agentDir: string, location: string, name: string): string | undefined;
|
package/dist/content/declare.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WHAT AN AUTHOR DECLARES an agent works on
|
|
2
|
+
* WHAT AN AUTHOR DECLARES an agent works on, read and refused in ONE place (file.ts reads the files through
|
|
3
3
|
* it): the `content` map of `context.json`, which is shared and so holds only repositories every instance reaches, and
|
|
4
4
|
* the `content` map of `context.local.json`, the directories of this machine it is bound to. Pure: what is on the disk
|
|
5
5
|
* is resolve.ts's question. docs/design/agent-model.md §3 is the rule.
|
|
@@ -11,8 +11,8 @@ import { CONTEXT_FILE, LOCAL_CONTEXT_FILE, isUnderDir } from "../paths.js";
|
|
|
11
11
|
* The keys an entry and a binding carry, in the order they are written: where it is, then how it is treated. One list
|
|
12
12
|
* each, so a key the reader accepts is one the writer keeps.
|
|
13
13
|
*/
|
|
14
|
-
const ENTRY_KEYS = ["github", "ref", "
|
|
15
|
-
const BINDING_KEYS = ["path", "
|
|
14
|
+
const ENTRY_KEYS = ["github", "ref", "description"];
|
|
15
|
+
const BINDING_KEYS = ["path", "description"];
|
|
16
16
|
/** A name becomes a directory name, so it is one path segment (the spelling a release's agent name has). */
|
|
17
17
|
const NAME = /^[A-Za-z0-9_-]+$/;
|
|
18
18
|
/** Whether `name` can name a content entry. */
|
|
@@ -62,16 +62,14 @@ export function declareBindings(raw, declared, agentDir) {
|
|
|
62
62
|
if (shared.name !== name) {
|
|
63
63
|
throw new Error(`${at} binds the entry ${CONTEXT_FILE} names "${shared.name}": spell it the same`);
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`every instance: give it only "path"`);
|
|
69
|
-
}
|
|
65
|
+
if (e.description !== undefined) {
|
|
66
|
+
throw new Error(`${at} binds this machine's checkout of the entry ${CONTEXT_FILE} declares, which says "description" for ` +
|
|
67
|
+
`every instance: give it only "path"`);
|
|
70
68
|
}
|
|
71
69
|
return { name, path, kind: "checkout" };
|
|
72
70
|
}
|
|
73
|
-
const
|
|
74
|
-
return { name, path, kind: "local", ...
|
|
71
|
+
const description = descriptionOf(at, e);
|
|
72
|
+
return { name, path, kind: "local", ...(description !== undefined ? { description } : {}) };
|
|
75
73
|
});
|
|
76
74
|
}
|
|
77
75
|
/** `path` as written in `context.local.json`, absolute: `~/` is the home directory, a relative one the agent's. */
|
|
@@ -112,14 +110,11 @@ function objectWithKeys(at, value, keys) {
|
|
|
112
110
|
}
|
|
113
111
|
return e;
|
|
114
112
|
}
|
|
115
|
-
function
|
|
113
|
+
function descriptionOf(at, e) {
|
|
116
114
|
if (e.description !== undefined && (typeof e.description !== "string" || e.description === "")) {
|
|
117
115
|
throw new Error(`${at}: "description" must be a non-empty string`);
|
|
118
116
|
}
|
|
119
|
-
|
|
120
|
-
throw new Error(`${at}: "readonly" must be a boolean`);
|
|
121
|
-
const description = e.description;
|
|
122
|
-
return { readonly: e.readonly === true, ...(description !== undefined ? { description } : {}) };
|
|
117
|
+
return e.description;
|
|
123
118
|
}
|
|
124
119
|
function declareOne(name, entry) {
|
|
125
120
|
const at = `content "${name}"`;
|
|
@@ -129,7 +124,7 @@ function declareOne(name, entry) {
|
|
|
129
124
|
throw new Error(`${at}: "${key}" must be a non-empty string`);
|
|
130
125
|
}
|
|
131
126
|
}
|
|
132
|
-
const
|
|
127
|
+
const description = descriptionOf(at, e);
|
|
133
128
|
if (e.github === undefined) {
|
|
134
129
|
throw new Error(`${at} needs "github": ${CONTEXT_FILE} is shared, so it declares only what every instance reaches — bind a ` +
|
|
135
130
|
`directory of this machine in ${LOCAL_CONTEXT_FILE} instead (fastagent content add <dir>)`);
|
|
@@ -141,11 +136,11 @@ function declareOne(name, entry) {
|
|
|
141
136
|
// git reads a leading "-" as an option, and no branch, tag or commit name has one.
|
|
142
137
|
if (ref?.startsWith("-"))
|
|
143
138
|
throw new Error(`${at}: "ref" must name a branch, tag or commit, got "${ref}"`);
|
|
144
|
-
return { name, repo, ...(ref !== undefined ? { ref } : {}), ...
|
|
139
|
+
return { name, repo, ...(ref !== undefined ? { ref } : {}), ...(description !== undefined ? { description } : {}) };
|
|
145
140
|
}
|
|
146
141
|
/**
|
|
147
142
|
* A directory the agent is bound to and the agent directory are kept apart (agent-model.md §2): a definition is
|
|
148
|
-
* released,
|
|
143
|
+
* released, content is kept, and one directory cannot be both. The ONE statement of the rule; resolve.ts asks
|
|
149
144
|
* it of the real paths.
|
|
150
145
|
*/
|
|
151
146
|
export function nestingError(agentDir, location, name) {
|
|
@@ -4,8 +4,6 @@ import { type CloneOutcome } from "./git.ts";
|
|
|
4
4
|
export type ResolvedContent = {
|
|
5
5
|
/** Unique within the agent, ignoring case; one path segment. */
|
|
6
6
|
name: string;
|
|
7
|
-
/** The agent knows it and does not write it. */
|
|
8
|
-
readonly: boolean;
|
|
9
7
|
/** What the declaration tells the agent about it. */
|
|
10
8
|
description?: string;
|
|
11
9
|
/** Where the agent reaches it on this instance, absolute. */
|
package/dist/content/resolve.js
CHANGED
|
@@ -33,10 +33,9 @@ export function absentNotice(binding) {
|
|
|
33
33
|
`not told of it`);
|
|
34
34
|
}
|
|
35
35
|
function resolveRepository(agentDir, entry, checkout) {
|
|
36
|
-
const { name,
|
|
36
|
+
const { name, description } = entry;
|
|
37
37
|
const repository = {
|
|
38
38
|
name,
|
|
39
|
-
readonly,
|
|
40
39
|
...(description !== undefined ? { description } : {}),
|
|
41
40
|
kind: "github",
|
|
42
41
|
repo: entry.repo,
|
|
@@ -62,12 +61,11 @@ function resolveRepository(agentDir, entry, checkout) {
|
|
|
62
61
|
return { ...repository, location, notices, clone: true };
|
|
63
62
|
}
|
|
64
63
|
function resolveLocal(agentDir, binding) {
|
|
65
|
-
const { name,
|
|
64
|
+
const { name, description } = binding;
|
|
66
65
|
const location = realpathSync(binding.path);
|
|
67
66
|
checkBinding(agentDir, name, location);
|
|
68
67
|
return {
|
|
69
68
|
name,
|
|
70
|
-
readonly,
|
|
71
69
|
...(description !== undefined ? { description } : {}),
|
|
72
70
|
location,
|
|
73
71
|
notices: [],
|
package/dist/content/source.d.ts
CHANGED
package/dist/content/source.js
CHANGED
|
@@ -14,7 +14,6 @@ import { checkoutOf, githubRepoOf } from "./git.js";
|
|
|
14
14
|
*/
|
|
15
15
|
export function readContentSource(source, cwd, options = {}) {
|
|
16
16
|
const treated = {
|
|
17
|
-
...(options.readonly ? { readonly: true } : {}),
|
|
18
17
|
...(options.description !== undefined ? { description: options.description } : {}),
|
|
19
18
|
};
|
|
20
19
|
const ref = options.ref !== undefined ? { ref: options.ref } : {};
|
package/dist/deploy/preflight.js
CHANGED
|
@@ -27,33 +27,25 @@ import { checkPackages, MISE_PACKAGE_NAMES, miseBinary, packageSpecs } from "../
|
|
|
27
27
|
/**
|
|
28
28
|
* What each content entry becomes on the host, one line each, so none is missing there unsaid (agent-model.md §3). A
|
|
29
29
|
* repository `context.json` declares is cloned there, as on this machine without a checkout bound. A directory only
|
|
30
|
-
* this machine binds stays here: the deployed agent works without it, which the line says and says how to change.
|
|
31
|
-
*
|
|
32
|
-
* note.
|
|
30
|
+
* this machine binds stays here: the deployed agent works without it, which the line says and says how to change. It
|
|
31
|
+
* is a warning, since the deployed agent lacks data it was meant to work on.
|
|
33
32
|
*/
|
|
34
33
|
function checkContent({ declared, bound }, storageResets, report) {
|
|
35
34
|
for (const entry of declared) {
|
|
36
35
|
const fate = storageResets
|
|
37
36
|
? "cloned afresh on every deployment, since the host's storage starts over; what the agent did not push is lost"
|
|
38
37
|
: "cloned on the host, and brought up to date in place at each start";
|
|
39
|
-
report.note(
|
|
38
|
+
report.note(`works on ${entry.name}: github ${entry.repo}${entry.ref ? `@${entry.ref}` : ""}, ${fate}`);
|
|
40
39
|
}
|
|
41
40
|
for (const binding of bound) {
|
|
42
41
|
if (binding.kind !== "local")
|
|
43
42
|
continue;
|
|
44
|
-
const stays =
|
|
43
|
+
const stays = `works on ${binding.name}: a directory of this machine (${binding.path}, bound in ` +
|
|
45
44
|
`${LOCAL_CONTEXT_FILE}), stays here, and the deployed agent works without it`;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
report.warn(`${stays}; to work on it from a host, move it to a GitHub repository and add it as github:owner/repo`);
|
|
51
|
-
}
|
|
45
|
+
report.warn(`${stays}; to work on it from a host, move it to a GitHub repository and add it as github:owner/repo, or copy ` +
|
|
46
|
+
`what it only needs to read there into the agent directory, which every release carries`);
|
|
52
47
|
}
|
|
53
48
|
}
|
|
54
|
-
function roleOf(entry) {
|
|
55
|
-
return entry.readonly ? "knows" : "works on";
|
|
56
|
-
}
|
|
57
49
|
/**
|
|
58
50
|
* What the image needs to install the packages the agent declares: the agent's own mise for linux in its package.json,
|
|
59
51
|
* so the image's install brings it, packages FastAgent installs from, and a PyPI package's runtime. A machine without
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModelThinkingLevel, Models, Provider } from "@earendil-works/pi-ai";
|
|
2
|
-
import { type Extension, type HarnessSettings, type
|
|
2
|
+
import { type Extension, type HarnessSettings, type Storage, type ToolRegistration } from "@earendil-works/pi-durable";
|
|
3
3
|
import type { ResolvedContent } from "../../content/resolve.ts";
|
|
4
4
|
import { type DeclaredSecret } from "../../declared-secrets.ts";
|
|
5
5
|
import type { ChannelModule, LongConnectionChannelModule } from "../../channel.ts";
|
|
@@ -14,7 +14,6 @@ export interface AgentDefinition {
|
|
|
14
14
|
config: AgentConfig;
|
|
15
15
|
registry: Registry;
|
|
16
16
|
}
|
|
17
|
-
export declare function modelRefOf(spec: string, where: string): ModelRef;
|
|
18
17
|
/** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
|
|
19
18
|
export declare function loadConfig(dir: string): Promise<AgentConfig>;
|
|
20
19
|
/** The default model a process here runs with: `--model` > `FASTAGENT_MODEL` > the configuration's `model`. */
|
|
@@ -65,7 +64,7 @@ export declare function buildDefinitionShape(definition: AgentDefinition, env: N
|
|
|
65
64
|
export declare function definitionProviders(agentDir: string, env?: NodeJS.ProcessEnv): Promise<Provider[]>;
|
|
66
65
|
export interface AssembleAgentOptions {
|
|
67
66
|
storage: Storage;
|
|
68
|
-
/** What the agent works on
|
|
67
|
+
/** What the agent works on (`context.json`'s content), resolved for this instance. */
|
|
69
68
|
content?: readonly ResolvedContent[];
|
|
70
69
|
/** The definition's code, built once with its secrets (`buildDefinition`). */
|
|
71
70
|
code: BuiltDefinition;
|
|
@@ -25,6 +25,7 @@ import { channelModuleProblem } from "../../channels/discover.js";
|
|
|
25
25
|
import { AGENT_CONFIG_FILE as CONFIG_FILE } from "../../paths.js";
|
|
26
26
|
import { readPackageJson } from "../../runtime.js";
|
|
27
27
|
import { DEV_SERVE_ENV } from "../../serving-command.js";
|
|
28
|
+
import { modelRefOf } from "../../model-ref.js";
|
|
28
29
|
import { MAX_SCHEDULES, scheduleConfigProblem } from "../../schedule/schedule.js";
|
|
29
30
|
import { openDurableAgent } from "./agent.js";
|
|
30
31
|
import { isNeeds } from "./define.js";
|
|
@@ -97,13 +98,6 @@ function checkConfig(config, file) {
|
|
|
97
98
|
}
|
|
98
99
|
return config;
|
|
99
100
|
}
|
|
100
|
-
export function modelRefOf(spec, where) {
|
|
101
|
-
const slash = typeof spec === "string" ? spec.indexOf("/") : -1;
|
|
102
|
-
if (slash < 1 || slash === spec.length - 1) {
|
|
103
|
-
throw new Error(`${where} must be "provider/modelId", got ${JSON.stringify(spec)}`);
|
|
104
|
-
}
|
|
105
|
-
return { provider: spec.slice(0, slash), modelId: spec.slice(slash + 1) };
|
|
106
|
-
}
|
|
107
101
|
/** The agent package's `fastagent.config.ts`, checked; `{}` without one. */
|
|
108
102
|
export async function loadConfig(dir) {
|
|
109
103
|
const configPath = join(dir, CONFIG_FILE);
|
|
@@ -39,7 +39,7 @@ export interface OpenedServing {
|
|
|
39
39
|
definition: AgentDefinition;
|
|
40
40
|
code: BuiltDefinition;
|
|
41
41
|
models: ModelEnvironment;
|
|
42
|
-
/** What the agent works on
|
|
42
|
+
/** What the agent works on, resolved for this instance. */
|
|
43
43
|
content: readonly ResolvedContent[];
|
|
44
44
|
agentDir: string;
|
|
45
45
|
/** Where the instance's state is: its storage, its channels' and schedules' state, its wake-ups. */
|
|
@@ -263,14 +263,12 @@ function contentKind(c) {
|
|
|
263
263
|
// changes stays, and reaches anyone else only when pushed. The declared ref is what it follows, not a claim about
|
|
264
264
|
// where it is: a clone the agent moved elsewhere is left there.
|
|
265
265
|
const clone = `a shallow clone of github ${c.repo}${c.ref ? ` (declared ${c.ref})` : ""} in your own storage`;
|
|
266
|
-
return
|
|
267
|
-
? `${clone}, brought up to date each time you start`
|
|
268
|
-
: `${clone}: what you change in it stays, and each time you start it is brought up to date where that touches nothing of yours; push to share a change`;
|
|
266
|
+
return `${clone}: what you change in it stays, and each time you start it is brought up to date where that touches nothing of yours; push to share a change`;
|
|
269
267
|
}
|
|
270
268
|
/**
|
|
271
269
|
* Where the agent is and what it works on (agent-model.md §4): its own directory is itself, and each content entry is named
|
|
272
|
-
* with its location
|
|
273
|
-
*
|
|
270
|
+
* with its location and where a change there goes. A result written where it does not last is how knowledge gets
|
|
271
|
+
* lost, so the agent is told which is which.
|
|
274
272
|
*/
|
|
275
273
|
function contentSection({ agentDir, content }) {
|
|
276
274
|
const own = `Your working directory, ${agentDir}, is your own directory: it holds your definition, and files you create ` +
|
|
@@ -278,15 +276,11 @@ function contentSection({ agentDir, content }) {
|
|
|
278
276
|
if (content.length === 0)
|
|
279
277
|
return `${own} Nothing else is declared for you: you work only in your own directory.`;
|
|
280
278
|
const line = (c) => `- ${c.name}: ${c.location} (${contentKind(c)})${c.description !== undefined ? ` ${c.description}` : ""}`;
|
|
281
|
-
const worksOn = content.filter((c) => !c.readonly);
|
|
282
|
-
const knows = content.filter((c) => c.readonly);
|
|
283
279
|
return [
|
|
284
280
|
own,
|
|
285
|
-
|
|
286
|
-
...
|
|
287
|
-
`Run a command in one of these directories with \`cd <its location> && …\`; file tools take the location directly
|
|
288
|
-
|
|
289
|
-
? " A result worth keeping belongs in a directory you work on, not in your own directory."
|
|
290
|
-
: " You write only in your own directory: the directories above are for reading."),
|
|
281
|
+
"You work on:",
|
|
282
|
+
...content.map(line),
|
|
283
|
+
`Run a command in one of these directories with \`cd <its location> && …\`; file tools take the location directly. ` +
|
|
284
|
+
`A result worth keeping belongs in a directory you work on, not in your own directory.`,
|
|
291
285
|
].join("\n");
|
|
292
286
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,11 @@ export type { Agent, AgentErrorData, AgentInfo, CompactOptions, ContentPart, Cre
|
|
|
7
7
|
export { AgentError, LIMITS, hasEnded } from "./agent-api.ts";
|
|
8
8
|
export { openAgent, type LocalAgent, type OpenAgentOptions, type ServeAgentOptions, } from "./harnesses/pi-durable/serve.ts";
|
|
9
9
|
export type { Service } from "./service.ts";
|
|
10
|
-
export { createAgent, type CreatedAgent } from "./authoring.ts";
|
|
10
|
+
export { ContentNameError, addContent, createAgent, listContent, removeContent, type ContentEdit, type CreatedAgent, } from "./authoring.ts";
|
|
11
|
+
export type { ContentAddition, SourceOptions } from "./content/source.ts";
|
|
12
|
+
export { readContentSource } from "./content/source.ts";
|
|
13
|
+
export type { BoundContent, ContentBinding, ContentEntry } from "./content/declare.ts";
|
|
14
|
+
export type { ResolvedContent } from "./content/resolve.ts";
|
|
11
15
|
export { SignInCancelled, type Credential, type CredentialStore, type ProviderAuth, type SignInEvent, type SignInInteraction, type SignInMethod, type SignInOption, type SignInPrompt, type SignInRequest, type SignInResult, } from "./sign-in-api.ts";
|
|
12
16
|
export { credentialFile, providerAuth, signIn, signInOptions } from "./harnesses/models/host.ts";
|
|
13
17
|
export { defineAgent, defineSchedule, needs, type AgentConfig, type Needs, type SecretSpec, type SecretValues, } from "./harnesses/pi-durable/define.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { AgentError, LIMITS, hasEnded } from "./agent-api.js";
|
|
2
2
|
export { openAgent, } from "./harnesses/pi-durable/serve.js";
|
|
3
|
-
export { createAgent } from "./authoring.js";
|
|
3
|
+
export { ContentNameError, addContent, createAgent, listContent, removeContent, } from "./authoring.js";
|
|
4
|
+
export { readContentSource } from "./content/source.js";
|
|
4
5
|
export { SignInCancelled, } from "./sign-in-api.js";
|
|
5
6
|
export { credentialFile, providerAuth, signIn, signInOptions } from "./harnesses/models/host.js";
|
|
6
7
|
export { defineAgent, defineSchedule, needs, } from "./harnesses/pi-durable/define.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function modelRefOf(spec, where) {
|
|
2
|
+
const slash = typeof spec === "string" ? spec.indexOf("/") : -1;
|
|
3
|
+
if (slash < 1 || slash === spec.length - 1) {
|
|
4
|
+
throw new Error(`${where} must be "provider/modelId", got ${JSON.stringify(spec)}`);
|
|
5
|
+
}
|
|
6
|
+
return { provider: spec.slice(0, slash), modelId: spec.slice(slash + 1) };
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastagent-sh/fastagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Vibe first. Then FastAgent: turn a local agent directory into a live service in your app, on Telegram, Slack, Feishu, or behind any channel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|