glove-foundry 0.0.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/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 dterminal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # Glove Foundry
2
+
3
+ Glove Foundry is an Effect-native application framework for typed, observable Glove agents. It gives agent projects the conventions that Next.js gives web projects: file routes, colocated composition, a development server, generated types, durable runtime data, and a stable client/API.
4
+
5
+ **[Read the Foundry handbook](https://glove.dterminal.net/foundry/docs)** for the guided architecture, installation, composition, applications, automation, workspaces, multi-agent, observability, and deployment documentation.
6
+
7
+ The development server includes a hierarchical inspector for definitions, instances, runs, automations, integrations, and shared workspaces. See the [inspector guide](./docs/inspector.md).
8
+
9
+ ```bash
10
+ npx glove-foundry init my-agent-app
11
+ cd my-agent-app
12
+ pnpm install
13
+ pnpm dev
14
+ ```
15
+
16
+ Inside an installed project, the framework binary also supports `glove foundry dev`
17
+ and `glove foundry start`.
18
+
19
+ ## The mental model
20
+
21
+ Foundry keeps code and data deliberately separate.
22
+
23
+ | Kind | Lives in | Purpose |
24
+ | --- | --- | --- |
25
+ | Agent definition | `agents/<route>/agent.ts` | Reusable behavior and lazy assembly |
26
+ | Application, transmission, tool, MCP, memory, layer | Beside the owning agent | Reusable capability catalogue |
27
+ | Agent instance | `FoundryDataAdapter` | Workspace identity, context, installations, playbooks |
28
+ | Conversation | `FoundryDataAdapter` | One conversation owned by one instance |
29
+ | Playbook subscription | `FoundryDataAdapter` | Background policy that may provision zero, one, or many instances |
30
+ | Route/account/binding | Application data or topology adapter | External integration topology without credential material |
31
+ | Application connection | Application definition | Long-lived inbound provider worker |
32
+ | Schedule definition | Lazy agent resolver | Agent-local desired timing policy |
33
+ | Scheduled activation | `FoundryDataAdapter` | Reconstructed trigger created from a definition or agent tool |
34
+
35
+ An agent definition never declares request input or output. Foundry owns the `FoundryRequest` and `FoundryResult` contracts. An instance can be changed and reconstructed without editing its definition.
36
+
37
+ ## Colocated agent composition
38
+
39
+ ```text
40
+ agents/
41
+ support-lead/
42
+ agent.ts
43
+ composition.ts
44
+ apps/helpdesk.app.ts
45
+ actions/respond.action.ts
46
+ events/message-received.event.ts
47
+ transmissions/messages.transmission.ts
48
+ predicates/is-urgent.predicate.ts
49
+ connections/provider-events.connection.ts
50
+ tools/customer-context.tool.ts
51
+ mcp/knowledge.mcp.ts
52
+ memory/customer.memory.ts
53
+ inboxes/work.inbox.ts
54
+ layers/audit.layer.ts
55
+ subscribers/metrics.subscriber.ts
56
+ foundry.application.ts
57
+ foundry.config.ts
58
+ ```
59
+
60
+ ```ts
61
+ // agents/support-lead/agent.ts
62
+ import { defineAgent } from "glove-foundry"
63
+ import { components } from "./composition.js"
64
+
65
+ export default defineAgent({
66
+ description: "Owns difficult support conversations",
67
+ components,
68
+ model: (_agent, ctx) => chooseModel(ctx.message),
69
+ systemPrompt: (_agent, ctx) => promptFor(ctx.agentInstance, ctx.message),
70
+ tools: async (_agent, ctx) => toolsAllowedFor(ctx.agentInstance, ctx.message),
71
+ memory: (_agent, ctx) => memoryFor(ctx.agentInstance, ctx.conversation),
72
+ inboxes: (_agent, ctx) => loadInbox(ctx.agentId, ctx.conversationId),
73
+ })
74
+ ```
75
+
76
+ Every lazy resolver receives the current native Glove `Message`, prior messages, request, definition id, instance id, conversation id, workspace id, instance context, and current installations.
77
+
78
+ ## Filenames own code identity; data owns runtime identity
79
+
80
+ Every static primitive default-exports one definition. Foundry derives its id
81
+ from the convention path: `agents/support-lead/tools/calendar/today.tool.ts`
82
+ becomes `calendar/today`. Static definitions do not repeat an `id` string.
83
+
84
+ Code-authored relationships use imported values. Runtime policies are composed lazily by the agent definition and normalized only when they become instance data.
85
+
86
+ ```ts
87
+ const inbound = defineInboundRoute({
88
+ id: "helpdesk-inbound",
89
+ transmission: helpdeskTransmission,
90
+ account: supportAccount,
91
+ visibility: "workspace",
92
+ enabled: true,
93
+ config: {},
94
+ })
95
+
96
+ export default defineAgent({
97
+ description: "Support lead",
98
+ playbooks: (_agent, ctx) => [composePlaybook({
99
+ name: "urgent-support",
100
+ transmission: helpdeskTransmission,
101
+ match: {
102
+ event: messageReceived,
103
+ routes: [inbound],
104
+ predicate: { definition: isUrgent, parameters: { minimum: ctx.agentInstance.context.minimum ?? 3 } },
105
+ },
106
+ directives: [{ action: respond, instruction: "Resolve the request." }],
107
+ applications: [helpdeskApp],
108
+ })],
109
+ // model, tools, and other lazy surfaces...
110
+ })
111
+ ```
112
+
113
+ The stored instance contains `definitionId`, installation ids, transmission ids, event ids, action ids, account ids, and route ids because JSON and databases cannot preserve object identity. Reconstructors validate and freeze that data on load. Raw string references remain appropriate at HTTP, database, and frontend boundaries. Accounts, routes, bindings, instances, and conversations also keep explicit IDs because they are dynamic records that a UI or adapter may create. Code points to those dynamic records through the record value, such as `{ account: supportAccount }`; it does not copy their ids.
114
+
115
+ Definition config is followed through the imported value. Zod config schemas infer `install(...)` and decoded install-hook config; Effect transmission schemas infer account metadata, route config, inbound events, and outbound input/output. `defineConfig` preserves its exact type and rejects unknown framework keys.
116
+
117
+ ## Applications, transmissions, and connections
118
+
119
+ Applications are headless, installable capability definitions. They may own multiple inbound and outbound transmissions. Outbound transmissions become tools only when the application is installed on an instance.
120
+
121
+ ```ts
122
+ // connections/provider-events.connection.ts
123
+ export default defineConnection({
124
+ description: "Receives provider events",
125
+ transmissions: [messageTransmission, reactionTransmission],
126
+ connect: (ctx) => Effect.gen(function* () {
127
+ const session = yield* openUserOwnedProviderAdapter(ctx.account)
128
+ yield* ctx.ready()
129
+ yield* session.consume((event) => ctx.receive({
130
+ route: chooseRoute(ctx.routes, event),
131
+ eventId: event.id,
132
+ threadKey: event.threadId,
133
+ raw: event,
134
+ }))
135
+ }),
136
+ })
137
+ ```
138
+
139
+ Foundry supervises connection lifetime and retry, but never acquires or refreshes credentials. Account references contain only metadata and an opaque `accessRef`. Your `accountSessions` or provider adapter owns credential material and refresh.
140
+
141
+ Connections are desired only when:
142
+
143
+ - an instance has installed the application and has a matching inbound playbook; or
144
+ - an enabled playbook subscription targets the application installation.
145
+
146
+ This covers webhook/socket ingestion and long-lived provider bots without exposing the execution backend as a framework primitive.
147
+
148
+ ## Background playbooks and lazy provisioning
149
+
150
+ A playbook is serializable runtime policy. It is composed by an agent resolver or frontend from transmission primitives, then persisted on the instance. Executable normalization, authentication, predicates, serialization, and delivery live on the transmission definition.
151
+
152
+ ```ts
153
+ const [urgentSupport] = agentInstance.playbooks
154
+ await runtime.putPlaybookSubscription({
155
+ id: subscriptionId, // runtime data id from your UI/data layer
156
+ workspaceId: agentInstance.workspaceId,
157
+ enabled: true,
158
+ playbook: urgentSupport,
159
+ targets: runtimeSelectedTargets,
160
+ createdAt: now,
161
+ updatedAt: now,
162
+ })
163
+ ```
164
+
165
+ Provisioning modes:
166
+
167
+ - `singleton`: one stable instance for the subscription target.
168
+ - `per-thread`: one instance per inbound route/thread.
169
+ - `per-event`: a new stable instance per external event id.
170
+ - `existing`: deliver only to listed persisted instance ids.
171
+ - `custom`: delegate one-to-many selection to the application `provisioner` adapter.
172
+
173
+ The data adapter atomically enforces `provisioningKey`. Inbound delivery claims are also adapter-backed, so retrying the same route/event does not create duplicate runs.
174
+
175
+ ## Conversations and shared work
176
+
177
+ One agent instance can own many conversations. Foundry also provides adapter-backed workspace entries, shared inbox items, tasks, and scoped environment values. These are data primitives, not prompt conventions, so agents can pass documents and work records by reference instead of copying context.
178
+
179
+ ## Immediate work, future work, and sleep
180
+
181
+ Schedules are composable agent-local primitives, never a root registry. A lazy `schedules(agent, ctx)` resolver loads desired schedules into Foundry, while running agents can manage their persisted triggers through framework-owned tools:
182
+
183
+ - `glove_foundry_spawn` invokes work immediately.
184
+ - `glove_foundry_schedule` creates a one-time (`at` or `after`), interval (`every`), or calendar-aware (`cron`) activation.
185
+ - `glove_foundry_schedules` lists, updates, or cancels triggers owned by the current instance.
186
+ - `glove_foundry_sleep` suspends the current logical run until an absolute time or for a duration, then wakes the same instance and conversation with a resolution message.
187
+
188
+ Both operations persist adapter-backed activation data before the execution backend is armed. Pending wake-ups and recurring work are reconstructed when the runtime starts; the bundled memory adapter is for development, while a durable `FoundryDataAdapter` supplies production persistence.
189
+
190
+ Durations accept compact forms such as `30s`, `20m`, and `2h`, as well as Effect duration forms such as `20 minutes`. The execution backend remains private; agent projects see only these purpose-built tools and correlated activation events.
191
+
192
+ ## Development and inspection
193
+
194
+ ```bash
195
+ pnpm dev
196
+ ```
197
+
198
+ The inspector presents a causal activation path:
199
+
200
+ ```text
201
+ arrival → matching playbook → provisioned workforce → runs and outcomes
202
+ ```
203
+
204
+ It also shows instance installations, active playbook subscriptions, application connections, and the correlated trace for each run. Backend-specific runner concepts are not part of the Foundry UI or public client.
205
+
206
+ Useful endpoints:
207
+
208
+ ```text
209
+ GET /api/manifest
210
+ GET /api/agent-instances
211
+ PATCH /api/agent-instances/:id
212
+ GET /api/playbook-subscriptions
213
+ PUT /api/playbook-subscriptions
214
+ GET /api/application-connections
215
+ POST /api/transmissions/:routeId/fire
216
+ GET /api/runs
217
+ GET /api/events
218
+ ```
219
+
220
+ See [Building with Foundry](./docs/building-with-foundry.md), [Architecture](./docs/architecture.md), and the runnable [`examples/foundry-agent`](../../examples/foundry-agent).
@@ -0,0 +1,28 @@
1
+ # Third-party notices
2
+
3
+ ## Phosphor Icons
4
+
5
+ The Foundry inspector includes a curated subset of SVG paths from
6
+ `@phosphor-icons/core` 2.1.1.
7
+
8
+ MIT License
9
+
10
+ Copyright (c) 2023 Phosphor Icons
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
@@ -0,0 +1,368 @@
1
+ // src/client.ts
2
+ async function readResponse(response) {
3
+ const payload = await response.json();
4
+ if (!response.ok) {
5
+ throw new Error(payload.error ?? `Foundry request failed (${response.status}).`);
6
+ }
7
+ return payload;
8
+ }
9
+ function routePath(route) {
10
+ return route.split("/").map(encodeURIComponent).join("/");
11
+ }
12
+ var FoundryRunHandle = class {
13
+ id;
14
+ initial;
15
+ client;
16
+ constructor(id, initial, client) {
17
+ this.id = id;
18
+ this.initial = initial;
19
+ this.client = client;
20
+ }
21
+ get() {
22
+ return this.client.getRun(this.id);
23
+ }
24
+ cancel() {
25
+ return this.client.cancelRun(this.id);
26
+ }
27
+ events() {
28
+ return this.client.getEvents({ runId: this.id });
29
+ }
30
+ async wait(options = {}) {
31
+ const pollMs = options.pollMs ?? 150;
32
+ const timeoutMs = options.timeoutMs ?? 6e4;
33
+ const deadline = Date.now() + timeoutMs;
34
+ while (Date.now() < deadline) {
35
+ if (options.signal?.aborted) {
36
+ throw new Error("Waiting for Foundry run was aborted.");
37
+ }
38
+ const run = await this.get();
39
+ if (run.status === "completed" || run.status === "failed" || run.status === "cancelled") {
40
+ return run;
41
+ }
42
+ await new Promise((resolve, reject) => {
43
+ const onAbort = () => {
44
+ clearTimeout(timer);
45
+ reject(new Error("Waiting for Foundry run was aborted."));
46
+ };
47
+ const timer = setTimeout(() => {
48
+ options.signal?.removeEventListener("abort", onAbort);
49
+ resolve();
50
+ }, pollMs);
51
+ options.signal?.addEventListener("abort", onAbort, { once: true });
52
+ });
53
+ }
54
+ throw new Error(`Timed out waiting for Foundry run "${this.id}".`);
55
+ }
56
+ };
57
+ var FoundryClient = class {
58
+ baseUrl;
59
+ fetcher;
60
+ constructor(options = {}) {
61
+ this.baseUrl = (options.baseUrl ?? "http://127.0.0.1:4141").replace(
62
+ /\/$/,
63
+ ""
64
+ );
65
+ this.fetcher = options.fetch ?? globalThis.fetch;
66
+ }
67
+ async health() {
68
+ return readResponse(await this.fetcher(`${this.baseUrl}/health`));
69
+ }
70
+ agent(route) {
71
+ return {
72
+ create: (options) => this.createAgent(route, options),
73
+ request: async (request) => {
74
+ const response = await this.fetcher(
75
+ `${this.baseUrl}/api/agents/${routePath(route)}/runs`,
76
+ {
77
+ method: "POST",
78
+ headers: { "content-type": "application/json" },
79
+ body: JSON.stringify(request)
80
+ }
81
+ );
82
+ const run = await readResponse(response);
83
+ return new FoundryRunHandle(run.id, run, this.asUntyped());
84
+ }
85
+ };
86
+ }
87
+ async createAgent(definitionId, options = {}) {
88
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/agent-instances`, {
89
+ method: "POST",
90
+ headers: { "content-type": "application/json" },
91
+ body: JSON.stringify({ definitionId, ...options })
92
+ }));
93
+ }
94
+ async agentInstances(definitionId) {
95
+ const query = definitionId ? `?definition=${encodeURIComponent(definitionId)}` : "";
96
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/agent-instances${query}`));
97
+ }
98
+ async configureAgent(agentId, options) {
99
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/agent-instances/${encodeURIComponent(agentId)}`, {
100
+ method: "PATCH",
101
+ headers: { "content-type": "application/json" },
102
+ body: JSON.stringify(options)
103
+ }));
104
+ }
105
+ async setAgentPlaybooks(agentId, playbooks) {
106
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/agent-instances/${encodeURIComponent(agentId)}/playbooks`, {
107
+ method: "PUT",
108
+ headers: { "content-type": "application/json" },
109
+ body: JSON.stringify({ playbooks })
110
+ }));
111
+ }
112
+ async createConversation(agentId, options = {}) {
113
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/conversations`, {
114
+ method: "POST",
115
+ headers: { "content-type": "application/json" },
116
+ body: JSON.stringify({ agentId, ...options })
117
+ }));
118
+ }
119
+ async conversations(agentId) {
120
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/conversations?agent=${encodeURIComponent(agentId)}`));
121
+ }
122
+ async workspaceEntries(workspaceId) {
123
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/entries`));
124
+ }
125
+ async putWorkspaceEntry(workspaceId, key, value) {
126
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/entries`, {
127
+ method: "PUT",
128
+ headers: { "content-type": "application/json" },
129
+ body: JSON.stringify({ key, value })
130
+ }));
131
+ }
132
+ async sharedInbox(workspaceId) {
133
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/inbox`));
134
+ }
135
+ async postSharedInbox(workspaceId, input) {
136
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/inbox`, {
137
+ method: "POST",
138
+ headers: { "content-type": "application/json" },
139
+ body: JSON.stringify(input)
140
+ }));
141
+ }
142
+ async updateSharedInbox(workspaceId, itemId, status) {
143
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/inbox/${encodeURIComponent(itemId)}`, {
144
+ method: "PATCH",
145
+ headers: { "content-type": "application/json" },
146
+ body: JSON.stringify({ status })
147
+ }));
148
+ }
149
+ async tasks(workspaceId) {
150
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/tasks`));
151
+ }
152
+ async createTask(workspaceId, input) {
153
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/tasks`, {
154
+ method: "POST",
155
+ headers: { "content-type": "application/json" },
156
+ body: JSON.stringify(input)
157
+ }));
158
+ }
159
+ async updateTask(workspaceId, taskId, status) {
160
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/tasks/${encodeURIComponent(taskId)}`, {
161
+ method: "PATCH",
162
+ headers: { "content-type": "application/json" },
163
+ body: JSON.stringify({ status })
164
+ }));
165
+ }
166
+ async dataEnvironment(workspaceId, scope) {
167
+ const query = new URLSearchParams();
168
+ if (scope?.agentId) query.set("agent", scope.agentId);
169
+ if (scope?.conversationId) query.set("conversation", scope.conversationId);
170
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/environment${query.size ? `?${query}` : ""}`));
171
+ }
172
+ async dispatchInbound(input) {
173
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/transmissions/${encodeURIComponent(input.routeId)}/fire`, {
174
+ method: "POST",
175
+ headers: { "content-type": "application/json" },
176
+ body: JSON.stringify(input)
177
+ }));
178
+ }
179
+ async playbookSubscriptions(workspaceId) {
180
+ const query = workspaceId ? `?workspace=${encodeURIComponent(workspaceId)}` : "";
181
+ return readResponse(
182
+ await this.fetcher(`${this.baseUrl}/api/playbook-subscriptions${query}`)
183
+ );
184
+ }
185
+ async activations(workspaceId) {
186
+ const query = workspaceId ? `?workspace=${encodeURIComponent(workspaceId)}` : "";
187
+ return readResponse(
188
+ await this.fetcher(`${this.baseUrl}/api/activations${query}`)
189
+ );
190
+ }
191
+ async putPlaybookSubscription(subscription) {
192
+ return readResponse(
193
+ await this.fetcher(`${this.baseUrl}/api/playbook-subscriptions`, {
194
+ method: "PUT",
195
+ headers: { "content-type": "application/json" },
196
+ body: JSON.stringify(subscription)
197
+ })
198
+ );
199
+ }
200
+ async deletePlaybookSubscription(id) {
201
+ const result = await readResponse(
202
+ await this.fetcher(
203
+ `${this.baseUrl}/api/playbook-subscriptions/${encodeURIComponent(id)}`,
204
+ { method: "DELETE" }
205
+ )
206
+ );
207
+ return result.removed;
208
+ }
209
+ async applicationConnections() {
210
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/application-connections`));
211
+ }
212
+ async reconnectApplicationConnection(id) {
213
+ await readResponse(
214
+ await this.fetcher(
215
+ `${this.baseUrl}/api/application-connections/${encodeURIComponent(id)}/reconnect`,
216
+ { method: "POST" }
217
+ )
218
+ );
219
+ }
220
+ async dispatchOutbound(input) {
221
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/transmissions/${encodeURIComponent(input.routeId)}/deliver`, {
222
+ method: "POST",
223
+ headers: { "content-type": "application/json" },
224
+ body: JSON.stringify(input)
225
+ }));
226
+ }
227
+ async send(agentId, conversationId, message, options = {}) {
228
+ const run = await readResponse(
229
+ await this.fetcher(`${this.baseUrl}/api/conversations/${encodeURIComponent(conversationId)}/messages`, {
230
+ method: "POST",
231
+ headers: { "content-type": "application/json" },
232
+ body: JSON.stringify({ agentId, message, ...options })
233
+ })
234
+ );
235
+ return new FoundryRunHandle(run.id, run, this.asUntyped());
236
+ }
237
+ async getRun(runId) {
238
+ const response = await this.fetcher(
239
+ `${this.baseUrl}/api/runs/${encodeURIComponent(runId)}`
240
+ );
241
+ return readResponse(response);
242
+ }
243
+ async runs(route) {
244
+ const query = route ? `?agent=${encodeURIComponent(route)}` : "";
245
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/runs${query}`));
246
+ }
247
+ async cancelRun(runId) {
248
+ const response = await this.fetcher(
249
+ `${this.baseUrl}/api/runs/${encodeURIComponent(runId)}/cancel`,
250
+ { method: "POST" }
251
+ );
252
+ const payload = await readResponse(response);
253
+ return payload.cancelled;
254
+ }
255
+ async getEvents(filter) {
256
+ const query = new URLSearchParams();
257
+ if (filter?.runId) query.set("runId", filter.runId);
258
+ if (filter?.agent) query.set("agent", filter.agent);
259
+ if (filter?.after !== void 0) query.set("after", String(filter.after));
260
+ if (filter?.category) query.set("category", filter.category);
261
+ const suffix = query.size > 0 ? `?${query}` : "";
262
+ const response = await this.fetcher(
263
+ `${this.baseUrl}/api/events${suffix}`,
264
+ { headers: { accept: "application/json" } }
265
+ );
266
+ return readResponse(response);
267
+ }
268
+ async manifest() {
269
+ return readResponse(
270
+ await this.fetcher(`${this.baseUrl}/api/manifest`)
271
+ );
272
+ }
273
+ async capabilities(definitionId) {
274
+ const query = new URLSearchParams({ definition: definitionId });
275
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/capabilities?${query}`));
276
+ }
277
+ async surfaces(definitionId) {
278
+ const query = new URLSearchParams({ definition: definitionId });
279
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/surfaces?${query}`));
280
+ }
281
+ async installations(agentId) {
282
+ const query = new URLSearchParams({ agent: agentId });
283
+ return readResponse(
284
+ await this.fetcher(`${this.baseUrl}/api/installations?${query}`)
285
+ );
286
+ }
287
+ async install(agentId, installation) {
288
+ return readResponse(
289
+ await this.fetcher(`${this.baseUrl}/api/installations`, {
290
+ method: "PUT",
291
+ headers: { "content-type": "application/json" },
292
+ body: JSON.stringify({ agentId, ...installation })
293
+ })
294
+ );
295
+ }
296
+ async uninstall(agentId, installation) {
297
+ return readResponse(
298
+ await this.fetcher(`${this.baseUrl}/api/installations`, {
299
+ method: "DELETE",
300
+ headers: { "content-type": "application/json" },
301
+ body: JSON.stringify({ agentId, ...installation })
302
+ })
303
+ );
304
+ }
305
+ async accounts() {
306
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/accounts`));
307
+ }
308
+ async routes() {
309
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/routes`));
310
+ }
311
+ async putRoute(route) {
312
+ return readResponse(
313
+ await this.fetcher(`${this.baseUrl}/api/routes`, {
314
+ method: "PUT",
315
+ headers: { "content-type": "application/json" },
316
+ body: JSON.stringify(route)
317
+ })
318
+ );
319
+ }
320
+ async removeRoute(id) {
321
+ await readResponse(
322
+ await this.fetcher(`${this.baseUrl}/api/routes/${encodeURIComponent(id)}`, {
323
+ method: "DELETE"
324
+ })
325
+ );
326
+ }
327
+ async bindings() {
328
+ return readResponse(await this.fetcher(`${this.baseUrl}/api/bindings`));
329
+ }
330
+ async putBinding(binding) {
331
+ return readResponse(
332
+ await this.fetcher(`${this.baseUrl}/api/bindings`, {
333
+ method: "PUT",
334
+ headers: { "content-type": "application/json" },
335
+ body: JSON.stringify(binding)
336
+ })
337
+ );
338
+ }
339
+ async removeBinding(id) {
340
+ await readResponse(
341
+ await this.fetcher(
342
+ `${this.baseUrl}/api/bindings/${encodeURIComponent(id)}`,
343
+ { method: "DELETE" }
344
+ )
345
+ );
346
+ }
347
+ async resolveGrant(request) {
348
+ return readResponse(
349
+ await this.fetcher(`${this.baseUrl}/api/grants/resolve`, {
350
+ method: "POST",
351
+ headers: { "content-type": "application/json" },
352
+ body: JSON.stringify(request)
353
+ })
354
+ );
355
+ }
356
+ asUntyped() {
357
+ return this;
358
+ }
359
+ };
360
+ function createFoundryClient(options) {
361
+ return new FoundryClient(options);
362
+ }
363
+
364
+ export {
365
+ FoundryRunHandle,
366
+ FoundryClient,
367
+ createFoundryClient
368
+ };