@economic/agents 0.0.1-beta.1 → 0.0.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,6 +202,18 @@ When `fastModel` is set, compaction runs automatically with a default threshold
202
202
 
203
203
  When `fastModel` is `undefined` (the default), compaction is disabled regardless of `maxMessagesBeforeCompaction`.
204
204
 
205
+ ### `getConversations` (callable)
206
+
207
+ `AIChatAgent` exposes a [callable method](https://developers.cloudflare.com/agents/api-reference/callable-methods/) via the Agents SDK `@callable()` decorator (`agents` package). From any connected client (for example the object returned by `useAgent` / `useAIChatAgent`), invoke:
208
+
209
+ ```typescript
210
+ const rows = await agent.call("getConversations");
211
+ ```
212
+
213
+ - **User scope**: `userId` is taken from the segment before the first `:` in the Durable Object name (`userId:chatId`), the same format enforced in `onConnect`.
214
+ - **Data**: Reads from `AGENT_DB`, returning all `conversations` rows whose `durable_object_name` matches `userId:%`, ordered by `updated_at` descending (newest first). Each row includes `durable_object_name`, `title`, `summary`, `created_at`, and `updated_at`.
215
+ - **No D1**: If `AGENT_DB` is not bound, the method returns `undefined` and does not throw.
216
+
205
217
  ### `getLoadedSkills()`
206
218
 
207
219
  Protected method on `AIChatAgent`. Returns skill names persisted from previous turns (read from DO SQLite). Used internally by `this.buildLLMParams()`.
@@ -546,12 +558,14 @@ No subclass code is needed — this runs automatically when `AGENT_DB` is bound
546
558
 
547
559
  ### Querying conversation lists
548
560
 
549
- To fetch all conversations for a user, ordered by most recent:
561
+ From a connected agent client, prefer the built-in callable (see **`getConversations` (callable)** under [`AIChatAgent`](#aichatagent)): `await agent.call("getConversations")`.
562
+
563
+ To query D1 directly (same logic as the callable), filter by `durable_object_name` prefix — one row per chat, keyed as `userId:chatId`:
550
564
 
551
565
  ```sql
552
- SELECT durable_object_id, title, summary, created_at, updated_at
566
+ SELECT durable_object_name, title, summary, created_at, updated_at
553
567
  FROM conversations
554
- WHERE user_id = '148583_matt'
568
+ WHERE durable_object_name LIKE '148583_matt:%'
555
569
  ORDER BY updated_at DESC;
556
570
  ```
557
571
 
@@ -563,9 +577,9 @@ If `userId` is not set on the request body, the upsert is skipped and a `console
563
577
 
564
578
  ### Classes
565
579
 
566
- | Export | Description |
567
- | ------------- | --------------------------------------------------------------------------------------------------------------------- |
568
- | `AIChatAgent` | Abstract CF Durable Object base class. Implement `onChatMessage`. Manages skill state, history replay, and audit log. |
580
+ | Export | Description |
581
+ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
582
+ | `AIChatAgent` | Abstract CF Durable Object base class. Implement `onChatMessage`. Manages skill state, history replay, audit log, and D1 `conversations` upserts. Exposes callable `getConversations` for listing a user’s conversations from the client. |
569
583
 
570
584
  ### Functions
571
585
 
package/dist/index.d.mts CHANGED
@@ -16,8 +16,9 @@ interface Skill {
16
16
  description: string;
17
17
  /**
18
18
  * Guidance text for this skill — e.g. rate limits, preferred patterns,
19
- * when to use each tool. Appended to the `system` prompt each turn for any
20
- * skill that is loaded, keeping `system` mostly cacheable.
19
+ * when to use each tool. Injected into the `## Tools` section of the
20
+ * system prompt via `buildSystemPrompt` in `llm.ts` whenever this skill
21
+ * is loaded.
21
22
  */
22
23
  guidance?: string;
23
24
  tools: ToolSet;
package/dist/index.mjs CHANGED
@@ -39,6 +39,9 @@ function buildActivateSkillDescription(skills) {
39
39
  skills.map((s) => `• ${s.name} — ${s.description}`).join("\n")
40
40
  ].join("\n");
41
41
  }
42
+ function buildAvailableSkillList(skills) {
43
+ return skills.map((s) => `**${s.name}**: ${s.description}`).join("\n");
44
+ }
42
45
  const LIST_CAPABILITIES_DESCRIPTION = "List all tools currently available to you, which skills are loaded, and which can still be loaded. Call this when the user asks about your capabilities or what you can do.";
43
46
  /**
44
47
  * Sentinel appended to a successful activate_skill result.
@@ -63,6 +66,7 @@ function createSkills(config) {
63
66
  const { tools: alwaysOnTools, skills } = config;
64
67
  const loadedSkills = new Set(config.initialLoadedSkills ?? []);
65
68
  const skillMap = new Map(skills.map((s) => [s.name, s]));
69
+ const availableSkillList = skills.length > 0 ? buildAvailableSkillList(skills) : "";
66
70
  const allTools = {};
67
71
  Object.assign(allTools, alwaysOnTools);
68
72
  for (const skill of skills) Object.assign(allTools, skill.tools);
@@ -80,12 +84,13 @@ function createSkills(config) {
80
84
  return names;
81
85
  }
82
86
  function getLoadedGuidance() {
83
- return [...loadedSkills].map((name) => skillMap.get(name)?.guidance).filter((g) => Boolean(g)).join("\n\n");
84
- }
85
- function getSystem() {
86
- const guidance = getLoadedGuidance();
87
- if (!config.systemPrompt) return guidance;
88
- return guidance ? `${config.systemPrompt}\n\n${guidance}` : config.systemPrompt;
87
+ const sections = [...loadedSkills].flatMap((name) => {
88
+ const skill = skillMap.get(name);
89
+ if (!skill?.guidance) return [];
90
+ return [`**${skill.name}**\n${skill.guidance}`];
91
+ });
92
+ if (sections.length === 0) return "";
93
+ return sections.join("\n\n");
89
94
  }
90
95
  allTools[ACTIVATE_SKILL] = tool({
91
96
  description: buildActivateSkillDescription(skills),
@@ -132,17 +137,14 @@ function createSkills(config) {
132
137
  }
133
138
  });
134
139
  const prepareStep = async () => {
135
- return {
136
- activeTools: getActiveToolNames(),
137
- ...config.systemPrompt !== void 0 && { system: getSystem() }
138
- };
140
+ return { activeTools: getActiveToolNames() };
139
141
  };
140
142
  return {
141
143
  tools: allTools,
142
144
  activeTools: getActiveToolNames(),
143
145
  prepareStep,
146
+ availableSkillList,
144
147
  getLoadedGuidance,
145
- getSystem,
146
148
  getLoadedSkills() {
147
149
  return [...loadedSkills];
148
150
  }
@@ -281,6 +283,47 @@ async function compactIfNeeded(messages, model, tailSize) {
281
283
  //#endregion
282
284
  //#region src/server/llm.ts
283
285
  /**
286
+ * Composes the full system prompt from its three parts: the consumer's base
287
+ * string, the static skill roster, and the dynamic loaded-skill guidance.
288
+ *
289
+ * The full shape, at a glance:
290
+ *
291
+ * {base}
292
+ *
293
+ * ## Tools
294
+ *
295
+ * Use `activate_skill` to load these skills (BE PROACTIVE on requesting
296
+ * tools based on the user's request AND you DON'T need to mention that you
297
+ * are loading more tools):
298
+ *
299
+ * **{name}**: {description}
300
+ * ...
301
+ *
302
+ * **Loaded skill instructions**
303
+ * The following skills are currently active. Apply their instructions when
304
+ * using the corresponding tools.
305
+ *
306
+ * **{name}**
307
+ * {guidance body}
308
+ */
309
+ function buildSystemPrompt(basePrompt, availableSkillList, loadedGuidance) {
310
+ let prompt = `${basePrompt}
311
+
312
+ ## Tools
313
+
314
+ ### Loading More Tools:
315
+ Use \`activate_skill\` to load these skills (BE PROACTIVE on requesting tools based on the user's request AND you DON'T need to mention that you are loading more tools):
316
+
317
+ ${availableSkillList}`;
318
+ if (loadedGuidance) prompt += `
319
+
320
+ ### Loaded skill instructions:
321
+ The following skills are currently active. Apply their instructions when using the corresponding tools.
322
+
323
+ ${loadedGuidance}`;
324
+ return prompt.trim();
325
+ }
326
+ /**
284
327
  * Builds the parameter object for a Vercel AI SDK `streamText` or `generateText` call.
285
328
  *
286
329
  * Handles message conversion, optional compaction, skill wiring (`activate_skill`,
@@ -306,22 +349,21 @@ async function buildLLMParams(config) {
306
349
  stopWhen: rest.stopWhen ?? stepCountIs(20)
307
350
  };
308
351
  if (!skills?.length) return baseParams;
352
+ const base = typeof rest.system === "string" ? rest.system : void 0;
309
353
  const skillsCtx = createSkills({
310
354
  tools: rest.tools ?? {},
311
355
  skills,
312
- initialLoadedSkills: activeSkills,
313
- systemPrompt: typeof rest.system === "string" ? rest.system : void 0
356
+ initialLoadedSkills: activeSkills
314
357
  });
315
358
  const prepareStep = async (stepOptions) => {
316
- const skillsResult = await skillsCtx.prepareStep(stepOptions) ?? {};
317
359
  return {
318
- activeTools: skillsResult.activeTools ?? [],
319
- system: skillsResult.system
360
+ activeTools: (await skillsCtx.prepareStep(stepOptions) ?? {}).activeTools ?? [],
361
+ system: buildSystemPrompt(base, skillsCtx.availableSkillList, skillsCtx.getLoadedGuidance())
320
362
  };
321
363
  };
322
364
  return {
323
365
  ...baseParams,
324
- system: skillsCtx.getSystem() || rest.system,
366
+ system: buildSystemPrompt(base, skillsCtx.availableSkillList, skillsCtx.getLoadedGuidance()),
325
367
  tools: skillsCtx.tools,
326
368
  activeTools: skillsCtx.activeTools,
327
369
  prepareStep
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "0.0.1-beta.1",
3
+ "version": "0.0.1-beta.3",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "license": "MIT",
6
6
  "files": [