@kuralle-agents/cf-agent 0.8.0 → 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.
@@ -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, SignalDelivery } 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';
@@ -150,6 +150,31 @@ export declare abstract class KuralleAgent<Env = unknown, State = unknown> exten
150
150
  * ```
151
151
  */
152
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>;
153
178
  /**
154
179
  * HTTP endpoint handler.
155
180
  * Adds Kuralle-specific endpoints on top of CF's defaults.
@@ -19,7 +19,7 @@
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';
@@ -223,6 +223,71 @@ export class KuralleAgent extends AIChatAgent {
223
223
  const store = new OrchestrationStore(this.getSql());
224
224
  return store.cleanup(maxAgeMs);
225
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
+ }
226
291
  /**
227
292
  * HTTP endpoint handler.
228
293
  * Adds Kuralle-specific endpoints on top of CF's defaults.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kuralle-agents/cf-agent",
3
- "version": "0.8.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",
@@ -21,20 +21,20 @@
21
21
  "dependencies": {
22
22
  "@cloudflare/ai-chat": "^0.8.4",
23
23
  "ai": "^6.0.90",
24
- "@kuralle-agents/core": "0.8.0",
25
- "@kuralle-agents/realtime-audio": "0.8.0"
24
+ "@kuralle-agents/core": "0.8.5",
25
+ "@kuralle-agents/realtime-audio": "0.8.5"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "agents": ">=0.14.0 <1.0.0",
29
29
  "zod": "^4.0.0",
30
- "@kuralle-agents/voice-protocol": "0.8.0"
30
+ "@kuralle-agents/voice-protocol": "0.8.5"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@cloudflare/vitest-pool-workers": "^0.12.7",
34
34
  "agents": "^0.15.0",
35
35
  "typescript": "^5.7.0",
36
36
  "vitest": "^3.2.4",
37
- "@kuralle-agents/voice-protocol": "0.8.0"
37
+ "@kuralle-agents/voice-protocol": "0.8.5"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=20"