@flue/sdk 0.4.0 → 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 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
- // `agent` -- Your initialized agent runtime including sandbox, tools, skills, etc.
41
- const agent = await init({ model: 'anthropic/claude-sonnet-4-6' });
42
- const session = await agent.session();
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 agent's 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.
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 agent's filesystem.
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 agent = await init({ sandbox, model: 'openrouter/moonshotai/kimi-k2.6' });
77
- const session = await agent.session();
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 agent = await init({
112
+ const harness = await init({
113
113
  sandbox: 'local',
114
114
  model: 'anthropic/claude-opus-4-7',
115
115
  });
116
- const session = await agent.session();
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 setupAgent = await init({
164
+ const setupHarness = await init({
165
165
  sandbox: daytona(sandbox),
166
166
  model: 'openai/gpt-5.5',
167
167
  });
168
- const setup = await setupAgent.session();
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 agent in the cloned repo. It shares the same sandbox, but
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 projectAgent = await init({
179
- id: 'project',
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 projectAgent.session();
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 agent = await init({
212
+ const harness = await init({
213
213
  model: 'anthropic/claude-sonnet-4-6',
214
214
  tools: github.tools,
215
215
  });
216
- const session = await agent.session();
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
- Every agent invocation runs inside an initialized agent runtime. For HTTP agents, the agent ID is the last path segment:
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
- By default, `agent.session()` opens the default session for that agent ID. Reuse the same agent ID to continue the same default conversation. Use a new agent ID to start fresh.
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
- Agents own sandbox state such as files written during a run. Sessions persist message history and conversation metadata inside an agent. 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.
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 agent ID for the sandbox/runtime scope you want to preserve. Use `agent.session(threadId)` when you need multiple conversations inside the same agent.
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 agent.session();
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 agent, session, or call level. Precedence is `call role > session role > agent role`. Role instructions are applied as call-scoped system prompt overlays, not injected into the persisted user message history.
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 agent = await init({ model: 'anthropic/claude-sonnet-4-6', role: 'coder' });
278
- const session = await agent.session('review-thread', { role: 'reviewer' });
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 `init()` instead of mutating global model state. They
292
- are runtime-scoped to that agent and apply to every model it resolves, including
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
- const agent = await init({
298
- model: 'anthropic/claude-sonnet-4-6',
299
- providers: {
300
- anthropic: {
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 agent = await init({
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 agent.session();
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-BAmV4f3Q.mjs";
2
- import { i as flue } from "./flue-app-CG8i4wNG.mjs";
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-DeTOZjPs.mjs";
2
- import { a as registerApiProvider, o as registerProvider, t as configureProvider } from "./providers-DeFRIwp0.mjs";
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 FlueAgent, f as FlueContext, g as FlueSession, h as FlueFs, i as BashFactory, j as ShellOptions, k as SessionOptions, m as FlueEventCallback, p as FlueEvent, r as AgentInit, t as AgentConfig, u as FileStat, v as ModelConfig, w as ProviderSettings, x as PromptResponse, y as PromptModel } from "./types-BAmV4f3Q.mjs";
2
- import { i as connectMcpServer, n as McpServerOptions, r as McpTransport, t as McpServerConnection } from "./mcp-C3UBXVkR.mjs";
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 FlueAgent, type FlueContext, FlueContextConfig, FlueContextInternal, type FlueEvent, type FlueEventCallback, type FlueFs, 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 };
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-DeFRIwp0.mjs";
3
- import { a as assertRoleExists } from "./session-CO_uGVOk.mjs";
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 AgentClient, t as connectMcpServer } from "./mcp-2SW_tpox.mjs";
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
- let currentEventCallback;
11
- const initializedAgentIds = /* @__PURE__ */ new Set();
12
- return {
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 id = options.id ?? config.id;
29
- if (initializedAgentIds.has(id)) throw new Error(`[flue] init() has already been called for agent "${id}" in this request.`);
30
- initializedAgentIds.add(id);
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
- return new AgentClient(id, {
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
- }, env, store, currentEventCallback, options.tools);
93
+ };
94
+ return new Harness(config.id, name, agentConfig, env, store, (event) => {
95
+ emitEvent(event);
96
+ }, options.tools);
47
97
  } catch (error) {
48
- initializedAgentIds.delete(id);
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
- currentEventCallback = callback;
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-BAmV4f3Q.mjs";
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-DeFRIwp0.mjs";
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-CO_uGVOk.mjs";
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:<requestId>', run)`.
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 = (requestId: string, run: () => Promise<unknown>) => Promise<unknown>;
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 };