@economic/agents 2.0.1 → 2.1.1

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/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-a-1XBTxr.mjs";
1
+ import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-BKgBMVF0.mjs";
2
2
  import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
3
3
  import { Agent as Agent$1, callable, getCurrentAgent, routeAgentRequest as routeAgentRequest$1 } from "agents";
4
4
  import { AIChatAgent } from "@cloudflare/ai-chat";
@@ -359,7 +359,7 @@ var Agent = class extends Agent$1 {
359
359
  isEnabled: true,
360
360
  tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
361
361
  agentName: this.constructor.name,
362
- conversationId: this.name,
362
+ chatId: this.name,
363
363
  userId: this.getUserId(),
364
364
  ...this.clientIp ? { clientIp: this.clientIp } : {},
365
365
  ...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {}
@@ -367,7 +367,7 @@ var Agent = class extends Agent$1 {
367
367
  metadata: {
368
368
  agentName: this.constructor.name,
369
369
  version: "v1",
370
- conversationId: this.name,
370
+ chatId: this.name,
371
371
  userId: this.getUserId(),
372
372
  ...this.clientIp ? { clientIp: this.clientIp } : {},
373
373
  ...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {},
@@ -769,7 +769,7 @@ var ChatAgent = class extends AIChatAgent {
769
769
  isEnabled: true,
770
770
  tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
771
771
  agentName: this.constructor.name,
772
- conversationId: this.name,
772
+ chatId: this.name,
773
773
  userId: this.getUserId(),
774
774
  ...this.clientIp ? { clientIp: this.clientIp } : {},
775
775
  ...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {}
@@ -777,7 +777,7 @@ var ChatAgent = class extends AIChatAgent {
777
777
  metadata: {
778
778
  agentName: this.constructor.name,
779
779
  version: "v1",
780
- conversationId: this.name,
780
+ chatId: this.name,
781
781
  userId: this.getUserId(),
782
782
  ...this.clientIp ? { clientIp: this.clientIp } : {},
783
783
  ...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {},
@@ -125,12 +125,12 @@ function parseJson(value) {
125
125
  function safePathSegment(value, fallback) {
126
126
  return (value || fallback).replace(/[^a-zA-Z0-9._-]/g, "_");
127
127
  }
128
- function createAuditLogKey(agentName, conversationId) {
128
+ function createAuditLogKey(agentName, chatId) {
129
129
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
130
130
  const id = crypto.randomUUID().slice(0, 8);
131
131
  return [
132
132
  safePathSegment(agentName, "unknown-agent"),
133
- safePathSegment(conversationId, "unknown-conversation"),
133
+ safePathSegment(chatId, "unknown-chat"),
134
134
  `${timestamp}-${id}.json`
135
135
  ].join("/");
136
136
  }
@@ -186,15 +186,15 @@ function pushToolCall(map, key, toolCall) {
186
186
  else map.set(key, [toolCall]);
187
187
  }
188
188
  /**
189
- * Stores the last ai.streamText.doStream span per conversation.
190
- * Its ai.prompt.messages contains the full conversation history including all
189
+ * Stores the last ai.streamText.doStream span per chat.
190
+ * Its ai.prompt.messages contains the full chat history including all
191
191
  * intermediate tool_use + tool_result turns from the agentic loop.
192
192
  */
193
- const lastDoStreamByConversation = /* @__PURE__ */ new Map();
193
+ const lastDoStreamByChat = /* @__PURE__ */ new Map();
194
194
  const toolCallsByParentSpan = /* @__PURE__ */ new Map();
195
- const toolCallsByConversation = /* @__PURE__ */ new Map();
195
+ const toolCallsByChat = /* @__PURE__ */ new Map();
196
196
  const pendingToolCalls = [];
197
- const currentSkillByConversation = /* @__PURE__ */ new Map();
197
+ const currentSkillByChat = /* @__PURE__ */ new Map();
198
198
  function rememberToolCall(span) {
199
199
  const parentSpanId = span.parentSpanContext?.spanId;
200
200
  const toolName = stringAttribute(span, "ai.toolCall.name");
@@ -208,34 +208,34 @@ function rememberToolCall(span) {
208
208
  if (parentSpanId) pushToolCall(toolCallsByParentSpan, parentSpanId, toolCall);
209
209
  pendingToolCalls.push(toolCall);
210
210
  }
211
- function attachToolCallsToConversation(span, conversationId) {
211
+ function attachToolCallsToChat(span, chatId) {
212
212
  const spanId = span.spanContext().spanId;
213
213
  const toolCalls = toolCallsByParentSpan.get(spanId) ?? pendingToolCalls.splice(0);
214
214
  if (!toolCalls.length) return;
215
215
  toolCallsByParentSpan.delete(spanId);
216
- const currentSkill = currentSkillByConversation.get(conversationId);
216
+ const currentSkill = currentSkillByChat.get(chatId);
217
217
  const attributedToolCalls = toolCalls.map((toolCall) => {
218
218
  const skillName = toolCall.skillName ?? currentSkill;
219
- if (toolCall.skillName) currentSkillByConversation.set(conversationId, toolCall.skillName);
219
+ if (toolCall.skillName) currentSkillByChat.set(chatId, toolCall.skillName);
220
220
  return {
221
221
  ...toolCall,
222
222
  ...skillName ? { skillName } : {}
223
223
  };
224
224
  });
225
- toolCallsByConversation.set(conversationId, [...toolCallsByConversation.get(conversationId) ?? [], ...attributedToolCalls]);
225
+ toolCallsByChat.set(chatId, [...toolCallsByChat.get(chatId) ?? [], ...attributedToolCalls]);
226
226
  }
227
227
  function buildAuditLog(span, context) {
228
- const lastDoStream = lastDoStreamByConversation.get(context.conversationId);
229
- lastDoStreamByConversation.delete(context.conversationId);
228
+ const lastDoStream = lastDoStreamByChat.get(context.chatId);
229
+ lastDoStreamByChat.delete(context.chatId);
230
230
  const promptMessages = parseJson(stringAttribute(lastDoStream ?? span, "ai.prompt.messages"));
231
231
  const fallbackPrompt = parseJson(stringAttribute(span, "ai.prompt"));
232
232
  const inputMessages = promptMessages ?? fallbackPrompt?.messages ?? [];
233
- const spanToolCalls = toolCallsByConversation.get(context.conversationId) ?? [];
234
- toolCallsByConversation.delete(context.conversationId);
233
+ const spanToolCalls = toolCallsByChat.get(context.chatId) ?? [];
234
+ toolCallsByChat.delete(context.chatId);
235
235
  return {
236
- id: createAuditLogKey(context.agentName, context.conversationId),
236
+ id: createAuditLogKey(context.agentName, context.chatId),
237
237
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
238
- conversationId: context.conversationId,
238
+ chatId: context.chatId,
239
239
  agent: {
240
240
  name: context.agentName,
241
241
  llm: {
@@ -256,8 +256,8 @@ function buildAuditLog(span, context) {
256
256
  tools: [...extractTools(inputMessages), ...spanToolCalls]
257
257
  };
258
258
  }
259
- function rememberDoStream(span, conversationId) {
260
- lastDoStreamByConversation.set(conversationId, span);
259
+ function rememberDoStream(span, chatId) {
260
+ lastDoStreamByChat.set(chatId, span);
261
261
  }
262
262
  async function handleAuditSpan(span, auditLogs, context) {
263
263
  const auditLog = buildAuditLog(span, context);
@@ -284,7 +284,7 @@ function handleAnalyticsSpan(span, analytics) {
284
284
  blobs: [
285
285
  "llm_call",
286
286
  stringAttribute(span, "ai.telemetry.metadata.agentName") ?? "",
287
- stringAttribute(span, "ai.telemetry.metadata.conversationId") ?? "",
287
+ stringAttribute(span, "ai.telemetry.metadata.chatId") ?? "",
288
288
  stringAttribute(span, "ai.model.id") ?? "",
289
289
  stringAttribute(span, "ai.model.provider") ?? "",
290
290
  stringAttribute(span, "ai.response.finishReason") ?? ""
@@ -305,15 +305,15 @@ function handleAnalyticsSpan(span, analytics) {
305
305
  if (span.name === "ai.toolCall") {
306
306
  const toolName = stringAttribute(span, "ai.toolCall.name");
307
307
  const toolInput = parseJson(stringAttribute(span, "ai.toolCall.args"));
308
- const conversationId = stringAttribute(span, "ai.telemetry.metadata.conversationId") ?? "";
309
- const skillName = extractSkillName(toolName, toolInput) ?? currentSkillByConversation.get(conversationId) ?? "";
308
+ const chatId = stringAttribute(span, "ai.telemetry.metadata.chatId") ?? "";
309
+ const skillName = extractSkillName(toolName, toolInput) ?? currentSkillByChat.get(chatId) ?? "";
310
310
  const success = span.status.code === 0;
311
311
  writeAnalyticsDatapoint(analytics, {
312
312
  indexes: [stringAttribute(span, "ai.telemetry.metadata.userId") ?? ""],
313
313
  blobs: [
314
314
  "tool_call",
315
315
  stringAttribute(span, "ai.telemetry.metadata.agentName") ?? "",
316
- conversationId,
316
+ chatId,
317
317
  toolName ?? "",
318
318
  skillName,
319
319
  success ? "success" : "error"
@@ -346,8 +346,8 @@ var AgentSpanExporter = class {
346
346
  (async () => {
347
347
  try {
348
348
  for (const span of spans) if (span.name === "ai.streamText.doStream") {
349
- rememberDoStream(span, this.context.conversationId);
350
- attachToolCallsToConversation(span, this.context.conversationId);
349
+ rememberDoStream(span, this.context.chatId);
350
+ attachToolCallsToChat(span, this.context.chatId);
351
351
  handleAnalyticsSpan(span, this.analytics);
352
352
  } else if (span.name === "ai.streamText") await handleAuditSpan(span, this.auditLogs, this.context);
353
353
  else if (span.name === "ai.toolCall") {
package/dist/v2.d.mts CHANGED
@@ -19,6 +19,13 @@ interface AgentEnv {
19
19
  AGENTS_ANALYTICS: AnalyticsEngineDataset;
20
20
  SKILLS_BUCKET: R2Bucket;
21
21
  }
22
+ type AgentConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized";
23
+ type AgentConnectionType = "agent" | "chat" | "assistant";
24
+ type AgentConnectionState = {
25
+ status: AgentConnectionStatus;
26
+ type: AgentConnectionType; /** Set by `Assistant` so clients can derive the sub-agent routing name from state. */
27
+ subAgentName?: string;
28
+ };
22
29
  //#endregion
23
30
  //#region src/server/v2/util/tools.d.ts
24
31
  type ToolSet = Record<string, Tool>;
@@ -39,12 +46,6 @@ declare function skill<Context extends Record<string, unknown> = Record<string,
39
46
  //#endregion
40
47
  //#region src/server/v2/agents/Agent.d.ts
41
48
  declare function getCurrentToolContext(): any;
42
- type AgentConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized";
43
- type AgentConnectionType = "agent" | "chat" | "assistant";
44
- type AgentConnectionState = {
45
- status: AgentConnectionStatus;
46
- type: AgentConnectionType;
47
- };
48
49
  declare abstract class Agent<RequestContext extends Record<string, unknown> = Record<string, unknown>, UserContext extends Record<string, unknown> = Record<string, unknown>> extends Think<Cloudflare.Env & AgentEnv, AgentConnectionState> {
49
50
  initialState: AgentConnectionState;
50
51
  protected clientIp?: string;
@@ -58,6 +59,7 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
58
59
  abstract getSystemPrompt(ctx?: ToolContext<RequestContext, UserContext>): string;
59
60
  configureSession(session: Session): Session;
60
61
  onStart(): Promise<void>;
62
+ onClose(): Promise<void>;
61
63
  onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
62
64
  /**
63
65
  * Merges the client request `body` into `experimental_context` for tools
@@ -130,21 +132,21 @@ declare abstract class ChatAgent<RequestContext extends Record<string, unknown>
130
132
  */
131
133
  rateMessage(messageId: string, rating: number, comment?: string): Promise<void>;
132
134
  /**
133
- * Returns all message ratings for the current conversation.
134
- * @returns All message ratings for the current conversation.
135
+ * Returns all message ratings for the current chat.
136
+ * @returns All message ratings for the current chat.
135
137
  */
136
138
  getMessageRatings(): Promise<any>;
137
139
  }
138
140
  //#endregion
139
- //#region src/server/v2/features/conversations.d.ts
140
- type Conversation = {
141
+ //#region src/server/v2/features/chats.d.ts
142
+ type Chat = {
141
143
  durable_object_name: string;
142
144
  title?: string;
143
145
  summary?: string;
144
146
  created_at: number;
145
147
  updated_at: number;
146
148
  };
147
- declare const DELETE_CONVERSATION_CALLBACK: "deleteConversationCallback";
149
+ declare const DELETE_CHAT_CALLBACK: "deleteChatCallback";
148
150
  //#endregion
149
151
  //#region src/server/v2/agents/Assistant.d.ts
150
152
  declare abstract class Assistant extends Agent$1<Cloudflare.Env, AgentConnectionState> {
@@ -152,13 +154,14 @@ declare abstract class Assistant extends Agent$1<Cloudflare.Env, AgentConnection
152
154
  protected abstract agent: SubAgentClass<ChatAgent>;
153
155
  protected abstract fastModel: LanguageModel;
154
156
  onStart(): void;
157
+ onClose(): Promise<void>;
155
158
  onConnect(): Promise<void>;
156
- createConversation(): Promise<string>;
157
- deleteConversation(id: string): Promise<void>;
158
- getConversations(): Promise<Conversation[]>;
159
- recordConversationTurn(durableObjectName: string, messages: UIMessage[]): Promise<void>;
160
- private [DELETE_CONVERSATION_CALLBACK];
161
- private scheduleConversationForAutoDeletion;
159
+ createChat(): Promise<string>;
160
+ deleteChat(id: string): Promise<void>;
161
+ getChats(): Promise<Chat[]>;
162
+ recordChatTurn(durableObjectName: string, messages: UIMessage[]): Promise<void>;
163
+ private [DELETE_CHAT_CALLBACK];
164
+ private scheduleChatForAutoDeletion;
162
165
  }
163
166
  //#endregion
164
- export { Agent, type AgentEnv, Assistant, ChatAgent, type Skill, type Tool, type ToolContext, type ToolSet, getCurrentToolContext, skill, tool };
167
+ export { Agent, type AgentConnectionState, type AgentConnectionStatus, type AgentConnectionType, type AgentEnv, Assistant, ChatAgent, type Skill, type Tool, type ToolContext, type ToolSet, getCurrentToolContext, skill, tool };
package/dist/v2.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-a-1XBTxr.mjs";
1
+ import { n as extractTokenFromConnectRequest, r as verifyJwt, t as createAgentTracer } from "./telemetry-BKgBMVF0.mjs";
2
2
  import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages, tool as tool$1 } from "ai";
3
3
  import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
4
4
  import { Think } from "@cloudflare/think";
5
5
  import { R2SkillProvider } from "agents/experimental/memory/session";
6
- import { createCompactFunction } from "agents/experimental/memory/utils";
7
6
  import { nanoid } from "nanoid";
7
+ import { createCompactFunction } from "agents/experimental/memory/utils";
8
8
  //#region src/server/v2/util/tools.ts
9
9
  function tool(tool) {
10
10
  return tool$1(tool);
@@ -106,6 +106,12 @@ var Agent = class extends Think {
106
106
  throw new Error("Could not connect to agent, bindings not found");
107
107
  }
108
108
  }
109
+ async onClose() {
110
+ this.setState({
111
+ ...this.initialState,
112
+ status: "disconnected"
113
+ });
114
+ }
109
115
  async onConnect(connection, ctx) {
110
116
  this.clientIp = ctx.request.headers.get("CF-Connecting-IP") ?? ctx.request.headers.get("X-Forwarded-For")?.split(",")[0]?.trim();
111
117
  this.forwardedFor = ctx.request.headers.get("X-Forwarded-For") ?? void 0;
@@ -179,7 +185,7 @@ var Agent = class extends Think {
179
185
  isEnabled: true,
180
186
  tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
181
187
  agentName: this.constructor.name,
182
- conversationId: this.name,
188
+ chatId: this.name,
183
189
  userId: this.getUserIdFromDurableObjectName(),
184
190
  clientIp: this.clientIp,
185
191
  forwardedFor: this.forwardedFor
@@ -187,7 +193,7 @@ var Agent = class extends Think {
187
193
  metadata: {
188
194
  agentName: this.constructor.name,
189
195
  version: "v2",
190
- conversationId: this.name,
196
+ chatId: this.name,
191
197
  userId: this.getUserIdFromDurableObjectName()
192
198
  }
193
199
  }
@@ -271,77 +277,14 @@ var Agent = class extends Think {
271
277
  _userContext;
272
278
  };
273
279
  //#endregion
274
- //#region src/server/v2/features/messages.ts
275
- const COMPACTION_TOKEN_THRESHOLD = 1e5;
276
- const createCompactFn = (model) => createCompactFunction({ summarize: (prompt) => generateText({
277
- model,
278
- prompt
279
- }).then((r) => r.text) });
280
- /**
281
- * Ensures that the ratings table exists.
282
- * @param sql - The SQL function to use to execute the query.
283
- */
284
- function ensureRatingsTableExists(sql) {
285
- try {
286
- sql`CREATE TABLE IF NOT EXISTS assistant_messages_ratings (
287
- message_id TEXT NOT NULL,
288
- durable_object_name TEXT NOT NULL,
289
- rating INTEGER,
290
- comment TEXT,
291
- created_at TEXT NOT NULL,
292
- updated_at TEXT NOT NULL,
293
- PRIMARY KEY (message_id, durable_object_name)
294
- )`;
295
- } catch (error) {
296
- console.error("[Agent] Failed to create ratings table", error);
297
- }
298
- }
299
- /**
300
- * Rates a message.
301
- * @param sql - The SQL function to use to execute the query.
302
- * @param messageId - The ID of the message to rate.
303
- * @param durable_object_name - The name of the Durable Object to rate the message for.
304
- * @param rating - The rating to give the message.
305
- * @param now - The date and time to use for the created_at and updated_at columns.
306
- */
307
- function rateMessage(sql, messageId, durable_object_name, rating, comment, now = /* @__PURE__ */ new Date()) {
308
- try {
309
- sql`INSERT INTO assistant_messages_ratings (message_id, durable_object_name, rating, comment, created_at, updated_at)
310
- VALUES (${messageId}, ${durable_object_name}, ${rating}, ${comment ?? null}, ${now.toISOString()}, ${now.toISOString()})
311
- ON CONFLICT (message_id, durable_object_name) DO UPDATE SET
312
- rating = excluded.rating,
313
- comment = excluded.comment,
314
- updated_at = excluded.updated_at`;
315
- } catch (error) {
316
- console.error("[Agent] Failed to rate message", error);
317
- }
318
- }
319
- /**
320
- * Gets the ratings for a message.
321
- * @param sql - The SQL function to use to execute the query.
322
- * @param durable_object_name - The name of the Durable Object to get the ratings for.
323
- * @returns A record of message IDs and their ratings.
324
- */
325
- function getMessageRatings(sql, durable_object_name) {
326
- try {
327
- const ratings = sql`SELECT message_id, rating, comment FROM assistant_messages_ratings WHERE durable_object_name = ${durable_object_name}`;
328
- return Object.fromEntries(ratings.map((row) => [row.message_id, {
329
- rating: row.rating,
330
- comment: row.comment
331
- }]));
332
- } catch {
333
- return {};
334
- }
335
- }
336
- //#endregion
337
- //#region src/server/v2/features/conversations.ts
280
+ //#region src/server/v2/features/chats.ts
338
281
  /**
339
- * Ensures that the conversations table exists.
282
+ * Ensures that the chats table exists.
340
283
  * @param sql - The SQL function to use to execute the query.
341
284
  */
342
- function ensureConversationsTableExists(sql) {
285
+ function ensureChatsTableExists(sql) {
343
286
  try {
344
- sql`CREATE TABLE IF NOT EXISTS conversations (
287
+ sql`CREATE TABLE IF NOT EXISTS chats (
345
288
  durable_object_name TEXT NOT NULL,
346
289
  title TEXT,
347
290
  summary TEXT,
@@ -350,80 +293,80 @@ function ensureConversationsTableExists(sql) {
350
293
  PRIMARY KEY (durable_object_name)
351
294
  )`;
352
295
  } catch (error) {
353
- console.error("[Agent] Failed to create conversations table", error);
296
+ console.error("[Agent] Failed to create chats table", error);
354
297
  }
355
298
  }
356
- function registerConversation(sql, durableObjectName, dateTime) {
357
- sql`INSERT INTO conversations (durable_object_name, created_at, updated_at)
299
+ function registerChat(sql, durableObjectName, dateTime) {
300
+ sql`INSERT INTO chats (durable_object_name, created_at, updated_at)
358
301
  VALUES (${durableObjectName}, ${dateTime}, ${dateTime})`;
359
302
  }
360
- function deleteConversation(sql, durableObjectName) {
361
- sql`DELETE FROM conversations WHERE durable_object_name = ${durableObjectName}`;
303
+ function deleteChat(sql, durableObjectName) {
304
+ sql`DELETE FROM chats WHERE durable_object_name = ${durableObjectName}`;
362
305
  }
363
- function getConversation(sql, durableObjectName) {
364
- return sql`SELECT * FROM conversations WHERE durable_object_name = ${durableObjectName}`[0] ?? null;
306
+ function getChat(sql, durableObjectName) {
307
+ return sql`SELECT * FROM chats WHERE durable_object_name = ${durableObjectName}`[0] ?? null;
365
308
  }
366
- async function getConversations(sql) {
367
- return sql`SELECT * FROM conversations ORDER BY updated_at DESC`;
309
+ async function getChats(sql) {
310
+ return sql`SELECT * FROM chats ORDER BY updated_at DESC`;
368
311
  }
369
- function getDeleteConversationScheduleIds(schedules) {
370
- return schedules.filter((schedule) => schedule.callback === DELETE_CONVERSATION_CALLBACK).map((schedule) => schedule.id);
312
+ function getDeleteChatScheduleIds(schedules) {
313
+ return schedules.filter((schedule) => schedule.callback === DELETE_CHAT_CALLBACK).map((schedule) => schedule.id);
371
314
  }
372
315
  /**
373
316
  * Number of recent messages passed to `generateSummary` for rolling
374
317
  * summarization. Keeping this bounded prevents the prompt growing
375
- * unboundedly regardless of conversation length.
318
+ * unboundedly regardless of chat length.
376
319
  */
377
- const CONVERSATION_RECENT_MESSAGES_COUNT = 20;
378
- async function summariseConversationWithAI(sql, durableObjectName, messages, model) {
379
- const conversation = getConversation(sql, durableObjectName);
380
- if (!(!conversation || !conversation.title || messages.length % CONVERSATION_RECENT_MESSAGES_COUNT === 0)) return;
320
+ const CHAT_RECENT_MESSAGES_COUNT = 20;
321
+ async function summariseChatWithAI(sql, durableObjectName, messages, model) {
322
+ const chat = getChat(sql, durableObjectName);
323
+ if (!(!chat || !chat.title || messages.length % CHAT_RECENT_MESSAGES_COUNT === 0)) return;
381
324
  let systemPrompt = `
382
- You are a helpful assistant that summarises conversations.
383
- You will be given a list of messages and you need to generate a title and summary for the conversation.
384
- The title should be a short title for the conversation, max 8-10 words.
385
- The summary should be a short 1-2 sentence summary of the conversation.
386
- The summary should reflect the direction of the conversation.`;
387
- if (conversation) systemPrompt += `${systemPrompt}\n\nThe previous summary: ${conversation.summary}`;
325
+ You are a helpful assistant that summarises chats.
326
+ You will be given a list of messages and you need to generate a title and summary for the chat.
327
+ The title should be a short title for the chat, max 8-10 words.
328
+ The summary should be a short 1-2 sentence summary of the chat.
329
+ The summary should reflect the direction of the chat.`;
330
+ if (chat) systemPrompt += `${systemPrompt}\n\nThe previous summary: ${chat.summary}`;
388
331
  try {
389
332
  const { output: { title, summary } } = await generateText({
390
333
  model,
391
334
  system: systemPrompt,
392
- messages: await convertToModelMessages(messages.slice(-CONVERSATION_RECENT_MESSAGES_COUNT)),
335
+ messages: await convertToModelMessages(messages.slice(-CHAT_RECENT_MESSAGES_COUNT)),
393
336
  output: Output.object({ schema: jsonSchema({
394
337
  type: "object",
395
338
  properties: {
396
339
  title: {
397
340
  type: "string",
398
- description: "Short title for the conversation, max 8-10 words"
341
+ description: "Short title for the chat, max 8-10 words"
399
342
  },
400
343
  summary: {
401
344
  type: "string",
402
- description: "A short 1-2 sentence summary of the conversation. If the conversation direction has changed from the previous summary, reflect the new direction."
345
+ description: "A short 1-2 sentence summary of the chat. If the chat direction has changed from the previous summary, reflect the new direction."
403
346
  }
404
347
  },
405
348
  required: ["title", "summary"]
406
349
  }) })
407
350
  });
408
351
  if (!title || !summary) {
409
- console.error("[Assistant] Failed to generate conversation title and summary", { durableObjectName });
352
+ console.error("[Assistant] Failed to generate chat title and summary", { durableObjectName });
410
353
  return;
411
354
  }
412
- sql`UPDATE conversations
355
+ sql`UPDATE chats
413
356
  SET title = ${title},
414
357
  summary = ${summary},
415
358
  updated_at = ${Date.now()}
416
359
  WHERE durable_object_name = ${durableObjectName}`;
417
- console.info("[Assistant] Generated conversation summary", { durableObjectName });
360
+ console.info("[Assistant] Generated chat summary", { durableObjectName });
418
361
  } catch (error) {
419
- console.error("[Assistant] Failed to generate conversation title and summary", {
362
+ console.error("[Assistant] Failed to generate chat title and summary", {
420
363
  durableObjectName,
421
364
  error
422
365
  });
423
366
  }
424
367
  }
425
- const DELETE_CONVERSATION_CALLBACK = "deleteConversationCallback";
426
- function getConversationRetentionMs(days) {
368
+ const DELETE_CHAT_CALLBACK = "deleteChatCallback";
369
+ function getChatRetentionMs(days) {
427
370
  if (typeof days !== "number" || !Number.isFinite(days) || days <= 0) return null;
428
371
  return Math.floor(days * 24 * 60 * 60 * 1e3);
429
372
  }
@@ -437,46 +380,118 @@ var Assistant = class extends Agent$1 {
437
380
  onStart() {
438
381
  this.setState({
439
382
  type: "assistant",
440
- status: "connecting"
383
+ status: "connecting",
384
+ subAgentName: this.agent.name
385
+ });
386
+ ensureChatsTableExists(this.sql.bind(this));
387
+ }
388
+ async onClose() {
389
+ this.setState({
390
+ type: "assistant",
391
+ status: "disconnected",
392
+ subAgentName: this.agent.name
441
393
  });
442
- ensureConversationsTableExists(this.sql.bind(this));
443
394
  }
444
395
  async onConnect() {
445
396
  this.setState({
446
397
  type: "assistant",
447
- status: "connected"
398
+ status: "connected",
399
+ subAgentName: this.agent.name
448
400
  });
449
401
  }
450
- @callable() async createConversation() {
402
+ @callable() async createChat() {
451
403
  const id = nanoid();
452
404
  const now = Date.now();
453
405
  await this.subAgent(this.agent, id);
454
- registerConversation(this.sql.bind(this), id, now);
406
+ registerChat(this.sql.bind(this), id, now);
455
407
  return id;
456
408
  }
457
- @callable() async deleteConversation(id) {
409
+ @callable() async deleteChat(id) {
458
410
  await this.deleteSubAgent(this.agent, id);
459
- deleteConversation(this.sql.bind(this), id);
411
+ deleteChat(this.sql.bind(this), id);
460
412
  }
461
- @callable() async getConversations() {
462
- return getConversations(this.sql.bind(this));
413
+ @callable() async getChats() {
414
+ return getChats(this.sql.bind(this));
463
415
  }
464
- async recordConversationTurn(durableObjectName, messages) {
465
- summariseConversationWithAI(this.sql.bind(this), durableObjectName, messages, this.fastModel);
466
- this.scheduleConversationForAutoDeletion(durableObjectName);
416
+ async recordChatTurn(durableObjectName, messages) {
417
+ summariseChatWithAI(this.sql.bind(this), durableObjectName, messages, this.fastModel);
418
+ this.scheduleChatForAutoDeletion(durableObjectName);
467
419
  }
468
- async [DELETE_CONVERSATION_CALLBACK](durableObjectName) {
469
- await this.deleteConversation(durableObjectName);
420
+ async [DELETE_CHAT_CALLBACK](durableObjectName) {
421
+ await this.deleteChat(durableObjectName);
470
422
  }
471
- async scheduleConversationForAutoDeletion(durableObjectName) {
472
- const retentionMs = getConversationRetentionMs(90);
423
+ async scheduleChatForAutoDeletion(durableObjectName) {
424
+ const retentionMs = getChatRetentionMs(90);
473
425
  if (retentionMs === null) return;
474
- const scheduleIds = getDeleteConversationScheduleIds(await this.listSchedules());
426
+ const scheduleIds = getDeleteChatScheduleIds(await this.listSchedules());
475
427
  await Promise.all(scheduleIds.map((scheduleId) => this.cancelSchedule(scheduleId)));
476
- await this.schedule(new Date(Date.now() + retentionMs), DELETE_CONVERSATION_CALLBACK, durableObjectName, { idempotent: true });
428
+ await this.schedule(new Date(Date.now() + retentionMs), DELETE_CHAT_CALLBACK, durableObjectName, { idempotent: true });
477
429
  }
478
430
  };
479
431
  //#endregion
432
+ //#region src/server/v2/features/messages.ts
433
+ const COMPACTION_TOKEN_THRESHOLD = 1e5;
434
+ const createCompactFn = (model) => createCompactFunction({ summarize: (prompt) => generateText({
435
+ model,
436
+ prompt
437
+ }).then((r) => r.text) });
438
+ /**
439
+ * Ensures that the ratings table exists.
440
+ * @param sql - The SQL function to use to execute the query.
441
+ */
442
+ function ensureRatingsTableExists(sql) {
443
+ try {
444
+ sql`CREATE TABLE IF NOT EXISTS assistant_messages_ratings (
445
+ message_id TEXT NOT NULL,
446
+ durable_object_name TEXT NOT NULL,
447
+ rating INTEGER,
448
+ comment TEXT,
449
+ created_at TEXT NOT NULL,
450
+ updated_at TEXT NOT NULL,
451
+ PRIMARY KEY (message_id, durable_object_name)
452
+ )`;
453
+ } catch (error) {
454
+ console.error("[Agent] Failed to create ratings table", error);
455
+ }
456
+ }
457
+ /**
458
+ * Rates a message.
459
+ * @param sql - The SQL function to use to execute the query.
460
+ * @param messageId - The ID of the message to rate.
461
+ * @param durable_object_name - The name of the Durable Object to rate the message for.
462
+ * @param rating - The rating to give the message.
463
+ * @param now - The date and time to use for the created_at and updated_at columns.
464
+ */
465
+ function rateMessage(sql, messageId, durable_object_name, rating, comment, now = /* @__PURE__ */ new Date()) {
466
+ try {
467
+ sql`INSERT INTO assistant_messages_ratings (message_id, durable_object_name, rating, comment, created_at, updated_at)
468
+ VALUES (${messageId}, ${durable_object_name}, ${rating}, ${comment ?? null}, ${now.toISOString()}, ${now.toISOString()})
469
+ ON CONFLICT (message_id, durable_object_name) DO UPDATE SET
470
+ rating = excluded.rating,
471
+ comment = excluded.comment,
472
+ updated_at = excluded.updated_at`;
473
+ } catch (error) {
474
+ console.error("[Agent] Failed to rate message", error);
475
+ }
476
+ }
477
+ /**
478
+ * Gets the ratings for a message.
479
+ * @param sql - The SQL function to use to execute the query.
480
+ * @param durable_object_name - The name of the Durable Object to get the ratings for.
481
+ * @returns A record of message IDs and their ratings.
482
+ */
483
+ function getMessageRatings(sql, durable_object_name) {
484
+ try {
485
+ const ratings = sql`SELECT message_id, rating, comment FROM assistant_messages_ratings WHERE durable_object_name = ${durable_object_name}`;
486
+ return Object.fromEntries(ratings.map((row) => [row.message_id, {
487
+ rating: row.rating,
488
+ comment: row.comment
489
+ }]));
490
+ } catch {
491
+ return {};
492
+ }
493
+ }
494
+ //#endregion
480
495
  //#region src/server/v2/agents/ChatAgent.ts
481
496
  var ChatAgent = class extends Agent {
482
497
  initialState = {
@@ -492,7 +507,7 @@ var ChatAgent = class extends Agent {
492
507
  }
493
508
  async onChatResponse(_result) {
494
509
  const parent = await this.getParentAgent();
495
- if (parent?.recordConversationTurn) await parent.recordConversationTurn(this.name, this.messages);
510
+ if (parent?.recordChatTurn) await parent.recordChatTurn(this.name, this.messages);
496
511
  }
497
512
  /**
498
513
  * Rate a message by its id.
@@ -504,10 +519,10 @@ var ChatAgent = class extends Agent {
504
519
  return rateMessage(this.sql.bind(this), messageId, this.name, rating, comment);
505
520
  }
506
521
  /**
507
- * Returns all message ratings for the current conversation.
508
- * @returns All message ratings for the current conversation.
522
+ * Returns all message ratings for the current chat.
523
+ * @returns All message ratings for the current chat.
509
524
  */
510
- @callable({ description: "Returns all message ratings for the current conversation" }) async getMessageRatings() {
525
+ @callable({ description: "Returns all message ratings for the current chat" }) async getMessageRatings() {
511
526
  return getMessageRatings(this.sql.bind(this), this.name);
512
527
  }
513
528
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "license": "MIT",
6
6
  "bin": {