@falai/agent 4.0.0-alpha.17 → 4.0.0-alpha.19

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/src/core/Speak.ts CHANGED
@@ -345,6 +345,7 @@ function buildPrompt<C, D>(
345
345
  ...body,
346
346
  instructionsSection([{ caption: "[Always]", items: req.instructions }], scope),
347
347
  inputSection(req.input, req.history),
348
+ saidSection(req.said),
348
349
  formatSection(envelope, options.fields),
349
350
  ),
350
351
  };
@@ -366,6 +367,21 @@ function inputSection(input: SpeakRequest["input"], history: History): string |
366
367
  return input.retry && history.at(-1)?.role === "user" ? ANSWER_PENDING : SPEAK_FIRST;
367
368
  }
368
369
 
370
+ /**
371
+ * What this turn already sent before the reply. It sits after the customer's message because that is
372
+ * when it went out; `history` stops before the turn, so it is the only place the model sees it.
373
+ */
374
+ function saidSection(said: SpeakRequest["said"]): string | null {
375
+ const lines = said.map(({ text, media }) =>
376
+ `- ${[text.trim() && `"${text.trim()}"`, media && `[media: ${media.slug}]`].filter(Boolean).join(" ")}`);
377
+ if (!lines.length) return null;
378
+ return [
379
+ "## Already sent",
380
+ "You already sent these messages this turn, in this order, and your reply goes out right after them. Do not repeat what they say.",
381
+ ...lines,
382
+ ].join("\n");
383
+ }
384
+
369
385
  /** The envelope restated in words, for providers that follow the schema by prompt only. */
370
386
  function formatSection(envelope: Envelope, fields: FieldDefs): string {
371
387
  const lines = [
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { TokenUsage } from "../types/ai.js";
11
- import type { Idle } from "../types/agent.js";
11
+ import type { Idle, OutboundMessage } from "../types/agent.js";
12
12
  import type { FieldDef, Flow, Instruction, StepBase, TalkStep } from "../types/flow.js";
13
13
  import type { History } from "../types/history.js";
14
14
  import type { Run, StepOutcomeCode } from "../types/session.js";
@@ -78,6 +78,11 @@ export interface SpeakRequest<C = unknown, D = unknown> {
78
78
  context: C;
79
79
  data: Partial<D>;
80
80
  history: History;
81
+ /**
82
+ * What this turn already sent before the reply, in order: a `say`, a fixed question. `history` ends
83
+ * before the turn, so without these the model cannot see them and says the same thing again.
84
+ */
85
+ said: OutboundMessage[];
81
86
  now: Date;
82
87
  /** Already filtered by their `if`; `when` stays for the prompt. */
83
88
  instructions: Instruction<C, D>[];
@@ -100,7 +100,9 @@ export interface RequestConfig {
100
100
  * How hard the model thinks, bound as this provider's default. What absent
101
101
  * means is the shape's own business, not one rule: on Gemini it is the
102
102
  * model's dynamic thinking; on the OpenAI and OpenRouter dialects nothing is
103
- * sent and not thinking is the default anyway; on the Anthropic shape — so
103
+ * sent, so the model decides — `medium` on GPT-5 and older, and on
104
+ * OpenRouter GLM 5.3 Flash thinks on most of the hosts that serve it. Set
105
+ * `"none"` where a turn must not think; on the Anthropic shape — so
104
106
  * `AnthropicProvider` and `ZaiProvider` — `@providerkit/core` resolves an
105
107
  * absent effort to `"none"`, and Z.ai is sent an explicit disabled marker
106
108
  * because its endpoint reads silence as thinking ON. Set a level to ask for