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

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
@@ -339,6 +339,33 @@ export const datetimeSkill: Skill = {
339
339
 
340
340
  ---
341
341
 
342
+ ## Surfacing source URLs from tools
343
+
344
+ Any tool can surface source URLs into the message stream by including a `sources` array in its return value. `buildLLMParams` automatically detects this and emits `source-url` stream parts that the playground's Sources block renders.
345
+
346
+ ```typescript
347
+ execute: async ({ query }) => {
348
+ const data = await fetchResults(query);
349
+ return {
350
+ results: data.results, // LLM receives full content
351
+ sources: data.results.map(r => ({ url: r.url, title: r.title })), // rendered as source links
352
+ };
353
+ },
354
+ ```
355
+
356
+ The `sources` array is picked up by a built-in `experimental_transform` inside `buildLLMParams` — no agent changes, no writer passed to the tool, no additional wiring. The LLM continues to receive the full result object including `sources`. The transform fires on every `tool-result` stream part and emits a `source` part for each entry.
357
+
358
+ Each source entry shape:
359
+
360
+ | Field | Type | Required | Description |
361
+ | ------- | -------- | -------- | ----------------------- |
362
+ | `url` | `string` | Yes | The URL to link to. |
363
+ | `title` | `string` | No | Display name in the UI. |
364
+
365
+ The playground's `UIMessageRenderer` collects all `source-url` parts from a message and displays them in a collapsible **Sources** block above the response text.
366
+
367
+ ---
368
+
342
369
  ## Compaction
343
370
 
344
371
  When `fastModel` is set on the agent class, compaction runs automatically before each turn:
package/dist/index.mjs CHANGED
@@ -324,6 +324,39 @@ ${loadedGuidance}`;
324
324
  return prompt.trim();
325
325
  }
326
326
  /**
327
+ * Convention: tools can include a `sources` array in their return value to surface
328
+ * source URLs in the message stream. This transform detects it automatically.
329
+ *
330
+ * ```typescript
331
+ * // In any tool's execute function:
332
+ * return { myContent: "...", sources: [{ url: "https://...", title: "Page title" }] };
333
+ * ```
334
+ */
335
+ function buildSourcesTransform(additional) {
336
+ const sourcesTransform = () => new TransformStream({ transform(chunk, controller) {
337
+ controller.enqueue(chunk);
338
+ if (chunk.type !== "tool-result") return;
339
+ const output = chunk.output;
340
+ if (!output || typeof output !== "object" || Array.isArray(output)) return;
341
+ const sources = output.sources;
342
+ if (!Array.isArray(sources)) return;
343
+ for (const source of sources) {
344
+ if (!source || typeof source !== "object") continue;
345
+ const s = source;
346
+ if (typeof s.url !== "string") continue;
347
+ controller.enqueue({
348
+ type: "source",
349
+ sourceType: "url",
350
+ id: crypto.randomUUID(),
351
+ url: s.url,
352
+ ...typeof s.title === "string" ? { title: s.title } : {}
353
+ });
354
+ }
355
+ } });
356
+ if (!additional) return sourcesTransform;
357
+ return [sourcesTransform, ...Array.isArray(additional) ? additional : [additional]];
358
+ }
359
+ /**
327
360
  * Builds the parameter object for a Vercel AI SDK `streamText` or `generateText` call.
328
361
  *
329
362
  * Handles message conversion, optional compaction, skill wiring (`activate_skill`,
@@ -338,11 +371,13 @@ ${loadedGuidance}`;
338
371
  * ```
339
372
  */
340
373
  async function buildLLMParams(config) {
341
- const { options, messages, activeSkills = [], skills, fastModel, maxMessagesBeforeCompaction, ...rest } = config;
374
+ const { options, messages, activeSkills = [], skills, fastModel, maxMessagesBeforeCompaction, experimental_transform, ...rest } = config;
342
375
  const rawMessages = await convertToModelMessages(messages);
343
376
  const processedMessages = fastModel && maxMessagesBeforeCompaction !== void 0 ? await compactIfNeeded(rawMessages, fastModel, maxMessagesBeforeCompaction) : rawMessages;
377
+ const composedTransform = buildSourcesTransform(experimental_transform);
344
378
  const baseParams = {
345
379
  ...rest,
380
+ experimental_transform: composedTransform,
346
381
  messages: processedMessages,
347
382
  experimental_context: options?.body,
348
383
  abortSignal: options?.abortSignal,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "0.0.1-beta.3",
3
+ "version": "0.0.1-beta.4",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "license": "MIT",
6
6
  "files": [