@managoat/fountain-sdk 1.25.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/CHANGELOG.md +653 -0
- package/LICENSE +202 -0
- package/README.md +445 -0
- package/dist/client.d.ts +190 -0
- package/dist/client.js +225 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +49 -0
- package/dist/config.js +87 -0
- package/dist/config.js.map +1 -0
- package/dist/conversation.d.ts +100 -0
- package/dist/conversation.js +189 -0
- package/dist/conversation.js.map +1 -0
- package/dist/errors.d.ts +102 -0
- package/dist/errors.js +197 -0
- package/dist/errors.js.map +1 -0
- package/dist/generated/openapi.d.ts +16654 -0
- package/dist/generated/openapi.js +6 -0
- package/dist/generated/openapi.js.map +1 -0
- package/dist/http.d.ts +37 -0
- package/dist/http.js +129 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.js +21 -0
- package/dist/node.js.map +1 -0
- package/dist/queue.d.ts +25 -0
- package/dist/queue.js +64 -0
- package/dist/queue.js.map +1 -0
- package/dist/resolve.d.ts +29 -0
- package/dist/resolve.js +89 -0
- package/dist/resolve.js.map +1 -0
- package/dist/resources.d.ts +126 -0
- package/dist/resources.js +206 -0
- package/dist/resources.js.map +1 -0
- package/dist/run.d.ts +81 -0
- package/dist/run.js +247 -0
- package/dist/run.js.map +1 -0
- package/dist/schemas.d.ts +90 -0
- package/dist/schemas.js +2 -0
- package/dist/schemas.js.map +1 -0
- package/dist/sse.d.ts +58 -0
- package/dist/sse.js +219 -0
- package/dist/sse.js.map +1 -0
- package/dist/team.d.ts +90 -0
- package/dist/team.js +183 -0
- package/dist/team.js.map +1 -0
- package/dist/turn.d.ts +46 -0
- package/dist/turn.js +205 -0
- package/dist/turn.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { HttpClient, type FetchLike, type RequestOptions } from "./http.ts";
|
|
2
|
+
import { type ConfigOptions, type ResolvedConfig } from "./config.ts";
|
|
3
|
+
import { Run, type RunOptions } from "./run.ts";
|
|
4
|
+
import { Conversation } from "./conversation.ts";
|
|
5
|
+
import { Agents, Connections, Environments, Vaults } from "./resources.ts";
|
|
6
|
+
import { Team } from "./team.ts";
|
|
7
|
+
import { type StreamRequest } from "./sse.ts";
|
|
8
|
+
import type { AuthMe, Catalog, ConversationRecord, LogEvent, SandboxDiff, SandboxFile, SandboxListing, SandboxRecord, SearchHit } from "./types.ts";
|
|
9
|
+
export interface FountainOptions extends ConfigOptions {
|
|
10
|
+
/** Swap the fetch implementation — a test server, a proxy, an instrumented one. */
|
|
11
|
+
fetch?: FetchLike;
|
|
12
|
+
/** Timeout for ordinary API calls, in ms. Streams are never timed out. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface RunConfig extends RunOptions {
|
|
16
|
+
/** The agent to run, by name or id. */
|
|
17
|
+
agent: string;
|
|
18
|
+
/**
|
|
19
|
+
* A vault of secrets, by name or id. Its values win over the environment's
|
|
20
|
+
* on a key collision, and they are attached when the sandbox spawns — the
|
|
21
|
+
* prompt never carries them.
|
|
22
|
+
*/
|
|
23
|
+
vault?: string;
|
|
24
|
+
/** An environment to provision from instead of the agent's own, by name or id. */
|
|
25
|
+
environment?: string;
|
|
26
|
+
/** Display title for the conversation. */
|
|
27
|
+
title?: string;
|
|
28
|
+
/** Images to attach to the first prompt, as the API's `ImageInput` shape. */
|
|
29
|
+
images?: unknown[];
|
|
30
|
+
/**
|
|
31
|
+
* Bind the conversation to an external channel. A second run with the same
|
|
32
|
+
* channel, agent and vault continues that conversation instead of opening a
|
|
33
|
+
* new one — how a chat harness keeps one thread on one sandbox.
|
|
34
|
+
*/
|
|
35
|
+
channelId?: string;
|
|
36
|
+
/** With `channelId`: open a new conversation anyway. */
|
|
37
|
+
fresh?: boolean;
|
|
38
|
+
/** Override the generated sandbox name. */
|
|
39
|
+
spriteName?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Attach to a sandbox you already have, by id, instead of provisioning a
|
|
42
|
+
* new one. It must be ready or suspended and built for the same agent,
|
|
43
|
+
* environment and vault; several conversations then share one disk.
|
|
44
|
+
*/
|
|
45
|
+
sandbox?: string;
|
|
46
|
+
/**
|
|
47
|
+
* `"ephemeral"` (a sandbox for this conversation alone) or `"persistent"`
|
|
48
|
+
* (the agent's own machine, shared by every conversation of that agent,
|
|
49
|
+
* environment and vault). Defaults to the agent's `sandbox_mode`.
|
|
50
|
+
*/
|
|
51
|
+
sandboxMode?: "ephemeral" | "persistent";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A Fountain client.
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* const fountain = new Fountain();
|
|
58
|
+
* const run = await fountain.run("Upgrade us to Phoenix 1.8 and open a PR", {
|
|
59
|
+
* agent: "reposage",
|
|
60
|
+
* vault: "github-bot",
|
|
61
|
+
* });
|
|
62
|
+
* console.log(run.text, run.url);
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* Credentials resolve the way the `fountain` CLI resolves them, so a script
|
|
66
|
+
* inherits whatever already works in your terminal. See `resolveConfig`.
|
|
67
|
+
*/
|
|
68
|
+
export declare class Fountain {
|
|
69
|
+
/** The raw HTTP client. Every endpoint this SDK does not wrap is still here. */
|
|
70
|
+
readonly api: HttpClient;
|
|
71
|
+
readonly config: ResolvedConfig;
|
|
72
|
+
/** Define, read, change and delete agents. */
|
|
73
|
+
readonly agents: Agents;
|
|
74
|
+
/** Environments and their secrets. */
|
|
75
|
+
readonly environments: Environments;
|
|
76
|
+
/** Vaults and their secrets. */
|
|
77
|
+
readonly vaults: Vaults;
|
|
78
|
+
/** The team: teammates, their threads and their routines. */
|
|
79
|
+
readonly team: Team;
|
|
80
|
+
/** Provider accounts Fountain holds the credential for (Gmail via OAuth). */
|
|
81
|
+
readonly connections: Connections;
|
|
82
|
+
private readonly resolver;
|
|
83
|
+
constructor(options?: FountainOptions);
|
|
84
|
+
/**
|
|
85
|
+
* Run an agent on a prompt in a fresh sandbox.
|
|
86
|
+
*
|
|
87
|
+
* Returns immediately with a handle: `await` it for the finished answer,
|
|
88
|
+
* `for await` it for events as they land, or read `.textStream`. The work
|
|
89
|
+
* starts either way.
|
|
90
|
+
*/
|
|
91
|
+
run(prompt: string, config: RunConfig): Run;
|
|
92
|
+
/**
|
|
93
|
+
* Pick a conversation back up. The sandbox is still there and so is the
|
|
94
|
+
* agent's session — `send` costs one prompt, not a re-explanation.
|
|
95
|
+
*/
|
|
96
|
+
resume(conversationId: string): Conversation;
|
|
97
|
+
/**
|
|
98
|
+
* The account's conversations, newest first.
|
|
99
|
+
*
|
|
100
|
+
* Roots only by default: a conversation an agent spawned from inside a
|
|
101
|
+
* sandbox is listed under its parent's tree, not again at the top level.
|
|
102
|
+
* Pass `{ rootsOnly: false }` for the flat list, children included.
|
|
103
|
+
*/
|
|
104
|
+
conversations(opts?: {
|
|
105
|
+
rootsOnly?: boolean;
|
|
106
|
+
sandboxId?: string;
|
|
107
|
+
}): Promise<ConversationRecord[]>;
|
|
108
|
+
/**
|
|
109
|
+
* Who this key belongs to. The cheapest way to check a key works.
|
|
110
|
+
*
|
|
111
|
+
* `request`, not `data`: this is one of the few endpoints that answers with
|
|
112
|
+
* the object itself rather than `{data: …}`, so unwrapping would hand back
|
|
113
|
+
* `null` for a call that succeeded. `test/server.ts` lists the others.
|
|
114
|
+
*/
|
|
115
|
+
me(): Promise<AuthMe>;
|
|
116
|
+
/**
|
|
117
|
+
* The form vocabulary: the runtimes, models and providers this deployment
|
|
118
|
+
* offers. A client that builds an agent form reads it from here rather than
|
|
119
|
+
* hard-coding a list that goes stale on the next Fountain release.
|
|
120
|
+
*/
|
|
121
|
+
catalog(): Promise<Catalog>;
|
|
122
|
+
/**
|
|
123
|
+
* The account's sandboxes — the machines conversations run on — newest
|
|
124
|
+
* first, each with the conversations on it. Pass one's id as
|
|
125
|
+
* `run({ sandbox })` to put another conversation on it.
|
|
126
|
+
*/
|
|
127
|
+
sandboxes(opts?: {
|
|
128
|
+
status?: string[];
|
|
129
|
+
}): Promise<SandboxRecord[]>;
|
|
130
|
+
/** One sandbox, by id. */
|
|
131
|
+
sandbox(id: string): Promise<SandboxRecord>;
|
|
132
|
+
/**
|
|
133
|
+
* Reset a persistent sandbox: destroy the agent's home so the next launch
|
|
134
|
+
* on the same agent, environment and vault builds a clean machine. The
|
|
135
|
+
* conversations on it are kept. Refused (`sandbox_not_resettable`) for an
|
|
136
|
+
* ephemeral or already-gone sandbox, and (`sandbox_mid_turn`) while a
|
|
137
|
+
* conversation on it runs a turn.
|
|
138
|
+
*/
|
|
139
|
+
resetSandbox(id: string): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* The entries of one directory on a sandbox, directories first then by
|
|
142
|
+
* name (ADR 0039). `path` is absolute or relative to the agent's working
|
|
143
|
+
* directory; omit it for that directory itself.
|
|
144
|
+
*
|
|
145
|
+
* The three disk reads — this, `sandboxFile` and `sandboxDiff` — are the
|
|
146
|
+
* whole of what the API offers on a disk; there is no exec, and to run a
|
|
147
|
+
* command you send a prompt. They need a `full`-scope key, answer only for
|
|
148
|
+
* a `ready` sandbox (a parked one is not woken: `sandbox_not_ready`), are
|
|
149
|
+
* confined to `/home/sprite` and the runtime's workspace
|
|
150
|
+
* (`path_outside_sandbox`), and come back redacted — the sandbox's
|
|
151
|
+
* environment and vault values read `[REDACTED]`, as in the transcript.
|
|
152
|
+
*/
|
|
153
|
+
sandboxFiles(id: string, path?: string): Promise<SandboxListing>;
|
|
154
|
+
/**
|
|
155
|
+
* One file on a sandbox. `content` is the text when `encoding` is `utf-8`
|
|
156
|
+
* and base64 otherwise; `size` is the whole file and `truncated` says
|
|
157
|
+
* whether `content` stopped at `maxBytes` (default 256 KiB, at most 4 MiB).
|
|
158
|
+
*/
|
|
159
|
+
sandboxFile(id: string, path: string, opts?: {
|
|
160
|
+
maxBytes?: number;
|
|
161
|
+
}): Promise<SandboxFile>;
|
|
162
|
+
/**
|
|
163
|
+
* `git diff` of the repository containing `path` on a sandbox (default:
|
|
164
|
+
* the agent's working directory). `staged` diffs the index; `ref` diffs
|
|
165
|
+
* against a commit, branch or tag instead of HEAD.
|
|
166
|
+
*/
|
|
167
|
+
sandboxDiff(id: string, opts?: {
|
|
168
|
+
path?: string;
|
|
169
|
+
staged?: boolean;
|
|
170
|
+
ref?: string;
|
|
171
|
+
maxBytes?: number;
|
|
172
|
+
}): Promise<SandboxDiff>;
|
|
173
|
+
/** Full-text search across the caller's conversations. */
|
|
174
|
+
search(query: string, opts?: {
|
|
175
|
+
limit?: number;
|
|
176
|
+
}): Promise<SearchHit[]>;
|
|
177
|
+
/**
|
|
178
|
+
* Every conversation the caller owns, on one connection.
|
|
179
|
+
*
|
|
180
|
+
* `team.stream()` is the same idea narrowed to the team. Use this when the
|
|
181
|
+
* client cares about conversations that are not teammates' — a dashboard,
|
|
182
|
+
* or a process watching work it spawned itself.
|
|
183
|
+
*/
|
|
184
|
+
events(options?: StreamRequest): AsyncIterable<LogEvent>;
|
|
185
|
+
/** Forget the memoized agent/vault/environment listings. */
|
|
186
|
+
refresh(): void;
|
|
187
|
+
/** Shorthand for `api.request`, for endpoints this SDK does not wrap. */
|
|
188
|
+
request<T = unknown>(method: string, path: string, options?: RequestOptions): Promise<T>;
|
|
189
|
+
private nextTurnNumber;
|
|
190
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { HttpClient } from "./http.js";
|
|
2
|
+
import { resolveConfig } from "./config.js";
|
|
3
|
+
import { Resolver } from "./resolve.js";
|
|
4
|
+
import { Run } from "./run.js";
|
|
5
|
+
import { Conversation } from "./conversation.js";
|
|
6
|
+
import { Agents, Connections, Environments, Vaults } from "./resources.js";
|
|
7
|
+
import { Team, normalizeStreams } from "./team.js";
|
|
8
|
+
import { streamPath } from "./sse.js";
|
|
9
|
+
/**
|
|
10
|
+
* A Fountain client.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const fountain = new Fountain();
|
|
14
|
+
* const run = await fountain.run("Upgrade us to Phoenix 1.8 and open a PR", {
|
|
15
|
+
* agent: "reposage",
|
|
16
|
+
* vault: "github-bot",
|
|
17
|
+
* });
|
|
18
|
+
* console.log(run.text, run.url);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Credentials resolve the way the `fountain` CLI resolves them, so a script
|
|
22
|
+
* inherits whatever already works in your terminal. See `resolveConfig`.
|
|
23
|
+
*/
|
|
24
|
+
export class Fountain {
|
|
25
|
+
/** The raw HTTP client. Every endpoint this SDK does not wrap is still here. */
|
|
26
|
+
api;
|
|
27
|
+
config;
|
|
28
|
+
/** Define, read, change and delete agents. */
|
|
29
|
+
agents;
|
|
30
|
+
/** Environments and their secrets. */
|
|
31
|
+
environments;
|
|
32
|
+
/** Vaults and their secrets. */
|
|
33
|
+
vaults;
|
|
34
|
+
/** The team: teammates, their threads and their routines. */
|
|
35
|
+
team;
|
|
36
|
+
/** Provider accounts Fountain holds the credential for (Gmail via OAuth). */
|
|
37
|
+
connections;
|
|
38
|
+
resolver;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.config = resolveConfig(options);
|
|
41
|
+
this.api = new HttpClient(this.config, {
|
|
42
|
+
fetch: options.fetch,
|
|
43
|
+
timeoutMs: options.timeoutMs,
|
|
44
|
+
});
|
|
45
|
+
this.resolver = new Resolver(this.api);
|
|
46
|
+
this.agents = new Agents(this.api, this.resolver);
|
|
47
|
+
this.environments = new Environments(this.api, this.resolver);
|
|
48
|
+
this.vaults = new Vaults(this.api, this.resolver);
|
|
49
|
+
this.team = new Team(this.api, this.resolver);
|
|
50
|
+
this.connections = new Connections(this.api);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Run an agent on a prompt in a fresh sandbox.
|
|
54
|
+
*
|
|
55
|
+
* Returns immediately with a handle: `await` it for the finished answer,
|
|
56
|
+
* `for await` it for events as they land, or read `.textStream`. The work
|
|
57
|
+
* starts either way.
|
|
58
|
+
*/
|
|
59
|
+
run(prompt, config) {
|
|
60
|
+
const options = {
|
|
61
|
+
timeoutMs: config.timeoutMs,
|
|
62
|
+
signal: config.signal,
|
|
63
|
+
collectEvents: config.collectEvents,
|
|
64
|
+
};
|
|
65
|
+
return new Run(this.api, {
|
|
66
|
+
start: async () => {
|
|
67
|
+
const [agent, vaultId, environmentId] = await Promise.all([
|
|
68
|
+
this.resolver.resolve("/api/agents", "agent", config.agent),
|
|
69
|
+
this.resolver.resolveId("/api/vaults", "vault", config.vault),
|
|
70
|
+
this.resolver.resolveId("/api/environments", "environment", config.environment),
|
|
71
|
+
]);
|
|
72
|
+
const body = { agent_id: agent.id };
|
|
73
|
+
if (prompt)
|
|
74
|
+
body.prompt = prompt;
|
|
75
|
+
if (vaultId)
|
|
76
|
+
body.vault_id = vaultId;
|
|
77
|
+
if (environmentId)
|
|
78
|
+
body.environment_id = environmentId;
|
|
79
|
+
if (config.title)
|
|
80
|
+
body.title = config.title;
|
|
81
|
+
if (config.images?.length)
|
|
82
|
+
body.images = config.images;
|
|
83
|
+
if (config.channelId)
|
|
84
|
+
body.channel_id = config.channelId;
|
|
85
|
+
if (config.fresh)
|
|
86
|
+
body.fresh = true;
|
|
87
|
+
if (config.spriteName)
|
|
88
|
+
body.sprite_name = config.spriteName;
|
|
89
|
+
if (config.sandbox)
|
|
90
|
+
body.sandbox_id = config.sandbox;
|
|
91
|
+
if (config.sandboxMode)
|
|
92
|
+
body.sandbox_mode = config.sandboxMode;
|
|
93
|
+
const conversation = await this.api.data("POST", "/api/conversations", { body });
|
|
94
|
+
// `channel_id` may have resumed an existing conversation, in which
|
|
95
|
+
// case this prompt is not turn 1. Ask, rather than assume.
|
|
96
|
+
const turnNumber = config.channelId ? await this.nextTurnNumber(conversation.id) : 1;
|
|
97
|
+
return { conversation, turnNumber, after: 0 };
|
|
98
|
+
},
|
|
99
|
+
}, options);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Pick a conversation back up. The sandbox is still there and so is the
|
|
103
|
+
* agent's session — `send` costs one prompt, not a re-explanation.
|
|
104
|
+
*/
|
|
105
|
+
resume(conversationId) {
|
|
106
|
+
return new Conversation(this.api, conversationId);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The account's conversations, newest first.
|
|
110
|
+
*
|
|
111
|
+
* Roots only by default: a conversation an agent spawned from inside a
|
|
112
|
+
* sandbox is listed under its parent's tree, not again at the top level.
|
|
113
|
+
* Pass `{ rootsOnly: false }` for the flat list, children included.
|
|
114
|
+
*/
|
|
115
|
+
async conversations(opts = {}) {
|
|
116
|
+
return this.api.list("/api/conversations", {
|
|
117
|
+
query: { roots_only: opts.rootsOnly === false ? undefined : "true", sandbox_id: opts.sandboxId },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Who this key belongs to. The cheapest way to check a key works.
|
|
122
|
+
*
|
|
123
|
+
* `request`, not `data`: this is one of the few endpoints that answers with
|
|
124
|
+
* the object itself rather than `{data: …}`, so unwrapping would hand back
|
|
125
|
+
* `null` for a call that succeeded. `test/server.ts` lists the others.
|
|
126
|
+
*/
|
|
127
|
+
async me() {
|
|
128
|
+
return this.api.request("GET", "/api/auth/me");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The form vocabulary: the runtimes, models and providers this deployment
|
|
132
|
+
* offers. A client that builds an agent form reads it from here rather than
|
|
133
|
+
* hard-coding a list that goes stale on the next Fountain release.
|
|
134
|
+
*/
|
|
135
|
+
async catalog() {
|
|
136
|
+
return this.api.data("GET", "/api/catalog");
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The account's sandboxes — the machines conversations run on — newest
|
|
140
|
+
* first, each with the conversations on it. Pass one's id as
|
|
141
|
+
* `run({ sandbox })` to put another conversation on it.
|
|
142
|
+
*/
|
|
143
|
+
async sandboxes(opts = {}) {
|
|
144
|
+
return this.api.list("/api/sandboxes", {
|
|
145
|
+
query: { status: opts.status?.join(",") },
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/** One sandbox, by id. */
|
|
149
|
+
async sandbox(id) {
|
|
150
|
+
return this.api.data("GET", `/api/sandboxes/${id}`);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reset a persistent sandbox: destroy the agent's home so the next launch
|
|
154
|
+
* on the same agent, environment and vault builds a clean machine. The
|
|
155
|
+
* conversations on it are kept. Refused (`sandbox_not_resettable`) for an
|
|
156
|
+
* ephemeral or already-gone sandbox, and (`sandbox_mid_turn`) while a
|
|
157
|
+
* conversation on it runs a turn.
|
|
158
|
+
*/
|
|
159
|
+
async resetSandbox(id) {
|
|
160
|
+
await this.api.request("DELETE", `/api/sandboxes/${id}`);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The entries of one directory on a sandbox, directories first then by
|
|
164
|
+
* name (ADR 0039). `path` is absolute or relative to the agent's working
|
|
165
|
+
* directory; omit it for that directory itself.
|
|
166
|
+
*
|
|
167
|
+
* The three disk reads — this, `sandboxFile` and `sandboxDiff` — are the
|
|
168
|
+
* whole of what the API offers on a disk; there is no exec, and to run a
|
|
169
|
+
* command you send a prompt. They need a `full`-scope key, answer only for
|
|
170
|
+
* a `ready` sandbox (a parked one is not woken: `sandbox_not_ready`), are
|
|
171
|
+
* confined to `/home/sprite` and the runtime's workspace
|
|
172
|
+
* (`path_outside_sandbox`), and come back redacted — the sandbox's
|
|
173
|
+
* environment and vault values read `[REDACTED]`, as in the transcript.
|
|
174
|
+
*/
|
|
175
|
+
async sandboxFiles(id, path) {
|
|
176
|
+
return this.api.data("GET", `/api/sandboxes/${id}/files`, { query: { path } });
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* One file on a sandbox. `content` is the text when `encoding` is `utf-8`
|
|
180
|
+
* and base64 otherwise; `size` is the whole file and `truncated` says
|
|
181
|
+
* whether `content` stopped at `maxBytes` (default 256 KiB, at most 4 MiB).
|
|
182
|
+
*/
|
|
183
|
+
async sandboxFile(id, path, opts = {}) {
|
|
184
|
+
return this.api.data("GET", `/api/sandboxes/${id}/file`, {
|
|
185
|
+
query: { path, max_bytes: opts.maxBytes },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* `git diff` of the repository containing `path` on a sandbox (default:
|
|
190
|
+
* the agent's working directory). `staged` diffs the index; `ref` diffs
|
|
191
|
+
* against a commit, branch or tag instead of HEAD.
|
|
192
|
+
*/
|
|
193
|
+
async sandboxDiff(id, opts = {}) {
|
|
194
|
+
return this.api.data("GET", `/api/sandboxes/${id}/diff`, {
|
|
195
|
+
query: { path: opts.path, staged: opts.staged, ref: opts.ref, max_bytes: opts.maxBytes },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** Full-text search across the caller's conversations. */
|
|
199
|
+
async search(query, opts = {}) {
|
|
200
|
+
return this.api.list("/api/search", { query: { q: query, limit: opts.limit } });
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Every conversation the caller owns, on one connection.
|
|
204
|
+
*
|
|
205
|
+
* `team.stream()` is the same idea narrowed to the team. Use this when the
|
|
206
|
+
* client cares about conversations that are not teammates' — a dashboard,
|
|
207
|
+
* or a process watching work it spawned itself.
|
|
208
|
+
*/
|
|
209
|
+
events(options = {}) {
|
|
210
|
+
return streamPath(this.api, "/api/events/stream", { blocks: true, ...normalizeStreams(options) });
|
|
211
|
+
}
|
|
212
|
+
/** Forget the memoized agent/vault/environment listings. */
|
|
213
|
+
refresh() {
|
|
214
|
+
this.resolver.clear();
|
|
215
|
+
}
|
|
216
|
+
/** Shorthand for `api.request`, for endpoints this SDK does not wrap. */
|
|
217
|
+
request(method, path, options) {
|
|
218
|
+
return this.api.request(method, path, options);
|
|
219
|
+
}
|
|
220
|
+
async nextTurnNumber(conversationId) {
|
|
221
|
+
const turns = await this.api.list(`/api/conversations/${conversationId}/turns`);
|
|
222
|
+
return turns.reduce((max, t) => Math.max(max, Number(t.turn_number) || 0), 0) + 1;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const DEFAULT_BASE_URL = "https://managoat.com";
|
|
2
|
+
/** Where a human reads a transcript. Fountain's own UI is a console (see CLAUDE.md). */
|
|
3
|
+
export declare const DEFAULT_APP_URL = "https://fountain-conversations.demo.managoat.com/";
|
|
4
|
+
export interface ResolvedConfig {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
appUrl: string;
|
|
8
|
+
/** Set when running inside a Fountain sandbox; stamps spawned conversations as children. */
|
|
9
|
+
parentConversationId: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface ConfigOptions {
|
|
12
|
+
/** Bearer token. Falls back to the environment, then `~/.fountain/credentials`. */
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
/** Your Fountain deployment. Defaults to the hosted one. */
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
/** Credentials-file profile. Defaults to `$FOUNTAIN_PROFILE` or `default`. */
|
|
17
|
+
profile?: string;
|
|
18
|
+
/** Base of the conversations app, for `run.url`. `""` falls back to the API URL. */
|
|
19
|
+
appUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* How to read `~/.fountain/credentials`, when there is a filesystem.
|
|
23
|
+
*
|
|
24
|
+
* The eleven apps built on Fountain so far are browser apps, and a bare
|
|
25
|
+
* `import "node:fs"` at the top of this module breaks their bundles. So the
|
|
26
|
+
* file reader is injected: `@managoat/fountain-sdk` resolves to a Node entry that
|
|
27
|
+
* installs one, and to this browser-safe module everywhere else. Nothing here
|
|
28
|
+
* touches a Node built-in.
|
|
29
|
+
*/
|
|
30
|
+
export type CredentialsReader = (profile: string) => Record<string, string>;
|
|
31
|
+
/** Install a credentials-file reader. Called by the Node entry point. */
|
|
32
|
+
export declare function setCredentialsReader(reader: CredentialsReader | null): void;
|
|
33
|
+
/** Parse the INI-ish file `fountain auth login` writes. Exported for the Node entry. */
|
|
34
|
+
export declare function parseCredentials(raw: string, profile: string): Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Resolve credentials the way the `fountain` CLI does, so a script inherits
|
|
37
|
+
* whatever already works in the terminal:
|
|
38
|
+
*
|
|
39
|
+
* apiKey: option → FOUNTAIN_API_KEY → FOUNTAIN_TOKEN → ~/.fountain/credentials
|
|
40
|
+
* baseUrl: option → FOUNTAIN_BASE_URL → ~/.fountain/credentials → hosted
|
|
41
|
+
*
|
|
42
|
+
* `FOUNTAIN_TOKEN` is what a Fountain sandbox exports for the agent inside it,
|
|
43
|
+
* so an agent that reaches for this SDK delegates with the conversation-scoped
|
|
44
|
+
* token it already has — and the conversations it starts are recorded as its
|
|
45
|
+
* children.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveConfig(options?: ConfigOptions): ResolvedConfig;
|
|
48
|
+
/** The URL a human opens to watch this conversation. */
|
|
49
|
+
export declare function conversationUrl(conversationId: string, config: ResolvedConfig): string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export const DEFAULT_BASE_URL = "https://managoat.com";
|
|
2
|
+
/** Where a human reads a transcript. Fountain's own UI is a console (see CLAUDE.md). */
|
|
3
|
+
export const DEFAULT_APP_URL = "https://fountain-conversations.demo.managoat.com/";
|
|
4
|
+
function unquote(value) {
|
|
5
|
+
const v = (value ?? "").trim();
|
|
6
|
+
if (v.length >= 2 && v[0] === v[v.length - 1] && (v[0] === '"' || v[0] === "'")) {
|
|
7
|
+
return v.slice(1, -1).trim();
|
|
8
|
+
}
|
|
9
|
+
return v;
|
|
10
|
+
}
|
|
11
|
+
let credentialsReader = null;
|
|
12
|
+
/** Install a credentials-file reader. Called by the Node entry point. */
|
|
13
|
+
export function setCredentialsReader(reader) {
|
|
14
|
+
credentialsReader = reader;
|
|
15
|
+
}
|
|
16
|
+
/** Parse the INI-ish file `fountain auth login` writes. Exported for the Node entry. */
|
|
17
|
+
export function parseCredentials(raw, profile) {
|
|
18
|
+
const out = {};
|
|
19
|
+
let section = "";
|
|
20
|
+
for (const line of raw.split("\n")) {
|
|
21
|
+
const text = line.trim();
|
|
22
|
+
if (!text || text.startsWith("#") || text.startsWith(";"))
|
|
23
|
+
continue;
|
|
24
|
+
if (text.startsWith("[") && text.endsWith("]")) {
|
|
25
|
+
section = text.slice(1, -1).trim();
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (section !== profile)
|
|
29
|
+
continue;
|
|
30
|
+
const eq = text.indexOf("=");
|
|
31
|
+
if (eq === -1)
|
|
32
|
+
continue;
|
|
33
|
+
out[text.slice(0, eq).trim()] = unquote(text.slice(eq + 1));
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
/** Absent, unreadable, or no reader installed is not an error — it contributes nothing. */
|
|
38
|
+
function readCredentials(profile) {
|
|
39
|
+
try {
|
|
40
|
+
return credentialsReader?.(profile) ?? {};
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const env = (name) => {
|
|
47
|
+
// `process` is absent in a browser and undefined-typed in a worker.
|
|
48
|
+
const vars = globalThis.process?.env;
|
|
49
|
+
return (vars?.[name] ?? "").trim();
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Resolve credentials the way the `fountain` CLI does, so a script inherits
|
|
53
|
+
* whatever already works in the terminal:
|
|
54
|
+
*
|
|
55
|
+
* apiKey: option → FOUNTAIN_API_KEY → FOUNTAIN_TOKEN → ~/.fountain/credentials
|
|
56
|
+
* baseUrl: option → FOUNTAIN_BASE_URL → ~/.fountain/credentials → hosted
|
|
57
|
+
*
|
|
58
|
+
* `FOUNTAIN_TOKEN` is what a Fountain sandbox exports for the agent inside it,
|
|
59
|
+
* so an agent that reaches for this SDK delegates with the conversation-scoped
|
|
60
|
+
* token it already has — and the conversations it starts are recorded as its
|
|
61
|
+
* children.
|
|
62
|
+
*/
|
|
63
|
+
export function resolveConfig(options = {}) {
|
|
64
|
+
const profile = (options.profile ?? "").trim() || env("FOUNTAIN_PROFILE") || "default";
|
|
65
|
+
let creds;
|
|
66
|
+
const credentials = () => (creds ??= readCredentials(profile));
|
|
67
|
+
let apiKey = (options.apiKey ?? "").trim() || env("FOUNTAIN_API_KEY") || env("FOUNTAIN_TOKEN");
|
|
68
|
+
if (!apiKey)
|
|
69
|
+
apiKey = credentials().api_key ?? "";
|
|
70
|
+
let baseUrl = (options.baseUrl ?? "").trim() || env("FOUNTAIN_BASE_URL");
|
|
71
|
+
if (!baseUrl)
|
|
72
|
+
baseUrl = credentials().base_url || DEFAULT_BASE_URL;
|
|
73
|
+
const appUrl = options.appUrl !== undefined ? options.appUrl.trim() : env("FOUNTAIN_APP_URL") || DEFAULT_APP_URL;
|
|
74
|
+
return {
|
|
75
|
+
baseUrl: baseUrl.replace(/\/+$/, ""),
|
|
76
|
+
apiKey,
|
|
77
|
+
appUrl: appUrl.replace(/\/+$/, ""),
|
|
78
|
+
parentConversationId: env("FOUNTAIN_CONVERSATION_ID") || undefined,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** The URL a human opens to watch this conversation. */
|
|
82
|
+
export function conversationUrl(conversationId, config) {
|
|
83
|
+
if (!config.appUrl)
|
|
84
|
+
return `${config.baseUrl}/api/conversations/${conversationId}`;
|
|
85
|
+
return `${config.appUrl}/#/c/${conversationId}`;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { HttpClient } from "./http.ts";
|
|
2
|
+
import type { ConversationRecord, LogEvent, Stream, Turn } from "./types.ts";
|
|
3
|
+
import { Run, type RunOptions } from "./run.ts";
|
|
4
|
+
import { type StreamOptions } from "./sse.ts";
|
|
5
|
+
export interface SendOptions extends RunOptions {
|
|
6
|
+
/** Images to attach to the prompt, as the API's `ImageInput` shape. */
|
|
7
|
+
images?: unknown[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A conversation you already have — the sandbox is still there, and so is
|
|
11
|
+
* everything the agent learned in it.
|
|
12
|
+
*
|
|
13
|
+
* This is the piece no stateless agent API has. `resume(id).send(...)` costs
|
|
14
|
+
* one prompt, not a re-explanation of the whole task, because the machine, the
|
|
15
|
+
* checkout and the session are exactly where the last turn left them.
|
|
16
|
+
*/
|
|
17
|
+
export declare class Conversation {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
private readonly http;
|
|
20
|
+
/** Where the log feed has been read to, so a follow-up skips the history. */
|
|
21
|
+
private cursorValue;
|
|
22
|
+
constructor(http: HttpClient, id: string, cursor?: number);
|
|
23
|
+
/** Where a human watches this conversation. */
|
|
24
|
+
get url(): string;
|
|
25
|
+
/** The conversation record: status, agent, turn count. */
|
|
26
|
+
get(): Promise<ConversationRecord>;
|
|
27
|
+
status(): Promise<ConversationRecord["status"]>;
|
|
28
|
+
turns(): Promise<Turn[]>;
|
|
29
|
+
/** Send the next turn. Returns a `Run` — await it, or stream it. */
|
|
30
|
+
send(prompt: string, options?: SendOptions): Run;
|
|
31
|
+
/**
|
|
32
|
+
* Answer a permission request the agent is holding a tool call on.
|
|
33
|
+
*
|
|
34
|
+
* `optionId` has to be one of the ids the agent offered on the
|
|
35
|
+
* `permission_request` block — the server refuses anything else with a 422
|
|
36
|
+
* rather than forwarding it. Answer promptly: the request expires, and an
|
|
37
|
+
* expired one is denied.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* for await (const event of run) {
|
|
41
|
+
* if (event.type === "permission") {
|
|
42
|
+
* const allow = event.request.options.find((o) => o.kind === "allow_once");
|
|
43
|
+
* if (allow) await conversation.answer(event.request.requestId, allow.optionId);
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
answer(requestId: string, optionId: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Mark everything so far as read, clearing the teammate's unread badge.
|
|
51
|
+
*
|
|
52
|
+
* Every application built on Fountain calls this — it is what stops a UI
|
|
53
|
+
* shouting about messages the person is currently looking at.
|
|
54
|
+
*/
|
|
55
|
+
markRead(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Everything the feed holds, oldest first, paged until drained.
|
|
58
|
+
*
|
|
59
|
+
* The other universal one: a UI opening a thread needs the transcript so
|
|
60
|
+
* far, and the JSON feed pages at 1000. `streams` narrows what comes back —
|
|
61
|
+
* `["acp"]` for a transcript, `["stage"]` for the lifecycle alone.
|
|
62
|
+
*/
|
|
63
|
+
history(options?: {
|
|
64
|
+
streams?: Stream[] | string;
|
|
65
|
+
after?: number;
|
|
66
|
+
limit?: number;
|
|
67
|
+
}): Promise<LogEvent[]>;
|
|
68
|
+
/** The conversations this one spawned, as a tree. */
|
|
69
|
+
tree(): Promise<unknown>;
|
|
70
|
+
/** Ask the agent to stop the turn it is on. The sandbox stays up. */
|
|
71
|
+
interrupt(): Promise<void>;
|
|
72
|
+
/** Tear the sandbox down. Nothing resumes after this. */
|
|
73
|
+
terminate(): Promise<void>;
|
|
74
|
+
/** Delete the conversation and its history. */
|
|
75
|
+
delete(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* The raw log feed, reconnecting on its own. Everything, from `after`
|
|
78
|
+
* onwards, of every turn — `run`/`send` is the filtered view of this.
|
|
79
|
+
*/
|
|
80
|
+
events(options?: StreamOptions): AsyncIterable<LogEvent>;
|
|
81
|
+
/** One page of the log feed as JSON, for a client that would rather poll. */
|
|
82
|
+
eventPage(after?: number, limit?: number): Promise<{
|
|
83
|
+
events: LogEvent[];
|
|
84
|
+
nextCursor: number;
|
|
85
|
+
hasMore: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
/** The highest turn number so far; the next prompt is this plus one. */
|
|
88
|
+
lastTurnNumber(): Promise<number>;
|
|
89
|
+
/** Where this handle has read to, discovering it if nobody has looked yet. */
|
|
90
|
+
cursor(): Promise<number>;
|
|
91
|
+
/**
|
|
92
|
+
* Find the end of the log feed cheaply.
|
|
93
|
+
*
|
|
94
|
+
* A cold `resume().send()` has no cursor, and starting from 0 would replay
|
|
95
|
+
* every event of every earlier turn before reaching ours. Draining the
|
|
96
|
+
* `stage` stream (`wait=false`) is a few rows for even a long conversation,
|
|
97
|
+
* and its ids are the same global ids the full feed uses.
|
|
98
|
+
*/
|
|
99
|
+
private discoverCursor;
|
|
100
|
+
}
|