@economic/agents 2.3.18 → 2.3.20
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
CHANGED
|
@@ -114,6 +114,32 @@ declare abstract class Agent<RequestContext extends Record<string, unknown> = Re
|
|
|
114
114
|
* cold start, so registration is not allowed to block the connection.
|
|
115
115
|
*/
|
|
116
116
|
protected registerInstance(): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* The `(agent_name, durable_object_name)` key of the registered row for this
|
|
119
|
+
* DO. For a parented (facet) agent this resolves to the top-level parent's
|
|
120
|
+
* row, since facets are not registered themselves.
|
|
121
|
+
*/
|
|
122
|
+
protected getRegisteredInstanceKey(): {
|
|
123
|
+
agentName: string;
|
|
124
|
+
durableObjectName: string;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Records activity against this DO's registered row by advancing its
|
|
128
|
+
* `updated_at`, so instances can be ordered by recency of activity.
|
|
129
|
+
* Best-effort and non-blocking — never allowed to interfere with a turn.
|
|
130
|
+
*/
|
|
131
|
+
protected touchInstance(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Resolves the analytics/telemetry agent name to the entry-point agent: the
|
|
134
|
+
* top-level DO registered in the `agent` catalog table.
|
|
135
|
+
*
|
|
136
|
+
* `parentPath` is root-first and empty for top-level DOs, so:
|
|
137
|
+
* - chat agent running under an Assistant → the Assistant's class name
|
|
138
|
+
* (e.g. `EvaAssistant`), since the Assistant is the catalog entry point;
|
|
139
|
+
* - standalone chat agent → its own class name (e.g. `EvaAgent`).
|
|
140
|
+
*/
|
|
141
|
+
protected getTelemetryAgentName(): string;
|
|
142
|
+
protected getTelemetryActorId(): string;
|
|
117
143
|
}
|
|
118
144
|
//#endregion
|
|
119
145
|
//#region src/server/features/messages.d.ts
|
|
@@ -238,6 +264,13 @@ declare abstract class Assistant<State = unknown> extends Agent$1<Cloudflare.Env
|
|
|
238
264
|
* Best-effort and idempotent: no-ops on conflict, retried on the next start.
|
|
239
265
|
*/
|
|
240
266
|
private registerInstance;
|
|
267
|
+
/**
|
|
268
|
+
* Records activity against this Assistant's registry row by advancing its
|
|
269
|
+
* `updated_at`, so instances can be ordered by recency of activity. An
|
|
270
|
+
* Assistant is a top-level DO, so its row is keyed by its own class + name.
|
|
271
|
+
* Best-effort and non-blocking.
|
|
272
|
+
*/
|
|
273
|
+
private touchInstance;
|
|
241
274
|
/**
|
|
242
275
|
* Binding of the legacy v1 chat Durable Object class, used to migrate a
|
|
243
276
|
* user's v1 chats into facets the first time they connect. Set this on the
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as verifyJwt, i as extractTokenFromConnectRequest, n as createAgentTracer, r as recordFeedbackAnalytics, t as routeAgentRequest } from "./route-agent-request-sspiKVkm.mjs";
|
|
2
2
|
import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
|
|
3
3
|
import { Think, skills } from "@cloudflare/think";
|
|
4
4
|
import { Output, convertToModelMessages, generateText, jsonSchema, pruneMessages, tool as tool$1 } from "ai";
|
|
@@ -28,12 +28,31 @@ const TURN_PROTOCOL_RULES_PROMPT = `These rules are specific for responding to u
|
|
|
28
28
|
* Registers a top-level DO in the global registry.
|
|
29
29
|
*
|
|
30
30
|
* Idempotent: keyed on `(agent_name, durable_object_name)`, so repeated calls
|
|
31
|
-
* across cold starts no-op and
|
|
31
|
+
* across cold starts no-op and timestamps reflect the first registration.
|
|
32
|
+
* `updated_at` is seeded to `created_at`; ongoing activity is recorded
|
|
33
|
+
* separately via {@link touchAgentInstance} so cold-start re-registration
|
|
34
|
+
* never inflates the activity timestamp.
|
|
32
35
|
*/
|
|
33
36
|
async function registerAgentInstance(db, input) {
|
|
34
|
-
await db.prepare(`INSERT INTO agent_registry (agent_name, durable_object_name, actor_id, created_at)
|
|
35
|
-
VALUES (?, ?, ?, ?)
|
|
36
|
-
ON CONFLICT (agent_name, durable_object_name) DO NOTHING`).bind(input.agentName, input.durableObjectName, input.actorId, input.createdAt).run();
|
|
37
|
+
await db.prepare(`INSERT INTO agent_registry (agent_name, durable_object_name, actor_id, created_at, updated_at)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?)
|
|
39
|
+
ON CONFLICT (agent_name, durable_object_name) DO NOTHING`).bind(input.agentName, input.durableObjectName, input.actorId, input.createdAt, input.createdAt).run();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Records activity on a registered top-level DO by advancing its `updated_at`.
|
|
43
|
+
*
|
|
44
|
+
* Used to order instances by recency of activity. Keyed on
|
|
45
|
+
* `(agent_name, durable_object_name)` to target the exact registered row —
|
|
46
|
+
* for a parented (facet) agent this is the top-level parent's row, since
|
|
47
|
+
* facets are not registered themselves.
|
|
48
|
+
*
|
|
49
|
+
* Best-effort: a no-op if the row does not yet exist (registration is
|
|
50
|
+
* retried on the next cold start).
|
|
51
|
+
*/
|
|
52
|
+
async function touchAgentInstance(db, input) {
|
|
53
|
+
await db.prepare(`UPDATE agent_registry
|
|
54
|
+
SET updated_at = ?
|
|
55
|
+
WHERE agent_name = ? AND durable_object_name = ?`).bind(input.updatedAt, input.agentName, input.durableObjectName).run();
|
|
37
56
|
}
|
|
38
57
|
//#endregion
|
|
39
58
|
//#region src/server/util/agent.ts
|
|
@@ -172,6 +191,7 @@ var Agent = class extends Think {
|
|
|
172
191
|
* `returned.tools` into your own `TurnConfig.tools` if you return tools.
|
|
173
192
|
*/
|
|
174
193
|
async beforeTurn(ctx) {
|
|
194
|
+
this.touchInstance();
|
|
175
195
|
this._toolContextForCurrentTurn = this.getToolContextForCurrentTurn(ctx.body);
|
|
176
196
|
this._toolsForCurrentTurn = ctx.tools;
|
|
177
197
|
const activeSkillName = this._getLastActivatedSkillName(ctx.messages);
|
|
@@ -195,17 +215,17 @@ var Agent = class extends Think {
|
|
|
195
215
|
experimental_telemetry: {
|
|
196
216
|
isEnabled: true,
|
|
197
217
|
tracer: createAgentTracer(this.env.AGENTS_AUDIT_LOGS, this.env.AGENTS_ANALYTICS, {
|
|
198
|
-
agentName: this.
|
|
218
|
+
agentName: this.getTelemetryAgentName(),
|
|
199
219
|
durableObjectName: this.name,
|
|
200
|
-
actorId: this.
|
|
220
|
+
actorId: this.getTelemetryActorId(),
|
|
201
221
|
clientIp: this.clientIp,
|
|
202
222
|
forwardedFor: this.forwardedFor
|
|
203
223
|
}),
|
|
204
224
|
metadata: {
|
|
205
|
-
agentName: this.
|
|
225
|
+
agentName: this.getTelemetryAgentName(),
|
|
206
226
|
version: "v2",
|
|
207
227
|
durableObjectName: this.name,
|
|
208
|
-
actorId: this.
|
|
228
|
+
actorId: this.getTelemetryActorId()
|
|
209
229
|
}
|
|
210
230
|
}
|
|
211
231
|
};
|
|
@@ -312,6 +332,47 @@ var Agent = class extends Think {
|
|
|
312
332
|
console.error("[Agent] Failed to register in agent_registry", error);
|
|
313
333
|
}
|
|
314
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* The `(agent_name, durable_object_name)` key of the registered row for this
|
|
337
|
+
* DO. For a parented (facet) agent this resolves to the top-level parent's
|
|
338
|
+
* row, since facets are not registered themselves.
|
|
339
|
+
*/
|
|
340
|
+
getRegisteredInstanceKey() {
|
|
341
|
+
return {
|
|
342
|
+
agentName: this.parentPath[0]?.className ?? this.constructor.name,
|
|
343
|
+
durableObjectName: this.parentPath[0]?.name ?? this.name
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Records activity against this DO's registered row by advancing its
|
|
348
|
+
* `updated_at`, so instances can be ordered by recency of activity.
|
|
349
|
+
* Best-effort and non-blocking — never allowed to interfere with a turn.
|
|
350
|
+
*/
|
|
351
|
+
async touchInstance() {
|
|
352
|
+
try {
|
|
353
|
+
await touchAgentInstance(this.env.AGENTS_DB, {
|
|
354
|
+
...this.getRegisteredInstanceKey(),
|
|
355
|
+
updatedAt: Date.now()
|
|
356
|
+
});
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error("[Agent] Failed to update agent_registry activity", error);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Resolves the analytics/telemetry agent name to the entry-point agent: the
|
|
363
|
+
* top-level DO registered in the `agent` catalog table.
|
|
364
|
+
*
|
|
365
|
+
* `parentPath` is root-first and empty for top-level DOs, so:
|
|
366
|
+
* - chat agent running under an Assistant → the Assistant's class name
|
|
367
|
+
* (e.g. `EvaAssistant`), since the Assistant is the catalog entry point;
|
|
368
|
+
* - standalone chat agent → its own class name (e.g. `EvaAgent`).
|
|
369
|
+
*/
|
|
370
|
+
getTelemetryAgentName() {
|
|
371
|
+
return this.parentPath[0]?.className ?? this.constructor.name;
|
|
372
|
+
}
|
|
373
|
+
getTelemetryActorId() {
|
|
374
|
+
return this.parentPath[0]?.name ?? this.getActorIdFromDurableObjectName();
|
|
375
|
+
}
|
|
315
376
|
};
|
|
316
377
|
//#endregion
|
|
317
378
|
//#region src/server/features/chats.ts
|
|
@@ -703,6 +764,7 @@ var Assistant = class extends Agent$1 {
|
|
|
703
764
|
const now = Date.now();
|
|
704
765
|
await this.subAgent(this.agent, id);
|
|
705
766
|
registerChat(this.sql.bind(this), id, now);
|
|
767
|
+
this.touchInstance(now);
|
|
706
768
|
return id;
|
|
707
769
|
}
|
|
708
770
|
async deleteChat(id) {
|
|
@@ -747,6 +809,23 @@ var Assistant = class extends Agent$1 {
|
|
|
747
809
|
}
|
|
748
810
|
}
|
|
749
811
|
/**
|
|
812
|
+
* Records activity against this Assistant's registry row by advancing its
|
|
813
|
+
* `updated_at`, so instances can be ordered by recency of activity. An
|
|
814
|
+
* Assistant is a top-level DO, so its row is keyed by its own class + name.
|
|
815
|
+
* Best-effort and non-blocking.
|
|
816
|
+
*/
|
|
817
|
+
async touchInstance(updatedAt) {
|
|
818
|
+
try {
|
|
819
|
+
await touchAgentInstance(this.env.AGENTS_DB, {
|
|
820
|
+
agentName: this.constructor.name,
|
|
821
|
+
durableObjectName: this.name,
|
|
822
|
+
updatedAt
|
|
823
|
+
});
|
|
824
|
+
} catch (error) {
|
|
825
|
+
console.error("[Assistant] Failed to update agent_registry activity", error);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
750
829
|
* Binding of the legacy v1 chat Durable Object class, used to migrate a
|
|
751
830
|
* user's v1 chats into facets the first time they connect. Set this on the
|
|
752
831
|
* concrete subclass to enable lazy v1 -> v2 migration; leave undefined to
|
|
@@ -1012,7 +1091,13 @@ var ChatAgent = class extends Agent {
|
|
|
1012
1091
|
* @returns The message id and the rating.
|
|
1013
1092
|
*/
|
|
1014
1093
|
async submitMessageFeedback(messageId, rating, comment) {
|
|
1015
|
-
|
|
1094
|
+
const result = submitMessageFeedback(this.sql.bind(this), messageId, rating, comment);
|
|
1095
|
+
recordFeedbackAnalytics(this.env.AGENTS_ANALYTICS, {
|
|
1096
|
+
agentName: this.getTelemetryAgentName(),
|
|
1097
|
+
durableObjectName: this.name,
|
|
1098
|
+
actorId: this.getTelemetryActorId()
|
|
1099
|
+
}, messageId, rating);
|
|
1100
|
+
return result;
|
|
1016
1101
|
}
|
|
1017
1102
|
/**
|
|
1018
1103
|
* Returns all message feedback for the current chat.
|
|
@@ -101,6 +101,27 @@ async function verifyJwt(request, config) {
|
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
//#endregion
|
|
104
|
+
//#region src/server/features/telemetry/skills.ts
|
|
105
|
+
/**
|
|
106
|
+
* Extracts the skill names loaded by an `activate_skill` tool call.
|
|
107
|
+
*
|
|
108
|
+
* v2 `activate_skill` input is `{ skills: string[] }` — a single call can load
|
|
109
|
+
* multiple skills, so this returns every requested skill name.
|
|
110
|
+
*/
|
|
111
|
+
function extractSkillNames(toolName, input) {
|
|
112
|
+
if (toolName !== "activate_skill" || !input || typeof input !== "object") return [];
|
|
113
|
+
const { skills } = input;
|
|
114
|
+
if (!Array.isArray(skills)) return [];
|
|
115
|
+
return skills.filter((skill) => typeof skill === "string");
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Single-skill variant used where one representative skill name is sufficient
|
|
119
|
+
* (e.g. audit-log attribution and current-skill tracking).
|
|
120
|
+
*/
|
|
121
|
+
function extractSkillName(toolName, input) {
|
|
122
|
+
return extractSkillNames(toolName, input)[0];
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
104
125
|
//#region src/server/features/telemetry/utils.ts
|
|
105
126
|
function durationMs(duration) {
|
|
106
127
|
return duration[0] * 1e3 + duration[1] / 1e6;
|
|
@@ -169,18 +190,6 @@ function extractTools(messages) {
|
|
|
169
190
|
}
|
|
170
191
|
return tools;
|
|
171
192
|
}
|
|
172
|
-
function extractSkillName(toolName, input) {
|
|
173
|
-
if (!input || typeof input !== "object") return;
|
|
174
|
-
const record = input;
|
|
175
|
-
if (toolName === "load_context") {
|
|
176
|
-
const key = record.key;
|
|
177
|
-
return typeof key === "string" ? key : void 0;
|
|
178
|
-
}
|
|
179
|
-
if (toolName === "activate_skill") {
|
|
180
|
-
const skill = record.skill ?? record.name ?? record.key;
|
|
181
|
-
return typeof skill === "string" ? skill : void 0;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
193
|
function pushToolCall(map, key, toolCall) {
|
|
185
194
|
const existing = map.get(key);
|
|
186
195
|
if (existing) existing.push(toolCall);
|
|
@@ -276,16 +285,35 @@ function writeAnalyticsDatapoint(analytics, dataPoint) {
|
|
|
276
285
|
console.error("[Agent] Failed to write analytics datapoint", error);
|
|
277
286
|
}
|
|
278
287
|
}
|
|
279
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Writes a message-feedback rating to Analytics Engine so sentiment can be
|
|
290
|
+
* aggregated across all chats without fanning out to every chat DO.
|
|
291
|
+
*
|
|
292
|
+
* Emitted on every rating change; the dashboard takes the latest rating per
|
|
293
|
+
* `messageId` (blob4) so re-rates and clears are idempotent.
|
|
294
|
+
*/
|
|
295
|
+
function recordFeedbackAnalytics(analytics, context, messageId, rating) {
|
|
296
|
+
writeAnalyticsDatapoint(analytics, {
|
|
297
|
+
indexes: [context.actorId],
|
|
298
|
+
blobs: [
|
|
299
|
+
"feedback",
|
|
300
|
+
context.agentName,
|
|
301
|
+
context.durableObjectName,
|
|
302
|
+
messageId
|
|
303
|
+
],
|
|
304
|
+
doubles: [rating]
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
function handleAnalyticsSpan(span, analytics, context) {
|
|
280
308
|
if (span.name === "ai.streamText.doStream") {
|
|
281
309
|
const promptMessages = parseJson(stringAttribute(span, "ai.prompt.messages"));
|
|
282
310
|
const responseText = stringAttribute(span, "ai.response.text") ?? "";
|
|
283
311
|
writeAnalyticsDatapoint(analytics, {
|
|
284
|
-
indexes: [
|
|
312
|
+
indexes: [context.actorId],
|
|
285
313
|
blobs: [
|
|
286
314
|
"llm_call",
|
|
287
|
-
|
|
288
|
-
|
|
315
|
+
context.agentName,
|
|
316
|
+
context.durableObjectName,
|
|
289
317
|
stringAttribute(span, "ai.model.id") ?? "",
|
|
290
318
|
stringAttribute(span, "ai.model.provider") ?? "",
|
|
291
319
|
stringAttribute(span, "ai.response.finishReason") ?? ""
|
|
@@ -305,25 +333,29 @@ function handleAnalyticsSpan(span, analytics) {
|
|
|
305
333
|
}
|
|
306
334
|
if (span.name === "ai.toolCall") {
|
|
307
335
|
const toolName = stringAttribute(span, "ai.toolCall.name");
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const skillName = extractSkillName(toolName, toolInput) ?? currentSkillByChat.get(durableObjectName) ?? "";
|
|
336
|
+
const argsRaw = stringAttribute(span, "ai.toolCall.args");
|
|
337
|
+
const toolInput = parseJson(argsRaw);
|
|
311
338
|
const success = span.status.code === 0;
|
|
312
|
-
|
|
313
|
-
|
|
339
|
+
const duration = durationMs(span.duration);
|
|
340
|
+
const argsSize = argsRaw?.length ?? 0;
|
|
341
|
+
const resultSize = stringAttribute(span, "ai.toolCall.result")?.length ?? 0;
|
|
342
|
+
const activatedSkills = extractSkillNames(toolName, toolInput);
|
|
343
|
+
const skillNames = activatedSkills.length ? activatedSkills : [currentSkillByChat.get(context.durableObjectName) ?? ""];
|
|
344
|
+
for (const skillName of skillNames) writeAnalyticsDatapoint(analytics, {
|
|
345
|
+
indexes: [context.actorId],
|
|
314
346
|
blobs: [
|
|
315
347
|
"tool_call",
|
|
316
|
-
|
|
317
|
-
durableObjectName,
|
|
348
|
+
context.agentName,
|
|
349
|
+
context.durableObjectName,
|
|
318
350
|
toolName ?? "",
|
|
319
351
|
skillName,
|
|
320
352
|
success ? "success" : "error"
|
|
321
353
|
],
|
|
322
354
|
doubles: [
|
|
323
|
-
|
|
355
|
+
duration,
|
|
324
356
|
success ? 1 : 0,
|
|
325
|
-
|
|
326
|
-
|
|
357
|
+
argsSize,
|
|
358
|
+
resultSize,
|
|
327
359
|
0,
|
|
328
360
|
0,
|
|
329
361
|
0,
|
|
@@ -349,11 +381,11 @@ var AgentSpanExporter = class {
|
|
|
349
381
|
for (const span of spans) if (span.name === "ai.streamText.doStream") {
|
|
350
382
|
rememberDoStream(span, this.context.durableObjectName);
|
|
351
383
|
attachToolCallsToChat(span, this.context.durableObjectName);
|
|
352
|
-
handleAnalyticsSpan(span, this.analytics);
|
|
384
|
+
handleAnalyticsSpan(span, this.analytics, this.context);
|
|
353
385
|
} else if (span.name === "ai.streamText") await handleAuditSpan(span, this.auditLogs, this.context);
|
|
354
386
|
else if (span.name === "ai.toolCall") {
|
|
355
387
|
rememberToolCall(span);
|
|
356
|
-
handleAnalyticsSpan(span, this.analytics);
|
|
388
|
+
handleAnalyticsSpan(span, this.analytics, this.context);
|
|
357
389
|
}
|
|
358
390
|
resultCallback({ code: 0 });
|
|
359
391
|
} catch (error) {
|
|
@@ -383,4 +415,4 @@ async function routeAgentRequest$1(request, env, options) {
|
|
|
383
415
|
return response;
|
|
384
416
|
}
|
|
385
417
|
//#endregion
|
|
386
|
-
export { verifyJwt as i, createAgentTracer as n,
|
|
418
|
+
export { verifyJwt as a, extractTokenFromConnectRequest as i, createAgentTracer as n, recordFeedbackAnalytics as r, routeAgentRequest$1 as t };
|
package/dist/v1.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as verifyJwt, i as extractTokenFromConnectRequest, n as createAgentTracer, t as routeAgentRequest } from "./route-agent-request-sspiKVkm.mjs";
|
|
2
2
|
import { Agent as Agent$1, callable, getCurrentAgent } from "agents";
|
|
3
3
|
import { Output, convertToModelMessages, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
|
|
4
4
|
import { AIChatAgent } from "@cloudflare/ai-chat";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economic/agents",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.20",
|
|
4
4
|
"description": "A starter for creating a TypeScript package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"@ai-sdk/anthropic": "^3.0.77",
|
|
27
27
|
"@ai-sdk/google": "^3.0.73",
|
|
28
28
|
"@cloudflare/ai-chat": "^0.8.5",
|
|
29
|
-
"@cloudflare/think": "
|
|
29
|
+
"@cloudflare/think": ">=0.9.0",
|
|
30
30
|
"@opentelemetry/sdk-trace-base": "^2.8.0",
|
|
31
|
-
"agents": "
|
|
31
|
+
"agents": ">=0.16.0 <1.0.0",
|
|
32
32
|
"ai": "^6.0.197",
|
|
33
33
|
"jose": "^6.2.3",
|
|
34
34
|
"nanoid": "^5.1.11"
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@cloudflare/think": ">=0.9.0",
|
|
49
|
-
"@cloudflare/vite-plugin": "
|
|
49
|
+
"@cloudflare/vite-plugin": "^1.42.0",
|
|
50
50
|
"@cloudflare/worker-bundler": ">=0.2.1",
|
|
51
51
|
"agents": ">=0.16.0 <1.0.0",
|
|
52
52
|
"vite": ">=8.0.0"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- ─── agent_registry.updated_at ────────────────────────────────────────────────────
|
|
2
|
+
-- Adds an activity timestamp to the registry so top-level DOs (one row per
|
|
3
|
+
-- Assistant, i.e. per user) can be ordered by recency of activity. Bumped by
|
|
4
|
+
-- the owning DO whenever a chat is created or has a turn.
|
|
5
|
+
--
|
|
6
|
+
-- Existing rows are backfilled to created_at so ordering remains sensible
|
|
7
|
+
-- before any new activity updates the column.
|
|
8
|
+
|
|
9
|
+
ALTER TABLE agent_registry ADD COLUMN updated_at INTEGER;
|
|
10
|
+
|
|
11
|
+
UPDATE agent_registry SET updated_at = created_at WHERE updated_at IS NULL;
|
|
12
|
+
|
|
13
|
+
CREATE INDEX IF NOT EXISTS agent_registry_updated ON agent_registry(updated_at);
|