@flue/sdk 0.4.1 → 0.5.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 +44 -42
- package/dist/app.d.mts +2 -2
- package/dist/app.mjs +2 -2
- package/dist/client.d.mts +7 -3
- package/dist/client.mjs +86 -14
- package/dist/cloudflare/index.d.mts +1 -1
- package/dist/cloudflare/index.mjs +2 -2
- package/dist/{flue-app-CG8i4wNG.d.mts → flue-app-O4_iqLkn.d.mts} +69 -4
- package/dist/{flue-app-DeTOZjPs.mjs → flue-app-SjL4I83Y.mjs} +520 -112
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +56 -6
- package/dist/internal.d.mts +40 -3
- package/dist/internal.mjs +284 -5
- package/dist/{mcp-C3UBXVkR.d.mts → mcp-BfcWmA-A.d.mts} +1 -1
- package/dist/{mcp-DM6yv_Qc.mjs → mcp-DwLSoSxp.mjs} +52 -38
- package/dist/node/index.d.mts +1 -1
- package/dist/{providers-DeFRIwp0.mjs → providers-BjEEoKLy.mjs} +11 -1
- package/dist/sandbox.d.mts +1 -1
- package/dist/sandbox.mjs +2 -2
- package/dist/{session-CFOByKnM.mjs → session-CRFfAJDq.mjs} +201 -79
- package/dist/{types-BAmV4f3Q.d.mts → types-Cdcq_ET2.d.mts} +87 -32
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -37,9 +37,9 @@ export const triggers = { webhook: true };
|
|
|
37
37
|
|
|
38
38
|
// The agent handler. Where the orchestration of the agent lives.
|
|
39
39
|
export default async function ({ init, payload }: FlueContext) {
|
|
40
|
-
// `
|
|
41
|
-
const
|
|
42
|
-
const session = await
|
|
40
|
+
// `harness` -- Your initialized harness including sandbox, tools, skills, etc.
|
|
41
|
+
const harness = await init({ model: 'anthropic/claude-sonnet-4-6' });
|
|
42
|
+
const session = await harness.session();
|
|
43
43
|
|
|
44
44
|
// prompt() sends a message in the session, triggering action.
|
|
45
45
|
const { data } = await session.prompt(`Translate this to ${payload.language}: "${payload.text}"`, {
|
|
@@ -56,7 +56,7 @@ export default async function ({ init, payload }: FlueContext) {
|
|
|
56
56
|
|
|
57
57
|
### Support Agent
|
|
58
58
|
|
|
59
|
-
A support agent can also run in a virtual sandbox, but we now add a file-system using an R2 bucket. The knowledge base is stored in R2 and mounted directly into the
|
|
59
|
+
A support agent can also run in a virtual sandbox, but we now add a file-system using an R2 bucket. The knowledge base is stored in R2 and mounted directly into the harness filesystem — the agent searches it with its built-in tools (grep, glob, read). Skills are also defined in the bucket that help the agent perform its task.
|
|
60
60
|
|
|
61
61
|
Because this agent is deployed to Cloudflare, message history and session state are automatically persisted for you. So you (or your customer) can revisit this support session days, weeks, or years later and pick up exactly where you left off.
|
|
62
62
|
|
|
@@ -69,12 +69,12 @@ import * as v from 'valibot';
|
|
|
69
69
|
export const triggers = { webhook: true };
|
|
70
70
|
|
|
71
71
|
export default async function ({ init, payload, env }: FlueContext) {
|
|
72
|
-
// Mount the R2 knowledge base bucket as the
|
|
72
|
+
// Mount the R2 knowledge base bucket as the harness filesystem.
|
|
73
73
|
// The agent can grep, glob, and read articles with bash, but
|
|
74
74
|
// without needing to spin up an entire container sandbox.
|
|
75
75
|
const sandbox = await getVirtualSandbox(env.KNOWLEDGE_BASE);
|
|
76
|
-
const
|
|
77
|
-
const session = await
|
|
76
|
+
const harness = await init({ sandbox, model: 'openrouter/moonshotai/kimi-k2.6' });
|
|
77
|
+
const session = await harness.session();
|
|
78
78
|
|
|
79
79
|
return await session.prompt(
|
|
80
80
|
`You are a support agent. Search the knowledge base for articles
|
|
@@ -109,11 +109,11 @@ export default async function ({ init, payload }: FlueContext) {
|
|
|
109
109
|
//
|
|
110
110
|
// `model` sets the default model for every prompt/skill call in this
|
|
111
111
|
// agent. Override per-call with `{ model: '...' }` on prompt()/skill().
|
|
112
|
-
const
|
|
112
|
+
const harness = await init({
|
|
113
113
|
sandbox: 'local',
|
|
114
114
|
model: 'anthropic/claude-opus-4-7',
|
|
115
115
|
});
|
|
116
|
-
const session = await
|
|
116
|
+
const session = await harness.session();
|
|
117
117
|
|
|
118
118
|
// Skills can be referenced either by their frontmatter `name:` (shown below)
|
|
119
119
|
// or by a relative path under `.agents/skills/` — e.g.
|
|
@@ -157,15 +157,15 @@ export default async function ({ init, payload, env }: FlueContext) {
|
|
|
157
157
|
// a full Linux environment with persistent filesystem and shell.
|
|
158
158
|
//
|
|
159
159
|
// For simplicity, we always create a new sandbox here. You could also
|
|
160
|
-
// first check for an existing sandbox for the agent id, and reuse that
|
|
160
|
+
// first check for an existing sandbox for the agent instance id, and reuse that
|
|
161
161
|
// instead to best pick up where you last left off in the conversation.
|
|
162
162
|
const client = new Daytona({ apiKey: env.DAYTONA_API_KEY });
|
|
163
163
|
const sandbox = await client.create();
|
|
164
|
-
const
|
|
164
|
+
const setupHarness = await init({
|
|
165
165
|
sandbox: daytona(sandbox),
|
|
166
166
|
model: 'openai/gpt-5.5',
|
|
167
167
|
});
|
|
168
|
-
const setup = await
|
|
168
|
+
const setup = await setupHarness.session();
|
|
169
169
|
|
|
170
170
|
// For simplicity, we clone the target repo into the sandbox here.
|
|
171
171
|
// You could also bake these into the container image snapshot for a
|
|
@@ -173,15 +173,15 @@ export default async function ({ init, payload, env }: FlueContext) {
|
|
|
173
173
|
await setup.shell(`git clone ${payload.repo} /workspace/project`);
|
|
174
174
|
await setup.shell('npm install', { cwd: '/workspace/project' });
|
|
175
175
|
|
|
176
|
-
// Start a second
|
|
176
|
+
// Start a second harness in the cloned repo. It shares the same sandbox, but
|
|
177
177
|
// discovers AGENTS.md and skills from /workspace/project.
|
|
178
|
-
const
|
|
179
|
-
|
|
178
|
+
const projectHarness = await init({
|
|
179
|
+
name: 'project',
|
|
180
180
|
sandbox: daytona(sandbox),
|
|
181
181
|
cwd: '/workspace/project',
|
|
182
182
|
model: 'openai/gpt-5.5',
|
|
183
183
|
});
|
|
184
|
-
const session = await
|
|
184
|
+
const session = await projectHarness.session();
|
|
185
185
|
|
|
186
186
|
// Coding agents don't hide the agent DX from the user, so no need to
|
|
187
187
|
// wrap the user's prompt in anything. Just send it to the agent directly
|
|
@@ -209,11 +209,11 @@ export default async function ({ init, payload, env }: FlueContext) {
|
|
|
209
209
|
});
|
|
210
210
|
|
|
211
211
|
try {
|
|
212
|
-
const
|
|
212
|
+
const harness = await init({
|
|
213
213
|
model: 'anthropic/claude-sonnet-4-6',
|
|
214
214
|
tools: github.tools,
|
|
215
215
|
});
|
|
216
|
-
const session = await
|
|
216
|
+
const session = await harness.session();
|
|
217
217
|
return await session.prompt(payload.prompt);
|
|
218
218
|
} finally {
|
|
219
219
|
await github.close();
|
|
@@ -223,15 +223,17 @@ export default async function ({ init, payload, env }: FlueContext) {
|
|
|
223
223
|
|
|
224
224
|
`connectMcpServer()` defaults to modern streamable HTTP. For legacy SSE servers, pass `transport: 'sse'`. Flue does not auto-detect transports, spawn local stdio MCP servers, or handle OAuth callbacks in this first version.
|
|
225
225
|
|
|
226
|
-
## Agents And Sessions
|
|
226
|
+
## Agents, Harnesses, And Sessions
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
An agent is the source file in `agents/<name>.ts`. For HTTP agents, the URL `<id>` segment identifies the agent instance: the durable runtime scope for one customer, repo, conversation space, or other caller-defined boundary.
|
|
229
229
|
|
|
230
230
|
```txt
|
|
231
231
|
POST /agents/<agent-name>/<id>
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
Inside a run, `init()` creates a harness: a configured handle for model defaults, tools, sandbox, filesystem, and sessions. The default harness is named `"default"`; pass `init({ name })` when one run needs multiple isolated harness scopes.
|
|
235
|
+
|
|
236
|
+
By default, `harness.session()` opens the default session inside the default harness for that agent instance. Reuse the same URL `<id>` to continue the same agent instance. Use a new URL `<id>` to start fresh.
|
|
235
237
|
|
|
236
238
|
```bash
|
|
237
239
|
# Start a conversation (port 3583 is `flue dev`'s default)
|
|
@@ -250,16 +252,16 @@ curl http://localhost:3583/agents/hello/session-xyz \
|
|
|
250
252
|
-d '{"name": "Alice"}'
|
|
251
253
|
```
|
|
252
254
|
|
|
253
|
-
|
|
255
|
+
Agent instances own sandbox state such as files written during a run. Harnesses group related session state within an instance. Sessions persist message history and conversation metadata inside a harness. On Cloudflare, session data is backed by Durable Objects and survives across requests. On Node.js, sessions are stored in memory by default unless you provide a custom store.
|
|
254
256
|
|
|
255
|
-
In production, generate a stable
|
|
257
|
+
In production, generate a stable URL `<id>` for the agent instance you want to preserve. Use `harness.session(threadName)` when you need multiple conversations inside the same harness.
|
|
256
258
|
|
|
257
259
|
### Tasks
|
|
258
260
|
|
|
259
261
|
Use `session.task()` to run a focused, one-shot child agent in a detached session. Tasks share the same sandbox/filesystem, but get their own message history and discover `AGENTS.md` plus `.agents/skills/` from their working directory. The same `task` tool is also available to the LLM during `prompt()` and `skill()` calls, so the agent can delegate parallel research or exploration work itself.
|
|
260
262
|
|
|
261
263
|
```ts
|
|
262
|
-
const session = await
|
|
264
|
+
const session = await harness.session();
|
|
263
265
|
|
|
264
266
|
const research = await session.task('Research the auth flow and summarize the key files.', {
|
|
265
267
|
cwd: '/workspace/project',
|
|
@@ -271,11 +273,11 @@ const answer = await session.prompt(
|
|
|
271
273
|
);
|
|
272
274
|
```
|
|
273
275
|
|
|
274
|
-
Roles can be set at the
|
|
276
|
+
Roles can be set at the harness, session, or call level. Precedence is `call role > session role > harness role`. Role instructions are applied as call-scoped system prompt overlays, not injected into the persisted user message history.
|
|
275
277
|
|
|
276
278
|
```ts
|
|
277
|
-
const
|
|
278
|
-
const session = await
|
|
279
|
+
const harness = await init({ model: 'anthropic/claude-sonnet-4-6', role: 'coder' });
|
|
280
|
+
const session = await harness.session('review-thread', { role: 'reviewer' });
|
|
279
281
|
|
|
280
282
|
await session.prompt('Review the latest changes.'); // uses reviewer
|
|
281
283
|
await session.task('Research related issues.', { role: 'researcher' }); // uses researcher
|
|
@@ -288,25 +290,25 @@ such as an enterprise API gateway, provider-compatible proxy, custom endpoint,
|
|
|
288
290
|
or gateway-specific credentials. This is common for managed credentials, audit
|
|
289
291
|
logging, traffic routing, or self-hosted OpenAI-compatible providers.
|
|
290
292
|
|
|
291
|
-
Configure these settings in `
|
|
292
|
-
|
|
293
|
-
agent defaults, role-level models, per-call model selections, tasks, and context
|
|
294
|
-
compaction.
|
|
293
|
+
Configure these settings in `app.ts` instead of mutating global model state. They
|
|
294
|
+
apply to every harness and session that resolves models through that provider.
|
|
295
295
|
|
|
296
296
|
```ts
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
297
|
+
// .flue/app.ts
|
|
298
|
+
import { configureProvider, flue } from '@flue/sdk/app';
|
|
299
|
+
|
|
300
|
+
export default {
|
|
301
|
+
fetch(req, env, ctx) {
|
|
302
|
+
configureProvider('anthropic', {
|
|
301
303
|
baseUrl: env.ANTHROPIC_BASE_URL,
|
|
302
|
-
headers: {
|
|
303
|
-
'X-Custom-Auth': env.GATEWAY_KEY,
|
|
304
|
-
},
|
|
304
|
+
headers: { 'X-Custom-Auth': env.GATEWAY_KEY },
|
|
305
305
|
// Use this when the proxy expects a synthetic or gateway-specific key.
|
|
306
306
|
apiKey: 'dummy',
|
|
307
|
-
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
return flue().fetch(req, env, ctx);
|
|
308
310
|
},
|
|
309
|
-
}
|
|
311
|
+
};
|
|
310
312
|
```
|
|
311
313
|
|
|
312
314
|
### Custom Virtual Sandboxes
|
|
@@ -318,11 +320,11 @@ import { Bash, InMemoryFs } from 'just-bash';
|
|
|
318
320
|
|
|
319
321
|
const fs = new InMemoryFs();
|
|
320
322
|
|
|
321
|
-
const
|
|
323
|
+
const harness = await init({
|
|
322
324
|
sandbox: () => new Bash({ fs, cwd: '/workspace', python: true }),
|
|
323
325
|
model: 'anthropic/claude-sonnet-4-6',
|
|
324
326
|
});
|
|
325
|
-
const session = await
|
|
327
|
+
const session = await harness.session();
|
|
326
328
|
```
|
|
327
329
|
|
|
328
330
|
## Connectors
|
package/dist/app.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { w as ProviderSettings } from "./types-
|
|
2
|
-
import { i as flue } from "./flue-app-
|
|
1
|
+
import { w as ProviderSettings } from "./types-Cdcq_ET2.mjs";
|
|
2
|
+
import { i as flue } from "./flue-app-O4_iqLkn.mjs";
|
|
3
3
|
import { t as CLOUDFLARE_AI_BINDING_API } from "./cloudflare-model-BeiZ1pLz.mjs";
|
|
4
4
|
import { Api, Model, registerApiProvider as registerApiProvider$1 } from "@mariozechner/pi-ai";
|
|
5
5
|
|
package/dist/app.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as flue } from "./flue-app-
|
|
2
|
-
import { a as registerApiProvider, o as registerProvider, t as configureProvider } from "./providers-
|
|
1
|
+
import { r as flue } from "./flue-app-SjL4I83Y.mjs";
|
|
2
|
+
import { a as registerApiProvider, o as registerProvider, t as configureProvider } from "./providers-BjEEoKLy.mjs";
|
|
3
3
|
|
|
4
4
|
export { configureProvider, flue, registerApiProvider, registerProvider };
|
package/dist/client.d.mts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { A as SessionStore, C as PromptUsage, D as SessionData, E as SandboxFactory, F as TaskOptions, I as ThinkingLevel, L as ToolDef, M as ShellResult, O as SessionEnv, P as SkillOptions, R as ToolParameters, S as PromptResultResponse, _ as FlueSessions, a as BashLike, b as PromptOptions, d as
|
|
2
|
-
import { i as connectMcpServer, n as McpServerOptions, r as McpTransport, t as McpServerConnection } from "./mcp-
|
|
1
|
+
import { A as SessionStore, C as PromptUsage, D as SessionData, E as SandboxFactory, F as TaskOptions, I as ThinkingLevel, L as ToolDef, M as ShellResult, O as SessionEnv, P as SkillOptions, R as ToolParameters, S as PromptResultResponse, _ as FlueSessions, a as BashLike, b as PromptOptions, d as FlueContext, f as FlueEvent, g as FlueSession, h as FlueHarness, i as BashFactory, j as ShellOptions, k as SessionOptions, m as FlueFs, p as FlueEventCallback, r as AgentInit, t as AgentConfig, u as FileStat, v as ModelConfig, w as ProviderSettings, x as PromptResponse, y as PromptModel } from "./types-Cdcq_ET2.mjs";
|
|
2
|
+
import { i as connectMcpServer, n as McpServerOptions, r as McpTransport, t as McpServerConnection } from "./mcp-BfcWmA-A.mjs";
|
|
3
3
|
import { Type } from "@mariozechner/pi-ai";
|
|
4
4
|
|
|
5
5
|
//#region src/client.d.ts
|
|
6
6
|
interface FlueContextConfig {
|
|
7
7
|
id: string;
|
|
8
|
+
runId: string;
|
|
8
9
|
payload: any;
|
|
9
10
|
env: Record<string, any>;
|
|
10
11
|
agentConfig: AgentConfig;
|
|
@@ -25,8 +26,11 @@ interface FlueContextConfig {
|
|
|
25
26
|
}
|
|
26
27
|
/** Extends FlueContext with server-only methods. Agent handlers only see FlueContext. */
|
|
27
28
|
interface FlueContextInternal extends FlueContext {
|
|
29
|
+
/** Decorate and dispatch an event, returning the decorated event. */
|
|
30
|
+
emitEvent(event: FlueEvent): FlueEvent;
|
|
31
|
+
subscribeEvent(callback: FlueEventCallback): () => void;
|
|
28
32
|
setEventCallback(callback: FlueEventCallback | undefined): void;
|
|
29
33
|
}
|
|
30
34
|
declare function createFlueContext(config: FlueContextConfig): FlueContextInternal;
|
|
31
35
|
//#endregion
|
|
32
|
-
export { type AgentInit, type BashFactory, type BashLike, type FileStat, type
|
|
36
|
+
export { type AgentInit, type BashFactory, type BashLike, type FileStat, type FlueContext, FlueContextConfig, FlueContextInternal, type FlueEvent, type FlueEventCallback, type FlueFs, type FlueHarness, type FlueSession, type FlueSessions, type McpServerConnection, type McpServerOptions, type McpTransport, type ModelConfig, type PromptModel, type PromptOptions, type PromptResponse, type PromptResultResponse, type PromptUsage, type ProviderSettings, type SandboxFactory, type SessionData, type SessionEnv, type SessionOptions, type SessionStore, type ShellOptions, type ShellResult, type SkillOptions, type TaskOptions, type ThinkingLevel, type ToolDef, type ToolParameters, Type, connectMcpServer, createFlueContext };
|
package/dist/client.mjs
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
import { u as discoverSessionContext } from "./result-K1IRhWKM.mjs";
|
|
2
|
-
import "./providers-
|
|
3
|
-
import { a as assertRoleExists } from "./session-
|
|
2
|
+
import "./providers-BjEEoKLy.mjs";
|
|
3
|
+
import { a as assertRoleExists } from "./session-CRFfAJDq.mjs";
|
|
4
4
|
import { bashFactoryToSessionEnv, createCwdSessionEnv } from "./sandbox.mjs";
|
|
5
|
-
import { n as
|
|
5
|
+
import { n as Harness, t as connectMcpServer } from "./mcp-DwLSoSxp.mjs";
|
|
6
6
|
import { Type } from "@mariozechner/pi-ai";
|
|
7
7
|
|
|
8
8
|
//#region src/client.ts
|
|
9
9
|
function createFlueContext(config) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
11
|
+
let handlerUnsubscribe;
|
|
12
|
+
let eventIndex = 0;
|
|
13
|
+
const initializedHarnessNames = /* @__PURE__ */ new Set();
|
|
14
|
+
const emitEvent = (event) => {
|
|
15
|
+
const decorated = {
|
|
16
|
+
...event,
|
|
17
|
+
runId: config.runId,
|
|
18
|
+
eventIndex: eventIndex++,
|
|
19
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20
|
+
};
|
|
21
|
+
for (const subscriber of subscribers) try {
|
|
22
|
+
Promise.resolve(subscriber(decorated)).catch((error) => {
|
|
23
|
+
console.error("[flue:subscriber] Event subscriber failed:", error);
|
|
24
|
+
});
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("[flue:subscriber] Event subscriber failed:", error);
|
|
27
|
+
}
|
|
28
|
+
return decorated;
|
|
29
|
+
};
|
|
30
|
+
const ctx = {
|
|
13
31
|
get id() {
|
|
14
32
|
return config.id;
|
|
15
33
|
},
|
|
34
|
+
get runId() {
|
|
35
|
+
return config.runId;
|
|
36
|
+
},
|
|
16
37
|
get payload() {
|
|
17
38
|
return config.payload;
|
|
18
39
|
},
|
|
@@ -22,37 +43,88 @@ function createFlueContext(config) {
|
|
|
22
43
|
get req() {
|
|
23
44
|
return config.req;
|
|
24
45
|
},
|
|
46
|
+
log: {
|
|
47
|
+
info(message, attributes) {
|
|
48
|
+
emitEvent({
|
|
49
|
+
type: "log",
|
|
50
|
+
level: "info",
|
|
51
|
+
message,
|
|
52
|
+
attributes: normalizeLogAttributes(attributes)
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
warn(message, attributes) {
|
|
56
|
+
emitEvent({
|
|
57
|
+
type: "log",
|
|
58
|
+
level: "warn",
|
|
59
|
+
message,
|
|
60
|
+
attributes: normalizeLogAttributes(attributes)
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
error(message, attributes) {
|
|
64
|
+
emitEvent({
|
|
65
|
+
type: "log",
|
|
66
|
+
level: "error",
|
|
67
|
+
message,
|
|
68
|
+
attributes: normalizeLogAttributes(attributes)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
},
|
|
25
72
|
async init(options) {
|
|
26
73
|
if (!options || !("model" in options)) throw new Error("[flue] init() requires a model. Pass { model: \"provider/model-id\" } or { model: false }.");
|
|
27
74
|
if (options.model !== false && typeof options.model !== "string") throw new Error("[flue] init({ model }) must be a model string or false.");
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
|
|
75
|
+
const name = options.name ?? "default";
|
|
76
|
+
if (initializedHarnessNames.has(name)) throw new Error(`[flue] init() has already been called with name "${name}" in this request.`);
|
|
77
|
+
initializedHarnessNames.add(name);
|
|
31
78
|
try {
|
|
32
79
|
assertRoleExists(config.agentConfig.roles, options.role);
|
|
33
80
|
const sandbox = options.sandbox;
|
|
34
|
-
const baseEnv = await resolveSessionEnv(id, sandbox, config, options.cwd);
|
|
81
|
+
const baseEnv = await resolveSessionEnv(config.id, sandbox, config, options.cwd);
|
|
35
82
|
const env = options.cwd ? createCwdSessionEnv(baseEnv, options.cwd) : baseEnv;
|
|
36
83
|
const store = options.persist ?? config.defaultStore;
|
|
37
84
|
const localContext = await discoverSessionContext(env);
|
|
38
85
|
const agentModel = config.agentConfig.resolveModel(options.model);
|
|
39
|
-
|
|
86
|
+
const agentConfig = {
|
|
40
87
|
...config.agentConfig,
|
|
41
88
|
systemPrompt: localContext.systemPrompt,
|
|
42
89
|
skills: localContext.skills,
|
|
43
90
|
model: agentModel,
|
|
44
91
|
role: options.role ?? config.agentConfig.role,
|
|
45
92
|
thinkingLevel: options.thinkingLevel ?? config.agentConfig.thinkingLevel
|
|
46
|
-
}
|
|
93
|
+
};
|
|
94
|
+
return new Harness(config.id, name, agentConfig, env, store, (event) => {
|
|
95
|
+
emitEvent(event);
|
|
96
|
+
}, options.tools);
|
|
47
97
|
} catch (error) {
|
|
48
|
-
|
|
98
|
+
initializedHarnessNames.delete(name);
|
|
49
99
|
throw error;
|
|
50
100
|
}
|
|
51
101
|
},
|
|
102
|
+
emitEvent,
|
|
103
|
+
subscribeEvent(callback) {
|
|
104
|
+
subscribers.add(callback);
|
|
105
|
+
return () => subscribers.delete(callback);
|
|
106
|
+
},
|
|
52
107
|
setEventCallback(callback) {
|
|
53
|
-
|
|
108
|
+
handlerUnsubscribe?.();
|
|
109
|
+
handlerUnsubscribe = callback ? ctx.subscribeEvent(callback) : void 0;
|
|
54
110
|
}
|
|
55
111
|
};
|
|
112
|
+
return ctx;
|
|
113
|
+
}
|
|
114
|
+
function normalizeLogAttributes(attributes) {
|
|
115
|
+
if (!attributes) return void 0;
|
|
116
|
+
if (!(attributes.error instanceof Error)) return attributes;
|
|
117
|
+
return {
|
|
118
|
+
...attributes,
|
|
119
|
+
error: serializeLogError(attributes.error)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function serializeLogError(error) {
|
|
123
|
+
return {
|
|
124
|
+
name: error.name,
|
|
125
|
+
message: error.message,
|
|
126
|
+
stack: error.stack
|
|
127
|
+
};
|
|
56
128
|
}
|
|
57
129
|
/** Duck-type detection for just-bash Bash instances. */
|
|
58
130
|
function isBashLike(value) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as SessionStore, O as SessionEnv } from "../types-
|
|
1
|
+
import { A as SessionStore, O as SessionEnv } from "../types-Cdcq_ET2.mjs";
|
|
2
2
|
import { n as CloudflareAIBindingApi } from "../cloudflare-model-BeiZ1pLz.mjs";
|
|
3
3
|
import { ApiProvider, StreamOptions } from "@mariozechner/pi-ai";
|
|
4
4
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../result-K1IRhWKM.mjs";
|
|
2
|
-
import { c as CLOUDFLARE_AI_BINDING_API, n as getModelBinding } from "../providers-
|
|
2
|
+
import { c as CLOUDFLARE_AI_BINDING_API, n as getModelBinding } from "../providers-BjEEoKLy.mjs";
|
|
3
3
|
import { t as abortErrorFor } from "../abort-Bg3qsAkU.mjs";
|
|
4
|
-
import "../session-
|
|
4
|
+
import "../session-CRFfAJDq.mjs";
|
|
5
5
|
import { createSandboxSessionEnv } from "../sandbox.mjs";
|
|
6
6
|
import { createAssistantMessageEventStream, parseStreamingJson } from "@mariozechner/pi-ai";
|
|
7
7
|
import { Workspace, WorkspaceFileSystem } from "@cloudflare/shell";
|
|
@@ -1,6 +1,58 @@
|
|
|
1
|
+
import { f as FlueEvent } from "./types-Cdcq_ET2.mjs";
|
|
1
2
|
import { FlueContextInternal } from "./client.mjs";
|
|
2
3
|
import { Hono } from "hono";
|
|
3
4
|
|
|
5
|
+
//#region src/runtime/run-store.d.ts
|
|
6
|
+
type RunStatus = 'active' | 'completed' | 'errored';
|
|
7
|
+
interface RunRecord {
|
|
8
|
+
runId: string;
|
|
9
|
+
instanceId: string;
|
|
10
|
+
agentName: string;
|
|
11
|
+
status: RunStatus;
|
|
12
|
+
startedAt: string;
|
|
13
|
+
endedAt?: string;
|
|
14
|
+
isError?: boolean;
|
|
15
|
+
durationMs?: number;
|
|
16
|
+
result?: unknown;
|
|
17
|
+
error?: unknown;
|
|
18
|
+
}
|
|
19
|
+
interface CreateRunInput {
|
|
20
|
+
runId: string;
|
|
21
|
+
instanceId: string;
|
|
22
|
+
agentName: string;
|
|
23
|
+
startedAt: string;
|
|
24
|
+
payload: unknown;
|
|
25
|
+
}
|
|
26
|
+
interface EndRunInput {
|
|
27
|
+
runId: string;
|
|
28
|
+
endedAt: string;
|
|
29
|
+
isError: boolean;
|
|
30
|
+
durationMs: number;
|
|
31
|
+
result?: unknown;
|
|
32
|
+
error?: unknown;
|
|
33
|
+
}
|
|
34
|
+
interface RunStore {
|
|
35
|
+
createRun(input: CreateRunInput): Promise<void>;
|
|
36
|
+
endRun(input: EndRunInput): Promise<void>;
|
|
37
|
+
appendEvent(runId: string, event: FlueEvent): Promise<void>;
|
|
38
|
+
getEvents(runId: string, fromIndex?: number): Promise<FlueEvent[]>;
|
|
39
|
+
getRun(runId: string): Promise<RunRecord | null>;
|
|
40
|
+
}
|
|
41
|
+
interface RunStoreOptions {
|
|
42
|
+
maxCompletedRuns?: number;
|
|
43
|
+
maxEventBytes?: number;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/runtime/run-subscribers.d.ts
|
|
47
|
+
type RunSubscriberListener = (event: FlueEvent) => void;
|
|
48
|
+
interface RunSubscriberRegistry {
|
|
49
|
+
subscribe(runId: string, listener: RunSubscriberListener): () => void;
|
|
50
|
+
publish(runId: string, event: FlueEvent): void;
|
|
51
|
+
/** Release registry state for a terminal run. */
|
|
52
|
+
complete(runId: string): void;
|
|
53
|
+
}
|
|
54
|
+
declare function createRunSubscriberRegistry(): RunSubscriberRegistry;
|
|
55
|
+
//#endregion
|
|
4
56
|
//#region src/runtime/handle-agent.d.ts
|
|
5
57
|
/**
|
|
6
58
|
* Agent handler signature — the default export of a `.flue/agents/<name>.ts`
|
|
@@ -13,18 +65,18 @@ type AgentHandler = (ctx: FlueContextInternal) => unknown | Promise<unknown>;
|
|
|
13
65
|
* - Node: env=process.env, defaultStore=in-memory, no resolveSandbox.
|
|
14
66
|
* - Cloudflare: env=DO env, defaultStore=DO SQLite, resolveSandbox=cfSandboxToSessionEnv.
|
|
15
67
|
*/
|
|
16
|
-
type CreateContextFn = (id: string, payload: unknown, request: Request) => FlueContextInternal;
|
|
68
|
+
type CreateContextFn = (id: string, runId: string, payload: unknown, request: Request) => FlueContextInternal;
|
|
17
69
|
/**
|
|
18
70
|
* Webhook execution wrapper. Receives the prepared run callback and returns
|
|
19
71
|
* a promise that resolves with the handler's return value. Implementations:
|
|
20
72
|
*
|
|
21
73
|
* - Node: just `run()` — no fiber, no DO.
|
|
22
|
-
* - Cloudflare: `doInstance.runFiber('flue:webhook:<
|
|
74
|
+
* - Cloudflare: `doInstance.runFiber('flue:webhook:<runId>', run)`.
|
|
23
75
|
*
|
|
24
76
|
* The caller is responsible for any logging on completion/error; this routine
|
|
25
77
|
* just kicks it off and returns the 202.
|
|
26
78
|
*/
|
|
27
|
-
type StartWebhookFn = (
|
|
79
|
+
type StartWebhookFn = (runId: string, run: () => Promise<unknown>) => Promise<unknown>;
|
|
28
80
|
/**
|
|
29
81
|
* Foreground handler execution wrapper. Wraps the call to `handler(ctx)` so
|
|
30
82
|
* targets can layer in keepalive / context propagation. Defaults to direct
|
|
@@ -61,6 +113,15 @@ interface HandleAgentOptions {
|
|
|
61
113
|
* mid-stream.
|
|
62
114
|
*/
|
|
63
115
|
runHandler?: RunHandlerFn;
|
|
116
|
+
/** Per-target run history store. If omitted, run persistence is disabled. */
|
|
117
|
+
runStore?: RunStore;
|
|
118
|
+
/**
|
|
119
|
+
* Per-target in-process subscriber registry used by the run-stream
|
|
120
|
+
* route to live-tail an active run. Optional — if omitted, the run
|
|
121
|
+
* still produces events and is persisted, but live-tail subscribers
|
|
122
|
+
* see only what's already in the store at the moment they connect.
|
|
123
|
+
*/
|
|
124
|
+
runSubscribers?: RunSubscriberRegistry;
|
|
64
125
|
}
|
|
65
126
|
/**
|
|
66
127
|
* Dispatch a single `/agents/:name/:id` request. The mode is chosen by
|
|
@@ -125,6 +186,10 @@ interface FlueRuntime {
|
|
|
125
186
|
startWebhook?: StartWebhookFn;
|
|
126
187
|
/** Optional Node foreground handler wrapper. Defaults to direct invocation. */
|
|
127
188
|
runHandler?: RunHandlerFn;
|
|
189
|
+
/** Node run history store. */
|
|
190
|
+
runStore?: RunStore;
|
|
191
|
+
/** Node in-process registry used for live run-stream tailing. */
|
|
192
|
+
runSubscribers?: RunSubscriberRegistry;
|
|
128
193
|
/**
|
|
129
194
|
* Forward an incoming request to the per-agent Durable Object via
|
|
130
195
|
* Cloudflare's Agents SDK. Required when {@link target} is `'cloudflare'`.
|
|
@@ -181,4 +246,4 @@ declare function flue(): Hono;
|
|
|
181
246
|
*/
|
|
182
247
|
declare function createDefaultFlueApp(): Hono;
|
|
183
248
|
//#endregion
|
|
184
|
-
export { AgentHandler as a, RunHandlerFn as c, flue as i, StartWebhookFn as l, configureFlueRuntime as n, CreateContextFn as o, createDefaultFlueApp as r, HandleAgentOptions as s, FlueRuntime as t, handleAgentRequest as u };
|
|
249
|
+
export { RunStatus as _, AgentHandler as a, RunHandlerFn as c, RunSubscriberListener as d, RunSubscriberRegistry as f, RunRecord as g, EndRunInput as h, flue as i, StartWebhookFn as l, CreateRunInput as m, configureFlueRuntime as n, CreateContextFn as o, createRunSubscriberRegistry as p, createDefaultFlueApp as r, HandleAgentOptions as s, FlueRuntime as t, handleAgentRequest as u, RunStore as v, RunStoreOptions as y };
|