@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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
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, N as Skill, O as SessionEnv, P as SkillOptions, R as ToolParameters, S as PromptResultResponse, T as Role, _ as FlueSessions, a as BashLike, b as PromptOptions, c as BuildPlugin, d as FlueAgent, f as FlueContext, g as FlueSession, h as FlueFs, i as BashFactory, j as ShellOptions, k as SessionOptions, l as CallHandle, m as FlueEventCallback, n as AgentInfo, o as BuildContext, p as FlueEvent, r as AgentInit, s as BuildOptions, t as AgentConfig, u as FileStat, v as ModelConfig, x as PromptResponse, y as PromptModel } from "./types-BAmV4f3Q.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, N as Skill, O as SessionEnv, P as SkillOptions, R as ToolParameters, S as PromptResultResponse, T as Role, _ as FlueSessions, a as BashLike, b as PromptOptions, c as BuildPlugin, d as FlueContext, f as FlueEvent, g as FlueSession, h as FlueHarness, i as BashFactory, j as ShellOptions, k as SessionOptions, l as CallHandle, m as FlueFs, n as AgentInfo, o as BuildContext, p as FlueEventCallback, r as AgentInit, s as BuildOptions, t as AgentConfig, u as FileStat, v as ModelConfig, x as PromptResponse, y as PromptModel } from "./types-Cdcq_ET2.mjs";
2
2
  import { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
3
3
 
4
4
  //#region src/build.d.ts
@@ -112,7 +112,7 @@ interface TaskToolParams {
112
112
  }
113
113
  interface TaskToolResultDetails {
114
114
  taskId: string;
115
- sessionId: string;
115
+ session: string;
116
116
  messageId?: string;
117
117
  role?: string;
118
118
  cwd?: string;
@@ -135,4 +135,4 @@ declare class ResultUnavailableError extends Error {
135
135
  constructor(reason: string, assistantText: string);
136
136
  }
137
137
  //#endregion
138
- export { type AgentConfig, type AgentInfo, type AgentInit, BUILTIN_TOOL_NAMES, type BashFactory, type BashLike, type BuildContext, type BuildOptions, type BuildPlugin, type CallHandle, DEFAULT_DEV_PORT, type DevOptions, type FileStat, type FlueAgent, type FlueContext, type FlueEvent, type FlueEventCallback, type FlueFs, type FlueSession, type FlueSessions, type ModelConfig, type PromptModel, type PromptOptions, type PromptResponse, type PromptResultResponse, type PromptUsage, ResultUnavailableError, type Role, type SandboxFactory, type SessionData, type SessionEnv, type SessionOptions, type SessionStore, type ShellOptions, type ShellResult, type Skill, type SkillOptions, type TaskOptions, type ThinkingLevel, type ToolDef, type ToolParameters, build, createTools, dev, parseEnvFiles, resolveEnvFiles, resolveSourceRoot };
138
+ export { type AgentConfig, type AgentInfo, type AgentInit, BUILTIN_TOOL_NAMES, type BashFactory, type BashLike, type BuildContext, type BuildOptions, type BuildPlugin, type CallHandle, DEFAULT_DEV_PORT, type DevOptions, type FileStat, type FlueContext, type FlueEvent, type FlueEventCallback, type FlueFs, type FlueHarness, type FlueSession, type FlueSessions, type ModelConfig, type PromptModel, type PromptOptions, type PromptResponse, type PromptResultResponse, type PromptUsage, ResultUnavailableError, type Role, type SandboxFactory, type SessionData, type SessionEnv, type SessionOptions, type SessionStore, type ShellOptions, type ShellResult, type Skill, type SkillOptions, type TaskOptions, type ThinkingLevel, type ToolDef, type ToolParameters, build, createTools, dev, parseEnvFiles, resolveEnvFiles, resolveSourceRoot };
package/dist/index.mjs CHANGED
@@ -422,9 +422,13 @@ import { Bash, InMemoryFs } from 'just-bash';
422
422
  import {
423
423
  createFlueContext,
424
424
  InMemorySessionStore,
425
+ InMemoryRunStore,
426
+ createDurableRunStore,
427
+ createRunSubscriberRegistry,
425
428
  bashFactoryToSessionEnv,
426
429
  resolveModel,
427
430
  handleAgentRequest,
431
+ handleRunRouteRequest,
428
432
  configureFlueRuntime,
429
433
  createDefaultFlueApp,
430
434
  } from '@flue/sdk/internal';
@@ -528,6 +532,10 @@ function resolveSandbox(sandbox) {
528
532
 
529
533
  // Fallback in-memory store (used if no DO storage is available).
530
534
  const memoryStore = new InMemorySessionStore();
535
+ const memoryRunStore = new InMemoryRunStore();
536
+
537
+ // Module-scoped per-isolate registry; run ids isolate buckets across DOs.
538
+ const runSubscribers = createRunSubscriberRegistry();
531
539
 
532
540
  // Create a DO-backed session store from the Durable Object's SQL storage.
533
541
  function createDOStore(sql) {
@@ -554,7 +562,7 @@ function createDOStore(sql) {
554
562
  };
555
563
  }
556
564
 
557
- function createContextForRequest(id, payload, doInstance, req) {
565
+ function createContextForRequest(id, runId, payload, doInstance, req) {
558
566
  // Use DO SQLite storage by default, fall back to in-memory
559
567
  const defaultStore = doInstance?.ctx?.storage?.sql
560
568
  ? createDOStore(doInstance.ctx.storage.sql)
@@ -562,6 +570,7 @@ function createContextForRequest(id, payload, doInstance, req) {
562
570
 
563
571
  return createFlueContext({
564
572
  id,
573
+ runId,
565
574
  payload,
566
575
  env: doInstance?.env ?? {},
567
576
  req,
@@ -575,6 +584,12 @@ function createContextForRequest(id, payload, doInstance, req) {
575
584
  });
576
585
  }
577
586
 
587
+ function createRunStoreForRequest(doInstance) {
588
+ return doInstance?.ctx?.storage?.sql
589
+ ? createDurableRunStore(doInstance.ctx.storage.sql)
590
+ : memoryRunStore;
591
+ }
592
+
578
593
  function runWithInstanceContext(doInstance, fn) {
579
594
  return runWithCloudflareContext(
580
595
  { env: doInstance.env, agentInstance: doInstance, storage: doInstance.ctx.storage },
@@ -612,28 +627,41 @@ async function handleFlueFiberRecovered(ctx, _doInstance, agentName) {
612
627
  */
613
628
  async function dispatchAgent(request, doInstance, agentName, handler) {
614
629
  const id = doInstance.name; // DO room name set by routeAgentRequest
630
+ const runRoute = parseRunRoute(request);
631
+ if (runRoute) {
632
+ return handleRunRouteRequest({
633
+ request,
634
+ agentName,
635
+ id,
636
+ runStore: createRunStoreForRequest(doInstance),
637
+ runSubscribers,
638
+ ...runRoute,
639
+ });
640
+ }
615
641
 
616
642
  return handleAgentRequest({
617
643
  request,
618
644
  agentName,
619
645
  id,
620
646
  handler,
621
- createContext: (id_, payload, req) => createContextForRequest(id_, payload, doInstance, req),
622
- startWebhook: (requestId, run) => {
647
+ runStore: createRunStoreForRequest(doInstance),
648
+ runSubscribers,
649
+ createContext: (id_, runId, payload, req) => createContextForRequest(id_, runId, payload, doInstance, req),
650
+ startWebhook: (runId, run) => {
623
651
  const wrapped = (fiber) => {
624
652
  fiber?.stash?.({
625
653
  version: 1,
626
654
  kind: 'webhook',
627
655
  agentName,
628
656
  id,
629
- requestId,
657
+ runId,
630
658
  phase: 'running',
631
659
  startedAt: Date.now(),
632
660
  });
633
661
  return runWithInstanceContext(doInstance, run);
634
662
  };
635
663
  assertAgentsDurabilityApi(doInstance, 'runFiber');
636
- return doInstance.runFiber('flue:webhook:' + requestId, wrapped);
664
+ return doInstance.runFiber('flue:webhook:' + runId, wrapped);
637
665
  },
638
666
  runHandler: (ctx, h) => runWithInstanceContext(doInstance, () => {
639
667
  assertAgentsDurabilityApi(doInstance, 'keepAliveWhile');
@@ -642,6 +670,21 @@ async function dispatchAgent(request, doInstance, agentName, handler) {
642
670
  });
643
671
  }
644
672
 
673
+ // Positional parse so an instance id of "runs" is not treated as the marker.
674
+ function parseRunRoute(request) {
675
+ const segments = new URL(request.url).pathname.split('/').filter(Boolean);
676
+ if (segments.length < 4) return null;
677
+ if (segments[0] !== 'agents') return null;
678
+ if (segments[3] !== 'runs') return null;
679
+ const runId = segments[4];
680
+ const child = segments[5];
681
+ if (!runId) return null;
682
+ if (!child) return { action: 'get', runId };
683
+ if (child === 'events') return { action: 'events', runId };
684
+ if (child === 'stream') return { action: 'stream', runId };
685
+ return null;
686
+ }
687
+
645
688
  // ─── Per-Agent Durable Object Classes ──────────────────────────────────────
646
689
 
647
690
  ${agentClasses}
@@ -769,6 +812,8 @@ import { Bash, InMemoryFs } from 'just-bash';
769
812
  import {
770
813
  createFlueContext,
771
814
  InMemorySessionStore,
815
+ InMemoryRunStore,
816
+ createRunSubscriberRegistry,
772
817
  bashFactoryToSessionEnv,
773
818
  resolveModel,
774
819
  configureFlueRuntime,
@@ -827,10 +872,13 @@ async function createLocalEnv() {
827
872
 
828
873
  // Default persistence store for Node — in-memory, process lifetime.
829
874
  const defaultStore = new InMemorySessionStore();
875
+ const runStore = new InMemoryRunStore();
876
+ const runSubscribers = createRunSubscriberRegistry();
830
877
 
831
- function createContextForRequest(id, payload, req) {
878
+ function createContextForRequest(id, runId, payload, req) {
832
879
  return createFlueContext({
833
880
  id,
881
+ runId,
834
882
  payload,
835
883
  env: process.env,
836
884
  req,
@@ -857,6 +905,8 @@ configureFlueRuntime({
857
905
  allowNonWebhook: isLocalMode,
858
906
  handlers,
859
907
  createContext: createContextForRequest,
908
+ runStore,
909
+ runSubscribers,
860
910
  });
861
911
 
862
912
  // ─── App composition ────────────────────────────────────────────────────────
@@ -1,10 +1,47 @@
1
- import { A as SessionStore, D as SessionData, v as ModelConfig } from "./types-BAmV4f3Q.mjs";
1
+ import { A as SessionStore, D as SessionData, f as FlueEvent, v as ModelConfig } from "./types-Cdcq_ET2.mjs";
2
2
  import { FlueContextConfig, FlueContextInternal, createFlueContext } from "./client.mjs";
3
- import { a as AgentHandler, c as RunHandlerFn, l as StartWebhookFn, n as configureFlueRuntime, o as CreateContextFn, r as createDefaultFlueApp, s as HandleAgentOptions, t as FlueRuntime, u as handleAgentRequest } from "./flue-app-CG8i4wNG.mjs";
3
+ import { _ as RunStatus, a as AgentHandler, c as RunHandlerFn, d as RunSubscriberListener, f as RunSubscriberRegistry, g as RunRecord, h as EndRunInput, l as StartWebhookFn, m as CreateRunInput, n as configureFlueRuntime, o as CreateContextFn, p as createRunSubscriberRegistry, r as createDefaultFlueApp, s as HandleAgentOptions, t as FlueRuntime, u as handleAgentRequest, v as RunStore, y as RunStoreOptions } from "./flue-app-O4_iqLkn.mjs";
4
4
  import { bashFactoryToSessionEnv } from "./sandbox.mjs";
5
5
  import { Api, Model } from "@mariozechner/pi-ai";
6
6
  import { AgentMessage } from "@mariozechner/pi-agent-core";
7
7
 
8
+ //#region src/cloudflare/run-store.d.ts
9
+ interface SqlResult {
10
+ toArray(): SqlRow[];
11
+ }
12
+ type SqlRow = Record<string, unknown>;
13
+ interface SqlStorage {
14
+ exec(query: string, ...bindings: unknown[]): SqlResult;
15
+ }
16
+ declare function createDurableRunStore(sql: SqlStorage, options?: RunStoreOptions): RunStore;
17
+ //#endregion
18
+ //#region src/node/run-store.d.ts
19
+ declare class InMemoryRunStore implements RunStore {
20
+ private instances;
21
+ private maxCompletedRuns;
22
+ private maxEventBytes;
23
+ constructor(options?: RunStoreOptions);
24
+ createRun(input: CreateRunInput): Promise<void>;
25
+ endRun(input: EndRunInput): Promise<void>;
26
+ appendEvent(runId: string, event: FlueEvent): Promise<void>;
27
+ getEvents(runId: string, fromIndex?: number): Promise<FlueEvent[]>;
28
+ getRun(runId: string): Promise<RunRecord | null>;
29
+ private getInstance;
30
+ private pruneCompletedRuns;
31
+ }
32
+ //#endregion
33
+ //#region src/runtime/handle-run-routes.d.ts
34
+ interface HandleRunRouteOptions {
35
+ request: Request;
36
+ runStore?: RunStore;
37
+ runSubscribers?: RunSubscriberRegistry;
38
+ agentName: string;
39
+ id: string;
40
+ runId?: string;
41
+ action: 'get' | 'events' | 'stream';
42
+ }
43
+ declare function handleRunRouteRequest(opts: HandleRunRouteOptions): Promise<Response>;
44
+ //#endregion
8
45
  //#region src/session.d.ts
9
46
  /** In-memory session store. Sessions persist for the lifetime of the process. */
10
47
  declare class InMemorySessionStore implements SessionStore {
@@ -21,4 +58,4 @@ declare class InMemorySessionStore implements SessionStore {
21
58
  */
22
59
  declare function resolveModel(model: ModelConfig | undefined): Model<Api> | undefined;
23
60
  //#endregion
24
- export { type AgentHandler, type CreateContextFn, type FlueContextConfig, type FlueContextInternal, type FlueRuntime, type HandleAgentOptions, InMemorySessionStore, type RunHandlerFn, type StartWebhookFn, bashFactoryToSessionEnv, configureFlueRuntime, createDefaultFlueApp, createFlueContext, handleAgentRequest, resolveModel };
61
+ export { type AgentHandler, type CreateContextFn, type FlueContextConfig, type FlueContextInternal, type FlueRuntime, type HandleAgentOptions, type HandleRunRouteOptions, InMemoryRunStore, InMemorySessionStore, type RunHandlerFn, type RunRecord, type RunStatus, type RunStore, type RunSubscriberListener, type RunSubscriberRegistry, type StartWebhookFn, bashFactoryToSessionEnv, configureFlueRuntime, createDefaultFlueApp, createDurableRunStore, createFlueContext, createRunSubscriberRegistry, handleAgentRequest, handleRunRouteRequest, resolveModel };
package/dist/internal.mjs CHANGED
@@ -1,12 +1,291 @@
1
1
  import "./result-K1IRhWKM.mjs";
2
- import { i as handleAgentRequest, n as createDefaultFlueApp, t as configureFlueRuntime } from "./flue-app-DeTOZjPs.mjs";
3
- import { r as getProviderConfiguration, s as resolveRegisteredModel } from "./providers-DeFRIwp0.mjs";
4
- import { t as InMemorySessionStore } from "./session-CFOByKnM.mjs";
2
+ import { a as handleAgentRequest, i as handleRunRouteRequest, n as createDefaultFlueApp, t as configureFlueRuntime } from "./flue-app-SjL4I83Y.mjs";
3
+ import { r as getProviderConfiguration, s as resolveRegisteredModel } from "./providers-BjEEoKLy.mjs";
4
+ import { t as InMemorySessionStore } from "./session-CRFfAJDq.mjs";
5
5
  import { bashFactoryToSessionEnv } from "./sandbox.mjs";
6
- import "./mcp-DM6yv_Qc.mjs";
6
+ import "./mcp-DwLSoSxp.mjs";
7
7
  import { createFlueContext } from "./client.mjs";
8
8
  import { getModel } from "@mariozechner/pi-ai";
9
9
 
10
+ //#region src/runtime/run-store.ts
11
+ const DEFAULT_MAX_COMPLETED_RUNS = 50;
12
+ const DEFAULT_MAX_EVENT_BYTES = 256 * 1024;
13
+ const TRUNCATABLE_FIELDS = [
14
+ "result",
15
+ "args",
16
+ "text",
17
+ "content"
18
+ ];
19
+ const PREVIEW_CHARS = 1024;
20
+ const ENCODER = new TextEncoder();
21
+ function truncateEventForPersistence(event, maxBytes = DEFAULT_MAX_EVENT_BYTES) {
22
+ const candidate = cloneJson(event);
23
+ let serialized = JSON.stringify(candidate);
24
+ if (byteLength(serialized) <= maxBytes) return event;
25
+ const originalSerialized = serialized;
26
+ const originalSize = byteLength(originalSerialized);
27
+ const truncatedFields = /* @__PURE__ */ new Set();
28
+ while (byteLength(serialized) > maxBytes) {
29
+ let largestField;
30
+ let largestSize = 0;
31
+ for (const field of TRUNCATABLE_FIELDS) {
32
+ if (truncatedFields.has(field) || !(field in candidate)) continue;
33
+ const fieldSize = byteLength(JSON.stringify(candidate[field]));
34
+ if (fieldSize > largestSize) {
35
+ largestField = field;
36
+ largestSize = fieldSize;
37
+ }
38
+ }
39
+ if (!largestField) break;
40
+ candidate[largestField] = truncateValue(candidate[largestField], largestSize);
41
+ truncatedFields.add(largestField);
42
+ serialized = JSON.stringify(candidate);
43
+ }
44
+ if (byteLength(serialized) <= maxBytes) return candidate;
45
+ return {
46
+ ...pickEventIdentity(candidate),
47
+ truncated: true,
48
+ originalSize,
49
+ preview: originalSerialized.slice(0, PREVIEW_CHARS)
50
+ };
51
+ }
52
+ function truncateValue(value, originalSize) {
53
+ return {
54
+ truncated: true,
55
+ originalSize,
56
+ preview: JSON.stringify(value).slice(0, PREVIEW_CHARS)
57
+ };
58
+ }
59
+ function pickEventIdentity(event) {
60
+ const identity = {};
61
+ for (const key of [
62
+ "type",
63
+ "runId",
64
+ "eventIndex",
65
+ "timestamp",
66
+ "session",
67
+ "parentSession",
68
+ "harness",
69
+ "taskId",
70
+ "toolCallId",
71
+ "operationId"
72
+ ]) if (key in event) identity[key] = event[key];
73
+ return identity;
74
+ }
75
+ function cloneJson(value) {
76
+ return JSON.parse(JSON.stringify(value));
77
+ }
78
+ function byteLength(value) {
79
+ return ENCODER.encode(value).byteLength;
80
+ }
81
+
82
+ //#endregion
83
+ //#region src/cloudflare/run-store.ts
84
+ function createDurableRunStore(sql, options = {}) {
85
+ ensureRunTables(sql);
86
+ return new DurableRunStore(sql, options);
87
+ }
88
+ var DurableRunStore = class {
89
+ maxCompletedRuns;
90
+ maxEventBytes;
91
+ constructor(sql, options) {
92
+ this.sql = sql;
93
+ this.maxCompletedRuns = options.maxCompletedRuns ?? DEFAULT_MAX_COMPLETED_RUNS;
94
+ this.maxEventBytes = options.maxEventBytes ?? DEFAULT_MAX_EVENT_BYTES;
95
+ }
96
+ async createRun(input) {
97
+ this.sql.exec(`INSERT OR REPLACE INTO flue_runs
98
+ (run_id, instance_id, agent_name, status, started_at, ended_at, is_error, duration_ms, result, error)
99
+ VALUES (?, ?, ?, ?, ?, NULL, NULL, NULL, NULL, NULL)`, input.runId, input.instanceId, input.agentName, "active", input.startedAt);
100
+ }
101
+ async endRun(input) {
102
+ this.sql.exec(`UPDATE flue_runs
103
+ SET status = ?, ended_at = ?, is_error = ?, duration_ms = ?, result = ?, error = ?
104
+ WHERE run_id = ?`, input.isError ? "errored" : "completed", input.endedAt, input.isError ? 1 : 0, input.durationMs, JSON.stringify(input.result ?? null), JSON.stringify(input.error ?? null), input.runId);
105
+ this.pruneCompletedRuns();
106
+ }
107
+ async appendEvent(runId, event) {
108
+ const storedEvent = truncateEventForPersistence(event, this.maxEventBytes);
109
+ this.sql.exec(`INSERT OR REPLACE INTO flue_run_events
110
+ (run_id, event_index, type, payload, timestamp)
111
+ VALUES (?, ?, ?, ?, ?)`, runId, storedEvent.eventIndex ?? 0, storedEvent.type, JSON.stringify(storedEvent), storedEvent.timestamp ?? (/* @__PURE__ */ new Date()).toISOString());
112
+ }
113
+ async getEvents(runId, fromIndex) {
114
+ return this.sql.exec(fromIndex === void 0 ? "SELECT payload FROM flue_run_events WHERE run_id = ? ORDER BY event_index ASC" : "SELECT payload FROM flue_run_events WHERE run_id = ? AND event_index >= ? ORDER BY event_index ASC", ...fromIndex === void 0 ? [runId] : [runId, fromIndex]).toArray().flatMap((row) => typeof row.payload === "string" ? [JSON.parse(row.payload)] : []);
115
+ }
116
+ async getRun(runId) {
117
+ const row = this.sql.exec("SELECT * FROM flue_runs WHERE run_id = ?", runId).toArray()[0];
118
+ if (!row) return null;
119
+ return rowToRunRecord(row);
120
+ }
121
+ pruneCompletedRuns() {
122
+ const rows = this.sql.exec(`SELECT run_id FROM flue_runs
123
+ WHERE status != 'active'
124
+ ORDER BY started_at ASC`).toArray();
125
+ const deleteCount = rows.length - this.maxCompletedRuns;
126
+ if (deleteCount <= 0) return;
127
+ for (const row of rows.slice(0, deleteCount)) {
128
+ this.sql.exec("DELETE FROM flue_run_events WHERE run_id = ?", row.run_id);
129
+ this.sql.exec("DELETE FROM flue_runs WHERE run_id = ?", row.run_id);
130
+ }
131
+ }
132
+ };
133
+ function ensureRunTables(sql) {
134
+ sql.exec(`CREATE TABLE IF NOT EXISTS flue_runs (
135
+ run_id TEXT PRIMARY KEY,
136
+ instance_id TEXT NOT NULL,
137
+ agent_name TEXT NOT NULL,
138
+ status TEXT NOT NULL,
139
+ started_at TEXT NOT NULL,
140
+ ended_at TEXT,
141
+ is_error INTEGER,
142
+ duration_ms INTEGER,
143
+ result TEXT,
144
+ error TEXT
145
+ )`);
146
+ sql.exec(`CREATE TABLE IF NOT EXISTS flue_run_events (
147
+ run_id TEXT NOT NULL,
148
+ event_index INTEGER NOT NULL,
149
+ type TEXT NOT NULL,
150
+ payload TEXT NOT NULL,
151
+ timestamp TEXT NOT NULL,
152
+ PRIMARY KEY (run_id, event_index)
153
+ )`);
154
+ sql.exec("CREATE INDEX IF NOT EXISTS flue_runs_instance_started_idx ON flue_runs (instance_id, started_at DESC)");
155
+ sql.exec("CREATE INDEX IF NOT EXISTS flue_run_events_run_idx ON flue_run_events (run_id, event_index ASC)");
156
+ }
157
+ function rowToRunRecord(row) {
158
+ const result = typeof row.result === "string" ? JSON.parse(row.result) : void 0;
159
+ const error = typeof row.error === "string" ? JSON.parse(row.error) : void 0;
160
+ return {
161
+ runId: String(row.run_id),
162
+ instanceId: String(row.instance_id),
163
+ agentName: String(row.agent_name),
164
+ status: row.status,
165
+ startedAt: String(row.started_at),
166
+ endedAt: typeof row.ended_at === "string" ? row.ended_at : void 0,
167
+ isError: row.is_error === null || row.is_error === void 0 ? void 0 : Boolean(row.is_error),
168
+ durationMs: typeof row.duration_ms === "number" ? row.duration_ms : void 0,
169
+ result,
170
+ error
171
+ };
172
+ }
173
+
174
+ //#endregion
175
+ //#region src/node/run-store.ts
176
+ var InMemoryRunStore = class {
177
+ instances = /* @__PURE__ */ new Map();
178
+ maxCompletedRuns;
179
+ maxEventBytes;
180
+ constructor(options = {}) {
181
+ this.maxCompletedRuns = options.maxCompletedRuns ?? DEFAULT_MAX_COMPLETED_RUNS;
182
+ this.maxEventBytes = options.maxEventBytes ?? DEFAULT_MAX_EVENT_BYTES;
183
+ }
184
+ async createRun(input) {
185
+ const instance = this.getInstance(input.instanceId);
186
+ instance.runs.set(input.runId, {
187
+ runId: input.runId,
188
+ instanceId: input.instanceId,
189
+ agentName: input.agentName,
190
+ status: "active",
191
+ startedAt: input.startedAt
192
+ });
193
+ instance.events.set(input.runId, []);
194
+ }
195
+ async endRun(input) {
196
+ const existing = await this.getRun(input.runId);
197
+ if (!existing) return;
198
+ const instance = this.getInstance(existing.instanceId);
199
+ instance.runs.set(input.runId, {
200
+ ...existing,
201
+ status: input.isError ? "errored" : "completed",
202
+ endedAt: input.endedAt,
203
+ isError: input.isError,
204
+ durationMs: input.durationMs,
205
+ result: input.result,
206
+ error: input.error
207
+ });
208
+ this.pruneCompletedRuns(instance);
209
+ }
210
+ async appendEvent(runId, event) {
211
+ const run = await this.getRun(runId);
212
+ if (!run) return;
213
+ const instance = this.getInstance(run.instanceId);
214
+ const events = instance.events.get(runId) ?? [];
215
+ events.push(truncateEventForPersistence(event, this.maxEventBytes));
216
+ instance.events.set(runId, events);
217
+ }
218
+ async getEvents(runId, fromIndex) {
219
+ const run = await this.getRun(runId);
220
+ if (!run) return [];
221
+ const events = this.getInstance(run.instanceId).events.get(runId) ?? [];
222
+ if (fromIndex === void 0) return [...events];
223
+ return events.filter((event) => typeof event.eventIndex === "number" && event.eventIndex >= fromIndex);
224
+ }
225
+ async getRun(runId) {
226
+ for (const instance of this.instances.values()) {
227
+ const run = instance.runs.get(runId);
228
+ if (run) return run;
229
+ }
230
+ return null;
231
+ }
232
+ getInstance(instanceId) {
233
+ let instance = this.instances.get(instanceId);
234
+ if (!instance) {
235
+ instance = {
236
+ runs: /* @__PURE__ */ new Map(),
237
+ events: /* @__PURE__ */ new Map()
238
+ };
239
+ this.instances.set(instanceId, instance);
240
+ }
241
+ return instance;
242
+ }
243
+ pruneCompletedRuns(instance) {
244
+ const completed = [...instance.runs.values()].filter((run) => run.status !== "active").sort((a, b) => a.startedAt.localeCompare(b.startedAt));
245
+ const deleteCount = completed.length - this.maxCompletedRuns;
246
+ if (deleteCount <= 0) return;
247
+ for (const run of completed.slice(0, deleteCount)) {
248
+ instance.runs.delete(run.runId);
249
+ instance.events.delete(run.runId);
250
+ }
251
+ }
252
+ };
253
+
254
+ //#endregion
255
+ //#region src/runtime/run-subscribers.ts
256
+ function createRunSubscriberRegistry() {
257
+ const listeners = /* @__PURE__ */ new Map();
258
+ return {
259
+ subscribe(runId, listener) {
260
+ let bucket = listeners.get(runId);
261
+ if (!bucket) {
262
+ bucket = /* @__PURE__ */ new Set();
263
+ listeners.set(runId, bucket);
264
+ }
265
+ bucket.add(listener);
266
+ return () => {
267
+ const current = listeners.get(runId);
268
+ if (!current) return;
269
+ current.delete(listener);
270
+ if (current.size === 0) listeners.delete(runId);
271
+ };
272
+ },
273
+ publish(runId, event) {
274
+ const bucket = listeners.get(runId);
275
+ if (!bucket || bucket.size === 0) return;
276
+ for (const listener of [...bucket]) try {
277
+ listener(event);
278
+ } catch (error) {
279
+ console.error("[flue:run-subscribers] listener threw:", error);
280
+ }
281
+ },
282
+ complete(runId) {
283
+ listeners.delete(runId);
284
+ }
285
+ };
286
+ }
287
+
288
+ //#endregion
10
289
  //#region src/internal.ts
11
290
  /**
12
291
  * Internal runtime helpers consumed by the generated server entry point.
@@ -54,4 +333,4 @@ function applyProviderSettings(model, providerSettings) {
54
333
  }
55
334
 
56
335
  //#endregion
57
- export { InMemorySessionStore, bashFactoryToSessionEnv, configureFlueRuntime, createDefaultFlueApp, createFlueContext, handleAgentRequest, resolveModel };
336
+ export { InMemoryRunStore, InMemorySessionStore, bashFactoryToSessionEnv, configureFlueRuntime, createDefaultFlueApp, createDurableRunStore, createFlueContext, createRunSubscriberRegistry, handleAgentRequest, handleRunRouteRequest, resolveModel };
@@ -1,4 +1,4 @@
1
- import { L as ToolDef } from "./types-BAmV4f3Q.mjs";
1
+ import { L as ToolDef } from "./types-Cdcq_ET2.mjs";
2
2
 
3
3
  //#region src/mcp.d.ts
4
4
  type McpTransport = 'streamable-http' | 'sse';