@economic/agents 0.0.1-alpha.4 → 0.0.1-alpha.5

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
@@ -105,6 +105,14 @@ interface SkillsConfig {
105
105
  tools: ToolSet;
106
106
  /** All available skills that can be loaded on demand */
107
107
  skills: Skill[];
108
+ /**
109
+ * The base system prompt for the agent. When provided, createSkills uses
110
+ * this to compose the full system string (base + guidance) returned by
111
+ * getSystem() and from prepareStep. Guidance is kept in the system
112
+ * parameter rather than the messages array — Anthropic/Gemini only allow
113
+ * system messages at the start of the conversation.
114
+ */
115
+ systemPrompt?: string;
108
116
  /**
109
117
  * Skill names that were loaded in previous turns, read from D1 at turn
110
118
  * start. Seeds the in-memory loadedSkills set so prior state is restored
@@ -149,14 +157,17 @@ interface SkillContext {
149
157
  /** Currently active tool names — spread into streamText */
150
158
  activeTools: string[];
151
159
  /**
152
- * Updates active tools and the guidance system message before each LLM step.
153
- * Spread into streamText.
160
+ * Updates activeTools before each LLM step. Spread into streamText.
154
161
  */
155
162
  prepareStep: ai.PrepareStepFunction;
156
163
  /**
157
- * Conversation messages read from D1 with current skill guidance already
158
- * injected just before the last message (the current user turn). Pass
159
- * directly as the `messages` param of streamText.
164
+ * Guidance text from all currently-loaded skills. Compose your system
165
+ * prompt as: `${myBase}${guidance ? '\n\n' + guidance : ''}`
166
+ */
167
+ guidance: string;
168
+ /**
169
+ * Plain conversation history from DO SQLite — no guidance injected.
170
+ * Pass directly as the `messages` param of streamText.
160
171
  */
161
172
  messages: ai.ModelMessage[];
162
173
  }
@@ -169,6 +180,12 @@ interface SkillsResult {
169
180
  getLoadedGuidance(): string;
170
181
  /** Current loaded skill names */
171
182
  getLoadedSkills(): string[];
183
+ /**
184
+ * Full system string: base system prompt concatenated with guidance from
185
+ * all currently-loaded skills. Only meaningful when `systemPrompt` was
186
+ * passed to createSkills. Use as the `system` param of streamText.
187
+ */
188
+ getSystem(): string;
172
189
  }
173
190
  //#endregion
174
191
  //#region src/agents/chat/AIChatAgentBase.d.ts
@@ -331,13 +348,14 @@ declare abstract class AIChatAgentBase<Env extends Cloudflare.Env = Cloudflare.E
331
348
  /**
332
349
  * Called by the @withSkills decorator at the start of each turn.
333
350
  *
334
- * Reads loaded skill state from D1, seeds createSkills, injects guidance,
335
- * and returns a SkillContext ready to use in a streamText call.
351
+ * Reads loaded skill state from D1, seeds createSkills, and returns a
352
+ * SkillContext ready to use in a streamText call.
353
+ *
354
+ * Guidance is exposed as `ctx.guidance` — compose your system prompt as:
355
+ * `${myBase}${ctx.guidance ? '\n\n' + ctx.guidance : ''}`
336
356
  *
337
- * The returned `messages` already has guidance injected just before the
338
- * current user turn pass it directly as the `messages` param of streamText.
339
- * Guidance is never stored in DO SQLite, so loaded_skills in D1 is the
340
- * single source of truth for which skills are active.
357
+ * Messages are plain (no guidance injected). Guidance stays out of the
358
+ * messages arrayAnthropic/Gemini only allow system messages at position 0.
341
359
  */
342
360
  protected _prepareSkillContext(): Promise<SkillContext>;
343
361
  }
@@ -358,10 +376,11 @@ declare abstract class AIChatAgentBase<Env extends Cloudflare.Env = Cloudflare.E
358
376
  * ctx: SkillContext,
359
377
  * options?: OnChatMessageOptions,
