@kuralle-agents/cf-agent 0.12.0 → 0.13.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.
@@ -52,6 +52,7 @@ export class BridgeSessionStore {
52
52
  // Restore the durable run journal so durable tools / suspend-resume can find
53
53
  // the run (SessionRunStore reads it off the Session object).
54
54
  session[DURABLE_RUNS_KEY] = orchState?.durableRuns ?? {};
55
+ session.version = orchState?.version ?? 0;
55
56
  return session;
56
57
  }
57
58
  /**
@@ -72,6 +73,7 @@ export class BridgeSessionStore {
72
73
  })),
73
74
  state: session.state,
74
75
  durableRuns: session[DURABLE_RUNS_KEY],
76
+ version: session.version ?? 0,
75
77
  };
76
78
  await this.orchestration.save(session.id, state);
77
79
  }
@@ -1,3 +1,4 @@
1
+ import { StaleWriteError } from '@kuralle-agents/core';
1
2
  /**
2
3
  * Lightweight store for Kuralle orchestration state, keyed by session id.
3
4
  *
@@ -54,7 +55,19 @@ export class OrchestrationStore {
54
55
  }
55
56
  async save(id, state) {
56
57
  this.ensureTable();
57
- const json = JSON.stringify(state);
58
+ const existing = await this.get(id);
59
+ const expected = state.version ?? 0;
60
+ if (existing) {
61
+ const stored = existing.version ?? 0;
62
+ if (stored !== expected) {
63
+ throw new StaleWriteError(id, expected, stored);
64
+ }
65
+ }
66
+ else if (expected !== 0) {
67
+ throw new StaleWriteError(id, expected, 0);
68
+ }
69
+ const toSave = { ...state, version: expected + 1 };
70
+ const json = JSON.stringify(toSave);
58
71
  this.sql `
59
72
  INSERT INTO kuralle_orchestration (id, state, updated_at)
60
73
  VALUES (${id}, ${json}, datetime('now'))
@@ -0,0 +1,11 @@
1
+ import type { AgentSpan, AgentTrace, TraceListWindow, TraceStore } from '@kuralle-agents/core';
2
+ import type { SqlExecutor } from './types.js';
3
+ export declare class SqlTraceStore implements TraceStore {
4
+ private readonly sql;
5
+ constructor(sql: SqlExecutor);
6
+ write(span: AgentSpan): void;
7
+ putSpan(span: AgentSpan): void;
8
+ getTrace(traceId: string): Promise<AgentTrace | null>;
9
+ listTraces(sessionId: string, window?: TraceListWindow): Promise<AgentTrace[]>;
10
+ cleanup(maxAgeMs: number): Promise<number>;
11
+ }
@@ -0,0 +1,50 @@
1
+ import { traceFromSpans } from '@kuralle-agents/core/tracing';
2
+ export class SqlTraceStore {
3
+ sql;
4
+ constructor(sql) {
5
+ this.sql = sql;
6
+ this.sql `CREATE TABLE IF NOT EXISTS kuralle_trace_spans (
7
+ trace_id TEXT NOT NULL,
8
+ span_id TEXT NOT NULL,
9
+ session_id TEXT NOT NULL,
10
+ started_at INTEGER NOT NULL,
11
+ payload TEXT NOT NULL,
12
+ PRIMARY KEY (trace_id, span_id)
13
+ )`;
14
+ this.sql `CREATE INDEX IF NOT EXISTS kuralle_trace_session_started_idx
15
+ ON kuralle_trace_spans (session_id, started_at DESC)`;
16
+ }
17
+ write(span) { this.putSpan(span); }
18
+ putSpan(span) {
19
+ this.sql `INSERT INTO kuralle_trace_spans (trace_id, span_id, session_id, started_at, payload)
20
+ VALUES (${span.traceId}, ${span.spanId}, ${span.attributes.sessionId}, ${span.startTime}, ${JSON.stringify(span)})
21
+ ON CONFLICT(trace_id, span_id) DO UPDATE SET
22
+ session_id = excluded.session_id, started_at = excluded.started_at, payload = excluded.payload`;
23
+ }
24
+ async getTrace(traceId) {
25
+ const rows = this.sql `SELECT trace_id, payload FROM kuralle_trace_spans
26
+ WHERE trace_id = ${traceId} ORDER BY started_at ASC`;
27
+ return traceFromSpans(rows.map((row) => JSON.parse(row.payload)));
28
+ }
29
+ async listTraces(sessionId, window) {
30
+ const from = window?.from?.getTime() ?? 0;
31
+ const to = window?.to?.getTime() ?? Number.MAX_SAFE_INTEGER;
32
+ const limit = window?.limit ?? 100;
33
+ const rows = this.sql `SELECT trace_id, MIN(started_at) AS started_at
34
+ FROM kuralle_trace_spans WHERE session_id = ${sessionId}
35
+ GROUP BY trace_id HAVING MIN(started_at) >= ${from} AND MIN(started_at) <= ${to}
36
+ ORDER BY started_at DESC LIMIT ${limit}`;
37
+ const traces = await Promise.all(rows.map((row) => this.getTrace(row.trace_id)));
38
+ return traces.filter((trace) => trace !== null);
39
+ }
40
+ async cleanup(maxAgeMs) {
41
+ const cutoff = Date.now() - maxAgeMs;
42
+ const before = this.sql `SELECT COUNT(*) AS count FROM (
43
+ SELECT trace_id FROM kuralle_trace_spans GROUP BY trace_id HAVING MIN(started_at) < ${cutoff}
44
+ )`[0]?.count ?? 0;
45
+ this.sql `DELETE FROM kuralle_trace_spans WHERE trace_id IN (
46
+ SELECT trace_id FROM kuralle_trace_spans GROUP BY trace_id HAVING MIN(started_at) < ${cutoff}
47
+ )`;
48
+ return Number(before);
49
+ }
50
+ }
package/dist/index.d.ts CHANGED
@@ -27,6 +27,7 @@ export { KuralleAgent, KuralleAgent as CfChatAgent } from './KuralleAgent.js';
27
27
  export { BridgeSessionStore } from './BridgeSessionStore.js';
28
28
  export { OrchestrationStore } from './OrchestrationStore.js';
29
29
  export { SqlPersistentMemoryStore } from './SqlPersistentMemoryStore.js';
30
+ export { SqlTraceStore } from './SqlTraceStore.js';
30
31
  export { createSqlExecutor } from './sqlExecutor.js';
31
32
  export { createSSEResponse } from './StreamAdapter.js';
32
33
  export { lastUserInputFromMessages } from './cfMessageInput.js';
package/dist/index.js CHANGED
@@ -27,6 +27,7 @@ export { KuralleAgent, KuralleAgent as CfChatAgent } from './KuralleAgent.js';
27
27
  export { BridgeSessionStore } from './BridgeSessionStore.js';
28
28
  export { OrchestrationStore } from './OrchestrationStore.js';
29
29
  export { SqlPersistentMemoryStore } from './SqlPersistentMemoryStore.js';
30
+ export { SqlTraceStore } from './SqlTraceStore.js';
30
31
  export { createSqlExecutor } from './sqlExecutor.js';
31
32
  export { createSSEResponse } from './StreamAdapter.js';
32
33
  export { lastUserInputFromMessages } from './cfMessageInput.js';
package/dist/types.d.ts CHANGED
@@ -35,6 +35,8 @@ export interface OrchestrationState {
35
35
  * with "Run not found" on CF.
36
36
  */
