@kuralle-agents/cf-agent 0.7.2 → 0.8.5
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/BridgeSessionStore.js +7 -1
- package/dist/KuralleAgent.d.ts +55 -2
- package/dist/KuralleAgent.js +145 -32
- package/dist/cfMessageInput.d.ts +11 -0
- package/dist/cfMessageInput.js +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +8 -0
- package/package.json +8 -8
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DURABLE_RUNS_KEY } from '@kuralle-agents/core';
|
|
1
2
|
import { getToolName, isToolUIPart } from 'ai';
|
|
2
3
|
import { OrchestrationStore } from './OrchestrationStore.js';
|
|
3
4
|
/**
|
|
@@ -32,7 +33,7 @@ export class BridgeSessionStore {
|
|
|
32
33
|
const orchState = await this.orchestration.get(key);
|
|
33
34
|
// Convert CF UIMessages to Kuralle ModelMessages
|
|
34
35
|
const messages = convertUIMessagesToModelMessages(this.cfMessages);
|
|
35
|
-
|
|
36
|
+
const session = {
|
|
36
37
|
id: key,
|
|
37
38
|
conversationId: key,
|
|
38
39
|
channelId: 'web',
|
|
@@ -48,6 +49,10 @@ export class BridgeSessionStore {
|
|
|
48
49
|
})),
|
|
49
50
|
state: orchState?.state,
|
|
50
51
|
};
|
|
52
|
+
// Restore the durable run journal so durable tools / suspend-resume can find
|
|
53
|
+
// the run (SessionRunStore reads it off the Session object).
|
|
54
|
+
session[DURABLE_RUNS_KEY] = orchState?.durableRuns ?? {};
|
|
55
|
+
return session;
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
53
58
|
* Save only orchestration state. CF handles message persistence.
|
|
@@ -66,6 +71,7 @@ export class BridgeSessionStore {
|
|
|
66
71
|
: String(h.timestamp),
|
|
67
72
|
})),
|
|
68
73
|
state: session.state,
|
|
74
|
+
durableRuns: session[DURABLE_RUNS_KEY],
|
|
69
75
|
};
|
|
70
76
|
await this.orchestration.save(session.id, state);
|
|
71
77
|
}
|
package/dist/KuralleAgent.d.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { AIChatAgent } from '@cloudflare/ai-chat';
|
|
22
22
|
import { type HarnessConfig } from '@kuralle-agents/core';
|
|
23
|
-
import type { PersistentMemoryStore } from '@kuralle-agents/core';
|
|
23
|
+
import type { PersistentMemoryStore, SignalDelivery, ScheduledJob, Scheduler, WakeJobPayload } from '@kuralle-agents/core';
|
|
24
24
|
import type { StreamTextOnFinishCallback, ToolSet } from 'ai';
|
|
25
25
|
import type { OnChatMessageOptions } from '@cloudflare/ai-chat';
|
|
26
26
|
import type { StreamAdapterConfig } from './types.js';
|
|
@@ -79,6 +79,12 @@ export declare abstract class KuralleAgent<Env = unknown, State = unknown> exten
|
|
|
79
79
|
* Get the Durable Object ID as the session identifier.
|
|
80
80
|
*/
|
|
81
81
|
private getSessionId;
|
|
82
|
+
/**
|
|
83
|
+
* The hex Durable Object id for this instance. Subclasses use it to mint
|
|
84
|
+
* out-of-band callbacks (e.g. a payment link) that route back to this exact DO
|
|
85
|
+
* via `namespace.idFromString(...)`, then resume it through `resumeWithSignal`.
|
|
86
|
+
*/
|
|
87
|
+
protected getDurableObjectId(): string;
|
|
82
88
|
/**
|
|
83
89
|
* Called by CF when a chat message arrives.
|
|
84
90
|
*
|
|
@@ -102,7 +108,29 @@ export declare abstract class KuralleAgent<Env = unknown, State = unknown> exten
|
|
|
102
108
|
*/
|
|
103
109
|
onChatMessage(onFinish: StreamTextOnFinishCallback<ToolSet>, options?: OnChatMessageOptions): Promise<Response>;
|
|
104
110
|
/**
|
|
105
|
-
*
|
|
111
|
+
* Build a Kuralle runtime for this DO (fresh per request to pick up latest
|
|
112
|
+
* config). Bridges CF messages + DO-backed orchestration/working-memory state.
|
|
113
|
+
* Shared by the chat path and the durable resume path.
|
|
114
|
+
*/
|
|
115
|
+
private buildRuntime;
|
|
116
|
+
/**
|
|
117
|
+
* Resume a suspended run by delivering a durable signal — the server-side
|
|
118
|
+
* counterpart to a human/out-of-band event (e.g. a paid checkout link being
|
|
119
|
+
* hit). Drives the resumed turn to completion, then persists **and broadcasts**
|
|
120
|
+
* the resumed assistant reply through CF's machinery, so a live client sees it
|
|
121
|
+
* and a reconnecting client replays it from history.
|
|
122
|
+
*
|
|
123
|
+
* Idempotent at the durable layer: delivering the same `signal.signalId` twice
|
|
124
|
+
* is deduplicated by the effect log, so a double-clicked link is safe.
|
|
125
|
+
*
|
|
126
|
+
* @returns the assistant text produced by the resumed turn (may be empty).
|
|
127
|
+
*/
|
|
128
|
+
protected resumeWithSignal(signal: SignalDelivery): Promise<{
|
|
129
|
+
text: string;
|
|
130
|
+
}>;
|
|
131
|
+
/**
|
|
132
|
+
* Extract the last user turn from CF's messages as runtime input (multimodal).
|
|
133
|
+
* See `lastUserInputFromMessages`.
|
|
106
134
|
*/
|
|
107
135
|
private getLastUserInput;
|
|
108
136
|
/**
|
|
@@ -122,6 +150,31 @@ export declare abstract class KuralleAgent<Env = unknown, State = unknown> exten
|
|
|
122
150
|
* ```
|
|
123
151
|
*/
|
|
124
152
|
protected cleanupOrchestrationRows(maxAgeMs: number): Promise<number>;
|
|
153
|
+
/**
|
|
154
|
+
* Durable scheduler backed by the agents SDK's DO-alarm scheduling
|
|
155
|
+
* (`this.schedule`). Jobs survive isolate restarts and fire in this exact
|
|
156
|
+
* DO via `runScheduledKuralleJob`. Satisfies the core `Scheduler` contract,
|
|
157
|
+
* so engagement drips/broadcasts and runtime wake turns can share it.
|
|
158
|
+
*/
|
|
159
|
+
protected wakeScheduler(): Scheduler;
|
|
160
|
+
/**
|
|
161
|
+
* Schedule a proactive wake turn for this DO's conversation (cart
|
|
162
|
+
* abandonment nudge, "check back in an hour", delivery follow-up).
|
|
163
|
+
* Returns the schedule id (cancellable via `wakeScheduler().cancel`).
|
|
164
|
+
*/
|
|
165
|
+
protected scheduleWake(delayMs: number, wake: Omit<WakeJobPayload, 'sessionId'>): Promise<string>;
|
|
166
|
+
/**
|
|
167
|
+
* DO-alarm callback for scheduled jobs. Wake jobs run an agent-initiated
|
|
168
|
+
* turn and persist + broadcast the assistant reply through CF's machinery
|
|
169
|
+
* (same path as `resumeWithSignal`); other job kinds go to
|
|
170
|
+
* `onScheduledJob` for subclasses to handle.
|
|
171
|
+
*/
|
|
172
|
+
runScheduledKuralleJob(job: ScheduledJob): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Override to handle non-wake scheduled jobs (engagement drips, cleanup…).
|
|
175
|
+
* Default: no-op with a warning, so a mis-routed job is visible.
|
|
176
|
+
*/
|
|
177
|
+
protected onScheduledJob(job: ScheduledJob): Promise<void>;
|
|
125
178
|
/**
|
|
126
179
|
* HTTP endpoint handler.
|
|
127
180
|
* Adds Kuralle-specific endpoints on top of CF's defaults.
|
package/dist/KuralleAgent.js
CHANGED
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
* is stored in a separate lightweight SQLite table via OrchestrationStore.
|
|
20
20
|
*/
|
|
21
21
|
import { AIChatAgent } from '@cloudflare/ai-chat';
|
|
22
|
-
import { createRuntime } from '@kuralle-agents/core';
|
|
22
|
+
import { createRuntime, isWakeJob, wakeJob } from '@kuralle-agents/core';
|
|
23
23
|
import { BridgeSessionStore } from './BridgeSessionStore.js';
|
|
24
24
|
import { OrchestrationStore } from './OrchestrationStore.js';
|
|
25
25
|
import { SqlPersistentMemoryStore } from './SqlPersistentMemoryStore.js';
|
|
26
26
|
import { createSSEResponse } from './StreamAdapter.js';
|
|
27
27
|
import { DEFAULT_STREAM_CONFIG } from './types.js';
|
|
28
28
|
import { durableAgentSurface } from './durable-agent-surface.js';
|
|
29
|
+
import { lastUserInputFromMessages } from './cfMessageInput.js';
|
|
29
30
|
/**
|
|
30
31
|
* Abstract base class for running Kuralle agents on Cloudflare.
|
|
31
32
|
*
|
|
@@ -83,6 +84,14 @@ export class KuralleAgent extends AIChatAgent {
|
|
|
83
84
|
getSessionId() {
|
|
84
85
|
return durableAgentSurface(this).ctx.id.toString();
|
|
85
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* The hex Durable Object id for this instance. Subclasses use it to mint
|
|
89
|
+
* out-of-band callbacks (e.g. a payment link) that route back to this exact DO
|
|
90
|
+
* via `namespace.idFromString(...)`, then resume it through `resumeWithSignal`.
|
|
91
|
+
*/
|
|
92
|
+
getDurableObjectId() {
|
|
93
|
+
return this.getSessionId();
|
|
94
|
+
}
|
|
86
95
|
/**
|
|
87
96
|
* Called by CF when a chat message arrives.
|
|
88
97
|
*
|
|
@@ -110,19 +119,42 @@ export class KuralleAgent extends AIChatAgent {
|
|
|
110
119
|
if (!lastUserMessage) {
|
|
111
120
|
return new Response('No user message', { status: 400 });
|
|
112
121
|
}
|
|
122
|
+
const sessionId = this.getSessionId();
|
|
123
|
+
this.runtime = this.buildRuntime();
|
|
124
|
+
const handle = this.runtime.run({
|
|
125
|
+
input: lastUserMessage,
|
|
126
|
+
sessionId,
|
|
127
|
+
userId: options?.body?.userId,
|
|
128
|
+
abortSignal: options?.abortSignal,
|
|
129
|
+
});
|
|
130
|
+
const streamConfig = {
|
|
131
|
+
...DEFAULT_STREAM_CONFIG,
|
|
132
|
+
...this.getStreamConfig(),
|
|
133
|
+
};
|
|
134
|
+
async function* parts() {
|
|
135
|
+
for await (const part of handle.events) {
|
|
136
|
+
yield part;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return createSSEResponse(parts(), streamConfig);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build a Kuralle runtime for this DO (fresh per request to pick up latest
|
|
143
|
+
* config). Bridges CF messages + DO-backed orchestration/working-memory state.
|
|
144
|
+
* Shared by the chat path and the durable resume path.
|
|
145
|
+
*/
|
|
146
|
+
buildRuntime() {
|
|
113
147
|
const sessionId = this.getSessionId();
|
|
114
148
|
const defaultAgentId = this.getDefaultAgentId();
|
|
115
|
-
// Build session store that bridges CF messages with Kuralle state
|
|
116
149
|
const sessionStore = new BridgeSessionStore({
|
|
117
150
|
sqlExecutor: this.getSql(),
|
|
118
151
|
cfMessages: this.messages,
|
|
119
152
|
sessionId,
|
|
120
153
|
defaultAgentId,
|
|
121
154
|
});
|
|
122
|
-
// Build runtime (fresh per request to pick up latest config)
|
|
123
155
|
const extraConfig = this.getRuntimeConfig();
|
|
124
156
|
const workingMemoryStore = this.getWorkingMemoryStore();
|
|
125
|
-
|
|
157
|
+
return createRuntime({
|
|
126
158
|
...extraConfig,
|
|
127
159
|
agents: this.getAgents(),
|
|
128
160
|
defaultAgentId,
|
|
@@ -131,39 +163,45 @@ export class KuralleAgent extends AIChatAgent {
|
|
|
131
163
|
? { defaultWorkingMemoryStore: workingMemoryStore }
|
|
132
164
|
: {}),
|
|
133
165
|
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Resume a suspended run by delivering a durable signal — the server-side
|
|
169
|
+
* counterpart to a human/out-of-band event (e.g. a paid checkout link being
|
|
170
|
+
* hit). Drives the resumed turn to completion, then persists **and broadcasts**
|
|
171
|
+
* the resumed assistant reply through CF's machinery, so a live client sees it
|
|
172
|
+
* and a reconnecting client replays it from history.
|
|
173
|
+
*
|
|
174
|
+
* Idempotent at the durable layer: delivering the same `signal.signalId` twice
|
|
175
|
+
* is deduplicated by the effect log, so a double-clicked link is safe.
|
|
176
|
+
*
|
|
177
|
+
* @returns the assistant text produced by the resumed turn (may be empty).
|
|
178
|
+
*/
|
|
179
|
+
async resumeWithSignal(signal) {
|
|
180
|
+
const sessionId = this.getSessionId();
|
|
181
|
+
const runtime = this.buildRuntime();
|
|
182
|
+
const handle = runtime.run({ sessionId, signalDelivery: signal });
|
|
183
|
+
let text = '';
|
|
184
|
+
for await (const part of handle.events) {
|
|
185
|
+
if (part.type === 'text-delta')
|
|
186
|
+
text += part.delta;
|
|
148
187
|
}
|
|
149
|
-
|
|
188
|
+
await handle;
|
|
189
|
+
if (text.trim()) {
|
|
190
|
+
const assistantMessage = {
|
|
191
|
+
id: crypto.randomUUID(),
|
|
192
|
+
role: 'assistant',
|
|
193
|
+
parts: [{ type: 'text', text }],
|
|
194
|
+
};
|
|
195
|
+
await this.persistMessages([...this.messages, assistantMessage]);
|
|
196
|
+
}
|
|
197
|
+
return { text };
|
|
150
198
|
}
|
|
151
199
|
/**
|
|
152
|
-
* Extract the
|
|
200
|
+
* Extract the last user turn from CF's messages as runtime input (multimodal).
|
|
201
|
+
* See `lastUserInputFromMessages`.
|
|
153
202
|
*/
|
|
154
203
|
getLastUserInput() {
|
|
155
|
-
|
|
156
|
-
const msg = this.messages[i];
|
|
157
|
-
if (msg.role !== 'user')
|
|
158
|
-
continue;
|
|
159
|
-
const text = msg.parts
|
|
160
|
-
?.filter((part) => part.type === 'text')
|
|
161
|
-
.map((part) => part.text)
|
|
162
|
-
.join('');
|
|
163
|
-
if (text?.trim())
|
|
164
|
-
return text;
|
|
165
|
-
}
|
|
166
|
-
return null;
|
|
204
|
+
return lastUserInputFromMessages(this.messages);
|
|
167
205
|
}
|
|
168
206
|
/**
|
|
169
207
|
* Delete orchestration rows older than `maxAgeMs`. Returns the number of
|
|
@@ -185,12 +223,87 @@ export class KuralleAgent extends AIChatAgent {
|
|
|
185
223
|
const store = new OrchestrationStore(this.getSql());
|
|
186
224
|
return store.cleanup(maxAgeMs);
|
|
187
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Durable scheduler backed by the agents SDK's DO-alarm scheduling
|
|
228
|
+
* (`this.schedule`). Jobs survive isolate restarts and fire in this exact
|
|
229
|
+
* DO via `runScheduledKuralleJob`. Satisfies the core `Scheduler` contract,
|
|
230
|
+
* so engagement drips/broadcasts and runtime wake turns can share it.
|
|
231
|
+
*/
|
|
232
|
+
wakeScheduler() {
|
|
233
|
+
return {
|
|
234
|
+
enqueue: async (job, opts) => {
|
|
235
|
+
const delaySeconds = Math.max(0, Math.ceil((opts?.delayMs ?? 0) / 1000));
|
|
236
|
+
const schedule = await this.schedule(delaySeconds, 'runScheduledKuralleJob', job);
|
|
237
|
+
return schedule.id;
|
|
238
|
+
},
|
|
239
|
+
cancel: async (jobId) => {
|
|
240
|
+
await this.cancelSchedule(jobId);
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Schedule a proactive wake turn for this DO's conversation (cart
|
|
246
|
+
* abandonment nudge, "check back in an hour", delivery follow-up).
|
|
247
|
+
* Returns the schedule id (cancellable via `wakeScheduler().cancel`).
|
|
248
|
+
*/
|
|
249
|
+
async scheduleWake(delayMs, wake) {
|
|
250
|
+
return this.wakeScheduler().enqueue(wakeJob({ ...wake, sessionId: this.getSessionId() }), { delayMs });
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* DO-alarm callback for scheduled jobs. Wake jobs run an agent-initiated
|
|
254
|
+
* turn and persist + broadcast the assistant reply through CF's machinery
|
|
255
|
+
* (same path as `resumeWithSignal`); other job kinds go to
|
|
256
|
+
* `onScheduledJob` for subclasses to handle.
|
|
257
|
+
*/
|
|
258
|
+
async runScheduledKuralleJob(job) {
|
|
259
|
+
if (!isWakeJob(job)) {
|
|
260
|
+
await this.onScheduledJob(job);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const { reason, payload } = job.payload;
|
|
264
|
+
const runtime = this.buildRuntime();
|
|
265
|
+
const handle = runtime.run({
|
|
266
|
+
sessionId: this.getSessionId(),
|
|
267
|
+
wake: { reason, payload },
|
|
268
|
+
});
|
|
269
|
+
let text = '';
|
|
270
|
+
for await (const part of handle.events) {
|
|
271
|
+
if (part.type === 'text-delta')
|
|
272
|
+
text += part.delta;
|
|
273
|
+
}
|
|
274
|
+
await handle;
|
|
275
|
+
if (text.trim()) {
|
|
276
|
+
const assistantMessage = {
|
|
277
|
+
id: crypto.randomUUID(),
|
|
278
|
+
role: 'assistant',
|
|
279
|
+
parts: [{ type: 'text', text }],
|
|
280
|
+
};
|
|
281
|
+
await this.persistMessages([...this.messages, assistantMessage]);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Override to handle non-wake scheduled jobs (engagement drips, cleanup…).
|
|
286
|
+
* Default: no-op with a warning, so a mis-routed job is visible.
|
|
287
|
+
*/
|
|
288
|
+
async onScheduledJob(job) {
|
|
289
|
+
console.warn(`[KuralleAgent] Unhandled scheduled job kind: ${job.kind}`);
|
|
290
|
+
}
|
|
188
291
|
/**
|
|
189
292
|
* HTTP endpoint handler.
|
|
190
293
|
* Adds Kuralle-specific endpoints on top of CF's defaults.
|
|
191
294
|
*/
|
|
192
295
|
async onRequest(request) {
|
|
193
296
|
const url = new URL(request.url);
|
|
297
|
+
// Durable resume: deliver a signal to a suspended run (e.g. a paid checkout
|
|
298
|
+
// link). Body: { signalId: string, name: string, payload?: unknown }.
|
|
299
|
+
if (request.method === 'POST' && url.pathname.endsWith('/resume')) {
|
|
300
|
+
const body = (await request.json().catch(() => null));
|
|
301
|
+
if (!body || typeof body.signalId !== 'string' || typeof body.name !== 'string') {
|
|
302
|
+
return Response.json({ error: 'signalId and name are required' }, { status: 400 });
|
|
303
|
+
}
|
|
304
|
+
const { text } = await this.resumeWithSignal(body);
|
|
305
|
+
return Response.json({ ok: true, text });
|
|
306
|
+
}
|
|
194
307
|
if (url.pathname.endsWith('/orchestration-state')) {
|
|
195
308
|
const store = new OrchestrationStore(this.getSql());
|
|
196
309
|
// OrchestrationStore is now keyed by sessionId (was a single `'default'`
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UserInputContent } from '@kuralle-agents/core';
|
|
2
|
+
import type { UIMessage } from 'ai';
|
|
3
|
+
/**
|
|
4
|
+
* Map the last user turn in a CF `UIMessage[]` to runtime `UserInputContent`.
|
|
5
|
+
*
|
|
6
|
+
* Text parts → `TextPart`; file parts (images / documents / audio uploaded via the
|
|
7
|
+
* CF chat client) → `FilePart` carrying the part's URL, so multimodal input reaches
|
|
8
|
+
* the model instead of being dropped. A text-only turn collapses to a plain string.
|
|
9
|
+
* Returns `null` when the last user turn carries no usable content.
|
|
10
|
+
*/
|
|
11
|
+
export declare function lastUserInputFromMessages(messages: UIMessage[]): UserInputContent | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map the last user turn in a CF `UIMessage[]` to runtime `UserInputContent`.
|
|
3
|
+
*
|
|
4
|
+
* Text parts → `TextPart`; file parts (images / documents / audio uploaded via the
|
|
5
|
+
* CF chat client) → `FilePart` carrying the part's URL, so multimodal input reaches
|
|
6
|
+
* the model instead of being dropped. A text-only turn collapses to a plain string.
|
|
7
|
+
* Returns `null` when the last user turn carries no usable content.
|
|
8
|
+
*/
|
|
9
|
+
export function lastUserInputFromMessages(messages) {
|
|
10
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
11
|
+
const msg = messages[i];
|
|
12
|
+
if (!msg || msg.role !== 'user')
|
|
13
|
+
continue;
|
|
14
|
+
const content = [];
|
|
15
|
+
for (const part of msg.parts ?? []) {
|
|
16
|
+
if (part.type === 'text' && typeof part.text === 'string') {
|
|
17
|
+
content.push({ type: 'text', text: part.text });
|
|
18
|
+
}
|
|
19
|
+
else if (part.type === 'file' &&
|
|
20
|
+
typeof part.url === 'string' &&
|
|
21
|
+
typeof part.mediaType === 'string') {
|
|
22
|
+
content.push({
|
|
23
|
+
type: 'file',
|
|
24
|
+
data: part.url,
|
|
25
|
+
mediaType: part.mediaType,
|
|
26
|
+
filename: part.filename,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (content.length === 0)
|
|
31
|
+
continue;
|
|
32
|
+
const hasFile = content.some((p) => p.type === 'file');
|
|
33
|
+
if (!hasFile) {
|
|
34
|
+
const text = content.map((p) => (p.type === 'text' ? p.text : '')).join('');
|
|
35
|
+
if (text.trim())
|
|
36
|
+
return text;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
return content;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export { OrchestrationStore } from './OrchestrationStore.js';
|
|
|
29
29
|
export { SqlPersistentMemoryStore } from './SqlPersistentMemoryStore.js';
|
|
30
30
|
export { createSqlExecutor } from './sqlExecutor.js';
|
|
31
31
|
export { createSSEResponse } from './StreamAdapter.js';
|
|
32
|
+
export { lastUserInputFromMessages } from './cfMessageInput.js';
|
|
32
33
|
export type { StreamAdapterConfig, OrchestrationState, SqlExecutor, } from './types.js';
|
|
33
34
|
export { DEFAULT_STREAM_CONFIG } from './types.js';
|
|
34
35
|
export type { HarnessConfig, HarnessHooks, HarnessStreamPart, Session, } from '@kuralle-agents/core';
|
package/dist/index.js
CHANGED
|
@@ -29,4 +29,5 @@ export { OrchestrationStore } from './OrchestrationStore.js';
|
|
|
29
29
|
export { SqlPersistentMemoryStore } from './SqlPersistentMemoryStore.js';
|
|
30
30
|
export { createSqlExecutor } from './sqlExecutor.js';
|
|
31
31
|
export { createSSEResponse } from './StreamAdapter.js';
|
|
32
|
+
export { lastUserInputFromMessages } from './cfMessageInput.js';
|
|
32
33
|
export { DEFAULT_STREAM_CONFIG } from './types.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SessionDurableRuns } from '@kuralle-agents/core';
|
|
1
2
|
/**
|
|
2
3
|
* SQL executor type matching Cloudflare's Durable Object sql binding.
|
|
3
4
|
*
|
|
@@ -27,6 +28,13 @@ export interface OrchestrationState {
|
|
|
27
28
|
timestamp: string;
|
|
28
29
|
}>;
|
|
29
30
|
state?: Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Kuralle durable run journal (effect log + run state) for this session.
|
|
33
|
+
* `SessionRunStore` keeps the run on the Session object, so the bridge must
|
|
34
|
+
* persist + restore it here — otherwise durable tools and suspend/resume fail
|
|
35
|
+
* with "Run not found" on CF.
|
|
36
|
+
*/
|
|
37
|
+
durableRuns?: SessionDurableRuns;
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
40
|
* Configuration for the stream adapter.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kuralle-agents/cf-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Kuralle agent integration for Cloudflare Workers with AIChatAgent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,22 +19,22 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@cloudflare/ai-chat": "^0.
|
|
22
|
+
"@cloudflare/ai-chat": "^0.8.4",
|
|
23
23
|
"ai": "^6.0.90",
|
|
24
|
-
"@kuralle-agents/core": "0.
|
|
25
|
-
"@kuralle-agents/realtime-audio": "0.
|
|
24
|
+
"@kuralle-agents/core": "0.8.5",
|
|
25
|
+
"@kuralle-agents/realtime-audio": "0.8.5"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"agents": "
|
|
28
|
+
"agents": ">=0.14.0 <1.0.0",
|
|
29
29
|
"zod": "^4.0.0",
|
|
30
|
-
"@kuralle-agents/voice-protocol": "0.
|
|
30
|
+
"@kuralle-agents/voice-protocol": "0.8.5"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@cloudflare/vitest-pool-workers": "^0.12.7",
|
|
34
|
-
"agents": "^0.
|
|
34
|
+
"agents": "^0.15.0",
|
|
35
35
|
"typescript": "^5.7.0",
|
|
36
36
|
"vitest": "^3.2.4",
|
|
37
|
-
"@kuralle-agents/voice-protocol": "0.
|
|
37
|
+
"@kuralle-agents/voice-protocol": "0.8.5"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=20"
|