@kuralle-syrinx/server-workers-mastra 4.4.1 → 4.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/durable-run-store.d.ts +19 -0
- package/dist/durable-run-store.d.ts.map +1 -0
- package/dist/durable-run-store.js +38 -0
- package/dist/durable-run-store.js.map +1 -0
- package/dist/mock-model.d.ts +37 -0
- package/dist/mock-model.d.ts.map +1 -0
- package/dist/mock-model.js +92 -0
- package/dist/mock-model.js.map +1 -0
- package/dist/worker.d.ts +14 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +193 -0
- package/dist/worker.js.map +1 -0
- package/package.json +14 -7
- package/src/worker.test.ts +149 -0
- package/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/.wrangler/state/v3/do/voice-server-workers-mastra-MastraAgentDO/35f1372a1ef6c9d67aa12e58d4a5515ab33180e2b6b7a63a9818e0eead57ffc1.sqlite +0 -0
- package/.wrangler/state/v3/do/voice-server-workers-mastra-MastraAgentDO/35f1372a1ef6c9d67aa12e58d4a5515ab33180e2b6b7a63a9818e0eead57ffc1.sqlite-shm +0 -0
- package/.wrangler/state/v3/do/voice-server-workers-mastra-MastraAgentDO/35f1372a1ef6c9d67aa12e58d4a5515ab33180e2b6b7a63a9818e0eead57ffc1.sqlite-wal +0 -0
- package/.wrangler/state/v3/do/voice-server-workers-mastra-MastraAgentDO/metadata.sqlite +0 -0
- package/.wrangler/state/v3/do/voice-server-workers-mastra-MastraAgentDO/metadata.sqlite-shm +0 -0
- package/.wrangler/state/v3/do/voice-server-workers-mastra-MastraAgentDO/metadata.sqlite-wal +0 -0
- package/wrangler.toml +0 -13
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RunPointer, RunStore } from "@kuralle-syrinx/aisdk";
|
|
2
|
+
type SqlCursor<T> = Iterable<T>;
|
|
3
|
+
interface SqlStorage {
|
|
4
|
+
exec(query: string, ...bindings: unknown[]): SqlCursor<Record<string, unknown>>;
|
|
5
|
+
}
|
|
6
|
+
export interface DurableRunStorage {
|
|
7
|
+
readonly sql: SqlStorage;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_RUN_POINTER_TTL_MS: number;
|
|
10
|
+
export declare class DurableObjectRunStore implements RunStore {
|
|
11
|
+
private readonly storage;
|
|
12
|
+
private readonly ttlMs;
|
|
13
|
+
constructor(storage: DurableRunStorage, ttlMs?: number);
|
|
14
|
+
save(contextId: string, runId: string): void;
|
|
15
|
+
takePending(contextId: string): RunPointer | null;
|
|
16
|
+
discard(contextId: string): void;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=durable-run-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-run-store.d.ts","sourceRoot":"","sources":["../src/durable-run-store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAElE,KAAK,SAAS,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEhC,UAAU,UAAU;IAClB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B;AAED,eAAO,MAAM,0BAA0B,QAAiB,CAAC;AAEzD,qBAAa,qBAAsB,YAAW,QAAQ;IAElD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,OAAO,EAAE,iBAAiB,EAC1B,KAAK,SAA6B;IAWrD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAS5C,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAcjD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGjC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// RunStore for the mastra HITL suspend/resume flow, backed by the Durable Object's
|
|
4
|
+
// SQLite (the storage Mastra's own CloudflareDOStorage uses). Stale run pointers
|
|
5
|
+
// expire lazily on read — no scheduled-alarm cleanup needed, so the DO does not
|
|
6
|
+
// re-implement a task scheduler.
|
|
7
|
+
export const DEFAULT_RUN_POINTER_TTL_MS = 15 * 60 * 1000;
|
|
8
|
+
export class DurableObjectRunStore {
|
|
9
|
+
storage;
|
|
10
|
+
ttlMs;
|
|
11
|
+
constructor(storage, ttlMs = DEFAULT_RUN_POINTER_TTL_MS) {
|
|
12
|
+
this.storage = storage;
|
|
13
|
+
this.ttlMs = ttlMs;
|
|
14
|
+
this.storage.sql.exec(`CREATE TABLE IF NOT EXISTS reasoning_run_pointers (
|
|
15
|
+
context_id TEXT PRIMARY KEY,
|
|
16
|
+
run_id TEXT NOT NULL,
|
|
17
|
+
created_at_ms INTEGER NOT NULL
|
|
18
|
+
)`);
|
|
19
|
+
}
|
|
20
|
+
save(contextId, runId) {
|
|
21
|
+
this.storage.sql.exec("INSERT OR REPLACE INTO reasoning_run_pointers (context_id, run_id, created_at_ms) VALUES (?, ?, ?)", contextId, runId, Date.now());
|
|
22
|
+
}
|
|
23
|
+
takePending(contextId) {
|
|
24
|
+
const [row] = [...this.storage.sql.exec("SELECT run_id, created_at_ms FROM reasoning_run_pointers WHERE context_id = ?", contextId)];
|
|
25
|
+
if (!row)
|
|
26
|
+
return null;
|
|
27
|
+
// Lazy TTL: a pointer past its window is treated as absent and swept on read.
|
|
28
|
+
if (Date.now() - Number(row.created_at_ms) > this.ttlMs) {
|
|
29
|
+
this.discard(contextId);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return { runId: row.run_id };
|
|
33
|
+
}
|
|
34
|
+
discard(contextId) {
|
|
35
|
+
this.storage.sql.exec("DELETE FROM reasoning_run_pointers WHERE context_id = ?", contextId);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=durable-run-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-run-store.js","sourceRoot":"","sources":["../src/durable-run-store.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,mFAAmF;AACnF,iFAAiF;AACjF,gFAAgF;AAChF,iCAAiC;AAcjC,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzD,MAAM,OAAO,qBAAqB;IAEb;IACA;IAFnB,YACmB,OAA0B,EAC1B,QAAQ,0BAA0B;QADlC,YAAO,GAAP,OAAO,CAAmB;QAC1B,UAAK,GAAL,KAAK,CAA6B;QAEnD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CACnB;;;;QAIE,CACH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,SAAiB,EAAE,KAAa;QACnC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CACnB,oGAAoG,EACpG,SAAS,EACT,KAAK,EACL,IAAI,CAAC,GAAG,EAAE,CACX,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,SAAiB;QAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CACrC,+EAA+E,EAC/E,SAAS,CACV,CAAqD,CAAC;QACvD,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,8EAA8E;QAC9E,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO,CAAC,SAAiB;QACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,yDAAyD,EAAE,SAAS,CAAC,CAAC;IAC9F,CAAC;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type SqlStorage = {
|
|
2
|
+
exec(query: string, ...bindings: unknown[]): Iterable<Record<string, unknown>>;
|
|
3
|
+
};
|
|
4
|
+
export declare function createSpikeMockModel(sql: SqlStorage, key?: string): {
|
|
5
|
+
specificationVersion: "v2";
|
|
6
|
+
provider: string;
|
|
7
|
+
modelId: string;
|
|
8
|
+
supportedUrls: {};
|
|
9
|
+
doStream: () => Promise<{
|
|
10
|
+
stream: ReadableStream<unknown>;
|
|
11
|
+
rawCall: {
|
|
12
|
+
rawPrompt: null;
|
|
13
|
+
rawSettings: {};
|
|
14
|
+
};
|
|
15
|
+
warnings: never[];
|
|
16
|
+
}>;
|
|
17
|
+
doGenerate: () => Promise<{
|
|
18
|
+
content: {
|
|
19
|
+
type: string;
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
finishReason: "stop";
|
|
23
|
+
usage: {
|
|
24
|
+
inputTokens: number;
|
|
25
|
+
outputTokens: number;
|
|
26
|
+
totalTokens: number;
|
|
27
|
+
};
|
|
28
|
+
rawCall: {
|
|
29
|
+
rawPrompt: null;
|
|
30
|
+
rawSettings: {};
|
|
31
|
+
};
|
|
32
|
+
warnings: never[];
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
export declare function resetMockModelCalls(sql: SqlStorage, key?: string): void;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=mock-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-model.d.ts","sourceRoot":"","sources":["../src/mock-model.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAChF,CAAC;AAkEF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,SAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuB3E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,SAAmB,GAAG,IAAI,CAQjF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
function nextModelCall(sql, key) {
|
|
3
|
+
sql.exec(`CREATE TABLE IF NOT EXISTS spike_mock_calls (
|
|
4
|
+
key TEXT PRIMARY KEY,
|
|
5
|
+
count INTEGER NOT NULL
|
|
6
|
+
)`);
|
|
7
|
+
const [row] = [...sql.exec("SELECT count FROM spike_mock_calls WHERE key = ?", key)];
|
|
8
|
+
const current = typeof row?.count === "number" ? row.count : 0;
|
|
9
|
+
const next = current + 1;
|
|
10
|
+
sql.exec("INSERT OR REPLACE INTO spike_mock_calls (key, count) VALUES (?, ?)", key, next);
|
|
11
|
+
return next;
|
|
12
|
+
}
|
|
13
|
+
function toolCallStream() {
|
|
14
|
+
return new ReadableStream({
|
|
15
|
+
start(controller) {
|
|
16
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
17
|
+
controller.enqueue({
|
|
18
|
+
type: "response-metadata",
|
|
19
|
+
id: "id-0",
|
|
20
|
+
modelId: "mock",
|
|
21
|
+
timestamp: new Date(0),
|
|
22
|
+
});
|
|
23
|
+
controller.enqueue({
|
|
24
|
+
type: "tool-call",
|
|
25
|
+
toolCallId: "call-1",
|
|
26
|
+
toolName: "confirmAction",
|
|
27
|
+
input: '{"action":"deploy"}',
|
|
28
|
+
providerExecuted: false,
|
|
29
|
+
});
|
|
30
|
+
controller.enqueue({
|
|
31
|
+
type: "finish",
|
|
32
|
+
finishReason: "tool-calls",
|
|
33
|
+
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
|
34
|
+
});
|
|
35
|
+
controller.close();
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function textStream() {
|
|
40
|
+
return new ReadableStream({
|
|
41
|
+
start(controller) {
|
|
42
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
43
|
+
controller.enqueue({
|
|
44
|
+
type: "response-metadata",
|
|
45
|
+
id: "id-1",
|
|
46
|
+
modelId: "mock",
|
|
47
|
+
timestamp: new Date(0),
|
|
48
|
+
});
|
|
49
|
+
controller.enqueue({ type: "text-start", id: "text-1" });
|
|
50
|
+
controller.enqueue({ type: "text-delta", id: "text-1", delta: "Deployed successfully." });
|
|
51
|
+
controller.enqueue({ type: "text-end", id: "text-1" });
|
|
52
|
+
controller.enqueue({
|
|
53
|
+
type: "finish",
|
|
54
|
+
finishReason: "stop",
|
|
55
|
+
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
|
56
|
+
});
|
|
57
|
+
controller.close();
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function createSpikeMockModel(sql, key = "mastra-session") {
|
|
62
|
+
return {
|
|
63
|
+
specificationVersion: "v2",
|
|
64
|
+
provider: "spike-mock",
|
|
65
|
+
modelId: "spike-mock-v1",
|
|
66
|
+
supportedUrls: {},
|
|
67
|
+
doStream: async () => {
|
|
68
|
+
const callCount = nextModelCall(sql, key);
|
|
69
|
+
const stream = callCount === 1 ? toolCallStream() : textStream();
|
|
70
|
+
return {
|
|
71
|
+
stream,
|
|
72
|
+
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
73
|
+
warnings: [],
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
doGenerate: async () => ({
|
|
77
|
+
content: [{ type: "text", text: "mock" }],
|
|
78
|
+
finishReason: "stop",
|
|
79
|
+
usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
|
|
80
|
+
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
81
|
+
warnings: [],
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function resetMockModelCalls(sql, key = "mastra-session") {
|
|
86
|
+
sql.exec(`CREATE TABLE IF NOT EXISTS spike_mock_calls (
|
|
87
|
+
key TEXT PRIMARY KEY,
|
|
88
|
+
count INTEGER NOT NULL
|
|
89
|
+
)`);
|
|
90
|
+
sql.exec("DELETE FROM spike_mock_calls WHERE key = ?", key);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=mock-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-model.js","sourceRoot":"","sources":["../src/mock-model.ts"],"names":[],"mappings":"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"}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DurableObject } from "cloudflare:workers";
|
|
2
|
+
export interface Env {
|
|
3
|
+
MASTRA_AGENT: DurableObjectNamespace;
|
|
4
|
+
/** When set, the agent uses a real OpenAI model (gpt-4.1-mini); else a deterministic stub (tests). */
|
|
5
|
+
OPENAI_API_KEY?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class MastraAgentDO extends DurableObject<Env> {
|
|
8
|
+
fetch(request: Request): Promise<Response>;
|
|
9
|
+
}
|
|
10
|
+
declare const _default: {
|
|
11
|
+
fetch(request: Request, env: Env): Promise<Response>;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
14
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAcnD,MAAM,WAAW,GAAG;IAClB,YAAY,EAAE,sBAAsB,CAAC;IACrC,sGAAsG;IACtG,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AA6JD,qBAAa,aAAc,SAAQ,aAAa,CAAC,GAAG,CAAC;IAC7C,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAiDjD;;mBAGsB,OAAO,OAAO,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;;AAD5D,wBAUE"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { DurableObject } from "cloudflare:workers";
|
|
3
|
+
import { CloudflareDOStorage } from "@mastra/cloudflare/do";
|
|
4
|
+
import { Agent } from "@mastra/core/agent";
|
|
5
|
+
import { Mastra } from "@mastra/core";
|
|
6
|
+
import { createTool } from "@mastra/core/tools";
|
|
7
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { PipelineBusImpl, Route } from "@kuralle-syrinx/core";
|
|
10
|
+
import { ReasoningBridge } from "@kuralle-syrinx/aisdk";
|
|
11
|
+
import { fromMastraAgent } from "@kuralle-syrinx/mastra";
|
|
12
|
+
import { DurableObjectRunStore } from "./durable-run-store.js";
|
|
13
|
+
import { createSpikeMockModel, resetMockModelCalls } from "./mock-model.js";
|
|
14
|
+
const DEFAULT_CONTEXT_ID = "mastra-session";
|
|
15
|
+
function isConfirmed(resumeData) {
|
|
16
|
+
if (typeof resumeData === "object" && resumeData !== null && "confirmed" in resumeData) {
|
|
17
|
+
return resumeData.confirmed === true;
|
|
18
|
+
}
|
|
19
|
+
if (typeof resumeData === "string") {
|
|
20
|
+
const normalized = resumeData.trim().toLowerCase();
|
|
21
|
+
return normalized === "yes" || normalized === "confirm" || normalized === "confirmed";
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const confirmTool = createTool({
|
|
26
|
+
id: "confirm-action",
|
|
27
|
+
description: "Confirms an action with the user",
|
|
28
|
+
inputSchema: z.object({ action: z.string() }),
|
|
29
|
+
suspendSchema: z.object({
|
|
30
|
+
action: z.string(),
|
|
31
|
+
reason: z.string().optional(),
|
|
32
|
+
}),
|
|
33
|
+
resumeSchema: z.object({
|
|
34
|
+
confirmed: z.boolean(),
|
|
35
|
+
}),
|
|
36
|
+
execute: async (input, context) => {
|
|
37
|
+
const ctx = context;
|
|
38
|
+
const resumeData = ctx?.resumeData ?? ctx?.agent?.resumeData ?? ctx?.workflow?.resumeData;
|
|
39
|
+
if (isConfirmed(resumeData)) {
|
|
40
|
+
return { result: `Action "${input.action}" confirmed`, resumed: true };
|
|
41
|
+
}
|
|
42
|
+
const suspend = ctx?.suspend ?? ctx?.agent?.suspend ?? ctx?.workflow?.suspend;
|
|
43
|
+
if (!suspend)
|
|
44
|
+
throw new Error("suspend not available in tool context");
|
|
45
|
+
return await suspend({ action: input.action, reason: "Needs user confirmation" });
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
function toMastraAgentLike(agent) {
|
|
49
|
+
return {
|
|
50
|
+
stream: (messages, options) => agent.stream(messages, {
|
|
51
|
+
abortSignal: options?.abortSignal,
|
|
52
|
+
requireToolApproval: false,
|
|
53
|
+
autoResumeSuspendedTools: false,
|
|
54
|
+
}),
|
|
55
|
+
resumeStream: (resumeData, options) => agent.resumeStream(resumeData, {
|
|
56
|
+
runId: options.runId,
|
|
57
|
+
toolCallId: options.toolCallId,
|
|
58
|
+
abortSignal: options.abortSignal,
|
|
59
|
+
requireToolApproval: false,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function createMastra(sql, apiKey) {
|
|
64
|
+
const storage = new CloudflareDOStorage({ sql });
|
|
65
|
+
// Real OpenAI model when a key is provided (live deploy); deterministic stub otherwise (tests).
|
|
66
|
+
const model = apiKey
|
|
67
|
+
? createOpenAI({ apiKey })("gpt-4.1-mini")
|
|
68
|
+
: createSpikeMockModel(sql);
|
|
69
|
+
const agent = new Agent({
|
|
70
|
+
id: "support",
|
|
71
|
+
name: "Support Agent",
|
|
72
|
+
instructions: "You confirm potentially destructive actions with the user before doing them. " +
|
|
73
|
+
"When asked to deploy, call the confirmAction tool to get confirmation first.",
|
|
74
|
+
model,
|
|
75
|
+
tools: { confirmAction: confirmTool },
|
|
76
|
+
});
|
|
77
|
+
const mastra = new Mastra({
|
|
78
|
+
agents: { support: agent },
|
|
79
|
+
storage,
|
|
80
|
+
logger: false,
|
|
81
|
+
});
|
|
82
|
+
return { mastra, agent };
|
|
83
|
+
}
|
|
84
|
+
function turnComplete(contextId, text) {
|
|
85
|
+
return {
|
|
86
|
+
kind: "eos.turn_complete",
|
|
87
|
+
contextId,
|
|
88
|
+
timestampMs: Date.now(),
|
|
89
|
+
text,
|
|
90
|
+
transcripts: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function listMastraTables(sql) {
|
|
94
|
+
const rows = [...sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")];
|
|
95
|
+
return rows.map((row) => row.name);
|
|
96
|
+
}
|
|
97
|
+
function readPointer(sql, contextId) {
|
|
98
|
+
const [row] = [...sql.exec("SELECT run_id FROM reasoning_run_pointers WHERE context_id = ?", contextId)];
|
|
99
|
+
return row ? { runId: row.run_id } : null;
|
|
100
|
+
}
|
|
101
|
+
async function driveTurn(storage, contextId, userText, apiKey) {
|
|
102
|
+
const runStore = new DurableObjectRunStore(storage);
|
|
103
|
+
const { agent } = createMastra(storage.sql, apiKey);
|
|
104
|
+
const bridge = new ReasoningBridge(fromMastraAgent(toMastraAgentLike(agent)), {
|
|
105
|
+
runStore,
|
|
106
|
+
onResumeConflict: "restart",
|
|
107
|
+
});
|
|
108
|
+
const packets = [];
|
|
109
|
+
const bus = new PipelineBusImpl({
|
|
110
|
+
onPacket: (route, packet) => {
|
|
111
|
+
packets.push({ route, packet: packet });
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const drain = bus.start();
|
|
115
|
+
await bridge.initialize(bus, { timeout_ms: 30_000, max_history_turns: 12 });
|
|
116
|
+
bus.push(Route.Main, turnComplete(contextId, userText));
|
|
117
|
+
const deadline = Date.now() + 30_000;
|
|
118
|
+
while (Date.now() < deadline) {
|
|
119
|
+
const done = packets.some(({ packet }) => packet["kind"] === "llm.done" || packet["kind"] === "reasoning.suspended");
|
|
120
|
+
if (done)
|
|
121
|
+
break;
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
123
|
+
}
|
|
124
|
+
bus.stop();
|
|
125
|
+
await drain;
|
|
126
|
+
await bridge.close();
|
|
127
|
+
return {
|
|
128
|
+
packets,
|
|
129
|
+
pointer: readPointer(storage.sql, contextId),
|
|
130
|
+
mastraTables: listMastraTables(storage.sql),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export class MastraAgentDO extends DurableObject {
|
|
134
|
+
async fetch(request) {
|
|
135
|
+
const url = new URL(request.url);
|
|
136
|
+
const contextId = url.searchParams.get("contextId") ?? DEFAULT_CONTEXT_ID;
|
|
137
|
+
try {
|
|
138
|
+
if (url.pathname === "/health") {
|
|
139
|
+
return new Response("ok");
|
|
140
|
+
}
|
|
141
|
+
if (url.pathname === "/suspend") {
|
|
142
|
+
if (!this.env.OPENAI_API_KEY)
|
|
143
|
+
resetMockModelCalls(this.ctx.storage.sql, contextId);
|
|
144
|
+
const result = await driveTurn(this.ctx.storage, contextId, "Deploy to production", this.env.OPENAI_API_KEY);
|
|
145
|
+
const suspended = result.packets.find(({ packet }) => packet["kind"] === "reasoning.suspended");
|
|
146
|
+
const llmDone = result.packets.find(({ packet }) => packet["kind"] === "llm.done");
|
|
147
|
+
return Response.json({
|
|
148
|
+
phase: "suspend",
|
|
149
|
+
contextId,
|
|
150
|
+
suspended: suspended !== undefined,
|
|
151
|
+
runId: suspended?.packet["runId"] ?? null,
|
|
152
|
+
prompt: suspended?.packet["prompt"] ?? llmDone?.packet["text"] ?? null,
|
|
153
|
+
pointer: result.pointer,
|
|
154
|
+
mastraTables: result.mastraTables,
|
|
155
|
+
packetKinds: result.packets.map(({ packet }) => packet["kind"]),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (url.pathname === "/resume") {
|
|
159
|
+
const body = await request.json();
|
|
160
|
+
const userText = body.userText ?? "yes";
|
|
161
|
+
const result = await driveTurn(this.ctx.storage, contextId, userText, this.env.OPENAI_API_KEY);
|
|
162
|
+
const llmDone = result.packets.find(({ packet }) => packet["kind"] === "llm.done");
|
|
163
|
+
const suspended = result.packets.find(({ packet }) => packet["kind"] === "reasoning.suspended");
|
|
164
|
+
return Response.json({
|
|
165
|
+
phase: "resume",
|
|
166
|
+
contextId,
|
|
167
|
+
suspended: suspended !== undefined,
|
|
168
|
+
text: llmDone?.packet["text"] ?? "",
|
|
169
|
+
pointer: result.pointer,
|
|
170
|
+
mastraTables: result.mastraTables,
|
|
171
|
+
packetKinds: result.packets.map(({ packet }) => packet["kind"]),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return new Response("not found", { status: 404 });
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}\n${error.stack ?? ""}` : String(error);
|
|
178
|
+
return Response.json({ error: message }, { status: 500 });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export default {
|
|
183
|
+
async fetch(request, env) {
|
|
184
|
+
const url = new URL(request.url);
|
|
185
|
+
if (url.pathname === "/health") {
|
|
186
|
+
return new Response("ok");
|
|
187
|
+
}
|
|
188
|
+
const id = env.MASTRA_AGENT.idFromName("mastra-session");
|
|
189
|
+
const stub = env.MASTRA_AGENT.get(id);
|
|
190
|
+
return stub.fetch(request);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kuralle-syrinx/server-workers-mastra",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Cloudflare Workers voice host template wiring Syrinx to a Mastra reasoner",
|
|
6
6
|
"keywords": [
|
|
@@ -22,28 +22,35 @@
|
|
|
22
22
|
"url": "https://github.com/kuralle/syrinx/issues"
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|
|
25
|
-
"main": "./
|
|
26
|
-
"types": "./
|
|
25
|
+
"main": "./dist/worker.js",
|
|
26
|
+
"types": "./dist/worker.d.ts",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@ai-sdk/openai": "^3.0.67",
|
|
29
29
|
"@mastra/cloudflare": "^1.4.1",
|
|
30
30
|
"@mastra/core": "^1.41.0",
|
|
31
31
|
"zod": "^4.1.8",
|
|
32
|
-
"@kuralle-syrinx/core": "4.
|
|
33
|
-
"@kuralle-syrinx/
|
|
34
|
-
"@kuralle-syrinx/
|
|
32
|
+
"@kuralle-syrinx/core": "4.5.0",
|
|
33
|
+
"@kuralle-syrinx/aisdk": "4.5.0",
|
|
34
|
+
"@kuralle-syrinx/mastra": "4.5.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@cloudflare/workers-types": "^4.20260601.0",
|
|
38
|
+
"@types/node": "^22.0.0",
|
|
38
39
|
"miniflare": "^4.20260601.0",
|
|
39
40
|
"typescript": "^5.7.0",
|
|
40
41
|
"vitest": "^3.2.6",
|
|
41
42
|
"wrangler": "^4.19.0"
|
|
42
43
|
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"src",
|
|
47
|
+
"README.md"
|
|
48
|
+
],
|
|
43
49
|
"scripts": {
|
|
44
50
|
"typecheck": "tsc --noEmit",
|
|
45
51
|
"test": "vitest run",
|
|
46
52
|
"test:edge": "SYRINX_MASTRA_EDGE_TEST=1 vitest run",
|
|
47
|
-
"wrangler:dry-run": "wrangler deploy --dry-run"
|
|
53
|
+
"wrangler:dry-run": "wrangler deploy --dry-run",
|
|
54
|
+
"build": "tsc -p tsconfig.build.json"
|
|
48
55
|
}
|
|
49
56
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { mkdtemp, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
// Import Node's URL explicitly: this file's tsconfig loads @cloudflare/workers-types,
|
|
8
|
+
// whose global URL (as of 4.20260603) dropped the Node-style `new URL(path, base)` overload.
|
|
9
|
+
// This is a Node-side test (spawns wrangler `unstable_dev`), so it uses Node's URL.
|
|
10
|
+
import { fileURLToPath, URL } from "node:url";
|
|
11
|
+
import { execFile } from "node:child_process";
|
|
12
|
+
import { promisify } from "node:util";
|
|
13
|
+
import { unstable_dev, type Unstable_DevWorker } from "wrangler";
|
|
14
|
+
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
15
|
+
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
const PACKAGE_ROOT = fileURLToPath(new URL("../", import.meta.url));
|
|
18
|
+
const mastraEdgeEnabled = process.env["SYRINX_MASTRA_EDGE_TEST"] === "1";
|
|
19
|
+
|
|
20
|
+
type SuspendBody = {
|
|
21
|
+
phase: string;
|
|
22
|
+
contextId: string;
|
|
23
|
+
runId: string | null;
|
|
24
|
+
suspended: boolean;
|
|
25
|
+
prompt: string | null;
|
|
26
|
+
pointer: { runId: string } | null;
|
|
27
|
+
mastraTables: string[];
|
|
28
|
+
packetKinds: string[];
|
|
29
|
+
error?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type ResumeBody = {
|
|
33
|
+
phase: string;
|
|
34
|
+
contextId: string;
|
|
35
|
+
suspended: boolean;
|
|
36
|
+
text: string;
|
|
37
|
+
pointer: { runId: string } | null;
|
|
38
|
+
mastraTables: string[];
|
|
39
|
+
packetKinds: string[];
|
|
40
|
+
error?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const tempDirs: string[] = [];
|
|
44
|
+
let bundleOutdir = "";
|
|
45
|
+
let bundleSizeKb = 0;
|
|
46
|
+
let bundleLog = "";
|
|
47
|
+
|
|
48
|
+
afterEach(async () => {
|
|
49
|
+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("voice-server-workers-mastra default suite", () => {
|
|
53
|
+
it("gates workerd two-turn test behind SYRINX_MASTRA_EDGE_TEST", () => {
|
|
54
|
+
expect(mastraEdgeEnabled || process.env["SYRINX_MASTRA_EDGE_TEST"] !== "1").toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe.skipIf(!mastraEdgeEnabled)("mastra edge suspend/resume", () => {
|
|
59
|
+
beforeAll(async () => {
|
|
60
|
+
const outdir = await mkdtemp(join(tmpdir(), "mastra-edge-bundle-"));
|
|
61
|
+
const wranglerBin = join(PACKAGE_ROOT, "node_modules/.bin/wrangler");
|
|
62
|
+
const { stdout, stderr } = await execFileAsync(
|
|
63
|
+
wranglerBin,
|
|
64
|
+
["deploy", "--dry-run", "--outdir", outdir],
|
|
65
|
+
{ cwd: PACKAGE_ROOT, env: process.env },
|
|
66
|
+
);
|
|
67
|
+
bundleLog = `${stdout}\n${stderr}`;
|
|
68
|
+
const workerPath = join(outdir, "worker.js");
|
|
69
|
+
if (!existsSync(workerPath)) {
|
|
70
|
+
throw new Error(`wrangler bundle missing worker.js\n${bundleLog}`);
|
|
71
|
+
}
|
|
72
|
+
bundleOutdir = outdir;
|
|
73
|
+
const bundleStat = await stat(workerPath);
|
|
74
|
+
bundleSizeKb = Math.round(bundleStat.size / 1024);
|
|
75
|
+
}, 120_000);
|
|
76
|
+
|
|
77
|
+
async function startDevWorker(): Promise<Unstable_DevWorker> {
|
|
78
|
+
return unstable_dev(join(PACKAGE_ROOT, "src/worker.ts"), {
|
|
79
|
+
config: join(PACKAGE_ROOT, "wrangler.toml"),
|
|
80
|
+
local: true,
|
|
81
|
+
compatibilityDate: "2026-06-01",
|
|
82
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
83
|
+
experimental: { disableExperimentalWarning: true },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
it("bundles @mastra/core + @mastra/cloudflare via wrangler (nodejs_compat)", () => {
|
|
88
|
+
expect(bundleOutdir).toBeTruthy();
|
|
89
|
+
expect(bundleSizeKb).toBeGreaterThan(0);
|
|
90
|
+
expect(bundleLog).not.toMatch(/Could not resolve|unresolved|ERROR/i);
|
|
91
|
+
}, 10_000);
|
|
92
|
+
|
|
93
|
+
it("suspend → fresh DO/same SQL → resume via ReasoningBridge + pointer RunStore", async () => {
|
|
94
|
+
const worker = await startDevWorker();
|
|
95
|
+
const fsHits: string[] = [];
|
|
96
|
+
const originalConsoleError = console.error;
|
|
97
|
+
console.error = (...args: unknown[]) => {
|
|
98
|
+
const line = args.map(String).join(" ");
|
|
99
|
+
if (/ENOENT|readFileSync|existsSync|fs\./i.test(line)) fsHits.push(line);
|
|
100
|
+
originalConsoleError(...args);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const health = await worker.fetch("http://localhost/health");
|
|
105
|
+
expect(health.status).toBe(200);
|
|
106
|
+
expect(await health.text()).toBe("ok");
|
|
107
|
+
|
|
108
|
+
const suspendRes = await worker.fetch("http://localhost/suspend");
|
|
109
|
+
const suspendBody = await suspendRes.json() as SuspendBody | { error: string };
|
|
110
|
+
if ("error" in suspendBody) {
|
|
111
|
+
throw new Error(`suspend failed: ${suspendBody.error}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expect(suspendRes.status).toBe(200);
|
|
115
|
+
expect(suspendBody.phase).toBe("suspend");
|
|
116
|
+
expect(suspendBody.suspended).toBe(true);
|
|
117
|
+
expect(suspendBody.runId).toBeTruthy();
|
|
118
|
+
expect(suspendBody.pointer).toEqual({ runId: suspendBody.runId });
|
|
119
|
+
expect(suspendBody.packetKinds).toContain("reasoning.suspended");
|
|
120
|
+
expect(suspendBody.packetKinds).toContain("llm.done");
|
|
121
|
+
expect(suspendBody.mastraTables.some((name) => name.includes("mastra") || name.includes("workflow"))).toBe(true);
|
|
122
|
+
|
|
123
|
+
const resumeRes = await worker.fetch("http://localhost/resume", {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: { "content-type": "application/json" },
|
|
126
|
+
body: JSON.stringify({ userText: "yes" }),
|
|
127
|
+
});
|
|
128
|
+
const resumeBody = await resumeRes.json() as ResumeBody | { error: string };
|
|
129
|
+
if ("error" in resumeBody) {
|
|
130
|
+
throw new Error(`resume failed: ${resumeBody.error}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
expect(resumeRes.status).toBe(200);
|
|
134
|
+
expect(resumeBody.phase).toBe("resume");
|
|
135
|
+
expect(resumeBody.suspended).toBe(false);
|
|
136
|
+
expect(resumeBody.text).toContain("Deployed successfully");
|
|
137
|
+
expect(resumeBody.packetKinds).toContain("llm.done");
|
|
138
|
+
expect(resumeBody.pointer).toBeNull();
|
|
139
|
+
|
|
140
|
+
if (fsHits.length > 0) {
|
|
141
|
+
// eslint-disable-next-line no-console
|
|
142
|
+
console.log("[fs hits]", fsHits);
|
|
143
|
+
}
|
|
144
|
+
} finally {
|
|
145
|
+
console.error = originalConsoleError;
|
|
146
|
+
await worker.stop();
|
|
147
|
+
}
|
|
148
|
+
}, 120_000);
|
|
149
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/wrangler.toml
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
name = "voice-server-workers-mastra"
|
|
2
|
-
main = "src/worker.ts"
|
|
3
|
-
compatibility_date = "2026-06-01"
|
|
4
|
-
compatibility_flags = ["nodejs_compat"]
|
|
5
|
-
|
|
6
|
-
[durable_objects]
|
|
7
|
-
bindings = [
|
|
8
|
-
{ name = "MASTRA_AGENT", class_name = "MastraAgentDO" }
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
[[migrations]]
|
|
12
|
-
tag = "v1"
|
|
13
|
-
new_sqlite_classes = ["MastraAgentDO"]
|