37
37
  durableRuns?: SessionDurableRuns;
38
+ /** Optimistic-concurrency version for orchestration row CAS (C2). */
39
+ version?: number;
38
40
  }
39
41
  /**
40
42
  * 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.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Kuralle agent integration for Cloudflare Workers with AIChatAgent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,21 +21,22 @@
21
21
  "dependencies": {
22
22
  "@cloudflare/ai-chat": "^0.8.4",
23
23
  "ai": "^6.0.90",
24
- "@kuralle-agents/messaging": "0.12.0",
25
- "@kuralle-agents/realtime-audio": "0.12.0",
26
- "@kuralle-agents/core": "0.12.0"
24
+ "@kuralle-agents/core": "0.13.0",
25
+ "@kuralle-agents/realtime-audio": "0.13.0",
26
+ "@kuralle-agents/messaging": "0.13.0"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "agents": ">=0.14.0 <1.0.0",
30
30
  "zod": "^4.0.0",
31
- "@kuralle-agents/voice-protocol": "0.12.0"
31
+ "@kuralle-agents/voice-protocol": "0.13.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@cloudflare/vitest-pool-workers": "^0.12.7",
35
+ "@cloudflare/workers-types": "^4.0.0",
35
36
  "agents": "^0.15.0",
36
37
  "typescript": "^5.7.0",
37
38
  "vitest": "^3.2.4",
38
- "@kuralle-agents/voice-protocol": "0.12.0"
39
+ "@kuralle-agents/voice-protocol": "0.13.0"
39
40
  },
40
41
  "engines": {
41
42
  "node": ">=20"
@@ -53,7 +54,7 @@
53
54
  "repository": {
54
55
  "type": "git",
55
56
  "url": "git+https://github.com/kuralle/kuralle-agents.git",
56
- "directory": "packages/kuralle-cf-agent"
57
+ "directory": "packages/cf-agent"
57
58
  },
58
59
  "scripts": {
59
60
  "prebuild": "rm -rf dist",