@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/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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
622
|
-
|
|
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
|
-
|
|
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:' +
|
|
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 ────────────────────────────────────────────────────────
|
package/dist/internal.d.mts
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
|
-
import { A as SessionStore, D as SessionData, v as ModelConfig } from "./types-
|
|
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-
|
|
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 {
|
|
3
|
-
import { r as getProviderConfiguration, s as resolveRegisteredModel } from "./providers-
|
|
4
|
-
import { t as InMemorySessionStore } from "./session-
|
|
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-
|
|
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 };
|