360
378
  * ) {
361
- * const { messages, ...skillArgs } = ctx;
379
+ * const { messages, guidance, ...skillArgs } = ctx;
380
+ * const base = "Your base prompt";
362
381
  * return streamText({
363
382
  * model: this.getModel(),
364
- * system: "Your base prompt — static, never includes guidance",
383
+ * system: guidance ? `${base}\n\n${guidance}` : base,
365
384
  * messages,
366
385
  * ...skillArgs,
367
386
  * onFinish,
package/dist/index.mjs CHANGED
@@ -80,7 +80,11 @@ function createSkills(config) {
80
80
  function getLoadedGuidance() {
81
81
  return [...loadedSkills].map((name) => skillMap.get(name)?.guidance).filter((g) => Boolean(g)).join("\n\n");
82
82
  }
83
- let previousGuidance = getLoadedGuidance();
83
+ function getSystem() {
84
+ const guidance = getLoadedGuidance();
85
+ if (!config.systemPrompt) return guidance;
86
+ return guidance ? `${config.systemPrompt}\n\n${guidance}` : config.systemPrompt;
87
+ }
84
88
  allTools[ACTIVATE_SKILL] = tool({
85
89
  description: buildActivateSkillDescription(skills),
86
90
  inputSchema: jsonSchema({
@@ -136,13 +140,10 @@ function createSkills(config) {
136
140
  ].join("\n");
137
141
  }
138
142
  });
139
- const prepareStep = async ({ messages }) => {
140
- const guidance = getLoadedGuidance();
141
- const updatedMessages = injectGuidance(messages, guidance, previousGuidance);
142
- previousGuidance = guidance;
143
+ const prepareStep = async () => {
143
144
  return {
144
145
  activeTools: getActiveToolNames(),
145
- messages: updatedMessages
146
+ ...config.systemPrompt !== void 0 && { system: getSystem() }
146
147
  };
147
148
  };
148
149
  return {
@@ -150,6 +151,7 @@ function createSkills(config) {
150
151
  activeTools: getActiveToolNames(),
151
152
  prepareStep,
152
153
  getLoadedGuidance,
154
+ getSystem,
153
155
  getLoadedSkills() {
154
156
  return [...loadedSkills];
155
157
  }
@@ -237,13 +239,14 @@ function filterEphemeralMessages(messages, guidanceToStrip) {
237
239
  function injectGuidance(messages, guidance, previousGuidance) {
238
240
  if (!guidance) return messages;
239
241
  const base = previousGuidance ? messages.filter((m) => !(m.role === "system" && m.content === previousGuidance)) : messages;
242
+ const insertAt = base.findLastIndex((m) => m.role === "user");
240
243
  return [
241
- ...base.slice(0, -1),
244
+ ...base.slice(0, insertAt),
242
245
  {
243
246
  role: "system",
244
247
  content: guidance
245
248
  },
246
- base.at(-1)
249
+ ...base.slice(insertAt)
247
250
  ];
248
251
  }
249
252
  //#endregion
@@ -565,13 +568,14 @@ var AIChatAgentBase = class extends AIChatAgent$1 {
565
568
  /**
566
569
  * Called by the @withSkills decorator at the start of each turn.
567
570
  *
568
- * Reads loaded skill state from D1, seeds createSkills, injects guidance,
569
- * and returns a SkillContext ready to use in a streamText call.
571
+ * Reads loaded skill state from D1, seeds createSkills, and returns a
572
+ * SkillContext ready to use in a streamText call.
573
+ *
574
+ * Guidance is exposed as `ctx.guidance` — compose your system prompt as:
575
+ * `${myBase}${ctx.guidance ? '\n\n' + ctx.guidance : ''}`
570
576
  *
571
- * The returned `messages` already has guidance injected just before the
572
- * current user turn pass it directly as the `messages` param of streamText.
573
- * Guidance is never stored in DO SQLite, so loaded_skills in D1 is the
574
- * single source of truth for which skills are active.
577
+ * Messages are plain (no guidance injected). Guidance stays out of the
578
+ * messages arrayAnthropic/Gemini only allow system messages at position 0.
575
579
  */
576
580
  async _prepareSkillContext() {
577
581
  const loadedSkills = await this._readSkillState();
@@ -584,13 +588,12 @@ var AIChatAgentBase = class extends AIChatAgent$1 {
584
588
  },
585
589
  filterSkill: (name) => this.filterSkill(name)
586
590
  });
587
- const guidance = skills.getLoadedGuidance();
588
- const messages = injectGuidance(await convertToModelMessages(this.messages), guidance);
589
591
  return {
590
592
  tools: skills.tools,
591
593
  activeTools: skills.activeTools,
592
594
  prepareStep: skills.prepareStep,
593
- messages
595
+ guidance: skills.getLoadedGuidance(),
596
+ messages: await convertToModelMessages(this.messages)
594
597
  };
595
598
  }
596
599
  };
@@ -648,18 +651,17 @@ var AIChatAgent = class extends AIChatAgentBase {
648
651
  const skills = createSkills({
649
652
  tools: this.getTools(),
650
653
  skills: this.getSkills(),
654
+ systemPrompt: this.getSystemPrompt(),
651
655
  initialLoadedSkills: loadedSkills,
652
656
  onSkillsChanged: async (updated) => {
653
657
  this._pendingSkills = updated;
654
658
  },
655
659
  filterSkill: (name) => this.filterSkill(name)
656
660
  });
657
- const guidance = skills.getLoadedGuidance();
658
- const messages = injectGuidance(await convertToModelMessages(this.messages), guidance);
659
661
  return streamText({
660
662
  model: this.getModel(),
661
- system: this.getSystemPrompt(),
662
- messages,
663
+ system: skills.getSystem(),
664
+ messages: await convertToModelMessages(this.messages),
663
665
  tools: skills.tools,
664
666
  activeTools: skills.activeTools,
665
667
  prepareStep: skills.prepareStep,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@economic/agents",
3
- "version": "0.0.1-alpha.4",
3
+ "version": "0.0.1-alpha.5",
4
4
  "description": "A starter for creating a TypeScript package.",
5
5
  "homepage": "https://github.com/author/library#readme",
6
6
  "bugs": {