@economic/agents 1.7.3 → 1.8.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/dist/index.d.mts +12 -33
- package/dist/index.mjs +353 -168
- package/package.json +3 -2
- package/schema/chat/0003_drop_events.sql +5 -0
package/dist/index.d.mts
CHANGED
|
@@ -48,8 +48,7 @@ declare function buildLLMParams(config: BuildLLMParamsConfig): LLMParams;
|
|
|
48
48
|
//#region src/server/v1/types.d.ts
|
|
49
49
|
/**
|
|
50
50
|
* The context object available throughout an agent's lifetime — passed via
|
|
51
|
-
* `experimental_context` to tool `execute` functions.
|
|
52
|
-
* request body merged with platform capabilities like `logEvent`.
|
|
51
|
+
* `experimental_context` to tool `execute` functions.
|
|
53
52
|
*
|
|
54
53
|
* Define your own body shape and compose:
|
|
55
54
|
* ```typescript
|
|
@@ -58,11 +57,12 @@ declare function buildLLMParams(config: BuildLLMParamsConfig): LLMParams;
|
|
|
58
57
|
* ```
|
|
59
58
|
*/
|
|
60
59
|
type AgentToolContext<TBody = Record<string, unknown>, TUserContext = Record<string, unknown> | undefined> = TBody & {
|
|
61
|
-
_logEvent: (message: string, payload?: Record<string, unknown>) => void | Promise<void>;
|
|
62
60
|
_userContext?: TUserContext;
|
|
63
61
|
};
|
|
64
62
|
interface AgentEnv {
|
|
65
63
|
AGENT_DB: D1Database;
|
|
64
|
+
AGENTS_AUDIT_LOGS: R2Bucket;
|
|
65
|
+
AGENTS_ANALYTICS: AnalyticsEngineDataset;
|
|
66
66
|
}
|
|
67
67
|
interface ChatAgentEnv extends AgentEnv {}
|
|
68
68
|
//#endregion
|
|
@@ -86,11 +86,10 @@ interface JwtAuthConfig<TClaims extends Record<string, unknown> = Record<string,
|
|
|
86
86
|
//#endregion
|
|
87
87
|
//#region src/server/v1/agent/Agent.d.ts
|
|
88
88
|
/**
|
|
89
|
-
* Base agent for Cloudflare Agents SDK Durable Objects with lazy skill loading
|
|
90
|
-
*
|
|
89
|
+
* Base agent for Cloudflare Agents SDK Durable Objects with lazy skill loading
|
|
90
|
+
* and `buildLLMParams` wiring.
|
|
91
91
|
*
|
|
92
|
-
* Handles CF infrastructure concerns: DO SQLite persistence for loaded skill state
|
|
93
|
-
* and writing audit events to D1.
|
|
92
|
+
* Handles CF infrastructure concerns: DO SQLite persistence for loaded skill state.
|
|
94
93
|
*
|
|
95
94
|
* For chat agents with message history, compaction, and conversation recording,
|
|
96
95
|
* extend {@link ChatAgent} instead.
|
|
@@ -123,20 +122,9 @@ declare abstract class Agent<Env extends Cloudflare.Env = Cloudflare.Env, TUserC
|
|
|
123
122
|
*/
|
|
124
123
|
protected getUserId(): string;
|
|
125
124
|
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
126
|
-
/**
|
|
127
|
-
* Writes an audit event to D1 if `AGENT_DB` is bound on the environment,
|
|
128
|
-
* otherwise silently does nothing.
|
|
129
|
-
*
|
|
130
|
-
* Called automatically at the end of each LLM turn (from `onFinish` in
|
|
131
|
-
* `buildLLMParams`). Also available via `experimental_context.logEvent` in tool
|
|
132
|
-
* `execute` functions.
|
|
133
|
-
*/
|
|
134
|
-
protected logEvent(message: string, payload?: Record<string, unknown>): Promise<void>;
|
|
135
125
|
/**
|
|
136
126
|
* Builds the parameter object for a `streamText` or `generateText` call,
|
|
137
127
|
* pre-filling `activeSkills` from this agent instance.
|
|
138
|
-
* Injects `logEvent` into `experimental_context` and wires `onFinish` for
|
|
139
|
-
* turn-completed audit events.
|
|
140
128
|
*/
|
|
141
129
|
protected buildLLMParams<TBody = Record<string, unknown>>(config: Omit<BuildLLMParamsConfig, "messages"> & {
|
|
142
130
|
options?: OnChatMessageOptions;
|
|
@@ -151,12 +139,12 @@ interface MessageRating {
|
|
|
151
139
|
//#endregion
|
|
152
140
|
//#region src/server/v1/agent-chat/ChatAgent.d.ts
|
|
153
141
|
/**
|
|
154
|
-
* Chat agent for Cloudflare Agents SDK: lazy skill loading,
|
|
155
|
-
*
|
|
142
|
+
* Chat agent for Cloudflare Agents SDK: lazy skill loading, message persistence,
|
|
143
|
+
* compaction, and conversation metadata in D1.
|
|
156
144
|
*
|
|
157
145
|
* Handles CF infrastructure concerns: DO SQLite for loaded skill state,
|
|
158
|
-
* stripping skill meta-tool messages before persistence, history replay to
|
|
159
|
-
* newly connected clients
|
|
146
|
+
* stripping skill meta-tool messages before persistence, and history replay to
|
|
147
|
+
* newly connected clients.
|
|
160
148
|
*
|
|
161
149
|
* Skill loading, compaction, and LLM calls use `buildLLMParams` from
|
|
162
150
|
* `@economic/agents` inside `onChatMessage`.
|
|
@@ -197,6 +185,8 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env, TU
|
|
|
197
185
|
* Default is 15.
|
|
198
186
|
*/
|
|
199
187
|
protected maxMessagesBeforeCompaction?: number | undefined;
|
|
188
|
+
protected clientIp?: string;
|
|
189
|
+
protected forwardedFor?: string;
|
|
200
190
|
/**
|
|
201
191
|
* Override to enable JWT authentication on WebSocket connections.
|
|
202
192
|
* Return the auth config based on the incoming request, or undefined to skip auth.
|
|
@@ -223,20 +213,9 @@ declare abstract class ChatAgent<Env extends Cloudflare.Env = Cloudflare.Env, TU
|
|
|
223
213
|
protected getUserId(): string;
|
|
224
214
|
onConnect(connection: Connection, ctx: ConnectionContext): Promise<void>;
|
|
225
215
|
protected _pendingUserContextRequest?: Promise<void>;
|
|
226
|
-
/**
|
|
227
|
-
* Writes an audit event to D1 if `AGENT_DB` is bound on the environment,
|
|
228
|
-
* otherwise silently does nothing.
|
|
229
|
-
*
|
|
230
|
-
* Called automatically at the end of each LLM turn (from `onFinish` in
|
|
231
|
-
* `buildLLMParams`). Also available via `experimental_context.logEvent` in tool
|
|
232
|
-
* `execute` functions.
|
|
233
|
-
*/
|
|
234
|
-
protected logEvent(message: string, payload?: Record<string, unknown>): Promise<void>;
|
|
235
216
|
/**
|
|
236
217
|
* Builds the parameter object for a `streamText` or `generateText` call,
|
|
237
218
|
* pre-filling `messages`, `activeSkills`, and `fastModel` from this agent instance.
|
|
238
|
-
* Injects `logEvent` into `experimental_context` and wires `onFinish` for
|
|
239
|
-
* turn-completed audit events and conversation recording.
|
|
240
219
|
*
|
|
241
220
|
* **Compaction** runs automatically when `fastModel` is set on the class, using
|
|
242
221
|
* `DEFAULT_MAX_MESSAGES_BEFORE_COMPACTION` (30) as the threshold. Override the
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs,
|
|
|
2
2
|
import { Agent as Agent$1, callable, getCurrentAgent, routeAgentRequest as routeAgentRequest$1 } from "agents";
|
|
3
3
|
import { AIChatAgent } from "@cloudflare/ai-chat";
|
|
4
4
|
import { createRemoteJWKSet, decodeJwt, errors, jwtVerify } from "jose";
|
|
5
|
+
import { BasicTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
5
6
|
//#region src/server/shared/features/skills/index.ts
|
|
6
7
|
const TOOL_NAME_ACTIVATE_SKILL = "activate_skill";
|
|
7
8
|
const TOOL_NAME_LIST_CAPABILITIES = "list_capabilities";
|
|
@@ -269,98 +270,6 @@ async function routeAgentRequest(request, env, options) {
|
|
|
269
270
|
return response;
|
|
270
271
|
}
|
|
271
272
|
//#endregion
|
|
272
|
-
//#region src/server/shared/features/audit/audit.ts
|
|
273
|
-
/**
|
|
274
|
-
* Inserts a single audit event row into the shared `audit_events` D1 table.
|
|
275
|
-
*
|
|
276
|
-
* Called by `ChatAgentHarness.logEvent()` (and `AgentHarness.logEvent()`). Not intended for direct use.
|
|
277
|
-
*/
|
|
278
|
-
const SENSITIVE_KEYS = /^(password|token|secret|api_key|apikey|authorization|credentials)$/i;
|
|
279
|
-
const REDACTED = "[REDACTED]";
|
|
280
|
-
/** Deep-clone and redact values for keys that look like secrets (for audit logging). */
|
|
281
|
-
function sanitizePayload(value) {
|
|
282
|
-
if (value === null || value === void 0) return value;
|
|
283
|
-
if (Array.isArray(value)) return value.map(sanitizePayload);
|
|
284
|
-
if (typeof value === "object") {
|
|
285
|
-
const result = {};
|
|
286
|
-
for (const [key, val] of Object.entries(value)) result[key] = SENSITIVE_KEYS.test(key) ? REDACTED : sanitizePayload(val);
|
|
287
|
-
return result;
|
|
288
|
-
}
|
|
289
|
-
return value;
|
|
290
|
-
}
|
|
291
|
-
async function insertAuditEvent(db, durableObjectName, message, payload) {
|
|
292
|
-
await db.prepare(`INSERT INTO audit_events (id, durable_object_name, message, payload, created_at)
|
|
293
|
-
VALUES (?, ?, ?, ?, ?)`).bind(crypto.randomUUID(), durableObjectName, message, payload ? JSON.stringify(sanitizePayload(payload)) : null, (/* @__PURE__ */ new Date()).toISOString()).run();
|
|
294
|
-
}
|
|
295
|
-
function stringifyForSkillScan(output) {
|
|
296
|
-
if (typeof output === "string") return output;
|
|
297
|
-
if (output === null || output === void 0) return "";
|
|
298
|
-
try {
|
|
299
|
-
return JSON.stringify(output);
|
|
300
|
-
} catch {
|
|
301
|
-
return "";
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
function forEachStep(event, fn) {
|
|
305
|
-
if (event.steps.length > 0) for (const step of event.steps) fn(step);
|
|
306
|
-
else fn(event);
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* User text from provider request body (`contents` is e.g. Gemini format).
|
|
310
|
-
*/
|
|
311
|
-
function extractUserInputFromRequestBody(body) {
|
|
312
|
-
const firstContent = body?.contents?.[0];
|
|
313
|
-
if (firstContent?.role !== "user" || !firstContent.parts?.length) return "";
|
|
314
|
-
return firstContent.parts.map((p) => p.text).filter((t) => typeof t === "string").join(" ").trim();
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Builds the payload for a "Turn completed" audit event from the AI SDK
|
|
318
|
-
* `OnFinishEvent`.
|
|
319
|
-
*
|
|
320
|
-
* Returns:
|
|
321
|
-
* - `input`: user message text from `request.body.contents[0]` (Gemini-style)
|
|
322
|
-
* - `output`: assistant response text (truncated to 200 chars)
|
|
323
|
-
* - `toolCalls`: array of { toolName, toolInput, toolOutput }
|
|
324
|
-
* - `loadedSkills`: skill names extracted from activate_skill results
|
|
325
|
-
*/
|
|
326
|
-
function buildTurnLogPayload(event) {
|
|
327
|
-
const toolCalls = [];
|
|
328
|
-
let latestSkills;
|
|
329
|
-
const toolOutputs = /* @__PURE__ */ new Map();
|
|
330
|
-
forEachStep(event, (step) => {
|
|
331
|
-
for (const tr of step.toolResults) toolOutputs.set(tr.toolCallId, tr.output);
|
|
332
|
-
});
|
|
333
|
-
forEachStep(event, (step) => {
|
|
334
|
-
for (const tc of step.toolCalls) toolCalls.push({
|
|
335
|
-
name: tc.toolName,
|
|
336
|
-
input: tc.input,
|
|
337
|
-
output: toolOutputs.get(tc.toolCallId)
|
|
338
|
-
});
|
|
339
|
-
const considerToolResultForSkills = (toolName, output) => {
|
|
340
|
-
if (toolName !== "activate_skill") return;
|
|
341
|
-
const s = stringifyForSkillScan(output);
|
|
342
|
-
const sentinelIdx = s.indexOf(SKILL_STATE_SENTINEL);
|
|
343
|
-
if (sentinelIdx === -1) return;
|
|
344
|
-
try {
|
|
345
|
-
const stateJson = s.slice(sentinelIdx + 18);
|
|
346
|
-
latestSkills = JSON.parse(stateJson);
|
|
347
|
-
} catch {}
|
|
348
|
-
};
|
|
349
|
-
for (const tr of step.toolResults) considerToolResultForSkills(tr.toolName, tr.output);
|
|
350
|
-
});
|
|
351
|
-
const input = extractUserInputFromRequestBody(event.request?.body);
|
|
352
|
-
return {
|
|
353
|
-
detail: {
|
|
354
|
-
model: event.model.modelId,
|
|
355
|
-
tokens: event.usage?.totalTokens
|
|
356
|
-
},
|
|
357
|
-
loadedSkills: latestSkills ?? [],
|
|
358
|
-
toolCalls,
|
|
359
|
-
input: input.slice(0, 200),
|
|
360
|
-
output: (event.text ?? "").slice(0, 200)
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
//#endregion
|
|
364
273
|
//#region src/server/shared/features/auth/index.ts
|
|
365
274
|
const jwksByIssuer = /* @__PURE__ */ new Map();
|
|
366
275
|
function getJwksForIssuer(issuer) {
|
|
@@ -461,13 +370,281 @@ async function verifyJwt(request, config) {
|
|
|
461
370
|
};
|
|
462
371
|
}
|
|
463
372
|
//#endregion
|
|
373
|
+
//#region src/server/shared/features/telemetry/utils.ts
|
|
374
|
+
function durationMs(duration) {
|
|
375
|
+
return duration[0] * 1e3 + duration[1] / 1e6;
|
|
376
|
+
}
|
|
377
|
+
function stringAttribute(span, key) {
|
|
378
|
+
const value = span.attributes[key];
|
|
379
|
+
return typeof value === "string" ? value : void 0;
|
|
380
|
+
}
|
|
381
|
+
function numberAttribute(span, key) {
|
|
382
|
+
const value = span.attributes[key];
|
|
383
|
+
return typeof value === "number" ? value : void 0;
|
|
384
|
+
}
|
|
385
|
+
function parseJson(value) {
|
|
386
|
+
if (!value) return;
|
|
387
|
+
try {
|
|
388
|
+
return JSON.parse(value);
|
|
389
|
+
} catch {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/server/shared/features/telemetry/audit-logs.ts
|
|
395
|
+
function safePathSegment(value, fallback) {
|
|
396
|
+
return (value || fallback).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
397
|
+
}
|
|
398
|
+
function createAuditLogKey(agentName, conversationId) {
|
|
399
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
400
|
+
const id = crypto.randomUUID().slice(0, 8);
|
|
401
|
+
return [
|
|
402
|
+
safePathSegment(agentName, "unknown-agent"),
|
|
403
|
+
safePathSegment(conversationId, "unknown-conversation"),
|
|
404
|
+
`${timestamp}-${id}.json`
|
|
405
|
+
].join("/");
|
|
406
|
+
}
|
|
407
|
+
function textFromContent(content) {
|
|
408
|
+
if (typeof content === "string") return content;
|
|
409
|
+
if (Array.isArray(content)) return content.map((part) => {
|
|
410
|
+
if (!part || typeof part !== "object") return "";
|
|
411
|
+
const value = part.text;
|
|
412
|
+
return typeof value === "string" ? value : "";
|
|
413
|
+
}).filter(Boolean).join("\n");
|
|
414
|
+
return "";
|
|
415
|
+
}
|
|
416
|
+
function extractPrompt(messages) {
|
|
417
|
+
const firstUserMessage = messages.find((message) => message.role === "user");
|
|
418
|
+
return firstUserMessage ? textFromContent(firstUserMessage.content) : "";
|
|
419
|
+
}
|
|
420
|
+
function promptCharCount(messages) {
|
|
421
|
+
const firstUserMessage = messages.find((message) => message.role === "user");
|
|
422
|
+
return firstUserMessage ? textFromContent(firstUserMessage.content).length : 0;
|
|
423
|
+
}
|
|
424
|
+
function extractTools(messages) {
|
|
425
|
+
const tools = [];
|
|
426
|
+
for (const message of messages) {
|
|
427
|
+
if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
|
|
428
|
+
for (const part of message.content) {
|
|
429
|
+
if (!part || typeof part !== "object") continue;
|
|
430
|
+
const record = part;
|
|
431
|
+
if (record.type !== "tool_use") continue;
|
|
432
|
+
tools.push({
|
|
433
|
+
name: typeof record.name === "string" ? record.name : void 0,
|
|
434
|
+
input: record.input,
|
|
435
|
+
status: "success"
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return tools;
|
|
440
|
+
}
|
|
441
|
+
function extractSkillName(toolName, input) {
|
|
442
|
+
if (!input || typeof input !== "object") return;
|
|
443
|
+
const record = input;
|
|
444
|
+
if (toolName === "load_context") {
|
|
445
|
+
const key = record.key;
|
|
446
|
+
return typeof key === "string" ? key : void 0;
|
|
447
|
+
}
|
|
448
|
+
if (toolName === "activate_skill") {
|
|
449
|
+
const skill = record.skill ?? record.name ?? record.key;
|
|
450
|
+
return typeof skill === "string" ? skill : void 0;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function pushToolCall(map, key, toolCall) {
|
|
454
|
+
const existing = map.get(key);
|
|
455
|
+
if (existing) existing.push(toolCall);
|
|
456
|
+
else map.set(key, [toolCall]);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Stores the last ai.streamText.doStream span per conversation.
|
|
460
|
+
* Its ai.prompt.messages contains the full conversation history including all
|
|
461
|
+
* intermediate tool_use + tool_result turns from the agentic loop.
|
|
462
|
+
*/
|
|
463
|
+
const lastDoStreamByConversation = /* @__PURE__ */ new Map();
|
|
464
|
+
const toolCallsByParentSpan = /* @__PURE__ */ new Map();
|
|
465
|
+
const toolCallsByConversation = /* @__PURE__ */ new Map();
|
|
466
|
+
const pendingToolCalls = [];
|
|
467
|
+
const currentSkillByConversation = /* @__PURE__ */ new Map();
|
|
468
|
+
function rememberToolCall(span) {
|
|
469
|
+
const parentSpanId = span.parentSpanContext?.spanId;
|
|
470
|
+
const toolName = stringAttribute(span, "ai.toolCall.name");
|
|
471
|
+
const input = parseJson(stringAttribute(span, "ai.toolCall.args"));
|
|
472
|
+
const toolCall = {
|
|
473
|
+
name: toolName,
|
|
474
|
+
input,
|
|
475
|
+
status: span.status.code === 0 ? "success" : "error",
|
|
476
|
+
skillName: extractSkillName(toolName, input)
|
|
477
|
+
};
|
|
478
|
+
if (parentSpanId) pushToolCall(toolCallsByParentSpan, parentSpanId, toolCall);
|
|
479
|
+
pendingToolCalls.push(toolCall);
|
|
480
|
+
}
|
|
481
|
+
function attachToolCallsToConversation(span, conversationId) {
|
|
482
|
+
const spanId = span.spanContext().spanId;
|
|
483
|
+
const toolCalls = toolCallsByParentSpan.get(spanId) ?? pendingToolCalls.splice(0);
|
|
484
|
+
if (!toolCalls.length) return;
|
|
485
|
+
toolCallsByParentSpan.delete(spanId);
|
|
486
|
+
const currentSkill = currentSkillByConversation.get(conversationId);
|
|
487
|
+
const attributedToolCalls = toolCalls.map((toolCall) => {
|
|
488
|
+
const skillName = toolCall.skillName ?? currentSkill;
|
|
489
|
+
if (toolCall.skillName) currentSkillByConversation.set(conversationId, toolCall.skillName);
|
|
490
|
+
return {
|
|
491
|
+
...toolCall,
|
|
492
|
+
...skillName ? { skillName } : {}
|
|
493
|
+
};
|
|
494
|
+
});
|
|
495
|
+
toolCallsByConversation.set(conversationId, [...toolCallsByConversation.get(conversationId) ?? [], ...attributedToolCalls]);
|
|
496
|
+
}
|
|
497
|
+
function buildAuditLog(span, context) {
|
|
498
|
+
const lastDoStream = lastDoStreamByConversation.get(context.conversationId);
|
|
499
|
+
lastDoStreamByConversation.delete(context.conversationId);
|
|
500
|
+
const promptMessages = parseJson(stringAttribute(lastDoStream ?? span, "ai.prompt.messages"));
|
|
501
|
+
const fallbackPrompt = parseJson(stringAttribute(span, "ai.prompt"));
|
|
502
|
+
const inputMessages = promptMessages ?? fallbackPrompt?.messages ?? [];
|
|
503
|
+
const spanToolCalls = toolCallsByConversation.get(context.conversationId) ?? [];
|
|
504
|
+
toolCallsByConversation.delete(context.conversationId);
|
|
505
|
+
return {
|
|
506
|
+
id: createAuditLogKey(context.agentName, context.conversationId),
|
|
507
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
508
|
+
conversationId: context.conversationId,
|
|
509
|
+
agent: {
|
|
510
|
+
name: context.agentName,
|
|
511
|
+
llm: {
|
|
512
|
+
model: stringAttribute(span, "ai.model.id"),
|
|
513
|
+
provider: stringAttribute(span, "ai.model.provider")
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
actor: {
|
|
517
|
+
userId: context.userId,
|
|
518
|
+
ip: {
|
|
519
|
+
client: context.clientIp ?? context.forwardedFor?.split(",").map((ip) => ip.trim()).filter(Boolean)[0],
|
|
520
|
+
forwardedFor: context.forwardedFor?.split(",").map((ip) => ip.trim()).filter(Boolean) ?? []
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
prompt: extractPrompt(inputMessages),
|
|
524
|
+
response: stringAttribute(span, "ai.response.text") ?? "",
|
|
525
|
+
status: span.status.code === 0 ? "success" : "error",
|
|
526
|
+
tools: [...extractTools(inputMessages), ...spanToolCalls]
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function rememberDoStream(span, conversationId) {
|
|
530
|
+
lastDoStreamByConversation.set(conversationId, span);
|
|
531
|
+
}
|
|
532
|
+
async function handleAuditSpan(span, auditLogs, context) {
|
|
533
|
+
const auditLog = buildAuditLog(span, context);
|
|
534
|
+
if (!auditLogs) return;
|
|
535
|
+
await auditLogs.put(auditLog.id, JSON.stringify(auditLog, null, 2), { httpMetadata: { contentType: "application/json" } });
|
|
536
|
+
console.log("[AuditLog] Created", auditLog.id);
|
|
537
|
+
}
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/server/shared/features/telemetry/analytics.ts
|
|
540
|
+
function writeAnalyticsDatapoint(analytics, dataPoint) {
|
|
541
|
+
if (!analytics) return;
|
|
542
|
+
try {
|
|
543
|
+
analytics.writeDataPoint(dataPoint);
|
|
544
|
+
} catch (error) {
|
|
545
|
+
console.error("[Agent] Failed to write analytics datapoint", error);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
function handleAnalyticsSpan(span, analytics) {
|
|
549
|
+
if (span.name === "ai.streamText.doStream") {
|
|
550
|
+
const promptMessages = parseJson(stringAttribute(span, "ai.prompt.messages"));
|
|
551
|
+
const responseText = stringAttribute(span, "ai.response.text") ?? "";
|
|
552
|
+
writeAnalyticsDatapoint(analytics, {
|
|
553
|
+
indexes: [stringAttribute(span, "ai.telemetry.metadata.userId") ?? ""],
|
|
554
|
+
blobs: [
|
|
555
|
+
"llm_call",
|
|
556
|
+
stringAttribute(span, "ai.telemetry.metadata.agentName") ?? "",
|
|
557
|
+
stringAttribute(span, "ai.telemetry.metadata.conversationId") ?? "",
|
|
558
|
+
stringAttribute(span, "ai.model.id") ?? "",
|
|
559
|
+
stringAttribute(span, "ai.model.provider") ?? "",
|
|
560
|
+
stringAttribute(span, "ai.response.finishReason") ?? ""
|
|
561
|
+
],
|
|
562
|
+
doubles: [
|
|
563
|
+
numberAttribute(span, "ai.usage.inputTokens") ?? 0,
|
|
564
|
+
numberAttribute(span, "ai.usage.outputTokens") ?? 0,
|
|
565
|
+
numberAttribute(span, "ai.usage.totalTokens") ?? 0,
|
|
566
|
+
numberAttribute(span, "ai.usage.inputTokenDetails.cacheReadTokens") ?? 0,
|
|
567
|
+
numberAttribute(span, "ai.usage.inputTokenDetails.cacheWriteTokens") ?? 0,
|
|
568
|
+
durationMs(span.duration),
|
|
569
|
+
promptCharCount(promptMessages ?? []),
|
|
570
|
+
responseText.length
|
|
571
|
+
]
|
|
572
|
+
});
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (span.name === "ai.toolCall") {
|
|
576
|
+
const toolName = stringAttribute(span, "ai.toolCall.name");
|
|
577
|
+
const toolInput = parseJson(stringAttribute(span, "ai.toolCall.args"));
|
|
578
|
+
const conversationId = stringAttribute(span, "ai.telemetry.metadata.conversationId") ?? "";
|
|
579
|
+
const skillName = extractSkillName(toolName, toolInput) ?? currentSkillByConversation.get(conversationId) ?? "";
|
|
580
|
+
const success = span.status.code === 0;
|
|
581
|
+
writeAnalyticsDatapoint(analytics, {
|
|
582
|
+
indexes: [stringAttribute(span, "ai.telemetry.metadata.userId") ?? ""],
|
|
583
|
+
blobs: [
|
|
584
|
+
"tool_call",
|
|
585
|
+
stringAttribute(span, "ai.telemetry.metadata.agentName") ?? "",
|
|
586
|
+
conversationId,
|
|
587
|
+
toolName ?? "",
|
|
588
|
+
skillName,
|
|
589
|
+
success ? "success" : "error"
|
|
590
|
+
],
|
|
591
|
+
doubles: [
|
|
592
|
+
durationMs(span.duration),
|
|
593
|
+
success ? 1 : 0,
|
|
594
|
+
0,
|
|
595
|
+
0,
|
|
596
|
+
0,
|
|
597
|
+
0,
|
|
598
|
+
0,
|
|
599
|
+
0
|
|
600
|
+
]
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
//#endregion
|
|
605
|
+
//#region src/server/shared/features/telemetry/index.ts
|
|
606
|
+
var AgentSpanExporter = class {
|
|
607
|
+
auditLogs;
|
|
608
|
+
analytics;
|
|
609
|
+
context;
|
|
610
|
+
constructor(auditLogs, analytics, context) {
|
|
611
|
+
this.auditLogs = auditLogs;
|
|
612
|
+
this.analytics = analytics;
|
|
613
|
+
this.context = context;
|
|
614
|
+
}
|
|
615
|
+
export(spans, resultCallback) {
|
|
616
|
+
(async () => {
|
|
617
|
+
try {
|
|
618
|
+
for (const span of spans) if (span.name === "ai.streamText.doStream") {
|
|
619
|
+
rememberDoStream(span, this.context.conversationId);
|
|
620
|
+
attachToolCallsToConversation(span, this.context.conversationId);
|
|
621
|
+
handleAnalyticsSpan(span, this.analytics);
|
|
622
|
+
} else if (span.name === "ai.streamText") await handleAuditSpan(span, this.auditLogs, this.context);
|
|
623
|
+
else if (span.name === "ai.toolCall") {
|
|
624
|
+
rememberToolCall(span);
|
|
625
|
+
handleAnalyticsSpan(span, this.analytics);
|
|
626
|
+
}
|
|
627
|
+
resultCallback({ code: 0 });
|
|
628
|
+
} catch (error) {
|
|
629
|
+
resultCallback({
|
|
630
|
+
code: 1,
|
|
631
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
})();
|
|
635
|
+
}
|
|
636
|
+
async shutdown() {}
|
|
637
|
+
};
|
|
638
|
+
function createAgentTracer(auditLogs, analytics, context) {
|
|
639
|
+
return new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(new AgentSpanExporter(auditLogs, analytics, context))] }).getTracer("@economic/agents");
|
|
640
|
+
}
|
|
641
|
+
//#endregion
|
|
464
642
|
//#region src/server/v1/agent/Agent.ts
|
|
465
643
|
/**
|
|
466
|
-
* Base agent for Cloudflare Agents SDK Durable Objects with lazy skill loading
|
|
467
|
-
*
|
|
644
|
+
* Base agent for Cloudflare Agents SDK Durable Objects with lazy skill loading
|
|
645
|
+
* and `buildLLMParams` wiring.
|
|
468
646
|
*
|
|
469
|
-
* Handles CF infrastructure concerns: DO SQLite persistence for loaded skill state
|
|
470
|
-
* and writing audit events to D1.
|
|
647
|
+
* Handles CF infrastructure concerns: DO SQLite persistence for loaded skill state.
|
|
471
648
|
*
|
|
472
649
|
* For chat agents with message history, compaction, and conversation recording,
|
|
473
650
|
* extend {@link ChatAgent} instead.
|
|
@@ -490,14 +667,22 @@ var Agent = class extends Agent$1 {
|
|
|
490
667
|
return this.name.split(":")[0];
|
|
491
668
|
}
|
|
492
669
|
async onConnect(connection, ctx) {
|
|
670
|
+
this.clientIp = ctx.request.headers.get("CF-Connecting-IP") ?? ctx.request.headers.get("X-Forwarded-For")?.split(",")[0]?.trim();
|
|
671
|
+
this.forwardedFor = ctx.request.headers.get("X-Forwarded-For") ?? void 0;
|
|
493
672
|
if (!this.env.AGENT_DB) {
|
|
494
|
-
console.error("[
|
|
495
|
-
connection.close(3e3, "Could not connect to agent
|
|
673
|
+
console.error("[Agent] Connection rejected: no AGENT_DB bound");
|
|
674
|
+
connection.close(3e3, "Could not connect to agent");
|
|
496
675
|
return;
|
|
497
676
|
}
|
|
677
|
+
if (!this.env.AGENTS_AUDIT_LOGS) {
|
|
678
|
+
console.error("[Agent] Connection rejected: no AGENTS_AUDIT_LOGS bound. Audit logs are required.");
|
|
679
|
+
connection.close(3e3, "Could not connect to agent");
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (!this.env.AGENTS_ANALYTICS) console.warn("[Agent] No AGENTS_ANALYTICS bound. Analytics will not be collected.");
|
|
498
683
|
if (!this.getUserId()) {
|
|
499
|
-
console.error("[
|
|
500
|
-
connection.close(3e3, "Could not connect to agent
|
|
684
|
+
console.error("[Agent] Connection rejected: name must be in the format userId:uniqueChatId");
|
|
685
|
+
connection.close(3e3, "Could not connect to agent");
|
|
501
686
|
return;
|
|
502
687
|
}
|
|
503
688
|
if (this.getJwtAuthConfig) {
|
|
@@ -507,7 +692,7 @@ var Agent = class extends Agent$1 {
|
|
|
507
692
|
try {
|
|
508
693
|
result = await verifyJwt(ctx.request, config);
|
|
509
694
|
} catch (error) {
|
|
510
|
-
console.error("[
|
|
695
|
+
console.error("[Agent] JWT verification error", error);
|
|
511
696
|
connection.close(4001, "Unauthorized");
|
|
512
697
|
return;
|
|
513
698
|
}
|
|
@@ -525,43 +710,40 @@ var Agent = class extends Agent$1 {
|
|
|
525
710
|
return super.onConnect(connection, ctx);
|
|
526
711
|
}
|
|
527
712
|
/**
|
|
528
|
-
* Writes an audit event to D1 if `AGENT_DB` is bound on the environment,
|
|
529
|
-
* otherwise silently does nothing.
|
|
530
|
-
*
|
|
531
|
-
* Called automatically at the end of each LLM turn (from `onFinish` in
|
|
532
|
-
* `buildLLMParams`). Also available via `experimental_context.logEvent` in tool
|
|
533
|
-
* `execute` functions.
|
|
534
|
-
*/
|
|
535
|
-
async logEvent(message, payload) {
|
|
536
|
-
try {
|
|
537
|
-
await insertAuditEvent(this.env.AGENT_DB, this.name, message, payload);
|
|
538
|
-
} catch (error) {
|
|
539
|
-
console.error("[Agent] Failed to write audit event", error);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
713
|
* Builds the parameter object for a `streamText` or `generateText` call,
|
|
544
714
|
* pre-filling `activeSkills` from this agent instance.
|
|
545
|
-
* Injects `logEvent` into `experimental_context` and wires `onFinish` for
|
|
546
|
-
* turn-completed audit events.
|
|
547
715
|
*/
|
|
548
716
|
async buildLLMParams(config) {
|
|
549
717
|
const activeSkills = await getStoredSkills(this.sql.bind(this));
|
|
550
718
|
const experimental_context = {
|
|
551
719
|
...config.experimental_context,
|
|
552
720
|
...config.options?.body,
|
|
553
|
-
_userContext: this.userContext
|
|
554
|
-
_logEvent: this.logEvent.bind(this)
|
|
555
|
-
};
|
|
556
|
-
const onFinish = async (event) => {
|
|
557
|
-
this.logEvent("Turn completed", buildTurnLogPayload(event));
|
|
558
|
-
await config.onFinish?.(event);
|
|
721
|
+
_userContext: this.userContext
|
|
559
722
|
};
|
|
560
723
|
return buildLLMParams({
|
|
561
724
|
...config,
|
|
562
725
|
activeSkills,
|
|
563
726
|
experimental_context,
|
|
564
|
-
|
|
727
|
+
experimental_telemetry: {
|
|
728
|
+
...config.experimental_telemetry,
|
|
729
|
+
isEnabled: true,
|
|
730
|
+
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
|
|
731
|
+
agentName: this.constructor.name,
|
|
732
|
+
conversationId: this.name,
|
|
733
|
+
userId: this.getUserId(),
|
|
734
|
+
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
735
|
+
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {}
|
|
736
|
+
}),
|
|
737
|
+
metadata: {
|
|
738
|
+
agentName: this.constructor.name,
|
|
739
|
+
version: "v1",
|
|
740
|
+
conversationId: this.name,
|
|
741
|
+
userId: this.getUserId(),
|
|
742
|
+
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
743
|
+
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {},
|
|
744
|
+
...config.experimental_telemetry?.metadata
|
|
745
|
+
}
|
|
746
|
+
}
|
|
565
747
|
});
|
|
566
748
|
}
|
|
567
749
|
};
|
|
@@ -844,12 +1026,12 @@ async function getMessageRatings(db, durableObjectName) {
|
|
|
844
1026
|
//#endregion
|
|
845
1027
|
//#region src/server/v1/agent-chat/ChatAgent.ts
|
|
846
1028
|
/**
|
|
847
|
-
* Chat agent for Cloudflare Agents SDK: lazy skill loading,
|
|
848
|
-
*
|
|
1029
|
+
* Chat agent for Cloudflare Agents SDK: lazy skill loading, message persistence,
|
|
1030
|
+
* compaction, and conversation metadata in D1.
|
|
849
1031
|
*
|
|
850
1032
|
* Handles CF infrastructure concerns: DO SQLite for loaded skill state,
|
|
851
|
-
* stripping skill meta-tool messages before persistence, history replay to
|
|
852
|
-
* newly connected clients
|
|
1033
|
+
* stripping skill meta-tool messages before persistence, and history replay to
|
|
1034
|
+
* newly connected clients.
|
|
853
1035
|
*
|
|
854
1036
|
* Skill loading, compaction, and LLM calls use `buildLLMParams` from
|
|
855
1037
|
* `@economic/agents` inside `onChatMessage`.
|
|
@@ -869,6 +1051,8 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
869
1051
|
* Default is 15.
|
|
870
1052
|
*/
|
|
871
1053
|
maxMessagesBeforeCompaction = 15;
|
|
1054
|
+
clientIp;
|
|
1055
|
+
forwardedFor;
|
|
872
1056
|
/**
|
|
873
1057
|
* The user context for the session.
|
|
874
1058
|
* Define getUserContext to set a user context.
|
|
@@ -884,14 +1068,22 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
884
1068
|
return this.name.split(":")[0];
|
|
885
1069
|
}
|
|
886
1070
|
async onConnect(connection, ctx) {
|
|
1071
|
+
this.clientIp = ctx.request.headers.get("CF-Connecting-IP") ?? ctx.request.headers.get("X-Forwarded-For")?.split(",")[0]?.trim();
|
|
1072
|
+
this.forwardedFor = ctx.request.headers.get("X-Forwarded-For") ?? void 0;
|
|
887
1073
|
if (!this.env.AGENT_DB) {
|
|
888
|
-
console.error("[
|
|
889
|
-
connection.close(3e3, "Could not connect to agent
|
|
1074
|
+
console.error("[Agent] Connection rejected: no AGENT_DB bound");
|
|
1075
|
+
connection.close(3e3, "Could not connect to agent");
|
|
890
1076
|
return;
|
|
891
1077
|
}
|
|
1078
|
+
if (!this.env.AGENTS_AUDIT_LOGS) {
|
|
1079
|
+
console.error("[Agent] Connection rejected: no AGENTS_AUDIT_LOGS bound. Audit logs are required.");
|
|
1080
|
+
connection.close(3e3, "Could not connect to agent");
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
if (!this.env.AGENTS_ANALYTICS) console.warn("[Agent] No AGENTS_ANALYTICS bound. Analytics will not be collected.");
|
|
892
1084
|
if (!this.getUserId()) {
|
|
893
|
-
console.error("[
|
|
894
|
-
connection.close(3e3, "Could not connect to agent
|
|
1085
|
+
console.error("[Agent] Connection rejected: name must be in the format userId:uniqueChatId");
|
|
1086
|
+
connection.close(3e3, "Could not connect to agent");
|
|
895
1087
|
return;
|
|
896
1088
|
}
|
|
897
1089
|
if (this.getJwtAuthConfig) {
|
|
@@ -901,7 +1093,7 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
901
1093
|
try {
|
|
902
1094
|
result = await verifyJwt(ctx.request, config);
|
|
903
1095
|
} catch (error) {
|
|
904
|
-
console.error("[
|
|
1096
|
+
console.error("[Agent] JWT verification error", error);
|
|
905
1097
|
connection.close(4001, "Unauthorized");
|
|
906
1098
|
return;
|
|
907
1099
|
}
|
|
@@ -919,25 +1111,8 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
919
1111
|
}
|
|
920
1112
|
_pendingUserContextRequest;
|
|
921
1113
|
/**
|
|
922
|
-
* Writes an audit event to D1 if `AGENT_DB` is bound on the environment,
|
|
923
|
-
* otherwise silently does nothing.
|
|
924
|
-
*
|
|
925
|
-
* Called automatically at the end of each LLM turn (from `onFinish` in
|
|
926
|
-
* `buildLLMParams`). Also available via `experimental_context.logEvent` in tool
|
|
927
|
-
* `execute` functions.
|
|
928
|
-
*/
|
|
929
|
-
async logEvent(message, payload) {
|
|
930
|
-
try {
|
|
931
|
-
await insertAuditEvent(this.env.AGENT_DB, this.name, message, payload);
|
|
932
|
-
} catch (error) {
|
|
933
|
-
console.error("[ChatAgent] Failed to write audit event", error);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
1114
|
* Builds the parameter object for a `streamText` or `generateText` call,
|
|
938
1115
|
* pre-filling `messages`, `activeSkills`, and `fastModel` from this agent instance.
|
|
939
|
-
* Injects `logEvent` into `experimental_context` and wires `onFinish` for
|
|
940
|
-
* turn-completed audit events and conversation recording.
|
|
941
1116
|
*
|
|
942
1117
|
* **Compaction** runs automatically when `fastModel` is set on the class, using
|
|
943
1118
|
* `DEFAULT_MAX_MESSAGES_BEFORE_COMPACTION` (30) as the threshold. Override the
|
|
@@ -949,22 +1124,36 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
949
1124
|
const experimental_context = {
|
|
950
1125
|
...config.experimental_context,
|
|
951
1126
|
...config.options?.body,
|
|
952
|
-
_userContext: this.userContext
|
|
953
|
-
_logEvent: this.logEvent.bind(this)
|
|
1127
|
+
_userContext: this.userContext
|
|
954
1128
|
};
|
|
955
1129
|
const messages = await convertToModelMessages(this.messages);
|
|
956
1130
|
const fastModel = this.getFastModel();
|
|
957
1131
|
const processedMessages = fastModel && this.maxMessagesBeforeCompaction !== void 0 ? await compactIfNeeded(messages, fastModel, this.maxMessagesBeforeCompaction) : messages;
|
|
958
|
-
const onFinish = async (event) => {
|
|
959
|
-
this.logEvent("Turn completed", buildTurnLogPayload(event));
|
|
960
|
-
await config.onFinish?.(event);
|
|
961
|
-
};
|
|
962
1132
|
return buildLLMParams({
|
|
963
1133
|
...config,
|
|
964
1134
|
activeSkills,
|
|
965
1135
|
messages: processedMessages,
|
|
966
1136
|
experimental_context,
|
|
967
|
-
|
|
1137
|
+
experimental_telemetry: {
|
|
1138
|
+
...config.experimental_telemetry,
|
|
1139
|
+
isEnabled: true,
|
|
1140
|
+
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
|
|
1141
|
+
agentName: this.constructor.name,
|
|
1142
|
+
conversationId: this.name,
|
|
1143
|
+
userId: this.getUserId(),
|
|
1144
|
+
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
1145
|
+
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {}
|
|
1146
|
+
}),
|
|
1147
|
+
metadata: {
|
|
1148
|
+
agentName: this.constructor.name,
|
|
1149
|
+
version: "v1",
|
|
1150
|
+
conversationId: this.name,
|
|
1151
|
+
userId: this.getUserId(),
|
|
1152
|
+
...this.clientIp ? { clientIp: this.clientIp } : {},
|
|
1153
|
+
...this.forwardedFor ? { forwardedFor: this.forwardedFor } : {},
|
|
1154
|
+
...config.experimental_telemetry?.metadata
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
968
1157
|
});
|
|
969
1158
|
}
|
|
970
1159
|
async persistMessages(messages, excludeBroadcastIds = [], options) {
|
|
@@ -975,7 +1164,7 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
975
1164
|
}
|
|
976
1165
|
async onChatResponse(result) {
|
|
977
1166
|
if (result.error) {
|
|
978
|
-
console.error("[
|
|
1167
|
+
console.error("[Agent] Chat response error", result.error);
|
|
979
1168
|
return;
|
|
980
1169
|
}
|
|
981
1170
|
recordConversationSummary(this.env.AGENT_DB, this.name, this.messages, this.getFastModel());
|
|
@@ -992,11 +1181,7 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
992
1181
|
}
|
|
993
1182
|
@callable({ description: "Delete a conversation by its id" }) async deleteConversation(id) {
|
|
994
1183
|
if (!id.startsWith(`${this.getUserId()}:`)) {
|
|
995
|
-
console.error("[
|
|
996
|
-
conversationName: id,
|
|
997
|
-
userId: this.getUserId()
|
|
998
|
-
});
|
|
999
|
-
this.logEvent("Failed to delete conversation: Not owned by current user", {
|
|
1184
|
+
console.error("[Agent] Failed to delete conversation: Not owned by current user", {
|
|
1000
1185
|
conversationName: id,
|
|
1001
1186
|
userId: this.getUserId()
|
|
1002
1187
|
});
|
|
@@ -1005,7 +1190,7 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
1005
1190
|
try {
|
|
1006
1191
|
await deleteConversationRow(this.env.AGENT_DB, id);
|
|
1007
1192
|
} catch (error) {
|
|
1008
|
-
console.error("[
|
|
1193
|
+
console.error("[Agent] Failed to delete conversation row", {
|
|
1009
1194
|
conversationName: id,
|
|
1010
1195
|
error
|
|
1011
1196
|
});
|
|
@@ -1018,12 +1203,12 @@ var ChatAgent = class extends AIChatAgent {
|
|
|
1018
1203
|
for (const connection of this.getConnections()) try {
|
|
1019
1204
|
connection.close(CONVERSATION_EXPIRED_CLOSE_CODE, CONVERSATION_EXPIRED_CLOSE_REASON);
|
|
1020
1205
|
} catch (error) {
|
|
1021
|
-
console.error("[
|
|
1206
|
+
console.error("[Agent] Failed to close expired conversation connection", error);
|
|
1022
1207
|
}
|
|
1023
1208
|
return super.destroy();
|
|
1024
1209
|
}
|
|
1025
1210
|
async deleteConversationCallback() {
|
|
1026
|
-
if (await this.deleteConversation(this.name))
|
|
1211
|
+
if (await this.deleteConversation(this.name)) console.log("[Agent] Conversation deleted due to inactivity", {
|
|
1027
1212
|
conversationName: this.name,
|
|
1028
1213
|
retentionDays: this.conversationRetentionDays ?? null
|
|
1029
1214
|
});
|
|
@@ -1061,7 +1246,7 @@ var ChatAgentHarness = class extends ChatAgent {
|
|
|
1061
1246
|
return [];
|
|
1062
1247
|
}
|
|
1063
1248
|
async onChatMessage(onFinish, options) {
|
|
1064
|
-
await this._pendingUserContextRequest;
|
|
1249
|
+
if (this._pendingUserContextRequest) await this._pendingUserContextRequest;
|
|
1065
1250
|
const ctx = options?.body;
|
|
1066
1251
|
return streamText(await this.buildLLMParams({
|
|
1067
1252
|
options,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"prepublishOnly": "npm run build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@clack/prompts": "^1.2.0"
|
|
28
|
+
"@clack/prompts": "^1.2.0",
|
|
29
|
+
"@opentelemetry/sdk-trace-base": "^2.7.1"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@cloudflare/ai-chat": "^0.6.2",
|