@projectaria/cf-agents 0.1.0 → 0.1.2
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 +359 -202
- package/dist/AriaCFChatAgent.d.ts +37 -0
- package/dist/AriaCFChatAgent.d.ts.map +1 -0
- package/dist/AriaCFChatAgent.js +200 -0
- package/dist/AriaCFChatAgent.js.map +1 -0
- package/dist/CloudflareSessionStore.d.ts +37 -0
- package/dist/CloudflareSessionStore.d.ts.map +1 -0
- package/dist/CloudflareSessionStore.js +101 -0
- package/dist/CloudflareSessionStore.js.map +1 -0
- package/dist/aria-to-ui-stream.d.ts +21 -0
- package/dist/aria-to-ui-stream.d.ts.map +1 -0
- package/dist/aria-to-ui-stream.js +118 -0
- package/dist/aria-to-ui-stream.js.map +1 -0
- package/dist/index.d.ts +25 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -23
- package/dist/index.js.map +1 -1
- package/dist/message-format.d.ts +39 -0
- package/dist/message-format.d.ts.map +1 -0
- package/dist/message-format.js +124 -0
- package/dist/message-format.js.map +1 -0
- package/dist/types.d.ts +59 -146
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -7
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
- package/dist/AriaAgent.d.ts +0 -173
- package/dist/AriaAgent.d.ts.map +0 -1
- package/dist/AriaAgent.js +0 -735
- package/dist/AriaAgent.js.map +0 -1
- package/dist/aria-agent-integration.d.ts +0 -52
- package/dist/aria-agent-integration.d.ts.map +0 -1
- package/dist/aria-agent-integration.js +0 -186
- package/dist/aria-agent-integration.js.map +0 -1
- package/dist/message-converter.d.ts +0 -48
- package/dist/message-converter.d.ts.map +0 -1
- package/dist/message-converter.js +0 -138
- package/dist/message-converter.js.map +0 -1
package/dist/AriaAgent.js
DELETED
|
@@ -1,735 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ARIA Agent - Cloudflare Agents integration with proper memory primitives
|
|
3
|
-
*
|
|
4
|
-
* Memory Architecture:
|
|
5
|
-
* 1. Session: Current conversation thread with chronological messages
|
|
6
|
-
* 2. State: Temporary data within the current conversation (working memory)
|
|
7
|
-
* 3. Memory: Searchable, cross-session information (knowledge base)
|
|
8
|
-
*
|
|
9
|
-
* AI SDK 6.0 Message Types (matching agents@0.3.3):
|
|
10
|
-
* - UIMessage: Format with UI metadata (id, role, parts)
|
|
11
|
-
* - ModelMessage: Format expected by LLM models
|
|
12
|
-
* - convertToModelMessages: Converts UIMessage[] to ModelMessage[]
|
|
13
|
-
*/
|
|
14
|
-
import { AIChatAgent } from "agents/ai-chat-agent";
|
|
15
|
-
import { nanoid } from "nanoid";
|
|
16
|
-
import { batchAriaToChat, batchChatToAria, extractConversationSummary, } from "./message-converter";
|
|
17
|
-
/**
|
|
18
|
-
* Default configuration
|
|
19
|
-
*/
|
|
20
|
-
const DEFAULT_CONFIG = {
|
|
21
|
-
historyLimit: 50,
|
|
22
|
-
enableMemory: true,
|
|
23
|
-
memoryRetentionMs: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
24
|
-
trackTransitions: true,
|
|
25
|
-
generateSessionId: () => crypto.randomUUID(),
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* ARIA Agent extending AIChatAgent
|
|
29
|
-
*
|
|
30
|
-
* Provides:
|
|
31
|
-
* - Session management with conversation history
|
|
32
|
-
* - State management (working memory) for session-local data
|
|
33
|
-
* - Memory layer for cross-session knowledge
|
|
34
|
-
* - Flow orchestration integration
|
|
35
|
-
* - Multi-turn conversation support
|
|
36
|
-
*/
|
|
37
|
-
export class AriaAgent extends AIChatAgent {
|
|
38
|
-
config;
|
|
39
|
-
flowDefinitions;
|
|
40
|
-
constructor(ctx, env) {
|
|
41
|
-
super(ctx, env);
|
|
42
|
-
this.config = { ...DEFAULT_CONFIG };
|
|
43
|
-
this.flowDefinitions = new Map();
|
|
44
|
-
// Create ARIA-specific tables
|
|
45
|
-
this.initializeTables();
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Initialize ARIA-specific database tables
|
|
49
|
-
*/
|
|
50
|
-
initializeTables() {
|
|
51
|
-
// Memory table for cross-session knowledge
|
|
52
|
-
this.sql `
|
|
53
|
-
create table if not exists aria_memory (
|
|
54
|
-
id text primary key,
|
|
55
|
-
session_id text,
|
|
56
|
-
key text not null,
|
|
57
|
-
value text not null,
|
|
58
|
-
type text not null check(type in ('preference', 'fact', 'context', 'entity', 'custom')),
|
|
59
|
-
created_at integer default (unixepoch()),
|
|
60
|
-
last_accessed_at integer default (unixepoch()),
|
|
61
|
-
access_count integer default 1,
|
|
62
|
-
embedding text,
|
|
63
|
-
metadata text
|
|
64
|
-
)
|
|
65
|
-
`;
|
|
66
|
-
this.sql `create index if not exists idx_memory_session
|
|
67
|
-
on aria_memory(session_id)`;
|
|
68
|
-
this.sql `create index if not exists idx_memory_key
|
|
69
|
-
on aria_memory(key)`;
|
|
70
|
-
this.sql `create index if not exists idx_memory_type
|
|
71
|
-
on aria_memory(type)`;
|
|
72
|
-
// Flow transitions table for tracking execution history
|
|
73
|
-
this.sql `
|
|
74
|
-
create table if not exists aria_flow_transitions (
|
|
75
|
-
id text primary key,
|
|
76
|
-
flow_id text not null,
|
|
77
|
-
from_node_id text,
|
|
78
|
-
to_node_id text not null,
|
|
79
|
-
transitioned_at integer default (unixepoch()),
|
|
80
|
-
metadata text
|
|
81
|
-
)
|
|
82
|
-
`;
|
|
83
|
-
this.sql `create index if not exists idx_flow_transitions_flow
|
|
84
|
-
on aria_flow_transitions(flow_id, transitioned_at)`;
|
|
85
|
-
// Session history table (in addition to AIChatAgent's messages)
|
|
86
|
-
this.sql `
|
|
87
|
-
create table if not exists aria_session_events (
|
|
88
|
-
id text primary key,
|
|
89
|
-
event_type text not null,
|
|
90
|
-
event_data text,
|
|
91
|
-
occurred_at integer default (unixepoch())
|
|
92
|
-
)
|
|
93
|
-
`;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Initial state when no session exists
|
|
97
|
-
*/
|
|
98
|
-
initialState = {
|
|
99
|
-
workingMemory: {},
|
|
100
|
-
activeFlowId: undefined,
|
|
101
|
-
activeNodeId: undefined,
|
|
102
|
-
historyLimit: DEFAULT_CONFIG.historyLimit,
|
|
103
|
-
};
|
|
104
|
-
// ============================================================
|
|
105
|
-
// SESSION MANAGEMENT
|
|
106
|
-
// ============================================================
|
|
107
|
-
/**
|
|
108
|
-
* Get the current session
|
|
109
|
-
* Session = Current conversation thread with chronological messages
|
|
110
|
-
*/
|
|
111
|
-
getSession() {
|
|
112
|
-
const state = this.state;
|
|
113
|
-
return {
|
|
114
|
-
id: this.name,
|
|
115
|
-
messages: batchChatToAria(this.messages),
|
|
116
|
-
activeFlow: state.activeFlowId && state.activeNodeId
|
|
117
|
-
? {
|
|
118
|
-
flowId: state.activeFlowId,
|
|
119
|
-
currentNodeId: state.activeNodeId,
|
|
120
|
-
enteredAt: Date.now(),
|
|
121
|
-
}
|
|
122
|
-
: undefined,
|
|
123
|
-
historyLimit: state.historyLimit,
|
|
124
|
-
createdAt: this.getCreatedAt(),
|
|
125
|
-
updatedAt: Date.now(),
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Get session creation time from storage
|
|
130
|
-
*/
|
|
131
|
-
getCreatedAt() {
|
|
132
|
-
const row = this.sql `
|
|
133
|
-
select min(created_at) as created_at from aria_session_events
|
|
134
|
-
`;
|
|
135
|
-
return row?.[0]?.created_at || Date.now();
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Record a session event for tracking
|
|
139
|
-
*/
|
|
140
|
-
recordSessionEvent(eventType, eventData) {
|
|
141
|
-
this.sql `
|
|
142
|
-
insert into aria_session_events (id, event_type, event_data)
|
|
143
|
-
values (${nanoid()}, ${eventType}, ${JSON.stringify(eventData)})
|
|
144
|
-
`;
|
|
145
|
-
}
|
|
146
|
-
// ============================================================
|
|
147
|
-
// STATE MANAGEMENT (Session-Local Data)
|
|
148
|
-
// ============================================================
|
|
149
|
-
/**
|
|
150
|
-
* Get working memory (state within current conversation)
|
|
151
|
-
* Examples: shopping cart items, user preferences mentioned in this session
|
|
152
|
-
*/
|
|
153
|
-
getWorkingMemory() {
|
|
154
|
-
return this.state.workingMemory || {};
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Set a value in working memory
|
|
158
|
-
*/
|
|
159
|
-
setWorkingMemory(key, value) {
|
|
160
|
-
const current = this.getWorkingMemory();
|
|
161
|
-
this.setState({
|
|
162
|
-
...this.state,
|
|
163
|
-
workingMemory: {
|
|
164
|
-
...current,
|
|
165
|
-
[key]: value,
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Clear all working memory
|
|
171
|
-
*/
|
|
172
|
-
clearWorkingMemory() {
|
|
173
|
-
this.setState({
|
|
174
|
-
...this.state,
|
|
175
|
-
workingMemory: {},
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
// ============================================================
|
|
179
|
-
// MEMORY LAYER (Cross-Session Knowledge)
|
|
180
|
-
// ============================================================
|
|
181
|
-
/**
|
|
182
|
-
* Store a memory for cross-session retrieval
|
|
183
|
-
* Memory = Searchable, cross-session information (knowledge base)
|
|
184
|
-
*/
|
|
185
|
-
storeMemory(memory) {
|
|
186
|
-
const id = nanoid();
|
|
187
|
-
const now = Date.now();
|
|
188
|
-
this.sql `
|
|
189
|
-
insert into aria_memory (
|
|
190
|
-
id, session_id, key, value, type,
|
|
191
|
-
created_at, last_accessed_at, access_count, metadata
|
|
192
|
-
)
|
|
193
|
-
values (
|
|
194
|
-
${id},
|
|
195
|
-
${memory.sessionId ?? null},
|
|
196
|
-
${memory.key},
|
|
197
|
-
${JSON.stringify(memory.value)},
|
|
198
|
-
${memory.type},
|
|
199
|
-
${Math.floor(now / 1000)},
|
|
200
|
-
${Math.floor(now / 1000)},
|
|
201
|
-
1,
|
|
202
|
-
${memory.metadata ? JSON.stringify(memory.metadata) : null}
|
|
203
|
-
)
|
|
204
|
-
`;
|
|
205
|
-
return id;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Search memory by query
|
|
209
|
-
* Returns relevant memories across all sessions
|
|
210
|
-
*/
|
|
211
|
-
searchMemories(query) {
|
|
212
|
-
const limit = query.limit || 10;
|
|
213
|
-
let rows = [];
|
|
214
|
-
// Use different queries based on search criteria
|
|
215
|
-
// Note: SQLite tagged template literals don't support dynamic queries
|
|
216
|
-
if (query.query && query.types && query.types.length > 0) {
|
|
217
|
-
// Search with text and type filter
|
|
218
|
-
const typeList = query.types.join(",");
|
|
219
|
-
const searchPattern = `%${query.query}%`;
|
|
220
|
-
rows = this.sql `
|
|
221
|
-
select * from aria_memory
|
|
222
|
-
where key like ${searchPattern}
|
|
223
|
-
and type in (${typeList})
|
|
224
|
-
order by access_count desc, last_accessed_at desc
|
|
225
|
-
limit ${limit}
|
|
226
|
-
` || [];
|
|
227
|
-
}
|
|
228
|
-
else if (query.query) {
|
|
229
|
-
// Search with text only
|
|
230
|
-
const searchPattern = `%${query.query}%`;
|
|
231
|
-
rows = this.sql `
|
|
232
|
-
select * from aria_memory
|
|
233
|
-
where key like ${searchPattern}
|
|
234
|
-
order by access_count desc, last_accessed_at desc
|
|
235
|
-
limit ${limit}
|
|
236
|
-
` || [];
|
|
237
|
-
}
|
|
238
|
-
else if (query.types && query.types.length > 0) {
|
|
239
|
-
// Search with type filter only
|
|
240
|
-
const typeList = query.types.join(",");
|
|
241
|
-
rows = this.sql `
|
|
242
|
-
select * from aria_memory
|
|
243
|
-
where type in (${typeList})
|
|
244
|
-
order by access_count desc, last_accessed_at desc
|
|
245
|
-
limit ${limit}
|
|
246
|
-
` || [];
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
// Get all memories
|
|
250
|
-
rows = this.sql `
|
|
251
|
-
select * from aria_memory
|
|
252
|
-
order by access_count desc, last_accessed_at desc
|
|
253
|
-
limit ${limit}
|
|
254
|
-
` || [];
|
|
255
|
-
}
|
|
256
|
-
return rows.map((row) => ({
|
|
257
|
-
memory: {
|
|
258
|
-
id: row.id,
|
|
259
|
-
sessionId: row.session_id,
|
|
260
|
-
key: row.key,
|
|
261
|
-
value: JSON.parse(row.value),
|
|
262
|
-
type: row.type,
|
|
263
|
-
createdAt: row.created_at * 1000,
|
|
264
|
-
lastAccessedAt: row.last_accessed_at * 1000,
|
|
265
|
-
accessCount: row.access_count,
|
|
266
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
|
|
267
|
-
},
|
|
268
|
-
score: Math.min(1, row.access_count / 10), // Simple relevance score
|
|
269
|
-
relevance: row.access_count > 5 ? "high" : row.access_count > 2 ? "medium" : "low",
|
|
270
|
-
}));
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Get a specific memory by key
|
|
274
|
-
*/
|
|
275
|
-
getMemory(key) {
|
|
276
|
-
const rows = this.sql `
|
|
277
|
-
select * from aria_memory where key = ${key} limit 1
|
|
278
|
-
`;
|
|
279
|
-
if (!rows || rows.length === 0) {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
id: key,
|
|
284
|
-
sessionId: null,
|
|
285
|
-
key,
|
|
286
|
-
value: JSON.parse(rows[0].value),
|
|
287
|
-
type: rows[0].type,
|
|
288
|
-
createdAt: Date.now(),
|
|
289
|
-
lastAccessedAt: Date.now(),
|
|
290
|
-
accessCount: 1,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Update memory access (increments access count and updates timestamp)
|
|
295
|
-
*/
|
|
296
|
-
updateMemoryAccess(key) {
|
|
297
|
-
this.sql `
|
|
298
|
-
update aria_memory
|
|
299
|
-
set access_count = access_count + 1, last_accessed_at = ${Math.floor(Date.now() / 1000)}
|
|
300
|
-
where key = ${key}
|
|
301
|
-
`;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Clean up old memories based on retention policy
|
|
305
|
-
*/
|
|
306
|
-
cleanupOldMemories() {
|
|
307
|
-
const cutoff = Math.floor((Date.now() - this.config.memoryRetentionMs) / 1000);
|
|
308
|
-
this.sql `delete from aria_memory where created_at < ${cutoff}`;
|
|
309
|
-
}
|
|
310
|
-
// ============================================================
|
|
311
|
-
// FLOW ORCHESTRATION
|
|
312
|
-
// ============================================================
|
|
313
|
-
/**
|
|
314
|
-
* Register a flow definition
|
|
315
|
-
*/
|
|
316
|
-
registerFlow(flow) {
|
|
317
|
-
this.flowDefinitions.set(flow.id, flow);
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Record a flow transition
|
|
321
|
-
*/
|
|
322
|
-
recordFlowTransition(flowId, fromNodeId, toNodeId, metadata) {
|
|
323
|
-
if (!this.config.trackTransitions) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
this.sql `
|
|
327
|
-
insert into aria_flow_transitions (id, flow_id, from_node_id, to_node_id, metadata)
|
|
328
|
-
values (${nanoid()}, ${flowId}, ${fromNodeId}, ${toNodeId}, ${metadata ? JSON.stringify(metadata) : null})
|
|
329
|
-
`;
|
|
330
|
-
this.setState({
|
|
331
|
-
...this.state,
|
|
332
|
-
activeFlowId: flowId,
|
|
333
|
-
activeNodeId: toNodeId,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Get flow transition history
|
|
338
|
-
*/
|
|
339
|
-
getFlowHistory(flowId, limit = 100) {
|
|
340
|
-
let query = "select * from aria_flow_transitions";
|
|
341
|
-
if (flowId) {
|
|
342
|
-
query += ` where flow_id = ${flowId}`;
|
|
343
|
-
}
|
|
344
|
-
query += ` order by transitioned_at desc limit ${limit}`;
|
|
345
|
-
const rows = this.sql `
|
|
346
|
-
${query}
|
|
347
|
-
`;
|
|
348
|
-
return (rows || []).map((row) => ({
|
|
349
|
-
id: row.id,
|
|
350
|
-
flowId: row.flow_id,
|
|
351
|
-
fromNodeId: row.from_node_id,
|
|
352
|
-
toNodeId: row.to_node_id,
|
|
353
|
-
transitionedAt: row.transitioned_at * 1000,
|
|
354
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
|
|
355
|
-
}));
|
|
356
|
-
}
|
|
357
|
-
// ============================================================
|
|
358
|
-
// MESSAGE HANDLING
|
|
359
|
-
// ============================================================
|
|
360
|
-
/**
|
|
361
|
-
* Handle incoming chat messages
|
|
362
|
-
* This is where ARIA's flow orchestration would be integrated
|
|
363
|
-
*/
|
|
364
|
-
async onChatMessage(onFinish, options) {
|
|
365
|
-
// Record session event
|
|
366
|
-
this.recordSessionEvent("message_received", {
|
|
367
|
-
messageCount: this.messages.length,
|
|
368
|
-
workingMemoryKeys: Object.keys(this.getWorkingMemory()),
|
|
369
|
-
});
|
|
370
|
-
// Get current session
|
|
371
|
-
const session = this.getSession();
|
|
372
|
-
// Get conversation context from memory
|
|
373
|
-
const relevantMemories = this.searchMemories({
|
|
374
|
-
query: session.messages[session.messages.length - 1]?.content || "",
|
|
375
|
-
limit: 5,
|
|
376
|
-
types: ["context", "entity", "preference"],
|
|
377
|
-
});
|
|
378
|
-
// Build system prompt with context
|
|
379
|
-
const systemPrompt = this.buildSystemPrompt(session, relevantMemories);
|
|
380
|
-
// Here you would integrate ARIA's flow orchestration
|
|
381
|
-
// For now, return a basic response
|
|
382
|
-
// TODO: Integrate with @projectaria/aria VoltRuntime
|
|
383
|
-
// Example: Save learned facts to memory
|
|
384
|
-
const summary = extractConversationSummary(session.messages);
|
|
385
|
-
for (const topic of summary.topics.slice(0, 3)) {
|
|
386
|
-
this.storeMemory({
|
|
387
|
-
sessionId: session.id,
|
|
388
|
-
key: topic,
|
|
389
|
-
value: { mentioned: true, count: 1 },
|
|
390
|
-
type: "context",
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
// Call parent implementation
|
|
394
|
-
return super.onChatMessage(onFinish, options);
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Build system prompt with conversation context
|
|
398
|
-
*/
|
|
399
|
-
buildSystemPrompt(session, memories) {
|
|
400
|
-
const parts = [
|
|
401
|
-
"You are a helpful AI assistant.",
|
|
402
|
-
"You have access to conversation history and can recall information from previous conversations.",
|
|
403
|
-
];
|
|
404
|
-
// Add active flow context
|
|
405
|
-
if (session.activeFlow) {
|
|
406
|
-
parts.push(`\nCurrent flow: ${session.activeFlow.flowId} (node: ${session.activeFlow.currentNodeId})`);
|
|
407
|
-
}
|
|
408
|
-
// Add working memory context
|
|
409
|
-
const workingMemory = this.getWorkingMemory();
|
|
410
|
-
if (Object.keys(workingMemory).length > 0) {
|
|
411
|
-
parts.push("\nCurrent session data:");
|
|
412
|
-
for (const [key, value] of Object.entries(workingMemory)) {
|
|
413
|
-
parts.push(` - ${key}: ${JSON.stringify(value)}`);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
// Add memory context
|
|
417
|
-
if (memories.length > 0) {
|
|
418
|
-
parts.push("\nRelevant information from previous conversations:");
|
|
419
|
-
for (const memory of memories) {
|
|
420
|
-
parts.push(` - ${memory.memory.key}: ${JSON.stringify(memory.memory.value)}`);
|
|
421
|
-
this.updateMemoryAccess(memory.memory.key);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return parts.join("\n");
|
|
425
|
-
}
|
|
426
|
-
// ============================================================
|
|
427
|
-
// ARIA AGENT INTEGRATION
|
|
428
|
-
// ============================================================
|
|
429
|
-
/**
|
|
430
|
-
* Run an ARIA agent with persistence
|
|
431
|
-
* This method allows you to use ARIA's createAgent() with persistent memory
|
|
432
|
-
*
|
|
433
|
-
* @param agent - The ARIA agent created via createAgent()
|
|
434
|
-
* @param input - The user input
|
|
435
|
-
* @returns The agent response with persisted session state
|
|
436
|
-
*/
|
|
437
|
-
async runAriaAgent(agent, input) {
|
|
438
|
-
// Get current session from persistent storage
|
|
439
|
-
const currentSession = this.getSession();
|
|
440
|
-
// Convert to ARIA AgentSession format
|
|
441
|
-
const ariaSession = {
|
|
442
|
-
sessionId: currentSession.id,
|
|
443
|
-
conversationId: currentSession.id,
|
|
444
|
-
history: batchAriaToChat(currentSession.messages),
|
|
445
|
-
workingMemory: this.getWorkingMemory(),
|
|
446
|
-
context: new Map(),
|
|
447
|
-
createdAt: new Date(currentSession.createdAt).toISOString(),
|
|
448
|
-
updatedAt: new Date(currentSession.updatedAt).toISOString(),
|
|
449
|
-
};
|
|
450
|
-
// Search for relevant memories
|
|
451
|
-
const relevantMemories = this.searchMemories({
|
|
452
|
-
query: input,
|
|
453
|
-
limit: 5,
|
|
454
|
-
types: ["preference", "fact", "context", "entity"],
|
|
455
|
-
});
|
|
456
|
-
// Build enhanced prompt with context
|
|
457
|
-
const prompt = this.buildAriaAgentPrompt(currentSession, relevantMemories, this.getWorkingMemory());
|
|
458
|
-
// Run the ARIA agent
|
|
459
|
-
// Note: systemPrompt should be passed via the agent config, not metadata
|
|
460
|
-
const result = await agent.generateText({
|
|
461
|
-
input,
|
|
462
|
-
session: ariaSession,
|
|
463
|
-
});
|
|
464
|
-
// Persist working memory changes
|
|
465
|
-
if (result.session) {
|
|
466
|
-
const stateBefore = JSON.stringify(ariaSession.workingMemory);
|
|
467
|
-
const stateAfter = JSON.stringify(result.session.workingMemory);
|
|
468
|
-
if (stateBefore !== stateAfter) {
|
|
469
|
-
// Merge the updated working memory into our persistent state
|
|
470
|
-
const currentWorkingMemory = this.getWorkingMemory();
|
|
471
|
-
const updatedWorkingMemory = {
|
|
472
|
-
...currentWorkingMemory,
|
|
473
|
-
...result.session.workingMemory,
|
|
474
|
-
};
|
|
475
|
-
this.setState({
|
|
476
|
-
...this.state,
|
|
477
|
-
workingMemory: updatedWorkingMemory,
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
// Extract and store memories from the conversation
|
|
481
|
-
await this.extractAndStoreMemories(result.session);
|
|
482
|
-
}
|
|
483
|
-
return {
|
|
484
|
-
text: result.text,
|
|
485
|
-
session: result.session,
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Stream an ARIA agent response with persistence
|
|
490
|
-
*
|
|
491
|
-
* @param agent - The ARIA agent created via createAgent()
|
|
492
|
-
* @param input - The user input
|
|
493
|
-
* @returns Stream result with session
|
|
494
|
-
*/
|
|
495
|
-
async streamAriaAgent(agent, input) {
|
|
496
|
-
// Get current session from persistent storage
|
|
497
|
-
const currentSession = this.getSession();
|
|
498
|
-
// Convert to ARIA AgentSession format
|
|
499
|
-
const ariaSession = {
|
|
500
|
-
sessionId: currentSession.id,
|
|
501
|
-
conversationId: currentSession.id,
|
|
502
|
-
history: batchAriaToChat(currentSession.messages),
|
|
503
|
-
workingMemory: this.getWorkingMemory(),
|
|
504
|
-
context: new Map(),
|
|
505
|
-
createdAt: new Date(currentSession.createdAt).toISOString(),
|
|
506
|
-
updatedAt: new Date(currentSession.updatedAt).toISOString(),
|
|
507
|
-
};
|
|
508
|
-
// Search for relevant memories
|
|
509
|
-
const relevantMemories = this.searchMemories({
|
|
510
|
-
query: input,
|
|
511
|
-
limit: 5,
|
|
512
|
-
types: ["preference", "fact", "context", "entity"],
|
|
513
|
-
});
|
|
514
|
-
// Build enhanced prompt with context
|
|
515
|
-
const prompt = this.buildAriaAgentPrompt(currentSession, relevantMemories, this.getWorkingMemory());
|
|
516
|
-
// Stream the ARIA agent response
|
|
517
|
-
// Note: systemPrompt should be passed via the agent config, not metadata
|
|
518
|
-
const result = await agent.streamText({
|
|
519
|
-
input,
|
|
520
|
-
session: ariaSession,
|
|
521
|
-
});
|
|
522
|
-
// Note: For streaming, we need to handle persistence after the stream completes
|
|
523
|
-
// This is typically done in the onFinish callback
|
|
524
|
-
if (result.usage) {
|
|
525
|
-
result.usage.then(async () => {
|
|
526
|
-
await this.extractAndStoreMemories(ariaSession);
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
return {
|
|
530
|
-
fullStream: result.fullStream,
|
|
531
|
-
textStream: result.textStream,
|
|
532
|
-
usage: result.usage,
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Build ARIA agent prompt with context from session and memories
|
|
537
|
-
*/
|
|
538
|
-
buildAriaAgentPrompt(session, memories, workingMemory) {
|
|
539
|
-
const parts = ["You are a helpful AI assistant with memory."];
|
|
540
|
-
// Add working memory/state context
|
|
541
|
-
if (Object.keys(workingMemory).length > 0) {
|
|
542
|
-
parts.push("\n📋 Current Session Data:");
|
|
543
|
-
for (const [key, value] of Object.entries(workingMemory)) {
|
|
544
|
-
parts.push(` ${key}: ${JSON.stringify(value)}`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
// Add memories from past conversations
|
|
548
|
-
if (memories.length > 0) {
|
|
549
|
-
parts.push("\n💭 Relevant Information from Past Conversations:");
|
|
550
|
-
for (const memoryResult of memories) {
|
|
551
|
-
const { memory } = memoryResult;
|
|
552
|
-
parts.push(` [${memory.type}] ${memory.key}: ${JSON.stringify(memory.value)}`);
|
|
553
|
-
// Update access count for relevance tracking
|
|
554
|
-
this.updateMemoryAccess(memory.key);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return parts.join("\n");
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Extract and store memories from ARIA agent conversation
|
|
561
|
-
*/
|
|
562
|
-
async extractAndStoreMemories(ariaSession) {
|
|
563
|
-
const conversation = ariaSession.history;
|
|
564
|
-
// Analyze each message for memory extraction
|
|
565
|
-
for (const message of conversation) {
|
|
566
|
-
if (message.role === "user") {
|
|
567
|
-
// Extract text content
|
|
568
|
-
const text = message.parts
|
|
569
|
-
.filter((p) => p.type === "text")
|
|
570
|
-
.map((p) => p.text)
|
|
571
|
-
.join(" ");
|
|
572
|
-
const content = text.toLowerCase();
|
|
573
|
-
// Detect preferences
|
|
574
|
-
if (content.includes("prefer") ||
|
|
575
|
-
content.includes("like") ||
|
|
576
|
-
content.includes("want")) {
|
|
577
|
-
const prefKey = this.extractPreferenceKey(text);
|
|
578
|
-
if (prefKey) {
|
|
579
|
-
this.storeMemory({
|
|
580
|
-
sessionId: ariaSession.sessionId,
|
|
581
|
-
key: prefKey,
|
|
582
|
-
value: { stated: text, timestamp: Date.now() },
|
|
583
|
-
type: "preference",
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
// Detect facts
|
|
588
|
-
if (content.includes("my name is") ||
|
|
589
|
-
content.includes("i live in") ||
|
|
590
|
-
content.includes("i am a")) {
|
|
591
|
-
const factKey = this.extractFactKey(text);
|
|
592
|
-
if (factKey) {
|
|
593
|
-
this.storeMemory({
|
|
594
|
-
sessionId: null, // Global memory
|
|
595
|
-
key: factKey,
|
|
596
|
-
value: { stated: text, timestamp: Date.now() },
|
|
597
|
-
type: "fact",
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* Extract preference key from message content
|
|
606
|
-
*/
|
|
607
|
-
extractPreferenceKey(content) {
|
|
608
|
-
const lower = content.toLowerCase();
|
|
609
|
-
if (lower.includes("dark mode"))
|
|
610
|
-
return "pref:theme:dark";
|
|
611
|
-
if (lower.includes("light mode"))
|
|
612
|
-
return "pref:theme:light";
|
|
613
|
-
if (lower.includes("email updates"))
|
|
614
|
-
return "pref:email-updates";
|
|
615
|
-
if (lower.includes("notifications"))
|
|
616
|
-
return "pref:notifications";
|
|
617
|
-
// Pattern matching
|
|
618
|
-
const preferMatch = content.match(/i (?:prefer|like|want) (.+?)(?:\.|,|!|$)/i);
|
|
619
|
-
if (preferMatch) {
|
|
620
|
-
return `pref:custom:${preferMatch[1].trim().toLowerCase()}`;
|
|
621
|
-
}
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
/**
|
|
625
|
-
* Extract fact key from message content
|
|
626
|
-
*/
|
|
627
|
-
extractFactKey(content) {
|
|
628
|
-
const lower = content.toLowerCase();
|
|
629
|
-
if (lower.includes("my name is")) {
|
|
630
|
-
const name = content.match(/name is (\w+)/i)?.[1];
|
|
631
|
-
return name ? `fact:name:${name}` : null;
|
|
632
|
-
}
|
|
633
|
-
if (lower.includes("i live in")) {
|
|
634
|
-
const location = content.match(/live in (\w+(?: \w+)*)/i)?.[1];
|
|
635
|
-
return location ? `fact:location:${location}` : null;
|
|
636
|
-
}
|
|
637
|
-
if (lower.includes("i am a")) {
|
|
638
|
-
const role = content.match(/i am a (\w+(?: \w+)*)/i)?.[1];
|
|
639
|
-
return role ? `fact:role:${role}` : null;
|
|
640
|
-
}
|
|
641
|
-
return null;
|
|
642
|
-
}
|
|
643
|
-
// ============================================================
|
|
644
|
-
// LIFECYCLE
|
|
645
|
-
// ============================================================
|
|
646
|
-
/**
|
|
647
|
-
* Called when agent starts
|
|
648
|
-
*/
|
|
649
|
-
async onStart() {
|
|
650
|
-
this.recordSessionEvent("session_started", {
|
|
651
|
-
agentName: this.name,
|
|
652
|
-
timestamp: Date.now(),
|
|
653
|
-
});
|
|
654
|
-
// Cleanup old memories periodically
|
|
655
|
-
this.cleanupOldMemories();
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Called when a client connects
|
|
659
|
-
*/
|
|
660
|
-
async onConnect(connection, ctx) {
|
|
661
|
-
this.recordSessionEvent("client_connected", {
|
|
662
|
-
connectionId: connection.id,
|
|
663
|
-
});
|
|
664
|
-
return super.onConnect(connection, ctx);
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Cleanup when destroyed
|
|
668
|
-
*/
|
|
669
|
-
async destroy() {
|
|
670
|
-
// Record session end
|
|
671
|
-
this.recordSessionEvent("session_ended", {
|
|
672
|
-
messageCount: this.messages.length,
|
|
673
|
-
finalWorkingMemory: this.getWorkingMemory(),
|
|
674
|
-
});
|
|
675
|
-
// Drop ARIA tables
|
|
676
|
-
this.sql `drop table if exists aria_memory`;
|
|
677
|
-
this.sql `drop table if exists aria_flow_transitions`;
|
|
678
|
-
this.sql `drop table if exists aria_session_events`;
|
|
679
|
-
await super.destroy();
|
|
680
|
-
}
|
|
681
|
-
// ============================================================
|
|
682
|
-
// HTTP ENDPOINTS
|
|
683
|
-
// ============================================================
|
|
684
|
-
/**
|
|
685
|
-
* Handle HTTP requests
|
|
686
|
-
*/
|
|
687
|
-
async onRequest(request) {
|
|
688
|
-
const url = new URL(request.url);
|
|
689
|
-
// GET /session - Get current session
|
|
690
|
-
if (url.pathname === "/session" && request.method === "GET") {
|
|
691
|
-
return Response.json(this.getSession());
|
|
692
|
-
}
|
|
693
|
-
// GET /working-memory - Get working memory
|
|
694
|
-
if (url.pathname === "/working-memory" && request.method === "GET") {
|
|
695
|
-
return Response.json(this.getWorkingMemory());
|
|
696
|
-
}
|
|
697
|
-
// PUT /working-memory - Set working memory value
|
|
698
|
-
if (url.pathname === "/working-memory" && request.method === "PUT") {
|
|
699
|
-
const body = await request.json();
|
|
700
|
-
this.setWorkingMemory(body.key, body.value);
|
|
701
|
-
return Response.json({ success: true });
|
|
702
|
-
}
|
|
703
|
-
// GET /memory - Search memories
|
|
704
|
-
if (url.pathname === "/memory" && request.method === "GET") {
|
|
705
|
-
const query = url.searchParams.get("q");
|
|
706
|
-
const type = url.searchParams.get("type");
|
|
707
|
-
const limit = parseInt(url.searchParams.get("limit") || "10", 10);
|
|
708
|
-
const results = this.searchMemories({
|
|
709
|
-
query: query || undefined,
|
|
710
|
-
types: type ? [type] : undefined,
|
|
711
|
-
limit,
|
|
712
|
-
});
|
|
713
|
-
return Response.json(results);
|
|
714
|
-
}
|
|
715
|
-
// POST /memory - Store memory
|
|
716
|
-
if (url.pathname === "/memory" && request.method === "POST") {
|
|
717
|
-
const body = await request.json();
|
|
718
|
-
const id = this.storeMemory(body);
|
|
719
|
-
return Response.json({ id, success: true });
|
|
720
|
-
}
|
|
721
|
-
// GET /flow-history - Get flow transition history
|
|
722
|
-
if (url.pathname === "/flow-history" && request.method === "GET") {
|
|
723
|
-
const flowId = url.searchParams.get("flowId") || undefined;
|
|
724
|
-
const limit = parseInt(url.searchParams.get("limit") || "100", 10);
|
|
725
|
-
return Response.json(this.getFlowHistory(flowId, limit));
|
|
726
|
-
}
|
|
727
|
-
// GET /summary - Get conversation summary
|
|
728
|
-
if (url.pathname === "/summary" && request.method === "GET") {
|
|
729
|
-
const summary = extractConversationSummary(batchChatToAria(this.messages));
|
|
730
|
-
return Response.json(summary);
|
|
731
|
-
}
|
|
732
|
-
return super.onRequest(request);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
//# sourceMappingURL=AriaAgent.js.map
|